diff --git a/.bowerrc b/.bowerrc deleted file mode 100644 index b5ecdd1..0000000 --- a/.bowerrc +++ /dev/null @@ -1,3 +0,0 @@ -{ - "directory": "lib" -} diff --git a/.commitlintrc.json b/.commitlintrc.json new file mode 100644 index 0000000..b5caa5a --- /dev/null +++ b/.commitlintrc.json @@ -0,0 +1,23 @@ +{ + "extends": ["@commitlint/config-conventional"], + "rules": { + "type-enum": [ + 2, + "always", + [ + "build", + "chore", + "ci", + "docs", + "feat", + "fix", + "perf", + "refactor", + "revert", + "style", + "test", + "website" + ] + ] + } +} diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..e3b39df --- /dev/null +++ b/.editorconfig @@ -0,0 +1,11 @@ +root = true + +[*] +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.{ts,json}] +indent_style = space +indent_size = 2 diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..665f4ed --- /dev/null +++ b/.eslintrc @@ -0,0 +1,42 @@ +{ + "parser": "@typescript-eslint/parser", + "extends": [ + "airbnb-base", + "plugin:@typescript-eslint/recommended", + "prettier", + "prettier/@typescript-eslint" + ], + "env": { + "browser": true + }, + "plugins": ["prettier"], + "ignorePatterns": ["/types/**/*.ts", "/website/vendor/**/*"], + "rules": { + "prettier/prettier": ["error"], + "@typescript-eslint/no-unused-vars": ["error", { "ignoreRestSiblings": true }], + "import/extensions": [ + "error", + "ignorePackages", + { + "js": "never", + "mjs": "never", + "jsx": "never", + "ts": "never", + "tsx": "never" + } + ] + }, + "overrides": [ + { + "files": ["spec/**/*.ts"], + "rules": { + "import/no-unresolved": "off" + } + } + ], + "settings": { + "import/resolver": { + "typescript": {} + } + } +} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..e79dc41 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,31 @@ +name: Continuous Integration + +on: + - pull_request + - push + +jobs: + ci: + name: Continuous integration + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Setup Node + uses: actions/setup-node@v2-beta + with: + node-version: "14" + - name: Install dependencies + run: npm install + - name: Check build + run: npm run build + - name: Check commit messages + uses: wagoid/commitlint-github-action@v2 + with: + configFile: .commitlintrc.json + - name: Check coding standards + run: npm run lint + - name: Run tests + run: npm test diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml new file mode 100644 index 0000000..dd1eeb1 --- /dev/null +++ b/.github/workflows/coverage.yml @@ -0,0 +1,26 @@ +name: Coverage + +on: + push: + branches: + - main + +jobs: + coverage: + name: Measure coverage and upload it + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Setup Node + uses: actions/setup-node@v2-beta + with: + node-version: "14" + - name: Install dependencies + run: npm install + - name: Run tests with coverage + run: npm run coverage-lcov + - name: Upload coverage + uses: codecov/codecov-action@v1 + with: + directory: ./coverage diff --git a/.github/workflows/deploy-website.yml b/.github/workflows/deploy-website.yml new file mode 100644 index 0000000..4df8c3f --- /dev/null +++ b/.github/workflows/deploy-website.yml @@ -0,0 +1,32 @@ +name: Deploy website + +on: + push: + branches: + - main + +jobs: + couscous: + name: Deploy website with Couscous + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: "7.4" + tools: composer + - name: Setup Node + uses: actions/setup-node@v2-beta + with: + node-version: "14" + - name: Setup Couscous + run: composer global require couscous/couscous:dev-master + - name: Couscous + run: couscous generate + - name: Deploy + uses: peaceiris/actions-gh-pages@v3 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: ./.couscous/generated diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..a04b92f --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,48 @@ +name: Release + +on: + push: + tags: + - 'v*' + +jobs: + release: + name: Create and publish a release + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Setup Node (npm registry) + uses: actions/setup-node@v2-beta + with: + node-version: "14" + registry-url: https://registry.npmjs.org + - name: Install dependencies + run: npm install + - name: Run tests + run: npm test + - name: Create a GitHub release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ github.ref }} + release_name: Release ${{ github.ref }} + - name: Publish to npm + run: npm publish + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + - name: Setup Node (GitHub registry) + uses: actions/setup-node@v2-beta + with: + node-version: "14" + registry-url: https://npm.pkg.github.com + - name: Change package name + uses: datamonsters/replace-action@v2 + with: + files: package.json + replacements: '"geocoder-js"="@geocoder-php/geocoder-js"' + - name: Publish to GitHub + run: npm publish + env: + NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index 9750bd9..f81248d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,10 @@ -.idea -lib node_modules -.grunt -.DS_Store +/.couscous +/.nyc_output +/coverage +/dist +/types +/website/vendor +/website/webfonts +/website/package-lock.json +/website/yarn.lock diff --git a/.nycrc.json b/.nycrc.json new file mode 100644 index 0000000..8e47560 --- /dev/null +++ b/.nycrc.json @@ -0,0 +1,4 @@ +{ + "cache": false, + "exclude": ["spec/**", "**/index.ts", "**/AdminLevel.ts", "**/*Geocoded.ts", "**/*GeocodeQuery.ts", "**/*ReverseQuery.ts", "**/ResponseError.ts", "src/provider/mapquest/MapQuestLocation.ts"] +} diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 4d7aab0..0000000 --- a/.travis.yml +++ /dev/null @@ -1,5 +0,0 @@ -language: node_js -node_js: - - "0.10" -before_install: - - npm install -g grunt-cli diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..54ab1c6 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,78 @@ +# Changelog + +## 0.8.0 + +* [Bing] Better Bing provider +* [Mapbox] Add `fuzzyMatch` geocode parameter +* [OpenCage] Add `precision` to `OpenCageGeocoded` +* [OpenCage] Replace `minConfidence` with `minPrecision` for geocode and geodecode + +## 0.7.0 + +* [Yandex] Better Yandex provider +* [Nominatim] Remove `viewBox` parameter in `GeocodeQuery` to use `bounds` instead +* [GeoJsonDumper] Use `bbox` member for `bounds` +* The `bounds` parameter in `GeocodeQuery` is now an object with `latitudeSW`, `longitudeSW`, `latitudeNE`, `longitudeNE` keys +* The `latitude` and `longitude` parameters in `GeocodeQuery` are replaced by a `coordinates` parameter (an object with `latitude` and `longitude` keys) +* `Geocoded` returns an object with `latitudeSW`, `longitudeSW`, `latitudeNE`, `longitudeNE` keys for the `bounds` +* The `latitude` and `longitude` parameters in `Geocoded` are replaced by a `coordinates` parameter (an object with `latitude` and `longitude` keys) + +## 0.6.0 + +* [MapQuest] Better MapQuest provider + +## 0.5.0 + +* Add special chain provider +* Add GeoPlugin provider +* Add `errorCallback` parameter to `geocode` and `geodecode` +* Add `body` parameter to `executeRequest` +* [MapQuest] Fix geocode request (bad encoded query) + +## 0.4.0 + +* [Google] Better Google Maps provider +* [Nominatim] Better handling of geodecode errors +* [ExternalLoader] Add `getOptions` method to interface +* [ExternalLoader] Rename `JSONPCallback` in params to `jsonpCallback` +* Rename `GoogleAPIProvider` to `GoogleMapsProvider` +* Add Node examples + +## 0.3.0 + +* Add OpenCage Provider +* Add time zone to `Geocoded` +* [Mapbox] Add `countryCodes`, `proximity` and `reverseMode` to geo(de)code parameters +* [Mapbox] Add `resultType` to `Geocoded` +* [Mapbox] Add admin levels to `Geocoded` +* Rename `ExternalURILoader` to `ExternalLoader` + +## 0.2.4 + +* [Nominatim] Fix error when no result is received + +## 0.2.3 + +* Fix TypeScript types for the library to be usable in a TS project + +## 0.2.2 + +* Use relative paths for build files + +## 0.2.1 + +* Missing types folder when publishing + +## 0.2.0 + +* Better Nominatim support +* Better GeoJSON support + +## 0.1.0 + +* Library rewritten in TypeScript +* Library can be used as a module as well +* Add Mapbox provider +* Formatted address, bounds and country support +* Better handling of provider options (JSONP, SSL) +* The methods `geocode` and `geodecode` can take an object or a `Query` diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..94afa85 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,6 @@ +Pull requests will **not** be accepted without: + +1) All unit/functional tests working. +2) Any potentially affected example files still functional. +3) Any additional functionality covered by unit/functional tests. +4) Code should be linted without errors (`npm run lint`). diff --git a/README.md b/README.md index 9311433..693e057 100644 --- a/README.md +++ b/README.md @@ -1,83 +1,316 @@ GeocoderJS ========== -**GeocoderJS** is a JavaScript port of the [Geocoder -PHP](http://geocoder-php.org/Geocoder/) library. It's meant as a compliment for -client-side geocoding applications. +[![CI](https://img.shields.io/github/workflow/status/geocoder-php/geocoder-js/Continuous%20Integration?event=push)](https://github.com/geocoder-php/geocoder-js/actions) +[![codecov](https://img.shields.io/codecov/c/gh/geocoder-php/geocoder-js/main)](https://codecov.io/gh/geocoder-php/geocoder-js) -[![Build -Status](https://travis-ci.org/geocoder-php/geocoder-js.png?branch=master)](https://travis-ci.org/geocoder-php/geocoder-js) +[![npm](https://img.shields.io/npm/v/geocoder-js)](https://www.npmjs.com/package/geocoder-js) +[![minified-size](https://img.shields.io/bundlephobia/min/geocoder-js)](https://bundlephobia.com/result?p=geocoder-js) +[![downloads](https://img.shields.io/npm/dw/geocoder-js)](https://www.npmjs.com/package/geocoder-js) -[![Dependency Status](https://david-dm.org/geocoder-php/geocoder-js.png)](https://david-dm.org/geocoder-php/geocoder-js) +GeocoderJS is a universal JavaScript library for client-side geocoding applications with multiple built-in providers. -[![devDependency Status](https://david-dm.org/geocoder-php/geocoder-js/dev-status.png)](https://david-dm.org/geocoder-php/geocoder-js#info=devDependencies) +Depending of the chosen provider, it can use geocoding, reverse geocoding or IP geolocation. -Building --------- +It is a port of the [Geocoder PHP](https://geocoder-php.org/) library. -You can build an uglified version of the script by running 'grunt build'. +This library is platform agnostic: it is available either server-side (**Node**) or client-side (**browsers**, **React Native**, **Electron**). -Testing -------- +Installation +------------ -Unit tests are handled by Jasmine. To run unit tests from the command line, use 'grunt test'. +Add the library to your project: -Contributing ------------- +```shell +npm install --save geocoder-js +``` + +> ⚠️ **Warning**: If you need to use this library in an environment not supporting the Promise API such as Internet Explorer, you must install an ES6 Promise compatible polyfill like [es6-promise](https://github.com/jakearchibald/es6-promise). + +Usage +----- + +You can either use GeocoderJS as a module or as a direct dependency. + +As a module: + +```javascript +import GeocoderJS from "geocoder-js"; + +const openStreetMapGeocoder = GeocoderJS.createGeocoder("openstreetmap"); + +openStreetMapGeocoder.geocode("1600 Pennsylvania Ave NW, Washington, DC", (result) => { + console.log(result); +}); +``` + +For this example, the output will be something like this: + +```javascript +[ + NominatimGeocoded { + coordinates: { latitude: 38.8976998, longitude: -77.03655348862276 }, + bounds: { + latitudeSW: 38.8974898, + longitudeSW: -77.0368542, + latitudeNE: 38.897911, + longitudeNE: -77.0362526 + }, + formattedAddress: undefined, + streetNumber: '1600', + streetName: 'Pennsylvania Avenue Northwest', + subLocality: undefined, + locality: 'Washington', + postalCode: '20500', + region: 'District of Columbia', + adminLevels: [ + AdminLevel { + level: 1, + name: 'District of Columbia', + code: undefined + }, + AdminLevel { + level: 2, + name: 'Washington', + code: undefined + } + ], + country: 'United States of America', + countryCode: 'us', + timezone: undefined, + displayName: 'White House, 1600, Pennsylvania Avenue Northwest, Washington, District of Columbia, 20500, United States of America', + osmId: 238241022, + osmType: 'way', + category: 'historic', + type: 'castle', + attribution: 'Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright' + }, + // ... (other results) +] +``` + +If you want to use the library as a direct dependecy (for browsers only), copy `dist/geocoder.js` or `dist/geocoder.min.js` to your dependencies. + +GeocoderJS will be available in the global environment: -Contibution libraries are installed by Bower. Unit tests are covered by Jasmine. -Pull requests will **not** be accepted without: +```javascript +const openStreetMapGeocoder = GeocoderJS.createGeocoder("openstreetmap"); -* a) all unit/functional tests working -* b) any potentially affected example files still functional -* c) any additional functionality covered by unit/functional tests. +openStreetMapGeocoder.geodecode("44.915", "-93.21", (result) => { + console.log(result); +}); +``` + +For a more advanced usage, see the example below: + +```javascript +import GeocoderJS, { ReverseQuery } from "geocoder-js"; + +const googleGeocoder = GeocoderJS.createGeocoder({ + provider: "googlemaps", + apiKey: "YOUR_API_KEY", + useSsl: true, + useJsonp: false, + // other specific provider options +}); + +googleGeocoder.geocode({ + text: "1600 Pennsylvania Ave, Washington, DC", + locale: "FR", + limit: 10, + // other specific provider parameters +}, (result) => { + console.log(result); +}); + +const reverseQuery = ReverseQuery.create({ + coordinates: { + latitude: "44.915", + longitude: "-93.21", + }, +}) +.withLocale("FR") +.withLimit(7); +googleGeocoder.geodecode(reverseQuery, (result) => { + console.log(result); +}); +``` + +### Common Options (`createGeocoder` method) + +- `useSsl`: boolean to use the HTTPS API of the providers +- `useJsonp`: boolean to use JSONP +- `apiKey`: the API key to use for the provider + +### Common `geocode` parameters (`GeocodeQuery` object) + +- `text`: what is searched +- `ip`: the IP searched +- `bounds` (object with `latitudeSW`, `longitudeSW`, `latitudeNE` and `longitudeNE` keys): the bounds to use (either bias or filter the results) +- `locale`: the locale to use for the query +- `limit`: the maximum number of results to have + +### Common `geodecode` parameters (`ReverseQuery` object) + +- `coordinates` (object with `latitude`, `longitude` keys): the coordinates to search for +- `locale`: the locale to use for the query +- `limit`: the maximum number of results to have + +### Common Result Properties (`Geocoded` object) + +The result of a query is a `Geocoded` object which maps the following common information: +- Coordinates (object with `latitute` and `longitude` keys) +- Bounds (object with `latitudeSW`, `longitudeSW`, `latitudeNE`, `longitudeNE` keys) +- Formatted address +- Address details: street number, street name, (sub) locality, postal code, region, administration levels, country (with its code) +- Time zone + +You can either use getter methods to retrieve them or use the `toObject` method to manipulate an object containing the properties. Providers --------- GeocoderJS comes with modules to integrate with various geocoding providers. + The following table summarizes the features of each: - - + + + - - - - + + + + + + + + + + + + - - - - + + + + + - - - - + + + + + + + + + + + + - - - + + + + - - - + + + + + + + + + + +
ProviderWorks in browsers?Works in Node.JS?CodenameSupports location geocoding? Supports reverse geocoding?Supports IP geolocation?
Google MapsyesyesyesOpenStreetMap (Nominatim)openstreetmap or nominatim✅️ yes✅️ yes❌️ no
OpenCageopencage✅️ yes✅️ yes❌️ no
MapquestyesuntestedyesGoogle Maps (Geocoding API)google or googlemaps✅️ yes✅️ yes❌️ no
OpenStreetMapyesuntestedyesMapboxmapbox✅️ yes✅️ yes❌️ no
MapQuestmapquest✅️ yes✅️ yes❌️ no
Bingyesuntestedyesbing✅️ yes✅️ yes❌️ no
Yandexyesuntestedyesyandex✅️ yes✅️ yes❌️ no
GeoPlugingeoplugin❌️ no❌️ no✅️ yes
+### Specific Provider Usage + +The documentation for specific provider options, parameters and results can be found [here](docs/provider_usage.md). + +Special Providers +----------------- + +A `chain` provider is available: it iterates over multiple providers. + +For more information, see [its documentation](docs/provider_usage/chain.md). + +Dumpers +------- + +Dumpers transform a `Geocoded` object to another format. + +### GeoJSON + +[GeoJSON](https://geojson.org/) is a format for encoding a variety of geographic data structures. + +#### Usage + +```javascript +import GeocoderJS, { GeoJsonDumper } from "geocoder-js"; + +const nominatimGeocoder = GeocoderJS.createGeocoder("nominatim"); + +nominatimGeocoder.geocode("1600 Pennsylvania Ave, Washington, DC", (result) => { + console.log(result); + console.log("GeoJSON:", GeoJsonDumper.dump(result[0])); +}); +``` + +Building +-------- + +```shell +npm run build +``` + +Testing +------- + +Unit and functional tests are handled by Jasmine. To run tests from the command line, use: + +```shell +npm test +``` + +If you need to record new API calls, use: + +```shell +npm run test-record +``` + +You can also check if the examples are running correctly. + +For the Web: + +```shell +npm run serve +``` + +Then go to http://localhost:8080/example/web, choose a provider and open the console. + +For Node: + +```shell +npm run ts-node -- example/node/provider.ts +``` diff --git a/bower.json b/bower.json deleted file mode 100644 index 5d4b0ee..0000000 --- a/bower.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "name": "geocoder-js", - "version": "0.0.1", - "authors": [ - "Brandon Morrison " - ], - "main": "dist/geocoder.min.js", - "license": "MIT", - "private": true, - "ignore": [ - "**/.*", - "node_modules", - "bower_components", - "test", - "tests" - ], - "devDependencies": { - "jasmine": "~v1.3.1" - } -} diff --git a/couscous.yml b/couscous.yml new file mode 100644 index 0000000..ca0834c --- /dev/null +++ b/couscous.yml @@ -0,0 +1,54 @@ +title: GeocoderJS +subTitle: A universal JavaScript library for client-side geocoding applications with multiple built-in providers + +github: + user: geocoder-php + repo: geocoder-js + +menu: + sections: + main: + items: + readme: + text: 🌏️ Home + provider_usage: + text: 🔎️ Provider Usage + relativeUrl: docs/provider_usage.html + changelog: + text: 📃️ Changelog + relativeUrl: changelog.html + special_provider_usage: + name: Special Provider + items: + chain: + text: ⛓️ Chain + relativeUrl: docs/provider_usage/chain.html + provider_usage: + name: Provider + items: + nominatim: + text: 📌️ OpenStreetMap (Nominatim) + relativeUrl: docs/provider_usage/nominatim.html + opencage: + text: 📌️ OpenCage + relativeUrl: docs/provider_usage/opencage.html + googlemaps: + text: 📌️ Google Maps (Geocoding API) + relativeUrl: docs/provider_usage/googlemaps.html + mapbox: + text: 📌️ Mapbox + relativeUrl: docs/provider_usage/mapbox.html + mapquest: + text: 📌️ MapQuest + relativeUrl: docs/provider_usage/mapquest.html + bing: + text: 📌️ Bing Maps + relativeUrl: docs/provider_usage/bing.html + yandex: + text: 📌️ Yandex + relativeUrl: docs/provider_usage/yandex.html + geoplugin: + text: 📌️ GeoPlugin + relativeUrl: docs/provider_usage/geoplugin.html + +baseUrl: /geocoder-js/ diff --git a/dist/geocoder.js b/dist/geocoder.js deleted file mode 100644 index 34d26ae..0000000 --- a/dist/geocoder.js +++ /dev/null @@ -1,465 +0,0 @@ -if (function() { - "use strict"; - var a = {}; - a.version = "0.0.0", a.createGeocoder = function(b) { - var c = new a.ProviderFactory(); - return c.createProvider(b); - }; - var b = "object" == typeof window ? window : "object" == typeof exports ? exports : {}; - b.GeocoderJS = a; -}(), "function" == typeof define && define.amd && define(GeocoderJS), "undefined" == typeof GeocoderJS && "function" == typeof require) var GeocoderJS = require("../GeocoderJS.js"); - -if (function(a) { - "use strict"; - a.ProviderBase = function() {}, a.ProviderBase.prototype = { - geocode: function() {}, - geodecode: function() {}, - mapToGeocoded: function() {}, - executeRequest: function() {} - }; -}(GeocoderJS), "undefined" == typeof GeocoderJS && "function" == typeof require) var GeocoderJS = require("./GeocoderJS.js"); - -if (function(a) { - "use strict"; - a.Geocoded = function() {}, a.Geocoded.prototype = { - getCoordinates: function() { - return [ this.latitude, this.longitude ]; - }, - getLatitude: function() { - return this.latitude; - }, - getLongitude: function() { - return this.longitude; - }, - getBounds: function() {}, - getStreetNumber: function() { - return this.streetNumber; - }, - getStreetName: function() { - return this.streetName; - }, - getCity: function() { - return this.city; - }, - getZipcode: function() { - return this.postal_code; - }, - getCityDistrict: function() {}, - getCounty: function() {}, - getCountyCode: function() {}, - getRegion: function() { - return this.region; - } - }; -}(GeocoderJS), "undefined" == typeof GeocoderJS && "function" == typeof require) var GeocoderJS = require("./GeocoderJS.js"); - -if (function(a) { - "use strict"; - var b = { - type: "Feature", - properties: {}, - geometry: { - type: "Point", - coordinates: [] - } - }; - a.GeoJSONDumper = function() { - return { - dump: function(a) { - var c = b; - return c.geometry.coordinates = [ a.getLongitude(), a.getLatitude() ], c; - } - }; - }; -}(GeocoderJS), "undefined" == typeof GeocoderJS && "function" == typeof require) { - var GeocoderJS = require("../GeocoderJS.js"); - require("../ExternalURILoader.js"); -} - -if (function(a) { - "use strict"; - a.ProviderFactory = function() {}, a.ProviderFactory.prototype.createProvider = function(b) { - "string" == typeof b && (b = { - provider: b - }); - var c, d = new a.ExternalURILoader(); - switch (b.provider) { - case "google": - c = new a.GoogleAPIProvider(d, b); - break; - - case "mapquest": - c = new a.MapquestProvider(d, b); - break; - - case "openstreetmap": - c = new a.OpenStreetMapProvider(d, b); - break; - - case "bing": - c = new a.BingProvider(d, b); - break; - - case "yandex": - c = new a.YandexProvider(d, b); - } - return c; - }; -}(GeocoderJS), "undefined" == typeof GeocoderJS && "function" == typeof require) var GeocoderJS = require("../GeocoderJS.js"); - -if (function(a, b) { - "use strict"; - function c(a) { - var c = Date.now(), d = "jsonp" + Math.round(c + 1000001 * Math.random()); - return b[d] = function(c) { - a(c), delete b[d]; - }, d; - } - a.ExternalURILoader = function(a) { - this.options = {}, void 0 === a && (a = {}), this.setOptions(a); - }, a.ExternalURILoader.prototype.setOptions = function(a) { - var b = { - protocol: null, - host: null, - pathname: null - }; - for (var c in b) this.options[c] = void 0 !== a[c] ? a[c] : b[c]; - }, a.ExternalURILoader.prototype.executeRequest = function(b, d) { - function e(b, c) { - var d, e = require("url"), f = require(g.options.protocol), h = { - protocol: g.options.protocol, - host: g.options.host, - pathname: g.options.pathname, - query: b - }; - d = e.format(h), f.get(d, function(b) { - if (200 != b.statusCode) throw "Received HTTP status code " + b.statusCode + " when attempting geocoding request."; - b.data = "", b.setEncoding("utf8"), b.on("data", function(a) { - b.data += a; - }), b.on("end", function() { - if (!b.data || !b.data.length) throw "Received empty data when attempting geocoding request."; - var d = !1, e = 0, f = []; - try { - d = JSON.parse(b.data); - } catch (g) { - throw "Received invalid JSON data when attempting geocoding request."; - } - if (d && d.status) { - if ("OVER_QUERY_LIMIT" === d.status) throw "Exceeded daily quota when attempting geocoding request."; - if ("OK" === d.status && d.results) { - for (;e < d.results.length; e++) f.push(a.GoogleAPIProvider.prototype.mapToGeocoded(d.results[e])); - return c(f); - } - } - throw "Received unexpected JSON data when attempting geocoding request."; - }); - }).on("error", function(a) { - throw a; - }); - } - function f(a, b) { - var d, e = new XMLHttpRequest(), f = g.options.protocol + "://" + g.options.host + "/" + g.options.pathname + "?", h = []; - a.JSONPCallback && (d = a.JSONPCallback, delete a.JSONPCallback, a[d] = c(b)); - for (var i in a) a.hasOwnProperty(i) && h.push(i + "=" + a[i]); - if (f += h.join("&"), d) { - var j = document.createElement("script"); - j.src = f, document.getElementsByTagName("head")[0].appendChild(j); - } else e.onload = function() { - if (200 != this.status) return console.log("Received HTTP status code " + this.status + " when attempting geocoding request."), - b(null); - if (!this.responseText || !this.responseText.length) return console.log("Received empty data when attempting geocoding request."), - b(null); - var a = !1; - try { - a = JSON.parse(this.responseText); - } catch (c) { - return console.log("Received invalid JSON data when attempting geocoding request."), - b(null); - } - return a ? b(a) : (console.log("Received unexpected JSON data when attempting geocoding request."), - b(null)); - }, e.open("GET", f), e.send(); - } - var g = this; - if ("undefined" != typeof XMLHttpRequest) return f(b, d); - try { - { - require("url"); - } - return e(b, d); - } catch (h) {} - return d(null); - }; -}(GeocoderJS, window), "undefined" == typeof GeocoderJS && "function" == typeof require) { - var GeocoderJS = require("../GeocoderJS.js"); - require("../Geocoded.js"), require("../providers/ProviderBase.js"); -} - -if (function(a) { - "use strict"; - var b, c; - a.BingProvider = function(a, d) { - if (void 0 === a) throw "No external loader defined."; - this.externalLoader = a, d = d ? d : {}, b = d.useSSL ? d.useSSL : !1, c = d.apiKey ? d.apiKey : null, - c && (b = !0); - }, a.BingProvider.prototype = new a.ProviderBase(), a.BingProvider.prototype.constructor = a.BingProvider, - a.BingProvider.prototype.geocode = function(a, d) { - this.externalLoader.setOptions({ - protocol: b === !0 ? "https" : "http", - host: "dev.virtualearth.net", - pathname: "REST/v1/Locations/" + a - }); - var e = { - key: c, - JSONPCallback: "jsonp" - }; - this.executeRequest(e, d); - }, a.BingProvider.prototype.geodecode = function(a, d, e) { - this.externalLoader.setOptions({ - protocol: b === !0 ? "https" : "http", - host: "dev.virtualearth.net", - pathname: "REST/v1/Locations/" + a + "," + d - }); - var f = { - key: c, - JSONPCallback: "jsonp" - }; - this.executeRequest(f, e); - }, a.BingProvider.prototype.executeRequest = function(a, b) { - var c = this; - this.externalLoader.executeRequest(a, function(a) { - var d = []; - for (var e in a.resourceSets[0].resources) d.push(c.mapToGeocoded(a.resourceSets[0].resources[e])); - b(d); - }); - }, a.BingProvider.prototype.mapToGeocoded = function(b) { - var c = new a.Geocoded(); - return c.latitude = b.point.coordinates[0], c.longitude = b.point.coordinates[1], - c.streetName = b.address.addressLine, c.city = b.address.locality, c.region = b.address.adminDistrict, - c.postal_code = b.address.postalCode, c; - }; -}(GeocoderJS), "undefined" == typeof GeocoderJS && "function" == typeof require) { - var GeocoderJS = require("../GeocoderJS.js"); - require("../Geocoded.js"), require("../providers/ProviderBase.js"); -} - -if (function(a) { - "use strict"; - var b, c; - a.GoogleAPIProvider = function(a, d) { - if (void 0 === a) throw "No external loader defined."; - this.externalLoader = a, d = d ? d : {}, b = d.useSSL ? d.useSSL : !1, c = d.apiKey ? d.apiKey : null, - c && (b = !0); - }, a.GoogleAPIProvider.prototype = new a.ProviderBase(), a.GoogleAPIProvider.prototype.constructor = a.GoogleAPIProvider, - a.GoogleAPIProvider.prototype.geocode = function(a, d) { - this.externalLoader.setOptions({ - protocol: b === !0 ? "https" : "http", - host: "maps.googleapis.com", - pathname: "maps/api/geocode/json" - }); - var e = { - sensor: !1, - address: a - }; - c && (e.key = c), this.executeRequest(e, d); - }, a.GoogleAPIProvider.prototype.geodecode = function(a, d, e) { - this.externalLoader.setOptions({ - protocol: b ? "https" : "http", - host: "maps.googleapis.com", - pathname: "maps/api/geocode/json" - }); - var f = { - sensor: !1, - latlng: a + "," + d - }; - c && (f.key = c), this.executeRequest(f, e); - }, a.GoogleAPIProvider.prototype.executeRequest = function(a, b) { - var c = this; - this.externalLoader.executeRequest(a, function(a) { - var d = []; - for (var e in a.results) d.push(c.mapToGeocoded(a.results[e])); - b(d); - }); - }, a.GoogleAPIProvider.prototype.mapToGeocoded = function(b) { - var c = new a.Geocoded(); - c.latitude = b.geometry.location.lat, c.longitude = b.geometry.location.lng; - for (var d in b.address_components) for (var e in b.address_components[d].types) switch (b.address_components[d].types[e]) { - case "street_number": - c.streetNumber = b.address_components[d].long_name; - break; - - case "route": - c.streetName = b.address_components[d].long_name; - break; - - case "locality": - c.city = b.address_components[d].long_name; - break; - - case "administrative_area_level_1": - c.region = b.address_components[d].long_name; - break; - - case "postal_code": - c.postal_code = b.address_components[d].long_name; - } - return c; - }; -}(GeocoderJS), "undefined" == typeof GeocoderJS && "function" == typeof require) var GeocoderJS = require("../GeocoderJS.js"); - -if (function(a) { - "use strict"; - a.MapquestProvider = function(a, b) { - if (void 0 === a) throw "No external loader defined."; - this.externalLoader = a, "object" != typeof b && (b = {}); - var c = { - apiKey: "" - }; - for (var d in c) void 0 === b[d] && (b[d] = c[d]); - this.apiKey = b.apiKey; - }, a.MapquestProvider.prototype = new a.ProviderBase(), a.MapquestProvider.prototype.constructor = a.MapquestProvider, - a.MapquestProvider.prototype.geocode = function(a, b) { - this.externalLoader.setOptions({ - protocol: "http", - host: "www.mapquestapi.com", - pathname: "geocoding/v1/address" - }); - var c = { - key: this.apiKey, - outputFormat: "json", - location: encodeURIComponent(a), - JSONPCallback: "callback" - }; - this.executeRequest(c, b); - }, a.MapquestProvider.prototype.geodecode = function(a, b, c) { - this.externalLoader.setOptions({ - protocol: "http", - host: "www.mapquestapi.com", - pathname: "geocoding/v1/reverse" - }); - var d = { - key: this.apiKey, - outputFormat: "json", - JSONPCallback: "callback", - location: a + "," + b - }; - this.executeRequest(d, c); - }, a.MapquestProvider.prototype.mapToGeocoded = function(b) { - var c = new a.Geocoded(); - return c.latitude = b.latLng.lat, c.longitude = b.latLng.lng, c.city = b.adminArea5, - c.region = b.adminArea4, c.streetName = b.street, c.postal_code = b.postalCode, - c; - }, a.MapquestProvider.prototype.executeRequest = function(a, b) { - var c = this; - this.externalLoader.executeRequest(a, function(a) { - var d = []; - if (a.results[0].locations.length) for (var e in a.results[0].locations) d.push(c.mapToGeocoded(a.results[0].locations[e])); - b(d); - }); - }; -}(GeocoderJS), "undefined" == typeof GeocoderJS && "function" == typeof require) { - var GeocoderJS = require("../GeocoderJS.js"); - require("../Geocoded.js"), require("../ExternalURILoader.js"), require("../providers/ProviderBase.js"); -} - -if (function(a) { - "use strict"; - a.OpenStreetMapProvider = function(a) { - if (void 0 === a) throw "No external loader defined."; - this.externalLoader = a; - }, a.OpenStreetMapProvider.prototype = new a.ProviderBase(), a.OpenStreetMapProvider.prototype.constructor = a.OpenStreetMapProvider, - a.OpenStreetMapProvider.prototype.geocode = function(a, b) { - this.externalLoader.setOptions({ - protocol: "http", - host: "nominatim.openstreetmap.org", - pathname: "search" - }); - var c = { - format: "json", - q: a, - addressdetails: 1 - }; - this.executeRequest(c, b); - }, a.OpenStreetMapProvider.prototype.geodecode = function(a, b, c) { - this.externalLoader.setOptions({ - protocol: "http", - host: "nominatim.openstreetmap.org", - pathname: "reverse" - }); - var d = { - format: "json", - lat: a, - lon: b, - addressdetails: 1, - zoom: 18 - }; - this.executeRequest(d, c); - }, a.OpenStreetMapProvider.prototype.executeRequest = function(a, b) { - var c = this; - this.externalLoader.executeRequest(a, function(a) { - var d = []; - if (a.length) for (var e in a) d.push(c.mapToGeocoded(a[e])); else d.push(c.mapToGeocoded(a)); - b(d); - }); - }, a.OpenStreetMapProvider.prototype.mapToGeocoded = function(b) { - var c = new a.Geocoded(); - return c.latitude = 1 * b.lat, c.longitude = 1 * b.lon, c.streetNumber = void 0 !== b.address.house_number ? b.address.house_number : void 0, - c.streetName = b.address.road, c.city = b.address.city, c.region = b.address.state, - c.postal_code = b.address.postcode, c; - }; -}(GeocoderJS), "undefined" == typeof GeocoderJS && "function" == typeof require) { - var GeocoderJS = require("../GeocoderJS.js"); - require("../Geocoded.js"), require("../providers/ProviderBase.js"); -} - -!function(a) { - "use strict"; - var b; - a.YandexProvider = function(a, c) { - if (void 0 === a) throw "No external loader defined."; - this.externalLoader = a, c = c ? c : {}, b = c.useSSL ? c.useSSL : !1, this.lang = c.lang ? c.lang : "en-US"; - }, a.YandexProvider.prototype = new a.ProviderBase(), a.YandexProvider.prototype.constructor = a.YandexProvider, - a.YandexProvider.prototype.geocode = function(a, c) { - this.externalLoader.setOptions({ - protocol: b === !0 ? "https" : "http", - host: "geocode-maps.yandex.ru", - pathname: "1.x" - }); - var d = { - format: "json", - geocode: a, - JSONPCallback: "callback", - lang: this.lang - }; - this.executeRequest(d, c); - }, a.YandexProvider.prototype.geodecode = function(a, c, d) { - this.externalLoader.setOptions({ - protocol: b === !0 ? "https" : "http", - host: "geocode-maps.yandex.ru", - pathname: "1.x" - }); - var e = { - format: "json", - geocode: c + "," + a, - JSONPCallback: "callback", - lang: this.lang - }; - this.executeRequest(e, d); - }, a.YandexProvider.prototype.executeRequest = function(a, b) { - var c = this; - this.externalLoader.executeRequest(a, function(a) { - var d = []; - for (var e in a.response.GeoObjectCollection.featureMember) d.push(c.mapToGeocoded(a.response.GeoObjectCollection.featureMember[e].GeoObject)); - b(d); - }); - }, a.YandexProvider.prototype.mapToGeocoded = function(b) { - var c = new a.Geocoded(), d = b.Point.pos.split(" "); - if (c.latitude = d[1], c.longitude = d[0], b.metaDataProperty.GeocoderMetaData.AddressDetails.Country) { - var e = b.metaDataProperty.GeocoderMetaData.AddressDetails.Country; - e.AdministrativeArea && (e = e.AdministrativeArea, c.region = e.AdministrativeAreaName, - e.SubAdministrativeArea && (e = e.SubAdministrativeArea, e.Locality && (e = e.Locality, - c.city = e.LocalityName, e.Thoroughfare && (e = e.Thoroughfare, c.streetName = e.ThoroughfareName)))); - } - return c; - }; -}(GeocoderJS); \ No newline at end of file diff --git a/dist/geocoder.min.js b/dist/geocoder.min.js deleted file mode 100644 index 4b09bcb..0000000 --- a/dist/geocoder.min.js +++ /dev/null @@ -1 +0,0 @@ -if(function(){"use strict";var a={};a.version="0.0.0",a.createGeocoder=function(b){var c=new a.ProviderFactory;return c.createProvider(b)};var b="object"==typeof window?window:"object"==typeof exports?exports:{};b.GeocoderJS=a}(),"function"==typeof define&&define.amd&&define(GeocoderJS),"undefined"==typeof GeocoderJS&&"function"==typeof require)var GeocoderJS=require("../GeocoderJS.js");if(function(a){"use strict";a.ProviderBase=function(){},a.ProviderBase.prototype={geocode:function(){},geodecode:function(){},mapToGeocoded:function(){},executeRequest:function(){}}}(GeocoderJS),"undefined"==typeof GeocoderJS&&"function"==typeof require)var GeocoderJS=require("./GeocoderJS.js");if(function(a){"use strict";a.Geocoded=function(){},a.Geocoded.prototype={getCoordinates:function(){return[this.latitude,this.longitude]},getLatitude:function(){return this.latitude},getLongitude:function(){return this.longitude},getBounds:function(){},getStreetNumber:function(){return this.streetNumber},getStreetName:function(){return this.streetName},getCity:function(){return this.city},getZipcode:function(){return this.postal_code},getCityDistrict:function(){},getCounty:function(){},getCountyCode:function(){},getRegion:function(){return this.region}}}(GeocoderJS),"undefined"==typeof GeocoderJS&&"function"==typeof require)var GeocoderJS=require("./GeocoderJS.js");if(function(a){"use strict";var b={type:"Feature",properties:{},geometry:{type:"Point",coordinates:[]}};a.GeoJSONDumper=function(){return{dump:function(a){var c=b;return c.geometry.coordinates=[a.getLongitude(),a.getLatitude()],c}}}}(GeocoderJS),"undefined"==typeof GeocoderJS&&"function"==typeof require){var GeocoderJS=require("../GeocoderJS.js");require("../ExternalURILoader.js")}if(function(a){"use strict";a.ProviderFactory=function(){},a.ProviderFactory.prototype.createProvider=function(b){"string"==typeof b&&(b={provider:b});var c,d=new a.ExternalURILoader;switch(b.provider){case"google":c=new a.GoogleAPIProvider(d,b);break;case"mapquest":c=new a.MapquestProvider(d,b);break;case"openstreetmap":c=new a.OpenStreetMapProvider(d,b);break;case"bing":c=new a.BingProvider(d,b);break;case"yandex":c=new a.YandexProvider(d,b)}return c}}(GeocoderJS),"undefined"==typeof GeocoderJS&&"function"==typeof require)var GeocoderJS=require("../GeocoderJS.js");if(function(a,b){"use strict";function c(a){var c=Date.now(),d="jsonp"+Math.round(c+1000001*Math.random());return b[d]=function(c){a(c),delete b[d]},d}a.ExternalURILoader=function(a){this.options={},void 0===a&&(a={}),this.setOptions(a)},a.ExternalURILoader.prototype.setOptions=function(a){var b={protocol:null,host:null,pathname:null};for(var c in b)this.options[c]=void 0!==a[c]?a[c]:b[c]},a.ExternalURILoader.prototype.executeRequest=function(b,d){function e(b,c){var d,e=require("url"),f=require(g.options.protocol),h={protocol:g.options.protocol,host:g.options.host,pathname:g.options.pathname,query:b};d=e.format(h),f.get(d,function(b){if(200!=b.statusCode)throw"Received HTTP status code "+b.statusCode+" when attempting geocoding request.";b.data="",b.setEncoding("utf8"),b.on("data",function(a){b.data+=a}),b.on("end",function(){if(!b.data||!b.data.length)throw"Received empty data when attempting geocoding request.";var d=!1,e=0,f=[];try{d=JSON.parse(b.data)}catch(g){throw"Received invalid JSON data when attempting geocoding request."}if(d&&d.status){if("OVER_QUERY_LIMIT"===d.status)throw"Exceeded daily quota when attempting geocoding request.";if("OK"===d.status&&d.results){for(;e + + + Option, Method or Result + Parameter + Description + Provider + + + + + option + countryCodes + Restrict the results to one or more countries + Google Maps, Mapbox, OpenCage + + + option + method + HTTP method to use when executing the query + MapQuest + + + option + clientId + To use a client ID instead of the API key + Google Maps + + + option + secret + URL signing secret to use to digitally sign the request + Google Maps + + + option + geocodingMode + Geocoding mode to use + Mapbox + + + option + openDomain + To use the Open Geocoding API + MapQuest + + + option + userAgent + User-Agent identifying your application + Nominatim + + + option + referer + Referer for the request + Nominatim + + + option + host + Host to use + Nominatim + + + geocode + countryCodes + Restrict the results to one or more countries + Google Maps, Mapbox, Nominatim, OpenCage + + + geocode + bounded + Restrict the results to items within the bounds + Nominatim, Yandex + + + geocode + proximity + Bias or filter the response to favor results that are closer to the specified location + Mapbox, OpenCage, Yandex + + + geocode + fuzzyMatch + Should approximate the request terms or do an exact matching + Mapbox + + + geocode + span + Bias or filter the response to favor results that are closer to the specified span + Yandex + + + geocode + minPrecision + Only results with at least this precision will be returned + OpenCage + + + geocode + components + Filter the results by components + Google Maps + + + geocode + locationTypes + Filter the results to a subset of location types + Mapbox + + + geocode + excludePlaceIds + For excluding some locations from the results + Nominatim + + + geocode + skip + Number of items to skip in the response + Yandex + + + geocode + noRecord + Ask for the query to not be logged + OpenCage + + + geocode + channel + Channel to use for the request + Google Maps + + + geodecode + countryCodes + Restrict the results to one or more countries + Mapbox, OpenCage + + + geodecode + minPrecision + Only results with at least this precision will be returned + OpenCage + + + geodecode + locationTypes + Filter the results by location types + Google Maps, Mapbox, Yandex + + + geodecode + resultTypes + Filter the results by address types + Google Maps + + + geodecode + zoom + Level of details required for the address + Npminatim + + + geodecode + reverseMode + How results are sorted + Mapbox + + + geodecode + skip + Number of items to skip in the response + Yandex + + + geodecode + noRecord + Ask for the query to not be logged + OpenCage + + + geodecode + channel + Channel to use for the request + Google Maps + + + geocoded + attribution + Credit information + Bing, GeoPlugin, MapQuest, Nominatim + + + geocoded + precision + Quality of the geocoding result + Bing, MapQuest, Yandex + + + geocoded + precisionCode + Quality code of the geocoding result + MapQuest + + + geocoded + callingCode + International telephone calling code for the country of the result + OpenCage + + + geocoded + placeId + Unique identifier for the location + Google Maps + + + geocoded + osmId + Reference to the OpenStreetMap object + Nominatim + + + geocoded + osmType + Reference to the OpenStreetMap object + Nominatim + + + geocoded + mapUrl + URL to a static map thumbnail image for the location being geocoded + MapQuest + + + geocoded + partialMatch + Indicate the geocoder did not return an exact match + Google Maps + + + geocoded + resultType + Result types + Google Maps, Mapbox + + + geocoded + locationType + Type representing the precision of the result + Google Maps, Yandex + + + geocoded + streetAddress + Street number with the street name + Google Maps + + + geocoded + displayName + Full comma-separated address + Google Maps + + + geocoded + category + Category of the location + Nominatim + + + geocoded + type + Type of the location + Nominatim + + + geocoded + subLocalityLevels + Levels for the sublocality + Google Maps + + + geocoded + flag + Emoji flag of the country of the result + OpenCage + + + geocoded + mgrs + Military Grid Reference System code for the center point of the result + OpenCage + + + geocoded + maidenhead + Maidenhead location reference for the center point of the result + OpenCage + + + geocoded + geohash + Geohash for the center point of the result + OpenCage + + + geocoded + what3words + Key words whose value is a 3 words address (3wa) + OpenCage + + + diff --git a/docs/provider_usage/bing.md b/docs/provider_usage/bing.md new file mode 100644 index 0000000..0b9e320 --- /dev/null +++ b/docs/provider_usage/bing.md @@ -0,0 +1,6 @@ +# Bing Usage + +## `Geocoded` properties + +- `attribution`: copyright notice +- `precision`: level of confidence that the geocoded location result is a match diff --git a/docs/provider_usage/chain.md b/docs/provider_usage/chain.md new file mode 100644 index 0000000..4a0202b --- /dev/null +++ b/docs/provider_usage/chain.md @@ -0,0 +1,7 @@ +# Special Chain Provider Usage + +## Options + +- `providers`: the list of providers to iterate over +- `parallelize`: if `true`, the requests to the providers will be sent at the same time (but the responses are checked in the order) +- `first`: if `true`, the result will be the one of the fastest provider to respond (the order does not matter) diff --git a/docs/provider_usage/geoplugin.md b/docs/provider_usage/geoplugin.md new file mode 100644 index 0000000..68be0aa --- /dev/null +++ b/docs/provider_usage/geoplugin.md @@ -0,0 +1,5 @@ +# GeoPlugin Usage + +## `Geocoded` properties + +- `attribution`: credit information diff --git a/docs/provider_usage/googlemaps.md b/docs/provider_usage/googlemaps.md new file mode 100644 index 0000000..c2258fc --- /dev/null +++ b/docs/provider_usage/googlemaps.md @@ -0,0 +1,29 @@ +# Google Maps (Geocoding API) Usage + +## Options + +- `countryCodes`: to restrict the results to one or more countries (ccTLD country codes) +- `clientId`: to use a client ID instead of the API key (Premium only) +- `secret`: the URL signing secret to use to digitally sign the request (Premium only and only server-side) + +## `geocode` parameters + +- `countryCodes`: to restrict the results to one or more countries (ccTLD country codes) +- `components` (array of objects with `name` and `value` properties): to filter the results by components +- `channel`: channel to use for this request (Premium only) + +## `geodecode` parameters + +- `resultTypes`: to filter the results by address types +- `locationTypes` (possible values: "ROOFTOP", "RANGE_INTERPOLATED", "GEOMETRIC_CENTER", "APPROXIMATE"): to filter the results by location types +- `channel`: channel to use for this request (Premium only) + +## `Geocoded` properties + +- `placeId`: a unique identifier used with other Google APIs +- `partialMatch`: to indicate the geocoder did not return an exact match +- `resultType`: an array of result types +- `locationType`: a type representing the precision of the result +- `streetAddress`: the street number with the street name +- `intersection`, `political`, `colloquialArea`, `ward`, `neighborhood`, `premise`, `subpremise`, `naturalFeature`, `airport`, `park`, `pointOfInterest`, `establishment`, `postalCodeSuffix`: additional address components +- `subLocalityLevels`: levels for the sublocality diff --git a/docs/provider_usage/mapbox.md b/docs/provider_usage/mapbox.md new file mode 100644 index 0000000..053ea81 --- /dev/null +++ b/docs/provider_usage/mapbox.md @@ -0,0 +1,23 @@ +# Mapbox Usage + +## Options + +- `geocodingMode` (default: "mapbox.places"): the geocoding mode to use +- `countryCodes`: to restrict the results to one or more countries (ISO 3166 alpha 2 country codes) + +## `geocode` parameters + +- `countryCodes`: to restrict the results to one or more countries (ISO 3166 alpha 2 country codes) +- `proximity` (object with `latitude` and `longitude` keys): to bias the response to favor results that are closer to the specified location +- `fuzzyMatch`: to approximate the request terms or do an exact matching +- `locationTypes` (possible values: "country", "region", "postcode", "district", "place", "locality", "neighborhood", "address", "poi"): to filter the results to a subset of location types + +## `geodecode` parameters + +- `countryCodes`: to restrict the results to one or more countries (ISO 3166 alpha 2 country codes) +- `reverseMode` ("distance" or "score", default: "distance"): how results are sorted +- `locationTypes` (possible values: "country", "region", "postcode", "district", "place", "locality", "neighborhood", "address", "poi"): to filter the results to a subset of location types + +## `Geocoded` properties + +- `resultType`: an array of result types diff --git a/docs/provider_usage/mapquest.md b/docs/provider_usage/mapquest.md new file mode 100644 index 0000000..a6ec5f9 --- /dev/null +++ b/docs/provider_usage/mapquest.md @@ -0,0 +1,17 @@ +# MapQuest Usage + +## Options + +- `method` ("GET" or "POST", default: "GET"): the HTTP method to use when executing the query +- `openDomain` (default: false): boolean to use the Open Geocoding API (relies solely on data contributed to OpenStreetMap) + +## `geocode` parameters + +- `location`: use this parameter to use an advanced location. You can specify it using the MapQuest format or the Geocoded class or format. + +## `Geocoded` properties + +- `precision`: the quality of the geocoding result +- `precisionCode`: the quality code of the geocoding result +- `mapUrl`: the URL to a static map thumbnail image for the location being geocoded +- `attribution`: MapQuest copyright information diff --git a/docs/provider_usage/nominatim.md b/docs/provider_usage/nominatim.md new file mode 100644 index 0000000..57b2699 --- /dev/null +++ b/docs/provider_usage/nominatim.md @@ -0,0 +1,24 @@ +# OpenStreetMap (Nominatim) Usage + +## Options + +- `userAgent` (required if default host): a User-Agent identifying your application is needed to use Nominatim (see https://operations.osmfoundation.org/policies/nominatim/) +- `referer`: if you want to set a Referer as well +- `host` (default: "nominatim.openstreetmap.org"): to use another host + +## `geocode` parameters + +- `countryCodes`: to restrict the results to one or more countries (ISO 3166 alpha 2 country codes) +- `excludePlaceIds`: for excluding some OpenStreetMap objects from the results +- `bounded` (only if `bounds` is used): boolean to restrict the results to items within the bounds + +## `geodecode` parameters + +- `zoom` (default: 18, from 0 to 18): the level of details required for the address + +## `Geocoded` properties + +- `displayName`: full comma-separated address +- `osmId`, `osmType`: reference to the OpenStreetMap object +- `category`, `type`: key and value of the main OpenStreetMap tag +- `attribution`: OpenStreetMap licensing information diff --git a/docs/provider_usage/opencage.md b/docs/provider_usage/opencage.md new file mode 100644 index 0000000..e5f96c6 --- /dev/null +++ b/docs/provider_usage/opencage.md @@ -0,0 +1,27 @@ +# OpenCage Usage + +## Options + +- `countryCodes`: to restrict the results to one or more countries (ISO 3166 alpha 2 country codes) + +## `geocode` parameters + +- `countryCodes`: to restrict the results to one or more countries (ISO 3166 alpha 2 country codes) +- `proximity` (object with `latitude` and `longitude` keys): to provide a hint to bias results in favour of those closer to the specified location +- `minPrecision` (from 1 to 10): only results with at least this precision will be returned +- `noRecord`: boolean to ask for the query to not be logged + +## `geodecode` parameters + +- `countryCodes`: to restrict the results to one or more countries (ISO 3166 alpha 2 country codes) +- `minPrecision` (from 1 to 10): only results with at least this precision will be returned +- `noRecord`: boolean to ask for the query to not be logged + +## `Geocoded` properties + +- `callingCode`: the international telephone calling code for the country of the result +- `flag`: emoji flag of the country of the result +- `mgrs`: Military Grid Reference System code for the center point of the result +- `maidenhead`: Maidenhead location reference for the center point of the result +- `geohash`: Geohash for the center point of the result +- `what3words`: key words whose value is a 3 words address (3wa) diff --git a/docs/provider_usage/yandex.md b/docs/provider_usage/yandex.md new file mode 100644 index 0000000..23577b1 --- /dev/null +++ b/docs/provider_usage/yandex.md @@ -0,0 +1,18 @@ +# Yandex Usage + +## `geocode` parameters + +- `bounded` (only if `bounds` or `proximity` is used): boolean to restrict the results to items within the bounds +- `proximity` (object with `latitude` and `longitude` keys): center of the search area +- `span` (object with `spanLatitude` and `spanLongitude` keys): span of the search area +- `skip`: the number of items to skip in the response + +## `geodecode` parameters + +- `locationTypes` (possible values: "house", "street", "metro", "district", "locality", ...): to filter the results by location types +- `skip`: the number of items to skip in the response + +## `Geocoded` properties + +- `locationType`: the type of location +- `precision`: the quality of the geocoding result diff --git a/example/bing.html b/example/bing.html deleted file mode 100644 index cbf3e57..0000000 --- a/example/bing.html +++ /dev/null @@ -1,20 +0,0 @@ - - - - Geocoder.js: Mapquest Simple Example - - - - - - - \ No newline at end of file diff --git a/example/google.html b/example/google.html deleted file mode 100644 index 6dedadd..0000000 --- a/example/google.html +++ /dev/null @@ -1,20 +0,0 @@ - - - - Geocoder.js: Google Simple Example - - - - - - - \ No newline at end of file diff --git a/example/mapquest.html b/example/mapquest.html deleted file mode 100644 index f480a84..0000000 --- a/example/mapquest.html +++ /dev/null @@ -1,20 +0,0 @@ - - - - Geocoder.js: Mapquest Simple Example - - - - - - - \ No newline at end of file diff --git a/example/node/bing.ts b/example/node/bing.ts new file mode 100644 index 0000000..fb401b3 --- /dev/null +++ b/example/node/bing.ts @@ -0,0 +1,16 @@ +/* eslint-disable no-console */ +import GeocoderJS from "../../dist/GeocoderJS"; +import GeoJsonDumper from "../../dist/GeoJsonDumper"; + +const bingGeocoder = GeocoderJS.createGeocoder({ + provider: "bing", + apiKey: "As11PsBXYvAoGEXmz59ZWl93T8_OACdXi2QnRKWMRIUK6hzOXgN3BcZHnbKyPZYo", +}); +bingGeocoder.geocode("1600 Pennsylvania Ave NW, Washington, DC", (result) => { + console.log(result); + console.log("GeoJSON:", GeoJsonDumper.dump(result[0])); +}); + +bingGeocoder.geodecode("44.915", "-93.21", (result) => { + console.log(result); +}); diff --git a/example/node/chain.ts b/example/node/chain.ts new file mode 100644 index 0000000..7defc67 --- /dev/null +++ b/example/node/chain.ts @@ -0,0 +1,21 @@ +/* eslint-disable no-console */ +import GeocoderJS from "../../dist/GeocoderJS"; + +const yandexGeocoder = GeocoderJS.createGeocoder("yandex"); +const openStreetMapGeocoder = GeocoderJS.createGeocoder({ + provider: "openstreetmap", + userAgent: "GeocoderJS Example", +}); +const chainGeocoder = GeocoderJS.createGeocoder({ + provider: "chain", + providers: [yandexGeocoder, openStreetMapGeocoder], + parallelize: true, +}); + +chainGeocoder.geocode("1600 Pennsylvania Ave, Washington, DC", (result) => { + console.log(result); +}); + +chainGeocoder.geodecode("44.915", "-93.21", (result) => { + console.log(result); +}); diff --git a/example/node/geoplugin.ts b/example/node/geoplugin.ts new file mode 100644 index 0000000..45cdb33 --- /dev/null +++ b/example/node/geoplugin.ts @@ -0,0 +1,9 @@ +/* eslint-disable no-console */ +import GeocoderJS from "../../dist/GeocoderJS"; + +const geoPluginGeocoder = GeocoderJS.createGeocoder({ + provider: "geoplugin", +}); +geoPluginGeocoder.geocode("190.226.155.134", (result) => { + console.log(result); +}); diff --git a/example/node/googlemaps.ts b/example/node/googlemaps.ts new file mode 100644 index 0000000..88216d7 --- /dev/null +++ b/example/node/googlemaps.ts @@ -0,0 +1,14 @@ +/* eslint-disable no-console */ +import GeocoderJS from "../../dist/GeocoderJS"; + +const googleGeocoder = GeocoderJS.createGeocoder({ + provider: "googlemaps", + useSsl: true, +}); +googleGeocoder.geocode("1600 Pennsylvania Ave, Washington, DC", (result) => { + console.log(result); +}); + +googleGeocoder.geodecode("44.915", "-93.21", (result) => { + console.log(result); +}); diff --git a/example/node/mapbox.ts b/example/node/mapbox.ts new file mode 100644 index 0000000..95afac2 --- /dev/null +++ b/example/node/mapbox.ts @@ -0,0 +1,11 @@ +/* eslint-disable no-console */ +import GeocoderJS from "../../dist/GeocoderJS"; + +const mapboxGeocoder = GeocoderJS.createGeocoder({ provider: "mapbox" }); +mapboxGeocoder.geocode("1600 Pennsylvania Ave NW, Washington, DC", (result) => { + console.log(result); +}); + +mapboxGeocoder.geodecode("44.915", "-93.21", (result) => { + console.log(result); +}); diff --git a/example/node/mapquest.ts b/example/node/mapquest.ts new file mode 100644 index 0000000..6bfa442 --- /dev/null +++ b/example/node/mapquest.ts @@ -0,0 +1,61 @@ +/* eslint-disable no-console */ +import GeocoderJS from "../../dist/GeocoderJS"; +import Geocoded from "../../dist/Geocoded"; +import { MapQuestGeocodeQuery, MapQuestLocation } from "../../dist/provider"; + +let mapQuestGeocoder = GeocoderJS.createGeocoder({ + provider: "mapquest", + apiKey: "Fmjtd|luurnu6al1,bg=o5-9wbg94", +}); +mapQuestGeocoder.geocode( + "1600 Pennsylvania Ave NW, Washington, DC", + (result) => { + console.log(result); + } +); + +mapQuestGeocoder.geodecode("44.915", "-93.21", (result) => { + console.log(result); +}); + +mapQuestGeocoder.geocode( + MapQuestGeocodeQuery.create({ + location: Geocoded.create({ + streetNumber: 1600, + streetName: "Pennsylvania Ave NW", + locality: "Washington", + }), + }), + (result) => { + console.log(result); + } +); + +mapQuestGeocoder = GeocoderJS.createGeocoder({ + provider: "mapquest", + apiKey: "Fmjtd|luurnu6al1,bg=o5-9wbg94", + method: "POST", + openDomain: true, +}); +mapQuestGeocoder.geocode( + "1600 Pennsylvania Ave NW, Washington, DC", + (result) => { + console.log(result); + } +); + +mapQuestGeocoder.geodecode("44.915", "-93.21", (result) => { + console.log(result); +}); + +mapQuestGeocoder.geocode( + MapQuestGeocodeQuery.create({ + location: MapQuestLocation.create({ + street: "1600 Pennsylvania Ave NW", + city: "Washington", + }), + }), + (result) => { + console.log(result); + } +); diff --git a/example/node/opencage.ts b/example/node/opencage.ts new file mode 100644 index 0000000..7e5c842 --- /dev/null +++ b/example/node/opencage.ts @@ -0,0 +1,16 @@ +/* eslint-disable no-console */ +import GeocoderJS from "../../dist/GeocoderJS"; + +const openCageGeocoder = GeocoderJS.createGeocoder({ + provider: "opencage", +}); +openCageGeocoder.geocode( + "1600 Pennsylvania Ave NW, Washington, DC", + (result) => { + console.log(result); + } +); + +openCageGeocoder.geodecode("44.915", "-93.21", (result) => { + console.log(result); +}); diff --git a/example/node/openstreetmap.ts b/example/node/openstreetmap.ts new file mode 100644 index 0000000..47287d9 --- /dev/null +++ b/example/node/openstreetmap.ts @@ -0,0 +1,17 @@ +/* eslint-disable no-console */ +import GeocoderJS from "../../dist/GeocoderJS"; + +const openStreetMapGeocoder = GeocoderJS.createGeocoder({ + provider: "openstreetmap", + userAgent: "GeocoderJS Example", +}); +openStreetMapGeocoder.geocode( + "1600 Pennsylvania Ave NW, Washington, DC", + (result) => { + console.log(result); + } +); + +openStreetMapGeocoder.geodecode("44.915", "-93.21", (result) => { + console.log(result); +}); diff --git a/example/node/yandex.ts b/example/node/yandex.ts new file mode 100644 index 0000000..a699d7f --- /dev/null +++ b/example/node/yandex.ts @@ -0,0 +1,17 @@ +/* eslint-disable no-console */ +import GeocoderJS from "../../dist/GeocoderJS"; + +const yandexGeocoder = GeocoderJS.createGeocoder("yandex"); +yandexGeocoder.geocode( + { text: "1600 Pennsylvania Ave, Washington, DC", locale: "en-US" }, + (result) => { + console.log(result); + } +); + +yandexGeocoder.geodecode( + { coordinates: { latitude: "44.915", longitude: "-93.21" }, locale: "en-US" }, + (result) => { + console.log(result); + } +); diff --git a/example/openstreetmap.html b/example/openstreetmap.html deleted file mode 100644 index 6255c85..0000000 --- a/example/openstreetmap.html +++ /dev/null @@ -1,20 +0,0 @@ - - - - Geocoder.js: OpenStreetMap Example - - - - - - - \ No newline at end of file diff --git a/example/web/bing.html b/example/web/bing.html new file mode 100644 index 0000000..907dcfb --- /dev/null +++ b/example/web/bing.html @@ -0,0 +1,21 @@ + + + + Geocoder.js: Bing Example + + + + + + + \ No newline at end of file diff --git a/example/web/chain.html b/example/web/chain.html new file mode 100644 index 0000000..bc0901f --- /dev/null +++ b/example/web/chain.html @@ -0,0 +1,22 @@ + + + + Geocoder.js: Chain Example + + + + + + + \ No newline at end of file diff --git a/example/web/geoplugin.html b/example/web/geoplugin.html new file mode 100644 index 0000000..860e27d --- /dev/null +++ b/example/web/geoplugin.html @@ -0,0 +1,16 @@ + + + + Geocoder.js: GeoPlugin Example + + + + + + + \ No newline at end of file diff --git a/example/web/googlemaps.html b/example/web/googlemaps.html new file mode 100644 index 0000000..7860929 --- /dev/null +++ b/example/web/googlemaps.html @@ -0,0 +1,20 @@ + + + + Geocoder.js: Google Maps Example + + + + + + + \ No newline at end of file diff --git a/example/web/mapbox.html b/example/web/mapbox.html new file mode 100644 index 0000000..56b34c3 --- /dev/null +++ b/example/web/mapbox.html @@ -0,0 +1,20 @@ + + + + Geocoder.js: Mapbox Example + + + + + + + \ No newline at end of file diff --git a/example/web/mapquest.html b/example/web/mapquest.html new file mode 100644 index 0000000..729f2b5 --- /dev/null +++ b/example/web/mapquest.html @@ -0,0 +1,49 @@ + + + + Geocoder.js: Mapquest Example + + + + + + + \ No newline at end of file diff --git a/example/web/opencage.html b/example/web/opencage.html new file mode 100644 index 0000000..8776e25 --- /dev/null +++ b/example/web/opencage.html @@ -0,0 +1,20 @@ + + + + Geocoder.js: OpenCage Example + + + + + + + \ No newline at end of file diff --git a/example/web/openstreetmap.html b/example/web/openstreetmap.html new file mode 100644 index 0000000..7b29bc4 --- /dev/null +++ b/example/web/openstreetmap.html @@ -0,0 +1,20 @@ + + + + Geocoder.js: Nominatim Example + + + + + + + \ No newline at end of file diff --git a/example/web/yandex.html b/example/web/yandex.html new file mode 100644 index 0000000..b72b393 --- /dev/null +++ b/example/web/yandex.html @@ -0,0 +1,20 @@ + + + + Geocoder.js: Yandex Example + + + + + + + \ No newline at end of file diff --git a/example/yandex.html b/example/yandex.html deleted file mode 100644 index ca040ef..0000000 --- a/example/yandex.html +++ /dev/null @@ -1,20 +0,0 @@ - - - - Geocoder.js: Yandex Simple Example - - - - - - - \ No newline at end of file diff --git a/gruntfile.js b/gruntfile.js deleted file mode 100644 index 994332f..0000000 --- a/gruntfile.js +++ /dev/null @@ -1,71 +0,0 @@ -module.exports = function (grunt) { - 'use strict'; - - var srcFiles = [ - 'src/GeocoderJS.js', - 'src/providers/ProviderBase.js', - 'src/Geocoded.js', - 'src/GeoJSONDumper.js', - 'src/GeocoderProviderFactory.js', - 'src/ExternalURILoader.js', - 'src/providers/*.js' - ]; - - grunt.initConfig({ - uglify: { - develop: { - src: srcFiles, - dest: 'dist/geocoder.js', - options: { - beautify: true - } - }, - production: { - src: srcFiles, - dest: 'dist/geocoder.min.js', - options: {} - } - }, - jasmine: { - src: srcFiles, - options: { - specs: ['spec/*.js', 'spec/providers/*.js'], - helpers : 'spec/helpers/*.js', - keepRunner: true, - outfile: 'spec/runner.html', - } - }, - jshint: { - all: [ - 'Gruntfile.js', - 'src/*.js', - 'spec/*.js' - ] - }, - complexity: { - generic: { - src: ['src/**/*.js'], - options: { - cyclomatic: [10, 30, 50], // or optionally a single value, like 3 - halstead: [25, 30, 40], // or optionally a single value, like 8 - maintainability: 100 - } - } - }, - watch: { - files: ['src/**/*.js'], - tasks: ['default'] - } - }); - - grunt.loadNpmTasks('grunt-contrib-watch'); - grunt.loadNpmTasks('grunt-contrib-uglify'); - grunt.loadNpmTasks('grunt-contrib-jasmine'); - grunt.loadNpmTasks('grunt-contrib-jshint'); - grunt.loadNpmTasks('grunt-complexity'); - - grunt.registerTask('test', ['jshint', 'complexity', 'jasmine']); - grunt.registerTask('ci', ['jshint', 'jasmine']); - grunt.registerTask('build', ['uglify']); - grunt.registerTask('default', ['test', 'build']); -}; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..0a20930 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,6081 @@ +{ + "name": "geocoder-js", + "version": "0.8.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@babel/code-frame": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", + "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", + "dev": true, + "requires": { + "@babel/highlight": "^7.10.4" + } + }, + "@babel/core": { + "version": "7.12.3", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.3.tgz", + "integrity": "sha512-0qXcZYKZp3/6N2jKYVxZv0aNCsxTSVCiK72DTiTYZAu7sjg73W0/aynWjMbiGd87EQL4WyA8reiJVh92AVla9g==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/generator": "^7.12.1", + "@babel/helper-module-transforms": "^7.12.1", + "@babel/helpers": "^7.12.1", + "@babel/parser": "^7.12.3", + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.12.1", + "@babel/types": "^7.12.1", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.1", + "json5": "^2.1.2", + "lodash": "^4.17.19", + "resolve": "^1.3.2", + "semver": "^5.4.1", + "source-map": "^0.5.0" + }, + "dependencies": { + "json5": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", + "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "@babel/generator": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.1.tgz", + "integrity": "sha512-DB+6rafIdc9o72Yc3/Ph5h+6hUjeOp66pF0naQBgUFFuPqzQwIlPTm3xZR7YNvduIMtkDIj2t21LSQwnbCrXvg==", + "dev": true, + "requires": { + "@babel/types": "^7.12.1", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + } + }, + "@babel/helper-function-name": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz", + "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.10.4", + "@babel/template": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz", + "integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==", + "dev": true, + "requires": { + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.1.tgz", + "integrity": "sha512-k0CIe3tXUKTRSoEx1LQEPFU9vRQfqHtl+kf8eNnDqb4AUJEy5pz6aIiog+YWtVm2jpggjS1laH68bPsR+KWWPQ==", + "dev": true, + "requires": { + "@babel/types": "^7.12.1" + } + }, + "@babel/helper-module-imports": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.1.tgz", + "integrity": "sha512-ZeC1TlMSvikvJNy1v/wPIazCu3NdOwgYZLIkmIyAsGhqkNpiDoQQRmaCK8YP4Pq3GPTLPV9WXaPCJKvx06JxKA==", + "dev": true, + "requires": { + "@babel/types": "^7.12.1" + } + }, + "@babel/helper-module-transforms": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.12.1.tgz", + "integrity": "sha512-QQzehgFAZ2bbISiCpmVGfiGux8YVFXQ0abBic2Envhej22DVXV9nCFaS5hIQbkyo1AdGb+gNME2TSh3hYJVV/w==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.12.1", + "@babel/helper-replace-supers": "^7.12.1", + "@babel/helper-simple-access": "^7.12.1", + "@babel/helper-split-export-declaration": "^7.11.0", + "@babel/helper-validator-identifier": "^7.10.4", + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.12.1", + "@babel/types": "^7.12.1", + "lodash": "^4.17.19" + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz", + "integrity": "sha512-n3UGKY4VXwXThEiKrgRAoVPBMqeoPgHVqiHZOanAJCG9nQUL2pLRQirUzl0ioKclHGpGqRgIOkgcIJaIWLpygg==", + "dev": true, + "requires": { + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-replace-supers": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.1.tgz", + "integrity": "sha512-zJjTvtNJnCFsCXVi5rUInstLd/EIVNmIKA1Q9ynESmMBWPWd+7sdR+G4/wdu+Mppfep0XLyG2m7EBPvjCeFyrw==", + "dev": true, + "requires": { + "@babel/helper-member-expression-to-functions": "^7.12.1", + "@babel/helper-optimise-call-expression": "^7.10.4", + "@babel/traverse": "^7.12.1", + "@babel/types": "^7.12.1" + } + }, + "@babel/helper-simple-access": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.12.1.tgz", + "integrity": "sha512-OxBp7pMrjVewSSC8fXDFrHrBcJATOOFssZwv16F3/6Xtc138GHybBfPbm9kfiqQHKhYQrlamWILwlDCeyMFEaA==", + "dev": true, + "requires": { + "@babel/types": "^7.12.1" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz", + "integrity": "sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==", + "dev": true, + "requires": { + "@babel/types": "^7.11.0" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", + "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", + "dev": true + }, + "@babel/helpers": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.12.1.tgz", + "integrity": "sha512-9JoDSBGoWtmbay98efmT2+mySkwjzeFeAL9BuWNoVQpkPFQF8SIIFUfY5os9u8wVzglzoiPRSW7cuJmBDUt43g==", + "dev": true, + "requires": { + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.12.1", + "@babel/types": "^7.12.1" + } + }, + "@babel/highlight": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", + "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@babel/parser": { + "version": "7.12.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.3.tgz", + "integrity": "sha512-kFsOS0IbsuhO5ojF8Hc8z/8vEIOkylVBrjiZUbLTE3XFe0Qi+uu6HjzQixkFaqr0ZPAMZcBVxEwmsnsLPZ2Xsw==", + "dev": true + }, + "@babel/runtime": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.12.1.tgz", + "integrity": "sha512-J5AIf3vPj3UwXaAzb5j1xM4WAQDX3EMgemF8rjCP3SoW09LfRKAXQKt6CoVYl230P6iWdRcBbnLDDdnqWxZSCA==", + "dev": true, + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "@babel/template": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz", + "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/parser": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/traverse": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.1.tgz", + "integrity": "sha512-MA3WPoRt1ZHo2ZmoGKNqi20YnPt0B1S0GTZEPhhd+hw2KGUzBlHuVunj6K4sNuK+reEvyiPwtp0cpaqLzJDmAw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/generator": "^7.12.1", + "@babel/helper-function-name": "^7.10.4", + "@babel/helper-split-export-declaration": "^7.11.0", + "@babel/parser": "^7.12.1", + "@babel/types": "^7.12.1", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.19" + }, + "dependencies": { + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + } + } + }, + "@babel/types": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.1.tgz", + "integrity": "sha512-BzSY3NJBKM4kyatSOWh3D/JJ2O3CVzBybHWxtgxnggaxEuaSTTDqeiSb/xk9lrkw2Tbqyivw5ZU4rT+EfznQsA==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + }, + "@commitlint/cli": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-11.0.0.tgz", + "integrity": "sha512-YWZWg1DuqqO5Zjh7vUOeSX76vm0FFyz4y0cpGMFhrhvUi5unc4IVfCXZ6337R9zxuBtmveiRuuhQqnRRer+13g==", + "dev": true, + "requires": { + "@babel/runtime": "^7.11.2", + "@commitlint/format": "^11.0.0", + "@commitlint/lint": "^11.0.0", + "@commitlint/load": "^11.0.0", + "@commitlint/read": "^11.0.0", + "chalk": "4.1.0", + "core-js": "^3.6.1", + "get-stdin": "8.0.0", + "lodash": "^4.17.19", + "resolve-from": "5.0.0", + "resolve-global": "1.0.0", + "yargs": "^15.1.0" + } + }, + "@commitlint/config-conventional": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-11.0.0.tgz", + "integrity": "sha512-SNDRsb5gLuDd2PL83yCOQX6pE7gevC79UPFx+GLbLfw6jGnnbO9/tlL76MLD8MOViqGbo7ZicjChO9Gn+7tHhA==", + "dev": true, + "requires": { + "conventional-changelog-conventionalcommits": "^4.3.1" + } + }, + "@commitlint/ensure": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-11.0.0.tgz", + "integrity": "sha512-/T4tjseSwlirKZdnx4AuICMNNlFvRyPQimbZIOYujp9DSO6XRtOy9NrmvWujwHsq9F5Wb80QWi4WMW6HMaENug==", + "dev": true, + "requires": { + "@commitlint/types": "^11.0.0", + "lodash": "^4.17.19" + } + }, + "@commitlint/execute-rule": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-11.0.0.tgz", + "integrity": "sha512-g01p1g4BmYlZ2+tdotCavrMunnPFPhTzG1ZiLKTCYrooHRbmvqo42ZZn4QMStUEIcn+jfLb6BRZX3JzIwA1ezQ==", + "dev": true + }, + "@commitlint/format": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-11.0.0.tgz", + "integrity": "sha512-bpBLWmG0wfZH/svzqD1hsGTpm79TKJWcf6EXZllh2J/LSSYKxGlv967lpw0hNojme0sZd4a/97R3qA2QHWWSLg==", + "dev": true, + "requires": { + "@commitlint/types": "^11.0.0", + "chalk": "^4.0.0" + } + }, + "@commitlint/is-ignored": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-11.0.0.tgz", + "integrity": "sha512-VLHOUBN+sOlkYC4tGuzE41yNPO2w09sQnOpfS+pSPnBFkNUUHawEuA44PLHtDvQgVuYrMAmSWFQpWabMoP5/Xg==", + "dev": true, + "requires": { + "@commitlint/types": "^11.0.0", + "semver": "7.3.2" + } + }, + "@commitlint/lint": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-11.0.0.tgz", + "integrity": "sha512-Q8IIqGIHfwKr8ecVZyYh6NtXFmKw4YSEWEr2GJTB/fTZXgaOGtGFZDWOesCZllQ63f1s/oWJYtVv5RAEuwN8BQ==", + "dev": true, + "requires": { + "@commitlint/is-ignored": "^11.0.0", + "@commitlint/parse": "^11.0.0", + "@commitlint/rules": "^11.0.0", + "@commitlint/types": "^11.0.0" + } + }, + "@commitlint/load": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-11.0.0.tgz", + "integrity": "sha512-t5ZBrtgvgCwPfxmG811FCp39/o3SJ7L+SNsxFL92OR4WQxPcu6c8taD0CG2lzOHGuRyuMxZ7ps3EbngT2WpiCg==", + "dev": true, + "requires": { + "@commitlint/execute-rule": "^11.0.0", + "@commitlint/resolve-extends": "^11.0.0", + "@commitlint/types": "^11.0.0", + "chalk": "4.1.0", + "cosmiconfig": "^7.0.0", + "lodash": "^4.17.19", + "resolve-from": "^5.0.0" + } + }, + "@commitlint/message": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-11.0.0.tgz", + "integrity": "sha512-01ObK/18JL7PEIE3dBRtoMmU6S3ecPYDTQWWhcO+ErA3Ai0KDYqV5VWWEijdcVafNpdeUNrEMigRkxXHQLbyJA==", + "dev": true + }, + "@commitlint/parse": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-11.0.0.tgz", + "integrity": "sha512-DekKQAIYWAXIcyAZ6/PDBJylWJ1BROTfDIzr9PMVxZRxBPc1gW2TG8fLgjZfBP5mc0cuthPkVi91KQQKGri/7A==", + "dev": true, + "requires": { + "conventional-changelog-angular": "^5.0.0", + "conventional-commits-parser": "^3.0.0" + } + }, + "@commitlint/read": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-11.0.0.tgz", + "integrity": "sha512-37V0V91GSv0aDzMzJioKpCoZw6l0shk7+tRG8RkW1GfZzUIytdg3XqJmM+IaIYpaop0m6BbZtfq+idzUwJnw7g==", + "dev": true, + "requires": { + "@commitlint/top-level": "^11.0.0", + "fs-extra": "^9.0.0", + "git-raw-commits": "^2.0.0" + } + }, + "@commitlint/resolve-extends": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-11.0.0.tgz", + "integrity": "sha512-WinU6Uv6L7HDGLqn/To13KM1CWvZ09VHZqryqxXa1OY+EvJkfU734CwnOEeNlSCK7FVLrB4kmodLJtL1dkEpXw==", + "dev": true, + "requires": { + "import-fresh": "^3.0.0", + "lodash": "^4.17.19", + "resolve-from": "^5.0.0", + "resolve-global": "^1.0.0" + } + }, + "@commitlint/rules": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-11.0.0.tgz", + "integrity": "sha512-2hD9y9Ep5ZfoNxDDPkQadd2jJeocrwC4vJ98I0g8pNYn/W8hS9+/FuNpolREHN8PhmexXbkjrwyQrWbuC0DVaA==", + "dev": true, + "requires": { + "@commitlint/ensure": "^11.0.0", + "@commitlint/message": "^11.0.0", + "@commitlint/to-lines": "^11.0.0", + "@commitlint/types": "^11.0.0" + } + }, + "@commitlint/to-lines": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-11.0.0.tgz", + "integrity": "sha512-TIDTB0Y23jlCNubDROUVokbJk6860idYB5cZkLWcRS9tlb6YSoeLn1NLafPlrhhkkkZzTYnlKYzCVrBNVes1iw==", + "dev": true + }, + "@commitlint/top-level": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-11.0.0.tgz", + "integrity": "sha512-O0nFU8o+Ws+py5pfMQIuyxOtfR/kwtr5ybqTvR+C2lUPer2x6lnQU+OnfD7hPM+A+COIUZWx10mYQvkR3MmtAA==", + "dev": true, + "requires": { + "find-up": "^5.0.0" + }, + "dependencies": { + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "requires": { + "p-locate": "^5.0.0" + } + }, + "p-limit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.0.2.tgz", + "integrity": "sha512-iwqZSOoWIW+Ew4kAGUlN16J4M7OB3ysMLSZtnhmqx7njIHFPlxWBX8xo3lVTyFVq6mI/lL9qt2IsN1sHwaxJkg==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "requires": { + "p-limit": "^3.0.2" + } + } + } + }, + "@commitlint/types": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-11.0.0.tgz", + "integrity": "sha512-VoNqai1vR5anRF5Tuh/+SWDFk7xi7oMwHrHrbm1BprYXjB2RJsWLhUrStMssDxEl5lW/z3EUdg8RvH/IUBccSQ==", + "dev": true + }, + "@eslint/eslintrc": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.2.1.tgz", + "integrity": "sha512-XRUeBZ5zBWLYgSANMpThFddrZZkEbGHgUdt5UJjZfnlN9BGCiUBrf+nvbRupSjMvqzwnQN0qwCmOxITt1cfywA==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.1.1", + "espree": "^7.3.0", + "globals": "^12.1.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^3.13.1", + "lodash": "^4.17.19", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + } + } + }, + "@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + } + }, + "@istanbuljs/schema": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz", + "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==", + "dev": true + }, + "@nodelib/fs.scandir": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz", + "integrity": "sha512-eGmwYQn3gxo4r7jdQnkrrN6bY478C3P+a/y72IJukF8LjB6ZHeB3c+Ehacj3sYeSmUXGlnA67/PmbM9CVwL7Dw==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.3", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz", + "integrity": "sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz", + "integrity": "sha512-1V9XOY4rDW0rehzbrcqAmHnz8e7SKvX27gh8Gt2WgB0+pdzdiLV83p72kZPU+jvMbS1qU5mauP2iOvO8rhmurQ==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.3", + "fastq": "^1.6.0" + } + }, + "@pollyjs/adapter": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@pollyjs/adapter/-/adapter-5.0.0.tgz", + "integrity": "sha512-GaJUp9hKKKbRbh1FDbjVfxNGIYs8/1QQI5SR+zjz+OjpjV2btRPFCq1cqO4ORrHA2pTIC8IvEL3lgPSG4v/cPg==", + "dev": true, + "requires": { + "@pollyjs/utils": "^5.0.0" + } + }, + "@pollyjs/adapter-node-http": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@pollyjs/adapter-node-http/-/adapter-node-http-5.0.0.tgz", + "integrity": "sha512-9WjV6UVHWpcDZc1Dt5Slxn191iN55RpCirEAdt+h5wvPgRJmsLuXMI1vT3r9va8Z+a3qCqM458waNfr5us2Wuw==", + "dev": true, + "requires": { + "@pollyjs/adapter": "^5.0.0", + "@pollyjs/utils": "^5.0.0", + "lodash-es": "^4.17.11", + "nock": "^12.0.3" + } + }, + "@pollyjs/core": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@pollyjs/core/-/core-5.0.0.tgz", + "integrity": "sha512-f/v5z7aKSWdeBCZQFiQGL4aZmdWAuQWV+U/fMbAIEYjkX8Av0gWPhkFyeMfeFriYMg7Ts1XxPNI7LYroCnjn7w==", + "dev": true, + "requires": { + "@pollyjs/utils": "^5.0.0", + "@sindresorhus/fnv1a": "^1.2.0", + "blueimp-md5": "^2.10.0", + "fast-json-stable-stringify": "^2.0.0", + "is-absolute-url": "^3.0.0", + "lodash-es": "^4.17.11", + "route-recognizer": "^0.3.4", + "slugify": "^1.3.4" + } + }, + "@pollyjs/node-server": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@pollyjs/node-server/-/node-server-5.0.0.tgz", + "integrity": "sha512-DK1hqsZnIU7TXECNPI6E+ojaSpv62lt3+8eGn3qFbsa61ToTFQLFEMNfcTWA6ZXArrpdzYAdKVTt8BsdEujHkg==", + "dev": true, + "requires": { + "@pollyjs/utils": "^5.0.0", + "body-parser": "^1.19.0", + "cors": "^2.8.5", + "express": "^4.17.1", + "fs-extra": "^8.0.1", + "http-graceful-shutdown": "^2.3.1", + "morgan": "^1.9.1", + "nocache": "^2.1.0" + }, + "dependencies": { + "fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true + } + } + }, + "@pollyjs/persister": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@pollyjs/persister/-/persister-5.0.0.tgz", + "integrity": "sha512-bTJes0c2/y4xrXi1vPypJhup/N/VPC1jY7maOuSv4pOFo6VAvoVJGT2sgMMnPU7qHSRn9Y6v+S7QqGtLOZCyAA==", + "dev": true, + "requires": { + "@pollyjs/utils": "^5.0.0", + "bowser": "^2.4.0", + "fast-json-stable-stringify": "^2.0.0", + "lodash-es": "^4.17.11", + "set-cookie-parser": "^2.3.5", + "utf8-byte-length": "^1.0.4" + } + }, + "@pollyjs/persister-fs": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@pollyjs/persister-fs/-/persister-fs-5.0.0.tgz", + "integrity": "sha512-ju75JJ6sSJ/q68rB74wxIlvXCq/riuCqW3pS/Ucd4SFfks/HxXxM3yGTHn0Qq5SrmY9cYbQFfpOz0WCNDIqr+A==", + "dev": true, + "requires": { + "@pollyjs/node-server": "^5.0.0", + "@pollyjs/persister": "^5.0.0" + } + }, + "@pollyjs/utils": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@pollyjs/utils/-/utils-5.0.0.tgz", + "integrity": "sha512-zXoR13NGR1fVoUAcKR9/8AYCXZsMkG5EdvTZbR1nk5hCYJN1AR73HAKXMPsk/2vkLnBr6LWoAr3f9LjwqJOehQ==", + "dev": true, + "requires": { + "qs": "^6.7.0", + "url-parse": "^1.4.7" + } + }, + "@sindresorhus/fnv1a": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/fnv1a/-/fnv1a-1.2.0.tgz", + "integrity": "sha512-5ezb/dBSTWtKQ4sLQwMgOJyREXJcZZkTMbendMwKrXTghUhWjZhstzkkmt4/WkFy/GSTSGzfJOKU7dEXv3C/XQ==", + "dev": true + }, + "@types/eslint": { + "version": "7.2.4", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.2.4.tgz", + "integrity": "sha512-YCY4kzHMsHoyKspQH+nwSe+70Kep7Vjt2X+dZe5Vs2vkRudqtoFoUIv1RlJmZB8Hbp7McneupoZij4PadxsK5Q==", + "dev": true, + "requires": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "@types/eslint-scope": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.0.tgz", + "integrity": "sha512-O/ql2+rrCUe2W2rs7wMR+GqPRcgB6UiqN5RhrR5xruFlY7l9YLMn0ZkDzjoHLeiFkR8MCQZVudUuuvQ2BLC9Qw==", + "dev": true, + "requires": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "@types/estree": { + "version": "0.0.45", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.45.tgz", + "integrity": "sha512-jnqIUKDUqJbDIUxm0Uj7bnlMnRm1T/eZ9N+AVMqhPgzrba2GhGG5o/jCTwmdPK709nEZsGoMzXEDUjcXHa3W0g==", + "dev": true + }, + "@types/jasmine": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-3.6.0.tgz", + "integrity": "sha512-CPT4r0a63e5wpNj5ejMnconM7a+0Hdx6/APsyw8AQOHk0/Mxp3xYrym1ZabWJiYuQkgKB3MonYoN04mxtvAvRA==", + "dev": true + }, + "@types/json-schema": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.6.tgz", + "integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==", + "dev": true + }, + "@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", + "dev": true + }, + "@types/minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-aaI6OtKcrwCX8G7aWbNh7i8GOfY=", + "dev": true + }, + "@types/node": { + "version": "14.14.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.6.tgz", + "integrity": "sha512-6QlRuqsQ/Ox/aJEQWBEJG7A9+u7oSYl3mem/K8IzxXG/kAGbV1YPD9Bg9Zw3vyxC/YP+zONKwy8hGkSt1jxFMw==", + "dev": true + }, + "@types/normalize-package-data": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz", + "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==", + "dev": true + }, + "@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", + "dev": true + }, + "@types/pollyjs__adapter": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@types/pollyjs__adapter/-/pollyjs__adapter-4.3.1.tgz", + "integrity": "sha512-aQXE2CDxaNV/doHMBaYMsGTj1Xn6GihD7owLRN2kd+rCw6YBRSWXEirtkwuutJYquWU2lbBl2m4usTQearpr6Q==", + "dev": true, + "requires": { + "@types/pollyjs__core": "*" + } + }, + "@types/pollyjs__adapter-node-http": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/pollyjs__adapter-node-http/-/pollyjs__adapter-node-http-2.0.1.tgz", + "integrity": "sha512-bUEZZbzCwvUZfBUrVJXYbQfdGVKL7TM/xrPFJYLNPC94I0PWngYtbFdzCkJSKfiOHP9xptgGTQpViDlNoKXYoQ==", + "dev": true, + "requires": { + "@types/pollyjs__adapter": "*" + } + }, + "@types/pollyjs__core": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@types/pollyjs__core/-/pollyjs__core-4.3.1.tgz", + "integrity": "sha512-YxAH9Ms9A2jStQKko/wnCCLN6VYQHfrSPvHRC8YlVXuZVoVx56tz8I88QyBxrNO9ZEC4o2dvQqkDO1A8neOQZQ==", + "dev": true, + "requires": { + "@types/pollyjs__adapter": "*", + "@types/pollyjs__persister": "*" + } + }, + "@types/pollyjs__persister": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@types/pollyjs__persister/-/pollyjs__persister-4.3.1.tgz", + "integrity": "sha512-h8MNPbBGM5fRCXGA/xAw6bFZT1oJhnPaoTpDp07PHN63YZvRR0NdeJYphFod+PX1GOhDHrIXxtB912JlwJwGHw==", + "dev": true + }, + "@types/pollyjs__persister-fs": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/pollyjs__persister-fs/-/pollyjs__persister-fs-2.0.1.tgz", + "integrity": "sha512-wYqBN0sqHg+XHGo0Hy5J7pCF0IW6+aWEz9+wKWN6VuLmq3Bh2D/pzLOADSbSH8g5IwYQWNRO4GTYONCffNSlwg==", + "dev": true, + "requires": { + "@types/pollyjs__persister": "*" + } + }, + "@types/pollyjs__utils": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@types/pollyjs__utils/-/pollyjs__utils-2.6.1.tgz", + "integrity": "sha512-NYrw4j4QEBCGnjTBVqjQRszsdwoPhykW3hGz3IymFquEXGmeh5EvRgOwXmJE+FvJ9hdcG55UmLfTO6cJ7gzO5w==", + "dev": true + }, + "@types/setup-polly-jest": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@types/setup-polly-jest/-/setup-polly-jest-0.5.1.tgz", + "integrity": "sha512-YNyz4ANZOtmZxQMQOU2q43jjacEGKWywDTL8CkIBSosmRL7HEgoelcI3GyXSrmlennCCmvC/NEenHYFSqYMg+A==", + "dev": true, + "requires": { + "@types/pollyjs__core": "*" + } + }, + "@types/webpack-env": { + "version": "1.15.3", + "resolved": "https://registry.npmjs.org/@types/webpack-env/-/webpack-env-1.15.3.tgz", + "integrity": "sha512-5oiXqR7kwDGZ6+gmzIO2lTC+QsriNuQXZDWNYRV3l2XRN/zmPgnC21DLSx2D05zvD8vnXW6qUg7JnXZ4I6qLVQ==", + "dev": true + }, + "@typescript-eslint/eslint-plugin": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.6.0.tgz", + "integrity": "sha512-1+419X+Ynijytr1iWI+/IcX/kJryc78YNpdaXR1aRO1sU3bC0vZrIAF1tIX7rudVI84W7o7M4zo5p1aVt70fAg==", + "dev": true, + "requires": { + "@typescript-eslint/experimental-utils": "4.6.0", + "@typescript-eslint/scope-manager": "4.6.0", + "debug": "^4.1.1", + "functional-red-black-tree": "^1.0.1", + "regexpp": "^3.0.0", + "semver": "^7.3.2", + "tsutils": "^3.17.1" + } + }, + "@typescript-eslint/experimental-utils": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.6.0.tgz", + "integrity": "sha512-pnh6Beh2/4xjJVNL+keP49DFHk3orDHHFylSp3WEjtgW3y1U+6l+jNnJrGlbs6qhAz5z96aFmmbUyKhunXKvKw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.3", + "@typescript-eslint/scope-manager": "4.6.0", + "@typescript-eslint/types": "4.6.0", + "@typescript-eslint/typescript-estree": "4.6.0", + "eslint-scope": "^5.0.0", + "eslint-utils": "^2.0.0" + } + }, + "@typescript-eslint/parser": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.6.0.tgz", + "integrity": "sha512-Dj6NJxBhbdbPSZ5DYsQqpR32MwujF772F2H3VojWU6iT4AqL4BKuoNWOPFCoSZvCcADDvQjDpa6OLDAaiZPz2Q==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "4.6.0", + "@typescript-eslint/types": "4.6.0", + "@typescript-eslint/typescript-estree": "4.6.0", + "debug": "^4.1.1" + } + }, + "@typescript-eslint/scope-manager": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.6.0.tgz", + "integrity": "sha512-uZx5KvStXP/lwrMrfQQwDNvh2ppiXzz5TmyTVHb+5TfZ3sUP7U1onlz3pjoWrK9konRyFe1czyxObWTly27Ang==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.6.0", + "@typescript-eslint/visitor-keys": "4.6.0" + } + }, + "@typescript-eslint/types": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.6.0.tgz", + "integrity": "sha512-5FAgjqH68SfFG4UTtIFv+rqYJg0nLjfkjD0iv+5O27a0xEeNZ5rZNDvFGZDizlCD1Ifj7MAbSW2DPMrf0E9zjA==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.6.0.tgz", + "integrity": "sha512-s4Z9qubMrAo/tw0CbN0IN4AtfwuehGXVZM0CHNMdfYMGBDhPdwTEpBrecwhP7dRJu6d9tT9ECYNaWDHvlFSngA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.6.0", + "@typescript-eslint/visitor-keys": "4.6.0", + "debug": "^4.1.1", + "globby": "^11.0.1", + "is-glob": "^4.0.1", + "lodash": "^4.17.15", + "semver": "^7.3.2", + "tsutils": "^3.17.1" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.6.0.tgz", + "integrity": "sha512-38Aa9Ztl0XyFPVzmutHXqDMCu15Xx8yKvUo38Gu3GhsuckCh3StPI5t2WIO9LHEsOH7MLmlGfKUisU8eW1Sjhg==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.6.0", + "eslint-visitor-keys": "^2.0.0" + } + }, + "@webassemblyjs/ast": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz", + "integrity": "sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA==", + "dev": true, + "requires": { + "@webassemblyjs/helper-module-context": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/wast-parser": "1.9.0" + } + }, + "@webassemblyjs/floating-point-hex-parser": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.0.tgz", + "integrity": "sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA==", + "dev": true + }, + "@webassemblyjs/helper-api-error": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz", + "integrity": "sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw==", + "dev": true + }, + "@webassemblyjs/helper-buffer": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.0.tgz", + "integrity": "sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA==", + "dev": true + }, + "@webassemblyjs/helper-code-frame": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.0.tgz", + "integrity": "sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA==", + "dev": true, + "requires": { + "@webassemblyjs/wast-printer": "1.9.0" + } + }, + "@webassemblyjs/helper-fsm": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.0.tgz", + "integrity": "sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw==", + "dev": true + }, + "@webassemblyjs/helper-module-context": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.0.tgz", + "integrity": "sha512-MJCW8iGC08tMk2enck1aPW+BE5Cw8/7ph/VGZxwyvGbJwjktKkDK7vy7gAmMDx88D7mhDTCNKAW5tED+gZ0W8g==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0" + } + }, + "@webassemblyjs/helper-wasm-bytecode": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz", + "integrity": "sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw==", + "dev": true + }, + "@webassemblyjs/helper-wasm-section": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz", + "integrity": "sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0" + } + }, + "@webassemblyjs/ieee754": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz", + "integrity": "sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg==", + "dev": true, + "requires": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "@webassemblyjs/leb128": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.9.0.tgz", + "integrity": "sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw==", + "dev": true, + "requires": { + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/utf8": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.9.0.tgz", + "integrity": "sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w==", + "dev": true + }, + "@webassemblyjs/wasm-edit": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz", + "integrity": "sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/helper-wasm-section": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0", + "@webassemblyjs/wasm-opt": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0", + "@webassemblyjs/wast-printer": "1.9.0" + } + }, + "@webassemblyjs/wasm-gen": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz", + "integrity": "sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/ieee754": "1.9.0", + "@webassemblyjs/leb128": "1.9.0", + "@webassemblyjs/utf8": "1.9.0" + } + }, + "@webassemblyjs/wasm-opt": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz", + "integrity": "sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0" + } + }, + "@webassemblyjs/wasm-parser": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz", + "integrity": "sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-api-error": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/ieee754": "1.9.0", + "@webassemblyjs/leb128": "1.9.0", + "@webassemblyjs/utf8": "1.9.0" + } + }, + "@webassemblyjs/wast-parser": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.9.0.tgz", + "integrity": "sha512-qsqSAP3QQ3LyZjNC/0jBJ/ToSxfYJ8kYyuiGvtn/8MK89VrNEfwj7BPQzJVHi0jGTRK2dGdJ5PRqhtjzoww+bw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/floating-point-hex-parser": "1.9.0", + "@webassemblyjs/helper-api-error": "1.9.0", + "@webassemblyjs/helper-code-frame": "1.9.0", + "@webassemblyjs/helper-fsm": "1.9.0", + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/wast-printer": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz", + "integrity": "sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/wast-parser": "1.9.0", + "@xtuc/long": "4.2.2" + } + }, + "@webpack-cli/info": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.0.2.tgz", + "integrity": "sha512-FEfLQwmN4pXZSYSrtp+KC84rFanoCIxXFpS2wUvviDCE2fnajwxw2GXzbj83IlH4Dl8Wq8kJjavVwvxv3YJmnw==", + "dev": true, + "requires": { + "envinfo": "^7.7.3" + } + }, + "@webpack-cli/serve": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.0.1.tgz", + "integrity": "sha512-WGMaTMTK6NOe29Hw1WBEok9vGLfKg5C6jWzNOS/6HH1YadR+RL+TRWRcSyc81Dzulljhk/Ree9mrDM4Np9GGOQ==", + "dev": true + }, + "@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true + }, + "@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true + }, + "@zerollup/ts-helpers": { + "version": "1.7.18", + "resolved": "https://registry.npmjs.org/@zerollup/ts-helpers/-/ts-helpers-1.7.18.tgz", + "integrity": "sha512-S9zN+y+i5yN/evfWquzSO3lubqPXIsPQf6p9OiPMpRxDx/0totPLF39XoRw48Dav5dSvbIE8D2eAPpXXJxvKwg==", + "dev": true, + "requires": { + "resolve": "^1.12.0" + } + }, + "@zerollup/ts-transform-paths": { + "version": "1.7.18", + "resolved": "https://registry.npmjs.org/@zerollup/ts-transform-paths/-/ts-transform-paths-1.7.18.tgz", + "integrity": "sha512-YPVUxvWQVzRx1OBN0Pmkd58+R9FcfUJuwTaPUSoi5rKxuXMtxevTXdfi0w5mEaIH8b0DfL+wg0wFDHiJE+S2zA==", + "dev": true, + "requires": { + "@zerollup/ts-helpers": "^1.7.18" + } + }, + "JSONStream": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", + "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", + "dev": true, + "requires": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + } + }, + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "dev": true, + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true + }, + "acorn-jsx": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", + "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==", + "dev": true + }, + "aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + } + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true + }, + "ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true + }, + "ansi-escapes": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", + "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", + "dev": true, + "requires": { + "type-fest": "^0.11.0" + }, + "dependencies": { + "type-fest": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", + "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", + "dev": true + } + } + }, + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "append-transform": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", + "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", + "dev": true, + "requires": { + "default-require-extensions": "^3.0.0" + } + }, + "archy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", + "dev": true + }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "array-back": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-4.0.1.tgz", + "integrity": "sha512-Z/JnaVEXv+A9xabHzN43FiiiWEE7gPCRXMrVmRm00tWbjZRul1iHm7ECzlyNq1p4a4ATXz+G9FJ3GqGOkOV3fg==", + "dev": true + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", + "dev": true + }, + "array-ify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", + "integrity": "sha1-nlKHYrSpBmrRY6aWKjZEGOlibs4=", + "dev": true + }, + "array-includes": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.1.tgz", + "integrity": "sha512-c2VXaCHl7zPsvpkFsw4nxvFie4fh1ur9bpcgsVkIjqn0H/Xwdg+7fv3n2r/isyS8EBj5b06M9kHyZuIr4El6WQ==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0", + "is-string": "^1.0.5" + }, + "dependencies": { + "es-abstract": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", + "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + } + } + }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, + "array.prototype.flat": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.3.tgz", + "integrity": "sha512-gBlRZV0VSmfPIeWfuuy56XZMvbVfbEUnOXUvt3F/eUUUSyzlgLxhEX4YAEpxNAogRGehPSnfXyPtYyKAhkzQhQ==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1" + }, + "dependencies": { + "es-abstract": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", + "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + } + } + }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "dev": true + }, + "astral-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", + "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "dev": true + }, + "async": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "dev": true, + "requires": { + "lodash": "^4.17.14" + } + }, + "at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" + } + }, + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true + }, + "blueimp-md5": { + "version": "2.18.0", + "resolved": "https://registry.npmjs.org/blueimp-md5/-/blueimp-md5-2.18.0.tgz", + "integrity": "sha512-vE52okJvzsVWhcgUHOv+69OG3Mdg151xyn41aVQN/5W5S+S43qZhxECtYLAEHMSFWX6Mv5IZrzj3T5+JqXfj5Q==", + "dev": true + }, + "body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "dev": true, + "requires": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", + "dev": true + } + } + }, + "bowser": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", + "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "browserslist": { + "version": "4.14.5", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.14.5.tgz", + "integrity": "sha512-Z+vsCZIvCBvqLoYkBFTwEYH3v5MCQbsAjp50ERycpOjnPmolg1Gjy4+KaWWpm8QOJt9GHkhdqAl14NpCX73CWA==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001135", + "electron-to-chromium": "^1.3.571", + "escalade": "^3.1.0", + "node-releases": "^1.1.61" + } + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", + "dev": true + }, + "caching-transform": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", + "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==", + "dev": true, + "requires": { + "hasha": "^5.0.0", + "make-dir": "^3.0.0", + "package-hash": "^4.0.0", + "write-file-atomic": "^3.0.0" + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "camelcase-keys": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", + "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" + } + }, + "caniuse-lite": { + "version": "1.0.30001153", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001153.tgz", + "integrity": "sha512-qv14w7kWwm2IW7DBvAKWlCqGTmV2XxNtSejJBVplwRjhkohHuhRUpeSlPjtu9erru0+A12zCDUiSmvx/AcqVRA==", + "dev": true + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "chrome-trace-event": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz", + "integrity": "sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true + }, + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true + }, + "cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "requires": { + "restore-cursor": "^3.1.0" + } + }, + "cli-truncate": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", + "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", + "dev": true, + "requires": { + "slice-ansi": "^3.0.0", + "string-width": "^4.2.0" + }, + "dependencies": { + "astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true + }, + "slice-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", + "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + } + } + } + }, + "cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "colorette": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.1.tgz", + "integrity": "sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw==", + "dev": true + }, + "colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "dev": true + }, + "command-line-usage": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-6.1.0.tgz", + "integrity": "sha512-Ew1clU4pkUeo6AFVDFxCbnN7GIZfXl48HIOQeFQnkO3oOqvpI7wdqtLRwv9iOCZ/7A+z4csVZeiDdEcj8g6Wiw==", + "dev": true, + "requires": { + "array-back": "^4.0.0", + "chalk": "^2.4.2", + "table-layout": "^1.0.0", + "typical": "^5.2.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "commander": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.0.tgz", + "integrity": "sha512-zP4jEKbe8SHzKJYQmq8Y9gYjtO/POJLgIdKgV7B9qNmABVFVc+ctqSX6iXh4mCpJfRBOabiZ2YKPg8ciDw6C+Q==", + "dev": true + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, + "compare-func": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", + "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==", + "dev": true, + "requires": { + "array-ify": "^1.0.0", + "dot-prop": "^5.1.0" + } + }, + "compare-versions": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.6.0.tgz", + "integrity": "sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "confusing-browser-globals": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.10.tgz", + "integrity": "sha512-gNld/3lySHwuhaVluJUKLePYirM3QNCKzVxqAdhJII9/WXKVX5PURzMVJspS1jTslSqjeuG4KMVTSouit5YPHA==", + "dev": true + }, + "contains-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", + "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", + "dev": true + }, + "content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "dev": true + }, + "conventional-changelog-angular": { + "version": "5.0.11", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.11.tgz", + "integrity": "sha512-nSLypht/1yEflhuTogC03i7DX7sOrXGsRn14g131Potqi6cbGbGEE9PSDEHKldabB6N76HiSyw9Ph+kLmC04Qw==", + "dev": true, + "requires": { + "compare-func": "^2.0.0", + "q": "^1.5.1" + } + }, + "conventional-changelog-conventionalcommits": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-4.4.0.tgz", + "integrity": "sha512-ybvx76jTh08tpaYrYn/yd0uJNLt5yMrb1BphDe4WBredMlvPisvMghfpnJb6RmRNcqXeuhR6LfGZGewbkRm9yA==", + "dev": true, + "requires": { + "compare-func": "^2.0.0", + "lodash": "^4.17.15", + "q": "^1.5.1" + } + }, + "conventional-commits-parser": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.1.0.tgz", + "integrity": "sha512-RSo5S0WIwXZiRxUGTPuYFbqvrR4vpJ1BDdTlthFgvHt5kEdnd1+pdvwWphWn57/oIl4V72NMmOocFqqJ8mFFhA==", + "dev": true, + "requires": { + "JSONStream": "^1.0.4", + "is-text-path": "^1.0.1", + "lodash": "^4.17.15", + "meow": "^7.0.0", + "split2": "^2.0.0", + "through2": "^3.0.0", + "trim-off-newlines": "^1.0.0" + } + }, + "convert-source-map": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", + "dev": true + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", + "dev": true + }, + "core-js": { + "version": "3.6.5", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.5.tgz", + "integrity": "sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA==", + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dev": true, + "requires": { + "object-assign": "^4", + "vary": "^1" + } + }, + "corser": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/corser/-/corser-2.0.1.tgz", + "integrity": "sha1-jtolLsqrWEDc2XXOuQ2TcMgZ/4c=", + "dev": true + }, + "cosmiconfig": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.0.tgz", + "integrity": "sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA==", + "dev": true, + "requires": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + } + }, + "cross-fetch": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.0.6.tgz", + "integrity": "sha512-KBPUbqgFjzWlVcURG+Svp9TlhA5uliYtiNx/0r8nv0pdypeQCRJ9IaSIc3q/x3q8t3F75cHuwxVql1HFGHCNJQ==", + "requires": { + "node-fetch": "2.6.1" + } + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "dargs": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/dargs/-/dargs-7.0.0.tgz", + "integrity": "sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg==", + "dev": true + }, + "debug": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "decamelize-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.0.tgz", + "integrity": "sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk=", + "dev": true, + "requires": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + }, + "dependencies": { + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "dev": true + } + } + }, + "dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=", + "dev": true + }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true + }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, + "default-require-extensions": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.0.tgz", + "integrity": "sha512-ek6DpXq/SCpvjhpFsLFRVtIxJCRw6fUR42lYMVZuUMK7n8eMz4Uh5clckdBjEpLhn/gEBZo7hDJnJcwdKLKQjg==", + "dev": true, + "requires": { + "strip-bom": "^4.0.0" + }, + "dependencies": { + "strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true + } + } + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "dev": true + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", + "dev": true + }, + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "requires": { + "path-type": "^4.0.0" + } + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dev": true, + "requires": { + "is-obj": "^2.0.0" + } + }, + "ecstatic": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/ecstatic/-/ecstatic-3.3.2.tgz", + "integrity": "sha512-fLf9l1hnwrHI2xn9mEDT7KIi22UDqA2jaCwyCbSUJh9a1V+LEUSL/JO/6TIz/QyuBURWUHrFL5Kg2TtO1bkkog==", + "dev": true, + "requires": { + "he": "^1.1.1", + "mime": "^1.6.0", + "minimist": "^1.1.0", + "url-join": "^2.0.5" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", + "dev": true + }, + "electron-to-chromium": { + "version": "1.3.584", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.584.tgz", + "integrity": "sha512-NB3DzrTzJFhWkUp+nl2KtUtoFzrfGXTir2S+BU4tXGyXH9vlluPuFpE3pTKeH7+PY460tHLjKzh6K2+TWwW+Ww==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "dev": true + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, + "enhanced-resolve": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.3.0.tgz", + "integrity": "sha512-3e87LvavsdxyoCfGusJnrZ5G8SLPOFeHSNpZI/ATL9a5leXo2k0w6MKnbqhdBad9qTobSfB20Ld7UmgoNbAZkQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.5.0", + "tapable": "^1.0.0" + } + }, + "enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "requires": { + "ansi-colors": "^4.1.1" + } + }, + "envinfo": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.7.3.tgz", + "integrity": "sha512-46+j5QxbPWza0PB1i15nZx0xQ4I/EfQxg9J8Had3b408SV63nEtor2e+oiY63amTo9KTuh2a3XLObNwduxYwwA==", + "dev": true + }, + "errno": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", + "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", + "dev": true, + "requires": { + "prr": "~1.0.1" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es-abstract": { + "version": "1.18.0-next.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", + "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-negative-zero": "^2.0.0", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "dev": true + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "eslint": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.12.1.tgz", + "integrity": "sha512-HlMTEdr/LicJfN08LB3nM1rRYliDXOmfoO4vj39xN6BLpFzF00hbwBoqHk8UcJ2M/3nlARZWy/mslvGEuZFvsg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@eslint/eslintrc": "^0.2.1", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "enquirer": "^2.3.5", + "eslint-scope": "^5.1.1", + "eslint-utils": "^2.1.0", + "eslint-visitor-keys": "^2.0.0", + "espree": "^7.3.0", + "esquery": "^1.2.0", + "esutils": "^2.0.2", + "file-entry-cache": "^5.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.0.0", + "globals": "^12.1.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash": "^4.17.19", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "progress": "^2.0.0", + "regexpp": "^3.1.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.0", + "strip-json-comments": "^3.1.0", + "table": "^5.2.3", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "dependencies": { + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + } + } + }, + "eslint-config-airbnb-base": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-14.2.0.tgz", + "integrity": "sha512-Snswd5oC6nJaevs3nZoLSTvGJBvzTfnBqOIArkf3cbyTyq9UD79wOk8s+RiL6bhca0p/eRO6veczhf6A/7Jy8Q==", + "dev": true, + "requires": { + "confusing-browser-globals": "^1.0.9", + "object.assign": "^4.1.0", + "object.entries": "^1.1.2" + } + }, + "eslint-config-prettier": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.15.0.tgz", + "integrity": "sha512-a1+kOYLR8wMGustcgAjdydMsQ2A/2ipRPwRKUmfYaSxc9ZPcrku080Ctl6zrZzZNs/U82MjSv+qKREkoq3bJaw==", + "dev": true, + "requires": { + "get-stdin": "^6.0.0" + }, + "dependencies": { + "get-stdin": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz", + "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==", + "dev": true + } + } + }, + "eslint-import-resolver-node": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz", + "integrity": "sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA==", + "dev": true, + "requires": { + "debug": "^2.6.9", + "resolve": "^1.13.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "eslint-import-resolver-typescript": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-2.3.0.tgz", + "integrity": "sha512-MHSXvmj5e0SGOOBhBbt7C+fWj1bJbtSYFAD85Xeg8nvUtuooTod2HQb8bfhE9f5QyyNxEfgzqOYFCvmdDIcCuw==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "glob": "^7.1.6", + "is-glob": "^4.0.1", + "resolve": "^1.17.0", + "tsconfig-paths": "^3.9.0" + } + }, + "eslint-module-utils": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz", + "integrity": "sha512-6j9xxegbqe8/kZY8cYpcp0xhbK0EgJlg3g9mib3/miLaExuuwc3n5UEfSnU6hWMbT0FAYVvDbL9RrRgpUeQIvA==", + "dev": true, + "requires": { + "debug": "^2.6.9", + "pkg-dir": "^2.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "eslint-plugin-import": { + "version": "2.22.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.22.1.tgz", + "integrity": "sha512-8K7JjINHOpH64ozkAhpT3sd+FswIZTfMZTjdx052pnWrgRCVfp8op9tbjpAk3DdUeI/Ba4C8OjdC0r90erHEOw==", + "dev": true, + "requires": { + "array-includes": "^3.1.1", + "array.prototype.flat": "^1.2.3", + "contains-path": "^0.1.0", + "debug": "^2.6.9", + "doctrine": "1.5.0", + "eslint-import-resolver-node": "^0.3.4", + "eslint-module-utils": "^2.6.0", + "has": "^1.0.3", + "minimatch": "^3.0.4", + "object.values": "^1.1.1", + "read-pkg-up": "^2.0.0", + "resolve": "^1.17.0", + "tsconfig-paths": "^3.9.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "doctrine": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", + "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "isarray": "^1.0.0" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "path-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", + "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", + "dev": true, + "requires": { + "pify": "^2.0.0" + } + }, + "read-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", + "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", + "dev": true, + "requires": { + "load-json-file": "^2.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^2.0.0" + } + }, + "read-pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", + "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^2.0.0" + } + } + } + }, + "eslint-plugin-prettier": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.4.tgz", + "integrity": "sha512-jZDa8z76klRqo+TdGDTFJSavwbnWK2ZpqGKNZ+VvweMW516pDUMmQ2koXvxEE4JhzNvTv+radye/bWGBmA6jmg==", + "dev": true, + "requires": { + "prettier-linter-helpers": "^1.0.0" + } + }, + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + } + } + }, + "eslint-visitor-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz", + "integrity": "sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==", + "dev": true + }, + "espree": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.0.tgz", + "integrity": "sha512-dksIWsvKCixn1yrEXO8UosNSxaDoSYpq9reEjZSbHLpT5hpaCAKTLBwq0RHtLrIr+c0ByiYzWT8KTMRzoRCNlw==", + "dev": true, + "requires": { + "acorn": "^7.4.0", + "acorn-jsx": "^5.2.0", + "eslint-visitor-keys": "^1.3.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + } + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "esquery": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.3.1.tgz", + "integrity": "sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + }, + "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true + } + } + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true + } + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "dev": true + }, + "eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true + }, + "events": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.2.0.tgz", + "integrity": "sha512-/46HWwbfCX2xTawVfkKLGxMifJYQBWMwY1mjywRtb4c9x8l5NP3KoJtnIOiL1hfdRkIuYhETxQlo62IF8tcnlg==", + "dev": true + }, + "execa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", + "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + } + }, + "express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "dev": true, + "requires": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", + "dev": true + } + } + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "fast-diff": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", + "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", + "dev": true + }, + "fast-glob": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.4.tgz", + "integrity": "sha512-kr/Oo6PX51265qeuCYsyGypiO5uJFgBS0jksyG7FUeCyQzNwYnzrNIMR1NXfkZXsMYXYLRAHgISHBz8gQcxKHQ==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.0", + "merge2": "^1.3.0", + "micromatch": "^4.0.2", + "picomatch": "^2.2.1" + } + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "fastq": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.9.0.tgz", + "integrity": "sha512-i7FVWL8HhVY+CTkwFxkN2mk3h+787ixS5S63eb78diVRc1MCssarHq3W5cj0av7YDSwmaV928RNag+U1etRQ7w==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, + "figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "file-entry-cache": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", + "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", + "dev": true, + "requires": { + "flat-cache": "^2.0.1" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "dev": true, + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "find-cache-dir": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz", + "integrity": "sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "dependencies": { + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + } + } + } + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "find-versions": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/find-versions/-/find-versions-3.2.0.tgz", + "integrity": "sha512-P8WRou2S+oe222TOCHitLy8zj+SIsVJh52VP4lvXkaFVnOFFdoWv1H1Jjvel1aI6NCFOAaeAVm8qrI0odiLcww==", + "dev": true, + "requires": { + "semver-regex": "^2.0.0" + } + }, + "flat-cache": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", + "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", + "dev": true, + "requires": { + "flatted": "^2.0.0", + "rimraf": "2.6.3", + "write": "1.0.3" + } + }, + "flatted": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", + "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", + "dev": true + }, + "follow-redirects": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.0.tgz", + "integrity": "sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA==", + "dev": true + }, + "foreground-child": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", + "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "signal-exit": "^3.0.2" + } + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", + "dev": true + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "dev": true + }, + "fromentries": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.1.tgz", + "integrity": "sha512-w4t/zm2J+uAcrpeKyW0VmYiIs3aqe/xKQ+2qwazVNZSCklQHhaVjk6XzKw5GtImq5thgL0IVRjGRAOastb08RQ==", + "dev": true + }, + "fs-extra": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.1.tgz", + "integrity": "sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==", + "dev": true, + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^1.0.0" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, + "gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "get-own-enumerable-property-symbols": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", + "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==", + "dev": true + }, + "get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true + }, + "get-stdin": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz", + "integrity": "sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==", + "dev": true + }, + "get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "git-raw-commits": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-2.0.7.tgz", + "integrity": "sha512-SkwrTqrDxw8y0G1uGJ9Zw13F7qu3LF8V4BifyDeiJCxSnjRGZD9SaoMiMqUvvXMXh6S3sOQ1DsBN7L2fMUZW/g==", + "dev": true, + "requires": { + "dargs": "^7.0.0", + "lodash.template": "^4.0.2", + "meow": "^7.0.0", + "split2": "^2.0.0", + "through2": "^3.0.0" + } + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", + "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true + }, + "global-dirs": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", + "integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=", + "dev": true, + "requires": { + "ini": "^1.3.4" + } + }, + "globals": { + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", + "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "dev": true, + "requires": { + "type-fest": "^0.8.1" + }, + "dependencies": { + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + } + } + }, + "globby": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.1.tgz", + "integrity": "sha512-iH9RmgwCmUJHi2z5o2l3eTtGBtXek1OYlHrbcxOYugyHLmAsZrPj43OtHThd62Buh/Vv6VyCBD2bdyWcGNQqoQ==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.1.1", + "ignore": "^5.1.4", + "merge2": "^1.3.0", + "slash": "^3.0.0" + } + }, + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "dev": true + }, + "hard-rejection": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", + "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", + "dev": true + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "dev": true + }, + "hasha": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", + "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==", + "dev": true, + "requires": { + "is-stream": "^2.0.0", + "type-fest": "^0.8.0" + }, + "dependencies": { + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + } + } + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true + }, + "hosted-git-info": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", + "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", + "dev": true + }, + "html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + } + } + }, + "http-graceful-shutdown": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/http-graceful-shutdown/-/http-graceful-shutdown-2.3.2.tgz", + "integrity": "sha512-Dn7fJjHWboN7WjNDuo7d7ZISdUlbnyQEtOjBwMGJig45ZztHQxCsnW9N89Pr3gb6VzvZy1HySgAu2Q98j6S17w==", + "dev": true, + "requires": { + "debug": "^4.1.1" + } + }, + "http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dev": true, + "requires": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + } + }, + "http-server": { + "version": "0.12.3", + "resolved": "https://registry.npmjs.org/http-server/-/http-server-0.12.3.tgz", + "integrity": "sha512-be0dKG6pni92bRjq0kvExtj/NrrAd28/8fCXkaI/4piTwQMSDSLMhWyW0NI1V+DBI3aa1HMlQu46/HjVLfmugA==", + "dev": true, + "requires": { + "basic-auth": "^1.0.3", + "colors": "^1.4.0", + "corser": "^2.0.1", + "ecstatic": "^3.3.2", + "http-proxy": "^1.18.0", + "minimist": "^1.2.5", + "opener": "^1.5.1", + "portfinder": "^1.0.25", + "secure-compare": "3.0.1", + "union": "~0.5.0" + }, + "dependencies": { + "basic-auth": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-1.1.0.tgz", + "integrity": "sha1-RSIe5Cn37h5QNb4/UVM/HN/SmIQ=", + "dev": true + } + } + }, + "human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "dev": true + }, + "husky": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/husky/-/husky-4.3.0.tgz", + "integrity": "sha512-tTMeLCLqSBqnflBZnlVDhpaIMucSGaYyX6855jM4AguGeWCeSzNdb1mfyWduTZ3pe3SJVvVWGL0jO1iKZVPfTA==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "ci-info": "^2.0.0", + "compare-versions": "^3.6.0", + "cosmiconfig": "^7.0.0", + "find-versions": "^3.2.0", + "opencollective-postinstall": "^2.0.2", + "pkg-dir": "^4.2.0", + "please-upgrade-node": "^3.2.0", + "slash": "^3.0.0", + "which-pm-runs": "^1.0.0" + }, + "dependencies": { + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + } + } + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ignore": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", + "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "dev": true + }, + "import-fresh": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", + "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + } + } + }, + "import-local": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.2.tgz", + "integrity": "sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA==", + "dev": true, + "requires": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "dependencies": { + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + } + } + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "dev": true + }, + "interpret": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", + "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", + "dev": true + }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true + }, + "is-absolute-url": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz", + "integrity": "sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==", + "dev": true + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-callable": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", + "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==", + "dev": true + }, + "is-core-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.0.0.tgz", + "integrity": "sha512-jq1AH6C8MuteOoBPwkxHafmByhL9j5q4OaPGdbuD+ZtQJVzH+i6E3BJDQcBA09k57i2Hh2yQbEG8yObZ0jdlWw==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-date-object": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", + "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-negative-zero": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.0.tgz", + "integrity": "sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE=", + "dev": true + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true + }, + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", + "dev": true + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "is-regex": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", + "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", + "dev": true, + "requires": { + "has-symbols": "^1.0.1" + } + }, + "is-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", + "integrity": "sha1-/S2INUXEa6xaYz57mgnof6LLUGk=", + "dev": true + }, + "is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", + "dev": true + }, + "is-string": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", + "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", + "dev": true + }, + "is-symbol": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", + "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", + "dev": true, + "requires": { + "has-symbols": "^1.0.1" + } + }, + "is-text-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-1.0.1.tgz", + "integrity": "sha1-Thqg+1G/vLPpJogAE5cgLBd1tm4=", + "dev": true, + "requires": { + "text-extensions": "^1.0.0" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "istanbul-lib-coverage": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", + "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", + "dev": true + }, + "istanbul-lib-hook": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", + "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", + "dev": true, + "requires": { + "append-transform": "^2.0.0" + } + }, + "istanbul-lib-instrument": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", + "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", + "dev": true, + "requires": { + "@babel/core": "^7.7.5", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "istanbul-lib-processinfo": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.2.tgz", + "integrity": "sha512-kOwpa7z9hme+IBPZMzQ5vdQj8srYgAtaRqeI48NGmAQ+/5yKiHLV0QbYqQpxsdEF0+w14SoB8YbnHKcXE2KnYw==", + "dev": true, + "requires": { + "archy": "^1.0.0", + "cross-spawn": "^7.0.0", + "istanbul-lib-coverage": "^3.0.0-alpha.1", + "make-dir": "^3.0.0", + "p-map": "^3.0.0", + "rimraf": "^3.0.0", + "uuid": "^3.3.3" + }, + "dependencies": { + "p-map": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "dev": true, + "requires": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + } + }, + "istanbul-lib-source-maps": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz", + "integrity": "sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "istanbul-reports": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz", + "integrity": "sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw==", + "dev": true, + "requires": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + } + }, + "jasmine": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-3.6.2.tgz", + "integrity": "sha512-Uc0o2MRnC8TS1MjDrB8jE1umKEo2mflzGvdg0Ncs+yuLtOJ+uz/Wz8VmGsNGtuASr8+E0LDgPkOpvdoC76m5WQ==", + "dev": true, + "requires": { + "glob": "^7.1.6", + "jasmine-core": "~3.6.0" + } + }, + "jasmine-core": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.6.0.tgz", + "integrity": "sha512-8uQYa7zJN8hq9z+g8z1bqCfdC8eoDAeVnM5sfqs7KHv9/ifoJ500m018fpFc7RDaO6SWCLCXwo/wPSNcdYTgcw==", + "dev": true + }, + "jasmine-spec-reporter": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/jasmine-spec-reporter/-/jasmine-spec-reporter-6.0.0.tgz", + "integrity": "sha512-MvTOVoMxDZAftQYBApIlSfKnGMzi9cj351nXeqtnZTuXffPlbONN31+Es7F+Ke4okUeQ2xISukt4U1npfzLVrQ==", + "dev": true, + "requires": { + "colors": "1.4.0" + } + }, + "jest-worker": { + "version": "26.6.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.1.tgz", + "integrity": "sha512-R5IE3qSGz+QynJx8y+ICEkdI2OJ3RJjRQVEyCcFAd3yVhQSEtquziPO29Mlzgn07LOVE8u8jhJ1FqcwegiXWOw==", + "dev": true, + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^7.0.0" + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "js-yaml": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", + "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true + }, + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "jsonfile": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.0.1.tgz", + "integrity": "sha512-jR2b5v7d2vIOust+w3wtFKZIfpC2pnRmFAhAC/BuweZFQR8qZzxH1OyrQ10HmdVYiXWkYUqPVsz91cG7EL2FBg==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^1.0.0" + } + }, + "jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", + "dev": true + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true + }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, + "lines-and-columns": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", + "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", + "dev": true + }, + "lint-staged": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-10.5.0.tgz", + "integrity": "sha512-gjC9+HGkBubOF+Yyoj9pd52Qfm/kYB+dRX1UOgWjHKvSDYl+VHkZXlBMlqSZa2cH3Kp5/uNL480sV6e2dTgXSg==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "cli-truncate": "^2.1.0", + "commander": "^6.0.0", + "cosmiconfig": "^7.0.0", + "debug": "^4.1.1", + "dedent": "^0.7.0", + "enquirer": "^2.3.6", + "execa": "^4.0.3", + "listr2": "^2.6.0", + "log-symbols": "^4.0.0", + "micromatch": "^4.0.2", + "normalize-path": "^3.0.0", + "please-upgrade-node": "^3.2.0", + "string-argv": "0.3.1", + "stringify-object": "^3.3.0" + } + }, + "listr2": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-2.6.2.tgz", + "integrity": "sha512-6x6pKEMs8DSIpA/tixiYY2m/GcbgMplMVmhQAaLFxEtNSKLeWTGjtmU57xvv6QCm2XcqzyNXL/cTSVf4IChCRA==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "cli-truncate": "^2.1.0", + "figures": "^3.2.0", + "indent-string": "^4.0.0", + "log-update": "^4.0.0", + "p-map": "^4.0.0", + "rxjs": "^6.6.2", + "through": "^2.3.8" + } + }, + "load-json-file": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "strip-bom": "^3.0.0" + }, + "dependencies": { + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "requires": { + "error-ex": "^1.2.0" + } + } + } + }, + "loader-runner": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.1.0.tgz", + "integrity": "sha512-oR4lB4WvwFoC70ocraKhn5nkKSs23t57h9udUgw8o0iH8hMXeEoRuUgfcvgUwAJ1ZpRqBvcou4N2SMvM1DwMrA==", + "dev": true + }, + "loader-utils": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", + "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "lodash": { + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", + "dev": true + }, + "lodash-es": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.15.tgz", + "integrity": "sha512-rlrc3yU3+JNOpZ9zj5pQtxnx2THmvRykwL4Xlxoa8I9lHBlVbbyPhgyPMioxVZ4NqyxaVVtaJnzsyOidQIhyyQ==", + "dev": true + }, + "lodash._reinterpolate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", + "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=", + "dev": true + }, + "lodash.flattendeep": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", + "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=", + "dev": true + }, + "lodash.template": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz", + "integrity": "sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==", + "dev": true, + "requires": { + "lodash._reinterpolate": "^3.0.0", + "lodash.templatesettings": "^4.0.0" + } + }, + "lodash.templatesettings": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz", + "integrity": "sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==", + "dev": true, + "requires": { + "lodash._reinterpolate": "^3.0.0" + } + }, + "log-symbols": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", + "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", + "dev": true, + "requires": { + "chalk": "^4.0.0" + } + }, + "log-update": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", + "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", + "dev": true, + "requires": { + "ansi-escapes": "^4.3.0", + "cli-cursor": "^3.1.0", + "slice-ansi": "^4.0.0", + "wrap-ansi": "^6.2.0" + }, + "dependencies": { + "astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true + }, + "slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + } + } + } + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "requires": { + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "map-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.1.0.tgz", + "integrity": "sha512-glc9y00wgtwcDmp7GaE/0b0OnxpNJsVf3ael/An6Fe2Q51LLwN1er6sdomLRzz5h0+yMpiYLhWYF5R7HeqVd4g==", + "dev": true + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "dev": true + }, + "memory-fs": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz", + "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==", + "dev": true, + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + }, + "meow": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/meow/-/meow-7.1.1.tgz", + "integrity": "sha512-GWHvA5QOcS412WCo8vwKDlTelGLsCGBVevQB5Kva961rmNfun0PCbv5+xta2kUMFJyR8/oWnn7ddeKdosbAPbA==", + "dev": true, + "requires": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^2.5.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.13.1", + "yargs-parser": "^18.1.3" + } + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", + "dev": true + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "dev": true + }, + "micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.0.5" + } + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true + }, + "mime-db": { + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", + "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==", + "dev": true + }, + "mime-types": { + "version": "2.1.27", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", + "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", + "dev": true, + "requires": { + "mime-db": "1.44.0" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "minimist-options": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", + "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", + "dev": true, + "requires": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" + } + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "morgan": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", + "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==", + "dev": true, + "requires": { + "basic-auth": "~2.0.1", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-finished": "~2.3.0", + "on-headers": "~1.0.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", + "dev": true + }, + "neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "nocache": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/nocache/-/nocache-2.1.0.tgz", + "integrity": "sha512-0L9FvHG3nfnnmaEQPjT9xhfN4ISk0A8/2j4M37Np4mcDesJjHgEUfgPhdCyZuFI954tjokaIj/A3NdpFNdEh4Q==", + "dev": true + }, + "nock": { + "version": "12.0.3", + "resolved": "https://registry.npmjs.org/nock/-/nock-12.0.3.tgz", + "integrity": "sha512-QNb/j8kbFnKCiyqi9C5DD0jH/FubFGj5rt9NQFONXwQm3IPB0CULECg/eS3AU1KgZb/6SwUa4/DTRKhVxkGABw==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "json-stringify-safe": "^5.0.1", + "lodash": "^4.17.13", + "propagate": "^2.0.0" + } + }, + "node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" + }, + "node-preload": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", + "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", + "dev": true, + "requires": { + "process-on-spawn": "^1.0.0" + } + }, + "node-releases": { + "version": "1.1.64", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.64.tgz", + "integrity": "sha512-Iec8O9166/x2HRMJyLLLWkd0sFFLrFNy+Xf+JQfSQsdBJzPcHpNl3JQ9gD4j+aJxmCa25jNsIbM4bmACtSbkSg==", + "dev": true + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "requires": { + "path-key": "^3.0.0" + } + }, + "nyc": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz", + "integrity": "sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==", + "dev": true, + "requires": { + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "caching-transform": "^4.0.0", + "convert-source-map": "^1.7.0", + "decamelize": "^1.2.0", + "find-cache-dir": "^3.2.0", + "find-up": "^4.1.0", + "foreground-child": "^2.0.0", + "get-package-type": "^0.1.0", + "glob": "^7.1.6", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-hook": "^3.0.0", + "istanbul-lib-instrument": "^4.0.0", + "istanbul-lib-processinfo": "^2.0.2", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.0.2", + "make-dir": "^3.0.0", + "node-preload": "^0.2.1", + "p-map": "^3.0.0", + "process-on-spawn": "^1.0.0", + "resolve-from": "^5.0.0", + "rimraf": "^3.0.0", + "signal-exit": "^3.0.2", + "spawn-wrap": "^2.0.0", + "test-exclude": "^6.0.0", + "yargs": "^15.0.2" + }, + "dependencies": { + "p-map": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "object-inspect": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", + "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==", + "dev": true + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + }, + "object.assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.1.tgz", + "integrity": "sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.0", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + }, + "object.entries": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.2.tgz", + "integrity": "sha512-BQdB9qKmb/HyNdMNWVr7O3+z5MUIx3aiegEIJqjMBbBf0YT9RRxTJSim4mzFqtyr7PDAHigq0N9dO0m0tRakQA==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5", + "has": "^1.0.3" + }, + "dependencies": { + "es-abstract": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", + "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + } + } + }, + "object.values": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.1.tgz", + "integrity": "sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1", + "function-bind": "^1.1.1", + "has": "^1.0.3" + }, + "dependencies": { + "es-abstract": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", + "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + } + } + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "dev": true, + "requires": { + "ee-first": "1.1.1" + } + }, + "on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "opencollective-postinstall": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz", + "integrity": "sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==", + "dev": true + }, + "opener": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", + "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", + "dev": true + }, + "optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "requires": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "package-hash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", + "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.15", + "hasha": "^5.0.0", + "lodash.flattendeep": "^4.4.0", + "release-zalgo": "^1.0.0" + } + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, + "parse-json": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.1.0.tgz", + "integrity": "sha512-+mi/lmVVNKFNVyLXV31ERiy2CY5E1/F6QtJFEzoChPRwwngMNXRDQ9GJ5WdE2Z2P4AujsOi0/+2qHID68KwfIQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + } + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", + "dev": true + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + }, + "picomatch": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", + "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", + "dev": true + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "pkg-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", + "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", + "dev": true, + "requires": { + "find-up": "^2.1.0" + }, + "dependencies": { + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + } + } + }, + "please-upgrade-node": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz", + "integrity": "sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==", + "dev": true, + "requires": { + "semver-compare": "^1.0.0" + } + }, + "portfinder": { + "version": "1.0.28", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", + "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==", + "dev": true, + "requires": { + "async": "^2.6.2", + "debug": "^3.1.1", + "mkdirp": "^0.5.5" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true + }, + "prettier": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.1.2.tgz", + "integrity": "sha512-16c7K+x4qVlJg9rEbXl7HEGmQyZlG4R9AgP+oHKRMsMsuk8s+ATStlf1NpDqyBI1HpVyfjLOeMhH2LvuNvV5Vg==", + "dev": true + }, + "prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "requires": { + "fast-diff": "^1.1.2" + } + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "process-on-spawn": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", + "integrity": "sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==", + "dev": true, + "requires": { + "fromentries": "^1.2.0" + } + }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true + }, + "propagate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz", + "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==", + "dev": true + }, + "proxy-addr": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", + "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", + "dev": true, + "requires": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.9.1" + } + }, + "prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", + "dev": true + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=", + "dev": true + }, + "qs": { + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.4.tgz", + "integrity": "sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ==", + "dev": true + }, + "querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "dev": true + }, + "quick-lru": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", + "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", + "dev": true + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true + }, + "raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "dev": true, + "requires": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, + "read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "requires": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "dependencies": { + "type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true + } + } + }, + "read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "requires": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "dependencies": { + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + } + } + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "rechoir": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.0.tgz", + "integrity": "sha512-ADsDEH2bvbjltXEP+hTIAmeFekTFK0V2BTxMkok6qILyAJEXV0AFfoWcAq4yfll5VdIMd/RVXq0lR+wQi5ZU3Q==", + "dev": true, + "requires": { + "resolve": "^1.9.0" + } + }, + "redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "requires": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + } + }, + "reduce-flatten": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-2.0.0.tgz", + "integrity": "sha512-EJ4UNY/U1t2P/2k6oqotuX2Cc3T6nxJwsM0N0asT7dhrtH1ltUxDn4NalSYmPE2rCkVpcf/X6R0wDwcFpzhd4w==", + "dev": true + }, + "regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", + "dev": true + }, + "regexpp": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", + "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", + "dev": true + }, + "release-zalgo": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", + "integrity": "sha1-CXALflB0Mpc5Mw5TXFqQ+2eFFzA=", + "dev": true, + "requires": { + "es6-error": "^4.0.1" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", + "dev": true + }, + "resolve": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.18.1.tgz", + "integrity": "sha512-lDfCPaMKfOJXjy0dPayzPdF1phampNWr3qFCjAu+rw/qbQmr5jWH5xN2hwh9QKfw9E5v4hwV7A+jrCmL8yjjqA==", + "dev": true, + "requires": { + "is-core-module": "^2.0.0", + "path-parse": "^1.0.6" + } + }, + "resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "requires": { + "resolve-from": "^5.0.0" + } + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + }, + "resolve-global": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-global/-/resolve-global-1.0.0.tgz", + "integrity": "sha512-zFa12V4OLtT5XUX/Q4VLvTfBf+Ok0SPc1FNGM/z9ctUdiU618qwKpWnd0CHs3+RqROfyEg/DhuHbMWYqcgljEw==", + "dev": true, + "requires": { + "global-dirs": "^0.1.1" + } + }, + "restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "requires": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + } + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "route-recognizer": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/route-recognizer/-/route-recognizer-0.3.4.tgz", + "integrity": "sha512-2+MhsfPhvauN1O8KaXpXAOfR/fwe8dnUXVM+xw7yt40lJRfPVQxV6yryZm0cgRvAj5fMF/mdRZbL2ptwbs5i2g==", + "dev": true + }, + "run-parallel": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.10.tgz", + "integrity": "sha512-zb/1OuZ6flOlH6tQyMPUrE3x3Ulxjlo9WIVXR4yVYi4H9UXQaeIsPbLn2R3O3vQCnDKkAl2qHiuocKKX4Tz/Sw==", + "dev": true + }, + "rxjs": { + "version": "6.6.3", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.3.tgz", + "integrity": "sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "schema-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", + "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.6", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + }, + "secure-compare": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/secure-compare/-/secure-compare-3.0.1.tgz", + "integrity": "sha1-8aAymzCLIh+uN7mXTz1XjQypmeM=", + "dev": true + }, + "semver": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", + "dev": true + }, + "semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=", + "dev": true + }, + "semver-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-2.0.0.tgz", + "integrity": "sha512-mUdIBBvdn0PLOeP3TEkMH7HHeUP3GjsXCwKarjv/kGmUFOYg1VqEemKhoQpWMu6X2I8kHeuVdGibLGkVK+/5Qw==", + "dev": true + }, + "send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "dev": true, + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + } + } + }, + "serialize-javascript": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", + "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, + "serve-static": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "dev": true, + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "set-cookie-parser": { + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.4.6.tgz", + "integrity": "sha512-mNCnTUF0OYPwYzSHbdRdCfNNHqrne+HS5tS5xNb6yJbdP9wInV0q5xPLE0EyfV/Q3tImo3y/OXpD8Jn0Jtnjrg==", + "dev": true + }, + "setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==", + "dev": true + }, + "setup-polly-jest": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/setup-polly-jest/-/setup-polly-jest-0.9.1.tgz", + "integrity": "sha512-BbOxCvMnDHAPG9e3Qw3Ol4/ZWLwnDnhODqrfiIpAtF7Azu0O3PI4svZIfsh+/EV9xNj15LMkvchaWsDvF4A3hQ==", + "dev": true + }, + "shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "requires": { + "kind-of": "^6.0.2" + } + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", + "dev": true + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "slice-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", + "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "astral-regex": "^1.0.0", + "is-fullwidth-code-point": "^2.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + } + } + }, + "slugify": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/slugify/-/slugify-1.4.5.tgz", + "integrity": "sha512-WpECLAgYaxHoEAJ8Q1Lo8HOs1ngn7LN7QjXgOLbmmfkcWvosyk4ZTXkTzKyhngK640USTZUlgoQJfED1kz5fnQ==", + "dev": true + }, + "source-list-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", + "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, + "source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "spawn-wrap": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", + "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==", + "dev": true, + "requires": { + "foreground-child": "^2.0.0", + "is-windows": "^1.0.2", + "make-dir": "^3.0.0", + "rimraf": "^3.0.0", + "signal-exit": "^3.0.2", + "which": "^2.0.1" + }, + "dependencies": { + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.6.tgz", + "integrity": "sha512-+orQK83kyMva3WyPf59k1+Y525csj5JejicWut55zeTWANuN17qSiSLUXWtzHeNWORSvT7GLDJ/E/XiIWoXBTw==", + "dev": true + }, + "split2": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-2.2.0.tgz", + "integrity": "sha512-RAb22TG39LhI31MbreBgIuKiIKhVsawfTgEGqKHTK87aG+ul/PB8Sqoi3I7kVdRWiCfrKxK3uo4/YUkpNvhPbw==", + "dev": true, + "requires": { + "through2": "^2.0.2" + }, + "dependencies": { + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + } + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "dev": true + }, + "string-argv": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", + "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", + "dev": true + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "string.prototype.trimend": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.2.tgz", + "integrity": "sha512-8oAG/hi14Z4nOVP0z6mdiVZ/wqjDtWSLygMigTzAb+7aPEDTleeFf+WrF+alzecxIRkckkJVn+dTlwzJXORATw==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.1" + } + }, + "string.prototype.trimstart": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.2.tgz", + "integrity": "sha512-7F6CdBTl5zyu30BJFdzSTlSlLPwODC23Od+iLoVH8X6+3fvDPPuBVVj9iaB1GOsSTSIgVfsfm27R2FGrAPznWg==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "stringify-object": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", + "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", + "dev": true, + "requires": { + "get-own-enumerable-property-symbols": "^3.0.0", + "is-obj": "^1.0.1", + "is-regexp": "^1.0.0" + }, + "dependencies": { + "is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", + "dev": true + } + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true + }, + "strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "requires": { + "min-indent": "^1.0.0" + } + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "table": { + "version": "5.4.6", + "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", + "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", + "dev": true, + "requires": { + "ajv": "^6.10.2", + "lodash": "^4.17.14", + "slice-ansi": "^2.1.0", + "string-width": "^3.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "table-layout": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-1.0.1.tgz", + "integrity": "sha512-dEquqYNJiGwY7iPfZ3wbXDI944iqanTSchrACLL2nOB+1r+h1Nzu2eH+DuPPvWvm5Ry7iAPeFlgEtP5bIp5U7Q==", + "dev": true, + "requires": { + "array-back": "^4.0.1", + "deep-extend": "~0.6.0", + "typical": "^5.2.0", + "wordwrapjs": "^4.0.0" + } + }, + "tapable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", + "dev": true + }, + "terser": { + "version": "5.3.8", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.3.8.tgz", + "integrity": "sha512-zVotuHoIfnYjtlurOouTazciEfL7V38QMAOhGqpXDEg6yT13cF4+fEP9b0rrCEQTn+tT46uxgFsTZzhygk+CzQ==", + "dev": true, + "requires": { + "commander": "^2.20.0", + "source-map": "~0.7.2", + "source-map-support": "~0.5.19" + }, + "dependencies": { + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "dev": true + } + } + }, + "terser-webpack-plugin": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.0.3.tgz", + "integrity": "sha512-zFdGk8Lh9ZJGPxxPE6jwysOlATWB8GMW8HcfGULWA/nPal+3VdATflQvSBSLQJRCmYZnfFJl6vkRTiwJGNgPiQ==", + "dev": true, + "requires": { + "jest-worker": "^26.6.1", + "p-limit": "^3.0.2", + "schema-utils": "^3.0.0", + "serialize-javascript": "^5.0.1", + "source-map": "^0.6.1", + "terser": "^5.3.8" + }, + "dependencies": { + "p-limit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.0.2.tgz", + "integrity": "sha512-iwqZSOoWIW+Ew4kAGUlN16J4M7OB3ysMLSZtnhmqx7njIHFPlxWBX8xo3lVTyFVq6mI/lL9qt2IsN1sHwaxJkg==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "requires": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + } + }, + "text-extensions": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-1.9.0.tgz", + "integrity": "sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ==", + "dev": true + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "through2": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", + "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", + "dev": true, + "requires": { + "inherits": "^2.0.4", + "readable-stream": "2 || 3" + } + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", + "dev": true + }, + "trim-newlines": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.0.tgz", + "integrity": "sha512-C4+gOpvmxaSMKuEf9Qc134F1ZuOHVXKRbtEflf4NTtuuJDEIJ9p5PXsalL8SkeRw+qit1Mo+yuvMPAKwWg/1hA==", + "dev": true + }, + "trim-off-newlines": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/trim-off-newlines/-/trim-off-newlines-1.0.1.tgz", + "integrity": "sha1-n5up2e+odkw4dpi8v+sshI8RrbM=", + "dev": true + }, + "ts-loader": { + "version": "8.0.7", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-8.0.7.tgz", + "integrity": "sha512-ooa4wxlZ9TOXaJ/iVyZlWsim79Ul4KyifSwyT2hOrbQA6NZJypsLOE198o8Ko+JV+ZHnMArvWcl4AnRqpCU/Mw==", + "dev": true, + "requires": { + "chalk": "^2.3.0", + "enhanced-resolve": "^4.0.0", + "loader-utils": "^1.0.2", + "micromatch": "^4.0.0", + "semver": "^6.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "ts-node": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.0.0.tgz", + "integrity": "sha512-/TqB4SnererCDR/vb4S/QvSZvzQMJN8daAslg7MeaiHvD8rDZsSfXmNeNumyZZzMned72Xoq/isQljYSt8Ynfg==", + "dev": true, + "requires": { + "arg": "^4.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "source-map-support": "^0.5.17", + "yn": "3.1.1" + } + }, + "tsconfig-paths": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz", + "integrity": "sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw==", + "dev": true, + "requires": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.0", + "strip-bom": "^3.0.0" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "tsutils": { + "version": "3.17.1", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.17.1.tgz", + "integrity": "sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + }, + "ttypescript": { + "version": "1.5.12", + "resolved": "https://registry.npmjs.org/ttypescript/-/ttypescript-1.5.12.tgz", + "integrity": "sha512-1ojRyJvpnmgN9kIHmUnQPlEV1gq+VVsxVYjk/NfvMlHSmYxjK5hEvOOU2MQASrbekTUiUM7pR/nXeCc8bzvMOQ==", + "dev": true, + "requires": { + "resolve": ">=1.9.0" + } + }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1" + } + }, + "type-fest": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", + "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", + "dev": true + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "requires": { + "is-typedarray": "^1.0.0" + } + }, + "typescript": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.5.tgz", + "integrity": "sha512-ywmr/VrTVCmNTJ6iV2LwIrfG1P+lv6luD8sUJs+2eI9NLGigaN+nUQc13iHqisq7bra9lnmUSYqbJvegraBOPQ==", + "dev": true + }, + "typical": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-5.2.0.tgz", + "integrity": "sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg==", + "dev": true + }, + "union": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/union/-/union-0.5.0.tgz", + "integrity": "sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA==", + "dev": true, + "requires": { + "qs": "^6.4.0" + } + }, + "universalify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz", + "integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==", + "dev": true + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "dev": true + }, + "uri-js": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz", + "integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "url-join": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-2.0.5.tgz", + "integrity": "sha1-WvIvGMBSoACkjXuCxenC4v7tpyg=", + "dev": true + }, + "url-parse": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.7.tgz", + "integrity": "sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==", + "dev": true, + "requires": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "utf8-byte-length": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.4.tgz", + "integrity": "sha1-9F8VDExm7uloGGUFq5P8u4rWv2E=", + "dev": true + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "dev": true + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true + }, + "v8-compile-cache": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz", + "integrity": "sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q==", + "dev": true + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "dev": true + }, + "watchpack": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.0.0.tgz", + "integrity": "sha512-xSdCxxYZWNk3VK13bZRYhsQpfa8Vg63zXG+3pyU8ouqSLRCv4IGXIp9Kr226q6GBkGRlZrST2wwKtjfKz2m7Cg==", + "dev": true, + "requires": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + } + }, + "webpack": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.3.1.tgz", + "integrity": "sha512-pQfG9Mjyis1HkHb5gpXYF+ymjnuq7/7ssE+m1VdiyulwmCpxjXDPNcNXyObb7vGBZ4vEXnsjPCXUYSQLf1TJAQ==", + "dev": true, + "requires": { + "@types/eslint-scope": "^3.7.0", + "@types/estree": "^0.0.45", + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-module-context": "1.9.0", + "@webassemblyjs/wasm-edit": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0", + "acorn": "^8.0.4", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.3.1", + "eslint-scope": "^5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.4", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^4.1.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "pkg-dir": "^4.2.0", + "schema-utils": "^3.0.0", + "tapable": "^2.0.0", + "terser-webpack-plugin": "^5.0.3", + "watchpack": "^2.0.0", + "webpack-sources": "^2.1.1" + }, + "dependencies": { + "acorn": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.0.4.tgz", + "integrity": "sha512-XNP0PqF1XD19ZlLKvB7cMmnZswW4C/03pRHgirB30uSJTaS3A3V1/P4sS3HPvFmjoriPCJQs+JDSbm4bL1TxGQ==", + "dev": true + }, + "enhanced-resolve": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.3.1.tgz", + "integrity": "sha512-G1XD3MRGrGfNcf6Hg0LVZG7GIKcYkbfHa5QMxt1HDUTdYoXH0JR1xXyg+MaKLF73E9A27uWNVxvFivNRYeUB6w==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.4", + "tapable": "^2.0.0" + } + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + } + }, + "tapable": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.0.0.tgz", + "integrity": "sha512-bjzn0C0RWoffnNdTzNi7rNDhs1Zlwk2tRXgk8EiHKAOX1Mag3d6T0Y5zNa7l9CJ+EoUne/0UHdwS8tMbkh9zDg==", + "dev": true + } + } + }, + "webpack-cli": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.1.0.tgz", + "integrity": "sha512-NdhxXMZmoik62Y05t0h1y65LjBM7BwFPq311ihXuMM3RY6dlc4KkCTyHLzTuBEc+bqq6d3xh+CWmU0xRexNJBA==", + "dev": true, + "requires": { + "@webpack-cli/info": "^1.0.2", + "@webpack-cli/serve": "^1.0.1", + "ansi-escapes": "^4.3.1", + "colorette": "^1.2.1", + "command-line-usage": "^6.1.0", + "commander": "^6.0.0", + "enquirer": "^2.3.4", + "execa": "^4.0.0", + "import-local": "^3.0.2", + "interpret": "^2.0.0", + "rechoir": "^0.7.0", + "v8-compile-cache": "^2.1.0", + "webpack-merge": "^4.2.2" + }, + "dependencies": { + "webpack-merge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.2.2.tgz", + "integrity": "sha512-TUE1UGoTX2Cd42j3krGYqObZbOD+xF7u28WB7tfUordytSjbWTIjK/8V0amkBfTYN4/pB/GIDlJZZ657BGG19g==", + "dev": true, + "requires": { + "lodash": "^4.17.15" + } + } + } + }, + "webpack-merge": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.2.0.tgz", + "integrity": "sha512-QBglJBg5+lItm3/Lopv8KDDK01+hjdg2azEwi/4vKJ8ZmGPdtJsTpjtNNOW3a4WiqzXdCATtTudOZJngE7RKkA==", + "dev": true, + "requires": { + "clone-deep": "^4.0.1", + "wildcard": "^2.0.0" + } + }, + "webpack-sources": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-2.2.0.tgz", + "integrity": "sha512-bQsA24JLwcnWGArOKUxYKhX3Mz/nK1Xf6hxullKERyktjNMC4x8koOeaDNTA2fEJ09BdWLbM/iTW0ithREUP0w==", + "dev": true, + "requires": { + "source-list-map": "^2.0.1", + "source-map": "^0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "which-pm-runs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.0.0.tgz", + "integrity": "sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs=", + "dev": true + }, + "wildcard": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", + "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", + "dev": true + }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, + "wordwrapjs": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-4.0.0.tgz", + "integrity": "sha512-Svqw723a3R34KvsMgpjFBYCgNOSdcW3mQFK4wIfhGQhtaFVOJmdYoXgi63ne3dTlWgatVcUc7t4HtQ/+bUVIzQ==", + "dev": true, + "requires": { + "reduce-flatten": "^2.0.0", + "typical": "^5.0.0" + } + }, + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "write": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", + "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", + "dev": true, + "requires": { + "mkdirp": "^0.5.1" + } + }, + "write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true + }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "dev": true + }, + "yaml": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.0.tgz", + "integrity": "sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg==", + "dev": true + }, + "yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dev": true, + "requires": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + } + }, + "yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true + } + } +} diff --git a/package.json b/package.json index 95b4e14..9aed4c4 100644 --- a/package.json +++ b/package.json @@ -1,16 +1,104 @@ { "name": "geocoder-js", - "version": "0.0.0", + "description": "Universal geocoding abstraction with multiple built-in providers", + "version": "0.8.0", + "keywords": [ + "geocoder", + "geocoding", + "geolocation", + "ip-geolocation", + "typescript", + "isomorphic", + "universal", + "node", + "browser", + "react-native", + "electron" + ], + "license": "(MIT AND GPL-2.0)", + "author": "Brandon Morrison ", + "contributors": [ + "Alan Poulain " + ], + "homepage": "https://geocoder-php.github.io/geocoder-js/", + "bugs": "https://github.com/geocoder-php/geocoder-js/issues", + "repository": { + "type": "git", + "url": "git+https://github.com/geocoder-php/geocoder-js.git" + }, + "main": "dist/index.js", + "types": "types/index.d.ts", + "files": [ + "/dist", + "/src", + "/types" + ], + "dependencies": { + "cross-fetch": "^3.0.0" + }, "devDependencies": { - "grunt": "~0.4.1", - "grunt-contrib-uglify": "~0.6", - "grunt-contrib-watch": "~0.6", - "grunt-contrib-jasmine": "~0.8.0", - "grunt-contrib-jshint": "~0.10", - "grunt-complexity": "~0.2.0" + "@commitlint/cli": "^11.0.0", + "@commitlint/config-conventional": "^11.0.0", + "@pollyjs/adapter-node-http": "^5.0.0", + "@pollyjs/core": "^5.0.0", + "@pollyjs/persister-fs": "^5.0.0", + "@pollyjs/utils": "^5.0.0", + "@types/jasmine": "^3.5.0", + "@types/node": "^14.0.0", + "@types/pollyjs__adapter-node-http": "^2.0.0", + "@types/pollyjs__core": "^4.3.0", + "@types/pollyjs__persister-fs": "^2.0.0", + "@types/pollyjs__utils": "^2.6.0", + "@types/setup-polly-jest": "^0.5.0", + "@types/webpack-env": "^1.15.0", + "@typescript-eslint/eslint-plugin": "^4.0.0", + "@typescript-eslint/parser": "^4.0.0", + "@zerollup/ts-transform-paths": "^1.7.0", + "eslint": "^7.0.0", + "eslint-config-airbnb-base": "^14.0.0", + "eslint-config-prettier": "^6.0.0", + "eslint-import-resolver-typescript": "^2.0.0", + "eslint-plugin-import": "^2.0.0", + "eslint-plugin-prettier": "^3.0.0", + "http-server": "^0.12.0", + "husky": "^4.2.0", + "jasmine": "^3.5.0", + "jasmine-spec-reporter": "^6.0.0", + "lint-staged": "^10.2.0", + "nyc": "^15.1.0", + "prettier": "^2.0.0", + "setup-polly-jest": "^0.9.0", + "ts-loader": "^8.0.0", + "ts-node": "^9.0.0", + "tsconfig-paths": "^3.9.0", + "ttypescript": "^1.5.0", + "typescript": "^4.0.0", + "webpack": "^5.3.0", + "webpack-cli": "^4.1.0", + "webpack-merge": "^5.1.0" }, "scripts": { - "build": "grunt --verbose", - "test": "grunt ci --verbose" + "build": "rm -rf types && rm -rf dist && webpack --config webpack.dev.js && webpack --config webpack.prod.js && ttsc", + "coverage": "nyc npm test", + "coverage-lcov": "nyc --reporter=lcov npm test", + "lint": "eslint --fix --ext .ts .", + "prepublishOnly": "npm run build", + "serve": "http-server", + "test": "ts-node --compiler ttypescript -r tsconfig-paths/register node_modules/.bin/jasmine", + "test-record": "POLLY_RECORD=1 npm test", + "ts-node": "ts-node --skip-project", + "watch": "webpack --config webpack.dev.js --watch" + }, + "husky": { + "hooks": { + "pre-commit": "lint-staged", + "commit-msg": "commitlint --env HUSKY_GIT_PARAMS" + } + }, + "lint-staged": { + "*.ts": [ + "npm run lint", + "git add" + ] } } diff --git a/spec/ExternalLoader.spec.ts b/spec/ExternalLoader.spec.ts new file mode 100644 index 0000000..7811cab --- /dev/null +++ b/spec/ExternalLoader.spec.ts @@ -0,0 +1,9 @@ +import ExternalLoader from "ExternalLoader"; + +describe("ExternalLoader", () => { + const loader = new ExternalLoader(); + + it("has executeRequest method", () => { + expect(loader.executeRequest).toBeDefined(); + }); +}); diff --git a/spec/ExternalURILoader.spec.js b/spec/ExternalURILoader.spec.js deleted file mode 100644 index 9ba69e2..0000000 --- a/spec/ExternalURILoader.spec.js +++ /dev/null @@ -1,7 +0,0 @@ -describe("ExternalURILoader method access", function() { - var loader = new GeocoderJS.ExternalURILoader(); - - it ("ExternalURILoader has executeRequest method", function() { - expect(loader.executeRequest).toBeDefined(); - }); -}); diff --git a/spec/GeoJSONDumper.spec.js b/spec/GeoJSONDumper.spec.js deleted file mode 100644 index 87401d1..0000000 --- a/spec/GeoJSONDumper.spec.js +++ /dev/null @@ -1,22 +0,0 @@ -describe("GeoJSON Dumper", function() { - var geocoded = new GeocoderJS.Geocoded(); - geocoded.latitude = 38.8978378; - geocoded.longitude = -77.0365123; - var dumper = new GeocoderJS.GeoJSONDumper(); - - var expectedGeoJSON = { - "type": "Feature", - "properties": {}, - "geometry": { - "type": "Point", - "coordinates": [ - -77.0365123, - 38.8978378 - ] - } - }; - - it("provides a valid geojson dump", function() { - expect(dumper.dump(geocoded)).toEqual(expectedGeoJSON); - }); -}); \ No newline at end of file diff --git a/spec/GeoJsonDumper.spec.ts b/spec/GeoJsonDumper.spec.ts new file mode 100644 index 0000000..e718abd --- /dev/null +++ b/spec/GeoJsonDumper.spec.ts @@ -0,0 +1,41 @@ +import Geocoded from "Geocoded"; +import GeoJsonDumper, { GeoJson } from "GeoJsonDumper"; + +describe("GeoJSON Dumper", () => { + const geocoded = Geocoded.create({ + coordinates: { + latitude: 38.8978378, + longitude: -77.0365123, + }, + bounds: { + latitudeSW: 38.89380528242933, + longitudeSW: -77.04317326462667, + latitudeNE: 38.90153071757068, + longitudeNE: -77.02993873537334, + }, + formattedAddress: + "1600 Pennsylvania Avenue Northwest, Washington, DC 20050", + }); + + const expectedGeoJson: GeoJson = { + type: "Feature", + properties: { + formattedAddress: + "1600 Pennsylvania Avenue Northwest, Washington, DC 20050", + }, + geometry: { + type: "Point", + coordinates: [-77.0365123, 38.8978378], + }, + bbox: [ + -77.04317326462667, + 38.89380528242933, + -77.02993873537334, + 38.90153071757068, + ], + }; + + it("provides a valid GeoJSON dump", () => { + expect(GeoJsonDumper.dump(geocoded)).toEqual(expectedGeoJson); + }); +}); diff --git a/spec/Geocoded.spec.js b/spec/Geocoded.spec.js deleted file mode 100644 index 97542e3..0000000 --- a/spec/Geocoded.spec.js +++ /dev/null @@ -1,82 +0,0 @@ -describe("Geocoded method access", function() { - var geocoded = new GeocoderJS.Geocoded(); - - it ("Geocoded has getCoordinates method", function() { - expect(geocoded.getCoordinates).toBeDefined(); - }); - it ("Geocoded has getLatitude method", function() { - expect(geocoded.getLatitude).toBeDefined(); - }); - it ("Geocoded has getLongitude method", function() { - expect(geocoded.getLongitude).toBeDefined(); - }); - it ("Geocoded has getBounds method", function() { - expect(geocoded.getBounds).toBeDefined(); - }); - it ("Geocoded has getStreetNumber method", function() { - expect(geocoded.getStreetNumber).toBeDefined(); - }); - it ("Geocoded has getStreetName method", function() { - expect(geocoded.getStreetName).toBeDefined(); - }); - it ("Geocoded has getCity method", function() { - expect(geocoded.getCity).toBeDefined(); - }); - it ("Geocoded has getZipcode method", function() { - expect(geocoded.getZipcode).toBeDefined(); - }); - it ("Geocoded has getCityDistrict method", function() { - expect(geocoded.getCityDistrict).toBeDefined(); - }); - it ("Geocoded has getCounty method", function() { - expect(geocoded.getCounty).toBeDefined(); - }); - it ("Geocoded has getCountyCode method", function() { - expect(geocoded.getCountyCode).toBeDefined(); - }); - it ("Geocoded has getRegion method", function() { - expect(geocoded.getRegion).toBeDefined(); - }); -}); - -describe("Geocoded returns data properly", function() { - var geocoded = new GeocoderJS.Geocoded(); - geocoded.latitude = 38.8978378; - geocoded.longitude = -77.0365123; - geocoded.streetNumber = "1600"; - geocoded.streetName = "Pennsylvania Avenue Northwest"; - geocoded.city = "Washington"; - geocoded.region = "DC"; - geocoded.postal_code = "20050"; - - it ("Geocoded returns proper coordinates", function() { - var expectedCoordinates = [38.8978378, -77.0365123]; - expect(geocoded.getCoordinates()).toEqual(expectedCoordinates); - }); - - it ("Geocoded returns latitude/longitude individually", function() { - expect(geocoded.getLatitude()).toEqual(38.8978378); - expect(geocoded.getLongitude()).toEqual(-77.0365123); - }); - - it ("Geocoded returns proper Street Number", function() { - expect(geocoded.getStreetNumber()).toEqual("1600"); - }); - - it ("Geocoded returns proper Street Name", function() { - expect(geocoded.getStreetName()).toEqual("Pennsylvania Avenue Northwest"); - }); - - it ("Geocoded returns proper City", function() { - expect(geocoded.getCity()).toEqual("Washington"); - }); - - it ("Geocoded returns proper Region", function() { - expect(geocoded.getRegion()).toEqual("DC"); - }); - - it ("Geocoded returns proper Postal Code", function() { - expect(geocoded.getZipcode()).toEqual("20050"); - }); - -}); diff --git a/spec/Geocoded.spec.ts b/spec/Geocoded.spec.ts new file mode 100644 index 0000000..84aa425 --- /dev/null +++ b/spec/Geocoded.spec.ts @@ -0,0 +1,126 @@ +import Geocoded from "Geocoded"; + +describe("Geocoded API", () => { + const geocoded = Geocoded.create({}); + + it("has getCoordinates method", () => { + expect(geocoded.getCoordinates).toBeDefined(); + }); + it("has getBounds method", () => { + expect(geocoded.getBounds).toBeDefined(); + }); + it("has getFormattedAddress method", () => { + expect(geocoded.getFormattedAddress).toBeDefined(); + }); + it("has getStreetNumber method", () => { + expect(geocoded.getStreetNumber).toBeDefined(); + }); + it("has getStreetName method", () => { + expect(geocoded.getStreetName).toBeDefined(); + }); + it("has getSubLocality method", () => { + expect(geocoded.getSubLocality).toBeDefined(); + }); + it("has getLocality method", () => { + expect(geocoded.getLocality).toBeDefined(); + }); + it("has getPostalCode method", () => { + expect(geocoded.getPostalCode).toBeDefined(); + }); + it("has getRegion method", () => { + expect(geocoded.getRegion).toBeDefined(); + }); + it("has getCountry method", () => { + expect(geocoded.getCountry).toBeDefined(); + }); + it("has getCountryCode method", () => { + expect(geocoded.getCountryCode).toBeDefined(); + }); + it("has getTimezone method", () => { + expect(geocoded.getTimezone).toBeDefined(); + }); +}); + +describe("Geocoded returns data properly", () => { + let geocoded = Geocoded.create({ + coordinates: { + latitude: 38.8978378, + longitude: -77.0365123, + }, + formattedAddress: + "1600 Pennsylvania Avenue Northwest, Washington, DC 20050", + streetNumber: "1600", + streetName: "Pennsylvania Avenue Northwest", + locality: "Washington", + postalCode: "20050", + region: "DC", + country: "United States", + countryCode: "US", + timezone: "America/New_York", + }); + geocoded = geocoded.withBounds({ + latitudeSW: 38.89380528242933, + longitudeSW: -77.04317326462667, + latitudeNE: 38.90153071757068, + longitudeNE: -77.02993873537334, + }); + + it("returns proper coordinates", () => { + const expectedCoordinates = { + latitude: 38.8978378, + longitude: -77.0365123, + }; + expect(geocoded.getCoordinates()).toEqual(expectedCoordinates); + }); + + it("returns proper bounds", () => { + expect(geocoded.getBounds()).toEqual({ + latitudeSW: 38.89380528242933, + longitudeSW: -77.04317326462667, + latitudeNE: 38.90153071757068, + longitudeNE: -77.02993873537334, + }); + }); + + it("returns proper formatted address", () => { + expect(geocoded.getFormattedAddress()).toEqual( + "1600 Pennsylvania Avenue Northwest, Washington, DC 20050" + ); + }); + + it("returns proper street number", () => { + expect(geocoded.getStreetNumber()).toEqual("1600"); + }); + + it("returns proper street name", () => { + expect(geocoded.getStreetName()).toEqual("Pennsylvania Avenue Northwest"); + }); + + it("returns proper sublocality", () => { + expect(geocoded.getSubLocality()).toEqual(undefined); + }); + + it("returns proper locality", () => { + expect(geocoded.getLocality()).toEqual("Washington"); + }); + + it("returns proper postal code", () => { + expect(geocoded.getPostalCode()).toEqual("20050"); + }); + + it("returns proper region", () => { + expect(geocoded.getRegion()).toEqual("DC"); + }); + + it("returns proper country", () => { + expect(geocoded.getCountry()).toEqual("United States"); + }); + + it("returns proper country code", () => { + expect(geocoded.getCountryCode()).toEqual("US"); + }); + + it("returns proper timezone", () => { + expect(geocoded.getTimezone()).toEqual("America/New_York"); + }); +}); diff --git a/spec/GeocoderProviderFactory.spec.js b/spec/GeocoderProviderFactory.spec.js deleted file mode 100644 index 419cbd3..0000000 --- a/spec/GeocoderProviderFactory.spec.js +++ /dev/null @@ -1,25 +0,0 @@ -describe("Geocoder Factory method tests", function() { - var factory; - beforeEach(function() { - factory = new GeocoderJS.ProviderFactory(); - }); - - it ("expects ProviderFactory to exist", function() { - expect(factory).toBeDefined(); - }); - - it ("expects createProvider to return undefined for an unregistered provider", function() { - provider = factory.createProvider('nonexistantProvider'); - expect(provider).toBeUndefined(); - }); - - it ("expects createProvider() to return a GoogleAPIProvider", function() { - provider = factory.createProvider('google'); - expect(provider).toBeDefined(); - }); - - it ("expects createProvider() to return a MapquestProvider", function() { - provider = factory.createProvider('mapquest'); - expect(provider).toBeDefined(); - }); -}); diff --git a/spec/GeocoderProviderFactory.spec.ts b/spec/GeocoderProviderFactory.spec.ts new file mode 100644 index 0000000..821ba7b --- /dev/null +++ b/spec/GeocoderProviderFactory.spec.ts @@ -0,0 +1,23 @@ +import ProviderFactory, { + GeocoderProviderFactoryOptions, +} from "GeocoderProviderFactory"; +import { OpenStreetMapProviderOptionsInterface } from "provider"; + +describe("Geocoder Factory", () => { + it("expects createProvider method to return undefined for an unregistered provider", () => { + const provider = ProviderFactory.createProvider("nonexistentProvider"); + expect(provider).toBeUndefined(); + }); + + it("expects createProvider method to return a GeoPlugin Provider", () => { + const provider = ProviderFactory.createProvider("geoplugin"); + expect(provider).toBeDefined(); + }); + + it("expects createProvider method to return an OpenStreetMap Provider", () => { + const provider = ProviderFactory.createProvider(< + GeocoderProviderFactoryOptions & OpenStreetMapProviderOptionsInterface + >{ provider: "openstreetmap", userAgent: "Test User-Agent" }); + expect(provider).toBeDefined(); + }); +}); diff --git a/spec/helpers/GoogleHelper.js b/spec/helpers/GoogleHelper.js deleted file mode 100644 index 6d0680e..0000000 --- a/spec/helpers/GoogleHelper.js +++ /dev/null @@ -1,12 +0,0 @@ -google = {}; -google.maps = {}; -google.maps.Geocoder = (function() { - - function Geocoder() {} - - Geocoder.prototype.geocode = function(searchString) { - console.log('searched for ' + searchString); - }; - - return Geocoder; -})(); \ No newline at end of file diff --git a/spec/helpers/reporter.ts b/spec/helpers/reporter.ts new file mode 100644 index 0000000..282e389 --- /dev/null +++ b/spec/helpers/reporter.ts @@ -0,0 +1,10 @@ +import { SpecReporter, StacktraceOption } from "jasmine-spec-reporter"; + +jasmine.getEnv().clearReporters(); +jasmine.getEnv().addReporter( + new SpecReporter({ + spec: { + displayStacktrace: StacktraceOption.PRETTY, + }, + }) +); diff --git a/spec/provider/BingProvider.spec.ts b/spec/provider/BingProvider.spec.ts new file mode 100644 index 0000000..11bf7a5 --- /dev/null +++ b/spec/provider/BingProvider.spec.ts @@ -0,0 +1,169 @@ +import GeocoderJS from "GeocoderJS"; +import { BingGeocoded, BingProvider } from "provider"; +import ExternalLoader from "ExternalLoader"; +import AdminLevel from "AdminLevel"; +import setupPolly, { cleanRecording } from "../setupPolly"; + +describe("Bing Geocoder Provider", () => { + const pollyContext = setupPolly(); + + beforeEach(() => { + cleanRecording(pollyContext); + }); + + afterEach(async () => { + await pollyContext.polly.flush(); + }); + + it("expects API Key to be required on initiation", () => { + expect(() => new BingProvider(new ExternalLoader())).toThrowError( + Error, + 'An API key is required for the Bing provider. Please add it in the "apiKey" option.' + ); + }); + + it("expects to not support IP geolocation", () => { + const provider = GeocoderJS.createGeocoder({ + provider: "bing", + useSsl: true, + apiKey: "api_key", + }); + + expect(() => + provider?.geocode( + "66.147.244.214", + // eslint-disable-next-line @typescript-eslint/no-empty-function + () => {} + ) + ).toThrowError( + Error, + "The Bing provider does not support IP geolocation, only location geocoding." + ); + }); + + it("receives correct geocoding results", (done) => { + const provider = GeocoderJS.createGeocoder({ + provider: "bing", + useSsl: true, + apiKey: "api_key", + }); + + provider?.geocode( + "1600 Pennsylvania Ave, Washington, DC", + (results: BingGeocoded[]) => { + const geocoded = results[0]; + + expect(geocoded).toBeDefined(); + expect(geocoded.getCoordinates()).toEqual({ + latitude: 38.897639, + longitude: -77.036475, + }); + expect(geocoded.getBounds()).toEqual({ + latitudeSW: 38.89377628242932, + longitudeSW: -77.04309226192471, + latitudeNE: 38.901501717570675, + longitudeNE: -77.02985773807528, + }); + expect(geocoded.getFormattedAddress()).toEqual( + "1600 Pennsylvania Ave NW, Washington, DC 20006" + ); + expect(geocoded.getStreetNumber()).toEqual(undefined); + expect(geocoded.getStreetName()).toEqual("1600 Pennsylvania Ave NW"); + expect(geocoded.getSubLocality()).toEqual(undefined); + expect(geocoded.getLocality()).toEqual("Washington"); + expect(geocoded.getPostalCode()).toEqual("20006"); + expect(geocoded.getRegion()).toEqual("DC"); + expect(geocoded.getAdminLevels()).toEqual([ + AdminLevel.create({ + level: 1, + name: "DC", + }), + AdminLevel.create({ + level: 2, + name: "City of Washington", + }), + ]); + expect(geocoded.getCountry()).toEqual("United States"); + expect(geocoded.getCountryCode()).toEqual("US"); + expect(geocoded.getAttribution()).toEqual( + "Copyright © 2020 Microsoft and its suppliers. All rights reserved. This API cannot be accessed and the content and any results may not be used, reproduced or transmitted in any manner without express written permission from Microsoft Corporation." + ); + expect(geocoded.getPrecision()).toEqual("Medium"); + + done(); + } + ); + }); + + it("receives correct geodecoding results", (done) => { + const provider = GeocoderJS.createGeocoder({ + provider: "bing", + useSsl: true, + apiKey: "api_key", + }); + + provider?.geodecode(48.8631507, 2.388911, (results: BingGeocoded[]) => { + const geocoded = results[0]; + + expect(geocoded).toBeDefined(); + expect(geocoded.getCoordinates()).toEqual({ + latitude: 48.8631093, + longitude: 2.3887809, + }); + expect(geocoded.getBounds()).toEqual({ + latitudeSW: 48.85924658242932, + longitudeSW: 2.380952653445271, + latitudeNE: 48.866972017570674, + longitudeNE: 2.396609146554729, + }); + expect(geocoded.getFormattedAddress()).toEqual( + "8 Avenue Gambetta, 75020 Paris" + ); + expect(geocoded.getStreetNumber()).toEqual(undefined); + expect(geocoded.getStreetName()).toEqual("8 Avenue Gambetta"); + expect(geocoded.getSubLocality()).toEqual(undefined); + expect(geocoded.getLocality()).toEqual("Paris"); + expect(geocoded.getPostalCode()).toEqual("75020"); + expect(geocoded.getRegion()).toEqual("Île-de-France"); + expect(geocoded.getAdminLevels()).toEqual([ + AdminLevel.create({ + level: 1, + name: "Île-de-France", + }), + AdminLevel.create({ + level: 2, + name: "Paris", + }), + ]); + expect(geocoded.getCountry()).toEqual("France"); + expect(geocoded.getCountryCode()).toEqual("FR"); + expect(geocoded.getAttribution()).toEqual( + "Copyright © 2020 Microsoft and its suppliers. All rights reserved. This API cannot be accessed and the content and any results may not be used, reproduced or transmitted in any manner without express written permission from Microsoft Corporation." + ); + expect(geocoded.getPrecision()).toEqual("High"); + + done(); + }); + }); + + it("receives error when the API key is bad", (done) => { + const provider = GeocoderJS.createGeocoder({ + provider: "bing", + useSsl: true, + apiKey: "api_key", + }); + + provider?.geocode( + "1600 Pennsylvania Ave, Washington, DC", + () => { + done(); + }, + (error) => { + expect(error.message).toEqual( + "Access was denied. You may have entered your credentials incorrectly, or you might not have access to the requested resource or operation." + ); + done(); + } + ); + }); +}); diff --git a/spec/provider/ChainProvider.spec.ts b/spec/provider/ChainProvider.spec.ts new file mode 100644 index 0000000..c375d0f --- /dev/null +++ b/spec/provider/ChainProvider.spec.ts @@ -0,0 +1,422 @@ +import GeocoderJS from "GeocoderJS"; +import Geocoded from "Geocoded"; +import AdminLevel from "AdminLevel"; +import setupPolly, { cleanRecording } from "../setupPolly"; + +const expectGeocodedYandex = (geocoded: Geocoded) => { + expect(geocoded).toBeDefined(); + expect(geocoded.getCoordinates()).toEqual({ + latitude: 38.895512, + longitude: -77.033608, + }); + expect(geocoded.getBounds()).toEqual({ + latitudeSW: 38.890612, + longitudeSW: -77.058105, + latitudeNE: 38.905248, + longitudeNE: -77.012426, + }); + expect(geocoded.getFormattedAddress()).toEqual(undefined); + expect(geocoded.getStreetNumber()).toEqual(undefined); + expect(geocoded.getStreetName()).toEqual("Pennsylvania Avenue Northwest"); + expect(geocoded.getSubLocality()).toEqual(undefined); + expect(geocoded.getLocality()).toEqual("Washington"); + expect(geocoded.getPostalCode()).toEqual(undefined); + expect(geocoded.getRegion()).toEqual("District of Columbia"); + expect(geocoded.getAdminLevels()).toEqual([ + AdminLevel.create({ level: 1, name: "District of Columbia" }), + ]); + expect(geocoded.getCountry()).toEqual("United States of America"); + expect(geocoded.getCountryCode()).toEqual("US"); +}; + +const expectGeodecodedYandex = (geocoded: Geocoded) => { + expect(geocoded).toBeDefined(); + expect(geocoded.getCoordinates()).toEqual({ + latitude: 48.8631, + longitude: 2.388899, + }); + expect(geocoded.getBounds()).toEqual({ + latitudeSW: 48.860391, + longitudeSW: 2.384794, + latitudeNE: 48.865808, + longitudeNE: 2.393004, + }); + expect(geocoded.getFormattedAddress()).toEqual(undefined); + expect(geocoded.getStreetNumber()).toEqual("10"); + expect(geocoded.getStreetName()).toEqual("Avenue Gambetta"); + expect(geocoded.getSubLocality()).toEqual("20e Arrondissement"); + expect(geocoded.getLocality()).toEqual("Paris"); + expect(geocoded.getPostalCode()).toEqual(undefined); + expect(geocoded.getRegion()).toEqual("Île-de-France"); + expect(geocoded.getAdminLevels()).toEqual([ + AdminLevel.create({ level: 1, name: "Île-de-France" }), + ]); + expect(geocoded.getCountry()).toEqual("France"); + expect(geocoded.getCountryCode()).toEqual("FR"); +}; + +const expectGeocodedOpenStreetMap = (geocoded: Geocoded) => { + expect(geocoded).toBeDefined(); + expect(geocoded.getCoordinates()).toEqual({ + latitude: 38.8636383, + longitude: -76.9463651, + }); + expect(geocoded.getBounds()).toEqual({ + latitudeSW: 38.8633822, + longitudeSW: -76.9467576, + latitudeNE: 38.8637409, + longitudeNE: -76.945632, + }); + expect(geocoded.getFormattedAddress()).toEqual(undefined); + expect(geocoded.getStreetNumber()).toEqual(undefined); + expect(geocoded.getStreetName()).toEqual("Pennsylvania Avenue"); + expect(geocoded.getSubLocality()).toEqual(undefined); + expect(geocoded.getLocality()).toEqual("Dillon Park"); + expect(geocoded.getPostalCode()).toEqual("20746-8001"); + expect(geocoded.getRegion()).toEqual("Washington, D.C."); + expect(geocoded.getAdminLevels()).toEqual([ + AdminLevel.create({ level: 1, name: "Washington, D.C." }), + AdminLevel.create({ level: 2, name: "Prince George's County" }), + ]); + expect(geocoded.getCountry()).toEqual("United States of America"); + expect(geocoded.getCountryCode()).toEqual("us"); +}; + +const expectGeodecodedOpenStreetMap = (geocoded: Geocoded) => { + expect(geocoded.getCoordinates()).toEqual({ + latitude: 48.863744499999996, + longitude: 2.3911562136123106, + }); + expect(geocoded.getBounds()).toEqual({ + latitudeSW: 48.8625929, + longitudeSW: 2.3877078, + latitudeNE: 48.8648832, + longitudeNE: 2.3956964, + }); + expect(geocoded.getFormattedAddress()).toEqual(undefined); + expect(geocoded.getStreetNumber()).toEqual(undefined); + expect(geocoded.getStreetName()).toEqual(undefined); + expect(geocoded.getSubLocality()).toEqual("Quartier du Père-Lachaise"); + expect(geocoded.getLocality()).toEqual("Paris"); + expect(geocoded.getPostalCode()).toEqual("75020"); + expect(geocoded.getRegion()).toEqual("Ile-de-France"); + expect(geocoded.getAdminLevels()).toEqual([ + AdminLevel.create({ level: 1, name: "Ile-de-France" }), + AdminLevel.create({ level: 2, name: "Paris" }), + ]); + expect(geocoded.getCountry()).toEqual("France"); + expect(geocoded.getCountryCode()).toEqual("fr"); +}; + +describe("Chain Geocoder Provider", () => { + const pollyContext = setupPolly(); + + beforeEach(() => { + cleanRecording(pollyContext); + }); + + afterEach(async () => { + await pollyContext.polly.flush(); + }); + + it("receives correct geocoding results (first provider OK)", (done) => { + const yandexGeocoder = GeocoderJS.createGeocoder({ + provider: "yandex", + useSsl: true, + apiKey: "api_key", + }); + const openStreetMapGeocoder = GeocoderJS.createGeocoder({ + provider: "openstreetmap", + useSsl: true, + userAgent: "GeocoderJS Example", + }); + const provider = GeocoderJS.createGeocoder({ + provider: "chain", + providers: [yandexGeocoder, openStreetMapGeocoder], + }); + + provider?.geocode( + { text: "1600 Pennsylvania Ave, Washington, DC", locale: "en_US" }, + (results: Geocoded[]) => { + const geocoded = results[0]; + + expectGeocodedYandex(geocoded); + + done(); + } + ); + }); + + it("receives correct geocoding results (first provider KO)", (done) => { + const yandexGeocoder = GeocoderJS.createGeocoder({ + provider: "yandex", + useSsl: true, + apiKey: "api_key", + }); + const openStreetMapGeocoder = GeocoderJS.createGeocoder({ + provider: "openstreetmap", + useSsl: true, + userAgent: "GeocoderJS Example", + }); + const provider = GeocoderJS.createGeocoder({ + provider: "chain", + providers: [yandexGeocoder, openStreetMapGeocoder], + }); + + provider?.geocode( + { text: "1600 Pennsylvania Ave, Washington, DC", locale: "en_US" }, + (results: Geocoded[]) => { + const geocoded = results[0]; + + expectGeocodedOpenStreetMap(geocoded); + + done(); + } + ); + }); + + it("receives correct geocoding results (parallelize - first provider OK)", (done) => { + const yandexGeocoder = GeocoderJS.createGeocoder({ + provider: "yandex", + useSsl: true, + apiKey: "api_key", + }); + const openStreetMapGeocoder = GeocoderJS.createGeocoder({ + provider: "openstreetmap", + useSsl: true, + userAgent: "GeocoderJS Example", + }); + const provider = GeocoderJS.createGeocoder({ + provider: "chain", + providers: [yandexGeocoder, openStreetMapGeocoder], + parallelize: true, + }); + + provider?.geocode( + { text: "1600 Pennsylvania Ave, Washington, DC", locale: "en_US" }, + (results: Geocoded[]) => { + const geocoded = results[0]; + + expectGeocodedYandex(geocoded); + + done(); + } + ); + }); + + it("receives correct geocoding results (parallelize - first provider KO)", (done) => { + const yandexGeocoder = GeocoderJS.createGeocoder({ + provider: "yandex", + useSsl: true, + apiKey: "api_key", + }); + const openStreetMapGeocoder = GeocoderJS.createGeocoder({ + provider: "openstreetmap", + useSsl: true, + userAgent: "GeocoderJS Example", + }); + const provider = GeocoderJS.createGeocoder({ + provider: "chain", + providers: [yandexGeocoder, openStreetMapGeocoder], + parallelize: true, + }); + + provider?.geocode( + { text: "1600 Pennsylvania Ave, Washington, DC", locale: "en_US" }, + (results: Geocoded[]) => { + const geocoded = results[0]; + + expectGeocodedOpenStreetMap(geocoded); + + done(); + } + ); + }); + + it("receives correct geocoding results (first - first provider OK)", (done) => { + const yandexGeocoder = GeocoderJS.createGeocoder({ + provider: "yandex", + useSsl: true, + apiKey: "api_key", + }); + const openStreetMapGeocoder = GeocoderJS.createGeocoder({ + provider: "openstreetmap", + useSsl: true, + userAgent: "GeocoderJS Example", + }); + const provider = GeocoderJS.createGeocoder({ + provider: "chain", + providers: [yandexGeocoder, openStreetMapGeocoder], + first: true, + }); + + provider?.geocode( + { text: "1600 Pennsylvania Ave, Washington, DC", locale: "en_US" }, + (results: Geocoded[]) => { + const geocoded = results[0]; + + expectGeocodedOpenStreetMap(geocoded); + + done(); + } + ); + }); + + it("receives correct geodecoding results (first provider OK)", (done) => { + const yandexGeocoder = GeocoderJS.createGeocoder({ + provider: "yandex", + useSsl: true, + apiKey: "api_key", + }); + const openStreetMapGeocoder = GeocoderJS.createGeocoder({ + provider: "openstreetmap", + useSsl: true, + userAgent: "GeocoderJS Example", + }); + const provider = GeocoderJS.createGeocoder({ + provider: "chain", + providers: [yandexGeocoder, openStreetMapGeocoder], + }); + + provider?.geodecode( + { + coordinates: { latitude: 48.8631507, longitude: 2.388911 }, + locale: "en_US", + }, + (results: Geocoded[]) => { + const geocoded = results[0]; + + expectGeodecodedYandex(geocoded); + + done(); + } + ); + }); + + it("receives correct geodecoding results (first provider KO)", (done) => { + const yandexGeocoder = GeocoderJS.createGeocoder({ + provider: "yandex", + useSsl: true, + apiKey: "api_key", + }); + const openStreetMapGeocoder = GeocoderJS.createGeocoder({ + provider: "openstreetmap", + useSsl: true, + userAgent: "GeocoderJS Example", + }); + const provider = GeocoderJS.createGeocoder({ + provider: "chain", + providers: [yandexGeocoder, openStreetMapGeocoder], + }); + + provider?.geodecode( + { + coordinates: { latitude: 48.8631507, longitude: 2.388911 }, + locale: "en_US", + }, + (results: Geocoded[]) => { + const geocoded = results[0]; + + expectGeodecodedOpenStreetMap(geocoded); + + done(); + } + ); + }); + + it("receives correct geodecoding results (parallelize - first provider OK)", (done) => { + const yandexGeocoder = GeocoderJS.createGeocoder({ + provider: "yandex", + useSsl: true, + apiKey: "api_key", + }); + const openStreetMapGeocoder = GeocoderJS.createGeocoder({ + provider: "openstreetmap", + useSsl: true, + userAgent: "GeocoderJS Example", + }); + const provider = GeocoderJS.createGeocoder({ + provider: "chain", + providers: [yandexGeocoder, openStreetMapGeocoder], + parallelize: true, + }); + + provider?.geodecode( + { + coordinates: { latitude: 48.8631507, longitude: 2.388911 }, + locale: "en_US", + }, + (results: Geocoded[]) => { + const geocoded = results[0]; + + expectGeodecodedYandex(geocoded); + + done(); + } + ); + }); + + it("receives correct geodecoding results (parallelize - first provider KO)", (done) => { + const yandexGeocoder = GeocoderJS.createGeocoder({ + provider: "yandex", + useSsl: true, + apiKey: "api_key", + }); + const openStreetMapGeocoder = GeocoderJS.createGeocoder({ + provider: "openstreetmap", + useSsl: true, + userAgent: "GeocoderJS Example", + }); + const provider = GeocoderJS.createGeocoder({ + provider: "chain", + providers: [yandexGeocoder, openStreetMapGeocoder], + parallelize: true, + }); + + provider?.geodecode( + { + coordinates: { latitude: 48.8631507, longitude: 2.388911 }, + locale: "en_US", + }, + (results: Geocoded[]) => { + const geocoded = results[0]; + + expectGeodecodedOpenStreetMap(geocoded); + + done(); + } + ); + }); + + it("receives correct geodecoding results (first - first provider OK)", (done) => { + const yandexGeocoder = GeocoderJS.createGeocoder({ + provider: "yandex", + useSsl: true, + apiKey: "api_key", + }); + const openStreetMapGeocoder = GeocoderJS.createGeocoder({ + provider: "openstreetmap", + useSsl: true, + userAgent: "GeocoderJS Example", + }); + const provider = GeocoderJS.createGeocoder({ + provider: "chain", + providers: [yandexGeocoder, openStreetMapGeocoder], + first: true, + }); + + provider?.geodecode( + { + coordinates: { latitude: 48.8631507, longitude: 2.388911 }, + locale: "en_US", + }, + (results: Geocoded[]) => { + const geocoded = results[0]; + + expectGeodecodedOpenStreetMap(geocoded); + + done(); + } + ); + }); +}); diff --git a/spec/provider/GeoPluginProvider.spec.ts b/spec/provider/GeoPluginProvider.spec.ts new file mode 100644 index 0000000..2770ed8 --- /dev/null +++ b/spec/provider/GeoPluginProvider.spec.ts @@ -0,0 +1,125 @@ +import GeocoderJS from "GeocoderJS"; +import { GeoPluginGeocoded } from "provider"; +import AdminLevel from "AdminLevel"; +import setupPolly, { cleanRecording } from "../setupPolly"; + +describe("GeoPlugin Geocoder Provider", () => { + const pollyContext = setupPolly(); + + beforeEach(() => { + cleanRecording(pollyContext); + }); + + afterEach(async () => { + await pollyContext.polly.flush(); + }); + + it("expects to not support location geocoding", () => { + const provider = GeocoderJS.createGeocoder({ + provider: "geoplugin", + }); + + expect(() => + provider?.geocode( + "1600 Pennsylvania Ave, Washington, DC", + // eslint-disable-next-line @typescript-eslint/no-empty-function + () => {} + ) + ).toThrowError( + Error, + "The GeoPlugin provider does not support location geocoding, only IP geolocation." + ); + }); + + it("expects to not support reverse geocoding", () => { + const provider = GeocoderJS.createGeocoder({ + provider: "geoplugin", + }); + + expect(() => + provider?.geodecode( + 48.8631507, + 2.388911, + // eslint-disable-next-line @typescript-eslint/no-empty-function + () => {} + ) + ).toThrowError( + Error, + "The GeoPlugin provider does not support reverse geocoding." + ); + }); + + it("receives correct IP geolocation results", (done) => { + const provider = GeocoderJS.createGeocoder({ + provider: "geoplugin", + }); + + provider?.geocode("190.226.155.134", (results: GeoPluginGeocoded[]) => { + const geocoded = results[0]; + + expect(geocoded).toBeDefined(); + expect(geocoded.getCoordinates()).toEqual({ + latitude: -32.2167, + longitude: -58.1333, + }); + expect(geocoded.getBounds()).toEqual(undefined); + expect(geocoded.getFormattedAddress()).toEqual(undefined); + expect(geocoded.getStreetNumber()).toEqual(undefined); + expect(geocoded.getStreetName()).toEqual(undefined); + expect(geocoded.getSubLocality()).toEqual(undefined); + expect(geocoded.getLocality()).toEqual("Colon"); + expect(geocoded.getPostalCode()).toEqual(undefined); + expect(geocoded.getRegion()).toEqual("Entre Rios"); + expect(geocoded.getAdminLevels()).toEqual([ + AdminLevel.create({ level: 1, name: "Entre Rios", code: "E" }), + ]); + expect(geocoded.getCountry()).toEqual("Argentina"); + expect(geocoded.getCountryCode()).toEqual("AR"); + + done(); + }); + }); + + it("receives correct IP geolocation localhost results", (done) => { + const provider = GeocoderJS.createGeocoder({ + provider: "geoplugin", + }); + + provider?.geocode("127.0.0.1", (results: GeoPluginGeocoded[]) => { + const geocoded = results[0]; + + expect(geocoded).toBeDefined(); + expect(geocoded.getCoordinates()).toEqual(undefined); + expect(geocoded.getBounds()).toEqual(undefined); + expect(geocoded.getFormattedAddress()).toEqual(undefined); + expect(geocoded.getStreetNumber()).toEqual(undefined); + expect(geocoded.getStreetName()).toEqual(undefined); + expect(geocoded.getSubLocality()).toEqual(undefined); + expect(geocoded.getLocality()).toEqual("localhost"); + expect(geocoded.getPostalCode()).toEqual(undefined); + expect(geocoded.getRegion()).toEqual(undefined); + expect(geocoded.getAdminLevels()).toEqual([]); + expect(geocoded.getCountry()).toEqual("localhost"); + expect(geocoded.getCountryCode()).toEqual(undefined); + + done(); + }); + }); + + it("receives error when IP is not found", (done) => { + const provider = GeocoderJS.createGeocoder({ + provider: "geoplugin", + }); + + provider?.geocode( + "192.168.1.10", + () => { + done(); + }, + (error) => { + expect(error.message).toEqual("An error has occurred. Status: 404."); + done(); + } + ); + }); +}); diff --git a/spec/provider/GoogleMapsProvider.spec.ts b/spec/provider/GoogleMapsProvider.spec.ts new file mode 100644 index 0000000..e5ad630 --- /dev/null +++ b/spec/provider/GoogleMapsProvider.spec.ts @@ -0,0 +1,199 @@ +import GeocoderJS from "GeocoderJS"; +import { GoogleMapsGeocoded, GoogleMapsProvider } from "provider"; +import ExternalLoader from "ExternalLoader"; +import AdminLevel from "AdminLevel"; +import setupPolly, { cleanRecording } from "../setupPolly"; + +describe("Google Maps Geocoder Provider", () => { + const pollyContext = setupPolly(); + + beforeEach(() => { + cleanRecording(pollyContext); + }); + + afterEach(async () => { + await pollyContext.polly.flush(); + }); + + it("expects API Key or client ID to be required on initiation", () => { + expect(() => new GoogleMapsProvider(new ExternalLoader())).toThrowError( + Error, + 'An API key or a client ID is required for the Google Maps provider. Please add it in the "apiKey" or the "clientId" option.' + ); + }); + + it("expects secret to be required on initiation when client ID is set", () => { + expect( + () => + new GoogleMapsProvider(new ExternalLoader(), { + clientId: "client_id", + }) + ).toThrowError( + Error, + 'An URL signing secret is required if you use a client ID (Premium only). Please add it in the "secret" option.' + ); + }); + + it("expects country codes option to have only one value when set", () => { + expect( + () => + new GoogleMapsProvider(new ExternalLoader(), { + apiKey: "api_key", + countryCodes: ["fr", "uk"], + }) + ).toThrowError( + Error, + 'The "countryCodes" option must have only one country code top-level domain.' + ); + }); + + it("expects to not support IP geolocation", () => { + const provider = GeocoderJS.createGeocoder({ + provider: "googlemaps", + useSsl: true, + apiKey: "api_key", + }); + + expect(() => + provider?.geocode( + "66.147.244.214", + // eslint-disable-next-line @typescript-eslint/no-empty-function + () => {} + ) + ).toThrowError( + Error, + "The GoogleMaps provider does not support IP geolocation, only location geocoding." + ); + }); + + it("receives correct geocoding results", (done) => { + const provider = GeocoderJS.createGeocoder({ + provider: "googlemaps", + useSsl: true, + apiKey: "api_key", + }); + + provider?.geocode("1600 Pennsylvania Ave, Washington, DC", (results) => { + const geocoded = results[0]; + + expect(geocoded).toBeDefined(); + expect(geocoded.getCoordinates()).toEqual({ + latitude: 38.8976633, + longitude: -77.0365739, + }); + expect(geocoded.getBounds()).toEqual({ + latitudeSW: 38.8973063, + longitudeSW: -77.03795749999999, + latitudeNE: 38.8979044, + longitudeNE: -77.0355124, + }); + expect(geocoded.getFormattedAddress()).toEqual( + "1600 Pennsylvania Avenue NW, Washington, DC 20500, USA" + ); + expect(geocoded.getStreetNumber()).toEqual("1600"); + expect(geocoded.getStreetName()).toEqual("Pennsylvania Avenue Northwest"); + expect(geocoded.getSubLocality()).toEqual(undefined); + expect(geocoded.getLocality()).toEqual("Washington"); + expect(geocoded.getPostalCode()).toEqual("20500"); + expect(geocoded.getRegion()).toEqual("District of Columbia"); + expect(geocoded.getAdminLevels()).toEqual([ + AdminLevel.create({ + level: 1, + name: "District of Columbia", + code: "DC", + }), + ]); + expect(geocoded.getCountry()).toEqual("United States"); + expect(geocoded.getCountryCode()).toEqual("US"); + expect(geocoded.getPlaceId()).toEqual("ChIJGVtI4by3t4kRr51d_Qm_x58"); + expect(geocoded.getResultType()).toEqual([ + "establishment", + "point_of_interest", + "premise", + ]); + expect(geocoded.getLocationType()).toEqual("ROOFTOP"); + expect(geocoded.getPolitical()).toEqual("United States"); + expect(geocoded.getNeighborhood()).toEqual("Northwest Washington"); + + done(); + }); + }); + + it("receives correct geodecoding results", (done) => { + const provider = GeocoderJS.createGeocoder({ + provider: "googlemaps", + useSsl: true, + apiKey: "api_key", + }); + + provider?.geodecode( + 48.8631507, + 2.388911, + (results: GoogleMapsGeocoded[]) => { + const geocoded = results[0]; + + expect(geocoded).toBeDefined(); + expect(geocoded.getCoordinates()).toEqual({ + latitude: 48.8631361, + longitude: 2.3889219, + }); + expect(geocoded.getBounds()).toEqual({ + latitudeSW: 48.8617871197085, + longitudeSW: 2.387572919708498, + latitudeNE: 48.8644850802915, + longitudeNE: 2.390270880291502, + }); + expect(geocoded.getFormattedAddress()).toEqual( + "12 Avenue Gambetta, 75020 Paris, France" + ); + expect(geocoded.getStreetNumber()).toEqual("12"); + expect(geocoded.getStreetName()).toEqual("Avenue Gambetta"); + expect(geocoded.getSubLocality()).toEqual(undefined); + expect(geocoded.getLocality()).toEqual("Paris"); + expect(geocoded.getPostalCode()).toEqual("75020"); + expect(geocoded.getRegion()).toEqual("Île-de-France"); + expect(geocoded.getAdminLevels()).toEqual([ + AdminLevel.create({ + level: 2, + name: "Arrondissement de Paris", + code: "Arrondissement de Paris", + }), + AdminLevel.create({ + level: 1, + name: "Île-de-France", + code: "IDF", + }), + ]); + expect(geocoded.getCountry()).toEqual("France"); + expect(geocoded.getCountryCode()).toEqual("FR"); + expect(geocoded.getPlaceId()).toEqual("ChIJ9aLL3vJt5kcR61GCze3v6fg"); + expect(geocoded.getResultType()).toEqual(["street_address"]); + expect(geocoded.getLocationType()).toEqual("ROOFTOP"); + expect(geocoded.getPolitical()).toEqual("France"); + + done(); + } + ); + }); + + it("receives error when the API key is bad", (done) => { + const provider = GeocoderJS.createGeocoder({ + provider: "googlemaps", + useSsl: true, + apiKey: "api_key", + }); + + provider?.geocode( + "1600 Pennsylvania Ave, Washington, DC", + () => { + done(); + }, + (error) => { + expect(error.message).toEqual( + "Request has been denied: The provided API key is invalid." + ); + done(); + } + ); + }); +}); diff --git a/spec/provider/MapQuestProvider.spec.ts b/spec/provider/MapQuestProvider.spec.ts new file mode 100644 index 0000000..3545ab8 --- /dev/null +++ b/spec/provider/MapQuestProvider.spec.ts @@ -0,0 +1,252 @@ +import GeocoderJS from "GeocoderJS"; +import { MapQuestGeocoded, MapQuestProvider } from "provider"; +import ExternalLoader from "ExternalLoader"; +import AdminLevel from "AdminLevel"; +import setupPolly, { cleanRecording } from "../setupPolly"; + +describe("MapQuest Geocoder Provider", () => { + const pollyContext = setupPolly(); + + beforeEach(() => { + cleanRecording(pollyContext); + }); + + afterEach(async () => { + await pollyContext.polly.flush(); + }); + + it("expects API Key to be required on initiation", () => { + expect(() => new MapQuestProvider(new ExternalLoader())).toThrowError( + Error, + 'An API key is required for the MapQuest provider. Please add it in the "apiKey" option.' + ); + }); + + it("expects to not support IP geolocation", () => { + const provider = GeocoderJS.createGeocoder({ + provider: "mapquest", + useSsl: true, + apiKey: "api_key", + }); + + expect(() => + provider?.geocode( + "66.147.244.214", + // eslint-disable-next-line @typescript-eslint/no-empty-function + () => {} + ) + ).toThrowError( + Error, + "The MapQuest provider does not support IP geolocation, only location geocoding." + ); + }); + + it("receives correct geocoding results", (done) => { + const provider = GeocoderJS.createGeocoder({ + provider: "mapquest", + useSsl: true, + apiKey: "api_key", + }); + + provider?.geocode( + "1600 Pennsylvania Ave, Washington, DC", + (results: MapQuestGeocoded[]) => { + const geocoded = results[0]; + + expect(geocoded).toBeDefined(); + expect(geocoded.getCoordinates()).toEqual({ + latitude: 38.895206, + longitude: -77.036515, + }); + expect(geocoded.getBounds()).toEqual(undefined); + expect(geocoded.getFormattedAddress()).toEqual(undefined); + expect(geocoded.getStreetNumber()).toEqual(undefined); + expect(geocoded.getStreetName()).toEqual("1600 Pennsylvania Ave"); + expect(geocoded.getSubLocality()).toEqual(""); + expect(geocoded.getLocality()).toEqual("Washington"); + expect(geocoded.getPostalCode()).toEqual(""); + expect(geocoded.getRegion()).toEqual("District Of Columbia"); + expect(geocoded.getAdminLevels()).toEqual([ + AdminLevel.create({ level: 1, name: "DC", code: "DC" }), + AdminLevel.create({ level: 2, name: "District Of Columbia" }), + ]); + expect(geocoded.getCountry()).toEqual("US"); + expect(geocoded.getCountryCode()).toEqual("US"); + expect(geocoded.getPrecision()).toEqual("POINT"); + expect(geocoded.getPrecisionCode()).toEqual("P1AAA"); + expect(geocoded.getMapUrl()).toEqual( + "http://www.mapquestapi.com/staticmap/v5/map?type=map&size=225,160&locations=38.895206,-77.036515|marker-sm-50318A-1&scalebar=true&zoom=15&rand=1195279033" + ); + expect(geocoded.getAttribution()).toEqual("© 2020 MapQuest, Inc."); + + done(); + } + ); + }); + + it("receives correct geocoding results using POST and open domain", (done) => { + const provider = GeocoderJS.createGeocoder({ + provider: "mapquest", + useSsl: true, + apiKey: "api_key", + method: "POST", + openDomain: true, + }); + + provider?.geocode( + "1600 Pennsylvania Ave, Washington, DC", + (results: MapQuestGeocoded[]) => { + const geocoded = results[0]; + + expect(geocoded).toBeDefined(); + expect(geocoded.getCoordinates()).toEqual({ + latitude: 38.895854, + longitude: -77.030713, + }); + expect(geocoded.getBounds()).toEqual(undefined); + expect(geocoded.getFormattedAddress()).toEqual(undefined); + expect(geocoded.getStreetNumber()).toEqual(undefined); + expect(geocoded.getStreetName()).toEqual(""); + expect(geocoded.getSubLocality()).toEqual("Penn Quarter"); + expect(geocoded.getLocality()).toEqual("Washington"); + expect(geocoded.getPostalCode()).toEqual("20006"); + expect(geocoded.getRegion()).toEqual(""); + expect(geocoded.getAdminLevels()).toEqual([ + AdminLevel.create({ level: 1, name: "District of Columbia" }), + ]); + expect(geocoded.getCountry()).toEqual("US"); + expect(geocoded.getCountryCode()).toEqual("US"); + expect(geocoded.getPrecision()).toEqual("STREET"); + expect(geocoded.getPrecisionCode()).toEqual("B1XAX"); + expect(geocoded.getMapUrl()).toEqual( + "http://open.mapquestapi.com/staticmap/v5/map?type=map&size=225,160&locations=38.8958536,-77.0307129|marker-sm-50318A-1&scalebar=true&zoom=15&rand=942576975" + ); + expect(geocoded.getAttribution()).toEqual("© 2020 MapQuest, Inc."); + + done(); + } + ); + }); + + it("receives correct geodecoding results", (done) => { + const provider = GeocoderJS.createGeocoder({ + provider: "mapquest", + useSsl: true, + apiKey: "api_key", + }); + + provider?.geodecode(48.8631507, 2.388911, (results: MapQuestGeocoded[]) => { + const geocoded = results[0]; + + expect(geocoded).toBeDefined(); + expect(geocoded.getCoordinates()).toEqual({ + latitude: 48.863116, + longitude: 2.38878, + }); + expect(geocoded.getBounds()).toEqual(undefined); + expect(geocoded.getFormattedAddress()).toEqual(undefined); + expect(geocoded.getStreetNumber()).toEqual(undefined); + expect(geocoded.getStreetName()).toEqual("8 Avenue Gambetta"); + expect(geocoded.getSubLocality()).toEqual(""); + expect(geocoded.getLocality()).toEqual("Paris"); + expect(geocoded.getPostalCode()).toEqual("75020"); + expect(geocoded.getRegion()).toEqual(""); + expect(geocoded.getAdminLevels()).toEqual([ + AdminLevel.create({ level: 1, name: "Ile-de-France" }), + ]); + expect(geocoded.getCountry()).toEqual("FR"); + expect(geocoded.getCountryCode()).toEqual("FR"); + expect(geocoded.getPrecision()).toEqual("POINT"); + expect(geocoded.getPrecisionCode()).toEqual("P1AAA"); + expect(geocoded.getMapUrl()).toEqual( + "http://www.mapquestapi.com/staticmap/v5/map?type=map&size=225,160&locations=48.8631163,2.38878|marker-sm-50318A-1&scalebar=true&zoom=15&rand=1980906355" + ); + expect(geocoded.getAttribution()).toEqual("© 2020 MapQuest, Inc."); + + done(); + }); + }); + + it("receives correct geodecoding results using POST and open domain", (done) => { + const provider = GeocoderJS.createGeocoder({ + provider: "mapquest", + useSsl: true, + apiKey: "api_key", + method: "POST", + openDomain: true, + }); + + provider?.geodecode(48.8631507, 2.388911, (results: MapQuestGeocoded[]) => { + const geocoded = results[0]; + + expect(geocoded).toBeDefined(); + expect(geocoded.getCoordinates()).toEqual({ + latitude: 48.863116, + longitude: 2.38878, + }); + expect(geocoded.getBounds()).toEqual(undefined); + expect(geocoded.getFormattedAddress()).toEqual(undefined); + expect(geocoded.getStreetNumber()).toEqual(undefined); + expect(geocoded.getStreetName()).toEqual("8 Avenue Gambetta"); + expect(geocoded.getSubLocality()).toEqual(""); + expect(geocoded.getLocality()).toEqual("Paris"); + expect(geocoded.getPostalCode()).toEqual("75020"); + expect(geocoded.getRegion()).toEqual(""); + expect(geocoded.getAdminLevels()).toEqual([ + AdminLevel.create({ level: 1, name: "Ile-de-France" }), + ]); + expect(geocoded.getCountry()).toEqual("FR"); + expect(geocoded.getCountryCode()).toEqual("FR"); + expect(geocoded.getPrecision()).toEqual("POINT"); + expect(geocoded.getPrecisionCode()).toEqual("P1AAA"); + expect(geocoded.getMapUrl()).toEqual( + "http://open.mapquestapi.com/staticmap/v5/map?type=map&size=225,160&locations=48.8631163,2.38878|marker-sm-50318A-1&scalebar=true&zoom=15&rand=-1000393223" + ); + expect(geocoded.getAttribution()).toEqual("© 2020 MapQuest, Inc."); + + done(); + }); + }); + + it("receives error when parameters are missing", (done) => { + const provider = GeocoderJS.createGeocoder({ + provider: "mapquest", + useSsl: true, + apiKey: "api_key", + }); + + provider?.geocode( + { location: {} }, + () => { + done(); + }, + (error) => { + expect(error.message).toEqual( + "An error has occurred (400): Illegal argument from request: Insufficient info for location" + ); + done(); + } + ); + }); + + it("receives error when the API key is bad", (done) => { + const provider = GeocoderJS.createGeocoder({ + provider: "mapquest", + useSsl: true, + apiKey: "api_key", + }); + + provider?.geocode( + "1600 Pennsylvania Ave, Washington, DC", + () => { + done(); + }, + (error) => { + expect(error.message).toEqual( + "Received HTTP status code 403 when attempting geocoding request." + ); + done(); + } + ); + }); +}); diff --git a/spec/provider/MapboxProvider.spec.ts b/spec/provider/MapboxProvider.spec.ts new file mode 100644 index 0000000..2c6a162 --- /dev/null +++ b/spec/provider/MapboxProvider.spec.ts @@ -0,0 +1,175 @@ +import GeocoderJS from "GeocoderJS"; +import { MapboxGeocoded, MapboxProvider } from "provider"; +import ExternalLoader from "ExternalLoader"; +import AdminLevel from "AdminLevel"; +import setupPolly, { cleanRecording } from "../setupPolly"; + +describe("Mapbox Geocoder Provider", () => { + const pollyContext = setupPolly(); + + beforeEach(() => { + cleanRecording(pollyContext); + }); + + afterEach(async () => { + await pollyContext.polly.flush(); + }); + + it("expects API Key to be required on initiation", () => { + expect(() => new MapboxProvider(new ExternalLoader())).toThrowError( + Error, + 'An API key is required for the Mapbox provider. Please add it in the "apiKey" option.' + ); + }); + + it("expects to not support IP geolocation", () => { + const provider = GeocoderJS.createGeocoder({ + provider: "mapbox", + useSsl: true, + apiKey: "api_key", + }); + + expect(() => + provider?.geocode( + "66.147.244.214", + // eslint-disable-next-line @typescript-eslint/no-empty-function + () => {} + ) + ).toThrowError( + Error, + "The Mapbox provider does not support IP geolocation, only location geocoding." + ); + }); + + it("receives correct geocoding results", (done) => { + const provider = GeocoderJS.createGeocoder({ + provider: "mapbox", + useSsl: true, + apiKey: "api_key", + }); + + provider?.geocode("1600 Pennsylvania Ave, Washington, DC", (results) => { + const geocoded = results[0]; + + expect(geocoded).toBeDefined(); + expect(geocoded.getCoordinates()).toEqual({ + latitude: 38.87925, + longitude: -76.98204, + }); + expect(geocoded.getBounds()).toEqual(undefined); + expect(geocoded.getFormattedAddress()).toEqual( + "1600 Pennsylvania Ave SE, Washington, District of Columbia 20003, United States" + ); + expect(geocoded.getStreetNumber()).toEqual("1600"); + expect(geocoded.getStreetName()).toEqual("Pennsylvania Ave SE"); + expect(geocoded.getSubLocality()).toEqual(undefined); + expect(geocoded.getLocality()).toEqual("Washington"); + expect(geocoded.getPostalCode()).toEqual("20003"); + expect(geocoded.getRegion()).toEqual("District of Columbia"); + expect(geocoded.getAdminLevels()).toEqual([ + AdminLevel.create({ level: 2, name: "Washington" }), + AdminLevel.create({ + level: 1, + name: "District of Columbia", + code: "DC", + }), + ]); + expect(geocoded.getCountry()).toEqual("United States"); + expect(geocoded.getCountryCode()).toEqual("us"); + expect(geocoded.getResultType()).toEqual(["address"]); + + done(); + }); + }); + + it("receives correct geocoding results with and without fuzzy match", (done) => { + const provider = GeocoderJS.createGeocoder({ + provider: "mapbox", + useSsl: true, + apiKey: "api_key", + }); + let numberResultsWithFuzzyMatch = -1; + let numberResultsWithoutFuzzyMatch = -1; + + const assertion = () => + expect(numberResultsWithFuzzyMatch).toBeGreaterThan( + numberResultsWithoutFuzzyMatch + ); + + provider?.geocode({ text: "wahsington", fuzzyMatch: true }, (results) => { + numberResultsWithFuzzyMatch = results.length; + if (numberResultsWithoutFuzzyMatch !== -1) { + assertion(); + done(); + } + }); + + provider?.geocode({ text: "wahsington", fuzzyMatch: false }, (results) => { + numberResultsWithoutFuzzyMatch = results.length; + if (numberResultsWithFuzzyMatch !== -1) { + assertion(); + done(); + } + }); + }); + + it("receives correct geodecoding results", (done) => { + const provider = GeocoderJS.createGeocoder({ + provider: "mapbox", + useSsl: true, + apiKey: "api_key", + }); + + provider?.geodecode(48.8631507, 2.388911, (results: MapboxGeocoded[]) => { + const geocoded = results[0]; + + expect(geocoded).toBeDefined(); + expect(geocoded.getCoordinates()).toEqual({ + latitude: 48.863134, + longitude: 2.388886, + }); + expect(geocoded.getBounds()).toEqual(undefined); + expect(geocoded.getFormattedAddress()).toEqual( + "12 Avenue Gambetta, 75020 Paris, France" + ); + expect(geocoded.getStreetNumber()).toEqual("12"); + expect(geocoded.getStreetName()).toEqual("Avenue Gambetta"); + expect(geocoded.getSubLocality()).toEqual(undefined); + expect(geocoded.getLocality()).toEqual("Paris"); + expect(geocoded.getPostalCode()).toEqual("75020"); + expect(geocoded.getRegion()).toEqual(undefined); + expect(geocoded.getAdminLevels()).toEqual([ + AdminLevel.create({ + level: 2, + name: "Paris", + }), + ]); + expect(geocoded.getCountry()).toEqual("France"); + expect(geocoded.getCountryCode()).toEqual("fr"); + expect(geocoded.getResultType()).toEqual(["address"]); + + done(); + }); + }); + + it("receives error when the API key is bad", (done) => { + const provider = GeocoderJS.createGeocoder({ + provider: "mapbox", + useSsl: true, + apiKey: "api_key", + }); + + provider?.geocode( + "1600 Pennsylvania Ave, Washington, DC", + () => { + done(); + }, + (error) => { + expect(error.message).toEqual( + "Received HTTP status code 401 when attempting geocoding request." + ); + done(); + } + ); + }); +}); diff --git a/spec/provider/NominatimProvider.spec.ts b/spec/provider/NominatimProvider.spec.ts new file mode 100644 index 0000000..3daab38 --- /dev/null +++ b/spec/provider/NominatimProvider.spec.ts @@ -0,0 +1,148 @@ +import GeocoderJS from "GeocoderJS"; +import { NominatimGeocoded, OpenStreetMapProvider } from "provider"; +import ExternalLoader from "ExternalLoader"; +import AdminLevel from "AdminLevel"; +import setupPolly, { cleanRecording } from "../setupPolly"; + +describe("OpenStreetMap / Nominatim Geocoder Provider", () => { + const pollyContext = setupPolly(); + + beforeEach(() => { + cleanRecording(pollyContext); + }); + + afterEach(async () => { + await pollyContext.polly.flush(); + }); + + it("expects User-Agent to be required on initiation", () => { + expect(() => new OpenStreetMapProvider(new ExternalLoader())).toThrowError( + Error, + 'An User-Agent identifying your application is required for the OpenStreetMap / Nominatim provider when using the default host. Please add it in the "userAgent" option.' + ); + }); + + it("expects to not support IP geolocation", () => { + const provider = GeocoderJS.createGeocoder({ + provider: "nominatim", + useSsl: true, + userAgent: "GeocoderJS Example", + }); + + expect(() => + provider?.geocode( + "66.147.244.214", + // eslint-disable-next-line @typescript-eslint/no-empty-function + () => {} + ) + ).toThrowError( + Error, + "The OpenStreetMap / Nominatim provider does not support IP geolocation, only location geocoding." + ); + }); + + it("receives correct geocoding results", (done) => { + const provider = GeocoderJS.createGeocoder({ + provider: "nominatim", + useSsl: true, + userAgent: "GeocoderJS Example", + }); + + provider?.geocode( + "1600 Pennsylvania Ave, Washington, DC", + (results: NominatimGeocoded[]) => { + const geocoded = results[0]; + + expect(geocoded).toBeDefined(); + expect(geocoded.getCoordinates()).toEqual({ + latitude: 38.8958536, + longitude: -77.0307129, + }); + expect(geocoded.getBounds()).toEqual({ + latitudeSW: 38.8957842, + longitudeSW: -77.0309688, + latitudeNE: 38.895924, + longitudeNE: -77.0304609, + }); + expect(geocoded.getFormattedAddress()).toEqual(undefined); + expect(geocoded.getDisplayName()).toEqual( + "Pennsylvania Avenue, Penn Quarter, Washington, District of Columbia, 20045, United States of America" + ); + expect(geocoded.getStreetNumber()).toEqual(undefined); + expect(geocoded.getStreetName()).toEqual("Pennsylvania Avenue"); + expect(geocoded.getSubLocality()).toEqual(undefined); + expect(geocoded.getLocality()).toEqual("Washington"); + expect(geocoded.getPostalCode()).toEqual("20045"); + expect(geocoded.getRegion()).toEqual("District of Columbia"); + expect(geocoded.getAdminLevels()).toEqual([ + AdminLevel.create({ level: 1, name: "District of Columbia" }), + AdminLevel.create({ level: 2, name: "Washington" }), + ]); + expect(geocoded.getCountry()).toEqual("United States of America"); + expect(geocoded.getCountryCode()).toEqual("us"); + expect(geocoded.getOsmId()).toEqual(564931814); + expect(geocoded.getOsmType()).toEqual("way"); + expect(geocoded.getCategory()).toEqual("highway"); + expect(geocoded.getType()).toEqual("path"); + expect(geocoded.getAttribution()).toEqual( + "Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright" + ); + + done(); + } + ); + }); + + it("receives correct geodecoding results", (done) => { + const provider = GeocoderJS.createGeocoder({ + provider: "nominatim", + useSsl: true, + userAgent: "GeocoderJS Example", + }); + + provider?.geodecode( + 48.8631507, + 2.388911, + (results: NominatimGeocoded[]) => { + const geocoded = results[0]; + + expect(geocoded).toBeDefined(); + expect(geocoded.getCoordinates()).toEqual({ + latitude: 48.863744499999996, + longitude: 2.3911562136123106, + }); + expect(geocoded.getBounds()).toEqual({ + latitudeSW: 48.8625929, + longitudeSW: 2.3877078, + latitudeNE: 48.8648832, + longitudeNE: 2.3956964, + }); + expect(geocoded.getFormattedAddress()).toEqual(undefined); + expect(geocoded.getDisplayName()).toEqual( + "Quartier du Père-Lachaise, Paris, Île-de-France, France métropolitaine, 75020, France" + ); + expect(geocoded.getStreetNumber()).toEqual(undefined); + expect(geocoded.getStreetName()).toEqual(undefined); + expect(geocoded.getSubLocality()).toEqual("Quartier du Père-Lachaise"); + expect(geocoded.getLocality()).toEqual("Paris"); + expect(geocoded.getPostalCode()).toEqual("75020"); + expect(geocoded.getRegion()).toEqual("Île-de-France"); + expect(geocoded.getAdminLevels()).toEqual([ + AdminLevel.create({ level: 1, name: "Île-de-France" }), + AdminLevel.create({ level: 2, name: "Paris" }), + ]); + expect(geocoded.getCountry()).toEqual("France"); + expect(geocoded.getCountryCode()).toEqual("fr"); + expect(geocoded.getOsmId()).toEqual(322777831); + expect(geocoded.getOsmType()).toEqual("way"); + expect(geocoded.getCategory()).toEqual("highway"); + expect(geocoded.getType()).toEqual("pedestrian"); + expect(geocoded.getAttribution()).toEqual( + "Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright" + ); + + done(); + } + ); + }); +}); diff --git a/spec/provider/OpenCageProvider.spec.ts b/spec/provider/OpenCageProvider.spec.ts new file mode 100644 index 0000000..ce8d31e --- /dev/null +++ b/spec/provider/OpenCageProvider.spec.ts @@ -0,0 +1,225 @@ +import GeocoderJS from "GeocoderJS"; +import { OpenCageGeocoded, OpenCageProvider } from "provider"; +import ExternalLoader from "ExternalLoader"; +import AdminLevel from "AdminLevel"; +import setupPolly, { cleanRecording } from "../setupPolly"; + +describe("OpenCage Geocoder Provider", () => { + const pollyContext = setupPolly(); + + beforeEach(() => { + cleanRecording(pollyContext); + }); + + afterEach(async () => { + await pollyContext.polly.flush(); + }); + + it("expects API Key to be required on initiation", () => { + expect(() => new OpenCageProvider(new ExternalLoader())).toThrowError( + Error, + 'An API key is required for the OpenCage provider. Please add it in the "apiKey" option.' + ); + }); + + it("expects to not support IP geolocation", () => { + const provider = GeocoderJS.createGeocoder({ + provider: "opencage", + useSsl: true, + apiKey: "api_key", + }); + + expect(() => + provider?.geocode( + "66.147.244.214", + // eslint-disable-next-line @typescript-eslint/no-empty-function + () => {} + ) + ).toThrowError( + Error, + "The OpenCage provider does not support IP geolocation, only location geocoding." + ); + }); + + it("receives correct geocoding results", (done) => { + const provider = GeocoderJS.createGeocoder({ + provider: "opencage", + useSsl: true, + apiKey: "api_key", + }); + + provider?.geocode("1600 Pennsylvania Ave, Washington, DC", (results) => { + const geocoded = results[0]; + + expect(geocoded).toBeDefined(); + expect(geocoded.getCoordinates()).toEqual({ + latitude: 38.8636383, + longitude: -76.9463651, + }); + expect(geocoded.getBounds()).toEqual({ + latitudeSW: 38.8633822, + longitudeSW: -76.9467576, + latitudeNE: 38.8637409, + longitudeNE: -76.945632, + }); + expect(geocoded.getFormattedAddress()).toEqual( + "Pennsylvania Avenue, Washington, DC 20746-8001, United States of America" + ); + expect(geocoded.getStreetNumber()).toEqual(undefined); + expect(geocoded.getStreetName()).toEqual("Pennsylvania Avenue"); + expect(geocoded.getSubLocality()).toEqual(undefined); + expect(geocoded.getLocality()).toEqual("Dillon Park"); + expect(geocoded.getPostalCode()).toEqual("20746-8001"); + expect(geocoded.getRegion()).toEqual("District of Columbia"); + expect(geocoded.getAdminLevels()).toEqual([ + AdminLevel.create({ + level: 1, + name: "District of Columbia", + code: "DC", + }), + AdminLevel.create({ level: 2, name: "Prince George's County" }), + ]); + expect(geocoded.getCountry()).toEqual("United States of America"); + expect(geocoded.getCountryCode()).toEqual("us"); + expect(geocoded.getTimezone()).toEqual("America/New_York"); + expect(geocoded.getCallingCode()).toEqual(1); + expect(geocoded.getFlag()).toEqual("🇺🇸"); + expect(geocoded.getPrecision()).toEqual(9); + expect(geocoded.getMgrs()).toEqual("18SUJ3113003444"); + expect(geocoded.getMaidenhead()).toEqual("FM18mu67kg"); + expect(geocoded.getGeohash()).toEqual("dqcm14cm5er8th99jt7w"); + expect(geocoded.getWhat3words()).toEqual("bottom.grace.mass"); + + done(); + }); + }); + + it("receives correct geodecoding results", (done) => { + const provider = GeocoderJS.createGeocoder({ + provider: "opencage", + useSsl: true, + apiKey: "api_key", + }); + + provider?.geodecode(48.8631507, 2.388911, (results: OpenCageGeocoded[]) => { + const geocoded = results[0]; + + expect(geocoded).toBeDefined(); + expect(geocoded.getCoordinates()).toEqual({ + latitude: 48.863116, + longitude: 2.38878, + }); + expect(geocoded.getBounds()).toEqual(undefined); + expect(geocoded.getFormattedAddress()).toEqual( + "8 Avenue Gambetta, 75020 Paris, France" + ); + expect(geocoded.getStreetNumber()).toEqual("8"); + expect(geocoded.getStreetName()).toEqual("Avenue Gambetta"); + expect(geocoded.getSubLocality()).toEqual(undefined); + expect(geocoded.getLocality()).toEqual("Paris"); + expect(geocoded.getPostalCode()).toEqual("75020"); + expect(geocoded.getRegion()).toEqual("Île-de-France"); + expect(geocoded.getAdminLevels()).toEqual([ + AdminLevel.create({ + level: 1, + name: "Île-de-France", + code: "IDF", + }), + ]); + expect(geocoded.getCountry()).toEqual("France"); + expect(geocoded.getCountryCode()).toEqual("fr"); + expect(geocoded.getTimezone()).toEqual("Europe/Paris"); + expect(geocoded.getCallingCode()).toEqual(33); + expect(geocoded.getFlag()).toEqual("🇫🇷"); + expect(geocoded.getPrecision()).toEqual(10); + expect(geocoded.getMgrs()).toEqual("31UDQ5517112419"); + expect(geocoded.getMaidenhead()).toEqual("JN18eu67pd"); + expect(geocoded.getGeohash()).toEqual("u09tyr72q952wcz9bf5x"); + expect(geocoded.getWhat3words()).toEqual("snatched.juniors.tedious"); + + done(); + }); + }); + + it("receives error when the quota is exceeded", (done) => { + const provider = GeocoderJS.createGeocoder({ + provider: "opencage", + useSsl: true, + // see https://opencagedata.com/api#testingkeys + apiKey: "4372eff77b8343cebfc843eb4da4ddc4", + }); + + provider?.geocode( + "1600 Pennsylvania Ave, Washington, DC", + () => { + done(); + }, + (error) => { + expect(error.message).toEqual("Quota exceeded (402): quota exceeded"); + done(); + } + ); + }); + + it("receives error when suspended", (done) => { + const provider = GeocoderJS.createGeocoder({ + provider: "opencage", + useSsl: true, + // see https://opencagedata.com/api#testingkeys + apiKey: "2e10e5e828262eb243ec0b54681d699a", + }); + + provider?.geocode( + "1600 Pennsylvania Ave, Washington, DC", + () => { + done(); + }, + (error) => { + expect(error.message).toEqual("Forbidden (403): suspended"); + done(); + } + ); + }); + + it("receives error when the IP address is rejected", (done) => { + const provider = GeocoderJS.createGeocoder({ + provider: "opencage", + useSsl: true, + // see https://opencagedata.com/api#testingkeys + apiKey: "6c79ee8e1ca44ad58ad1fc493ba9542f", + }); + + provider?.geocode( + "1600 Pennsylvania Ave, Washington, DC", + () => { + done(); + }, + (error) => { + expect(error.message).toEqual("Forbidden (403): IP address rejected"); + done(); + } + ); + }); + + it("receives error when requesting too quickly", (done) => { + const provider = GeocoderJS.createGeocoder({ + provider: "opencage", + useSsl: true, + // see https://opencagedata.com/api#testingkeys + apiKey: "d6d0f0065f4348a4bdfe4587ba02714b", + }); + + provider?.geocode( + "1600 Pennsylvania Ave, Washington, DC", + () => { + done(); + }, + (error) => { + expect(error.message).toEqual( + "Too many requests (429): requesting too quickly" + ); + done(); + } + ); + }); +}); diff --git a/spec/provider/YandexProvider.spec.ts b/spec/provider/YandexProvider.spec.ts new file mode 100644 index 0000000..bf91b84 --- /dev/null +++ b/spec/provider/YandexProvider.spec.ts @@ -0,0 +1,161 @@ +import GeocoderJS from "GeocoderJS"; +import { YandexGeocoded, YandexProvider } from "provider"; +import ExternalLoader from "ExternalLoader"; +import AdminLevel from "AdminLevel"; +import setupPolly, { cleanRecording } from "../setupPolly"; + +describe("Yandex Geocoder Provider", () => { + const pollyContext = setupPolly(); + + beforeEach(() => { + cleanRecording(pollyContext); + }); + + afterEach(async () => { + await pollyContext.polly.flush(); + }); + + it("expects API Key to be required on initiation", () => { + expect(() => new YandexProvider(new ExternalLoader())).toThrowError( + Error, + 'An API key is required for the Yandex provider. Please add it in the "apiKey" option.' + ); + }); + + it("expects to not support IP geolocation", () => { + const provider = GeocoderJS.createGeocoder({ + provider: "yandex", + useSsl: true, + apiKey: "api_key", + }); + + expect(() => + provider?.geocode( + "66.147.244.214", + // eslint-disable-next-line @typescript-eslint/no-empty-function + () => {} + ) + ).toThrowError( + Error, + "The Yandex provider does not support IP geolocation, only location geocoding." + ); + }); + + it("receives correct geocoding results", (done) => { + const provider = GeocoderJS.createGeocoder({ + provider: "yandex", + useSsl: true, + apiKey: "api_key", + }); + + provider?.geocode( + { text: "1600 Pennsylvania Ave, Washington, DC", locale: "en_US" }, + (results: YandexGeocoded[]) => { + const geocoded = results[0]; + + expect(geocoded).toBeDefined(); + expect(geocoded.getCoordinates()).toEqual({ + latitude: 38.895512, + longitude: -77.033608, + }); + expect(geocoded.getBounds()).toEqual({ + latitudeSW: 38.890612, + longitudeSW: -77.058105, + latitudeNE: 38.905248, + longitudeNE: -77.012426, + }); + expect(geocoded.getFormattedAddress()).toEqual(undefined); + expect(geocoded.getStreetNumber()).toEqual(undefined); + expect(geocoded.getStreetName()).toEqual( + "Pennsylvania Avenue Northwest" + ); + expect(geocoded.getSubLocality()).toEqual(undefined); + expect(geocoded.getLocality()).toEqual("Washington"); + expect(geocoded.getPostalCode()).toEqual(undefined); + expect(geocoded.getRegion()).toEqual("District of Columbia"); + expect(geocoded.getAdminLevels()).toEqual([ + AdminLevel.create({ + level: 1, + name: "District of Columbia", + }), + ]); + expect(geocoded.getCountry()).toEqual("United States of America"); + expect(geocoded.getCountryCode()).toEqual("US"); + expect(geocoded.getLocationType()).toEqual("street"); + expect(geocoded.getPrecision()).toEqual("street"); + + done(); + } + ); + }); + + it("receives correct geodecoding results", (done) => { + const provider = GeocoderJS.createGeocoder({ + provider: "yandex", + useSsl: true, + apiKey: "api_key", + }); + + provider?.geodecode( + { + coordinates: { latitude: 48.8631507, longitude: 2.388911 }, + locale: "en_US", + }, + (results: YandexGeocoded[]) => { + const geocoded = results[0]; + + expect(geocoded).toBeDefined(); + expect(geocoded.getCoordinates()).toEqual({ + latitude: 48.8631, + longitude: 2.388899, + }); + expect(geocoded.getBounds()).toEqual({ + latitudeSW: 48.860391, + longitudeSW: 2.384794, + latitudeNE: 48.865808, + longitudeNE: 2.393004, + }); + expect(geocoded.getFormattedAddress()).toEqual(undefined); + expect(geocoded.getStreetNumber()).toEqual("10"); + expect(geocoded.getStreetName()).toEqual("Avenue Gambetta"); + expect(geocoded.getSubLocality()).toEqual("20e Arrondissement"); + expect(geocoded.getLocality()).toEqual("Paris"); + expect(geocoded.getPostalCode()).toEqual(undefined); + expect(geocoded.getRegion()).toEqual("Île-de-France"); + expect(geocoded.getAdminLevels()).toEqual([ + AdminLevel.create({ + level: 1, + name: "Île-de-France", + }), + ]); + expect(geocoded.getCountry()).toEqual("France"); + expect(geocoded.getCountryCode()).toEqual("FR"); + expect(geocoded.getLocationType()).toEqual("house"); + expect(geocoded.getPrecision()).toEqual("exact"); + + done(); + } + ); + }); + + it("receives error when the API key is bad", (done) => { + const provider = GeocoderJS.createGeocoder({ + provider: "yandex", + useSsl: true, + apiKey: "api_key", + }); + + provider?.geocode( + "1600 Pennsylvania Ave, Washington, DC", + () => { + done(); + }, + (error) => { + expect(error.message).toEqual( + "Received HTTP status code 403 when attempting geocoding request." + ); + done(); + } + ); + }); +}); diff --git a/spec/providers/BingProvider.spec.js b/spec/providers/BingProvider.spec.js deleted file mode 100644 index b00b8ec..0000000 --- a/spec/providers/BingProvider.spec.js +++ /dev/null @@ -1,88 +0,0 @@ -describe("Bing Geocoder Provider raw result to Geocoded mapping tests", function() { - var provider = new GeocoderJS.BingProvider(new GeocoderJS.ExternalURILoader()), - geocoded; - - var stubBingResult = { - "__type": "Location:http://schemas.microsoft.com/search/local/ws/rest/v1", - "bbox": [ - 47.636186665473566, - -122.13744013372656, - 47.64391210061492, - -122.12215365108256 - ], - "name": "1 Microsoft Way, Redmond, WA 98052", - "point": { - "type": "Point", - "coordinates": [ - 47.64004938304424, - -122.12979689240456 - ] - }, - "address": { - "addressLine": "1 Microsoft Way", - "adminDistrict": "WA", - "adminDistrict2": "King Co.", - "countryRegion": "United States", - "formattedAddress": "1 Microsoft Way, Redmond, WA 98052", - "locality": "Redmond", - "postalCode": "98052" - }, - "confidence": "High", - "entityType": "Address", - "geocodePoints": [ - { - "type": "Point", - "coordinates": [ - 47.64004938304424, - -122.12979689240456 - ], - "calculationMethod": "InterpolationOffset", - "usageTypes": [ - "Display" - ] - }, - { - "type": "Point", - "coordinates": [ - 47.64006815850735, - -122.12985791265965 - ], - "calculationMethod": "Interpolation", - "usageTypes": [ - "Route" - ] - } - ], - "matchCodes": [ - "Good" - ] - }; - - beforeEach(function () { - geocoded = provider.mapToGeocoded(stubBingResult); - }); - - it ("receives results from the bing geocoder", function() { - expect(geocoded).toBeDefined(); - }); - - it ("maps coordinates correctly", function() { - expect(geocoded.getCoordinates()).toEqual([47.64004938304424, -122.12979689240456]); - }); - - it ("maps street name correctly", function() { - expect(geocoded.getStreetName()).toEqual("1 Microsoft Way"); - }); - - it ("maps city correctly", function() { - expect(geocoded.getCity()).toEqual("Redmond"); - }); - - it ("maps region correctly", function() { - expect(geocoded.getRegion()).toEqual("WA"); - }); - - it ("maps postal code correctly", function() { - expect(geocoded.getZipcode()).toEqual("98052"); - }); -}); diff --git a/spec/providers/GoogleAPIProvider.spec.js b/spec/providers/GoogleAPIProvider.spec.js deleted file mode 100644 index 2a2b9f7..0000000 --- a/spec/providers/GoogleAPIProvider.spec.js +++ /dev/null @@ -1,70 +0,0 @@ -describe("Google API Geocoder Provider raw result to Geocoded mapping tests", function() { - var provider = new GeocoderJS.GoogleAPIProvider(new GeocoderJS.ExternalURILoader()), - geocoded; - - var stubGoogleResult = [{ - address_components: [{ - long_name: "1600", - short_name: "1600", - types: ["street_number"] - }, - { - long_name: "Pennsylvania Avenue Northwest", - short_name: "Pennsylvania Avenue NW", - types: ["route"] - }, - { - long_name: "Washington, D.C.", - short_name: "Washington, D.C.", - types: ["locality", "political"] - }, - { - long_name: "District of Columbia", - short_name: "DC", - types: ["administrative_area_level_1", "political"] - }, - { - long_name: "20050", - short_name: "20050", - types: ["postal_code"] - }], - geometry: { - location: { - lat: 38.8978378, - lng: -77.0365123 - } - } - }]; - - beforeEach(function () { - geocoded = provider.mapToGeocoded(stubGoogleResult[0]); - }); - - it ("receives results from the google geocoder", function() { - expect(geocoded).toBeDefined(); - }); - - it ("maps coordinates correctly", function() { - expect(geocoded.getCoordinates()).toEqual([38.8978378, -77.0365123]); - }); - - it ("maps street number correctly", function() { - expect(geocoded.getStreetNumber()).toEqual("1600"); - }); - - it ("maps street name correctly", function() { - expect(geocoded.getStreetName()).toEqual("Pennsylvania Avenue Northwest"); - }); - - it ("maps city correctly", function() { - expect(geocoded.getCity()).toEqual("Washington, D.C."); - }); - - it ("maps region correctly", function() { - expect(geocoded.getRegion()).toEqual("District of Columbia"); - }); - - it ("maps postal code correctly", function() { - expect(geocoded.getZipcode()).toEqual("20050"); - }); -}); diff --git a/spec/providers/MapquestProvider.spec.js b/spec/providers/MapquestProvider.spec.js deleted file mode 100644 index cd0c5a7..0000000 --- a/spec/providers/MapquestProvider.spec.js +++ /dev/null @@ -1,61 +0,0 @@ -describe("Mapquest Provider to Geocoded mapping tests", function() { - var provider = new GeocoderJS.MapquestProvider(new GeocoderJS.ExternalURILoader()); - var geocoded; - - var stubMapquestResult = { - "adminArea1": "US", - "adminArea1Type": "Country", - "adminArea3": "", - "adminArea3Type": "State", - "latLng": { - "lng": -77.036372, - "lat": 38.895115 - }, - "adminArea4": "District of Columbia", - "adminArea5Type": "City", - "adminArea4Type": "County", - "adminArea5": "Washington", - "street": "1600 Pennsylvania Ave", - "type": "s", - "displayLatLng": { - "lng": -77.036372, - "lat": 38.895115 - }, - "linkId": 0, - "postalCode": "20050", - "sideOfStreet": "N", - "dragPoint": false, - "geocodeQuality": "CITY", - "geocodeQualityCode": "A5XCX" - }; - - beforeEach(function () { - geocoded = provider.mapToGeocoded(stubMapquestResult); - }); - - it ("expects API Key to be set on initiation.", function() { - var testProvider = new GeocoderJS.MapquestProvider(new GeocoderJS.ExternalURILoader(), {apiKey: '[stub-api-key]'}); - expect(testProvider.apiKey).toEqual('[stub-api-key]'); - }); - - it ("maps coordinates correctly", function() { - var expectedCoordinates = [38.895115, -77.036372]; - expect(geocoded.getCoordinates()).toEqual(expectedCoordinates); - }); - - it ("maps street correctly", function() { - expect(geocoded.getStreetName()).toEqual("1600 Pennsylvania Ave"); - }); - - it ("maps city correctly", function() { - expect(geocoded.getCity()).toEqual("Washington"); - }); - - it ("maps region correctly", function() { - expect(geocoded.getRegion()).toEqual("District of Columbia"); - }); - - it ("maps zipcode correctly", function() { - expect(geocoded.getZipcode()).toEqual("20050"); - }); -}); diff --git a/spec/providers/OpenStreetMapProvider.spec.js b/spec/providers/OpenStreetMapProvider.spec.js deleted file mode 100644 index 7f63715..0000000 --- a/spec/providers/OpenStreetMapProvider.spec.js +++ /dev/null @@ -1,68 +0,0 @@ -describe("OpenStreetMap raw result to Geocoded mapping tests", function() { - var provider = new GeocoderJS.OpenStreetMapProvider(new GeocoderJS.ExternalURILoader()), - geocoded; - - var stubOpenStreetMapResult = [{ - place_id: "9141060761", - licence: "Data © OpenStreetMap contributors, ODbL 1.0. http://www.openstreetmap.org/copyright", - osm_type: "way", - osm_id: "238241022", - boundingbox: [ - "38.8974876403809", - "38.8979148864746", - "-77.0368804931641", - "-77.036247253418" - ], - lat: "38.89770045", - lon: "-77.0365643605898", - display_name: "The White House, 1600, Pennsylvania Avenue Northwest, Foggy Bottom, Farragut Square, Washington, District of Columbia, 20500, United States of America", - class: "tourism", - type: "attraction", - importance: 1.5076757387296, - icon: "http://nominatim.openstreetmap.org/images/mapicons/poi_point_of_interest.p.20.png", - address: { - attraction: "The White House", - house_number: "1600", - road: "Pennsylvania Avenue Northwest", - neighbourhood: "Foggy Bottom", - suburb: "Farragut Square", - city: "Washington", - state: "District of Columbia", - postcode: "20500", - country: "United States of America", - country_code: "us" - } -}]; - - beforeEach(function () { - geocoded = provider.mapToGeocoded(stubOpenStreetMapResult[0]); - }); - - it ("receives results from the OpenStreetMap geocoder", function() { - expect(geocoded).toBeDefined(); - }); - - it ("expects coordinates to be set correctly", function() { - expect(geocoded.getCoordinates()).toEqual([38.89770045, -77.0365643605898]); - }); - - it ("maps street number correctly", function() { - expect(geocoded.getStreetNumber()).toEqual("1600"); - }); - - it ("maps street name correctly", function() { - expect(geocoded.getStreetName()).toEqual("Pennsylvania Avenue Northwest"); - }); - - it ("maps city correctly", function() { - expect(geocoded.getCity()).toEqual("Washington"); - }); - - it ("maps region correctly", function() { - expect(geocoded.getRegion()).toEqual("District of Columbia"); - }); - - it ("maps postal code correctly", function() { - expect(geocoded.getZipcode()).toEqual("20500"); - }); -}); diff --git a/spec/providers/ProviderBase.spec.js b/spec/providers/ProviderBase.spec.js deleted file mode 100644 index e50e7b9..0000000 --- a/spec/providers/ProviderBase.spec.js +++ /dev/null @@ -1,7 +0,0 @@ -describe("ProviderBase method access", function() { - var provider = new GeocoderJS.ProviderBase(); - - it ("ProviderBase has geocode method", function() { - expect(provider.geocode).toBeDefined(); - }); -}); diff --git a/spec/providers/YandexProvider.spec.js b/spec/providers/YandexProvider.spec.js deleted file mode 100644 index 0aa6c8e..0000000 --- a/spec/providers/YandexProvider.spec.js +++ /dev/null @@ -1,71 +0,0 @@ -describe("Yandex Geocoder Provider raw result to Geocoded mapping tests", function() { - var provider = new GeocoderJS.YandexProvider(new GeocoderJS.ExternalURILoader()), - geocoded; - - var stubYandexResult = { - "metaDataProperty": { - "GeocoderMetaData": { - "kind": "house", - "text": "United States, District of Columbia, Washington, Pennsylvania Ave NW, 1600", - "precision": "exact", - "AddressDetails": { - "Country": { - "AddressLine": "District of Columbia, Washington, Pennsylvania Ave NW, 1600", - "CountryNameCode": "US", - "CountryName": "United States", - "AdministrativeArea": { - "AdministrativeAreaName": "District of Columbia", - "SubAdministrativeArea": { - "SubAdministrativeAreaName": "District of Columbia", - "Locality": { - "LocalityName": "Washington", - "Thoroughfare": { - "ThoroughfareName": "Pennsylvania Ave NW", - "Premise": { - "PremiseNumber": "1600" - } - } - } - } - } - } - } - } - }, - "description": "Washington, District of Columbia, United States", - "name": "Pennsylvania Ave NW, 1600", - "boundedBy": { - "Envelope": { - "lowerCorner": "-77.046921 38.891265", - "upperCorner": "-77.030464 38.904125" - } - }, - "Point": { - "pos": "-77.038692 38.897695" - } - }; - - beforeEach(function () { - geocoded = provider.mapToGeocoded(stubYandexResult); - }); - - it ("receives results from the yandex geocoder", function() { - expect(geocoded).toBeDefined(); - }); - - it ("maps coordinates correctly", function() { - expect(geocoded.getCoordinates()).toEqual([38.897695, -77.038692]); - }); - - it ("maps street name correctly", function() { - expect(geocoded.getStreetName()).toEqual("Pennsylvania Ave NW"); - }); - - it ("maps city correctly", function() { - expect(geocoded.getCity()).toEqual("Washington"); - }); - - it ("maps region correctly", function() { - expect(geocoded.getRegion()).toEqual("District of Columbia"); - }); -}); diff --git a/spec/recordings/Bing-Geocoder-Provider_2231523888/receives-correct-geocoding-results_3640432360/recording.har b/spec/recordings/Bing-Geocoder-Provider_2231523888/receives-correct-geocoding-results_3640432360/recording.har new file mode 100644 index 0000000..c125835 --- /dev/null +++ b/spec/recordings/Bing-Geocoder-Provider_2231523888/receives-correct-geocoding-results_3640432360/recording.har @@ -0,0 +1,167 @@ +{ + "log": { + "_recordingName": "Bing Geocoder Provider/receives correct geocoding results", + "creator": { + "comment": "persister:fs", + "name": "Polly.JS", + "version": "5.0.0" + }, + "entries": [ + { + "_id": "5786db4fc459c37d9aa1330f3b69193c", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 0, + "cookies": [], + "headers": [ + { + "_fromType": "array", + "name": "accept", + "value": "*/*" + }, + { + "_fromType": "array", + "name": "user-agent", + "value": "node-fetch/1.0 (+https://github.com/bitinn/node-fetch)" + }, + { + "_fromType": "array", + "name": "accept-encoding", + "value": "gzip,deflate" + }, + { + "_fromType": "array", + "name": "connection", + "value": "close" + }, + { + "name": "host", + "value": "dev.virtualearth.net" + } + ], + "headersSize": 357, + "httpVersion": "HTTP/1.1", + "method": "GET", + "queryString": [ + { + "name": "maxRes", + "value": "5" + }, + { + "name": "incl", + "value": "ciso2" + } + ], + "url": "https://dev.virtualearth.net/REST/v1/Locations/1600%20Pennsylvania%20Ave,%20Washington,%20DC?maxRes=5&incl=ciso2" + }, + "response": { + "bodySize": 2365, + "content": { + "_isBinary": true, + "mimeType": "application/json; charset=utf-8", + "size": 2365, + "text": "[\"1f8b0800000000000400\",\"ec58eb6edb36147e1542bf5d87778afee75b87604d1be4b2025b8a8091689b80240a2295d468fb407b8d3dd90e65bb4b87a24d3af4b2612d6058e4b97c3cdff978e4bcc94c1f37b689ae30d1f9e6cc86be8a735fda6c92fd622a57ce3b5ba67d53856c94dd74a6299ff9b5bfec1c586c626c2757475747a5bd1ddfba2ef6a6b2a68b9b7163e3d5d12c59bb667d755481cb75ebef2c44bbbed98edb660dd10adf6e3bb7de4408353f7c477ffc8e28a6189db8a2f3c1af228228c8c58042dfb695b35d18a36955a1c13ca0ce06dbddda728c2e362ea0e9e9312a4cd3f8886e2c32456143b0e510034e8a0adf4438cff06c9a6df2861307549b2ddafbf4603f828db6f3655f80afef5084a384dac5088fae193c6b48623b74e7e2c6f711d9d72dc40ae8ae4b560d6a6d57bb10a0a868d5f9fade71e6be6b7d37d47b0c55002fdf77853db7316493dfde643644571bc874e1a3a9b209ffcb6667707d1db76d62e899dfd136794f442836b636615c1fb28d0b5fc332b0526c120f85a9ae8eeec2d51184048a6e4962f5c6bf86c02c1fe79a2925694e39d58cd2d113a5c69833ac29954453ae081b8199c64460a288120a4b25766654e74229966325a8d2af465963ea0492488cd1a96d9ab0ad6e4de30c9ade5af4fce508bd346103ed117d33428b39b08eb10438ad770db4c49b6c7fcad3e139b58befa09da032e1805649a677d999e44abc7a37ca4c59261e92fffeeb33d77c0a47967c6ad72c5c889d2b52332ee67f5fa4a9455ddc22bfba077b00d537b1db9ed935f00046978d4b3d721e0798a36ce53be01296a6075c8f2fc8c01a2407df0f72b73e4083ecd57a30fe00d071f009f9e579f62eed342b07622e92f9892d5d5f837d1277dc5eec4a7d0039cad6d6171078a8fdaeeb1ecf061899aae8aba1474f2ce8a48400671edad2b710a20f666d53e614218352b795d96640e2839231752f9ba6fa91e9ce40b41692811b10546c5219878d697de3d6bdefc30ec957149b124c50714f6c72acf35c7082b92432674a27b1e539c32ac7f7c40666e08ab5162c674452f939b19d2f3fda5bec5162539a12c1f628e132105f22b7f3e50f22b74f94e4c17263df556e1fe1e36b0a4e69cc69be4f4731e7ff3ebd69055ae24cc278e2bb9b4370caa40421a5d14577a34d489ce78a700a838ced2720c198c007a369c07dd96863ea316a8309abe01200810b8c95dac350308401b316700ffc0787dd50a287aa6f30fe6eea7b083f1fd7c731bc7fc2fbdf6ef5c56a156cfc67ca4c50b0a032cf09d68abc87c219c152730e957a08941f52b09a084a61ecc13b28d99f4c4821390c4792d4391c5fe64a4105141752f2c32b2ba3e0ab388c572ebe8d60195c1130bea12fe0730f436b2e35852d80f7bf5ebfab5e3f4bcf37932bc342132d09fc8812fa3d122699e0798ea9625f4faec336f012fbb0630688392c2c6c283ad7c65dabbcf81932c0afdec21ea7dc98526cccca4a52c2ccbc91393705bc8412c340a105c76f179738fd9b29f9168fd37ff2f6ccaed07482045c9373f974367dba9c71ac9ece16046ea765ae259f4d299128d9cd26683163cbc54f4b42281d96e693e1ef004f087982e905c927044fa8f8357bf7e700f7536879b5100000\"]" + }, + "cookies": [], + "headers": [ + { + "name": "cache-control", + "value": "no-cache" + }, + { + "name": "transfer-encoding", + "value": "chunked" + }, + { + "name": "content-type", + "value": "application/json; charset=utf-8" + }, + { + "name": "content-encoding", + "value": "gzip" + }, + { + "name": "vary", + "value": "Accept-Encoding" + }, + { + "name": "server", + "value": "Microsoft-IIS/10.0" + }, + { + "name": "x-bm-traceid", + "value": "0220aafe61d542b684ac9751a3152c40" + }, + { + "name": "x-bm-be-elapsed", + "value": "201" + }, + { + "name": "x-msedge-ref", + "value": "Ref A: 5615C6FBAFEB407FBD1476E8964BA216 Ref B: DB3EDGE1122 Ref C: 2020-11-02T18:10:25Z" + }, + { + "name": "x-bm-fe-elapsed", + "value": "224" + }, + { + "name": "x-bm-srv", + "value": "DU00000B76" + }, + { + "name": "x-ms-bm-ws-info", + "value": "0" + }, + { + "name": "access-control-allow-origin", + "value": "*" + }, + { + "name": "access-control-allow-methods", + "value": "POST, GET, OPTIONS" + }, + { + "name": "access-control-allow-headers", + "value": "Content-Type,X-FD-Features,X-FD-FLIGHT,PreferAnonymous" + }, + { + "name": "x-aspnet-version", + "value": "4.0.30319" + }, + { + "name": "x-powered-by", + "value": "ASP.NET" + }, + { + "name": "date", + "value": "Mon, 02 Nov 2020 18:10:25 GMT" + }, + { + "name": "connection", + "value": "close" + } + ], + "headersSize": 689, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 200, + "statusText": "OK" + }, + "startedDateTime": "2020-11-02T18:10:25.515Z", + "time": 318, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 318 + } + } + ], + "pages": [], + "version": "1.2" + } +} diff --git a/spec/recordings/Bing-Geocoder-Provider_2231523888/receives-correct-geodecoding-results_1602611079/recording.har b/spec/recordings/Bing-Geocoder-Provider_2231523888/receives-correct-geodecoding-results_1602611079/recording.har new file mode 100644 index 0000000..6638483 --- /dev/null +++ b/spec/recordings/Bing-Geocoder-Provider_2231523888/receives-correct-geodecoding-results_1602611079/recording.har @@ -0,0 +1,159 @@ +{ + "log": { + "_recordingName": "Bing Geocoder Provider/receives correct geodecoding results", + "creator": { + "comment": "persister:fs", + "name": "Polly.JS", + "version": "5.0.0" + }, + "entries": [ + { + "_id": "9a281064377e3aa13168e7fc6ca3f252", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 0, + "cookies": [], + "headers": [ + { + "_fromType": "array", + "name": "accept", + "value": "*/*" + }, + { + "_fromType": "array", + "name": "user-agent", + "value": "node-fetch/1.0 (+https://github.com/bitinn/node-fetch)" + }, + { + "_fromType": "array", + "name": "accept-encoding", + "value": "gzip,deflate" + }, + { + "_fromType": "array", + "name": "connection", + "value": "close" + }, + { + "name": "host", + "value": "dev.virtualearth.net" + } + ], + "headersSize": 322, + "httpVersion": "HTTP/1.1", + "method": "GET", + "queryString": [ + { + "name": "incl", + "value": "ciso2" + } + ], + "url": "https://dev.virtualearth.net/REST/v1/Locations/48.8631507,2.388911?incl=ciso2" + }, + "response": { + "bodySize": 1693, + "content": { + "_isBinary": true, + "mimeType": "application/json; charset=utf-8", + "size": 1693, + "text": "[\"1f8b0800000000000400\",\"94545d6edb3810be0ac167579664fd587e739cb61b6cb20dd2745fd6414093639b80440ae4c8ae90e60a7b8f5e637bb11d4a765b24bb58acfd2292df7c33df7c433e71d1e11e0c6a29505b7307beab716515f005ff5dd45aad1ca8702e6acf277ce38451d776673f394d883d62bb584fd7530587e8a01d76a206e1701f19c0f5f422a0b5d9ada735853cb6f608c4f6b8e9a3d6ec884ddab6777ab747a25a9dbfd95f5f591aa731bbd1d2596fb7c8888569f4cc776d5b6b703e62cbba6603dc33071edc0154c4eef7dab3e5ed1593c2188b6c034c4809de831a38482993d620e919d6c2f4219a147bd6889e9d623ac24fe8a07556759262ad6348527ca31169a9cd10d9501270eca8716f3b64f0b9252ecf8e2ea00c6bc135da7b6a2adb3adbfc2467655d6bddd0ef88ba4051b673123e027abef8e3898347dd08ca746f51d47c91fcc08c80c747ecdbe0d0b51d6d5b7c37c2cb3d34c247cd395b246d43dbe48adc071fa4a8d7d3a35f4f89922c3a24c1d58dfd4cc4d93c9ae7559a15f93ccdd26a962693349acde32a4f8b7c9665795a2693002a8aaa4ce3a4cccbb828b300aa8a22ae128accb332ad1e26dc8826143867cb03980ed87bd16c00514c589907736f85d361a05aab0dd9ffc44f8a6e8775180deb6874a80bfe5459314be26a3654342fa9a887e709174a859e87f8d3e7b536ff98970770a3cda5f6e8b40c13f7edcf1ade2878f38eac95f0129086624e454adb1974fd1deca8d7b4ff3d626b1d19454e2dcf85fcb762d247130c1207ae27be111e3ea2030835bdae9a90d628e1fa119384b26a21812dbb5de711d8cdb7afa80f742b5e83d31f8c2bed64570bede04509f763df2f008f00860e95f66d2dfadf46035f89d98c40f62f450cf7ea754ef26a183d8dfd4f7d6dada7013fbd3643975e36fbcadb20e2dd5d60206d5b4d8f910cf05fe8f2133a3c4dd89f449c5d98f01d5849b4c3348d77e6ffcf1781441d048436dd00dd72450477962e956d89a2f36207217360e09763dbf8c33305d250c87dd0351cbdb71449fbe1840463e747c9691c9f372ec14ba7db7126f8875f899e5e1c095721655648a14065c5a652591a97a282bcd86e8b2a8579b12de22f979fe2f0bb28f32f7114fe49d84ade2e6f87fdd9db397ffe7b0080b67764ea050000\"]" + }, + "cookies": [], + "headers": [ + { + "name": "cache-control", + "value": "no-cache" + }, + { + "name": "transfer-encoding", + "value": "chunked" + }, + { + "name": "content-type", + "value": "application/json; charset=utf-8" + }, + { + "name": "content-encoding", + "value": "gzip" + }, + { + "name": "vary", + "value": "Accept-Encoding" + }, + { + "name": "server", + "value": "Microsoft-IIS/10.0" + }, + { + "name": "x-bm-traceid", + "value": "46caded46b9d4207a9e56ff692e86f60" + }, + { + "name": "x-bm-be-elapsed", + "value": "96" + }, + { + "name": "x-bm-fe-elapsed", + "value": "116" + }, + { + "name": "x-bm-srv", + "value": "DU00000B75, DU01EAP000003E8" + }, + { + "name": "x-ms-bm-ws-info", + "value": "0" + }, + { + "name": "access-control-allow-origin", + "value": "*" + }, + { + "name": "access-control-allow-methods", + "value": "POST, GET, OPTIONS" + }, + { + "name": "access-control-allow-headers", + "value": "Content-Type,X-FD-Features,X-FD-FLIGHT,PreferAnonymous" + }, + { + "name": "x-aspnet-version", + "value": "4.0.30319" + }, + { + "name": "x-powered-by", + "value": "ASP.NET" + }, + { + "name": "date", + "value": "Mon, 02 Nov 2020 18:10:24 GMT" + }, + { + "name": "connection", + "value": "close" + } + ], + "headersSize": 603, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 200, + "statusText": "OK" + }, + "startedDateTime": "2020-11-02T18:10:25.267Z", + "time": 211, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 211 + } + } + ], + "pages": [], + "version": "1.2" + } +} diff --git a/spec/recordings/Bing-Geocoder-Provider_2231523888/receives-error-when-the-API-key-is-bad_860720186/recording.har b/spec/recordings/Bing-Geocoder-Provider_2231523888/receives-error-when-the-API-key-is-bad_860720186/recording.har new file mode 100644 index 0000000..66178ce --- /dev/null +++ b/spec/recordings/Bing-Geocoder-Provider_2231523888/receives-error-when-the-API-key-is-bad_860720186/recording.har @@ -0,0 +1,150 @@ +{ + "log": { + "_recordingName": "Bing Geocoder Provider/receives error when the API key is bad", + "creator": { + "comment": "persister:fs", + "name": "Polly.JS", + "version": "5.0.0" + }, + "entries": [ + { + "_id": "5786db4fc459c37d9aa1330f3b69193c", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 0, + "cookies": [], + "headers": [ + { + "_fromType": "array", + "name": "accept", + "value": "*/*" + }, + { + "_fromType": "array", + "name": "user-agent", + "value": "node-fetch/1.0 (+https://github.com/bitinn/node-fetch)" + }, + { + "_fromType": "array", + "name": "accept-encoding", + "value": "gzip,deflate" + }, + { + "_fromType": "array", + "name": "connection", + "value": "close" + }, + { + "name": "host", + "value": "dev.virtualearth.net" + } + ], + "headersSize": 300, + "httpVersion": "HTTP/1.1", + "method": "GET", + "queryString": [ + { + "name": "maxRes", + "value": "5" + }, + { + "name": "incl", + "value": "ciso2" + } + ], + "url": "https://dev.virtualearth.net/REST/v1/Locations/1600%20Pennsylvania%20Ave,%20Washington,%20DC?maxRes=5&incl=ciso2" + }, + "response": { + "bodySize": 681, + "content": { + "mimeType": "application/json; charset=utf-8", + "size": 681, + "text": "{\"authenticationResultCode\":\"InvalidCredentials\",\"brandLogoUri\":\"http:\\/\\/dev.virtualearth.net\\/Branding\\/logo_powered_by.png\",\"copyright\":\"Copyright © 2020 Microsoft and its suppliers. All rights reserved. This API cannot be accessed and the content and any results may not be used, reproduced or transmitted in any manner without express written permission from Microsoft Corporation.\",\"errorDetails\":[\"Access was denied. You may have entered your credentials incorrectly, or you might not have access to the requested resource or operation.\"],\"resourceSets\":[],\"statusCode\":401,\"statusDescription\":\"Unauthorized\",\"traceId\":\"b1d87489fca04848879a05015d540dc3|DU00000D67|0.0.0.1\"}" + }, + "cookies": [], + "headers": [ + { + "name": "cache-control", + "value": "no-cache" + }, + { + "name": "transfer-encoding", + "value": "chunked" + }, + { + "name": "content-type", + "value": "application/json; charset=utf-8" + }, + { + "name": "server", + "value": "Microsoft-IIS/10.0" + }, + { + "name": "x-bm-traceid", + "value": "b1d87489fca04848879a05015d540dc3" + }, + { + "name": "x-bm-fe-elapsed", + "value": "0" + }, + { + "name": "x-bm-srv", + "value": "DU00000D67" + }, + { + "name": "x-ms-bm-ws-info", + "value": "0" + }, + { + "name": "access-control-allow-origin", + "value": "*" + }, + { + "name": "access-control-allow-methods", + "value": "POST, GET, OPTIONS" + }, + { + "name": "access-control-allow-headers", + "value": "Content-Type,X-FD-Features,X-FD-FLIGHT,PreferAnonymous" + }, + { + "name": "x-aspnet-version", + "value": "4.0.30319" + }, + { + "name": "x-powered-by", + "value": "ASP.NET" + }, + { + "name": "date", + "value": "Mon, 02 Nov 2020 18:10:24 GMT" + }, + { + "name": "connection", + "value": "close" + } + ], + "headersSize": 516, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 401, + "statusText": "Unauthorized" + }, + "startedDateTime": "2020-11-02T18:10:25.103Z", + "time": 151, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 151 + } + } + ], + "pages": [], + "version": "1.2" + } +} diff --git a/spec/recordings/Chain-Geocoder-Provider_1409790517/receives-correct-geocoding-results-first-first-provider-OK-_818242361/recording.har b/spec/recordings/Chain-Geocoder-Provider_1409790517/receives-correct-geocoding-results-first-first-provider-OK-_818242361/recording.har new file mode 100644 index 0000000..387b277 --- /dev/null +++ b/spec/recordings/Chain-Geocoder-Provider_1409790517/receives-correct-geocoding-results-first-first-provider-OK-_818242361/recording.har @@ -0,0 +1,271 @@ +{ + "log": { + "_recordingName": "Chain Geocoder Provider/receives correct geocoding results (first - first provider OK)", + "creator": { + "comment": "persister:fs", + "name": "Polly.JS", + "version": "5.0.0" + }, + "entries": [ + { + "_id": "2b0f5eefe9e3c5b68007e213e96cddaa", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 0, + "cookies": [], + "headers": [ + { + "_fromType": "array", + "name": "user-agent", + "value": "GeocoderJS Example" + }, + { + "_fromType": "array", + "name": "accept", + "value": "*/*" + }, + { + "_fromType": "array", + "name": "accept-encoding", + "value": "gzip,deflate" + }, + { + "_fromType": "array", + "name": "connection", + "value": "close" + }, + { + "name": "host", + "value": "nominatim.openstreetmap.org" + } + ], + "headersSize": 310, + "httpVersion": "HTTP/1.1", + "method": "GET", + "queryString": [ + { + "name": "q", + "value": "1600 Pennsylvania Ave, Washington, DC" + }, + { + "name": "limit", + "value": "5" + }, + { + "name": "format", + "value": "jsonv2" + }, + { + "name": "addressdetails", + "value": "1" + }, + { + "name": "accept-language", + "value": "en_US" + } + ], + "url": "https://nominatim.openstreetmap.org/search?q=1600+Pennsylvania+Ave%2C+Washington%2C+DC&limit=5&format=jsonv2&addressdetails=1&accept-language=en_US" + }, + "response": { + "bodySize": 1385, + "content": { + "mimeType": "application/json; charset=UTF-8", + "size": 1385, + "text": "[{\"place_id\":176517326,\"licence\":\"Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright\",\"osm_type\":\"way\",\"osm_id\":397325778,\"boundingbox\":[\"38.8633822\",\"38.8637409\",\"-76.9467576\",\"-76.945632\"],\"lat\":\"38.8636383\",\"lon\":\"-76.9463651\",\"display_name\":\"Pennsylvania Avenue, Dillon Park, Coral Hills, Prince George's County, Washington, D.C., 20746-8001, United States of America\",\"place_rank\":26,\"category\":\"highway\",\"type\":\"trunk\",\"importance\":0.41000000000000003,\"address\":{\"road\":\"Pennsylvania Avenue\",\"hamlet\":\"Dillon Park\",\"locality\":\"Coral Hills\",\"county\":\"Prince George's County\",\"state\":\"Washington, D.C.\",\"postcode\":\"20746-8001\",\"country\":\"United States of America\",\"country_code\":\"us\"}},{\"place_id\":205327805,\"licence\":\"Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright\",\"osm_type\":\"way\",\"osm_id\":564931814,\"boundingbox\":[\"38.8957842\",\"38.895924\",\"-77.0309688\",\"-77.0304609\"],\"lat\":\"38.8958536\",\"lon\":\"-77.0307129\",\"display_name\":\"Pennsylvania Avenue, Penn Quarter, Washington D.C., Washington, Washington, D.C., 20045, United States of America\",\"place_rank\":27,\"category\":\"highway\",\"type\":\"path\",\"importance\":0.385,\"address\":{\"road\":\"Pennsylvania Avenue\",\"neighbourhood\":\"Penn Quarter\",\"city\":\"Washington D.C.\",\"county\":\"Washington\",\"state\":\"Washington, D.C.\",\"postcode\":\"20045\",\"country\":\"United States of America\",\"country_code\":\"us\"}}]" + }, + "cookies": [], + "headers": [ + { + "name": "server", + "value": "nginx" + }, + { + "name": "date", + "value": "Fri, 25 Sep 2020 18:26:50 GMT" + }, + { + "name": "content-type", + "value": "application/json; charset=UTF-8" + }, + { + "name": "transfer-encoding", + "value": "chunked" + }, + { + "name": "connection", + "value": "close" + }, + { + "name": "access-control-allow-origin", + "value": "*" + }, + { + "name": "access-control-allow-methods", + "value": "OPTIONS,GET" + } + ], + "headersSize": 223, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 200, + "statusText": "OK" + }, + "startedDateTime": "2020-09-25T18:26:49.713Z", + "time": 307, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 307 + } + }, + { + "_id": "78a91888b5016be96b3a58fdabda2662", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 0, + "cookies": [], + "headers": [ + { + "_fromType": "array", + "name": "accept", + "value": "*/*" + }, + { + "_fromType": "array", + "name": "user-agent", + "value": "node-fetch/1.0 (+https://github.com/bitinn/node-fetch)" + }, + { + "_fromType": "array", + "name": "accept-encoding", + "value": "gzip,deflate" + }, + { + "_fromType": "array", + "name": "connection", + "value": "close" + }, + { + "name": "host", + "value": "geocode-maps.yandex.ru" + } + ], + "headersSize": 347, + "httpVersion": "HTTP/1.1", + "method": "GET", + "queryString": [ + { + "name": "geocode", + "value": "1600 Pennsylvania Ave, Washington, DC" + }, + { + "name": "format", + "value": "json" + }, + { + "name": "lang", + "value": "en_US" + }, + { + "name": "results", + "value": "5" + } + ], + "url": "https://geocode-maps.yandex.ru/1.x?geocode=1600+Pennsylvania+Ave%2C+Washington%2C+DC&format=json&lang=en_US&results=5" + }, + "response": { + "bodySize": 1403, + "content": { + "_isBinary": true, + "mimeType": "application/json; charset=utf-8", + "size": 1403, + "text": "[\"1f8b0800000000000003e4945d6fd3301486ff4ae4eb30e5a3cec7ee4a8be062948981b840137293b3d590d8c1713aa6a9ff7dc7499a795b09bdd8540695aadace39f6fbbe6e9e1ba2a0aea4a8811cdf90b7203f2cbf43a667b228f0874b61964bd06cce343b55b202a5affbd24ce6a03ef6edeffb1af34cc1cf066a4d8e891f799e730a42d4d7c59a09ce9ce91a5ce70bab575c5c6a295c673e23ae11d114bac60e8ab30bd9881cc721d96c70064c370a0f2897a0c8f1574be69fc4d9a22a0519af5b47a4d60a40e3491a7e19999f05d7903b679a69a81d79e14c4b503c63a88e632dcfb459c4509a72c9d97dfd0fdd89069c85547a75652270c90fde7a198e9ce639baad8da40c7d6a75fdcd68352ace5aefaa641ad53cb3ac992cf1de4098d031d25e65af089f0b56c28804b27187a64ac9351719dc75ed92677714326305d7d63977d2edba21b4be6adcd4e67c33c43bc79be7459bf2acf784c3fee1091763de9e26def6d005ea9e59b76b2d8f856b6c945c18154cf3354c15b04effc3d5c548e02e39d9e68cbddbf1e251e02ef9b4924a3697ab0ba65a0ed8f3c55ed1779f3d2fca2539d499e2550718720f073ba31f496a696001f9ebd6e41bb186023960c685bc0235934a186a9057717ce4d1c4f7a813264749ea457e80dd4d553d2cf283491099a2d4a3c1246919742ab9686953c97a5b17869197749b518a9b19ff2f804d67b2d12b607f1b9b6c59ff0c9bee4c1d9a4df7e3fd0fd86445bf079bec780ec4262f9ca42d769238a1e92e36454769e2539ab645491c05fe6e36615d1ad030eeea422f7d3236490c493d1f9adef1cb15bef30397f27e97c3926950f522b0646536f27fef3d1d9a4a56b42f094973a800df6ba1ed86478b2370dac6bf079986880e812524494c11252d49280d03ba1b4b93200cfc0e37be17ff964a14d1d651294afc3435fecff17b0b0000ffff\",\"030097724dfd840e0000\"]" + }, + "cookies": [], + "headers": [ + { + "name": "server", + "value": "nginx" + }, + { + "name": "date", + "value": "Fri, 25 Sep 2020 18:26:49 GMT" + }, + { + "name": "content-type", + "value": "application/json; charset=utf-8" + }, + { + "name": "transfer-encoding", + "value": "chunked" + }, + { + "name": "connection", + "value": "close" + }, + { + "name": "vary", + "value": "Accept-Encoding" + }, + { + "name": "access-control-allow-origin", + "value": "*" + }, + { + "name": "access-control-allow-methods", + "value": "GET" + }, + { + "name": "access-control-allow-headers", + "value": "*" + }, + { + "name": "etag", + "value": "W/\"e84-Gkzjv5mxlcw0btuJhYzfeK72c44\"" + }, + { + "name": "x-xss-protection", + "value": "1; mode=block" + }, + { + "name": "x-content-type-options", + "value": "nosniff" + }, + { + "name": "content-encoding", + "value": "gzip" + }, + { + "name": "x-qloud-router", + "value": "man1-043f58f4a44d.qloud-c.yandex.net" + } + ], + "headersSize": 458, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 200, + "statusText": "OK" + }, + "startedDateTime": "2020-09-25T18:26:49.713Z", + "time": 534, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 534 + } + } + ], + "pages": [], + "version": "1.2" + } +} diff --git a/spec/recordings/Chain-Geocoder-Provider_1409790517/receives-correct-geocoding-results-first-provider-KO-_279561490/recording.har b/spec/recordings/Chain-Geocoder-Provider_1409790517/receives-correct-geocoding-results-first-provider-KO-_279561490/recording.har new file mode 100644 index 0000000..df18640 --- /dev/null +++ b/spec/recordings/Chain-Geocoder-Provider_1409790517/receives-correct-geocoding-results-first-provider-KO-_279561490/recording.har @@ -0,0 +1,254 @@ +{ + "log": { + "_recordingName": "Chain Geocoder Provider/receives correct geocoding results (first provider KO)", + "creator": { + "comment": "persister:fs", + "name": "Polly.JS", + "version": "5.0.0" + }, + "entries": [ + { + "_id": "78a91888b5016be96b3a58fdabda2662", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 0, + "cookies": [], + "headers": [ + { + "_fromType": "array", + "name": "accept", + "value": "*/*" + }, + { + "_fromType": "array", + "name": "user-agent", + "value": "node-fetch/1.0 (+https://github.com/bitinn/node-fetch)" + }, + { + "_fromType": "array", + "name": "accept-encoding", + "value": "gzip,deflate" + }, + { + "_fromType": "array", + "name": "connection", + "value": "close" + }, + { + "name": "host", + "value": "geocode-maps.yandex.ru" + } + ], + "headersSize": 318, + "httpVersion": "HTTP/1.1", + "method": "GET", + "queryString": [ + { + "name": "geocode", + "value": "1600 Pennsylvania Ave, Washington, DC" + }, + { + "name": "format", + "value": "json" + }, + { + "name": "lang", + "value": "en_US" + }, + { + "name": "results", + "value": "5" + } + ], + "url": "https://geocode-maps.yandex.ru/1.x?geocode=1600+Pennsylvania+Ave%2C+Washington%2C+DC&format=json&lang=en_US&results=5" + }, + "response": { + "bodySize": 62, + "content": { + "mimeType": "application/json; charset=utf-8", + "size": 62, + "text": "{\"statusCode\":403,\"error\":\"Forbidden\",\"message\":\"Invalid key\"}" + }, + "cookies": [], + "headers": [ + { + "name": "server", + "value": "nginx" + }, + { + "name": "date", + "value": "Fri, 25 Sep 2020 18:26:51 GMT" + }, + { + "name": "content-type", + "value": "application/json; charset=utf-8" + }, + { + "name": "content-length", + "value": "62" + }, + { + "name": "connection", + "value": "close" + }, + { + "name": "access-control-allow-origin", + "value": "*" + }, + { + "name": "access-control-allow-methods", + "value": "GET" + }, + { + "name": "access-control-allow-headers", + "value": "*" + }, + { + "name": "etag", + "value": "W/\"3e-cw06Pdy6OiytV8KcUexnEAwoCRw\"" + }, + { + "name": "x-qloud-router", + "value": "man2-7275f28abf80.qloud-c.yandex.net" + } + ], + "headersSize": 336, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 403, + "statusText": "Forbidden" + }, + "startedDateTime": "2020-09-25T18:26:51.027Z", + "time": 236, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 236 + } + }, + { + "_id": "2b0f5eefe9e3c5b68007e213e96cddaa", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 0, + "cookies": [], + "headers": [ + { + "_fromType": "array", + "name": "user-agent", + "value": "GeocoderJS Example" + }, + { + "_fromType": "array", + "name": "accept", + "value": "*/*" + }, + { + "_fromType": "array", + "name": "accept-encoding", + "value": "gzip,deflate" + }, + { + "_fromType": "array", + "name": "connection", + "value": "close" + }, + { + "name": "host", + "value": "nominatim.openstreetmap.org" + } + ], + "headersSize": 310, + "httpVersion": "HTTP/1.1", + "method": "GET", + "queryString": [ + { + "name": "q", + "value": "1600 Pennsylvania Ave, Washington, DC" + }, + { + "name": "limit", + "value": "5" + }, + { + "name": "format", + "value": "jsonv2" + }, + { + "name": "addressdetails", + "value": "1" + }, + { + "name": "accept-language", + "value": "en_US" + } + ], + "url": "https://nominatim.openstreetmap.org/search?q=1600+Pennsylvania+Ave%2C+Washington%2C+DC&limit=5&format=jsonv2&addressdetails=1&accept-language=en_US" + }, + "response": { + "bodySize": 1385, + "content": { + "mimeType": "application/json; charset=UTF-8", + "size": 1385, + "text": "[{\"place_id\":176517326,\"licence\":\"Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright\",\"osm_type\":\"way\",\"osm_id\":397325778,\"boundingbox\":[\"38.8633822\",\"38.8637409\",\"-76.9467576\",\"-76.945632\"],\"lat\":\"38.8636383\",\"lon\":\"-76.9463651\",\"display_name\":\"Pennsylvania Avenue, Dillon Park, Coral Hills, Prince George's County, Washington, D.C., 20746-8001, United States of America\",\"place_rank\":26,\"category\":\"highway\",\"type\":\"trunk\",\"importance\":0.41000000000000003,\"address\":{\"road\":\"Pennsylvania Avenue\",\"hamlet\":\"Dillon Park\",\"locality\":\"Coral Hills\",\"county\":\"Prince George's County\",\"state\":\"Washington, D.C.\",\"postcode\":\"20746-8001\",\"country\":\"United States of America\",\"country_code\":\"us\"}},{\"place_id\":205327805,\"licence\":\"Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright\",\"osm_type\":\"way\",\"osm_id\":564931814,\"boundingbox\":[\"38.8957842\",\"38.895924\",\"-77.0309688\",\"-77.0304609\"],\"lat\":\"38.8958536\",\"lon\":\"-77.0307129\",\"display_name\":\"Pennsylvania Avenue, Penn Quarter, Washington D.C., Washington, Washington, D.C., 20045, United States of America\",\"place_rank\":27,\"category\":\"highway\",\"type\":\"path\",\"importance\":0.385,\"address\":{\"road\":\"Pennsylvania Avenue\",\"neighbourhood\":\"Penn Quarter\",\"city\":\"Washington D.C.\",\"county\":\"Washington\",\"state\":\"Washington, D.C.\",\"postcode\":\"20045\",\"country\":\"United States of America\",\"country_code\":\"us\"}}]" + }, + "cookies": [], + "headers": [ + { + "name": "server", + "value": "nginx" + }, + { + "name": "date", + "value": "Fri, 25 Sep 2020 18:26:51 GMT" + }, + { + "name": "content-type", + "value": "application/json; charset=UTF-8" + }, + { + "name": "transfer-encoding", + "value": "chunked" + }, + { + "name": "connection", + "value": "close" + }, + { + "name": "access-control-allow-origin", + "value": "*" + }, + { + "name": "access-control-allow-methods", + "value": "OPTIONS,GET" + } + ], + "headersSize": 223, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 200, + "statusText": "OK" + }, + "startedDateTime": "2020-09-25T18:26:51.268Z", + "time": 243, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 243 + } + } + ], + "pages": [], + "version": "1.2" + } +} diff --git a/spec/recordings/Chain-Geocoder-Provider_1409790517/receives-correct-geocoding-results-first-provider-OK-_2076694978/recording.har b/spec/recordings/Chain-Geocoder-Provider_1409790517/receives-correct-geocoding-results-first-provider-OK-_2076694978/recording.har new file mode 100644 index 0000000..b081bdc --- /dev/null +++ b/spec/recordings/Chain-Geocoder-Provider_1409790517/receives-correct-geocoding-results-first-provider-OK-_2076694978/recording.har @@ -0,0 +1,155 @@ +{ + "log": { + "_recordingName": "Chain Geocoder Provider/receives correct geocoding results (first provider OK)", + "creator": { + "comment": "persister:fs", + "name": "Polly.JS", + "version": "5.0.0" + }, + "entries": [ + { + "_id": "78a91888b5016be96b3a58fdabda2662", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 0, + "cookies": [], + "headers": [ + { + "_fromType": "array", + "name": "accept", + "value": "*/*" + }, + { + "_fromType": "array", + "name": "user-agent", + "value": "node-fetch/1.0 (+https://github.com/bitinn/node-fetch)" + }, + { + "_fromType": "array", + "name": "accept-encoding", + "value": "gzip,deflate" + }, + { + "_fromType": "array", + "name": "connection", + "value": "close" + }, + { + "name": "host", + "value": "geocode-maps.yandex.ru" + } + ], + "headersSize": 347, + "httpVersion": "HTTP/1.1", + "method": "GET", + "queryString": [ + { + "name": "geocode", + "value": "1600 Pennsylvania Ave, Washington, DC" + }, + { + "name": "format", + "value": "json" + }, + { + "name": "lang", + "value": "en_US" + }, + { + "name": "results", + "value": "5" + } + ], + "url": "https://geocode-maps.yandex.ru/1.x?geocode=1600+Pennsylvania+Ave%2C+Washington%2C+DC&format=json&lang=en_US&results=5" + }, + "response": { + "bodySize": 1403, + "content": { + "_isBinary": true, + "mimeType": "application/json; charset=utf-8", + "size": 1403, + "text": "[\"1f8b0800000000000003e4945d6fd3301486ff4ae4eb30e5a3cec7ee4a8be062948981b840137293b3d590d8c1713aa6a9ff7dc7499a795b09bdd8540695aadace39f6fbbe6e9e1ba2a0aea4a8811cdf90b7203f2cbf43a667b228f0874b61964bd06cce343b55b202a5affbd24ce6a03ef6edeffb1af34cc1cf066a4d8e891f799e730a42d4d7c59a09ce9ce91a5ce70bab575c5c6a295c673e23ae11d114bac60e8ab30bd9881cc721d96c70064c370a0f2897a0c8f1574be69fc4d9a22a0519af5b47a4d60a40e3491a7e19999f05d7903b679a69a81d79e14c4b503c63a88e632dcfb459c4509a72c9d97dfd0fdd89069c85547a75652270c90fde7a198e9ce639baad8da40c7d6a75fdcd68352ace5aefaa641ad53cb3ac992cf1de4098d031d25e65af089f0b56c28804b27187a64ac9351719dc75ed92677714326305d7d63977d2edba21b4be6adcd4e67c33c43bc79be7459bf2acf784c3fee1091763de9e26def6d005ea9e59b76b2d8f856b6c945c18154cf3354c15b04effc3d5c548e02e39d9e68cbddbf1e251e02ef9b4924a3697ab0ba65a0ed8f3c55ed1779f3d2fca2539d499e2550718720f073ba31f496a696001f9ebd6e41bb186023960c685bc0235934a186a9057717ce4d1c4f7a813264749ea457e80dd4d553d2cf283491099a2d4a3c1246919742ab9686953c97a5b17869197749b518a9b19ff2f804d67b2d12b607f1b9b6c59ff0c9bee4c1d9a4df7e3fd0fd86445bf079bec780ec4262f9ca42d769238a1e92e36454769e2539ab645491c05fe6e36615d1ad030eeea422f7d3236490c493d1f9adef1cb15bef30397f27e97c3926950f522b0646536f27fef3d1d9a4a56b42f094973a800df6ba1ed86478b2370dac6bf079986880e812524494c11252d49280d03ba1b4b93200cfc0e37be17ff964a14d1d651294afc3435fecff17b0b0000ffff\",\"030097724dfd840e0000\"]" + }, + "cookies": [], + "headers": [ + { + "name": "server", + "value": "nginx" + }, + { + "name": "date", + "value": "Fri, 25 Sep 2020 18:26:53 GMT" + }, + { + "name": "content-type", + "value": "application/json; charset=utf-8" + }, + { + "name": "transfer-encoding", + "value": "chunked" + }, + { + "name": "connection", + "value": "close" + }, + { + "name": "vary", + "value": "Accept-Encoding" + }, + { + "name": "access-control-allow-origin", + "value": "*" + }, + { + "name": "access-control-allow-methods", + "value": "GET" + }, + { + "name": "access-control-allow-headers", + "value": "*" + }, + { + "name": "etag", + "value": "W/\"e84-Gkzjv5mxlcw0btuJhYzfeK72c44\"" + }, + { + "name": "x-xss-protection", + "value": "1; mode=block" + }, + { + "name": "x-content-type-options", + "value": "nosniff" + }, + { + "name": "content-encoding", + "value": "gzip" + }, + { + "name": "x-qloud-router", + "value": "man4-a74d0ae6f388.qloud-c.yandex.net" + } + ], + "headersSize": 458, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 200, + "statusText": "OK" + }, + "startedDateTime": "2020-09-25T18:26:53.060Z", + "time": 364, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 364 + } + } + ], + "pages": [], + "version": "1.2" + } +} diff --git a/spec/recordings/Chain-Geocoder-Provider_1409790517/receives-correct-geocoding-results-parallelize-first-provider-KO-_726443114/recording.har b/spec/recordings/Chain-Geocoder-Provider_1409790517/receives-correct-geocoding-results-parallelize-first-provider-KO-_726443114/recording.har new file mode 100644 index 0000000..79d0b43 --- /dev/null +++ b/spec/recordings/Chain-Geocoder-Provider_1409790517/receives-correct-geocoding-results-parallelize-first-provider-KO-_726443114/recording.har @@ -0,0 +1,254 @@ +{ + "log": { + "_recordingName": "Chain Geocoder Provider/receives correct geocoding results (parallelize - first provider KO)", + "creator": { + "comment": "persister:fs", + "name": "Polly.JS", + "version": "5.0.0" + }, + "entries": [ + { + "_id": "2b0f5eefe9e3c5b68007e213e96cddaa", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 0, + "cookies": [], + "headers": [ + { + "_fromType": "array", + "name": "user-agent", + "value": "GeocoderJS Example" + }, + { + "_fromType": "array", + "name": "accept", + "value": "*/*" + }, + { + "_fromType": "array", + "name": "accept-encoding", + "value": "gzip,deflate" + }, + { + "_fromType": "array", + "name": "connection", + "value": "close" + }, + { + "name": "host", + "value": "nominatim.openstreetmap.org" + } + ], + "headersSize": 310, + "httpVersion": "HTTP/1.1", + "method": "GET", + "queryString": [ + { + "name": "q", + "value": "1600 Pennsylvania Ave, Washington, DC" + }, + { + "name": "limit", + "value": "5" + }, + { + "name": "format", + "value": "jsonv2" + }, + { + "name": "addressdetails", + "value": "1" + }, + { + "name": "accept-language", + "value": "en_US" + } + ], + "url": "https://nominatim.openstreetmap.org/search?q=1600+Pennsylvania+Ave%2C+Washington%2C+DC&limit=5&format=jsonv2&addressdetails=1&accept-language=en_US" + }, + "response": { + "bodySize": 1385, + "content": { + "mimeType": "application/json; charset=UTF-8", + "size": 1385, + "text": "[{\"place_id\":176517326,\"licence\":\"Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright\",\"osm_type\":\"way\",\"osm_id\":397325778,\"boundingbox\":[\"38.8633822\",\"38.8637409\",\"-76.9467576\",\"-76.945632\"],\"lat\":\"38.8636383\",\"lon\":\"-76.9463651\",\"display_name\":\"Pennsylvania Avenue, Dillon Park, Coral Hills, Prince George's County, Washington, D.C., 20746-8001, United States of America\",\"place_rank\":26,\"category\":\"highway\",\"type\":\"trunk\",\"importance\":0.41000000000000003,\"address\":{\"road\":\"Pennsylvania Avenue\",\"hamlet\":\"Dillon Park\",\"locality\":\"Coral Hills\",\"county\":\"Prince George's County\",\"state\":\"Washington, D.C.\",\"postcode\":\"20746-8001\",\"country\":\"United States of America\",\"country_code\":\"us\"}},{\"place_id\":205327805,\"licence\":\"Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright\",\"osm_type\":\"way\",\"osm_id\":564931814,\"boundingbox\":[\"38.8957842\",\"38.895924\",\"-77.0309688\",\"-77.0304609\"],\"lat\":\"38.8958536\",\"lon\":\"-77.0307129\",\"display_name\":\"Pennsylvania Avenue, Penn Quarter, Washington D.C., Washington, Washington, D.C., 20045, United States of America\",\"place_rank\":27,\"category\":\"highway\",\"type\":\"path\",\"importance\":0.385,\"address\":{\"road\":\"Pennsylvania Avenue\",\"neighbourhood\":\"Penn Quarter\",\"city\":\"Washington D.C.\",\"county\":\"Washington\",\"state\":\"Washington, D.C.\",\"postcode\":\"20045\",\"country\":\"United States of America\",\"country_code\":\"us\"}}]" + }, + "cookies": [], + "headers": [ + { + "name": "server", + "value": "nginx" + }, + { + "name": "date", + "value": "Fri, 25 Sep 2020 18:26:50 GMT" + }, + { + "name": "content-type", + "value": "application/json; charset=UTF-8" + }, + { + "name": "transfer-encoding", + "value": "chunked" + }, + { + "name": "connection", + "value": "close" + }, + { + "name": "access-control-allow-origin", + "value": "*" + }, + { + "name": "access-control-allow-methods", + "value": "OPTIONS,GET" + } + ], + "headersSize": 223, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 200, + "statusText": "OK" + }, + "startedDateTime": "2020-09-25T18:26:50.035Z", + "time": 159, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 159 + } + }, + { + "_id": "78a91888b5016be96b3a58fdabda2662", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 0, + "cookies": [], + "headers": [ + { + "_fromType": "array", + "name": "accept", + "value": "*/*" + }, + { + "_fromType": "array", + "name": "user-agent", + "value": "node-fetch/1.0 (+https://github.com/bitinn/node-fetch)" + }, + { + "_fromType": "array", + "name": "accept-encoding", + "value": "gzip,deflate" + }, + { + "_fromType": "array", + "name": "connection", + "value": "close" + }, + { + "name": "host", + "value": "geocode-maps.yandex.ru" + } + ], + "headersSize": 318, + "httpVersion": "HTTP/1.1", + "method": "GET", + "queryString": [ + { + "name": "geocode", + "value": "1600 Pennsylvania Ave, Washington, DC" + }, + { + "name": "format", + "value": "json" + }, + { + "name": "lang", + "value": "en_US" + }, + { + "name": "results", + "value": "5" + } + ], + "url": "https://geocode-maps.yandex.ru/1.x?geocode=1600+Pennsylvania+Ave%2C+Washington%2C+DC&format=json&lang=en_US&results=5" + }, + "response": { + "bodySize": 62, + "content": { + "mimeType": "application/json; charset=utf-8", + "size": 62, + "text": "{\"statusCode\":403,\"error\":\"Forbidden\",\"message\":\"Invalid key\"}" + }, + "cookies": [], + "headers": [ + { + "name": "server", + "value": "nginx" + }, + { + "name": "date", + "value": "Fri, 25 Sep 2020 18:26:50 GMT" + }, + { + "name": "content-type", + "value": "application/json; charset=utf-8" + }, + { + "name": "content-length", + "value": "62" + }, + { + "name": "connection", + "value": "close" + }, + { + "name": "access-control-allow-origin", + "value": "*" + }, + { + "name": "access-control-allow-methods", + "value": "GET" + }, + { + "name": "access-control-allow-headers", + "value": "*" + }, + { + "name": "etag", + "value": "W/\"3e-cw06Pdy6OiytV8KcUexnEAwoCRw\"" + }, + { + "name": "x-qloud-router", + "value": "man2-7275f28abf80.qloud-c.yandex.net" + } + ], + "headersSize": 336, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 403, + "statusText": "Forbidden" + }, + "startedDateTime": "2020-09-25T18:26:50.035Z", + "time": 242, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 242 + } + } + ], + "pages": [], + "version": "1.2" + } +} diff --git a/spec/recordings/Chain-Geocoder-Provider_1409790517/receives-correct-geocoding-results-parallelize-first-provider-OK-_2565153306/recording.har b/spec/recordings/Chain-Geocoder-Provider_1409790517/receives-correct-geocoding-results-parallelize-first-provider-OK-_2565153306/recording.har new file mode 100644 index 0000000..9233a78 --- /dev/null +++ b/spec/recordings/Chain-Geocoder-Provider_1409790517/receives-correct-geocoding-results-parallelize-first-provider-OK-_2565153306/recording.har @@ -0,0 +1,271 @@ +{ + "log": { + "_recordingName": "Chain Geocoder Provider/receives correct geocoding results (parallelize - first provider OK)", + "creator": { + "comment": "persister:fs", + "name": "Polly.JS", + "version": "5.0.0" + }, + "entries": [ + { + "_id": "2b0f5eefe9e3c5b68007e213e96cddaa", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 0, + "cookies": [], + "headers": [ + { + "_fromType": "array", + "name": "user-agent", + "value": "GeocoderJS Example" + }, + { + "_fromType": "array", + "name": "accept", + "value": "*/*" + }, + { + "_fromType": "array", + "name": "accept-encoding", + "value": "gzip,deflate" + }, + { + "_fromType": "array", + "name": "connection", + "value": "close" + }, + { + "name": "host", + "value": "nominatim.openstreetmap.org" + } + ], + "headersSize": 310, + "httpVersion": "HTTP/1.1", + "method": "GET", + "queryString": [ + { + "name": "q", + "value": "1600 Pennsylvania Ave, Washington, DC" + }, + { + "name": "limit", + "value": "5" + }, + { + "name": "format", + "value": "jsonv2" + }, + { + "name": "addressdetails", + "value": "1" + }, + { + "name": "accept-language", + "value": "en_US" + } + ], + "url": "https://nominatim.openstreetmap.org/search?q=1600+Pennsylvania+Ave%2C+Washington%2C+DC&limit=5&format=jsonv2&addressdetails=1&accept-language=en_US" + }, + "response": { + "bodySize": 1385, + "content": { + "mimeType": "application/json; charset=UTF-8", + "size": 1385, + "text": "[{\"place_id\":176517326,\"licence\":\"Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright\",\"osm_type\":\"way\",\"osm_id\":397325778,\"boundingbox\":[\"38.8633822\",\"38.8637409\",\"-76.9467576\",\"-76.945632\"],\"lat\":\"38.8636383\",\"lon\":\"-76.9463651\",\"display_name\":\"Pennsylvania Avenue, Dillon Park, Coral Hills, Prince George's County, Washington, D.C., 20746-8001, United States of America\",\"place_rank\":26,\"category\":\"highway\",\"type\":\"trunk\",\"importance\":0.41000000000000003,\"address\":{\"road\":\"Pennsylvania Avenue\",\"hamlet\":\"Dillon Park\",\"locality\":\"Coral Hills\",\"county\":\"Prince George's County\",\"state\":\"Washington, D.C.\",\"postcode\":\"20746-8001\",\"country\":\"United States of America\",\"country_code\":\"us\"}},{\"place_id\":205327805,\"licence\":\"Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright\",\"osm_type\":\"way\",\"osm_id\":564931814,\"boundingbox\":[\"38.8957842\",\"38.895924\",\"-77.0309688\",\"-77.0304609\"],\"lat\":\"38.8958536\",\"lon\":\"-77.0307129\",\"display_name\":\"Pennsylvania Avenue, Penn Quarter, Washington D.C., Washington, Washington, D.C., 20045, United States of America\",\"place_rank\":27,\"category\":\"highway\",\"type\":\"path\",\"importance\":0.385,\"address\":{\"road\":\"Pennsylvania Avenue\",\"neighbourhood\":\"Penn Quarter\",\"city\":\"Washington D.C.\",\"county\":\"Washington\",\"state\":\"Washington, D.C.\",\"postcode\":\"20045\",\"country\":\"United States of America\",\"country_code\":\"us\"}}]" + }, + "cookies": [], + "headers": [ + { + "name": "server", + "value": "nginx" + }, + { + "name": "date", + "value": "Fri, 25 Sep 2020 18:26:51 GMT" + }, + { + "name": "content-type", + "value": "application/json; charset=UTF-8" + }, + { + "name": "transfer-encoding", + "value": "chunked" + }, + { + "name": "connection", + "value": "close" + }, + { + "name": "access-control-allow-origin", + "value": "*" + }, + { + "name": "access-control-allow-methods", + "value": "OPTIONS,GET" + } + ], + "headersSize": 223, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 200, + "statusText": "OK" + }, + "startedDateTime": "2020-09-25T18:26:50.529Z", + "time": 207, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 207 + } + }, + { + "_id": "78a91888b5016be96b3a58fdabda2662", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 0, + "cookies": [], + "headers": [ + { + "_fromType": "array", + "name": "accept", + "value": "*/*" + }, + { + "_fromType": "array", + "name": "user-agent", + "value": "node-fetch/1.0 (+https://github.com/bitinn/node-fetch)" + }, + { + "_fromType": "array", + "name": "accept-encoding", + "value": "gzip,deflate" + }, + { + "_fromType": "array", + "name": "connection", + "value": "close" + }, + { + "name": "host", + "value": "geocode-maps.yandex.ru" + } + ], + "headersSize": 347, + "httpVersion": "HTTP/1.1", + "method": "GET", + "queryString": [ + { + "name": "geocode", + "value": "1600 Pennsylvania Ave, Washington, DC" + }, + { + "name": "format", + "value": "json" + }, + { + "name": "lang", + "value": "en_US" + }, + { + "name": "results", + "value": "5" + } + ], + "url": "https://geocode-maps.yandex.ru/1.x?geocode=1600+Pennsylvania+Ave%2C+Washington%2C+DC&format=json&lang=en_US&results=5" + }, + "response": { + "bodySize": 1403, + "content": { + "_isBinary": true, + "mimeType": "application/json; charset=utf-8", + "size": 1403, + "text": "[\"1f8b0800000000000003e4945d6fd3301486ff4ae4eb30e5a3cec7ee4a8be062948981b840137293b3d590d8c1713aa6a9ff7dc7499a795b09bdd8540695aadace39f6fbbe6e9e1ba2a0aea4a8811cdf90b7203f2cbf43a667b228f0874b61964bd06cce343b55b202a5affbd24ce6a03ef6edeffb1af34cc1cf066a4d8e891f799e730a42d4d7c59a09ce9ce91a5ce70bab575c5c6a295c673e23ae11d114bac60e8ab30bd9881cc721d96c70064c370a0f2897a0c8f1574be69fc4d9a22a0519af5b47a4d60a40e3491a7e19999f05d7903b679a69a81d79e14c4b503c63a88e632dcfb459c4509a72c9d97dfd0fdd89069c85547a75652270c90fde7a198e9ce639baad8da40c7d6a75fdcd68352ace5aefaa641ad53cb3ac992cf1de4098d031d25e65af089f0b56c28804b27187a64ac9351719dc75ed92677714326305d7d63977d2edba21b4be6adcd4e67c33c43bc79be7459bf2acf784c3fee1091763de9e26def6d005ea9e59b76b2d8f856b6c945c18154cf3354c15b04effc3d5c548e02e39d9e68cbddbf1e251e02ef9b4924a3697ab0ba65a0ed8f3c55ed1779f3d2fca2539d499e2550718720f073ba31f496a696001f9ebd6e41bb186023960c685bc0235934a186a9057717ce4d1c4f7a813264749ea457e80dd4d553d2cf283491099a2d4a3c1246919742ab9686953c97a5b17869197749b518a9b19ff2f804d67b2d12b607f1b9b6c59ff0c9bee4c1d9a4df7e3fd0fd86445bf079bec780ec4262f9ca42d769238a1e92e36454769e2539ab645491c05fe6e36615d1ad030eeea422f7d3236490c493d1f9adef1cb15bef30397f27e97c3926950f522b0646536f27fef3d1d9a4a56b42f094973a800df6ba1ed86478b2370dac6bf079986880e812524494c11252d49280d03ba1b4b93200cfc0e37be17ff964a14d1d651294afc3435fecff17b0b0000ffff\",\"030097724dfd840e0000\"]" + }, + "cookies": [], + "headers": [ + { + "name": "server", + "value": "nginx" + }, + { + "name": "date", + "value": "Fri, 25 Sep 2020 18:26:50 GMT" + }, + { + "name": "content-type", + "value": "application/json; charset=utf-8" + }, + { + "name": "transfer-encoding", + "value": "chunked" + }, + { + "name": "connection", + "value": "close" + }, + { + "name": "vary", + "value": "Accept-Encoding" + }, + { + "name": "access-control-allow-origin", + "value": "*" + }, + { + "name": "access-control-allow-methods", + "value": "GET" + }, + { + "name": "access-control-allow-headers", + "value": "*" + }, + { + "name": "etag", + "value": "W/\"e84-Gkzjv5mxlcw0btuJhYzfeK72c44\"" + }, + { + "name": "x-xss-protection", + "value": "1; mode=block" + }, + { + "name": "x-content-type-options", + "value": "nosniff" + }, + { + "name": "content-encoding", + "value": "gzip" + }, + { + "name": "x-qloud-router", + "value": "man4-3cc44c5fdead.qloud-c.yandex.net" + } + ], + "headersSize": 458, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 200, + "statusText": "OK" + }, + "startedDateTime": "2020-09-25T18:26:50.529Z", + "time": 334, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 334 + } + } + ], + "pages": [], + "version": "1.2" + } +} diff --git a/spec/recordings/Chain-Geocoder-Provider_1409790517/receives-correct-geodecoding-results-first-first-provider-OK-_48817278/recording.har b/spec/recordings/Chain-Geocoder-Provider_1409790517/receives-correct-geodecoding-results-first-first-provider-OK-_48817278/recording.har new file mode 100644 index 0000000..ad1141c --- /dev/null +++ b/spec/recordings/Chain-Geocoder-Provider_1409790517/receives-correct-geodecoding-results-first-first-provider-OK-_48817278/recording.har @@ -0,0 +1,275 @@ +{ + "log": { + "_recordingName": "Chain Geocoder Provider/receives correct geodecoding results (first - first provider OK)", + "creator": { + "comment": "persister:fs", + "name": "Polly.JS", + "version": "5.0.0" + }, + "entries": [ + { + "_id": "c47d435f272384213688cbf36c8295a1", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 0, + "cookies": [], + "headers": [ + { + "_fromType": "array", + "name": "accept", + "value": "*/*" + }, + { + "_fromType": "array", + "name": "user-agent", + "value": "node-fetch/1.0 (+https://github.com/bitinn/node-fetch)" + }, + { + "_fromType": "array", + "name": "accept-encoding", + "value": "gzip,deflate" + }, + { + "_fromType": "array", + "name": "connection", + "value": "close" + }, + { + "name": "host", + "value": "geocode-maps.yandex.ru" + } + ], + "headersSize": 319, + "httpVersion": "HTTP/1.1", + "method": "GET", + "queryString": [ + { + "name": "geocode", + "value": "2.388911,48.8631507" + }, + { + "name": "format", + "value": "json" + }, + { + "name": "lang", + "value": "en_US" + }, + { + "name": "results", + "value": "5" + } + ], + "url": "https://geocode-maps.yandex.ru/1.x?geocode=2.388911%2C48.8631507&format=json&lang=en_US&results=5" + }, + "response": { + "bodySize": 1831, + "content": { + "_isBinary": true, + "mimeType": "application/json; charset=utf-8", + "size": 1831, + "text": "[\"1f8b0800000000000003ec97df6ed33014c65f25f27536e56f9becae74b09b51cac41d42c84bcea821b183e3944d535f81e7e03df6621c3b7fe6b55d2744a960a23775eccff677bea3fcd6dd12097525780de4e4969c817873f91932351545815f4c703d5d82a2a754d1b914154875d349339183bce8b6bfee347a6d2e18577a50899a9c90e0384c92d4f79d28394e46a11ffb64e5e2bd5f1ba895b5eef6ebde98e8f5ba2994de1fe3d39568788ee394ac70ef1550d548bcb3bc04494ede5bce9ff26bfbac2464ac364512b8a6b8d9250aaeb5a75792f20c5ce7ee7b0147391cf5cf732a59ed3a8107ce444ac17356d7500257ae3359026fc039a3684a29ea3abe87e77d61c6f7423418b14b26798e85d5faf60c4b52f2e6a3b6a56fbc3065ca922a05f93e2d4c45892d42416da2ea1c75d7e33aa7250cf7616b064925c592e9b941f3d08bad2d44460ba6acf38c4f5b8246956426e54eb25983ad473580a55eabce96f6f1764a2c7bf56135a47d8a3d6785097dda558dc36ef19cf1fbeaf792b6b961864ea65667ade9fbacb5c392719d0b556c091309b4b5b63e3bdb9abf4bcefbd871573f9ed9f9bbe4142ae0393ab6b51b93b3473be292770b2145f3697145a5c184fd3cdbde1b97cc2594acc54a379c35edeb6adad37f1eeb6e97660e752659d592688bbba141eb7d1b12bad4e480fc8529fb255f428150d0e3427c033915921b4f1a42d1388d5a487961eae3dea6aa1e4ad2d0f33a499c788981d136da2569dad34e97eaee0150c3dbb03742ddd36938fba0787ace683a387dfe93a78ffe09b01c1a2ac8820e0541b805299137f2bcd028c6f13808b62325f2bc20698f19a571b22fa608b5401bbf8394b70d958a8174f2c699dffd9070744eb30565e6aff1e6cbf567f9b2d3ccbf8a9a4dfd8e320fc09d27427e7608faa51376b5669d4b3b833c28a28271d2fe73168fa2603ba3c2a8fdd933f6127ff4c8cf9e34f0fdb88554e425d15f01a99f000000ffff\",\"dc96311202210c45afb24720d910a0b7b6b1b6d0597b1b0fe539bc98b0c40227b19165460b2a98c04f783fa9351c6b443f67365b9bc63f1ac33bcdaaea96e27ec4068e5c892566220d59e07996b1221298630522b38c2711d36064778ffb357be09aad69b94cafdac8e73d95ba7741d5ba6808a62243cee94fe985a02d74247e87db590fa16eec3f26a6a5ccd2d782f63d618884b872019e126b8071ca6dae1c490ec181d113d1395f03f91852287a8e793d010000ffff0300377eef3b77150000\"]" + }, + "cookies": [], + "headers": [ + { + "name": "server", + "value": "nginx" + }, + { + "name": "date", + "value": "Fri, 25 Sep 2020 18:26:49 GMT" + }, + { + "name": "content-type", + "value": "application/json; charset=utf-8" + }, + { + "name": "transfer-encoding", + "value": "chunked" + }, + { + "name": "connection", + "value": "close" + }, + { + "name": "vary", + "value": "Accept-Encoding" + }, + { + "name": "access-control-allow-origin", + "value": "*" + }, + { + "name": "access-control-allow-methods", + "value": "GET" + }, + { + "name": "access-control-allow-headers", + "value": "*" + }, + { + "name": "etag", + "value": "W/\"1577-SWIN9f7y+Rbp/Lt3aKBPRwRs6GI\"" + }, + { + "name": "x-xss-protection", + "value": "1; mode=block" + }, + { + "name": "x-content-type-options", + "value": "nosniff" + }, + { + "name": "content-encoding", + "value": "gzip" + }, + { + "name": "x-qloud-router", + "value": "man1-043f58f4a44d.qloud-c.yandex.net" + } + ], + "headersSize": 459, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 200, + "statusText": "OK" + }, + "startedDateTime": "2020-09-25T18:26:49.346Z", + "time": 348, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 348 + } + }, + { + "_id": "ab80792b623842c4091acf071a05be52", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 0, + "cookies": [], + "headers": [ + { + "_fromType": "array", + "name": "user-agent", + "value": "GeocoderJS Example" + }, + { + "_fromType": "array", + "name": "accept", + "value": "*/*" + }, + { + "_fromType": "array", + "name": "accept-encoding", + "value": "gzip,deflate" + }, + { + "_fromType": "array", + "name": "connection", + "value": "close" + }, + { + "name": "host", + "value": "nominatim.openstreetmap.org" + } + ], + "headersSize": 287, + "httpVersion": "HTTP/1.1", + "method": "GET", + "queryString": [ + { + "name": "lat", + "value": "48.8631507" + }, + { + "name": "lon", + "value": "2.388911" + }, + { + "name": "zoom", + "value": "18" + }, + { + "name": "format", + "value": "jsonv2" + }, + { + "name": "addressdetails", + "value": "1" + }, + { + "name": "accept-language", + "value": "en_US" + } + ], + "url": "https://nominatim.openstreetmap.org/reverse?lat=48.8631507&lon=2.388911&zoom=18&format=jsonv2&addressdetails=1&accept-language=en_US" + }, + "response": { + "bodySize": 674, + "content": { + "mimeType": "application/json; charset=UTF-8", + "size": 674, + "text": "{\"place_id\":161274479,\"licence\":\"Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright\",\"osm_type\":\"way\",\"osm_id\":322777831,\"lat\":\"48.863744499999996\",\"lon\":\"2.3911562136123106\",\"place_rank\":26,\"category\":\"highway\",\"type\":\"pedestrian\",\"importance\":0.09999999999999998,\"addresstype\":\"road\",\"name\":null,\"display_name\":\"Quartier du Père-Lachaise, Paris, Ile-de-France, Metropolitan France, 75020, France\",\"address\":{\"suburb\":\"Quartier du Père-Lachaise\",\"city\":\"Paris\",\"municipality\":\"Paris\",\"county\":\"Paris\",\"state\":\"Ile-de-France\",\"country\":\"France\",\"postcode\":\"75020\",\"country_code\":\"fr\"},\"boundingbox\":[\"48.8625929\",\"48.8648832\",\"2.3877078\",\"2.3956964\"]}" + }, + "cookies": [], + "headers": [ + { + "name": "server", + "value": "nginx" + }, + { + "name": "date", + "value": "Fri, 25 Sep 2020 18:26:49 GMT" + }, + { + "name": "content-type", + "value": "application/json; charset=UTF-8" + }, + { + "name": "transfer-encoding", + "value": "chunked" + }, + { + "name": "connection", + "value": "close" + }, + { + "name": "access-control-allow-origin", + "value": "*" + }, + { + "name": "access-control-allow-methods", + "value": "OPTIONS,GET" + } + ], + "headersSize": 223, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 200, + "statusText": "OK" + }, + "startedDateTime": "2020-08-14T18:24:50.603Z", + "time": 457, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 457 + } + } + ], + "pages": [], + "version": "1.2" + } +} diff --git a/spec/recordings/Chain-Geocoder-Provider_1409790517/receives-correct-geodecoding-results-first-provider-KO-_1165577657/recording.har b/spec/recordings/Chain-Geocoder-Provider_1409790517/receives-correct-geodecoding-results-first-provider-KO-_1165577657/recording.har new file mode 100644 index 0000000..d46f5a5 --- /dev/null +++ b/spec/recordings/Chain-Geocoder-Provider_1409790517/receives-correct-geodecoding-results-first-provider-KO-_1165577657/recording.har @@ -0,0 +1,258 @@ +{ + "log": { + "_recordingName": "Chain Geocoder Provider/receives correct geodecoding results (first provider KO)", + "creator": { + "comment": "persister:fs", + "name": "Polly.JS", + "version": "5.0.0" + }, + "entries": [ + { + "_id": "c47d435f272384213688cbf36c8295a1", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 0, + "cookies": [], + "headers": [ + { + "_fromType": "array", + "name": "accept", + "value": "*/*" + }, + { + "_fromType": "array", + "name": "user-agent", + "value": "node-fetch/1.0 (+https://github.com/bitinn/node-fetch)" + }, + { + "_fromType": "array", + "name": "accept-encoding", + "value": "gzip,deflate" + }, + { + "_fromType": "array", + "name": "connection", + "value": "close" + }, + { + "name": "host", + "value": "geocode-maps.yandex.ru" + } + ], + "headersSize": 290, + "httpVersion": "HTTP/1.1", + "method": "GET", + "queryString": [ + { + "name": "geocode", + "value": "2.388911,48.8631507" + }, + { + "name": "format", + "value": "json" + }, + { + "name": "lang", + "value": "en_US" + }, + { + "name": "results", + "value": "5" + } + ], + "url": "https://geocode-maps.yandex.ru/1.x?geocode=2.388911%2C48.8631507&format=json&lang=en_US&results=5" + }, + "response": { + "bodySize": 62, + "content": { + "mimeType": "application/json; charset=utf-8", + "size": 62, + "text": "{\"statusCode\":403,\"error\":\"Forbidden\",\"message\":\"Invalid key\"}" + }, + "cookies": [], + "headers": [ + { + "name": "server", + "value": "nginx" + }, + { + "name": "date", + "value": "Fri, 25 Sep 2020 18:26:52 GMT" + }, + { + "name": "content-type", + "value": "application/json; charset=utf-8" + }, + { + "name": "content-length", + "value": "62" + }, + { + "name": "connection", + "value": "close" + }, + { + "name": "access-control-allow-origin", + "value": "*" + }, + { + "name": "access-control-allow-methods", + "value": "GET" + }, + { + "name": "access-control-allow-headers", + "value": "*" + }, + { + "name": "etag", + "value": "W/\"3e-cw06Pdy6OiytV8KcUexnEAwoCRw\"" + }, + { + "name": "x-qloud-router", + "value": "man1-043f58f4a44d.qloud-c.yandex.net" + } + ], + "headersSize": 336, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 403, + "statusText": "Forbidden" + }, + "startedDateTime": "2020-09-25T18:26:51.999Z", + "time": 265, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 265 + } + }, + { + "_id": "ab80792b623842c4091acf071a05be52", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 0, + "cookies": [], + "headers": [ + { + "_fromType": "array", + "name": "user-agent", + "value": "GeocoderJS Example" + }, + { + "_fromType": "array", + "name": "accept", + "value": "*/*" + }, + { + "_fromType": "array", + "name": "accept-encoding", + "value": "gzip,deflate" + }, + { + "_fromType": "array", + "name": "connection", + "value": "close" + }, + { + "name": "host", + "value": "nominatim.openstreetmap.org" + } + ], + "headersSize": 287, + "httpVersion": "HTTP/1.1", + "method": "GET", + "queryString": [ + { + "name": "lat", + "value": "48.8631507" + }, + { + "name": "lon", + "value": "2.388911" + }, + { + "name": "zoom", + "value": "18" + }, + { + "name": "format", + "value": "jsonv2" + }, + { + "name": "addressdetails", + "value": "1" + }, + { + "name": "accept-language", + "value": "en_US" + } + ], + "url": "https://nominatim.openstreetmap.org/reverse?lat=48.8631507&lon=2.388911&zoom=18&format=jsonv2&addressdetails=1&accept-language=en_US" + }, + "response": { + "bodySize": 674, + "content": { + "mimeType": "application/json; charset=UTF-8", + "size": 674, + "text": "{\"place_id\":161274479,\"licence\":\"Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright\",\"osm_type\":\"way\",\"osm_id\":322777831,\"lat\":\"48.863744499999996\",\"lon\":\"2.3911562136123106\",\"place_rank\":26,\"category\":\"highway\",\"type\":\"pedestrian\",\"importance\":0.09999999999999998,\"addresstype\":\"road\",\"name\":null,\"display_name\":\"Quartier du Père-Lachaise, Paris, Ile-de-France, Metropolitan France, 75020, France\",\"address\":{\"suburb\":\"Quartier du Père-Lachaise\",\"city\":\"Paris\",\"municipality\":\"Paris\",\"county\":\"Paris\",\"state\":\"Ile-de-France\",\"country\":\"France\",\"postcode\":\"75020\",\"country_code\":\"fr\"},\"boundingbox\":[\"48.8625929\",\"48.8648832\",\"2.3877078\",\"2.3956964\"]}" + }, + "cookies": [], + "headers": [ + { + "name": "server", + "value": "nginx" + }, + { + "name": "date", + "value": "Fri, 25 Sep 2020 18:26:52 GMT" + }, + { + "name": "content-type", + "value": "application/json; charset=UTF-8" + }, + { + "name": "transfer-encoding", + "value": "chunked" + }, + { + "name": "connection", + "value": "close" + }, + { + "name": "access-control-allow-origin", + "value": "*" + }, + { + "name": "access-control-allow-methods", + "value": "OPTIONS,GET" + } + ], + "headersSize": 223, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 200, + "statusText": "OK" + }, + "startedDateTime": "2020-09-25T18:26:52.277Z", + "time": 217, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 217 + } + } + ], + "pages": [], + "version": "1.2" + } +} diff --git a/spec/recordings/Chain-Geocoder-Provider_1409790517/receives-correct-geodecoding-results-first-provider-OK-_2544227361/recording.har b/spec/recordings/Chain-Geocoder-Provider_1409790517/receives-correct-geodecoding-results-first-provider-OK-_2544227361/recording.har new file mode 100644 index 0000000..ae256d1 --- /dev/null +++ b/spec/recordings/Chain-Geocoder-Provider_1409790517/receives-correct-geodecoding-results-first-provider-OK-_2544227361/recording.har @@ -0,0 +1,155 @@ +{ + "log": { + "_recordingName": "Chain Geocoder Provider/receives correct geodecoding results (first provider OK)", + "creator": { + "comment": "persister:fs", + "name": "Polly.JS", + "version": "5.0.0" + }, + "entries": [ + { + "_id": "c47d435f272384213688cbf36c8295a1", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 0, + "cookies": [], + "headers": [ + { + "_fromType": "array", + "name": "accept", + "value": "*/*" + }, + { + "_fromType": "array", + "name": "user-agent", + "value": "node-fetch/1.0 (+https://github.com/bitinn/node-fetch)" + }, + { + "_fromType": "array", + "name": "accept-encoding", + "value": "gzip,deflate" + }, + { + "_fromType": "array", + "name": "connection", + "value": "close" + }, + { + "name": "host", + "value": "geocode-maps.yandex.ru" + } + ], + "headersSize": 319, + "httpVersion": "HTTP/1.1", + "method": "GET", + "queryString": [ + { + "name": "geocode", + "value": "2.388911,48.8631507" + }, + { + "name": "format", + "value": "json" + }, + { + "name": "lang", + "value": "en_US" + }, + { + "name": "results", + "value": "5" + } + ], + "url": "https://geocode-maps.yandex.ru/1.x?geocode=2.388911%2C48.8631507&format=json&lang=en_US&results=5" + }, + "response": { + "bodySize": 1831, + "content": { + "_isBinary": true, + "mimeType": "application/json; charset=utf-8", + "size": 1831, + "text": "[\"1f8b0800000000000003ec97df6ed33014c65f25f27536e56f9becae74b09b51cac41d42c84bcea821b183e3944d535f81e7e03df6621c3b7fe6b55d2744a960a23775eccff677bea3fcd6dd12097525780de4e4969c817873f91932351545815f4c703d5d82a2a754d1b914154875d349339183bce8b6bfee347a6d2e18577a50899a9c90e0384c92d4f79d28394e46a11ffb64e5e2bd5f1ba895b5eef6ebde98e8f5ba2994de1fe3d39568788ee394ac70ef1550d548bcb3bc04494ede5bce9ff26bfbac2464ac364512b8a6b8d9250aaeb5a75792f20c5ce7ee7b0147391cf5cf732a59ed3a8107ce444ac17356d7500257ae3359026fc039a3684a29ea3abe87e77d61c6f7423418b14b26798e85d5faf60c4b52f2e6a3b6a56fbc3065ca922a05f93e2d4c45892d42416da2ea1c75d7e33aa7250cf7616b064925c592e9b941f3d08bad2d44460ba6acf38c4f5b8246956426e54eb25983ad473580a55eabce96f6f1764a2c7bf56135a47d8a3d6785097dda558dc36ef19cf1fbeaf792b6b961864ea65667ade9fbacb5c392719d0b556c091309b4b5b63e3bdb9abf4bcefbd871573f9ed9f9bbe4142ae0393ab6b51b93b3473be292770b2145f3697145a5c184fd3cdbde1b97cc2594acc54a379c35edeb6adad37f1eeb6e97660e752659d592688bbba141eb7d1b12bad4e480fc8529fb255f428150d0e3427c033915921b4f1a42d1388d5a487961eae3dea6aa1e4ad2d0f33a499c788981d136da2569dad34e97eaee0150c3dbb03742ddd36938fba0787ace683a387dfe93a78ffe09b01c1a2ac8820e0541b805299137f2bcd028c6f13808b62325f2bc20698f19a571b22fa608b5401bbf8394b70d958a8174f2c699dffd9070744eb30565e6aff1e6cbf567f9b2d3ccbf8a9a4dfd8e320fc09d27427e7608faa51376b5669d4b3b833c28a28271d2fe73168fa2603ba3c2a8fdd933f6127ff4c8cf9e34f0fdb88554e425d15f01a99f000000ffff\",\"dc96311202210c45afb24720d910a0b7b6b1b6d0597b1b0fe539bc98b0c40227b19165460b2a98c04f783fa9351c6b443f67365b9bc63f1ac33bcdaaea96e27ec4068e5c892566220d59e07996b1221298630522b38c2711d36064778ffb357be09aad69b94cafdac8e73d95ba7741d5ba6808a62243cee94fe985a02d74247e87db590fa16eec3f26a6a5ccd2d782f63d618884b872019e126b8071ca6dae1c490ec181d113d1395f03f91852287a8e793d010000ffff0300377eef3b77150000\"]" + }, + "cookies": [], + "headers": [ + { + "name": "server", + "value": "nginx" + }, + { + "name": "date", + "value": "Fri, 25 Sep 2020 18:26:52 GMT" + }, + { + "name": "content-type", + "value": "application/json; charset=utf-8" + }, + { + "name": "transfer-encoding", + "value": "chunked" + }, + { + "name": "connection", + "value": "close" + }, + { + "name": "vary", + "value": "Accept-Encoding" + }, + { + "name": "access-control-allow-origin", + "value": "*" + }, + { + "name": "access-control-allow-methods", + "value": "GET" + }, + { + "name": "access-control-allow-headers", + "value": "*" + }, + { + "name": "etag", + "value": "W/\"1577-SWIN9f7y+Rbp/Lt3aKBPRwRs6GI\"" + }, + { + "name": "x-xss-protection", + "value": "1; mode=block" + }, + { + "name": "x-content-type-options", + "value": "nosniff" + }, + { + "name": "content-encoding", + "value": "gzip" + }, + { + "name": "x-qloud-router", + "value": "man4-3cc44c5fdead.qloud-c.yandex.net" + } + ], + "headersSize": 459, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 200, + "statusText": "OK" + }, + "startedDateTime": "2020-09-25T18:26:52.507Z", + "time": 241, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 241 + } + } + ], + "pages": [], + "version": "1.2" + } +} diff --git a/spec/recordings/Chain-Geocoder-Provider_1409790517/receives-correct-geodecoding-results-parallelize-first-provider-KO-_268891293/recording.har b/spec/recordings/Chain-Geocoder-Provider_1409790517/receives-correct-geodecoding-results-parallelize-first-provider-KO-_268891293/recording.har new file mode 100644 index 0000000..a657e1f --- /dev/null +++ b/spec/recordings/Chain-Geocoder-Provider_1409790517/receives-correct-geodecoding-results-parallelize-first-provider-KO-_268891293/recording.har @@ -0,0 +1,258 @@ +{ + "log": { + "_recordingName": "Chain Geocoder Provider/receives correct geodecoding results (parallelize - first provider KO)", + "creator": { + "comment": "persister:fs", + "name": "Polly.JS", + "version": "5.0.0" + }, + "entries": [ + { + "_id": "ab80792b623842c4091acf071a05be52", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 0, + "cookies": [], + "headers": [ + { + "_fromType": "array", + "name": "user-agent", + "value": "GeocoderJS Example" + }, + { + "_fromType": "array", + "name": "accept", + "value": "*/*" + }, + { + "_fromType": "array", + "name": "accept-encoding", + "value": "gzip,deflate" + }, + { + "_fromType": "array", + "name": "connection", + "value": "close" + }, + { + "name": "host", + "value": "nominatim.openstreetmap.org" + } + ], + "headersSize": 287, + "httpVersion": "HTTP/1.1", + "method": "GET", + "queryString": [ + { + "name": "lat", + "value": "48.8631507" + }, + { + "name": "lon", + "value": "2.388911" + }, + { + "name": "zoom", + "value": "18" + }, + { + "name": "format", + "value": "jsonv2" + }, + { + "name": "addressdetails", + "value": "1" + }, + { + "name": "accept-language", + "value": "en_US" + } + ], + "url": "https://nominatim.openstreetmap.org/reverse?lat=48.8631507&lon=2.388911&zoom=18&format=jsonv2&addressdetails=1&accept-language=en_US" + }, + "response": { + "bodySize": 674, + "content": { + "mimeType": "application/json; charset=UTF-8", + "size": 674, + "text": "{\"place_id\":161274479,\"licence\":\"Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright\",\"osm_type\":\"way\",\"osm_id\":322777831,\"lat\":\"48.863744499999996\",\"lon\":\"2.3911562136123106\",\"place_rank\":26,\"category\":\"highway\",\"type\":\"pedestrian\",\"importance\":0.09999999999999998,\"addresstype\":\"road\",\"name\":null,\"display_name\":\"Quartier du Père-Lachaise, Paris, Ile-de-France, Metropolitan France, 75020, France\",\"address\":{\"suburb\":\"Quartier du Père-Lachaise\",\"city\":\"Paris\",\"municipality\":\"Paris\",\"county\":\"Paris\",\"state\":\"Ile-de-France\",\"country\":\"France\",\"postcode\":\"75020\",\"country_code\":\"fr\"},\"boundingbox\":[\"48.8625929\",\"48.8648832\",\"2.3877078\",\"2.3956964\"]}" + }, + "cookies": [], + "headers": [ + { + "name": "server", + "value": "nginx" + }, + { + "name": "date", + "value": "Fri, 25 Sep 2020 18:26:51 GMT" + }, + { + "name": "content-type", + "value": "application/json; charset=UTF-8" + }, + { + "name": "transfer-encoding", + "value": "chunked" + }, + { + "name": "connection", + "value": "close" + }, + { + "name": "access-control-allow-origin", + "value": "*" + }, + { + "name": "access-control-allow-methods", + "value": "OPTIONS,GET" + } + ], + "headersSize": 223, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 200, + "statusText": "OK" + }, + "startedDateTime": "2020-09-25T18:26:51.531Z", + "time": 81, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 81 + } + }, + { + "_id": "c47d435f272384213688cbf36c8295a1", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 0, + "cookies": [], + "headers": [ + { + "_fromType": "array", + "name": "accept", + "value": "*/*" + }, + { + "_fromType": "array", + "name": "user-agent", + "value": "node-fetch/1.0 (+https://github.com/bitinn/node-fetch)" + }, + { + "_fromType": "array", + "name": "accept-encoding", + "value": "gzip,deflate" + }, + { + "_fromType": "array", + "name": "connection", + "value": "close" + }, + { + "name": "host", + "value": "geocode-maps.yandex.ru" + } + ], + "headersSize": 290, + "httpVersion": "HTTP/1.1", + "method": "GET", + "queryString": [ + { + "name": "geocode", + "value": "2.388911,48.8631507" + }, + { + "name": "format", + "value": "json" + }, + { + "name": "lang", + "value": "en_US" + }, + { + "name": "results", + "value": "5" + } + ], + "url": "https://geocode-maps.yandex.ru/1.x?geocode=2.388911%2C48.8631507&format=json&lang=en_US&results=5" + }, + "response": { + "bodySize": 62, + "content": { + "mimeType": "application/json; charset=utf-8", + "size": 62, + "text": "{\"statusCode\":403,\"error\":\"Forbidden\",\"message\":\"Invalid key\"}" + }, + "cookies": [], + "headers": [ + { + "name": "server", + "value": "nginx" + }, + { + "name": "date", + "value": "Fri, 25 Sep 2020 18:26:51 GMT" + }, + { + "name": "content-type", + "value": "application/json; charset=utf-8" + }, + { + "name": "content-length", + "value": "62" + }, + { + "name": "connection", + "value": "close" + }, + { + "name": "access-control-allow-origin", + "value": "*" + }, + { + "name": "access-control-allow-methods", + "value": "GET" + }, + { + "name": "access-control-allow-headers", + "value": "*" + }, + { + "name": "etag", + "value": "W/\"3e-cw06Pdy6OiytV8KcUexnEAwoCRw\"" + }, + { + "name": "x-qloud-router", + "value": "man4-3cc44c5fdead.qloud-c.yandex.net" + } + ], + "headersSize": 336, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 403, + "statusText": "Forbidden" + }, + "startedDateTime": "2020-09-25T18:26:51.531Z", + "time": 240, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 240 + } + } + ], + "pages": [], + "version": "1.2" + } +} diff --git a/spec/recordings/Chain-Geocoder-Provider_1409790517/receives-correct-geodecoding-results-parallelize-first-provider-OK-_4258976501/recording.har b/spec/recordings/Chain-Geocoder-Provider_1409790517/receives-correct-geodecoding-results-parallelize-first-provider-OK-_4258976501/recording.har new file mode 100644 index 0000000..774a264 --- /dev/null +++ b/spec/recordings/Chain-Geocoder-Provider_1409790517/receives-correct-geodecoding-results-parallelize-first-provider-OK-_4258976501/recording.har @@ -0,0 +1,275 @@ +{ + "log": { + "_recordingName": "Chain Geocoder Provider/receives correct geodecoding results (parallelize - first provider OK)", + "creator": { + "comment": "persister:fs", + "name": "Polly.JS", + "version": "5.0.0" + }, + "entries": [ + { + "_id": "c47d435f272384213688cbf36c8295a1", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 0, + "cookies": [], + "headers": [ + { + "_fromType": "array", + "name": "accept", + "value": "*/*" + }, + { + "_fromType": "array", + "name": "user-agent", + "value": "node-fetch/1.0 (+https://github.com/bitinn/node-fetch)" + }, + { + "_fromType": "array", + "name": "accept-encoding", + "value": "gzip,deflate" + }, + { + "_fromType": "array", + "name": "connection", + "value": "close" + }, + { + "name": "host", + "value": "geocode-maps.yandex.ru" + } + ], + "headersSize": 319, + "httpVersion": "HTTP/1.1", + "method": "GET", + "queryString": [ + { + "name": "geocode", + "value": "2.388911,48.8631507" + }, + { + "name": "format", + "value": "json" + }, + { + "name": "lang", + "value": "en_US" + }, + { + "name": "results", + "value": "5" + } + ], + "url": "https://geocode-maps.yandex.ru/1.x?geocode=2.388911%2C48.8631507&format=json&lang=en_US&results=5" + }, + "response": { + "bodySize": 1831, + "content": { + "_isBinary": true, + "mimeType": "application/json; charset=utf-8", + "size": 1831, + "text": "[\"1f8b0800000000000003ec97df6ed33014c65f25f27536e56f9becae74b09b51cac41d42c84bcea821b183e3944d535f81e7e03df6621c3b7fe6b55d2744a960a23775eccff677bea3fcd6dd12097525780de4e4969c817873f91932351545815f4c703d5d82a2a754d1b914154875d349339183bce8b6bfee347a6d2e18577a50899a9c90e0384c92d4f79d28394e46a11ffb64e5e2bd5f1ba895b5eef6ebde98e8f5ba2994de1fe3d39568788ee394ac70ef1550d548bcb3bc04494ede5bce9ff26bfbac2464ac364512b8a6b8d9250aaeb5a75792f20c5ce7ee7b0147391cf5cf732a59ed3a8107ce444ac17356d7500257ae3359026fc039a3684a29ea3abe87e77d61c6f7423418b14b26798e85d5faf60c4b52f2e6a3b6a56fbc3065ca922a05f93e2d4c45892d42416da2ea1c75d7e33aa7250cf7616b064925c592e9b941f3d08bad2d44460ba6acf38c4f5b8246956426e54eb25983ad473580a55eabce96f6f1764a2c7bf56135a47d8a3d6785097dda558dc36ef19cf1fbeaf792b6b961864ea65667ade9fbacb5c392719d0b556c091309b4b5b63e3bdb9abf4bcefbd871573f9ed9f9bbe4142ae0393ab6b51b93b3473be292770b2145f3697145a5c184fd3cdbde1b97cc2594acc54a379c35edeb6adad37f1eeb6e97660e752659d592688bbba141eb7d1b12bad4e480fc8529fb255f428150d0e3427c033915921b4f1a42d1388d5a487961eae3dea6aa1e4ad2d0f33a499c788981d136da2569dad34e97eaee0150c3dbb03742ddd36938fba0787ace683a387dfe93a78ffe09b01c1a2ac8820e0541b805299137f2bcd028c6f13808b62325f2bc20698f19a571b22fa608b5401bbf8394b70d958a8174f2c699dffd9070744eb30565e6aff1e6cbf567f9b2d3ccbf8a9a4dfd8e320fc09d27427e7608faa51376b5669d4b3b833c28a28271d2fe73168fa2603ba3c2a8fdd933f6127ff4c8cf9e34f0fdb88554e425d15f01a99f000000ffff\",\"dc96311202210c45afb24720d910a0b7b6b1b6d0597b1b0fe539bc98b0c40227b19165460b2a98c04f783fa9351c6b443f67365b9bc63f1ac33bcdaaea96e27ec4068e5c892566220d59e07996b1221298630522b38c2711d36064778ffb357be09aad69b94cafdac8e73d95ba7741d5ba6808a62243cee94fe985a02d74247e87db590fa16eec3f26a6a5ccd2d782f63d618884b872019e126b8071ca6dae1c490ec181d113d1395f03f91852287a8e793d010000ffff0300377eef3b77150000\"]" + }, + "cookies": [], + "headers": [ + { + "name": "server", + "value": "nginx" + }, + { + "name": "date", + "value": "Fri, 25 Sep 2020 18:26:53 GMT" + }, + { + "name": "content-type", + "value": "application/json; charset=utf-8" + }, + { + "name": "transfer-encoding", + "value": "chunked" + }, + { + "name": "connection", + "value": "close" + }, + { + "name": "vary", + "value": "Accept-Encoding" + }, + { + "name": "access-control-allow-origin", + "value": "*" + }, + { + "name": "access-control-allow-methods", + "value": "GET" + }, + { + "name": "access-control-allow-headers", + "value": "*" + }, + { + "name": "etag", + "value": "W/\"1577-SWIN9f7y+Rbp/Lt3aKBPRwRs6GI\"" + }, + { + "name": "x-xss-protection", + "value": "1; mode=block" + }, + { + "name": "x-content-type-options", + "value": "nosniff" + }, + { + "name": "content-encoding", + "value": "gzip" + }, + { + "name": "x-qloud-router", + "value": "man1-043f58f4a44d.qloud-c.yandex.net" + } + ], + "headersSize": 459, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 200, + "statusText": "OK" + }, + "startedDateTime": "2020-09-25T18:26:52.766Z", + "time": 280, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 280 + } + }, + { + "_id": "ab80792b623842c4091acf071a05be52", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 0, + "cookies": [], + "headers": [ + { + "_fromType": "array", + "name": "user-agent", + "value": "GeocoderJS Example" + }, + { + "_fromType": "array", + "name": "accept", + "value": "*/*" + }, + { + "_fromType": "array", + "name": "accept-encoding", + "value": "gzip,deflate" + }, + { + "_fromType": "array", + "name": "connection", + "value": "close" + }, + { + "name": "host", + "value": "nominatim.openstreetmap.org" + } + ], + "headersSize": 287, + "httpVersion": "HTTP/1.1", + "method": "GET", + "queryString": [ + { + "name": "lat", + "value": "48.8631507" + }, + { + "name": "lon", + "value": "2.388911" + }, + { + "name": "zoom", + "value": "18" + }, + { + "name": "format", + "value": "jsonv2" + }, + { + "name": "addressdetails", + "value": "1" + }, + { + "name": "accept-language", + "value": "en_US" + } + ], + "url": "https://nominatim.openstreetmap.org/reverse?lat=48.8631507&lon=2.388911&zoom=18&format=jsonv2&addressdetails=1&accept-language=en_US" + }, + "response": { + "bodySize": 674, + "content": { + "mimeType": "application/json; charset=UTF-8", + "size": 674, + "text": "{\"place_id\":161274479,\"licence\":\"Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright\",\"osm_type\":\"way\",\"osm_id\":322777831,\"lat\":\"48.863744499999996\",\"lon\":\"2.3911562136123106\",\"place_rank\":26,\"category\":\"highway\",\"type\":\"pedestrian\",\"importance\":0.09999999999999998,\"addresstype\":\"road\",\"name\":null,\"display_name\":\"Quartier du Père-Lachaise, Paris, Ile-de-France, Metropolitan France, 75020, France\",\"address\":{\"suburb\":\"Quartier du Père-Lachaise\",\"city\":\"Paris\",\"municipality\":\"Paris\",\"county\":\"Paris\",\"state\":\"Ile-de-France\",\"country\":\"France\",\"postcode\":\"75020\",\"country_code\":\"fr\"},\"boundingbox\":[\"48.8625929\",\"48.8648832\",\"2.3877078\",\"2.3956964\"]}" + }, + "cookies": [], + "headers": [ + { + "name": "server", + "value": "nginx" + }, + { + "name": "date", + "value": "Fri, 25 Sep 2020 18:26:52 GMT" + }, + { + "name": "content-type", + "value": "application/json; charset=UTF-8" + }, + { + "name": "transfer-encoding", + "value": "chunked" + }, + { + "name": "connection", + "value": "close" + }, + { + "name": "access-control-allow-origin", + "value": "*" + }, + { + "name": "access-control-allow-methods", + "value": "OPTIONS,GET" + } + ], + "headersSize": 223, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 200, + "statusText": "OK" + }, + "startedDateTime": "2020-09-25T18:26:52.766Z", + "time": 224, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 224 + } + } + ], + "pages": [], + "version": "1.2" + } +} diff --git a/spec/recordings/GeoPlugin-Geocoder-Provider_1191504894/receives-correct-IP-geolocation-results_4027179090/recording.har b/spec/recordings/GeoPlugin-Geocoder-Provider_1191504894/receives-correct-IP-geolocation-results_4027179090/recording.har new file mode 100644 index 0000000..74ff019 --- /dev/null +++ b/spec/recordings/GeoPlugin-Geocoder-Provider_1191504894/receives-correct-IP-geolocation-results_4027179090/recording.har @@ -0,0 +1,110 @@ +{ + "log": { + "_recordingName": "GeoPlugin Geocoder Provider/receives correct IP geolocation results", + "creator": { + "comment": "persister:fs", + "name": "Polly.JS", + "version": "5.0.0" + }, + "entries": [ + { + "_id": "05202cf8a6f2058fa16406cb3e37e3a4", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 0, + "cookies": [], + "headers": [ + { + "_fromType": "array", + "name": "accept", + "value": "*/*" + }, + { + "_fromType": "array", + "name": "user-agent", + "value": "node-fetch/1.0 (+https://github.com/bitinn/node-fetch)" + }, + { + "_fromType": "array", + "name": "accept-encoding", + "value": "gzip,deflate" + }, + { + "_fromType": "array", + "name": "connection", + "value": "close" + }, + { + "name": "host", + "value": "www.geoplugin.net" + } + ], + "headersSize": 224, + "httpVersion": "HTTP/1.1", + "method": "GET", + "queryString": [ + { + "name": "ip", + "value": "190.226.155.134" + } + ], + "url": "http://www.geoplugin.net/json.gp?ip=190.226.155.134" + }, + "response": { + "bodySize": 974, + "content": { + "mimeType": "text/plain; charset=utf-8", + "size": 974, + "text": "{\n \"geoplugin_request\":\"190.226.155.134\",\n \"geoplugin_status\":200,\n \"geoplugin_delay\":\"1ms\",\n \"geoplugin_credit\":\"Some of the returned data includes GeoLite data created by MaxMind, available from http:\\/\\/www.maxmind.com<\\/a>.\",\n \"geoplugin_city\":\"Colon\",\n \"geoplugin_region\":\"Entre Rios\",\n \"geoplugin_regionCode\":\"E\",\n \"geoplugin_regionName\":\"Entre Rios\",\n \"geoplugin_areaCode\":\"\",\n \"geoplugin_dmaCode\":\"\",\n \"geoplugin_countryCode\":\"AR\",\n \"geoplugin_countryName\":\"Argentina\",\n \"geoplugin_inEU\":0,\n \"geoplugin_euVATrate\":false,\n \"geoplugin_continentCode\":\"SA\",\n \"geoplugin_continentName\":\"South America\",\n \"geoplugin_latitude\":\"-32.2167\",\n \"geoplugin_longitude\":\"-58.1333\",\n \"geoplugin_locationAccuracyRadius\":\"500\",\n \"geoplugin_timezone\":\"America\\/Argentina\\/Cordoba\",\n \"geoplugin_currencyCode\":\"ARS\",\n \"geoplugin_currencySymbol\":\"$\",\n \"geoplugin_currencySymbol_UTF8\":\"$\",\n \"geoplugin_currencyConverter\":73.1556\n}" + }, + "cookies": [], + "headers": [ + { + "name": "date", + "value": "Sat, 15 Aug 2020 15:09:59 GMT" + }, + { + "name": "content-type", + "value": "text/plain; charset=utf-8" + }, + { + "name": "content-length", + "value": "974" + }, + { + "name": "connection", + "value": "close" + }, + { + "name": "server", + "value": "Apache" + }, + { + "name": "access-control-allow-origin", + "value": "*" + } + ], + "headersSize": 168, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 200, + "statusText": "OK" + }, + "startedDateTime": "2020-08-15T15:09:37.597Z", + "time": 58, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 58 + } + } + ], + "pages": [], + "version": "1.2" + } +} diff --git a/spec/recordings/GeoPlugin-Geocoder-Provider_1191504894/receives-error-when-IP-is-not-found_3609480229/recording.har b/spec/recordings/GeoPlugin-Geocoder-Provider_1191504894/receives-error-when-IP-is-not-found_3609480229/recording.har new file mode 100644 index 0000000..0ed1654 --- /dev/null +++ b/spec/recordings/GeoPlugin-Geocoder-Provider_1191504894/receives-error-when-IP-is-not-found_3609480229/recording.har @@ -0,0 +1,110 @@ +{ + "log": { + "_recordingName": "GeoPlugin Geocoder Provider/receives error when IP is not found", + "creator": { + "comment": "persister:fs", + "name": "Polly.JS", + "version": "5.0.0" + }, + "entries": [ + { + "_id": "75bbd00961b4fee62d52c7fec2a92956", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 0, + "cookies": [], + "headers": [ + { + "_fromType": "array", + "name": "accept", + "value": "*/*" + }, + { + "_fromType": "array", + "name": "user-agent", + "value": "node-fetch/1.0 (+https://github.com/bitinn/node-fetch)" + }, + { + "_fromType": "array", + "name": "accept-encoding", + "value": "gzip,deflate" + }, + { + "_fromType": "array", + "name": "connection", + "value": "close" + }, + { + "name": "host", + "value": "www.geoplugin.net" + } + ], + "headersSize": 221, + "httpVersion": "HTTP/1.1", + "method": "GET", + "queryString": [ + { + "name": "ip", + "value": "192.168.1.10" + } + ], + "url": "http://www.geoplugin.net/json.gp?ip=192.168.1.10" + }, + "response": { + "bodySize": 894, + "content": { + "mimeType": "text/plain; charset=utf-8", + "size": 894, + "text": "{\n \"geoplugin_request\":\"192.168.1.10\",\n \"geoplugin_status\":404,\n \"geoplugin_delay\":\"2ms\",\n \"geoplugin_credit\":\"Some of the returned data includes GeoLite data created by MaxMind, available from http:\\/\\/www.maxmind.com<\\/a>.\",\n \"geoplugin_city\":null,\n \"geoplugin_region\":null,\n \"geoplugin_regionCode\":null,\n \"geoplugin_regionName\":null,\n \"geoplugin_areaCode\":null,\n \"geoplugin_dmaCode\":null,\n \"geoplugin_countryCode\":null,\n \"geoplugin_countryName\":null,\n \"geoplugin_inEU\":0,\n \"geoplugin_euVATrate\":false,\n \"geoplugin_continentCode\":null,\n \"geoplugin_continentName\":null,\n \"geoplugin_latitude\":null,\n \"geoplugin_longitude\":null,\n \"geoplugin_locationAccuracyRadius\":null,\n \"geoplugin_timezone\":null,\n \"geoplugin_currencyCode\":null,\n \"geoplugin_currencySymbol\":null,\n \"geoplugin_currencySymbol_UTF8\":\"\",\n \"geoplugin_currencyConverter\":0\n}" + }, + "cookies": [], + "headers": [ + { + "name": "date", + "value": "Sat, 15 Aug 2020 15:39:23 GMT" + }, + { + "name": "content-type", + "value": "text/plain; charset=utf-8" + }, + { + "name": "content-length", + "value": "894" + }, + { + "name": "connection", + "value": "close" + }, + { + "name": "server", + "value": "Apache" + }, + { + "name": "access-control-allow-origin", + "value": "*" + } + ], + "headersSize": 168, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 200, + "statusText": "OK" + }, + "startedDateTime": "2020-08-15T15:39:01.398Z", + "time": 61, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 61 + } + } + ], + "pages": [], + "version": "1.2" + } +} diff --git a/spec/recordings/Google-Maps-Geocoder-Provider_3854403838/receives-correct-geocoding-results_3640432360/recording.har b/spec/recordings/Google-Maps-Geocoder-Provider_3854403838/receives-correct-geocoding-results_3640432360/recording.har new file mode 100644 index 0000000..9efb837 --- /dev/null +++ b/spec/recordings/Google-Maps-Geocoder-Provider_3854403838/receives-correct-geocoding-results_3640432360/recording.har @@ -0,0 +1,151 @@ +{ + "log": { + "_recordingName": "Google Maps Geocoder Provider/receives correct geocoding results", + "creator": { + "comment": "persister:fs", + "name": "Polly.JS", + "version": "5.0.0" + }, + "entries": [ + { + "_id": "abd7b981a33a2e9c17490b975032040c", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 0, + "cookies": [], + "headers": [ + { + "_fromType": "array", + "name": "accept", + "value": "*/*" + }, + { + "_fromType": "array", + "name": "user-agent", + "value": "node-fetch/1.0 (+https://github.com/bitinn/node-fetch)" + }, + { + "_fromType": "array", + "name": "accept-encoding", + "value": "gzip,deflate" + }, + { + "_fromType": "array", + "name": "connection", + "value": "close" + }, + { + "name": "host", + "value": "maps.googleapis.com" + } + ], + "headersSize": 326, + "httpVersion": "HTTP/1.1", + "method": "GET", + "queryString": [ + { + "name": "address", + "value": "1600 Pennsylvania Ave, Washington, DC" + } + ], + "url": "https://maps.googleapis.com/maps/api/geocode/json?address=1600+Pennsylvania+Ave%2C+Washington%2C+DC" + }, + "response": { + "bodySize": 1256, + "content": { + "_isBinary": true, + "mimeType": "application/json; charset=UTF-8", + "size": 1256, + "text": "[\"1f\",\"8b\",\"08\",\"00\",\"00\",\"00\",\"00\",\"00\",\"02\",\"ff\",\"ad\",\"965d6f9b301486eff32b2c5fb30ac2f7eeaa449bba494bd72cebc554594e3809d6c046c6a48baafcf71a885a28745025dc207c5edb0fafcf39f034410861097991a81ca3cfe84f39a0afa7d3bd0cd328d28a9c6c449a090ebcadece84fb312c17784d3144a31b63cd3c4464794c742aa41953a6450efa9672809a0a714e91a24460f2df1d1f810d42d709e1f923de58ca2eb3df002d00f0d143f42ae86697ba7df0ff04b51283893fb8511ddd33c667ca7041fc61d39ab09cb81ede2b590b110113610ce44c214dbd0e4cc17f808f668d8446830a60e97049d339d6f6ca390d8a2994874d2313a8c3c9f0da0d22865bc5c9a2ab60742255092c01e12625d927ec59982082d15557aeb41ecd572007b230aaee4450d9e9aee98c6f09eac4997895cd14477a9a85b5faf4f0f8d45f056c8942aed1139f5b8972e84fa8bdb68d48e81e63354811968b5bc6ed2e11d88144aabf47a6d0bf05a9b18e5dd4019e3658902d5cda72f5cb947ab981d5c05a11f9a8ed3f1a452f15da9fae4fb57a6edbad6d479ab3af658ae3b53ddf9466d6e9b9e3dbcb91fbabe13d65787e17f895395b362baea7b9d6aa1789edd45697378ae6f077d1cef6d4bcad4aad2e16eb1f8f26b71fb26fbf09ec163a6cfeb12271984ae63dbe634b4dc11e769076ee885be19385d4bcf3d566fea7a76b5f80892c00cecb0c6b6060ef7f5a98988b3846e80b0a8327a16df7cfbfa5bdd38eb83ad9cbf77d2b522f23325ffdca0555ccda2d7ef45d709cbe354ff97d48d897145c496e81bc8ea3bae0725a42c6fb4858aa76e05fa8f82aaa2ae\",\"fcc5773c394e9e014ab94ebe15090000\"]" + }, + "cookies": [], + "headers": [ + { + "name": "content-type", + "value": "application/json; charset=UTF-8" + }, + { + "name": "date", + "value": "Thu, 13 Aug 2020 14:48:14 GMT" + }, + { + "name": "pragma", + "value": "no-cache" + }, + { + "name": "expires", + "value": "Fri, 01 Jan 1990 00:00:00 GMT" + }, + { + "name": "cache-control", + "value": "no-cache, must-revalidate" + }, + { + "name": "vary", + "value": "Accept-Language" + }, + { + "name": "access-control-allow-origin", + "value": "*" + }, + { + "name": "x-goog-maps-metro-area", + "value": "Washington, DC" + }, + { + "name": "content-encoding", + "value": "gzip" + }, + { + "name": "server", + "value": "mafe" + }, + { + "name": "x-xss-protection", + "value": "0" + }, + { + "name": "x-frame-options", + "value": "SAMEORIGIN" + }, + { + "name": "server-timing", + "value": "gfet4t7; dur=545" + }, + { + "name": "alt-svc", + "value": "h3-29=\":443\"; ma=2592000,h3-27=\":443\"; ma=2592000,h3-T050=\":443\"; ma=2592000,h3-Q050=\":443\"; ma=2592000,h3-Q046=\":443\"; ma=2592000,h3-Q043=\":443\"; ma=2592000,quic=\":443\"; ma=2592000; v=\"46,43\"" + }, + { + "name": "connection", + "value": "close" + }, + { + "name": "transfer-encoding", + "value": "chunked" + } + ], + "headersSize": 652, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 200, + "statusText": "OK" + }, + "startedDateTime": "2020-08-13T14:48:14.144Z", + "time": 571, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 571 + } + } + ], + "pages": [], + "version": "1.2" + } +} diff --git a/spec/recordings/Google-Maps-Geocoder-Provider_3854403838/receives-correct-geodecoding-results_1602611079/recording.har b/spec/recordings/Google-Maps-Geocoder-Provider_3854403838/receives-correct-geodecoding-results_1602611079/recording.har new file mode 100644 index 0000000..811a0a0 --- /dev/null +++ b/spec/recordings/Google-Maps-Geocoder-Provider_3854403838/receives-correct-geodecoding-results_1602611079/recording.har @@ -0,0 +1,151 @@ +{ + "log": { + "_recordingName": "Google Maps Geocoder Provider/receives correct geodecoding results", + "creator": { + "comment": "persister:fs", + "name": "Polly.JS", + "version": "5.0.0" + }, + "entries": [ + { + "_id": "d768d27f86a79d9146e3a6ef1cc421f3", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 0, + "cookies": [], + "headers": [ + { + "_fromType": "array", + "name": "accept", + "value": "*/*" + }, + { + "_fromType": "array", + "name": "user-agent", + "value": "node-fetch/1.0 (+https://github.com/bitinn/node-fetch)" + }, + { + "_fromType": "array", + "name": "accept-encoding", + "value": "gzip,deflate" + }, + { + "_fromType": "array", + "name": "connection", + "value": "close" + }, + { + "name": "host", + "value": "maps.googleapis.com" + } + ], + "headersSize": 297, + "httpVersion": "HTTP/1.1", + "method": "GET", + "queryString": [ + { + "name": "latlng", + "value": "48.8631507,2.388911" + } + ], + "url": "https://maps.googleapis.com/maps/api/geocode/json?latlng=48.8631507%2C2.388911" + }, + "response": { + "bodySize": 3715, + "content": { + "_isBinary": true, + "mimeType": "application/json; charset=UTF-8", + "size": 3715, + "text": "[\"1f\",\"8b\",\"08\",\"00\",\"00\",\"00\",\"00\",\"00\",\"02\",\"ff\",\"ed\",\"9b\",\"dd\",\"72\",\"da381886cf7b151e4e37c948b2245b7b460824b4a1501a92b23b1d8f6314ec2db6b3fec94f3bb985bd8eddebe88dad0c9400c6d8043ba1a97b92127f42af2ceb7d3e7d56bebd9124a9723d0a7dcd7007bc22fd2e7d8b7e15fdd670ed6b377406b32b9573a67cf84d39913aba67f97b52c3d31d8357f67e34188edc4b7df418ae362ef0b449258a791847563cee87a3c08f42fe9c36fdd16774591f0c4444a44774ef70673132163f6d35729da1e6e8f6a4678866aa1e437cd3f5829498e0fe9a4ffa13f181c7b96810da97dcab489f17821ff6361254bde14ec8a5635d7c5710e8e9ead21bcc4bf5dc30e05b4a1c4f6abab0a4b0793923d7d04756705fd913cf962bfe6789cfdbde41cf739d81e5fbdc16cf8434e05246c1d91bce0f411fd896638947400fac1baee91ed7b511bfe1230de539aaefff8cf8fe80ef2f2da5c4c1348f1a4f160ef3149e5570a39ba2d710161378b93e2a0a0108a44b4b0a9b5777edfac1ccd296443d7efa3cf725952bd7b3f520e0036dea6453b7919696f49e34169060a6d1370db96bf3e8deccb9f26cc086985fd7895f1a5fd583e802560f542a4399c2f82047ce300a4107b2aa3204d9bafb3deb4d8b6ecd7838dd76bb71d6ee2cddbdca8dc56fafc54d5e2dcb11574caefbab2f2f0bc75825400588411293bf3802069002d4492840cbb10f2b1e046197e62dcf28042aaa0221135da4095115a208115128666a4c48d2e333af50b05837b8660dc6b7b96636df32fdf454be791b902f46\",\"97c2e3da572edfd0ab6165b1d10a804faf6d8af169b33498af90be029eb325f063edcc1ae48efc0c2b7e654c89fc12f925f233082f91bf21f2c14b215fe007a5201f40b28bc897659815f904e002912f139415f904919c916f023443be892efa678d00b1a0bf2df2e953914f33215fdc5cfd7264f96664bad1e216133f0accc932b79c4073af34f1837be359283c21a0e976b02aa4a874a0134db2540d87a11f70a9f5fd3fe1d596f8d674fa666e58a607657ab091f0323d78c1f4804a092b7bab2ce132f27d3f0f18cb4001721afd042a89923b7e11c3884dffa52ac0488529dc4dc86db26452504d299e1020ef62f1442644a8cf9049a94c2db47842894269b64c0ae65f3cf972c11cdc9964524af0d721fce3cefed8731756d3c2eaf6b86df9fc39ea15e52b8a322129139232215927ed17794581e414c8aa946ecad8eafbe3bad67c7f56ef76daa7d5b3fa51a1b8250a63590b1710b3e270abaa08642d5c4005e488dbba65dcb5acc3c3817d1e0e2e7ac32e6a047dfb1c0c8eeb7ef3c880ada3e66db371681af6e8eb69edf0d8b01b611ff5ac63b3d9af991fded5878e469d4f866acb76f7ae7ece87d54e0d86bd5eb7da4ae4f5b3bf6650f6618665bb32a8047709ee12dc198497e0de0cdcc26e7225778e35040c57bc89909636a044256c39666b12ca50a14a7af982a034f63dbd78801889537861e4103279c3c4e6b8de6ed5cfbacd9a56ab47d94da1690dc5184faa08f1046d712c0c60aaca7261790d135a2665843425aaa22840a6f9d611b4fd6b284feb088d13b77a741b5c8e1af5c4bc64099a85a5239defff7a7cff54374c3d2a5ba413362d7e7e100eb786e6a5eb99ae3bc8d343773c1bc8515e49faf5c24bd26f46fac5e5bb1b90578082579c3094e64d1903e1de4aee9027824fa9e9850c978e3a48f9211e0bdad17588672a5628ccd4db23e2ab9d4eb7fda9d9aa9ed50ba4fb2b98b7acf456f9f951cbbd1bd3fbc40f4f6d469b27b69a48ef0ce02b8ce80804a6a42fee2bddabac1bd20d5b2f9ad48f418a11fbe1e53c5ae73ece30b033902d73803207f8857280754b7c473202156398f2e21e432a768bf117f75b9205538ad23688627b08088df59c5f4a4041d2d985a5cc00a8993a7df6cce015cc5fd6d4806a5f750b93716a303c0c7b7aab5dabddf6920f083c9592c5bd91781e8f2af1be5a5e89f7f5c29f17ef1b917447580994943ab5f05a4c58dcf1b6f65ac2707cf72a2d9a2d65001675be8e30a6a2752572b1ffa418a24cbdbd00257ffe997b0a265b8dd33e36f45aedee760d265790a4b82a78499b9236bb4e9b17e5ccf8d8114ecbe9296308c45f996de956901025e520394262c3112f33e644194a215eb71523086dfe17f0cf0299d7306d591173a45c5987260b23c4f4df7e6c4d76621f121193e2d43f036c9ec9cd771c8f256c4ad83c0e73475cebe9b0212a8a0b58a40dc43b59f87b0df3f614dab46a7f9f4e68937cd07873ab2e0c40a55fe6ef978bf7f4658c931d88079c8078aa2a3dae02f980100600cdfb953c4400a8706de51d1e604c61fc8f3df3f14dcc1864ea1adfa4b20230dcc1a314af62dab2da6603dfdb55f5cc243da36bd42ea6b6997c0e72736729cc3677d87d5ec06d08140f4fc4d3b5b867079422b134f37d6ee1810cb1bc96f6fb245a3185d49da94824142aaff31a046505ef5e41e055cc5956a7699d0f70ebde1e9e574197b1d1c91de96bda859fe834eb57e5b8dbc94aacf8811e849385d77e5779f3f0e67fe4db093b05570000\"]" + }, + "cookies": [], + "headers": [ + { + "name": "content-type", + "value": "application/json; charset=UTF-8" + }, + { + "name": "date", + "value": "Thu, 13 Aug 2020 15:02:13 GMT" + }, + { + "name": "pragma", + "value": "no-cache" + }, + { + "name": "expires", + "value": "Fri, 01 Jan 1990 00:00:00 GMT" + }, + { + "name": "cache-control", + "value": "no-cache, must-revalidate" + }, + { + "name": "vary", + "value": "Accept-Language" + }, + { + "name": "access-control-allow-origin", + "value": "*" + }, + { + "name": "x-goog-maps-metro-area", + "value": "Paris" + }, + { + "name": "content-encoding", + "value": "gzip" + }, + { + "name": "server", + "value": "mafe" + }, + { + "name": "x-xss-protection", + "value": "0" + }, + { + "name": "x-frame-options", + "value": "SAMEORIGIN" + }, + { + "name": "server-timing", + "value": "gfet4t7; dur=174" + }, + { + "name": "alt-svc", + "value": "h3-29=\":443\"; ma=2592000,h3-27=\":443\"; ma=2592000,h3-T050=\":443\"; ma=2592000,h3-Q050=\":443\"; ma=2592000,h3-Q046=\":443\"; ma=2592000,h3-Q043=\":443\"; ma=2592000,quic=\":443\"; ma=2592000; v=\"46,43\"" + }, + { + "name": "connection", + "value": "close" + }, + { + "name": "transfer-encoding", + "value": "chunked" + } + ], + "headersSize": 643, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 200, + "statusText": "OK" + }, + "startedDateTime": "2020-08-13T15:02:13.751Z", + "time": 226, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 226 + } + } + ], + "pages": [], + "version": "1.2" + } +} diff --git a/spec/recordings/Google-Maps-Geocoder-Provider_3854403838/receives-error-when-the-API-key-is-bad_860720186/recording.har b/spec/recordings/Google-Maps-Geocoder-Provider_3854403838/receives-error-when-the-API-key-is-bad_860720186/recording.har new file mode 100644 index 0000000..b5c70a7 --- /dev/null +++ b/spec/recordings/Google-Maps-Geocoder-Provider_3854403838/receives-error-when-the-API-key-is-bad_860720186/recording.har @@ -0,0 +1,147 @@ +{ + "log": { + "_recordingName": "Google Maps Geocoder Provider/receives error when the API key is bad", + "creator": { + "comment": "persister:fs", + "name": "Polly.JS", + "version": "5.0.0" + }, + "entries": [ + { + "_id": "abd7b981a33a2e9c17490b975032040c", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 0, + "cookies": [], + "headers": [ + { + "_fromType": "array", + "name": "accept", + "value": "*/*" + }, + { + "_fromType": "array", + "name": "user-agent", + "value": "node-fetch/1.0 (+https://github.com/bitinn/node-fetch)" + }, + { + "_fromType": "array", + "name": "accept-encoding", + "value": "gzip,deflate" + }, + { + "_fromType": "array", + "name": "connection", + "value": "close" + }, + { + "name": "host", + "value": "maps.googleapis.com" + } + ], + "headersSize": 294, + "httpVersion": "HTTP/1.1", + "method": "GET", + "queryString": [ + { + "name": "address", + "value": "1600 Pennsylvania Ave, Washington, DC" + } + ], + "url": "https://maps.googleapis.com/maps/api/geocode/json?address=1600+Pennsylvania+Ave%2C+Washington%2C+DC" + }, + "response": { + "bodySize": 255, + "content": { + "_isBinary": true, + "mimeType": "application/json; charset=UTF-8", + "size": 255, + "text": "[\"1f\",\"8b\",\"08\",\"00\",\"00\",\"00000002ffabe6525050504a2d2aca2f8acf4d2d2e4e4c4f5552b052500ac948552828ca2fcb4c494d51700cf054c84ead54c82c56c8cc2b4bccc94cd153d2016b2c4a2d2ecd29290669898e85081597249694824594825c03435d8343e25d5cfd3c5d5d94b86ab9002ddcf75a6f000000\"]" + }, + "cookies": [], + "headers": [ + { + "name": "content-type", + "value": "application/json; charset=UTF-8" + }, + { + "name": "date", + "value": "Thu, 13 Aug 2020 14:48:14 GMT" + }, + { + "name": "pragma", + "value": "no-cache" + }, + { + "name": "expires", + "value": "Fri, 01 Jan 1990 00:00:00 GMT" + }, + { + "name": "cache-control", + "value": "no-cache, must-revalidate" + }, + { + "name": "vary", + "value": "Accept-Language" + }, + { + "name": "access-control-allow-origin", + "value": "*" + }, + { + "name": "content-encoding", + "value": "gzip" + }, + { + "name": "server", + "value": "mafe" + }, + { + "name": "x-xss-protection", + "value": "0" + }, + { + "name": "x-frame-options", + "value": "SAMEORIGIN" + }, + { + "name": "server-timing", + "value": "gfet4t7; dur=10" + }, + { + "name": "alt-svc", + "value": "h3-29=\":443\"; ma=2592000,h3-27=\":443\"; ma=2592000,h3-T050=\":443\"; ma=2592000,h3-Q050=\":443\"; ma=2592000,h3-Q046=\":443\"; ma=2592000,h3-Q043=\":443\"; ma=2592000,quic=\":443\"; ma=2592000; v=\"46,43\"" + }, + { + "name": "connection", + "value": "close" + }, + { + "name": "transfer-encoding", + "value": "chunked" + } + ], + "headersSize": 611, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 200, + "statusText": "OK" + }, + "startedDateTime": "2020-08-13T14:48:14.007Z", + "time": 85, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 85 + } + } + ], + "pages": [], + "version": "1.2" + } +} diff --git a/spec/recordings/MapQuest-Geocoder-Provider_1759071530/receives-correct-geocoding-results-using-POST-and-open-domain_4225723019/recording.har b/spec/recordings/MapQuest-Geocoder-Provider_1759071530/receives-correct-geocoding-results-using-POST-and-open-domain_4225723019/recording.har new file mode 100644 index 0000000..48cd991 --- /dev/null +++ b/spec/recordings/MapQuest-Geocoder-Provider_1759071530/receives-correct-geocoding-results-using-POST-and-open-domain_4225723019/recording.har @@ -0,0 +1,168 @@ +{ + "log": { + "_recordingName": "MapQuest Geocoder Provider/receives correct geocoding results using POST and open domain", + "creator": { + "comment": "persister:fs", + "name": "Polly.JS", + "version": "5.0.0" + }, + "entries": [ + { + "_id": "7e078b8475093693faf7f1bf13216ac4", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 81, + "cookies": [], + "headers": [ + { + "_fromType": "array", + "name": "content-type", + "value": "text/plain;charset=UTF-8" + }, + { + "_fromType": "array", + "name": "accept", + "value": "*/*" + }, + { + "_fromType": "array", + "name": "content-length", + "value": "81" + }, + { + "_fromType": "array", + "name": "user-agent", + "value": "node-fetch/1.0 (+https://github.com/bitinn/node-fetch)" + }, + { + "_fromType": "array", + "name": "accept-encoding", + "value": "gzip,deflate" + }, + { + "_fromType": "array", + "name": "connection", + "value": "close" + }, + { + "name": "host", + "value": "open.mapquestapi.com" + } + ], + "headersSize": 326, + "httpVersion": "HTTP/1.1", + "method": "POST", + "postData": { + "mimeType": "text/plain;charset=UTF-8", + "params": [], + "text": "{\"location\":\"1600 Pennsylvania Ave, Washington, DC\",\"options\":{\"maxResults\":\"5\"}}" + }, + "queryString": [], + "url": "https://open.mapquestapi.com/geocoding/v1/address" + }, + "response": { + "bodySize": 1816, + "content": { + "mimeType": "application/json;charset=UTF-8", + "size": 1816, + "text": "{\"info\":{\"statuscode\":0,\"copyright\":{\"text\":\"\\u00A9 2020 MapQuest, Inc.\",\"imageUrl\":\"http://api.mqcdn.com/res/mqlogo.gif\",\"imageAltText\":\"\\u00A9 2020 MapQuest, Inc.\"},\"messages\":[]},\"options\":{\"maxResults\":5,\"thumbMaps\":true,\"ignoreLatLngInput\":false},\"results\":[{\"providedLocation\":{\"street\":\"1600 Pennsylvania Ave, Washington, DC\"},\"locations\":[{\"street\":\"\",\"adminArea6\":\"Penn Quarter\",\"adminArea6Type\":\"Neighborhood\",\"adminArea5\":\"Washington\",\"adminArea5Type\":\"City\",\"adminArea4\":\"\",\"adminArea4Type\":\"County\",\"adminArea3\":\"District of Columbia\",\"adminArea3Type\":\"State\",\"adminArea1\":\"US\",\"adminArea1Type\":\"Country\",\"postalCode\":\"20006\",\"geocodeQualityCode\":\"B1XAX\",\"geocodeQuality\":\"STREET\",\"dragPoint\":false,\"sideOfStreet\":\"N\",\"linkId\":\"0\",\"unknownInput\":\"\",\"type\":\"s\",\"latLng\":{\"lat\":38.895854,\"lng\":-77.030713},\"displayLatLng\":{\"lat\":38.895854,\"lng\":-77.030713},\"mapUrl\":\"http://open.mapquestapi.com/staticmap/v5/map?type=map&size=225,160&locations=38.8958536,-77.0307129|marker-sm-50318A-1&scalebar=true&zoom=15&rand=942576975\"},{\"street\":\"Pennsylvania Avenue\",\"adminArea6\":\"\",\"adminArea6Type\":\"Neighborhood\",\"adminArea5\":\"\",\"adminArea5Type\":\"City\",\"adminArea4\":\"Prince George's County\",\"adminArea4Type\":\"County\",\"adminArea3\":\"District of Columbia\",\"adminArea3Type\":\"State\",\"adminArea1\":\"US\",\"adminArea1Type\":\"Country\",\"postalCode\":\"20020\",\"geocodeQualityCode\":\"B1XXX\",\"geocodeQuality\":\"STREET\",\"dragPoint\":false,\"sideOfStreet\":\"N\",\"linkId\":\"0\",\"unknownInput\":\"\",\"type\":\"s\",\"latLng\":{\"lat\":38.863638,\"lng\":-76.946365},\"displayLatLng\":{\"lat\":38.863638,\"lng\":-76.946365},\"mapUrl\":\"http://open.mapquestapi.com/staticmap/v5/map?key=Fmjtd|luurnu6al1,bg=o5-9wbg94&type=map&size=225,160&locations=38.8636383,-76.9463651|marker-sm-50318A-2&scalebar=true&zoom=15&rand=1210831717\"}]}]}" + }, + "cookies": [ + { + "httpOnly": true, + "name": "JSESSIONID", + "path": "/", + "value": "F38AEDBEA9FEDE1C7E22071726AE8DB6" + } + ], + "headers": [ + { + "name": "date", + "value": "Fri, 04 Sep 2020 15:04:40 GMT" + }, + { + "name": "content-type", + "value": "application/json;charset=UTF-8" + }, + { + "name": "content-length", + "value": "1816" + }, + { + "name": "connection", + "value": "close" + }, + { + "name": "access-control-allow-methods", + "value": "OPTIONS,GET,POST" + }, + { + "name": "access-control-allow-origin", + "value": "*" + }, + { + "name": "cache-control", + "value": "no-cache, must-revalidate" + }, + { + "name": "expires", + "value": "Mon, 20 Dec 1998 01:00:00 GMT" + }, + { + "name": "geocodetransactioncount", + "value": "1" + }, + { + "name": "last-modified", + "value": "Fri, 04 Sep 2020 15:04:40 GMT" + }, + { + "name": "pragma", + "value": "no-cache" + }, + { + "name": "reversegeocodetransactioncount", + "value": "0" + }, + { + "name": "server", + "value": "Apache-Coyote/1.1" + }, + { + "_fromType": "array", + "name": "set-cookie", + "value": "JSESSIONID=F38AEDBEA9FEDE1C7E22071726AE8DB6; Path=/; HttpOnly" + }, + { + "name": "status", + "value": "success" + }, + { + "name": "transactionweight", + "value": "1.0" + } + ], + "headersSize": 558, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 200, + "statusText": "OK" + }, + "startedDateTime": "2020-09-04T15:04:39.229Z", + "time": 871, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 871 + } + } + ], + "pages": [], + "version": "1.2" + } +} diff --git a/spec/recordings/MapQuest-Geocoder-Provider_1759071530/receives-correct-geocoding-results_3640432360/recording.har b/spec/recordings/MapQuest-Geocoder-Provider_1759071530/receives-correct-geocoding-results_3640432360/recording.har new file mode 100644 index 0000000..5e1b9f7 --- /dev/null +++ b/spec/recordings/MapQuest-Geocoder-Provider_1759071530/receives-correct-geocoding-results_3640432360/recording.har @@ -0,0 +1,167 @@ +{ + "log": { + "_recordingName": "MapQuest Geocoder Provider/receives correct geocoding results", + "creator": { + "comment": "persister:fs", + "name": "Polly.JS", + "version": "5.0.0" + }, + "entries": [ + { + "_id": "a9df5ae9db36156ea719a0bc70a91cbd", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 0, + "cookies": [], + "headers": [ + { + "_fromType": "array", + "name": "accept", + "value": "*/*" + }, + { + "_fromType": "array", + "name": "user-agent", + "value": "node-fetch/1.0 (+https://github.com/bitinn/node-fetch)" + }, + { + "_fromType": "array", + "name": "accept-encoding", + "value": "gzip,deflate" + }, + { + "_fromType": "array", + "name": "connection", + "value": "close" + }, + { + "name": "host", + "value": "www.mapquestapi.com" + } + ], + "headersSize": 335, + "httpVersion": "HTTP/1.1", + "method": "GET", + "queryString": [ + { + "name": "location", + "value": "1600 Pennsylvania Ave, Washington, DC" + }, + { + "name": "maxResults", + "value": "5" + } + ], + "url": "https://www.mapquestapi.com/geocoding/v1/address?location=1600+Pennsylvania+Ave%2C+Washington%2C+DC&maxResults=5" + }, + "response": { + "bodySize": 1113, + "content": { + "mimeType": "application/json;charset=UTF-8", + "size": 1113, + "text": "{\"info\":{\"statuscode\":0,\"copyright\":{\"text\":\"\\u00A9 2020 MapQuest, Inc.\",\"imageUrl\":\"http://api.mqcdn.com/res/mqlogo.gif\",\"imageAltText\":\"\\u00A9 2020 MapQuest, Inc.\"},\"messages\":[]},\"options\":{\"maxResults\":5,\"thumbMaps\":true,\"ignoreLatLngInput\":false},\"results\":[{\"providedLocation\":{\"location\":\"1600 Pennsylvania Ave, Washington, DC\"},\"locations\":[{\"street\":\"1600 Pennsylvania Ave\",\"adminArea6\":\"\",\"adminArea6Type\":\"Neighborhood\",\"adminArea5\":\"Washington\",\"adminArea5Type\":\"City\",\"adminArea4\":\"District Of Columbia\",\"adminArea4Type\":\"County\",\"adminArea3\":\"DC\",\"adminArea3Type\":\"State\",\"adminArea1\":\"US\",\"adminArea1Type\":\"Country\",\"postalCode\":\"\",\"geocodeQualityCode\":\"P1AAA\",\"geocodeQuality\":\"POINT\",\"dragPoint\":false,\"sideOfStreet\":\"N\",\"linkId\":\"58f8ddbf-64f2-4ea2-b8f9-78b5364924e9\",\"unknownInput\":\"\",\"type\":\"s\",\"latLng\":{\"lat\":38.895206,\"lng\":-77.036515},\"displayLatLng\":{\"lat\":38.895206,\"lng\":-77.036515},\"mapUrl\":\"http://www.mapquestapi.com/staticmap/v5/map?type=map&size=225,160&locations=38.895206,-77.036515|marker-sm-50318A-1&scalebar=true&zoom=15&rand=1195279033\"}]}]}" + }, + "cookies": [ + { + "httpOnly": true, + "name": "JSESSIONID", + "path": "/", + "secure": true, + "value": "E72DD9ED6E9A6E1FDA3B690993B6E232" + } + ], + "headers": [ + { + "name": "date", + "value": "Fri, 04 Sep 2020 15:04:40 GMT" + }, + { + "name": "content-type", + "value": "application/json;charset=UTF-8" + }, + { + "name": "content-length", + "value": "1113" + }, + { + "name": "connection", + "value": "close" + }, + { + "name": "access-control-allow-methods", + "value": "OPTIONS,GET,POST" + }, + { + "name": "access-control-allow-origin", + "value": "*" + }, + { + "name": "cache-control", + "value": "no-cache, must-revalidate" + }, + { + "name": "expires", + "value": "Mon, 20 Dec 1998 01:00:00 GMT" + }, + { + "name": "geocodetransactioncount", + "value": "1" + }, + { + "name": "last-modified", + "value": "Fri, 04 Sep 2020 15:04:40 GMT" + }, + { + "name": "pragma", + "value": "no-cache" + }, + { + "name": "reversegeocodetransactioncount", + "value": "0" + }, + { + "name": "server", + "value": "Apache-Coyote/1.1" + }, + { + "_fromType": "array", + "name": "set-cookie", + "value": "JSESSIONID=E72DD9ED6E9A6E1FDA3B690993B6E232; Path=/; HttpOnly; Secure" + }, + { + "name": "status", + "value": "success" + }, + { + "name": "transactionweight", + "value": "1.0" + }, + { + "name": "strict-transport-security", + "value": "max-age=0" + } + ], + "headersSize": 604, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 200, + "statusText": "OK" + }, + "startedDateTime": "2020-09-04T15:04:40.531Z", + "time": 424, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 424 + } + } + ], + "pages": [], + "version": "1.2" + } +} diff --git a/spec/recordings/MapQuest-Geocoder-Provider_1759071530/receives-correct-geodecoding-results-using-POST-and-open-domain_1453160210/recording.har b/spec/recordings/MapQuest-Geocoder-Provider_1759071530/receives-correct-geodecoding-results-using-POST-and-open-domain_1453160210/recording.har new file mode 100644 index 0000000..83dea1d --- /dev/null +++ b/spec/recordings/MapQuest-Geocoder-Provider_1759071530/receives-correct-geodecoding-results-using-POST-and-open-domain_1453160210/recording.har @@ -0,0 +1,168 @@ +{ + "log": { + "_recordingName": "MapQuest Geocoder Provider/receives correct geodecoding results using POST and open domain", + "creator": { + "comment": "persister:fs", + "name": "Polly.JS", + "version": "5.0.0" + }, + "entries": [ + { + "_id": "7d440f03ebc52909ae14852e82a068e9", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 57, + "cookies": [], + "headers": [ + { + "_fromType": "array", + "name": "content-type", + "value": "text/plain;charset=UTF-8" + }, + { + "_fromType": "array", + "name": "accept", + "value": "*/*" + }, + { + "_fromType": "array", + "name": "content-length", + "value": "57" + }, + { + "_fromType": "array", + "name": "user-agent", + "value": "node-fetch/1.0 (+https://github.com/bitinn/node-fetch)" + }, + { + "_fromType": "array", + "name": "accept-encoding", + "value": "gzip,deflate" + }, + { + "_fromType": "array", + "name": "connection", + "value": "close" + }, + { + "name": "host", + "value": "open.mapquestapi.com" + } + ], + "headersSize": 326, + "httpVersion": "HTTP/1.1", + "method": "POST", + "postData": { + "mimeType": "text/plain;charset=UTF-8", + "params": [], + "text": "{\"location\":{\"latLng\":{\"lat\":48.8631507,\"lng\":2.388911}}}" + }, + "queryString": [], + "url": "https://open.mapquestapi.com/geocoding/v1/reverse" + }, + "response": { + "bodySize": 1051, + "content": { + "mimeType": "application/json;charset=UTF-8", + "size": 1051, + "text": "{\"info\":{\"statuscode\":0,\"copyright\":{\"text\":\"\\u00A9 2020 MapQuest, Inc.\",\"imageUrl\":\"http://api.mqcdn.com/res/mqlogo.gif\",\"imageAltText\":\"\\u00A9 2020 MapQuest, Inc.\"},\"messages\":[]},\"options\":{\"maxResults\":1,\"thumbMaps\":true,\"ignoreLatLngInput\":false},\"results\":[{\"providedLocation\":{\"latLng\":{\"lat\":48.8631507,\"lng\":2.388911}},\"locations\":[{\"street\":\"8 Avenue Gambetta\",\"adminArea6\":\"\",\"adminArea6Type\":\"Neighborhood\",\"adminArea5\":\"Paris\",\"adminArea5Type\":\"City\",\"adminArea4\":\"\",\"adminArea4Type\":\"County\",\"adminArea3\":\"Ile-de-France\",\"adminArea3Type\":\"State\",\"adminArea1\":\"FR\",\"adminArea1Type\":\"Country\",\"postalCode\":\"75020\",\"geocodeQualityCode\":\"P1AAA\",\"geocodeQuality\":\"POINT\",\"dragPoint\":false,\"sideOfStreet\":\"N\",\"linkId\":\"0\",\"unknownInput\":\"\",\"type\":\"s\",\"latLng\":{\"lat\":48.863116,\"lng\":2.38878},\"displayLatLng\":{\"lat\":48.863116,\"lng\":2.38878},\"mapUrl\":\"http://open.mapquestapi.com/staticmap/v5/map?type=map&size=225,160&locations=48.8631163,2.38878|marker-sm-50318A-1&scalebar=true&zoom=15&rand=-1000393223\"}]}]}" + }, + "cookies": [ + { + "httpOnly": true, + "name": "JSESSIONID", + "path": "/", + "value": "91D16241582ABFB3D392A13162BCBAA5" + } + ], + "headers": [ + { + "name": "date", + "value": "Fri, 04 Sep 2020 15:04:41 GMT" + }, + { + "name": "content-type", + "value": "application/json;charset=UTF-8" + }, + { + "name": "content-length", + "value": "1051" + }, + { + "name": "connection", + "value": "close" + }, + { + "name": "access-control-allow-methods", + "value": "OPTIONS,GET,POST" + }, + { + "name": "access-control-allow-origin", + "value": "*" + }, + { + "name": "cache-control", + "value": "no-cache, must-revalidate" + }, + { + "name": "expires", + "value": "Mon, 20 Dec 1998 01:00:00 GMT" + }, + { + "name": "geocodetransactioncount", + "value": "0" + }, + { + "name": "last-modified", + "value": "Fri, 04 Sep 2020 15:04:41 GMT" + }, + { + "name": "pragma", + "value": "no-cache" + }, + { + "name": "reversegeocodetransactioncount", + "value": "1" + }, + { + "name": "server", + "value": "Apache-Coyote/1.1" + }, + { + "_fromType": "array", + "name": "set-cookie", + "value": "JSESSIONID=91D16241582ABFB3D392A13162BCBAA5; Path=/; HttpOnly" + }, + { + "name": "status", + "value": "success" + }, + { + "name": "transactionweight", + "value": "1.0" + } + ], + "headersSize": 558, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 200, + "statusText": "OK" + }, + "startedDateTime": "2020-09-04T15:04:40.972Z", + "time": 283, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 283 + } + } + ], + "pages": [], + "version": "1.2" + } +} diff --git a/spec/recordings/MapQuest-Geocoder-Provider_1759071530/receives-correct-geodecoding-results_1602611079/recording.har b/spec/recordings/MapQuest-Geocoder-Provider_1759071530/receives-correct-geodecoding-results_1602611079/recording.har new file mode 100644 index 0000000..bac998f --- /dev/null +++ b/spec/recordings/MapQuest-Geocoder-Provider_1759071530/receives-correct-geodecoding-results_1602611079/recording.har @@ -0,0 +1,163 @@ +{ + "log": { + "_recordingName": "MapQuest Geocoder Provider/receives correct geodecoding results", + "creator": { + "comment": "persister:fs", + "name": "Polly.JS", + "version": "5.0.0" + }, + "entries": [ + { + "_id": "b8e3b1d9aa55c0dd7339774f6f9af07d", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 0, + "cookies": [], + "headers": [ + { + "_fromType": "array", + "name": "accept", + "value": "*/*" + }, + { + "_fromType": "array", + "name": "user-agent", + "value": "node-fetch/1.0 (+https://github.com/bitinn/node-fetch)" + }, + { + "_fromType": "array", + "name": "accept-encoding", + "value": "gzip,deflate" + }, + { + "_fromType": "array", + "name": "connection", + "value": "close" + }, + { + "name": "host", + "value": "www.mapquestapi.com" + } + ], + "headersSize": 294, + "httpVersion": "HTTP/1.1", + "method": "GET", + "queryString": [ + { + "name": "location", + "value": "48.8631507,2.388911" + } + ], + "url": "https://www.mapquestapi.com/geocoding/v1/reverse?location=48.8631507%2C2.388911" + }, + "response": { + "bodySize": 1049, + "content": { + "mimeType": "application/json;charset=UTF-8", + "size": 1049, + "text": "{\"info\":{\"statuscode\":0,\"copyright\":{\"text\":\"\\u00A9 2020 MapQuest, Inc.\",\"imageUrl\":\"http://api.mqcdn.com/res/mqlogo.gif\",\"imageAltText\":\"\\u00A9 2020 MapQuest, Inc.\"},\"messages\":[]},\"options\":{\"maxResults\":1,\"thumbMaps\":true,\"ignoreLatLngInput\":false},\"results\":[{\"providedLocation\":{\"latLng\":{\"lat\":48.8631507,\"lng\":2.388911}},\"locations\":[{\"street\":\"8 Avenue Gambetta\",\"adminArea6\":\"\",\"adminArea6Type\":\"Neighborhood\",\"adminArea5\":\"Paris\",\"adminArea5Type\":\"City\",\"adminArea4\":\"\",\"adminArea4Type\":\"County\",\"adminArea3\":\"Ile-de-France\",\"adminArea3Type\":\"State\",\"adminArea1\":\"FR\",\"adminArea1Type\":\"Country\",\"postalCode\":\"75020\",\"geocodeQualityCode\":\"P1AAA\",\"geocodeQuality\":\"POINT\",\"dragPoint\":false,\"sideOfStreet\":\"N\",\"linkId\":\"0\",\"unknownInput\":\"\",\"type\":\"s\",\"latLng\":{\"lat\":48.863116,\"lng\":2.38878},\"displayLatLng\":{\"lat\":48.863116,\"lng\":2.38878},\"mapUrl\":\"http://www.mapquestapi.com/staticmap/v5/map?type=map&size=225,160&locations=48.8631163,2.38878|marker-sm-50318A-1&scalebar=true&zoom=15&rand=1980906355\"}]}]}" + }, + "cookies": [ + { + "httpOnly": true, + "name": "JSESSIONID", + "path": "/", + "secure": true, + "value": "D9956F384D31623C6B29759C36B34AD7" + } + ], + "headers": [ + { + "name": "date", + "value": "Fri, 04 Sep 2020 15:04:40 GMT" + }, + { + "name": "content-type", + "value": "application/json;charset=UTF-8" + }, + { + "name": "content-length", + "value": "1049" + }, + { + "name": "connection", + "value": "close" + }, + { + "name": "access-control-allow-methods", + "value": "OPTIONS,GET,POST" + }, + { + "name": "access-control-allow-origin", + "value": "*" + }, + { + "name": "cache-control", + "value": "no-cache, must-revalidate" + }, + { + "name": "expires", + "value": "Mon, 20 Dec 1998 01:00:00 GMT" + }, + { + "name": "geocodetransactioncount", + "value": "0" + }, + { + "name": "last-modified", + "value": "Fri, 04 Sep 2020 15:04:40 GMT" + }, + { + "name": "pragma", + "value": "no-cache" + }, + { + "name": "reversegeocodetransactioncount", + "value": "1" + }, + { + "name": "server", + "value": "Apache-Coyote/1.1" + }, + { + "_fromType": "array", + "name": "set-cookie", + "value": "JSESSIONID=D9956F384D31623C6B29759C36B34AD7; Path=/; HttpOnly; Secure" + }, + { + "name": "status", + "value": "success" + }, + { + "name": "transactionweight", + "value": "1.0" + }, + { + "name": "strict-transport-security", + "value": "max-age=0" + } + ], + "headersSize": 604, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 200, + "statusText": "OK" + }, + "startedDateTime": "2020-09-04T15:04:40.152Z", + "time": 365, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 365 + } + } + ], + "pages": [], + "version": "1.2" + } +} diff --git a/spec/recordings/MapQuest-Geocoder-Provider_1759071530/receives-error-when-parameters-are-missing_2925123479/recording.har b/spec/recordings/MapQuest-Geocoder-Provider_1759071530/receives-error-when-parameters-are-missing_2925123479/recording.har new file mode 100644 index 0000000..89fd43d --- /dev/null +++ b/spec/recordings/MapQuest-Geocoder-Provider_1759071530/receives-error-when-parameters-are-missing_2925123479/recording.har @@ -0,0 +1,163 @@ +{ + "log": { + "_recordingName": "MapQuest Geocoder Provider/receives error when parameters are missing", + "creator": { + "comment": "persister:fs", + "name": "Polly.JS", + "version": "5.0.0" + }, + "entries": [ + { + "_id": "e5ca2255cb904c7b4fba9d6b7d4f5ebd", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 0, + "cookies": [], + "headers": [ + { + "_fromType": "array", + "name": "accept", + "value": "*/*" + }, + { + "_fromType": "array", + "name": "user-agent", + "value": "node-fetch/1.0 (+https://github.com/bitinn/node-fetch)" + }, + { + "_fromType": "array", + "name": "accept-encoding", + "value": "gzip,deflate" + }, + { + "_fromType": "array", + "name": "connection", + "value": "close" + }, + { + "name": "host", + "value": "www.mapquestapi.com" + } + ], + "headersSize": 276, + "httpVersion": "HTTP/1.1", + "method": "GET", + "queryString": [ + { + "name": "maxResults", + "value": "5" + } + ], + "url": "https://www.mapquestapi.com/geocoding/v1/address?maxResults=5" + }, + "response": { + "bodySize": 369, + "content": { + "mimeType": "application/json;charset=UTF-8", + "size": 369, + "text": "{\"info\":{\"statuscode\":400,\"copyright\":{\"text\":\"\\u00A9 2020 MapQuest, Inc.\",\"imageUrl\":\"http://api.mqcdn.com/res/mqlogo.gif\",\"imageAltText\":\"\\u00A9 2020 MapQuest, Inc.\"},\"messages\":[\"Illegal argument from request: Insufficient info for location\"]},\"options\":{\"maxResults\":-1,\"thumbMaps\":true,\"ignoreLatLngInput\":false},\"results\":[{\"providedLocation\":{},\"locations\":[]}]}" + }, + "cookies": [ + { + "httpOnly": true, + "name": "JSESSIONID", + "path": "/", + "secure": true, + "value": "145949FDB8481DDBF9EEA1B4D3DB5C4E" + } + ], + "headers": [ + { + "name": "date", + "value": "Fri, 04 Sep 2020 15:04:41 GMT" + }, + { + "name": "content-type", + "value": "application/json;charset=UTF-8" + }, + { + "name": "content-length", + "value": "369" + }, + { + "name": "connection", + "value": "close" + }, + { + "name": "access-control-allow-methods", + "value": "OPTIONS,GET,POST" + }, + { + "name": "access-control-allow-origin", + "value": "*" + }, + { + "name": "cache-control", + "value": "no-cache, must-revalidate" + }, + { + "name": "expires", + "value": "Mon, 20 Dec 1998 01:00:00 GMT" + }, + { + "name": "geocodetransactioncount", + "value": "0" + }, + { + "name": "last-modified", + "value": "Fri, 04 Sep 2020 15:04:41 GMT" + }, + { + "name": "pragma", + "value": "no-cache" + }, + { + "name": "reversegeocodetransactioncount", + "value": "0" + }, + { + "name": "server", + "value": "Apache-Coyote/1.1" + }, + { + "_fromType": "array", + "name": "set-cookie", + "value": "JSESSIONID=145949FDB8481DDBF9EEA1B4D3DB5C4E; Path=/; HttpOnly; Secure" + }, + { + "name": "status", + "value": "success" + }, + { + "name": "transactionweight", + "value": "0.0" + }, + { + "name": "strict-transport-security", + "value": "max-age=0" + } + ], + "headersSize": 603, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 200, + "statusText": "OK" + }, + "startedDateTime": "2020-09-04T15:04:41.271Z", + "time": 367, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 367 + } + } + ], + "pages": [], + "version": "1.2" + } +} diff --git a/spec/recordings/MapQuest-Geocoder-Provider_1759071530/receives-error-when-the-API-key-is-bad_860720186/recording.har b/spec/recordings/MapQuest-Geocoder-Provider_1759071530/receives-error-when-the-API-key-is-bad_860720186/recording.har new file mode 100644 index 0000000..cdef2d7 --- /dev/null +++ b/spec/recordings/MapQuest-Geocoder-Provider_1759071530/receives-error-when-the-API-key-is-bad_860720186/recording.har @@ -0,0 +1,118 @@ +{ + "log": { + "_recordingName": "MapQuest Geocoder Provider/receives error when the API key is bad", + "creator": { + "comment": "persister:fs", + "name": "Polly.JS", + "version": "5.0.0" + }, + "entries": [ + { + "_id": "a9df5ae9db36156ea719a0bc70a91cbd", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 0, + "cookies": [], + "headers": [ + { + "_fromType": "array", + "name": "accept", + "value": "*/*" + }, + { + "_fromType": "array", + "name": "user-agent", + "value": "node-fetch/1.0 (+https://github.com/bitinn/node-fetch)" + }, + { + "_fromType": "array", + "name": "accept-encoding", + "value": "gzip,deflate" + }, + { + "_fromType": "array", + "name": "connection", + "value": "close" + }, + { + "name": "host", + "value": "www.mapquestapi.com" + } + ], + "headersSize": 307, + "httpVersion": "HTTP/1.1", + "method": "GET", + "queryString": [ + { + "name": "location", + "value": "1600 Pennsylvania Ave, Washington, DC" + }, + { + "name": "maxResults", + "value": "5" + } + ], + "url": "https://www.mapquestapi.com/geocoding/v1/address?location=1600+Pennsylvania+Ave%2C+Washington%2C+DC&maxResults=5" + }, + "response": { + "bodySize": 50, + "content": { + "mimeType": "text/plain", + "size": 50, + "text": "The AppKey submitted with this request is invalid." + }, + "cookies": [], + "headers": [ + { + "name": "date", + "value": "Fri, 04 Sep 2020 15:04:39 GMT" + }, + { + "name": "content-type", + "value": "text/plain" + }, + { + "name": "content-length", + "value": "50" + }, + { + "name": "connection", + "value": "close" + }, + { + "name": "access-control-allow-origin", + "value": "*" + }, + { + "name": "access-control-allow-methods", + "value": "OPTIONS,GET,POST" + }, + { + "name": "strict-transport-security", + "value": "max-age=0" + } + ], + "headersSize": 222, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 403, + "statusText": "Forbidden" + }, + "startedDateTime": "2020-09-04T15:04:38.848Z", + "time": 367, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 367 + } + } + ], + "pages": [], + "version": "1.2" + } +} diff --git a/spec/recordings/Mapbox-Geocoder-Provider_114047645/receives-correct-geocoding-results-with-and-without-fuzzy-match_4136676680/recording.har b/spec/recordings/Mapbox-Geocoder-Provider_114047645/receives-correct-geocoding-results-with-and-without-fuzzy-match_4136676680/recording.har new file mode 100644 index 0000000..5284297 --- /dev/null +++ b/spec/recordings/Mapbox-Geocoder-Provider_114047645/receives-correct-geocoding-results-with-and-without-fuzzy-match_4136676680/recording.har @@ -0,0 +1,320 @@ +{ + "log": { + "_recordingName": "Mapbox Geocoder Provider/receives correct geocoding results with and without fuzzy match", + "creator": { + "comment": "persister:fs", + "name": "Polly.JS", + "version": "5.0.0" + }, + "entries": [ + { + "_id": "354da0ca17937575ecb6f1f74508c349", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 0, + "cookies": [], + "headers": [ + { + "_fromType": "array", + "name": "accept", + "value": "*/*" + }, + { + "_fromType": "array", + "name": "user-agent", + "value": "node-fetch/1.0 (+https://github.com/bitinn/node-fetch)" + }, + { + "_fromType": "array", + "name": "accept-encoding", + "value": "gzip,deflate" + }, + { + "_fromType": "array", + "name": "connection", + "value": "close" + }, + { + "name": "host", + "value": "api.mapbox.com" + } + ], + "headersSize": 368, + "httpVersion": "HTTP/1.1", + "method": "GET", + "queryString": [ + { + "name": "fuzzyMatch", + "value": "false" + }, + { + "name": "limit", + "value": "5" + } + ], + "url": "https://api.mapbox.com/geocoding/v5/mapbox.places/wahsington.json?fuzzyMatch=false&limit=5" + }, + "response": { + "bodySize": 1631, + "content": { + "_isBinary": true, + "mimeType": "application/vnd.geo+json; charset=utf-8", + "size": 1631, + "text": "[\"1f8b0800000000000003\",\"b555ed6a1b39147d1531bf5a98cafa1acd8cff15d340caa6ede284a59412e4192556634b534913d70d79a07d8d7db2bdb2eb8ceb24a429148cc177ce3de77e1dcf4d16d79dcec6d99156b1f77ae2160bdd44e36c96675f7bedd7d9f853b652f360ec6584e8e73cbbd842033cb9c94c0bc9aa6de177c0b2ae782d0ac919154232061c3fd343a05ba8469f6fc39f769989d6eb85be56b681380598779df6d124999b4c354def5503c564217aad63760bd4fa5b84c03f2accb7c5a1e9f6d94ec4aaa57e0890a3896e95477fa92b9da363db1a65151292139ea3336ba26e01a92268e7d952c526a59fdfc9ed660198fde7079a7ba867e935da46ed6136afaa120bc189a0652e28e6152919a9605097da2d754c9bb9d98df783333615d338e75b6337548f32dc269cddb6b3dba0d5e6723e737eee5c8b392d4a92dd0d78e2dc15ccfb07b2732136aed59895bc92052d521bb22ef612369ded65a4c1604a199525ad28254c10f806fcca5c99564505a0bf192b58cdf665ef663650797d09a789d38d1585ac49554a29c8211515b48050983b1fcf53ad103c9bbe3a7e3790ff58c2c0dcb8dec24c31ad655955a448575c32561f727372c8dc8781f6e765de7ebea3df39a462b564a4a4b2a80a42b8f85d87105c3dc72353d7c7397ad2298fc072f4517d770e4d4c5ce7e8c484903e5d6710af692d9ef6cc14fd9a6b0e70cfd61dbc53130c47c04b2273ce702538a7523ccb3b0f333ce91d56b2aa16c345bc755687e856f601075130645df252820e9595dc3bfe4d83f72c4458919cc604a38cc87bb709ffbe156703c930bd7b0eaa685dd7043a2a4b2e85b8efa04d0f870e3a990ee47bebf8e32e82c5a918bd99f59b37d3387bf7fef478f2668cfefb1731c2083a51ddcc7d43cab6c8c48042df750ba37dc0e8f562813cec07a26023edaf758bd159d0c85da0383701a592904939b32ff0e643d1415cef184fb55f86849d42aa69347a318fb10be3d168b55ae1e506841bb71ca91978670481307a89d1696206bdce59904a65254e632f9c87cb871ea04c940e49191bd052ad917511cd34e4a450aaf1c3fbe317e125028f5f9b16a6315ba323d7fbf0b5575ee3ecf67fc34e93a5ba070000\"]" + }, + "cookies": [], + "headers": [ + { + "name": "content-type", + "value": "application/vnd.geo+json; charset=utf-8" + }, + { + "name": "transfer-encoding", + "value": "chunked" + }, + { + "name": "connection", + "value": "close" + }, + { + "name": "date", + "value": "Thu, 29 Oct 2020 15:19:03 GMT" + }, + { + "name": "x-powered-by", + "value": "Express" + }, + { + "name": "access-control-allow-origin", + "value": "*" + }, + { + "name": "access-control-expose-headers", + "value": "x-rate-limit-interval,x-rate-limit-limit,x-rate-limit-remaining,x-rate-limit-reset" + }, + { + "name": "cache-control", + "value": "max-age=604800" + }, + { + "name": "x-rate-limit-limit", + "value": "600" + }, + { + "name": "x-rate-limit-interval", + "value": "60" + }, + { + "name": "x-rate-limit-reset", + "value": "1603984803" + }, + { + "name": "last-modified", + "value": "Wed, 28 Oct 2020 10:22:29 GMT" + }, + { + "name": "etag", + "value": "\"fde07e5da194bfd951367f90138daa9a\"" + }, + { + "name": "content-encoding", + "value": "gzip" + }, + { + "name": "vary", + "value": "Accept-Encoding" + }, + { + "name": "x-cache", + "value": "Miss from cloudfront" + }, + { + "name": "via", + "value": "1.1 32ceb5729c4d415c2eb5bbab5ff21b8f.cloudfront.net (CloudFront)" + }, + { + "name": "x-amz-cf-pop", + "value": "CDG3-C2" + }, + { + "name": "x-amz-cf-id", + "value": "OShsj-DnMREMKlFs6Nb-c_Os4mMzYk5Jl_lYlDfD-zNTw6jdaS7mWw==" + } + ], + "headersSize": 757, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 200, + "statusText": "OK" + }, + "startedDateTime": "2020-10-29T15:19:03.143Z", + "time": 75, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 75 + } + }, + { + "_id": "381b4a2eab2448ad59afb59f8f3c3291", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 0, + "cookies": [], + "headers": [ + { + "_fromType": "array", + "name": "accept", + "value": "*/*" + }, + { + "_fromType": "array", + "name": "user-agent", + "value": "node-fetch/1.0 (+https://github.com/bitinn/node-fetch)" + }, + { + "_fromType": "array", + "name": "accept-encoding", + "value": "gzip,deflate" + }, + { + "_fromType": "array", + "name": "connection", + "value": "close" + }, + { + "name": "host", + "value": "api.mapbox.com" + } + ], + "headersSize": 367, + "httpVersion": "HTTP/1.1", + "method": "GET", + "queryString": [ + { + "name": "fuzzyMatch", + "value": "true" + }, + { + "name": "limit", + "value": "5" + } + ], + "url": "https://api.mapbox.com/geocoding/v5/mapbox.places/wahsington.json?fuzzyMatch=true&limit=5" + }, + "response": { + "bodySize": 2458, + "content": { + "_isBinary": true, + "mimeType": "application/vnd.geo+json; charset=utf-8", + "size": 2458, + "text": "[\"1f8b0800000000000003bd57db6edb3810fd15424f2da030bc5ff256a45b34c0f686a6e8164551d0121b73638bae44e582201fb4bfb15fb643398eaf49d60fed8b610d6786e4396766a49b225dcf7c7154bcf22ef5ad3f8e9389af52884d51163f7bdf5e17475f8b4b37ee427396c0faad2c7ecc5d3b58b929420dc1aeaee1b9c3ca1a6e85549c5121146390633d3d18661357f9ef73f3d745644edbfa89bf704d05760a6e6d9cf93685bccd4de1aaaa6f5d058729bad47a9f8a5b48edaf12183ebb6e3c3f1cfa385f5b6cd2b8a9dfe550a2635fbb16fde9ce7d894e9a3ab8c621a138e125fad484e46bf07409f62e8ba94b550eff7ebfdd020bf0595ddfd873c56baffd2adf24df0236074663213811549782626e8866c40050673e4e7dcaccdc2ce07d1f43930f53c5d8d6a119523d98e136fb35f3eb2c186c7c381b8f623b8eb1c69c4a4d8a7b808f633c07bcef3c67b14b55ac3d669a1b25a9ccd75056ae040c375b89c8c0604a19559a1a4a0913047ec1ff329c87da25074e1f1893ccb2d56def315ba66afd194813678d49a92c315a2941365351412598ba716cd3f77c56307efa7870f27699fc8e8465e62af60d608aa955da1822b38a3563763337279b99fb6e99769dccdb6f5b07d7946bab84d54c2832c7ecf10299076ed407c1566e96c81a008cf11d007c7eb1b36c1eae976d758e46f12a2b8b32818d62d46a6315d0510a89a5e012c8205433a9ca034a15b654114d943282818bc5044420803605b465292fc54e19c1c40a9080240ac02f85c104fe03134cf1bd54ff44aa5df2ffc5eccf2b402bcd052586d3ff4bfef0b427f78aee4df2cb004d355409c51f0806403f1d05f730f55a432d5bad8895f942ace4066b4ba5847e501e68209d586e69365b2ba584a735a6211e1a86caebc6cabd98dd08ddc5e45da15101d40b92d566b5e56c8b4829a936744791bc3c5e12ba0b98dfd632d65a32b31a26ead38a590d7a5a383b87e8eb9c22758f4cd13b8f12bd71cdd8a5e440436ffd25fa12dbf3e53f4409e13b06dcbd8e381c083ab82d05c18669a9583918a1ab303518415f4aad8b07d6a1cb0cab92d0fdd4b311bb4b3ef7d3cd4253b3d0ae24175c13b13297866b2d553089959b840432600a5a2133b903726de4d654a2ccda659a7becb606a5641a26b5d144c2a4905bd34dad9c6581f5d684a45099d6428796d4400deaadc370237688ffed97c772ff5ec1134bf96f11fc8b0bdff41e0a3cf308e0a103f4c64f631bdc04bd7700c0c395f05468895ec7be1b9aeca9bf721dd29a10fd704d58890154c275c92cd6a03dabcb6ce4302e61906563d6255bab89bc6ea51956f57ea37223f4d18aa054729d6509ad5e51b55a12c3ad36750caf03ca3066ac811702b1ad40788393cb1477386d0b9959653495265722135baf8d5470bb43c8a77f2d530fc8ff721503ee50ce6d18f5c3d713d4cfbbd393e33f8ed0bfff2046188166390396916b6a145287ba7e369b04df7618bd984c503bb454041f43bebdf035469f3a9fc74e1a870ee523a19063467fc3d7194a11ec7e91f1d4b7d32efb7e84d05079f46c9cd2ac3b3a3cbcbcbcc4d3c10957717ae846b14f8760e80e9f63749a33c37eb3d8c056f9583967687ec416be69b2984342590e2e341d9aba6bd4c484461e62b2299ff1fdbb9367dd73042576116a4063748d5ec5beed7ef6aef5b8b8fd0ffab35d1f5e0e0000\"]" + }, + "cookies": [], + "headers": [ + { + "name": "content-type", + "value": "application/vnd.geo+json; charset=utf-8" + }, + { + "name": "transfer-encoding", + "value": "chunked" + }, + { + "name": "connection", + "value": "close" + }, + { + "name": "date", + "value": "Thu, 29 Oct 2020 15:19:03 GMT" + }, + { + "name": "x-powered-by", + "value": "Express" + }, + { + "name": "access-control-allow-origin", + "value": "*" + }, + { + "name": "access-control-expose-headers", + "value": "x-rate-limit-interval,x-rate-limit-limit,x-rate-limit-remaining,x-rate-limit-reset" + }, + { + "name": "cache-control", + "value": "max-age=604800" + }, + { + "name": "x-rate-limit-limit", + "value": "600" + }, + { + "name": "x-rate-limit-interval", + "value": "60" + }, + { + "name": "x-rate-limit-reset", + "value": "1603984803" + }, + { + "name": "last-modified", + "value": "Wed, 28 Oct 2020 10:22:29 GMT" + }, + { + "name": "etag", + "value": "\"fde07e5da194bfd951367f90138daa9a\"" + }, + { + "name": "content-encoding", + "value": "gzip" + }, + { + "name": "vary", + "value": "Accept-Encoding" + }, + { + "name": "x-cache", + "value": "Miss from cloudfront" + }, + { + "name": "via", + "value": "1.1 4448f6f0cf46259e83792c753f97a4df.cloudfront.net (CloudFront)" + }, + { + "name": "x-amz-cf-pop", + "value": "CDG3-C2" + }, + { + "name": "x-amz-cf-id", + "value": "N3XTfGuEAA_H4ay29FVfKDcThiiTrVcmqR68-fPSlNaSyt2d5ln9LQ==" + } + ], + "headersSize": 757, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 200, + "statusText": "OK" + }, + "startedDateTime": "2020-10-29T15:19:03.143Z", + "time": 109, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 109 + } + } + ], + "pages": [], + "version": "1.2" + } +} diff --git a/spec/recordings/Mapbox-Geocoder-Provider_114047645/receives-correct-geocoding-results_3640432360/recording.har b/spec/recordings/Mapbox-Geocoder-Provider_114047645/receives-correct-geocoding-results_3640432360/recording.har new file mode 100644 index 0000000..c472f1b --- /dev/null +++ b/spec/recordings/Mapbox-Geocoder-Provider_114047645/receives-correct-geocoding-results_3640432360/recording.har @@ -0,0 +1,163 @@ +{ + "log": { + "_recordingName": "Mapbox Geocoder Provider/receives correct geocoding results", + "creator": { + "comment": "persister:fs", + "name": "Polly.JS", + "version": "5.0.0" + }, + "entries": [ + { + "_id": "30081e229aee3d9a6201d55010f0f3ad", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 0, + "cookies": [], + "headers": [ + { + "_fromType": "array", + "name": "accept", + "value": "*/*" + }, + { + "_fromType": "array", + "name": "user-agent", + "value": "node-fetch/1.0 (+https://github.com/bitinn/node-fetch)" + }, + { + "_fromType": "array", + "name": "accept-encoding", + "value": "gzip,deflate" + }, + { + "_fromType": "array", + "name": "connection", + "value": "close" + }, + { + "name": "host", + "value": "api.mapbox.com" + } + ], + "headersSize": 384, + "httpVersion": "HTTP/1.1", + "method": "GET", + "queryString": [ + { + "name": "limit", + "value": "5" + } + ], + "url": "https://api.mapbox.com/geocoding/v5/mapbox.places/1600%20Pennsylvania%20Ave,%20Washington,%20DC.json?limit=5" + }, + "response": { + "bodySize": 2319, + "content": { + "_isBinary": true, + "mimeType": "application/vnd.geo+json; charset=utf-8", + "size": 2319, + "text": "[\"1f8b0800000000000003\",\"ed98db4ee33818805fc5cad58c94093ec476d23ba6cc68e762194680b81821e4262ef54e1b671c8752211e685f639f6c7f97969636740b7bba411555edfc67ff9fed7017f959ada35ef4592bdf3addb7e3b12ebcb15514473f5bed6651ef7b4404c630ae755535b3f18daa8c82a1bad1f03d55cdc854d77eae5116d1651c0d1f6c35a07a179912acabb2847193604c31c68c519ae5390585a7de838fb12af4d5c3f4f7a55e30eaf45883e702e649fcf8a0b78ccdeb5b0fa393b510d1e18d46a79f1e8d566aa2170aa8432e46178fa9c4e8c834de99c2233b445094763200c179f0313aaf8cd7253af5ca43927154e8ca6b07f17e9022c9338ad39865492673ca21f0dad95a3b6f423dee225514ad53c52c14a58647b7660256a2fb38bad676a27d28f8ddb22c27d6543e38b0d695a69abbebf4721f64aa871a2c6b5e69733d1a5837b2b64c68ce5391adeaf451b94acf50dfb8621cbc2f746adbf8c2963a21324b894c4926b22ccfb274adc4f322aca984da26524896129c3142538179109f9a1fa6545e81cc374156faab2aaf8c387d0d2d9790148b34c594a4792e734637cd30ce21b060ab1959e7af42a8307f7efae1a8bf72d0b5742b57856d2ba872427221b30cf3940b2629cdb77ce14d376db3f2f1b405ee2fd72a68923c1729a39c3022f6e871d0d8e86f0ccb4b722a367b67a3149487d68000ae6de89a6804795b481c35105b8c9643101adad6353f5be5421c6c580a018b38842815a7b814434cb466203756553951ee47d4f3aed59b946d43737c111a775192b391461723708d7eb16da337b1db781ca3e74ceec9a1d8e610482a82e2d522a24e0f55abd745ff7a6b0085bd83ed3f17dada162113cc04e7624e6f2e250c2e5f447f97813d360092f2350a8fecb4f2f0d7c53e06ecb9c4544ac1e916fbe28dfd67d85f9e723423384f332e89608c3f1c4faf39e7601fc0b9603b8e1067edd0db7a8dc28eee45c790c868aa1bbfd751f844632f1821c51d30fe3d8ffde7ec6f1195ca051042be0aa827faf71d578c7f0cb0aefe58f215866f7cede64b3202db509a659c51c279f67ff3b5e71df341f4f5d7cc1712b5e5aabfe7fd9564195d5c2d492a5e865287fe6b507abbacfeb74051914aa049c870f6cb57bf9861b0c43b2eae6b4499d06ab51d4328e56eacf6652a461f9d9e80b3d0e4abf54279c698dcd5ec84d244a414e7324e65c225e44f5ed4edcf187892e2fc26fd0a04a08d53b1d6877032aef2ec608049f8504205958c43abac54e775d8448048413995340571f8c1b6180004895c4370dbf502835c1226e15522979d30418d5807021787bb19fb971a1f564779e06dd0ceffc5d18b8ebf9e7de97feaa13f7e87cd9162f4abaa07f616c17b1032be414d5bd763a35d93a0c3f118395821988585d4ee4697093a6f7400d7c3bb160a2121137406bf6900da5b98d74b8b67da4d9a207b0aaaa6d0e8ddc8fbbae91d1c4ca7d36432174a0a3b395003dbfa0398680ede27e82c58067fb5adc055082bd834d5d03a380e20070813855652a66ad044cd50653d1a68d0095321c693af5fde35ef111079634aa8c660863e3fbe0f26d1fd9f6f19c05303120000\"]" + }, + "cookies": [], + "headers": [ + { + "name": "content-type", + "value": "application/vnd.geo+json; charset=utf-8" + }, + { + "name": "transfer-encoding", + "value": "chunked" + }, + { + "name": "connection", + "value": "close" + }, + { + "name": "date", + "value": "Thu, 13 Aug 2020 22:45:54 GMT" + }, + { + "name": "x-powered-by", + "value": "Express" + }, + { + "name": "access-control-allow-origin", + "value": "*" + }, + { + "name": "access-control-expose-headers", + "value": "x-rate-limit-interval,x-rate-limit-limit,x-rate-limit-remaining,x-rate-limit-reset" + }, + { + "name": "cache-control", + "value": "max-age=604800" + }, + { + "name": "x-rate-limit-limit", + "value": "600" + }, + { + "name": "x-rate-limit-interval", + "value": "60" + }, + { + "name": "x-rate-limit-reset", + "value": "1597358814" + }, + { + "name": "last-modified", + "value": "Tue, 11 Aug 2020 20:26:36 GMT" + }, + { + "name": "etag", + "value": "\"c403e3c1ac6e74313bdf5900b6fb1885\"" + }, + { + "name": "content-encoding", + "value": "gzip" + }, + { + "name": "vary", + "value": "Accept-Encoding" + }, + { + "name": "x-cache", + "value": "Miss from cloudfront" + }, + { + "name": "via", + "value": "1.1 41dc616ebfce47f0587493804969040a.cloudfront.net (CloudFront)" + }, + { + "name": "x-amz-cf-pop", + "value": "CDG3-C2" + }, + { + "name": "x-amz-cf-id", + "value": "i2y6vfTB4FZ7qj4efugVrwZWHp7e6U_fanypH37d1mJ22qJ6bRuHoA==" + } + ], + "headersSize": 757, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 200, + "statusText": "OK" + }, + "startedDateTime": "2020-08-13T22:45:54.128Z", + "time": 592, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 592 + } + } + ], + "pages": [], + "version": "1.2" + } +} diff --git a/spec/recordings/Mapbox-Geocoder-Provider_114047645/receives-correct-geodecoding-results_1602611079/recording.har b/spec/recordings/Mapbox-Geocoder-Provider_114047645/receives-correct-geodecoding-results_1602611079/recording.har new file mode 100644 index 0000000..9182d42 --- /dev/null +++ b/spec/recordings/Mapbox-Geocoder-Provider_114047645/receives-correct-geodecoding-results_1602611079/recording.har @@ -0,0 +1,167 @@ +{ + "log": { + "_recordingName": "Mapbox Geocoder Provider/receives correct geodecoding results", + "creator": { + "comment": "persister:fs", + "name": "Polly.JS", + "version": "5.0.0" + }, + "entries": [ + { + "_id": "4caa3833527d0cf02613027b30e3e02d", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 0, + "cookies": [], + "headers": [ + { + "_fromType": "array", + "name": "accept", + "value": "*/*" + }, + { + "_fromType": "array", + "name": "user-agent", + "value": "node-fetch/1.0 (+https://github.com/bitinn/node-fetch)" + }, + { + "_fromType": "array", + "name": "accept-encoding", + "value": "gzip,deflate" + }, + { + "_fromType": "array", + "name": "connection", + "value": "close" + }, + { + "name": "host", + "value": "api.mapbox.com" + } + ], + "headersSize": 372, + "httpVersion": "HTTP/1.1", + "method": "GET", + "queryString": [ + { + "name": "types", + "value": "address" + }, + { + "name": "limit", + "value": "5" + } + ], + "url": "https://api.mapbox.com/geocoding/v5/mapbox.places/2.388911,48.8631507.json?types=address&limit=5" + }, + "response": { + "bodySize": 1519, + "content": { + "_isBinary": true, + "mimeType": "application/vnd.geo+json; charset=utf-8", + "size": 1519, + "text": "[\"1f8b0800000000000003\",\"ed565d4fdb3014fd2b969f8654d2d8f972fa86d89890c6e8187b4208b9c9857a4be2603b850af17fc6df187f6cd785405b3689ed016d1252a52a57f79c7beef189952beae62dd011dd01e93a03dbbaaaa0704a377440cf3b30733a3ae2412444ced820168148239684d9f1809ede212c365c515522872c4b7cb6014f44c652c1592898602132ad0ec1425bc9024eeeca473d9022ab810a66b229b0ceb0cde8168c537eca159545d11959a024da6ad5387a8dcc70e9f0796b064d07e4bdac27e09c7c98d0c8da0f669cac750c4896843c246369941d901db39839a005340e4cbfb41069bf7414a3ba33d035386fca55bfd378a104815a9b5235d22d1cf9051ac5f67b7a410b487327bf37b00175369d6833d5ba0c188f2311a6393acf63ceb9f7f1427d53a5c4fd46f45314b3888b9c3e7830befd6e60f3832ca652594073ee595b6d5da14b08181369c279c272a44d59143e6217663c422a5dc84ab9f91284f13c89e2751178c6195fe2e1a19b12698c6e4a652dd4e826d1a7772e2f29f26713b0388b73244843fccfa2689d3bf7053bd5c69d78f93e3f079b59b2b4f02a6ba1bb06cf26c0f53078f84b441ab3345de765315f273e358facf751b83e7e20ee73cd44c2c228ca127c051296f29708f6d8f391adeeacb30ec8deed8d533305e649c023f29bcee7053d89ef93caf3ec8f73be025e8d79f41af3ff2ee6110f451ef330ceb224cb63f112313fc0abf92d58b2554bb4148c5d0f38274f5a9e9becfe128e92fc2fa2bd8c5ec9f6eb0dfeef471bcf5b3a67d4a45b7cd28ce8c7fdc3dded7723f2e386709f9d3dd94ef425c14411e52cb15ddb563e5b01d9aa2a62f030b18a070e66066540be58f066b8a9b2c48b21ca63265ff19389388d75e8190fc1d4d6f77e46a8c28bf9cdd4b9d68e86c38b8b8ba05e340585ae8772a23b37c4821d6e04e4d033e3bc563738cacbf29caa39d5a6967e0794497ceaa46a2ca9e59c34da910920c697bcc6f1feee1bbb41f0d59ba9124a3299931ddd197bde490301bdfe0909438741f3090000\"]" + }, + "cookies": [], + "headers": [ + { + "name": "content-type", + "value": "application/vnd.geo+json; charset=utf-8" + }, + { + "name": "transfer-encoding", + "value": "chunked" + }, + { + "name": "connection", + "value": "close" + }, + { + "name": "date", + "value": "Thu, 13 Aug 2020 22:50:04 GMT" + }, + { + "name": "x-powered-by", + "value": "Express" + }, + { + "name": "access-control-allow-origin", + "value": "*" + }, + { + "name": "access-control-expose-headers", + "value": "x-rate-limit-interval,x-rate-limit-limit,x-rate-limit-remaining,x-rate-limit-reset" + }, + { + "name": "cache-control", + "value": "max-age=604800" + }, + { + "name": "x-rate-limit-limit", + "value": "600" + }, + { + "name": "x-rate-limit-interval", + "value": "60" + }, + { + "name": "x-rate-limit-reset", + "value": "1597359064" + }, + { + "name": "last-modified", + "value": "Tue, 11 Aug 2020 20:26:36 GMT" + }, + { + "name": "etag", + "value": "\"5a00586e9671c374ddca5293cd31d8a8\"" + }, + { + "name": "content-encoding", + "value": "gzip" + }, + { + "name": "vary", + "value": "Accept-Encoding" + }, + { + "name": "x-cache", + "value": "Miss from cloudfront" + }, + { + "name": "via", + "value": "1.1 9f63706579db7391acaa39a0dddcff5e.cloudfront.net (CloudFront)" + }, + { + "name": "x-amz-cf-pop", + "value": "CDG3-C2" + }, + { + "name": "x-amz-cf-id", + "value": "DPUT-1WE2wtLS3RZCtRCfaEw5Ogz6eNMO5K0OYLwkF0pwHGcgpkRgA==" + } + ], + "headersSize": 757, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 200, + "statusText": "OK" + }, + "startedDateTime": "2020-08-13T22:50:04.032Z", + "time": 129, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 129 + } + } + ], + "pages": [], + "version": "1.2" + } +} diff --git a/spec/recordings/Mapbox-Geocoder-Provider_114047645/receives-error-when-the-API-key-is-bad_860720186/recording.har b/spec/recordings/Mapbox-Geocoder-Provider_114047645/receives-error-when-the-API-key-is-bad_860720186/recording.har new file mode 100644 index 0000000..1419819 --- /dev/null +++ b/spec/recordings/Mapbox-Geocoder-Provider_114047645/receives-error-when-the-API-key-is-bad_860720186/recording.har @@ -0,0 +1,147 @@ +{ + "log": { + "_recordingName": "Mapbox Geocoder Provider/receives error when the API key is bad", + "creator": { + "comment": "persister:fs", + "name": "Polly.JS", + "version": "5.0.0" + }, + "entries": [ + { + "_id": "30081e229aee3d9a6201d55010f0f3ad", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 0, + "cookies": [], + "headers": [ + { + "_fromType": "array", + "name": "accept", + "value": "*/*" + }, + { + "_fromType": "array", + "name": "user-agent", + "value": "node-fetch/1.0 (+https://github.com/bitinn/node-fetch)" + }, + { + "_fromType": "array", + "name": "accept-encoding", + "value": "gzip,deflate" + }, + { + "_fromType": "array", + "name": "connection", + "value": "close" + }, + { + "name": "host", + "value": "api.mapbox.com" + } + ], + "headersSize": 299, + "httpVersion": "HTTP/1.1", + "method": "GET", + "queryString": [ + { + "name": "limit", + "value": "5" + } + ], + "url": "https://api.mapbox.com/geocoding/v5/mapbox.places/1600%20Pennsylvania%20Ave,%20Washington,%20DC.json?limit=5" + }, + "response": { + "bodySize": 135, + "content": { + "_isBinary": true, + "mimeType": "application/json; charset=utf-8", + "size": 135, + "text": "[\"1f8b0800000000000003\",\"ab56ca4d2d2e4e4c4f55b252f2cb2f51702c2dc9c82fcaac4a4d51d055f0cc2b4bccc94c5108c9cf4ecd53aa050000841cd42c000000\"]" + }, + "cookies": [], + "headers": [ + { + "name": "content-type", + "value": "application/json; charset=utf-8" + }, + { + "name": "transfer-encoding", + "value": "chunked" + }, + { + "name": "connection", + "value": "close" + }, + { + "name": "date", + "value": "Thu, 13 Aug 2020 22:57:32 GMT" + }, + { + "name": "x-powered-by", + "value": "Express" + }, + { + "name": "access-control-allow-origin", + "value": "*" + }, + { + "name": "access-control-expose-headers", + "value": "x-rate-limit-interval,x-rate-limit-limit,x-rate-limit-remaining,x-rate-limit-reset" + }, + { + "name": "cache-control", + "value": "max-age=604800" + }, + { + "name": "etag", + "value": "W/\"2c-d41c8400\"" + }, + { + "name": "content-encoding", + "value": "gzip" + }, + { + "name": "vary", + "value": "Accept-Encoding" + }, + { + "name": "x-cache", + "value": "Error from cloudfront" + }, + { + "name": "via", + "value": "1.1 8517cf95bad5514a037b3099aa429186.cloudfront.net (CloudFront)" + }, + { + "name": "x-amz-cf-pop", + "value": "CDG3-C2" + }, + { + "name": "x-amz-cf-id", + "value": "O5wUM40p7PL4AY__Ju41V01wTZEXIwMWA-DDDr5YgU7QBNZj0oY5PA==" + } + ], + "headersSize": 601, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 401, + "statusText": "Unauthorized" + }, + "startedDateTime": "2020-08-13T22:57:32.012Z", + "time": 77, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 77 + } + } + ], + "pages": [], + "version": "1.2" + } +} diff --git a/spec/recordings/OpenCage-Geocoder-Provider_1524131378/receives-correct-geocoding-results_3640432360/recording.har b/spec/recordings/OpenCage-Geocoder-Provider_1524131378/receives-correct-geocoding-results_3640432360/recording.har new file mode 100644 index 0000000..b6b2a58 --- /dev/null +++ b/spec/recordings/OpenCage-Geocoder-Provider_1524131378/receives-correct-geocoding-results_3640432360/recording.har @@ -0,0 +1,139 @@ +{ + "log": { + "_recordingName": "OpenCage Geocoder Provider/receives correct geocoding results", + "creator": { + "comment": "persister:fs", + "name": "Polly.JS", + "version": "5.0.0" + }, + "entries": [ + { + "_id": "5926205db8a399d6edc02640d8616cf2", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 0, + "cookies": [], + "headers": [ + { + "_fromType": "array", + "name": "accept", + "value": "*/*" + }, + { + "_fromType": "array", + "name": "user-agent", + "value": "node-fetch/1.0 (+https://github.com/bitinn/node-fetch)" + }, + { + "_fromType": "array", + "name": "accept-encoding", + "value": "gzip,deflate" + }, + { + "_fromType": "array", + "name": "connection", + "value": "close" + }, + { + "name": "host", + "value": "api.opencagedata.com" + } + ], + "headersSize": 317, + "httpVersion": "HTTP/1.1", + "method": "GET", + "queryString": [ + { + "name": "q", + "value": "1600 Pennsylvania Ave, Washington, DC" + }, + { + "name": "limit", + "value": "5" + } + ], + "url": "https://api.opencagedata.com/geocode/v1/json?q=1600+Pennsylvania+Ave%2C+Washington%2C+DC&limit=5" + }, + "response": { + "bodySize": 1972, + "content": { + "_isBinary": true, + "mimeType": "application/json; charset=utf-8", + "size": 1972, + "text": "[\"1f8b0800000000000003ed596d6fdc3612fe2b82aea8bfd83229529468c0280c6f92a6ad5f6ac7080ed742e04adc5dc592b8a6286ff602fff79bd16a6dafdf1a7b9bdee59a2f86c519ce90f3f23ca3d5273f37595be9da295798dadff127ce4d9b9ded6d33d575a6c63a574e0599a9b6d5b4f037fdb2c874dde8c6dff9d727bf5695862d8dd69e72ce16c3168d78e3b6c835e8b6b67cca6066755eb8c6bffa7dd3b7ca81a54f60be2a9cbf134684c0a2ae545117f51816b894b8d06890d248c64cb09090ab6ead2ddde23caaaecde2220d1a1b1c9c763615ecf159f25b4bc8907811ddf0b80c888c936463c33bc44ba10b3f164b0d011a22909433011aef7d70f3faed71672c336dede6a04d292114f636ae3b3a3ca3dac19b93537c484ecf7e6294324218e71cd40e14c4a49e689583f8f5014daa56c4e76394689b29672c5affe8ef6c2591880423014de8a60f9eb88823c6c34008010e8e4e0f50112397aec677369b0518e3c659ad5da5a681b1e36d54fc61a6e6bb0c621646719cfc0344bb34de6649900826f8f6560c77e582c57018889ffe2cbba8b85debd9e3d6be2fd55cdb66f7f05e253c6cf1870a12b5bbb4f37d559a7af7dad8138786a09c1da6075c625cac1e2fb3bf77f0eae4edfe1ea683500987383c3a79f7e3ab93c3b497a020c4149ea14ec209fcfbfee8e497010a48974e4c6ed1b82253653ab6a69d423162a9f907af06fbfeefa001921216330315bf0309cb5a6ba1cee77802553a6d6b288fb499574353763bcf4ebff3a1e0739d151558ad943d077f01f8ce8b4655c362dcdeec0009ea6f42e8aa32852e2dbadac395a231e9c22ba80cfa851a5ad916d9f575fa063dab0ba773ef144bb5f106a62c95c5d2850394ba712954a6a98aba8700b845d30edb1a1bd1df07a7fef542ea4cba10506cd0eb43e281160fe9a8b08deb8cb889691b55e74ddae8a9b28b1a074588daa854d871bfb579c232f89b8fd4adff13501a6b3351cd0494f28baca23cab226d133791f2838b67a070510c4be5ef44221021a0805179518f0c863db7c5a54e3b30b3c57882c747313c1eebba6ee6e5a5aa0be5ed5deabad5bd3075f32946cad9b63ec7cb4cb5ced3026d54d349570a6ddd1558d17440a5a670258ccd028d28270202a2a0a40dc612caa29790848620c98acb62b9444396104c4eeb6e2952122608681dc4dd7510c642260f3a089350de7110c68910f71c80f558902bf0e08a4affdbd4dd3dfa0ad9ebea466d1fea59fa4f63cf3b28984100d2bccfa6198de064904aa8ae2dca39e67fb906d0df21e81681658cdec45897f6a65f0dde61fc6613e5d8ccd8bc6bcefe1f7f689c335530b62ad341a51a6003d01d02c82ef46a3004a0d9b86b205f2040cc89ec717b810400992106cfb46e32d3f7f45912862bfa00aab1405fc044538845ed3a7f6f4f8f524685d8a2a92aa713b51576fd0577ba27619d640f4429e0b71e1b8badd9551a2cf5f5d43f66065ab7eeb2e91fe295bc3ee09d0c18a5dbbbdaa666745f69d9f16db35cc37dc710fe4c7b6f34c0a8de68bcfd85005043552556933f284a8053ef5875992d0d14c50249f68d55a5f72388d1e2d434aef71092988bad64c171cf6f9f9e140705d646e6f032fba66c01ded452bcbc0b006997867a840c99c1126476646c0523857ec4eda6f71ec0018ace997ad31bec7b37c7ddf49e0823a04aa5bb60af548760095bad0e26220ad5f1b923058381210a481c4ab13a52c4bd06cc1c11092221385f63a200dc9094c434960f4d14e5a58826eac189228e8002839845cb89424a983044ccd7992822c125832985df266719251190731c1006075d77a258b5f6f28902ed2c278aded81387fe3651fcdf4f141f2c190fe7a13c3f9f3419e596ad4c1451fc8713c5f307846e0c586f4060fc850342726f4088c3af634068a6a0d904c3b61c0619e44f7fd680202319f26b44effa9a0b221f1d106414273c5cdd204592fc7503c2a20d6f58cd1b04fbc1179f1c6efc613e35d43644d64e8c59f2aef76bab2c600e0e07ca4d1e1d02562607c2a3ffce0c008e9f4fff08fe4cace63ea6a17c1efd47511086d0410fd37fb8e151127001d5bf16fd732a0970397b98fea3687af108fd0b06e3098ecc1dfd2784b130809eb8a1fff5683a1171cf9f22e46bd034d8b94dd360ec7137df68fa6f40d31799cd3ecc483d0a1d2717e717ab342d5efae2ef1dbeffc6e17f19875700d781c9f32698420232ffeb7eed7ebaa49e477a902633ab5789f80e11264f1321b8bd4b842fa0400056b6c28080afcfe0bf08e82de0549047e88f6c78249084acc77e02889a481e3ff8737a3993e463f630fb5111268108973fa7432f4534805ef973d84fd29888052d1112462f66bfcece2df643638fbbf9c67e7f03f6b36d3e39a72dadec90cf3f5ef055f64bfe375f525ffc2bf6d74b7056156530d2ba0c20dd68e433de51a58c229edcfed5914826d963afa8b1a402de6e6e309a521947f20b50e9b45499bec5a50b44fe4264bade4b225fe5c63569b043d8db2c88387b855fa7f1186db3e02b3c4a886553e9a651633cd8d1cf3d68ce536c465be1693ef9c3d28c6fc13f3e06773f82e304302b1cbe61df68f62bdd47f223d8b08f6eb0e227aa3ec77a7b6dacd73670554fd5de52c3db3b7eeb2fda028e524dbbe35a0d77cf53b40cfbde4dda4d8f326faf1dc3db72483c1aed500694e8bd39788719ead501eb3ef6fd0c0513e27767076340995e7f6ce757ff015f2c82353a200000\"]" + }, + "cookies": [], + "headers": [ + { + "name": "date", + "value": "Thu, 13 Aug 2020 15:13:46 GMT" + }, + { + "name": "server", + "value": "Apache" + }, + { + "name": "access-control-allow-origin", + "value": "*" + }, + { + "name": "x-ratelimit-limit", + "value": "2500" + }, + { + "name": "x-ratelimit-remaining", + "value": "2499" + }, + { + "name": "x-ratelimit-reset", + "value": "1597363200" + }, + { + "name": "vary", + "value": "Accept-Encoding" + }, + { + "name": "content-encoding", + "value": "gzip" + }, + { + "name": "content-length", + "value": "1972" + }, + { + "name": "content-type", + "value": "application/json; charset=utf-8" + }, + { + "name": "strict-transport-security", + "value": "max-age=31536000; includeSubDomains; preload" + }, + { + "name": "connection", + "value": "close" + } + ], + "headersSize": 380, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 200, + "statusText": "OK" + }, + "startedDateTime": "2020-08-13T15:13:46.811Z", + "time": 464, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 464 + } + } + ], + "pages": [], + "version": "1.2" + } +} diff --git a/spec/recordings/OpenCage-Geocoder-Provider_1524131378/receives-correct-geodecoding-results_1602611079/recording.har b/spec/recordings/OpenCage-Geocoder-Provider_1524131378/receives-correct-geodecoding-results_1602611079/recording.har new file mode 100644 index 0000000..a5ae5a7 --- /dev/null +++ b/spec/recordings/OpenCage-Geocoder-Provider_1524131378/receives-correct-geodecoding-results_1602611079/recording.har @@ -0,0 +1,139 @@ +{ + "log": { + "_recordingName": "OpenCage Geocoder Provider/receives correct geodecoding results", + "creator": { + "comment": "persister:fs", + "name": "Polly.JS", + "version": "5.0.0" + }, + "entries": [ + { + "_id": "b817fbe792a368ef82b2256a30a81567", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 0, + "cookies": [], + "headers": [ + { + "_fromType": "array", + "name": "accept", + "value": "*/*" + }, + { + "_fromType": "array", + "name": "user-agent", + "value": "node-fetch/1.0 (+https://github.com/bitinn/node-fetch)" + }, + { + "_fromType": "array", + "name": "accept-encoding", + "value": "gzip,deflate" + }, + { + "_fromType": "array", + "name": "connection", + "value": "close" + }, + { + "name": "host", + "value": "api.opencagedata.com" + } + ], + "headersSize": 289, + "httpVersion": "HTTP/1.1", + "method": "GET", + "queryString": [ + { + "name": "q", + "value": "48.8631507,2.388911" + }, + { + "name": "limit", + "value": "5" + } + ], + "url": "https://api.opencagedata.com/geocode/v1/json?q=48.8631507%2C2.388911&limit=5" + }, + "response": { + "bodySize": 1207, + "content": { + "_isBinary": true, + "mimeType": "application/json; charset=utf-8", + "size": 1207, + "text": "[\"1f8b08000000000000037d556d6fdb3610fe2b020b341fe6c8a2dee5211882c409d2cd499734d887b5106889b6b848a44a5275dc20ff7d77b49cba4d370408ac7b79eeedb9e313a95535745c5a66859264461a6b7b339b4e55cf65c5d6bc6696f995eaa6ac1764425a517169b821b3bf9f88641d0717c3b9c7acd562392088b71e44cdc176d0edff01569ad7c21af2fc694234b380f404f09db0641626410042de3121855c83202e7214180e5a9a1459944661103c3bd9d0da5d3e4c4ab52bc420d8f9e2ce6132f02171fe71088265e025f4c88b333fa4591a1c1d79d758148620e1681046471e2dfc34c80334981388b2b8bc052c12d1fbf33f9384669486312dc075c1a056d9705683fadd35cdf990667d8d1aae2b6695c6141ea182342968e6675934215b324b435ac469ee674906f037770b3483ec79f97dd7369b8d8f9d3356736e3bd6fb4aafa76838957cf3060427349dc6b99fa7110da7a11fe57996bf6dd9966b7372fd6a0a3fc7fbad83269dec51de76ad922723d47f85c0aedc5f978bb8c0cc355fefbb3ebfbfbd793f8790340920fcc52db6d6fdfc6b7ef7617e7b5d1e582428beb9fde31cbe828022a8c1091a2b2ad6966bad861e0880e3258bf9f919f90416a06941582960d92c827e5683d640ae2d8667ade55a029d4ab3ed96aa45572058cd2bd10162c7f403c49ac05f63bbb604e60b0b8ee4ed9bc730383dfb1514c2a872078ec58c02095ba24505b2028a9fecb93f1fb4822f03d82d37b60432a84ec8719b286886e52091d3e40c629117416955b95350e4fa2e59b0fa388401abc85e52ae8436d621d9460d86c9da9486f74cefb8457cecd9aa656be75be75105ff6bbefcf67b9501da9aab8699068c86a0b05b9d859f8b24dc545f8be52a790483cf62d9328803cc0f219856ac1672a5b0a5b5165f78e9ae8316ebc615d1735e9702450fddb471731ba46382306e93590f3962c56e5dc322ca42289301ef14760866386af2244f4153892f622f2a688ac6920df6d0302f42dc7877037e0c10c5499aff3440944471fe7d8028ceb2fc5580280962080011ace8f857255d1d0773eef9f43d83fa70fa6a03d597f5381ab55a41563017e007d4197c93c05574c7e59700c4d8b846695b8ea067b010d8ba4dc36cb451ba760b34fe2006585435bcf6ff019e286d7c0bf7122840304538a03d6428ad73b9babb29239aa6c7b4646ddfb0e310fc2f90baaf3491d39c82aa84f3c4d74a23fb9783686bcc14c476dbf3ef45d56e45f6c5570a9646bad68f7d71c2415a8775a199ac0e44fb5d5a695c3a2880e3322d39b21737a957add8ad3bd6295f4099f4ee9d004d8c1d51b2043a497667823bce0741c55b7e5cf3e397c84eb98f7b757ef122aa054ea4b207d5ec2e21084ebf703970ef92416ed632e2ba2c5778df0114d614f64ce90e5e3a8ec73ef77e7098782e37cf014fbc976460f53aee5a33be45e329a5e9f8f28c17f5191f424c737033dd65efc8d47163e0e584a037bf8f27725be276ea0e537922cb56ad0f8e3c7efa3fbeb79089dd08485e1f588e12f71edf80c31986c10d68987c40125e28ed0d0668e0c138f616dee9fb2bb25b1348a5eb5dba9a4383eb1291c1ef43334c3c1a79a7c3da0bb12b34815a6634f62e171f901ba3398cfc715c3f78d0698cb1e10d6fcb97779d3eff0be5505bbfa5080000\"]" + }, + "cookies": [], + "headers": [ + { + "name": "date", + "value": "Thu, 13 Aug 2020 15:20:14 GMT" + }, + { + "name": "server", + "value": "Apache" + }, + { + "name": "access-control-allow-origin", + "value": "*" + }, + { + "name": "x-ratelimit-limit", + "value": "2500" + }, + { + "name": "x-ratelimit-remaining", + "value": "2498" + }, + { + "name": "x-ratelimit-reset", + "value": "1597363200" + }, + { + "name": "vary", + "value": "Accept-Encoding" + }, + { + "name": "content-encoding", + "value": "gzip" + }, + { + "name": "content-length", + "value": "1207" + }, + { + "name": "content-type", + "value": "application/json; charset=utf-8" + }, + { + "name": "strict-transport-security", + "value": "max-age=31536000; includeSubDomains; preload" + }, + { + "name": "connection", + "value": "close" + } + ], + "headersSize": 380, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 200, + "statusText": "OK" + }, + "startedDateTime": "2020-08-13T15:20:13.933Z", + "time": 285, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 285 + } + } + ], + "pages": [], + "version": "1.2" + } +} diff --git a/spec/recordings/OpenCage-Geocoder-Provider_1524131378/receives-error-when-requesting-too-quickly_2039275820/recording.har b/spec/recordings/OpenCage-Geocoder-Provider_1524131378/receives-error-when-requesting-too-quickly_2039275820/recording.har new file mode 100644 index 0000000..fcdf476 --- /dev/null +++ b/spec/recordings/OpenCage-Geocoder-Provider_1524131378/receives-error-when-requesting-too-quickly_2039275820/recording.har @@ -0,0 +1,127 @@ +{ + "log": { + "_recordingName": "OpenCage Geocoder Provider/receives error when requesting too quickly", + "creator": { + "comment": "persister:fs", + "name": "Polly.JS", + "version": "5.0.0" + }, + "entries": [ + { + "_id": "5926205db8a399d6edc02640d8616cf2", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 0, + "cookies": [], + "headers": [ + { + "_fromType": "array", + "name": "accept", + "value": "*/*" + }, + { + "_fromType": "array", + "name": "user-agent", + "value": "node-fetch/1.0 (+https://github.com/bitinn/node-fetch)" + }, + { + "_fromType": "array", + "name": "accept-encoding", + "value": "gzip,deflate" + }, + { + "_fromType": "array", + "name": "connection", + "value": "close" + }, + { + "name": "host", + "value": "api.opencagedata.com" + } + ], + "headersSize": 317, + "httpVersion": "HTTP/1.1", + "method": "GET", + "queryString": [ + { + "name": "q", + "value": "1600 Pennsylvania Ave, Washington, DC" + }, + { + "name": "limit", + "value": "5" + } + ], + "url": "https://api.opencagedata.com/geocode/v1/json?q=1600+Pennsylvania+Ave%2C+Washington%2C+DC&limit=5" + }, + "response": { + "bodySize": 296, + "content": { + "_isBinary": true, + "mimeType": "application/json; charset=utf-8", + "size": 296, + "text": "[\"1f8b08000000000000037d904d4b03311086ff4a98f3d2ee87b56c6e45503c881e7a1359d2ec741bba49b6c9042da5ffdd49b1523c78cb84f77de6494ed07b9d2c3a5264bc03093ba229caf9dc4fe8b41ab057a466dadbb99a0c14301a8d2e6204f97e02a72c7225220a4514cc2665881892e991b3298cff0175c0de5084f3470101631a295379882c93f87c02ed1924efeab6008b31729b81010f0923193708f25e1c92d1fbf108e74bf1d819b7f5c1629ffb9bd10f370a799cfdf56051fa3444186e923f3717cf572e3ce4d5bc8176caed590d1e7d10296607e5c43521566fcf19675896949d2e4f08a808fb2e93b9b7dea542548d58a541d4655d8a6a21eb7b59b5e2e965cddd6b3c39f305b25ab4cba6a99b659b777b5263f7fb53e5f91bb7128e78bd010000\"]" + }, + "cookies": [], + "headers": [ + { + "name": "date", + "value": "Thu, 13 Aug 2020 15:26:19 GMT" + }, + { + "name": "server", + "value": "Apache" + }, + { + "name": "access-control-allow-origin", + "value": "*" + }, + { + "name": "vary", + "value": "Accept-Encoding" + }, + { + "name": "content-encoding", + "value": "gzip" + }, + { + "name": "content-length", + "value": "296" + }, + { + "name": "content-type", + "value": "application/json; charset=utf-8" + }, + { + "name": "strict-transport-security", + "value": "max-age=31536000; includeSubDomains; preload" + }, + { + "name": "connection", + "value": "close" + } + ], + "headersSize": 294, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 429, + "statusText": "OK" + }, + "startedDateTime": "2020-08-13T15:26:19.277Z", + "time": 70, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 70 + } + } + ], + "pages": [], + "version": "1.2" + } +} diff --git a/spec/recordings/OpenCage-Geocoder-Provider_1524131378/receives-error-when-suspended_3006968534/recording.har b/spec/recordings/OpenCage-Geocoder-Provider_1524131378/receives-error-when-suspended_3006968534/recording.har new file mode 100644 index 0000000..8ddd56e --- /dev/null +++ b/spec/recordings/OpenCage-Geocoder-Provider_1524131378/receives-error-when-suspended_3006968534/recording.har @@ -0,0 +1,127 @@ +{ + "log": { + "_recordingName": "OpenCage Geocoder Provider/receives error when suspended", + "creator": { + "comment": "persister:fs", + "name": "Polly.JS", + "version": "5.0.0" + }, + "entries": [ + { + "_id": "5926205db8a399d6edc02640d8616cf2", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 0, + "cookies": [], + "headers": [ + { + "_fromType": "array", + "name": "accept", + "value": "*/*" + }, + { + "_fromType": "array", + "name": "user-agent", + "value": "node-fetch/1.0 (+https://github.com/bitinn/node-fetch)" + }, + { + "_fromType": "array", + "name": "accept-encoding", + "value": "gzip,deflate" + }, + { + "_fromType": "array", + "name": "connection", + "value": "close" + }, + { + "name": "host", + "value": "api.opencagedata.com" + } + ], + "headersSize": 317, + "httpVersion": "HTTP/1.1", + "method": "GET", + "queryString": [ + { + "name": "q", + "value": "1600 Pennsylvania Ave, Washington, DC" + }, + { + "name": "limit", + "value": "5" + } + ], + "url": "https://api.opencagedata.com/geocode/v1/json?q=1600+Pennsylvania+Ave%2C+Washington%2C+DC&limit=5" + }, + "response": { + "bodySize": 286, + "content": { + "_isBinary": true, + "mimeType": "application/json; charset=utf-8", + "size": 286, + "text": "[\"1f8b08000000000000037d904f4bc43010c5bf4a9873d9fe735d9adb22281e440f7b1329d966b60db6494926a894fdee4e8a2b8b076f33c37bbff79805b4ebe2849614196741c240340799e76e46dba91eb522b5e9dc94abd94006a3e9d0060c205f17b06a42b60444a188bc39c604117d341a591bfdf81fb0f3a80d0538bf65e031c491129597c06522cf0b748e41f2a6a83398300476a7b81818a551c379d57eb5c69e9c9ff8c096e3e8faabd4b46efe467337fa3044e8af943f97b5da331bee521a27d0a0ec3bb7817be7450cc6f64259715188fdcb63c219ee476a9ad7d61e15a16e13997d872166a2acc53ef6a22aaa42945b59ddcab2110f4f07f65ee4d19a4f90e5b6d9d57555ef9a94ed488dedef738af337a14951f7b0010000\"]" + }, + "cookies": [], + "headers": [ + { + "name": "date", + "value": "Thu, 13 Aug 2020 15:26:19 GMT" + }, + { + "name": "server", + "value": "Apache" + }, + { + "name": "access-control-allow-origin", + "value": "*" + }, + { + "name": "vary", + "value": "Accept-Encoding" + }, + { + "name": "content-encoding", + "value": "gzip" + }, + { + "name": "content-length", + "value": "286" + }, + { + "name": "content-type", + "value": "application/json; charset=utf-8" + }, + { + "name": "strict-transport-security", + "value": "max-age=31536000; includeSubDomains; preload" + }, + { + "name": "connection", + "value": "close" + } + ], + "headersSize": 294, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 403, + "statusText": "Forbidden" + }, + "startedDateTime": "2020-08-13T15:26:19.404Z", + "time": 70, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 70 + } + } + ], + "pages": [], + "version": "1.2" + } +} diff --git a/spec/recordings/OpenCage-Geocoder-Provider_1524131378/receives-error-when-the-IP-address-is-rejected_1610178983/recording.har b/spec/recordings/OpenCage-Geocoder-Provider_1524131378/receives-error-when-the-IP-address-is-rejected_1610178983/recording.har new file mode 100644 index 0000000..a6493ba --- /dev/null +++ b/spec/recordings/OpenCage-Geocoder-Provider_1524131378/receives-error-when-the-IP-address-is-rejected_1610178983/recording.har @@ -0,0 +1,98 @@ +{ + "log": { + "_recordingName": "OpenCage Geocoder Provider/receives error when the IP address is rejected", + "creator": { + "comment": "persister:fs", + "name": "Polly.JS", + "version": "5.0.0" + }, + "entries": [ + { + "_id": "5926205db8a399d6edc02640d8616cf2", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 0, + "cookies": [], + "headers": [ + { + "_fromType": "array", + "name": "accept", + "value": "*/*" + }, + { + "_fromType": "array", + "name": "user-agent", + "value": "node-fetch/1.0 (+https://github.com/bitinn/node-fetch)" + }, + { + "_fromType": "array", + "name": "accept-encoding", + "value": "gzip,deflate" + }, + { + "_fromType": "array", + "name": "connection", + "value": "close" + }, + { + "name": "host", + "value": "api.opencagedata.com" + } + ], + "headersSize": 317, + "httpVersion": "HTTP/1.1", + "method": "GET", + "queryString": [ + { + "name": "q", + "value": "1600 Pennsylvania Ave, Washington, DC" + }, + { + "name": "limit", + "value": "5" + } + ], + "url": "https://api.opencagedata.com/geocode/v1/json?q=1600+Pennsylvania+Ave%2C+Washington%2C+DC&limit=5" + }, + "response": { + "bodySize": 127, + "content": { + "mimeType": "application/json; charset=utf-8", + "size": 127, + "text": "{\n \"results\" : [],\n \"status\" : {\n \"code\" : 403,\n \"message\" : \"IP address rejected\"\n },\n \"total_results\" : 0\n}" + }, + "cookies": [], + "headers": [ + { + "name": "cache-control", + "value": "no-cache" + }, + { + "name": "content-type", + "value": "application/json; charset=utf-8" + } + ], + "headersSize": 74, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 403, + "statusText": "Forbidden" + }, + "startedDateTime": "2020-08-13T15:26:19.071Z", + "time": 159, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 159 + } + } + ], + "pages": [], + "version": "1.2" + } +} diff --git a/spec/recordings/OpenCage-Geocoder-Provider_1524131378/receives-error-when-the-quota-is-exceeded_98860701/recording.har b/spec/recordings/OpenCage-Geocoder-Provider_1524131378/receives-error-when-the-quota-is-exceeded_98860701/recording.har new file mode 100644 index 0000000..e6074f2 --- /dev/null +++ b/spec/recordings/OpenCage-Geocoder-Provider_1524131378/receives-error-when-the-quota-is-exceeded_98860701/recording.har @@ -0,0 +1,139 @@ +{ + "log": { + "_recordingName": "OpenCage Geocoder Provider/receives error when the quota is exceeded", + "creator": { + "comment": "persister:fs", + "name": "Polly.JS", + "version": "5.0.0" + }, + "entries": [ + { + "_id": "5926205db8a399d6edc02640d8616cf2", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 0, + "cookies": [], + "headers": [ + { + "_fromType": "array", + "name": "accept", + "value": "*/*" + }, + { + "_fromType": "array", + "name": "user-agent", + "value": "node-fetch/1.0 (+https://github.com/bitinn/node-fetch)" + }, + { + "_fromType": "array", + "name": "accept-encoding", + "value": "gzip,deflate" + }, + { + "_fromType": "array", + "name": "connection", + "value": "close" + }, + { + "name": "host", + "value": "api.opencagedata.com" + } + ], + "headersSize": 317, + "httpVersion": "HTTP/1.1", + "method": "GET", + "queryString": [ + { + "name": "q", + "value": "1600 Pennsylvania Ave, Washington, DC" + }, + { + "name": "limit", + "value": "5" + } + ], + "url": "https://api.opencagedata.com/geocode/v1/json?q=1600+Pennsylvania+Ave%2C+Washington%2C+DC&limit=5" + }, + "response": { + "bodySize": 337, + "content": { + "_isBinary": true, + "mimeType": "application/json; charset=utf-8", + "size": 337, + "text": "[\"1f8b08000000000000037d90414fc3300c85ff4a9473b565e956446e1312880382c36e0855596aba882629892386a6fe779c6a20e0b05becd8df7b7e27de05931d78d46883e78a1f10c7a496cb308237ba874ea35e98e0967ab4bce28335e01324ae9e4fdc6b07b4920098468c769f0b84f5d97640b3390e978026426731f1e9a5e25123914e847716b9921b21a8094e5b6f7dcfd55c25a0afd5e6faaa6e6a29c434f7f280c50c3112dd9053a1ec81f8d0ead6e484f48a976c8cd19aa2517113c8b65a0b59710729d110edbde7809ac1d10074d0f169d6f96cad7f0dd151a3c80da1ffa550cac57f19c2e38745fce3e5dc996d3cd2c24d9124053c68ff4697f0db10594ee48e69cfbe27d8f6e9bee02c9944edc66281b2a404bbb690696f77c8155bd56c9b7b2685146cb55172ad64c3ee1e76e5d2f378f6f6788eb496b2698a369d3bb43fc18ae90bdda5150a23020000\"]" + }, + "cookies": [], + "headers": [ + { + "name": "date", + "value": "Thu, 13 Aug 2020 15:24:26 GMT" + }, + { + "name": "server", + "value": "Apache" + }, + { + "name": "access-control-allow-origin", + "value": "*" + }, + { + "name": "x-ratelimit-limit", + "value": "2500" + }, + { + "name": "x-ratelimit-remaining", + "value": "0" + }, + { + "name": "x-ratelimit-reset", + "value": "1597363200" + }, + { + "name": "vary", + "value": "Accept-Encoding" + }, + { + "name": "content-encoding", + "value": "gzip" + }, + { + "name": "content-length", + "value": "337" + }, + { + "name": "content-type", + "value": "application/json; charset=utf-8" + }, + { + "name": "strict-transport-security", + "value": "max-age=31536000; includeSubDomains; preload" + }, + { + "name": "connection", + "value": "close" + } + ], + "headersSize": 376, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 402, + "statusText": "Payment Required" + }, + "startedDateTime": "2020-08-13T15:24:26.286Z", + "time": 209, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 209 + } + } + ], + "pages": [], + "version": "1.2" + } +} diff --git a/spec/recordings/OpenStreetMap-_925808948/-Nominatim-Geocoder-Provider_1884184056/receives-correct-geocoding-results_3640432360/recording.har b/spec/recordings/OpenStreetMap-_925808948/-Nominatim-Geocoder-Provider_1884184056/receives-correct-geocoding-results_3640432360/recording.har new file mode 100644 index 0000000..9ed050b --- /dev/null +++ b/spec/recordings/OpenStreetMap-_925808948/-Nominatim-Geocoder-Provider_1884184056/receives-correct-geocoding-results_3640432360/recording.har @@ -0,0 +1,126 @@ +{ + "log": { + "_recordingName": "OpenStreetMap / Nominatim Geocoder Provider/receives correct geocoding results", + "creator": { + "comment": "persister:fs", + "name": "Polly.JS", + "version": "5.0.0" + }, + "entries": [ + { + "_id": "a6ec9227dada30b33a2c103778277986", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 0, + "cookies": [], + "headers": [ + { + "_fromType": "array", + "name": "user-agent", + "value": "GeocoderJS Example" + }, + { + "_fromType": "array", + "name": "accept", + "value": "*/*" + }, + { + "_fromType": "array", + "name": "accept-encoding", + "value": "gzip,deflate" + }, + { + "_fromType": "array", + "name": "connection", + "value": "close" + }, + { + "name": "host", + "value": "nominatim.openstreetmap.org" + } + ], + "headersSize": 288, + "httpVersion": "HTTP/1.1", + "method": "GET", + "queryString": [ + { + "name": "q", + "value": "1600 Pennsylvania Ave, Washington, DC" + }, + { + "name": "limit", + "value": "5" + }, + { + "name": "format", + "value": "jsonv2" + }, + { + "name": "addressdetails", + "value": "1" + } + ], + "url": "https://nominatim.openstreetmap.org/search?q=1600+Pennsylvania+Ave%2C+Washington%2C+DC&limit=5&format=jsonv2&addressdetails=1" + }, + "response": { + "bodySize": 1363, + "content": { + "mimeType": "application/json; charset=UTF-8", + "size": 1363, + "text": "[{\"place_id\":205327805,\"licence\":\"Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright\",\"osm_type\":\"way\",\"osm_id\":564931814,\"boundingbox\":[\"38.8957842\",\"38.895924\",\"-77.0309688\",\"-77.0304609\"],\"lat\":\"38.8958536\",\"lon\":\"-77.0307129\",\"display_name\":\"Pennsylvania Avenue, Penn Quarter, Washington, District of Columbia, 20045, United States of America\",\"place_rank\":27,\"category\":\"highway\",\"type\":\"path\",\"importance\":0.385,\"address\":{\"road\":\"Pennsylvania Avenue\",\"neighbourhood\":\"Penn Quarter\",\"city\":\"Washington\",\"county\":\"Washington\",\"state\":\"District of Columbia\",\"postcode\":\"20045\",\"country\":\"United States of America\",\"country_code\":\"us\"}},{\"place_id\":176517326,\"licence\":\"Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright\",\"osm_type\":\"way\",\"osm_id\":397325778,\"boundingbox\":[\"38.8633822\",\"38.8637409\",\"-76.9467576\",\"-76.945632\"],\"lat\":\"38.8636383\",\"lon\":\"-76.9463651\",\"display_name\":\"Pennsylvania Avenue, Dillon Park, Coral Hills, Prince George's County, District of Columbia, 20746-8001, United States of America\",\"place_rank\":26,\"category\":\"highway\",\"type\":\"trunk\",\"importance\":0.3,\"address\":{\"road\":\"Pennsylvania Avenue\",\"hamlet\":\"Dillon Park\",\"locality\":\"Coral Hills\",\"county\":\"Prince George's County\",\"state\":\"District of Columbia\",\"postcode\":\"20746-8001\",\"country\":\"United States of America\",\"country_code\":\"us\"}}]" + }, + "cookies": [], + "headers": [ + { + "name": "server", + "value": "nginx" + }, + { + "name": "date", + "value": "Fri, 14 Aug 2020 16:44:22 GMT" + }, + { + "name": "content-type", + "value": "application/json; charset=UTF-8" + }, + { + "name": "transfer-encoding", + "value": "chunked" + }, + { + "name": "connection", + "value": "close" + }, + { + "name": "access-control-allow-origin", + "value": "*" + }, + { + "name": "access-control-allow-methods", + "value": "OPTIONS,GET" + } + ], + "headersSize": 223, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 200, + "statusText": "OK" + }, + "startedDateTime": "2020-08-14T16:44:22.076Z", + "time": 228, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 228 + } + } + ], + "pages": [], + "version": "1.2" + } +} diff --git a/spec/recordings/OpenStreetMap-_925808948/-Nominatim-Geocoder-Provider_1884184056/receives-correct-geodecoding-results_1602611079/recording.har b/spec/recordings/OpenStreetMap-_925808948/-Nominatim-Geocoder-Provider_1884184056/receives-correct-geodecoding-results_1602611079/recording.har new file mode 100644 index 0000000..4669ff7 --- /dev/null +++ b/spec/recordings/OpenStreetMap-_925808948/-Nominatim-Geocoder-Provider_1884184056/receives-correct-geodecoding-results_1602611079/recording.har @@ -0,0 +1,130 @@ +{ + "log": { + "_recordingName": "OpenStreetMap / Nominatim Geocoder Provider/receives correct geodecoding results", + "creator": { + "comment": "persister:fs", + "name": "Polly.JS", + "version": "5.0.0" + }, + "entries": [ + { + "_id": "d20abddf41db9792facbe92762e23fda", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 0, + "cookies": [], + "headers": [ + { + "_fromType": "array", + "name": "user-agent", + "value": "GeocoderJS Example" + }, + { + "_fromType": "array", + "name": "accept", + "value": "*/*" + }, + { + "_fromType": "array", + "name": "accept-encoding", + "value": "gzip,deflate" + }, + { + "_fromType": "array", + "name": "connection", + "value": "close" + }, + { + "name": "host", + "value": "nominatim.openstreetmap.org" + } + ], + "headersSize": 265, + "httpVersion": "HTTP/1.1", + "method": "GET", + "queryString": [ + { + "name": "lat", + "value": "48.8631507" + }, + { + "name": "lon", + "value": "2.388911" + }, + { + "name": "zoom", + "value": "18" + }, + { + "name": "format", + "value": "jsonv2" + }, + { + "name": "addressdetails", + "value": "1" + } + ], + "url": "https://nominatim.openstreetmap.org/reverse?lat=48.8631507&lon=2.388911&zoom=18&format=jsonv2&addressdetails=1" + }, + "response": { + "bodySize": 679, + "content": { + "mimeType": "application/json; charset=UTF-8", + "size": 679, + "text": "{\"place_id\":161274479,\"licence\":\"Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright\",\"osm_type\":\"way\",\"osm_id\":322777831,\"lat\":\"48.863744499999996\",\"lon\":\"2.3911562136123106\",\"place_rank\":26,\"category\":\"highway\",\"type\":\"pedestrian\",\"importance\":0.09999999999999998,\"addresstype\":\"road\",\"name\":null,\"display_name\":\"Quartier du Père-Lachaise, Paris, Île-de-France, France métropolitaine, 75020, France\",\"address\":{\"suburb\":\"Quartier du Père-Lachaise\",\"city\":\"Paris\",\"municipality\":\"Paris\",\"county\":\"Paris\",\"state\":\"Île-de-France\",\"country\":\"France\",\"postcode\":\"75020\",\"country_code\":\"fr\"},\"boundingbox\":[\"48.8625929\",\"48.8648832\",\"2.3877078\",\"2.3956964\"]}" + }, + "cookies": [], + "headers": [ + { + "name": "server", + "value": "nginx" + }, + { + "name": "date", + "value": "Fri, 14 Aug 2020 16:51:39 GMT" + }, + { + "name": "content-type", + "value": "application/json; charset=UTF-8" + }, + { + "name": "transfer-encoding", + "value": "chunked" + }, + { + "name": "connection", + "value": "close" + }, + { + "name": "access-control-allow-origin", + "value": "*" + }, + { + "name": "access-control-allow-methods", + "value": "OPTIONS,GET" + } + ], + "headersSize": 223, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 200, + "statusText": "OK" + }, + "startedDateTime": "2020-08-14T16:51:39.152Z", + "time": 159, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 159 + } + } + ], + "pages": [], + "version": "1.2" + } +} diff --git a/spec/recordings/Yandex-Geocoder-Provider_3381125107/receives-correct-geocoding-results_3640432360/recording.har b/spec/recordings/Yandex-Geocoder-Provider_3381125107/receives-correct-geocoding-results_3640432360/recording.har new file mode 100644 index 0000000..7561443 --- /dev/null +++ b/spec/recordings/Yandex-Geocoder-Provider_3381125107/receives-correct-geocoding-results_3640432360/recording.har @@ -0,0 +1,155 @@ +{ + "log": { + "_recordingName": "Yandex Geocoder Provider/receives correct geocoding results", + "creator": { + "comment": "persister:fs", + "name": "Polly.JS", + "version": "5.0.0" + }, + "entries": [ + { + "_id": "78a91888b5016be96b3a58fdabda2662", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 0, + "cookies": [], + "headers": [ + { + "_fromType": "array", + "name": "accept", + "value": "*/*" + }, + { + "_fromType": "array", + "name": "user-agent", + "value": "node-fetch/1.0 (+https://github.com/bitinn/node-fetch)" + }, + { + "_fromType": "array", + "name": "accept-encoding", + "value": "gzip,deflate" + }, + { + "_fromType": "array", + "name": "connection", + "value": "close" + }, + { + "name": "host", + "value": "geocode-maps.yandex.ru" + } + ], + "headersSize": 347, + "httpVersion": "HTTP/1.1", + "method": "GET", + "queryString": [ + { + "name": "geocode", + "value": "1600 Pennsylvania Ave, Washington, DC" + }, + { + "name": "format", + "value": "json" + }, + { + "name": "lang", + "value": "en_US" + }, + { + "name": "results", + "value": "5" + } + ], + "url": "https://geocode-maps.yandex.ru/1.x?geocode=1600+Pennsylvania+Ave%2C+Washington%2C+DC&format=json&lang=en_US&results=5" + }, + "response": { + "bodySize": 1400, + "content": { + "_isBinary": true, + "mimeType": "application/json; charset=utf-8", + "size": 1400, + "text": "[\"1f8b0800000000000003e4945d6fd3301486ff4ae4eb30e5a3cec7ee4a8be062948981b840137293b3d590d8c1713aa6a9ff7dc7499a795b09bdd8540695aadace39f6fbbe6e9e1ba2a0aea4a8811cdf90b7203f2cbf43a667b228f0874b61964bd06cce343b55b202a5affbd24ce6a03ef6edeffb1af34cc1cf066a4d8e891f799e730a42d4d7c59a09ce9ce91a5ce70bab575c5c6a295c673e23ae11d114bac60e8ab30bd9881cc721d96c70064c370a0f2897a0c8f1574be69fc4d9a22a0519af5b47a4d60a40e3491a7e19999f05d7903b679a69a81d79e14c4b503c63a88e632dcfb459c4509a72c9d97dfd0fdd89069c85547a75652270c90fde7a198e9ce639baad8da40c7d6a75fdcd68352ace5aefaa641ad53cb3ac992cf1de4098d031d25e65af089f0b56c28804b27187a64ac9351719dc75ed92677714326305d7d63977d2edba21b4be6adcd4e67c33c43bc79be7459bf2acf784c3fee1091763de9e26def6d005ea9e59b76b2d8f856b6c945c18154cf3354c15b04effc3d5c548e02e39d9e68cbddbf1e251e02ef9b4924a3697ab0ba65a0ed8f3c55ed1779f3d2fca2539d499e2550718720f073ba31f496a696001f9ebd6e41bb186023960c685bc0235934a186a9057717ce4d1c4f7a813264749ea457e80dd4d553d2cf283491099a2d4a3c1246919742ab9686953c97a5b17869197749b518a9b19ff2f804d67b2d12b607f1b9b6c59ff0c9bee4c1d9a4df7e3fd0fd86445bf079bec780ec4262f9ca42d769238a1e92e36454769e2539ab645491c05fe6e36615d1ad030eeea422f7d3236490c493d1f9adef1cb15bef30397f27e97c3926950f522b0646536f27fef3d1d9a4a56b42f094973a800df6ba1ed86478b2370dac6bf079986880e812524494c11252d49280d03ba1b4b93200cfc0e37be17ff964a14d1d651294afc3435fecff17b0b0000ffff030097724dfd840e0000\"]" + }, + "cookies": [], + "headers": [ + { + "name": "server", + "value": "nginx" + }, + { + "name": "date", + "value": "Fri, 25 Sep 2020 18:16:29 GMT" + }, + { + "name": "content-type", + "value": "application/json; charset=utf-8" + }, + { + "name": "transfer-encoding", + "value": "chunked" + }, + { + "name": "connection", + "value": "close" + }, + { + "name": "vary", + "value": "Accept-Encoding" + }, + { + "name": "access-control-allow-origin", + "value": "*" + }, + { + "name": "access-control-allow-methods", + "value": "GET" + }, + { + "name": "access-control-allow-headers", + "value": "*" + }, + { + "name": "etag", + "value": "W/\"e84-Gkzjv5mxlcw0btuJhYzfeK72c44\"" + }, + { + "name": "x-xss-protection", + "value": "1; mode=block" + }, + { + "name": "x-content-type-options", + "value": "nosniff" + }, + { + "name": "content-encoding", + "value": "gzip" + }, + { + "name": "x-qloud-router", + "value": "man4-a74d0ae6f388.qloud-c.yandex.net" + } + ], + "headersSize": 458, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 200, + "statusText": "OK" + }, + "startedDateTime": "2020-09-25T18:16:29.490Z", + "time": 274, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 274 + } + } + ], + "pages": [], + "version": "1.2" + } +} diff --git a/spec/recordings/Yandex-Geocoder-Provider_3381125107/receives-correct-geodecoding-results_1602611079/recording.har b/spec/recordings/Yandex-Geocoder-Provider_3381125107/receives-correct-geodecoding-results_1602611079/recording.har new file mode 100644 index 0000000..0b31798 --- /dev/null +++ b/spec/recordings/Yandex-Geocoder-Provider_3381125107/receives-correct-geodecoding-results_1602611079/recording.har @@ -0,0 +1,155 @@ +{ + "log": { + "_recordingName": "Yandex Geocoder Provider/receives correct geodecoding results", + "creator": { + "comment": "persister:fs", + "name": "Polly.JS", + "version": "5.0.0" + }, + "entries": [ + { + "_id": "c47d435f272384213688cbf36c8295a1", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 0, + "cookies": [], + "headers": [ + { + "_fromType": "array", + "name": "accept", + "value": "*/*" + }, + { + "_fromType": "array", + "name": "user-agent", + "value": "node-fetch/1.0 (+https://github.com/bitinn/node-fetch)" + }, + { + "_fromType": "array", + "name": "accept-encoding", + "value": "gzip,deflate" + }, + { + "_fromType": "array", + "name": "connection", + "value": "close" + }, + { + "name": "host", + "value": "geocode-maps.yandex.ru" + } + ], + "headersSize": 319, + "httpVersion": "HTTP/1.1", + "method": "GET", + "queryString": [ + { + "name": "geocode", + "value": "2.388911,48.8631507" + }, + { + "name": "format", + "value": "json" + }, + { + "name": "lang", + "value": "en_US" + }, + { + "name": "results", + "value": "5" + } + ], + "url": "https://geocode-maps.yandex.ru/1.x?geocode=2.388911%2C48.8631507&format=json&lang=en_US&results=5" + }, + "response": { + "bodySize": 1834, + "content": { + "_isBinary": true, + "mimeType": "application/json; charset=utf-8", + "size": 1834, + "text": "[\"1f8b0800000000000003ec97df6ed33014c65f25f27536e56f9becae74b09b51cac41d42c84bcea821b183e3944d535f81e7e03df6621c3b7fe6b55d2744a960a23775eccff677bea3fcd6dd12097525780de4e4969c817873f91932351545815f4c703d5d82a2a754d1b914154875d349339183bce8b6bfee347a6d2e18577a50899a9c90e0384c92d4f79d28394e46a11ffb64e5e2bd5f1ba895b5eef6ebde98e8f5ba2994de1fe3d39568788ee394ac70ef1550d548bcb3bc04494ede5bce9ff26bfbac2464ac364512b8a6b8d9250aaeb5a75792f20c5ce7ee7b0147391cf5cf732a59ed3a8107ce444ac17356d7500257ae3359026fc039a3684a29ea3abe87e77d61c6f7423418b14b26798e85d5faf60c4b52f2e6a3b6a56fbc3065ca922a05f93e2d4c45892d42416da2ea1c75d7e33aa7250cf7616b064925c592e9b941f3d08bad2d44460ba6acf38c4f5b8246956426e54eb25983ad473580a55eabce96f6f1764a2c7bf56135a47d8a3d6785097dda558dc36ef19cf1fbeaf792b6b961864ea65667ade9fbacb5c392719d0b556c091309b4b5b63e3bdb9abf4bcefbd871573f9ed9f9bbe4142ae0393ab6b51b93b3473be292770b2145f3697145a5c184fd3cdbde1b97cc2594acc54a379c35edeb6adad37f1eeb6e97660e752659d592688bbba141eb7d1b12bad4e480fc8529fb255f428150d0e3427c033915921b4f1a42d1388d5a487961eae3dea6aa1e4ad2d0f33a499c788981d136da2569dad34e97eaee0150c3dbb03742ddd36938fba0787ace683a387dfe93a78ffe09b01c1a2ac8820e0541b805299137f2bcd028c6f13808b62325f2bc20698f19a571b22fa608b5401bbf8394b70d958a8174f2c699dffd9070744eb30565e6aff1e6cbf567f9b2d3ccbf8a9a4dfd8e320fc09d27427e7608faa51376b5669d4b3b833c28a28271d2fe73168fa2603ba3c2a8fdd933f6127ff4c8cf9e34f0fdb88554e425d15f01a99f000000ffff\",\"dc96311202210c45afb24720d910a0b7b6b1b6d0597b1b0fe539bc98b0c40227b19165460b2a98c04f783fa9351c6b443f67365b9bc63f1ac33bcdaaea96e27ec4068e5c892566220d59e07996b1221298630522b38c2711d36064778ffb357be09aad69b94cafdac8e73d95ba7741d5ba6808a62243cee94fe985a02d74247e87db590fa16eec3f26a6a5ccd2d782f63d618884b872019e126b8071ca6dae1c490ec181d113d1395f03f91852287a8e793d010000ffff\",\"0300377eef3b77150000\"]" + }, + "cookies": [], + "headers": [ + { + "name": "server", + "value": "nginx" + }, + { + "name": "date", + "value": "Fri, 25 Sep 2020 18:16:29 GMT" + }, + { + "name": "content-type", + "value": "application/json; charset=utf-8" + }, + { + "name": "transfer-encoding", + "value": "chunked" + }, + { + "name": "connection", + "value": "close" + }, + { + "name": "vary", + "value": "Accept-Encoding" + }, + { + "name": "access-control-allow-origin", + "value": "*" + }, + { + "name": "access-control-allow-methods", + "value": "GET" + }, + { + "name": "access-control-allow-headers", + "value": "*" + }, + { + "name": "etag", + "value": "W/\"1577-SWIN9f7y+Rbp/Lt3aKBPRwRs6GI\"" + }, + { + "name": "x-xss-protection", + "value": "1; mode=block" + }, + { + "name": "x-content-type-options", + "value": "nosniff" + }, + { + "name": "content-encoding", + "value": "gzip" + }, + { + "name": "x-qloud-router", + "value": "man4-a74d0ae6f388.qloud-c.yandex.net" + } + ], + "headersSize": 459, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 200, + "statusText": "OK" + }, + "startedDateTime": "2020-09-25T18:16:29.142Z", + "time": 323, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 323 + } + } + ], + "pages": [], + "version": "1.2" + } +} diff --git a/spec/recordings/Yandex-Geocoder-Provider_3381125107/receives-error-when-the-API-key-is-bad_860720186/recording.har b/spec/recordings/Yandex-Geocoder-Provider_3381125107/receives-error-when-the-API-key-is-bad_860720186/recording.har new file mode 100644 index 0000000..3ea9f90 --- /dev/null +++ b/spec/recordings/Yandex-Geocoder-Provider_3381125107/receives-error-when-the-API-key-is-bad_860720186/recording.har @@ -0,0 +1,134 @@ +{ + "log": { + "_recordingName": "Yandex Geocoder Provider/receives error when the API key is bad", + "creator": { + "comment": "persister:fs", + "name": "Polly.JS", + "version": "5.0.0" + }, + "entries": [ + { + "_id": "dd683a194fccad3abe8af22c9534149d", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 0, + "cookies": [], + "headers": [ + { + "_fromType": "array", + "name": "accept", + "value": "*/*" + }, + { + "_fromType": "array", + "name": "user-agent", + "value": "node-fetch/1.0 (+https://github.com/bitinn/node-fetch)" + }, + { + "_fromType": "array", + "name": "accept-encoding", + "value": "gzip,deflate" + }, + { + "_fromType": "array", + "name": "connection", + "value": "close" + }, + { + "name": "host", + "value": "geocode-maps.yandex.ru" + } + ], + "headersSize": 307, + "httpVersion": "HTTP/1.1", + "method": "GET", + "queryString": [ + { + "name": "geocode", + "value": "1600 Pennsylvania Ave, Washington, DC" + }, + { + "name": "format", + "value": "json" + }, + { + "name": "results", + "value": "5" + } + ], + "url": "https://geocode-maps.yandex.ru/1.x?geocode=1600+Pennsylvania+Ave%2C+Washington%2C+DC&format=json&results=5" + }, + "response": { + "bodySize": 62, + "content": { + "mimeType": "application/json; charset=utf-8", + "size": 62, + "text": "{\"statusCode\":403,\"error\":\"Forbidden\",\"message\":\"Invalid key\"}" + }, + "cookies": [], + "headers": [ + { + "name": "server", + "value": "nginx" + }, + { + "name": "date", + "value": "Fri, 25 Sep 2020 18:16:29 GMT" + }, + { + "name": "content-type", + "value": "application/json; charset=utf-8" + }, + { + "name": "content-length", + "value": "62" + }, + { + "name": "connection", + "value": "close" + }, + { + "name": "access-control-allow-origin", + "value": "*" + }, + { + "name": "access-control-allow-methods", + "value": "GET" + }, + { + "name": "access-control-allow-headers", + "value": "*" + }, + { + "name": "etag", + "value": "W/\"3e-cw06Pdy6OiytV8KcUexnEAwoCRw\"" + }, + { + "name": "x-qloud-router", + "value": "man2-7275f28abf80.qloud-c.yandex.net" + } + ], + "headersSize": 336, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 403, + "statusText": "Forbidden" + }, + "startedDateTime": "2020-09-25T18:16:28.849Z", + "time": 279, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 279 + } + } + ], + "pages": [], + "version": "1.2" + } +} diff --git a/spec/runner.html b/spec/runner.html deleted file mode 100644 index 3d39359..0000000 --- a/spec/runner.html +++ /dev/null @@ -1,73 +0,0 @@ - - - - - Jasmine Spec Runner - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/spec/setupPolly.ts b/spec/setupPolly.ts new file mode 100644 index 0000000..fb7ecb7 --- /dev/null +++ b/spec/setupPolly.ts @@ -0,0 +1,56 @@ +import { Polly } from "@pollyjs/core"; +import NodeHttpAdapter from "@pollyjs/adapter-node-http"; +import FSPersister from "@pollyjs/persister-fs"; +import { MODES } from "@pollyjs/utils"; +import { setupPolly, Context } from "setup-polly-jest"; +import path from "path"; + +Polly.register(NodeHttpAdapter); +Polly.register(FSPersister); + +const excludedQueryParams = ["access_token", "apikey", "key"]; + +export const cleanRecording = (context: Context): void => { + context.polly.server + .any() + .on("beforePersist", (req, { request, response }) => { + request.queryString = request.queryString.filter( + ({ name }: { name: string }) => !excludedQueryParams.includes(name) + ); + const url = new URL(request.url); + excludedQueryParams.forEach((param) => url.searchParams.delete(param)); + request.url = url.toString(); + excludedQueryParams.forEach((param) => { + response.content.text = response.content.text.replace( + new RegExp(`${param}=[^&]+&`), + "" + ); + }); + }); +}; + +export default (): Context => + setupPolly({ + mode: MODES.REPLAY, + recordIfMissing: process.env.POLLY_RECORD + ? process.env.POLLY_RECORD === "1" + : false, + recordFailedRequests: true, + adapters: ["node-http"], + persister: "fs", + persisterOptions: { + fs: { + recordingsDir: path.resolve(__dirname, "./recordings"), + }, + }, + matchRequestsBy: { + url: { + query: (query) => { + const filteredQuery = { ...query }; + excludedQueryParams.forEach((param) => delete filteredQuery[param]); + + return filteredQuery; + }, + }, + }, + }); diff --git a/spec/support/jasmine.json b/spec/support/jasmine.json new file mode 100644 index 0000000..fac234e --- /dev/null +++ b/spec/support/jasmine.json @@ -0,0 +1,11 @@ +{ + "spec_dir": "spec", + "spec_files": [ + "**/*[sS]pec.ts" + ], + "helpers": [ + "helpers/**/*.ts" + ], + "stopSpecOnExpectationFailure": false, + "random": true +} diff --git a/spec/utils.spec.ts b/spec/utils.spec.ts new file mode 100644 index 0000000..5103f38 --- /dev/null +++ b/spec/utils.spec.ts @@ -0,0 +1,52 @@ +import { + filterUndefinedObjectValues, + decodeUrlSafeBase64, + encodeUrlSafeBase64, + decodeBase64, + isIpv4, + isIpv6, +} from "utils"; + +describe("Utilities", () => { + it("can filter undefined object values", () => { + const object = { + string: "string", + undefined, + boolean: true, + }; + + expect(filterUndefinedObjectValues(object)).toEqual({ + string: "string", + boolean: true, + }); + }); + + const encodedBase64 = + "RW5jb2RlZCBzdHJpbmcgd2l0aCBub24tc2FmZSBjaGFyYWN0ZXJzOiA/Lg=="; + const urlSafeBase64 = + "RW5jb2RlZCBzdHJpbmcgd2l0aCBub24tc2FmZSBjaGFyYWN0ZXJzOiA_Lg=="; + + it("can decode URL-safe base64", () => { + expect(decodeUrlSafeBase64(urlSafeBase64)).toEqual(encodedBase64); + }); + + it("can encode URL-safe base64", () => { + expect(encodeUrlSafeBase64(encodedBase64)).toEqual(urlSafeBase64); + }); + + it("can decode base64", () => { + expect(decodeBase64(encodedBase64)).toEqual( + "Encoded string with non-safe characters: ?." + ); + }); + + it("can test IPV4", () => { + expect(isIpv4("190.226.155.134")).toBeTrue(); + expect(isIpv4("1aa3:9379:0fcb:0044:f09a:ca53:baf4:0472")).toBeFalse(); + }); + + it("can test IPV6", () => { + expect(isIpv6("1aa3:9379:0fcb:0044:f09a:ca53:baf4:0472")).toBeTrue(); + expect(isIpv6("190.226.155.134")).toBeFalse(); + }); +}); diff --git a/src/AdminLevel.ts b/src/AdminLevel.ts new file mode 100644 index 0000000..186126f --- /dev/null +++ b/src/AdminLevel.ts @@ -0,0 +1,49 @@ +export interface AdminLevelObject { + readonly level: number; + readonly name: string; + readonly code?: string; +} + +// eslint-disable-next-line no-shadow +export enum ADMIN_LEVEL_CODES { + STATE_CODE = 1, + COUNTY_CODE = 2, +} + +export default class AdminLevel { + private readonly level: number; + + private readonly name: string; + + private readonly code?: string; + + private constructor({ level, name, code }: AdminLevelObject) { + this.level = level; + this.name = name; + this.code = code; + } + + public static create(object: AdminLevelObject): AdminLevel { + return new this(object); + } + + public toObject(): AdminLevelObject { + return { + level: this.level, + name: this.name, + code: this.code, + }; + } + + public getLevel(): number { + return this.level; + } + + public getName(): string { + return this.name; + } + + public getCode(): undefined | string { + return this.code; + } +} diff --git a/src/ExternalLoader.ts b/src/ExternalLoader.ts new file mode 100644 index 0000000..3d07542 --- /dev/null +++ b/src/ExternalLoader.ts @@ -0,0 +1,179 @@ +import fetch from "cross-fetch"; +import { ErrorCallback } from "provider"; +import { ResponseError } from "error"; +import { isBrowser, filterUndefinedObjectValues } from "utils"; +import { PartialSome } from "types"; + +export interface ExternalLoaderOptions { + readonly protocol: string; + readonly host?: string; + readonly pathname?: string; + readonly method: "GET" | "POST"; +} + +export interface ExternalLoaderBody { + [param: string]: ExternalLoaderBody | string | number | undefined; +} + +export interface ExternalLoaderParams { + [param: string]: string | undefined; + jsonpCallback?: string; +} + +export interface ExternalLoaderHeaders { + [header: string]: string | undefined; +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type ResponseCallback = (response: any) => void; + +export interface ExternalLoaderInterface { + setOptions(options: PartialSome): void; + getOptions(): ExternalLoaderOptions; + executeRequest( + params: ExternalLoaderParams, + callback: ResponseCallback, + headers?: ExternalLoaderHeaders, + body?: ExternalLoaderBody, + errorCallback?: ErrorCallback + ): void; +} + +const defaultOptions: ExternalLoaderOptions = { + protocol: "http", + method: "GET", +}; + +/** + * Load data from external geocoding engines. + */ +export default class ExternalLoader implements ExternalLoaderInterface { + private options: ExternalLoaderOptions = defaultOptions; + + public constructor( + options: PartialSome = defaultOptions + ) { + this.setOptions(options); + } + + public setOptions( + options: PartialSome + ): void { + this.options = { ...defaultOptions, ...options }; + } + + public getOptions(): ExternalLoaderOptions { + return this.options; + } + + public executeRequest( + params: ExternalLoaderParams, + callback: ResponseCallback, + externalLoaderHeaders?: ExternalLoaderHeaders, + body?: ExternalLoaderBody, + errorCallback?: ErrorCallback + ): void { + const { protocol, host, pathname, method } = this.options; + + if (!host) { + throw new Error("A host is required for the external loader."); + } + if (!pathname) { + throw new Error("A pathname is required for the external loader."); + } + + const requestUrl = new URL(`${protocol}://${host}/${pathname}`); + + const { jsonpCallback, ...requestParams } = params; + + const filteredRequestParams = filterUndefinedObjectValues(requestParams); + Object.keys(filteredRequestParams).forEach((paramKey) => + requestUrl.searchParams.append( + paramKey, + filteredRequestParams[paramKey] ?? "" + ) + ); + + if (jsonpCallback) { + ExternalLoader.runJsonpCallback(requestUrl, callback, jsonpCallback); + return; + } + + const headers = filterUndefinedObjectValues(externalLoaderHeaders || {}); + fetch(requestUrl.toString(), { + headers, + method, + body: method === "POST" ? JSON.stringify(body) : undefined, + }) + .then((response) => { + if (!response.ok) { + throw new ResponseError( + `Received HTTP status code ${response.status} when attempting geocoding request.`, + response + ); + } + return response.json(); + }) + .then((data) => callback(data)) + .catch((error) => { + if (errorCallback && error instanceof ResponseError) { + errorCallback(error); + return; + } + setTimeout(() => { + throw error; + }); + }); + } + + private static runJsonpCallback( + requestUrl: URL, + callback: ResponseCallback, + jsonpCallback: string + ): void { + if (!isBrowser()) { + throw new Error( + '"jsonpCallback" parameter can only be used in a browser environment.' + ); + } + + requestUrl.searchParams.append( + jsonpCallback, + ExternalLoader.generateJsonpCallback(callback) + ); + + // Create a new script element. + const scriptElement = document.createElement("script"); + + // Set its source to the JSONP API. + scriptElement.src = requestUrl.toString(); + + // Stick the script element in the page . + document.getElementsByTagName("head")[0].appendChild(scriptElement); + } + + /** + * Generates randomly-named function to use as a callback for JSONP requests. + * @see https://github.com/OscarGodson/JSONP + */ + private static generateJsonpCallback(callback: ResponseCallback): string { + // Use timestamp + a random factor to account for a lot of requests in a short time. + // e.g. jsonp1394571775161. + const timestamp = Date.now(); + const generatedFunction = `jsonp${Math.round( + timestamp + Math.random() * 1000001 + )}`; + + // Generate the temp JSONP function using the name above. + // First, call the function the user defined in the callback param [callback(json)]. + // Then delete the generated function from the window [delete window[generatedFunction]]. + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (window)[generatedFunction] = (json: string) => { + callback(json); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + delete (window)[generatedFunction]; + }; + + return generatedFunction; + } +} diff --git a/src/ExternalURILoader.js b/src/ExternalURILoader.js deleted file mode 100644 index a360450..0000000 --- a/src/ExternalURILoader.js +++ /dev/null @@ -1,202 +0,0 @@ -/** - * GeocoderJS.ExternalURILoader - * - Used to load data from external geocoding engines. - */ - -if (typeof GeocoderJS === "undefined" && typeof require === "function") { - var GeocoderJS = require("../GeocoderJS.js"); -} - -;(function (GeocoderJS, window) { - "use strict"; - - GeocoderJS.ExternalURILoader = function(options) { - this.options = {}; - - // @TODO: remove and add a throw if no options are passed. - if (options === undefined) { - options = {}; - } - - this.setOptions(options); - }; - - GeocoderJS.ExternalURILoader.prototype.setOptions = function(options) { - var defaults = { - protocol: null, - host: null, - pathname: null - }; - - for (var i in defaults) { - this.options[i] = (options[i] !== undefined) ? options[i] : defaults[i]; - } - }; - - GeocoderJS.ExternalURILoader.prototype.executeRequest = function(params, callback) { - var _this = this; - if (typeof XMLHttpRequest !== "undefined") { - return executeDOMRequest(params, callback); - } - - try { - var url = require("url"); - return executeNodeRequest(params, callback); - } - catch (err) { - // Intentionally empty. - } - - return callback(null); - - function executeNodeRequest(params, callback) { - var url = require("url"), - http = require(_this.options.protocol), - urlObj = { - "protocol": _this.options.protocol, - "host": _this.options.host, - "pathname": _this.options.pathname, - "query": params - }, - requestUrl; - - requestUrl = url.format(urlObj); - - http.get(requestUrl, function(res) { - if (res.statusCode != 200) { - throw("Received HTTP status code " + res.statusCode + " when attempting geocoding request."); - } - - res.data = ""; - res.setEncoding("utf8"); - - res.on("data", function (chunk) { - res.data += chunk; - }); - - res.on("end", function () { - if (!res.data || !res.data.length) { - throw("Received empty data when attempting geocoding request."); - } - - var data = false, - i = 0, - results = []; - try { - data = JSON.parse(res.data); - } - catch(e) { - throw("Received invalid JSON data when attempting geocoding request."); - } - - if (data && data.status) { - if (data.status === "OVER_QUERY_LIMIT") { - throw("Exceeded daily quota when attempting geocoding request."); - } - else if (data.status === "OK" && data.results) { - for (; i < data.results.length; i++) { - results.push(GeocoderJS.GoogleAPIProvider.prototype.mapToGeocoded(data.results[i])); - } - return callback(results); - } - } - - throw("Received unexpected JSON data when attempting geocoding request."); - }); - }).on("error", function(err) { - throw(err); - }); - } - - function executeDOMRequest(params, callback) { - var req = new XMLHttpRequest(), - requestUrl = _this.options.protocol + "://" + _this.options.host + "/" + _this.options.pathname + "?", - JSONPCallback; - - var paramsList = []; - - if (params.JSONPCallback) { - JSONPCallback = params.JSONPCallback; - delete params.JSONPCallback; - params[JSONPCallback] = generateJSONPCallback(callback); - } - - for (var key in params) { - if (params.hasOwnProperty(key)) { - paramsList.push(key + "=" + params[key]); - //} - } - } - - requestUrl += paramsList.join('&'); - - if (JSONPCallback) { - // Create a new script element - var script_element = document.createElement('script'); - - // Set its source to the JSONP API - script_element.src = requestUrl; - - // Stick the script element in the page - document.getElementsByTagName('head')[0].appendChild(script_element); - } else { - req.onload = function () { - if (this.status != 200) { - console.log("Received HTTP status code " + this.status + " when attempting geocoding request."); - return callback(null); - } - - if (!this.responseText || !this.responseText.length) { - console.log("Received empty data when attempting geocoding request."); - return callback(null); - } - - var data = false, - i = 0, - results = []; - try { - data = JSON.parse(this.responseText); - } - catch(e) { - console.log("Received invalid JSON data when attempting geocoding request."); - return callback(null); - } - - if (data) { - return callback(data); - } - - console.log("Received unexpected JSON data when attempting geocoding request."); - return callback(null); - }; - - req.open("GET", requestUrl); - req.send(); - } - } - - }; - - /** - * Generates randomly-named function to use as a callback for JSONP requests. - * From https://github.com/OscarGodson/JSONP - * @returns {string} - Function name - */ - function generateJSONPCallback(callback) { - //Use timestamp + a random factor to account for a lot of requests in a short time - //e.g. jsonp1394571775161 - var timestamp = Date.now(); - var generatedFunction = 'jsonp'+Math.round(timestamp+Math.random()*1000001); - - //Generate the temp JSONP function using the name above - //First, call the function the user defined in the callback param [callback(json)] - //Then delete the generated function from the window [delete window[generatedFunction]] - window[generatedFunction] = function(json){ - callback(json); - delete window[generatedFunction]; - }; - - return generatedFunction; - } - -})(GeocoderJS, window); diff --git a/src/GeoJSONDumper.js b/src/GeoJSONDumper.js deleted file mode 100644 index caa7978..0000000 --- a/src/GeoJSONDumper.js +++ /dev/null @@ -1,27 +0,0 @@ -if (typeof GeocoderJS === "undefined" && typeof require === "function") { - var GeocoderJS = require("./GeocoderJS.js"); -} - -;(function (GeocoderJS) { - "use strict"; - - var baseGeoJSON = { - "type": "Feature", - "properties": {}, - "geometry": { - "type": "Point", - "coordinates": [] - } - }; - - GeocoderJS.GeoJSONDumper = function() { - return { - dump: function(geocoded) { - var result = baseGeoJSON; - result.geometry.coordinates = [geocoded.getLongitude(), geocoded.getLatitude()]; - return result; - } - }; - }; - -})(GeocoderJS); diff --git a/src/GeoJsonDumper.ts b/src/GeoJsonDumper.ts new file mode 100644 index 0000000..42de3b8 --- /dev/null +++ b/src/GeoJsonDumper.ts @@ -0,0 +1,105 @@ +import Geocoded from "Geocoded"; +import AdminLevel, { AdminLevelObject } from "AdminLevel"; +import { + BoundingBox, + Coordinates, + FlatBoundingBox, + FlatCoordinates, +} from "types"; + +export interface GeoJson { + readonly type: "Feature"; + readonly properties: { + readonly [property: string]: + | string + | string[] + | number + | boolean + | Coordinates + | BoundingBox + | AdminLevel[] + | AdminLevelObject[] + | undefined; + }; + readonly geometry: { + readonly type: "Point"; + readonly coordinates: FlatCoordinates; + }; + readonly bbox?: FlatBoundingBox; +} + +export default class GeoJsonDumper { + private static baseGeoJson: GeoJson = { + type: "Feature", + properties: {}, + geometry: { + type: "Point", + coordinates: [0, 0], + }, + }; + + public static dump(geocoded: Geocoded): GeoJson { + let result = GeoJsonDumper.baseGeoJson; + const { + coordinates, + bounds, + adminLevels, + ...geocodedProperties + } = geocoded.toObject(); + + let properties: { + [property: string]: + | string + | string[] + | number + | boolean + | Coordinates + | BoundingBox + | AdminLevel[] + | AdminLevelObject[] + | undefined; + } = { ...geocodedProperties }; + Object.keys(properties).forEach( + (property) => + properties[property] === undefined && delete properties[property] + ); + + if (adminLevels && adminLevels.length > 0) { + properties = { + ...properties, + adminLevels: adminLevels.map((adminLevel) => adminLevel.toObject()), + }; + } + + result = { ...result, properties }; + + if (coordinates) { + result = { + ...result, + ...{ + geometry: { + ...result.geometry, + coordinates: [ + parseFloat(coordinates.longitude.toString()), + parseFloat(coordinates.latitude.toString()), + ], + }, + }, + }; + } + + if (bounds) { + result = { + ...result, + bbox: [ + parseFloat(bounds.longitudeSW.toString()), + parseFloat(bounds.latitudeSW.toString()), + parseFloat(bounds.longitudeNE.toString()), + parseFloat(bounds.latitudeNE.toString()), + ], + }; + } + + return result; + } +} diff --git a/src/Geocoded.js b/src/Geocoded.js deleted file mode 100644 index f7b8cc9..0000000 --- a/src/Geocoded.js +++ /dev/null @@ -1,25 +0,0 @@ -if (typeof GeocoderJS === "undefined" && typeof require === "function") { - var GeocoderJS = require("./GeocoderJS.js"); -} - -;(function (GeocoderJS) { - "use strict"; - - GeocoderJS.Geocoded = function() {}; - - GeocoderJS.Geocoded.prototype = { - getCoordinates: function() {return[this.latitude, this.longitude];}, - getLatitude: function() {return this.latitude;}, - getLongitude: function() {return this.longitude;}, - getBounds: function() {}, - getStreetNumber: function() {return this.streetNumber;}, - getStreetName: function() {return this.streetName;}, - getCity: function() {return this.city;}, - getZipcode: function() {return this.postal_code;}, - getCityDistrict: function() {}, - getCounty: function() {}, - getCountyCode: function() {}, - getRegion: function() {return this.region;} - }; - -})(GeocoderJS); diff --git a/src/Geocoded.ts b/src/Geocoded.ts new file mode 100644 index 0000000..85c1206 --- /dev/null +++ b/src/Geocoded.ts @@ -0,0 +1,177 @@ +import AdminLevel from "AdminLevel"; +import { BoundingBox, Coordinates } from "types"; + +export interface GeocodedObject { + readonly [property: string]: + | string + | string[] + | number + | boolean + | Coordinates + | BoundingBox + | AdminLevel[] + | undefined; + readonly coordinates?: Coordinates; + readonly bounds?: BoundingBox; + readonly formattedAddress?: string; + readonly streetNumber?: string; + readonly streetName?: string; + readonly subLocality?: string; + readonly locality?: string; + readonly postalCode?: string; + readonly region?: string; + readonly adminLevels?: AdminLevel[]; + readonly country?: string; + readonly countryCode?: string; + readonly timezone?: string; +} + +export default class Geocoded { + private readonly coordinates?: Coordinates; + + private readonly bounds?: BoundingBox; + + private readonly formattedAddress?: string; + + private readonly streetNumber?: string; + + private readonly streetName?: string; + + private readonly subLocality?: string; + + private readonly locality?: string; + + private readonly postalCode?: string; + + private readonly region?: string; + + private readonly adminLevels: AdminLevel[]; + + private readonly country?: string; + + private readonly countryCode?: string; + + private readonly timezone?: string; + + protected constructor({ + coordinates, + bounds, + formattedAddress, + streetNumber, + streetName, + subLocality, + locality, + postalCode, + region, + adminLevels, + country, + countryCode, + timezone, + }: GeocodedObject) { + this.coordinates = coordinates; + this.bounds = bounds; + this.formattedAddress = formattedAddress; + this.streetNumber = streetNumber; + this.streetName = streetName; + this.subLocality = subLocality; + this.locality = locality; + this.postalCode = postalCode; + this.region = region; + this.adminLevels = adminLevels || []; + this.country = country; + this.countryCode = countryCode; + this.timezone = timezone; + } + + public static create(object: GeocodedObject): Geocoded { + return new this(object); + } + + public toObject(): GeocodedObject { + return { + coordinates: this.coordinates, + bounds: this.bounds, + formattedAddress: this.formattedAddress, + streetNumber: this.streetNumber, + streetName: this.streetName, + subLocality: this.subLocality, + locality: this.locality, + postalCode: this.postalCode, + region: this.region, + adminLevels: this.adminLevels, + country: this.country, + countryCode: this.countryCode, + timezone: this.timezone, + }; + } + + public withBounds(bounds: BoundingBox): Geocoded { + return (this.constructor).create({ + ...this.toObject(), + bounds, + }); + } + + public withCoordinates(coordinates: Coordinates): Geocoded { + return (this.constructor).create({ + ...this.toObject(), + coordinates, + }); + } + + public getCoordinates(): undefined | Coordinates { + return this.coordinates; + } + + public getBounds(): undefined | BoundingBox { + return this.bounds; + } + + public getFormattedAddress(): undefined | string { + return this.formattedAddress; + } + + public getStreetNumber(): undefined | string { + return this.streetNumber; + } + + public getStreetName(): undefined | string { + return this.streetName; + } + + public getSubLocality(): undefined | string { + return this.subLocality; + } + + public getLocality(): undefined | string { + return this.locality; + } + + public getPostalCode(): undefined | string { + return this.postalCode; + } + + public getRegion(): undefined | string { + return this.region; + } + + public addAdminLevel(adminLevel: AdminLevel): void { + this.adminLevels.push(adminLevel); + } + + public getAdminLevels(): AdminLevel[] { + return this.adminLevels; + } + + public getCountry(): undefined | string { + return this.country; + } + + public getCountryCode(): undefined | string { + return this.countryCode; + } + + public getTimezone(): undefined | string { + return this.timezone; + } +} diff --git a/src/GeocoderJS.js b/src/GeocoderJS.js deleted file mode 100644 index 732e177..0000000 --- a/src/GeocoderJS.js +++ /dev/null @@ -1,19 +0,0 @@ -;(function () { - "use strict"; - - var GeocoderJS = {}; - GeocoderJS.version = '0.0.0'; - - GeocoderJS.createGeocoder = function(options) { - var factory = new GeocoderJS.ProviderFactory(); - return factory.createProvider(options); - }; - - var container = (typeof window === "object") ? window : (typeof exports === "object") ? exports : {}; - container.GeocoderJS = GeocoderJS; -})(); - -// Define GeocoderJS as an AMD module. -if (typeof define === 'function' && define.amd) { - define(GeocoderJS); -} diff --git a/src/GeocoderJS.ts b/src/GeocoderJS.ts new file mode 100644 index 0000000..0dccd64 --- /dev/null +++ b/src/GeocoderJS.ts @@ -0,0 +1,14 @@ +import ProviderFactory, { + GeocoderProviderByOptionsType, + GeocoderProviderFactoryOptions, +} from "GeocoderProviderFactory"; + +export default class GeocoderJS { + public version = "0.1.0"; + + public static createGeocoder( + options: string | O + ): GeocoderProviderByOptionsType | undefined { + return ProviderFactory.createProvider(options); + } +} diff --git a/src/GeocoderProviderFactory.js b/src/GeocoderProviderFactory.js deleted file mode 100644 index f91eda3..0000000 --- a/src/GeocoderProviderFactory.js +++ /dev/null @@ -1,52 +0,0 @@ -if (typeof GeocoderJS === "undefined" && typeof require === "function") { - var GeocoderJS = require("../GeocoderJS.js"); - require("../ExternalURILoader.js"); -} - -;(function (GeocoderJS) { - "use strict"; - - GeocoderJS.ProviderFactory = function() {}; - - /** - * Creates new Geocoder Provider instances. - * @options - * Either a string representing the registered provider, or an object with the - * following settings for instigating providers. - * - provider: A string representing the registered provider. - * @return - * An object compatable with the ProviderBase class, or undefined if there's - * not a registered provider. - */ - GeocoderJS.ProviderFactory.prototype.createProvider = function(options) { - if (typeof options === "string") { - options = { - 'provider': options - }; - } - - var provider; - var externalLoader = new GeocoderJS.ExternalURILoader(); - - switch (options.provider) { - case 'google': - provider = new GeocoderJS.GoogleAPIProvider(externalLoader, options); - break; - case 'mapquest': - provider = new GeocoderJS.MapquestProvider(externalLoader, options); - break; - case 'openstreetmap': - provider = new GeocoderJS.OpenStreetMapProvider(externalLoader, options); - break; - case 'bing': - provider = new GeocoderJS.BingProvider(externalLoader, options); - break; - case 'yandex': - provider = new GeocoderJS.YandexProvider(externalLoader, options); - break; - } - - return provider; - }; - -})(GeocoderJS); diff --git a/src/GeocoderProviderFactory.ts b/src/GeocoderProviderFactory.ts new file mode 100644 index 0000000..7b05254 --- /dev/null +++ b/src/GeocoderProviderFactory.ts @@ -0,0 +1,237 @@ +import { + BingProvider, + BingProviderOptionsInterface, + ChainProvider, + ChainProviderOptionsInterface, + GeoPluginProvider, + GoogleMapsProvider, + GoogleMapsProviderOptionsInterface, + MapboxProvider, + MapboxProviderOptionsInterface, + MapQuestProvider, + MapQuestProviderOptionsInterface, + NominatimProvider, + NominatimProviderOptionsInterface, + OpenCageProvider, + OpenCageProviderOptionsInterface, + YandexProvider, + YandexProviderOptionsInterface, + ProviderOptionsInterface, + defaultBingProviderOptions, + defaultChainProviderOptions, + defaultMapboxProviderOptions, + defaultMapQuestProviderOptions, + defaultNominatimProviderOptions, + defaultOpenCageProviderOptions, + defaultYandexProviderOptions, + defaultProviderOptions, +} from "provider"; +import ExternalLoader from "ExternalLoader"; + +interface ProviderOptionInterface { + provider: + | "bing" + | "bingmaps" + | "chain" + | "geoplugin" + | "google" + | "googlemaps" + | "mapbox" + | "mapquest" + | "microsoft" + | "nominatim" + | "opencage" + | "openstreetmap" + | "yandex"; +} + +interface ProviderFactoryOptions + extends ProviderOptionsInterface, + ProviderOptionInterface {} + +interface ChainGeocoderProviderFactoryOptions + extends ProviderOptionInterface, + ChainProviderOptionsInterface { + provider: "chain"; +} + +interface BingGeocoderProviderFactoryOptions + extends ProviderOptionInterface, + BingProviderOptionsInterface { + provider: "bing" | "bingmaps" | "microsoft"; +} + +interface GeoPluginGeocoderProviderFactoryOptions + extends ProviderOptionInterface { + provider: "geoplugin"; +} + +interface GoogleMapsGeocoderProviderFactoryOptions + extends ProviderOptionInterface, + GoogleMapsProviderOptionsInterface { + provider: "google" | "googlemaps"; +} + +interface MapboxGeocoderProviderFactoryOptions + extends ProviderOptionInterface, + MapboxProviderOptionsInterface { + provider: "mapbox"; +} + +interface MapQuestGeocoderProviderFactoryOptions + extends ProviderOptionInterface, + MapQuestProviderOptionsInterface { + provider: "mapquest"; +} + +interface NominatimGeocoderProviderFactoryOptions + extends ProviderOptionInterface, + NominatimProviderOptionsInterface { + provider: "nominatim" | "openstreetmap"; +} + +interface OpenCageGeocoderProviderFactoryOptions + extends ProviderOptionInterface, + OpenCageProviderOptionsInterface { + provider: "opencage"; +} + +interface YandexGeocoderProviderFactoryOptions + extends ProviderOptionInterface, + YandexProviderOptionsInterface { + provider: "yandex"; +} + +export type GeocoderProviderFactoryOptions = + | ProviderFactoryOptions + | ChainGeocoderProviderFactoryOptions + | BingGeocoderProviderFactoryOptions + | GeoPluginGeocoderProviderFactoryOptions + | GoogleMapsGeocoderProviderFactoryOptions + | MapboxGeocoderProviderFactoryOptions + | MapQuestGeocoderProviderFactoryOptions + | NominatimGeocoderProviderFactoryOptions + | OpenCageGeocoderProviderFactoryOptions + | YandexGeocoderProviderFactoryOptions; + +export type GeocoderProvider = + | BingProvider + | ChainProvider + | GeoPluginProvider + | GoogleMapsProvider + | MapboxProvider + | MapQuestProvider + | NominatimProvider + | OpenCageProvider + | YandexProvider; + +export type GeocoderProviderByOptionsType< + O +> = O extends ChainGeocoderProviderFactoryOptions + ? ChainProvider + : O extends BingGeocoderProviderFactoryOptions + ? BingProvider + : O extends GeoPluginGeocoderProviderFactoryOptions + ? GeoPluginProvider + : O extends GoogleMapsGeocoderProviderFactoryOptions + ? GoogleMapsProvider + : O extends MapboxGeocoderProviderFactoryOptions + ? MapboxProvider + : O extends MapQuestGeocoderProviderFactoryOptions + ? MapQuestProvider + : O extends NominatimGeocoderProviderFactoryOptions + ? NominatimProvider + : O extends OpenCageGeocoderProviderFactoryOptions + ? OpenCageProvider + : O extends YandexGeocoderProviderFactoryOptions + ? YandexProvider + : GeocoderProvider; + +export default class ProviderFactory { + /** + * Creates Geocoder Provider instances. + * @param options + * Either a string representing the registered provider, or an object with the + * following settings for instigating providers: + * - provider: A string representing the registered provider. + * @return + * An object compatible with ProviderInterface, or undefined if there's not a + * registered provider. + */ + public static createProvider( + options: string | O + ): GeocoderProviderByOptionsType | undefined { + const createProviderOptions = { + ...defaultProviderOptions, + ...(typeof options === "string" ? { provider: options } : options), + }; + + const externalLoader = new ExternalLoader(); + + const { provider, ...providerOptions } = createProviderOptions; + switch (provider) { + case "bing": + case "bingmaps": + case "microsoft": + return >( + new BingProvider(externalLoader, { + ...defaultBingProviderOptions, + ...providerOptions, + }) + ); + case "chain": + return >new ChainProvider({ + ...defaultChainProviderOptions, + ...providerOptions, + }); + case "geoplugin": + return >( + new GeoPluginProvider(externalLoader, providerOptions) + ); + case "google": + case "googlemaps": + return >( + new GoogleMapsProvider(externalLoader, providerOptions) + ); + case "mapbox": + return >( + new MapboxProvider(externalLoader, { + ...defaultMapboxProviderOptions, + ...providerOptions, + }) + ); + case "mapquest": + return >( + new MapQuestProvider(externalLoader, { + ...defaultMapQuestProviderOptions, + ...providerOptions, + }) + ); + case "openstreetmap": + case "nominatim": + return >( + new NominatimProvider(externalLoader, { + ...defaultNominatimProviderOptions, + ...providerOptions, + }) + ); + case "opencage": + return >( + new OpenCageProvider(externalLoader, { + ...defaultOpenCageProviderOptions, + ...providerOptions, + }) + ); + case "yandex": + return >( + new YandexProvider(externalLoader, { + ...defaultYandexProviderOptions, + ...providerOptions, + }) + ); + default: + } + + return undefined; + } +} diff --git a/src/error/ResponseError.ts b/src/error/ResponseError.ts new file mode 100644 index 0000000..a469ace --- /dev/null +++ b/src/error/ResponseError.ts @@ -0,0 +1,55 @@ +import { + BingResponse, + GeoPluginResult, + GoogleMapsResponse, + MapboxResponse, + MapQuestResponse, + NominatimResponse, + OpenCageResponse, +} from "provider"; + +export default class ResponseError extends Error { + public __proto__: ResponseError; + + private readonly response: + | Response + | BingResponse + | GeoPluginResult + | GoogleMapsResponse + | MapboxResponse + | MapQuestResponse + | NominatimResponse + | OpenCageResponse; + + public constructor( + message: string, + response: + | Response + | BingResponse + | GeoPluginResult + | GoogleMapsResponse + | MapboxResponse + | MapQuestResponse + | NominatimResponse + | OpenCageResponse + ) { + super(message); + this.name = "ResponseError"; + this.response = response; + + // eslint-disable-next-line no-proto + this.__proto__ = ResponseError.prototype; + } + + public getResponse(): + | Response + | BingResponse + | GeoPluginResult + | GoogleMapsResponse + | MapboxResponse + | MapQuestResponse + | NominatimResponse + | OpenCageResponse { + return this.response; + } +} diff --git a/src/error/index.ts b/src/error/index.ts new file mode 100644 index 0000000..f3dc9db --- /dev/null +++ b/src/error/index.ts @@ -0,0 +1,2 @@ +// eslint-disable-next-line import/prefer-default-export +export { default as ResponseError } from "error/ResponseError"; diff --git a/src/global.ts b/src/global.ts new file mode 100644 index 0000000..3fbee6a --- /dev/null +++ b/src/global.ts @@ -0,0 +1,20 @@ +import GeocoderJS from "GeocoderJS"; +import GeoJsonDumper from "GeoJsonDumper"; + +interface Container { + GeocoderJS: typeof GeocoderJS; + GeoJsonDumper: typeof GeoJsonDumper; +} + +declare global { + interface Window { + GeocoderJS: typeof GeocoderJS; + GeoJsonDumper: typeof GeoJsonDumper; + } +} + +const container: Window | Container = + typeof window === "object" ? window : ({} as Container); + +container.GeocoderJS = GeocoderJS; +container.GeoJsonDumper = GeoJsonDumper; diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..8a77ed0 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,14 @@ +export * from "provider"; +export * from "query"; +export * from "utils"; +export { default as AdminLevel } from "AdminLevel"; +export * from "AdminLevel"; +export { default as ExternalLoader } from "ExternalLoader"; +export * from "ExternalLoader"; +export { default as Geocoded } from "Geocoded"; +export * from "Geocoded"; +export { default as GeocoderProviderFactory } from "GeocoderProviderFactory"; +export * from "GeocoderProviderFactory"; +export { default as GeoJsonDumper } from "GeoJsonDumper"; +export * from "GeoJsonDumper"; +export { default } from "GeocoderJS"; diff --git a/src/provider/ChainProvider.ts b/src/provider/ChainProvider.ts new file mode 100644 index 0000000..43b8dae --- /dev/null +++ b/src/provider/ChainProvider.ts @@ -0,0 +1,300 @@ +import { + ExternalLoaderBody, + ExternalLoaderHeaders, + ExternalLoaderParams, +} from "ExternalLoader"; +import Geocoded from "Geocoded"; +import { + ErrorCallback, + GeocodedResultsCallback, + ProviderHelpers, + ProviderInterface, + ProviderOptionsInterface, + defaultProviderOptions, +} from "provider"; +import { + GeocodeQuery, + GeocodeQueryObject, + ReverseQuery, + ReverseQueryObject, +} from "query"; + +export interface ChainProviderOptionsInterface + extends ProviderOptionsInterface { + readonly providers: ProviderInterface[]; + readonly parallelize?: boolean; + readonly first?: boolean; +} + +export const defaultChainProviderOptions = { + ...defaultProviderOptions, + providers: [], +}; + +type ChainGeocodedResultsCallback = GeocodedResultsCallback; + +export default class ChainProvider implements ProviderInterface { + private options: ChainProviderOptionsInterface; + + public constructor( + options: ChainProviderOptionsInterface = defaultChainProviderOptions + ) { + this.options = options; + } + + public geocode( + query: string | GeocodeQuery | GeocodeQueryObject, + callback: ChainGeocodedResultsCallback, + errorCallback?: ErrorCallback + ): void { + if (this.options.parallelize || this.options.first) { + this.geocodeAllProviders(query, callback, errorCallback); + return; + } + + this.geocodeNextProvider( + this.options.providers, + query, + callback, + errorCallback + ); + } + + public geodecode( + latitudeOrQuery: number | string | ReverseQuery | ReverseQueryObject, + longitudeOrCallback: number | string | ChainGeocodedResultsCallback, + callbackOrErrorCallback?: ChainGeocodedResultsCallback | ErrorCallback, + errorCallback?: ErrorCallback + ): void { + const reverseQuery = ProviderHelpers.getReverseQueryFromParameters( + latitudeOrQuery, + longitudeOrCallback + ); + const reverseCallback = ProviderHelpers.getCallbackFromParameters( + longitudeOrCallback, + callbackOrErrorCallback + ); + const reverseErrorCallback = ProviderHelpers.getErrorCallbackFromParameters( + longitudeOrCallback, + callbackOrErrorCallback, + errorCallback + ); + + if (this.options.parallelize || this.options.first) { + this.geodecodeAllProviders( + reverseQuery, + reverseCallback, + reverseErrorCallback + ); + return; + } + + this.geodecodeNextProvider( + this.options.providers, + reverseQuery, + reverseCallback, + reverseErrorCallback + ); + } + + private geocodeNextProvider( + providers: ProviderInterface[], + query: string | GeocodeQuery | GeocodeQueryObject, + callback: ChainGeocodedResultsCallback, + errorCallback?: ErrorCallback + ): void { + const [provider, ...nextProviders] = providers; + const resultCallback: ChainGeocodedResultsCallback = (results) => { + if (results.length > 0) { + callback(results); + return; + } + this.geocodeNextProvider(nextProviders, query, callback, errorCallback); + }; + const resultErrorCallback: ErrorCallback = (responseError) => { + if (errorCallback) { + errorCallback(responseError); + } + if (!errorCallback) { + // eslint-disable-next-line no-console + console.error( + `An error has occurred when geocoding with the provider ${provider.constructor.name}`, + responseError + ); + } + resultCallback([]); + }; + + provider.geocode(query, resultCallback, resultErrorCallback); + } + + private geodecodeNextProvider( + providers: ProviderInterface[], + reverseQuery: ReverseQuery, + callback: ChainGeocodedResultsCallback, + errorCallback?: ErrorCallback + ): void { + const [provider, ...nextProviders] = providers; + const resultCallback: ChainGeocodedResultsCallback = (results) => { + if (results.length > 0) { + callback(results); + return; + } + this.geodecodeNextProvider( + nextProviders, + reverseQuery, + callback, + errorCallback + ); + }; + const resultErrorCallback: ErrorCallback = (responseError) => { + if (errorCallback) { + errorCallback(responseError); + } + if (!errorCallback) { + // eslint-disable-next-line no-console + console.error( + `An error has occurred when geodecoding with the provider ${provider.constructor.name}`, + responseError + ); + } + resultCallback([]); + }; + + provider.geodecode(reverseQuery, resultCallback, resultErrorCallback); + } + + private geocodeAllProviders( + query: string | GeocodeQuery | GeocodeQueryObject, + callback: ChainGeocodedResultsCallback, + errorCallback?: ErrorCallback + ): void { + const providerResults: Map = new Map(); + let callbackCalled = false; + const getProviderResult = () => + this.options.providers.reduce((result, provider) => { + let providerResult = result; + if (undefined === providerResult && this.options.first) { + providerResult = []; + } + if (undefined === providerResult) { + return undefined; + } + if (providerResult.length > 0) { + return providerResult; + } + + return providerResults.get(provider.constructor.name); + }, []); + const resultProviderCallback: ( + providerName: string + ) => ChainGeocodedResultsCallback = (providerName) => (results) => { + providerResults.set(providerName, results); + const providerResult = getProviderResult(); + if (!callbackCalled && providerResult) { + callback(providerResult); + callbackCalled = true; + } + }; + const resultProviderErrorCallback: ( + providerName: string + ) => ErrorCallback = (providerName) => (responseError) => { + if (errorCallback) { + errorCallback(responseError); + } + if (!errorCallback) { + // eslint-disable-next-line no-console + console.error( + `An error has occurred when geocoding with the provider ${providerName}`, + responseError + ); + } + resultProviderCallback(providerName)([]); + }; + + this.options.providers.forEach((provider) => { + const providerName = provider.constructor.name; + provider.geocode( + query, + resultProviderCallback(providerName), + resultProviderErrorCallback(providerName) + ); + }); + } + + private geodecodeAllProviders( + reverseQuery: ReverseQuery, + callback: ChainGeocodedResultsCallback, + errorCallback?: ErrorCallback + ): void { + const providerResults: Map = new Map(); + let callbackCalled = false; + const getProviderResult = () => + this.options.providers.reduce((result, provider) => { + let providerResult = result; + if (undefined === providerResult && this.options.first) { + providerResult = []; + } + if (undefined === providerResult) { + return undefined; + } + if (providerResult.length > 0) { + return providerResult; + } + + return providerResults.get(provider.constructor.name); + }, []); + const resultProviderCallback: ( + providerName: string + ) => ChainGeocodedResultsCallback = (providerName) => (results) => { + providerResults.set(providerName, results); + const providerResult = getProviderResult(); + if (!callbackCalled && providerResult) { + callback(providerResult); + callbackCalled = true; + } + }; + const resultProviderErrorCallback: ( + providerName: string + ) => ErrorCallback = (providerName) => (responseError) => { + if (errorCallback) { + errorCallback(responseError); + } + if (!errorCallback) { + // eslint-disable-next-line no-console + console.error( + `An error has occurred when geodecoding with the provider ${providerName}`, + responseError + ); + } + resultProviderCallback(providerName)([]); + }; + + this.options.providers.forEach((provider) => { + const providerName = provider.constructor.name; + provider.geodecode( + reverseQuery, + resultProviderCallback(providerName), + resultProviderErrorCallback(providerName) + ); + }); + } + + // eslint-disable-next-line class-methods-use-this + public executeRequest( + // eslint-disable-next-line @typescript-eslint/no-unused-vars + params: ExternalLoaderParams, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + callback: ChainGeocodedResultsCallback, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + headers?: ExternalLoaderHeaders, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + body?: ExternalLoaderBody, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + errorCallback?: ErrorCallback + ): void { + throw new Error( + "executeRequest cannot be called directly from the chain provider." + ); + } +} diff --git a/src/provider/ProviderHelpers.ts b/src/provider/ProviderHelpers.ts new file mode 100644 index 0000000..29ff2d5 --- /dev/null +++ b/src/provider/ProviderHelpers.ts @@ -0,0 +1,101 @@ +import { ErrorCallback, GeocodedResultsCallback } from "provider"; +import Geocoded from "Geocoded"; +import { + GeocodeQuery, + GeocodeQueryObject, + ReverseQuery, + ReverseQueryObject, +} from "query"; +import { isIpv4, isIpv6 } from "utils"; + +export default class ProviderHelpers { + public static getGeocodeQueryFromParameter( + query: string | GeocodeQuery | GeocodeQueryObject, + geocodeQuery = GeocodeQuery + ): GeocodeQuery { + if (typeof query === "string") { + if (isIpv4(query) || isIpv6(query)) { + return geocodeQuery.create({ ip: query }); + } + return geocodeQuery.create({ text: query }); + } + if (!(query instanceof geocodeQuery)) { + return geocodeQuery.create(query); + } + + return query; + } + + public static getReverseQueryFromParameters( + latitudeOrQuery: number | string | ReverseQuery | ReverseQueryObject, + longitudeOrCallback: number | string | GeocodedResultsCallback, + reverseQuery = ReverseQuery + ): ReverseQuery { + if ( + typeof latitudeOrQuery === "number" || + typeof latitudeOrQuery === "string" + ) { + if ( + !( + typeof longitudeOrCallback === "number" || + typeof longitudeOrCallback === "string" + ) + ) { + throw new Error( + "The second parameter of geodecode must be a longitude if the first one is a latitude" + ); + } + return reverseQuery.create({ + coordinates: { + latitude: latitudeOrQuery, + longitude: longitudeOrCallback, + }, + }); + } + if (!(latitudeOrQuery instanceof reverseQuery)) { + return reverseQuery.create(latitudeOrQuery); + } + + return latitudeOrQuery; + } + + public static getCallbackFromParameters( + longitudeOrCallback: number | string | GeocodedResultsCallback, + callbackOrErrorCallback?: GeocodedResultsCallback | ErrorCallback + ): GeocodedResultsCallback { + if ( + !( + typeof longitudeOrCallback === "number" || + typeof longitudeOrCallback === "string" + ) + ) { + return longitudeOrCallback; + } + if (callbackOrErrorCallback) { + return >callbackOrErrorCallback; + } + + throw new Error( + "A callback must be set at the last parameter of geodecode" + ); + } + + public static getErrorCallbackFromParameters( + longitudeOrCallback: number | string | GeocodedResultsCallback, + callbackOrErrorCallback?: GeocodedResultsCallback | ErrorCallback, + errorCallback?: ErrorCallback + ): undefined | ErrorCallback { + if (errorCallback) { + return errorCallback; + } + + if ( + typeof longitudeOrCallback === "number" || + typeof longitudeOrCallback === "string" + ) { + return undefined; + } + + return callbackOrErrorCallback; + } +} diff --git a/src/provider/ProviderInterface.ts b/src/provider/ProviderInterface.ts new file mode 100644 index 0000000..02b75cd --- /dev/null +++ b/src/provider/ProviderInterface.ts @@ -0,0 +1,52 @@ +import { + ExternalLoaderBody, + ExternalLoaderHeaders, + ExternalLoaderParams, +} from "ExternalLoader"; +import Geocoded from "Geocoded"; +import { + GeocodeQuery, + GeocodeQueryObject, + ReverseQuery, + ReverseQueryObject, +} from "query"; +import { ResponseError } from "error"; + +export const DEFAULT_RESULT_LIMIT = 5; + +export interface ProviderOptionsInterface { + readonly useSsl?: boolean; + readonly useJsonp?: boolean; + readonly apiKey?: string; +} + +export const defaultProviderOptions: ProviderOptionsInterface = { + useSsl: false, + useJsonp: false, +}; + +export type GeocodedResultsCallback = ( + results: G[] +) => void; +export type ErrorCallback = (responseError: ResponseError) => void; + +export default interface ProviderInterface { + geocode( + query: string | GeocodeQuery | GeocodeQueryObject, + callback: GeocodedResultsCallback, + errorCallback?: ErrorCallback + ): void; + geodecode( + latitudeOrQuery: number | string | ReverseQuery | ReverseQueryObject, + longitudeOrCallback: number | string | GeocodedResultsCallback, + callbackOrErrorCallback?: GeocodedResultsCallback | ErrorCallback, + errorCallback?: ErrorCallback + ): void; + executeRequest( + params: ExternalLoaderParams, + callback: GeocodedResultsCallback, + headers?: ExternalLoaderHeaders, + body?: ExternalLoaderBody, + errorCallback?: ErrorCallback + ): void; +} diff --git a/src/provider/bing/BingGeocoded.ts b/src/provider/bing/BingGeocoded.ts new file mode 100644 index 0000000..c0276b3 --- /dev/null +++ b/src/provider/bing/BingGeocoded.ts @@ -0,0 +1,57 @@ +import Geocoded, { GeocodedObject } from "Geocoded"; +import { BingPrecision } from "provider"; + +export interface BingGeocodedObject extends GeocodedObject { + readonly attribution?: string; + readonly precision?: BingPrecision; +} + +export default class BingGeocoded extends Geocoded { + private readonly attribution?: string; + + private readonly precision?: BingPrecision; + + protected constructor({ + attribution, + precision, + ...geocodedObject + }: BingGeocodedObject) { + super(geocodedObject); + this.attribution = attribution; + this.precision = precision; + } + + public static create(object: BingGeocodedObject): BingGeocoded { + return new this(object); + } + + public toObject(): BingGeocodedObject { + return { + ...super.toObject(), + attribution: this.attribution, + precision: this.precision, + }; + } + + public withAttribution(attribution: string): BingGeocoded { + return new BingGeocoded({ + ...this.toObject(), + attribution, + }); + } + + public getAttribution(): undefined | string { + return this.attribution; + } + + public withPrecision(precision: BingPrecision): BingGeocoded { + return new BingGeocoded({ + ...this.toObject(), + precision, + }); + } + + public getPrecision(): undefined | BingPrecision { + return this.precision; + } +} diff --git a/src/provider/bing/BingProvider.ts b/src/provider/bing/BingProvider.ts new file mode 100644 index 0000000..e5411b1 --- /dev/null +++ b/src/provider/bing/BingProvider.ts @@ -0,0 +1,301 @@ +import { + ExternalLoaderBody, + ExternalLoaderHeaders, + ExternalLoaderInterface, + ExternalLoaderParams, +} from "ExternalLoader"; +import { + BingGeocoded, + ErrorCallback, + GeocodedResultsCallback, + ProviderHelpers, + ProviderInterface, + ProviderOptionsInterface, + defaultProviderOptions, +} from "provider"; +import { + GeocodeQuery, + GeocodeQueryObject, + ReverseQuery, + ReverseQueryObject, +} from "query"; +import { FlatBoundingBox, FlatCoordinates } from "types"; +import AdminLevel from "AdminLevel"; +import { ResponseError } from "error"; + +interface BingRequestParams { + [param: string]: string | undefined; + readonly key?: string; + readonly inclnb?: string; + readonly incl?: string; + readonly maxRes?: string; + readonly includeEntityTypes?: string; + readonly c?: string; + readonly jsonpCallback?: string; +} + +export type BingPrecision = "High" | "Medium" | "Low"; + +export interface BingResult { + __type: string; + bbox: FlatBoundingBox; + name: string; + point: { + type: string; + coordinates: FlatCoordinates; + }; + address: { + addressLine: string; + neighborhood?: string; + adminDistrict: string; + adminDistrict2: string; + countryRegion: string; + countryRegionIso2?: string; + landmark?: string; + formattedAddress: string; + locality: string; + postalCode: string; + }; + confidence: BingPrecision; + entityType: string; + geocodePoints: { + type: string; + coordinates: FlatCoordinates; + calculationMethod: + | "Interpolation" + | "InterpolationOffset" + | "Parcel" + | "Rooftop"; + usageTypes: ("Display" | "Route")[]; + }[]; + queryParseValues?: { + property: + | " AddressLine" + | "Locality" + | "AdminDistrict" + | "AdminDistrict2" + | "PostalCode" + | "CountryRegion" + | "Landmark"; + value: string; + }; + matchCodes: ("Good" | "Ambiguous" | "UpHierarchy")[]; +} + +export interface BingResponse { + statusCode: number; + statusDescription: string | null; + authenticationResultCode: + | "ValidCredentials" + | "InvalidCredentials" + | "CredentialsExpired" + | "NotAuthorized" + | "NoCredentials" + | "None"; + traceId: string; + copyright: string; + brandLogoUri: string; + resourceSets: { + estimatedTotal: number; + resources: BingResult[]; + }[]; + errorDetails?: string[]; +} + +export interface BingProviderOptionsInterface extends ProviderOptionsInterface { + readonly apiKey: string; +} + +export const defaultBingProviderOptions = { + ...defaultProviderOptions, + apiKey: "", +}; + +type BingGeocodedResultsCallback = GeocodedResultsCallback; + +export default class BingProvider implements ProviderInterface { + private externalLoader: ExternalLoaderInterface; + + private options: BingProviderOptionsInterface; + + public constructor( + _externalLoader: ExternalLoaderInterface, + options: BingProviderOptionsInterface = defaultBingProviderOptions + ) { + this.externalLoader = _externalLoader; + this.options = { ...defaultBingProviderOptions, ...options }; + if (!this.options.apiKey) { + throw new Error( + 'An API key is required for the Bing provider. Please add it in the "apiKey" option.' + ); + } + } + + public geocode( + query: string | GeocodeQuery | GeocodeQueryObject, + callback: BingGeocodedResultsCallback, + errorCallback?: ErrorCallback + ): void { + const geocodeQuery = ProviderHelpers.getGeocodeQueryFromParameter(query); + + if (geocodeQuery.getIp()) { + throw new Error( + "The Bing provider does not support IP geolocation, only location geocoding." + ); + } + + this.externalLoader.setOptions({ + protocol: this.options.useSsl ? "https" : "http", + host: "dev.virtualearth.net", + pathname: `REST/v1/Locations/${geocodeQuery.getText()}`, + }); + + const params: BingRequestParams = this.withCommonParams( + { + maxRes: geocodeQuery.getLimit() + ? geocodeQuery.getLimit().toString() + : undefined, + }, + geocodeQuery + ); + + this.executeRequest(params, callback, {}, {}, errorCallback); + } + + public geodecode( + latitudeOrQuery: number | string | ReverseQuery | ReverseQueryObject, + longitudeOrCallback: number | string | BingGeocodedResultsCallback, + callbackOrErrorCallback?: BingGeocodedResultsCallback | ErrorCallback, + errorCallback?: ErrorCallback + ): void { + const reverseQuery = ProviderHelpers.getReverseQueryFromParameters( + latitudeOrQuery, + longitudeOrCallback + ); + const reverseCallback = ProviderHelpers.getCallbackFromParameters( + longitudeOrCallback, + callbackOrErrorCallback + ); + const reverseErrorCallback = ProviderHelpers.getErrorCallbackFromParameters( + longitudeOrCallback, + callbackOrErrorCallback, + errorCallback + ); + + this.externalLoader.setOptions({ + protocol: this.options.useSsl ? "https" : "http", + host: "dev.virtualearth.net", + pathname: `REST/v1/Locations/${reverseQuery.getCoordinates().latitude},${ + reverseQuery.getCoordinates().longitude + }`, + }); + + const params: BingRequestParams = this.withCommonParams({}, reverseQuery); + + this.executeRequest(params, reverseCallback, {}, {}, reverseErrorCallback); + } + + private withCommonParams( + params: Pick, + query: GeocodeQuery | ReverseQuery + ): BingRequestParams { + return { + ...params, + key: this.options.apiKey, + incl: "ciso2", + c: query.getLocale(), + jsonpCallback: this.options.useJsonp ? "jsonp" : undefined, + }; + } + + public executeRequest( + params: ExternalLoaderParams, + callback: BingGeocodedResultsCallback, + headers?: ExternalLoaderHeaders, + body?: ExternalLoaderBody, + errorCallback?: ErrorCallback + ): void { + this.externalLoader.executeRequest( + params, + (data: BingResponse) => { + callback( + data.resourceSets[0].resources.map((result: BingResult) => + BingProvider.mapToGeocoded(result, data.copyright) + ) + ); + }, + headers, + body, + (error) => { + const response = error.getResponse(); + response.json().then((data: BingResponse) => { + const errorMessage = + data.errorDetails && data.errorDetails.length > 0 + ? data.errorDetails[0] + : data.statusDescription || ""; + if (errorCallback) { + errorCallback(new ResponseError(errorMessage, data)); + return; + } + setTimeout(() => { + throw new Error(errorMessage); + }); + }); + } + ); + } + + public static mapToGeocoded( + result: BingResult, + attribution?: string + ): BingGeocoded { + const latitude = result.point.coordinates[0]; + const longitude = result.point.coordinates[1]; + const { formattedAddress } = result.address; + const streetName = result.address.addressLine; + const { locality, postalCode } = result.address; + const region = result.address.adminDistrict; + const country = result.address.countryRegion; + const countryCode = result.address.countryRegionIso2; + const precision = result.confidence; + + let geocoded = BingGeocoded.create({ + coordinates: { + latitude, + longitude, + }, + formattedAddress, + streetName, + locality, + postalCode, + region, + country, + countryCode, + attribution, + precision, + }); + geocoded = geocoded.withBounds({ + latitudeSW: result.bbox[0], + longitudeSW: result.bbox[1], + latitudeNE: result.bbox[2], + longitudeNE: result.bbox[3], + }); + + const adminLevels: ("adminDistrict" | "adminDistrict2")[] = [ + "adminDistrict", + "adminDistrict2", + ]; + adminLevels.forEach((adminLevel, level) => { + if (result.address[adminLevel]) { + geocoded.addAdminLevel( + AdminLevel.create({ + level: level + 1, + name: result.address[adminLevel] || "", + }) + ); + } + }); + + return geocoded; + } +} diff --git a/src/provider/bing/index.ts b/src/provider/bing/index.ts new file mode 100644 index 0000000..a6a34cc --- /dev/null +++ b/src/provider/bing/index.ts @@ -0,0 +1,3 @@ +export { default as BingGeocoded } from "provider/bing/BingGeocoded"; +export { default as BingProvider } from "provider/bing/BingProvider"; +export * from "provider/bing/BingProvider"; diff --git a/src/provider/geoplugin/GeoPluginGeocoded.ts b/src/provider/geoplugin/GeoPluginGeocoded.ts new file mode 100644 index 0000000..103fa9f --- /dev/null +++ b/src/provider/geoplugin/GeoPluginGeocoded.ts @@ -0,0 +1,39 @@ +import Geocoded, { GeocodedObject } from "Geocoded"; + +export interface GeoPluginGeocodedObject extends GeocodedObject { + readonly attribution?: string; +} + +export default class GeoPluginGeocoded extends Geocoded { + private readonly attribution?: string; + + protected constructor({ + attribution, + ...geocodedObject + }: GeoPluginGeocodedObject) { + super(geocodedObject); + this.attribution = attribution; + } + + public static create(object: GeoPluginGeocodedObject): GeoPluginGeocoded { + return new this(object); + } + + public toObject(): GeoPluginGeocodedObject { + return { + ...super.toObject(), + attribution: this.attribution, + }; + } + + public withAttribution(attribution: string): GeoPluginGeocoded { + return new GeoPluginGeocoded({ + ...this.toObject(), + attribution, + }); + } + + public getAttribution(): undefined | string { + return this.attribution; + } +} diff --git a/src/provider/geoplugin/GeoPluginProvider.ts b/src/provider/geoplugin/GeoPluginProvider.ts new file mode 100644 index 0000000..3322231 --- /dev/null +++ b/src/provider/geoplugin/GeoPluginProvider.ts @@ -0,0 +1,217 @@ +import { + ExternalLoaderBody, + ExternalLoaderHeaders, + ExternalLoaderInterface, + ExternalLoaderParams, +} from "ExternalLoader"; +import { + ErrorCallback, + GeocodedResultsCallback, + GeoPluginGeocoded, + ProviderHelpers, + ProviderInterface, + ProviderOptionsInterface, + defaultProviderOptions, +} from "provider"; +import { + GeocodeQuery, + GeocodeQueryObject, + ReverseQuery, + ReverseQueryObject, +} from "query"; +import { ResponseError } from "error"; +import AdminLevel, { ADMIN_LEVEL_CODES } from "AdminLevel"; + +interface GeoPluginRequestParams { + [param: string]: string | undefined; + readonly ip: string; +} + +export interface GeoPluginResult { + // eslint-disable-next-line camelcase + geoplugin_request: string; + // eslint-disable-next-line camelcase + geoplugin_status: number; + // eslint-disable-next-line camelcase + geoplugin_delay: string; + // eslint-disable-next-line camelcase + geoplugin_credit: string; + // eslint-disable-next-line camelcase + geoplugin_city: string; + // eslint-disable-next-line camelcase + geoplugin_region: string; + // eslint-disable-next-line camelcase + geoplugin_regionCode: string; + // eslint-disable-next-line camelcase + geoplugin_regionName: string; + // eslint-disable-next-line camelcase + geoplugin_areaCode: string; + // eslint-disable-next-line camelcase + geoplugin_dmaCode: string; + // eslint-disable-next-line camelcase + geoplugin_countryCode: string; + // eslint-disable-next-line camelcase + geoplugin_countryName: string; + // eslint-disable-next-line camelcase + geoplugin_inEU: boolean; + // eslint-disable-next-line camelcase + geoplugin_euVATrate: number; + // eslint-disable-next-line camelcase + geoplugin_continentCode: string; + // eslint-disable-next-line camelcase + geoplugin_continentName: string; + // eslint-disable-next-line camelcase + geoplugin_latitude: string; + // eslint-disable-next-line camelcase + geoplugin_longitude: string; + // eslint-disable-next-line camelcase + geoplugin_locationAccuracyRadius: string; + // eslint-disable-next-line camelcase + geoplugin_timezone: string; + // eslint-disable-next-line camelcase + geoplugin_currencyCode: string; + // eslint-disable-next-line camelcase + geoplugin_currencySymbol: string; + // eslint-disable-next-line camelcase + geoplugin_currencySymbol_UTF8: string; + // eslint-disable-next-line camelcase + geoplugin_currencyConverter: string; +} + +type GeoPluginGeocodedResultsCallback = GeocodedResultsCallback< + GeoPluginGeocoded +>; + +export default class GeoPluginProvider + implements ProviderInterface { + private externalLoader: ExternalLoaderInterface; + + private options: ProviderOptionsInterface; + + public constructor( + _externalLoader: ExternalLoaderInterface, + options: ProviderOptionsInterface = defaultProviderOptions + ) { + this.externalLoader = _externalLoader; + this.options = { ...defaultProviderOptions, ...options }; + } + + public geocode( + query: string | GeocodeQuery | GeocodeQueryObject, + callback: GeoPluginGeocodedResultsCallback, + errorCallback?: ErrorCallback + ): void { + const geocodeQuery = ProviderHelpers.getGeocodeQueryFromParameter(query); + + if (geocodeQuery.getText()) { + throw new Error( + "The GeoPlugin provider does not support location geocoding, only IP geolocation." + ); + } + + if (["127.0.0.1", "::1"].includes(geocodeQuery.getIp() || "")) { + callback([ + GeoPluginGeocoded.create({ + locality: "localhost", + country: "localhost", + }), + ]); + return; + } + + this.externalLoader.setOptions({ + protocol: this.options.useSsl ? "https" : "http", + host: "www.geoplugin.net", + pathname: "json.gp", + }); + + const params: GeoPluginRequestParams = { + ip: geocodeQuery.getIp() || "", + }; + + this.executeRequest(params, callback, {}, {}, errorCallback); + } + + // eslint-disable-next-line class-methods-use-this + public geodecode( + // eslint-disable-next-line @typescript-eslint/no-unused-vars + latitudeOrQuery: number | string | ReverseQuery | ReverseQueryObject, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + longitudeOrCallback: number | string | GeoPluginGeocodedResultsCallback, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + callbackOrErrorCallback?: GeoPluginGeocodedResultsCallback | ErrorCallback, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + errorCallback?: ErrorCallback + ): void { + throw new Error( + "The GeoPlugin provider does not support reverse geocoding." + ); + } + + public executeRequest( + params: ExternalLoaderParams, + callback: GeoPluginGeocodedResultsCallback, + headers?: ExternalLoaderHeaders, + body?: ExternalLoaderBody, + errorCallback?: ErrorCallback + ): void { + this.externalLoader.executeRequest( + params, + (data: GeoPluginResult) => { + if (![200, 206].includes(data.geoplugin_status)) { + const errorMessage = `An error has occurred. Status: ${data.geoplugin_status}.`; + if (errorCallback) { + errorCallback(new ResponseError(errorMessage, data)); + return; + } + setTimeout(() => { + throw new Error(errorMessage); + }); + return; + } + callback([GeoPluginProvider.mapToGeocoded(data)]); + }, + headers, + body, + errorCallback + ); + } + + public static mapToGeocoded(result: GeoPluginResult): GeoPluginGeocoded { + const latitude = parseFloat(result.geoplugin_latitude); + const longitude = parseFloat(result.geoplugin_longitude); + const locality = result.geoplugin_city || undefined; + const region = result.geoplugin_region || undefined; + const country = result.geoplugin_countryName || undefined; + const countryCode = result.geoplugin_countryCode || undefined; + const timezone = result.geoplugin_timezone || undefined; + const adminLevels: AdminLevel[] = []; + const attribution = result.geoplugin_credit || undefined; + + if (result.geoplugin_regionName) { + adminLevels.push( + AdminLevel.create({ + level: ADMIN_LEVEL_CODES.STATE_CODE, + name: result.geoplugin_regionName, + code: result.geoplugin_regionCode || undefined, + }) + ); + } + + const geocoded = GeoPluginGeocoded.create({ + coordinates: { + latitude, + longitude, + }, + locality, + region, + adminLevels, + country, + countryCode, + timezone, + attribution, + }); + + return geocoded; + } +} diff --git a/src/provider/geoplugin/index.ts b/src/provider/geoplugin/index.ts new file mode 100644 index 0000000..1144901 --- /dev/null +++ b/src/provider/geoplugin/index.ts @@ -0,0 +1,3 @@ +export { default as GeoPluginGeocoded } from "provider/geoplugin/GeoPluginGeocoded"; +export { default as GeoPluginProvider } from "provider/geoplugin/GeoPluginProvider"; +export * from "provider/geoplugin/GeoPluginProvider"; diff --git a/src/provider/googlemaps/GoogleMapsGeocodeQuery.ts b/src/provider/googlemaps/GoogleMapsGeocodeQuery.ts new file mode 100644 index 0000000..4438f48 --- /dev/null +++ b/src/provider/googlemaps/GoogleMapsGeocodeQuery.ts @@ -0,0 +1,78 @@ +import { GeocodeQuery, GeocodeQueryObject } from "query"; + +interface GoogleMapsComponent { + name: string; + value: string; +} + +export interface GoogleMapsGeocodeQueryObject extends GeocodeQueryObject { + readonly countryCodes?: string[]; + readonly components?: GoogleMapsComponent[]; + readonly channel?: string; +} + +export default class GoogleMapsGeocodeQuery extends GeocodeQuery { + private readonly countryCodes?: string[]; + + private readonly components?: GoogleMapsComponent[]; + + private readonly channel?: string; + + protected constructor({ + countryCodes, + components, + channel, + ...geocodeQueryObject + }: GoogleMapsGeocodeQueryObject) { + super(geocodeQueryObject); + if (countryCodes && countryCodes.length !== 1) { + throw new Error( + 'The "countryCodes" parameter must have only one country code top-level domain.' + ); + } + this.countryCodes = countryCodes; + this.components = components; + this.channel = channel; + } + + public static create( + object: GoogleMapsGeocodeQueryObject + ): GoogleMapsGeocodeQuery { + return new this(object); + } + + public toObject(): GoogleMapsGeocodeQueryObject { + return { + ...super.toObject(), + countryCodes: this.countryCodes, + components: this.components, + channel: this.channel, + }; + } + + public withCountryCodes(countryCodes: string[]): GoogleMapsGeocodeQuery { + return new GoogleMapsGeocodeQuery({ ...this.toObject(), countryCodes }); + } + + public getCountryCodes(): undefined | string[] { + return this.countryCodes; + } + + public withComponents( + components: GoogleMapsComponent[] + ): GoogleMapsGeocodeQuery { + return new GoogleMapsGeocodeQuery({ ...this.toObject(), components }); + } + + public getComponents(): undefined | GoogleMapsComponent[] { + return this.components; + } + + public withChannel(channel: string): GoogleMapsGeocodeQuery { + return new GoogleMapsGeocodeQuery({ ...this.toObject(), channel }); + } + + public getChannel(): undefined | string { + return this.channel; + } +} diff --git a/src/provider/googlemaps/GoogleMapsGeocoded.ts b/src/provider/googlemaps/GoogleMapsGeocoded.ts new file mode 100644 index 0000000..4740ef7 --- /dev/null +++ b/src/provider/googlemaps/GoogleMapsGeocoded.ts @@ -0,0 +1,343 @@ +import Geocoded, { GeocodedObject } from "Geocoded"; +import AdminLevel from "AdminLevel"; + +export interface GoogleMapsGeocodedObject extends GeocodedObject { + readonly placeId?: string; + readonly partialMatch?: boolean; + readonly resultType?: string[]; + readonly locationType?: string; + readonly streetAddress?: string; + readonly intersection?: string; + readonly political?: string; + readonly colloquialArea?: string; + readonly ward?: string; + readonly neighborhood?: string; + readonly premise?: string; + readonly subpremise?: string; + readonly naturalFeature?: string; + readonly airport?: string; + readonly park?: string; + readonly pointOfInterest?: string; + readonly establishment?: string; + readonly postalCodeSuffix?: string; + readonly subLocalityLevels?: AdminLevel[]; +} + +export default class GoogleMapsGeocoded extends Geocoded { + private readonly placeId?: string; + + private readonly partialMatch?: boolean; + + private readonly resultType?: string[]; + + private readonly locationType?: string; + + private readonly streetAddress?: string; + + private readonly intersection?: string; + + private readonly political?: string; + + private readonly colloquialArea?: string; + + private readonly ward?: string; + + private readonly neighborhood?: string; + + private readonly premise?: string; + + private readonly subpremise?: string; + + private readonly naturalFeature?: string; + + private readonly airport?: string; + + private readonly park?: string; + + private readonly pointOfInterest?: string; + + private readonly establishment?: string; + + private readonly postalCodeSuffix?: string; + + private readonly subLocalityLevels: AdminLevel[]; + + protected constructor({ + placeId, + partialMatch, + resultType, + locationType, + streetAddress, + intersection, + political, + colloquialArea, + ward, + neighborhood, + premise, + subpremise, + naturalFeature, + airport, + park, + pointOfInterest, + establishment, + postalCodeSuffix, + subLocalityLevels, + ...geocodedObject + }: GoogleMapsGeocodedObject) { + super(geocodedObject); + this.placeId = placeId; + this.partialMatch = partialMatch; + this.resultType = resultType; + this.locationType = locationType; + this.streetAddress = streetAddress; + this.intersection = intersection; + this.political = political; + this.colloquialArea = colloquialArea; + this.ward = ward; + this.neighborhood = neighborhood; + this.premise = premise; + this.subpremise = subpremise; + this.naturalFeature = naturalFeature; + this.airport = airport; + this.park = park; + this.pointOfInterest = pointOfInterest; + this.establishment = establishment; + this.postalCodeSuffix = postalCodeSuffix; + this.subLocalityLevels = subLocalityLevels || []; + } + + public static create(object: GoogleMapsGeocodedObject): GoogleMapsGeocoded { + return new this(object); + } + + public toObject(): GoogleMapsGeocodedObject { + return { + ...super.toObject(), + placeId: this.placeId, + partialMatch: this.partialMatch, + resultType: this.resultType, + locationType: this.locationType, + streetAddress: this.streetAddress, + intersection: this.intersection, + political: this.political, + colloquialArea: this.colloquialArea, + ward: this.ward, + neighborhood: this.neighborhood, + premise: this.premise, + subpremise: this.subpremise, + naturalFeature: this.naturalFeature, + airport: this.airport, + park: this.park, + pointOfInterest: this.pointOfInterest, + establishment: this.establishment, + postalCodeSuffix: this.postalCodeSuffix, + subLocalityLevels: this.subLocalityLevels, + }; + } + + public withPlaceId(placeId: string): GoogleMapsGeocoded { + return new GoogleMapsGeocoded({ + ...this.toObject(), + placeId, + }); + } + + public getPlaceId(): undefined | string { + return this.placeId; + } + + public withPartialMatch(partialMatch: boolean): GoogleMapsGeocoded { + return new GoogleMapsGeocoded({ + ...this.toObject(), + partialMatch, + }); + } + + public isPartialMatch(): undefined | boolean { + return this.partialMatch; + } + + public withResultType(resultType: string[]): GoogleMapsGeocoded { + return new GoogleMapsGeocoded({ + ...this.toObject(), + resultType, + }); + } + + public getResultType(): undefined | string[] { + return this.resultType; + } + + public withLocationType(locationType: string): GoogleMapsGeocoded { + return new GoogleMapsGeocoded({ + ...this.toObject(), + locationType, + }); + } + + public getLocationType(): undefined | string { + return this.locationType; + } + + public withStreetAddress(streetAddress: string): GoogleMapsGeocoded { + return new GoogleMapsGeocoded({ + ...this.toObject(), + streetAddress, + }); + } + + public getStreetAddress(): undefined | string { + return this.streetAddress; + } + + public withIntersection(intersection: string): GoogleMapsGeocoded { + return new GoogleMapsGeocoded({ + ...this.toObject(), + intersection, + }); + } + + public getIntersection(): undefined | string { + return this.intersection; + } + + public withPolitical(political: string): GoogleMapsGeocoded { + return new GoogleMapsGeocoded({ + ...this.toObject(), + political, + }); + } + + public getPolitical(): undefined | string { + return this.political; + } + + public withColloquialArea(colloquialArea: string): GoogleMapsGeocoded { + return new GoogleMapsGeocoded({ + ...this.toObject(), + colloquialArea, + }); + } + + public getColloquialArea(): undefined | string { + return this.colloquialArea; + } + + public withWard(ward: string): GoogleMapsGeocoded { + return new GoogleMapsGeocoded({ + ...this.toObject(), + ward, + }); + } + + public getWard(): undefined | string { + return this.ward; + } + + public withNeighborhood(neighborhood: string): GoogleMapsGeocoded { + return new GoogleMapsGeocoded({ + ...this.toObject(), + neighborhood, + }); + } + + public getNeighborhood(): undefined | string { + return this.neighborhood; + } + + public withPremise(premise: string): GoogleMapsGeocoded { + return new GoogleMapsGeocoded({ + ...this.toObject(), + premise, + }); + } + + public getPremise(): undefined | string { + return this.premise; + } + + public withSubpremise(subpremise: string): GoogleMapsGeocoded { + return new GoogleMapsGeocoded({ + ...this.toObject(), + subpremise, + }); + } + + public getSubpremise(): undefined | string { + return this.subpremise; + } + + public withNaturalFeature(naturalFeature: string): GoogleMapsGeocoded { + return new GoogleMapsGeocoded({ + ...this.toObject(), + naturalFeature, + }); + } + + public getNaturalFeature(): undefined | string { + return this.naturalFeature; + } + + public withAirport(airport: string): GoogleMapsGeocoded { + return new GoogleMapsGeocoded({ + ...this.toObject(), + airport, + }); + } + + public getAirport(): undefined | string { + return this.airport; + } + + public withPark(park: string): GoogleMapsGeocoded { + return new GoogleMapsGeocoded({ + ...this.toObject(), + park, + }); + } + + public getPark(): undefined | string { + return this.park; + } + + public withPointOfInterest(pointOfInterest: string): GoogleMapsGeocoded { + return new GoogleMapsGeocoded({ + ...this.toObject(), + pointOfInterest, + }); + } + + public getPointOfInterest(): undefined | string { + return this.pointOfInterest; + } + + public withEstablishment(establishment: string): GoogleMapsGeocoded { + return new GoogleMapsGeocoded({ + ...this.toObject(), + establishment, + }); + } + + public getEstablishment(): undefined | string { + return this.establishment; + } + + public withPostalCodeSuffix(postalCodeSuffix: string): GoogleMapsGeocoded { + return new GoogleMapsGeocoded({ + ...this.toObject(), + postalCodeSuffix, + }); + } + + public getPostalCodeSuffix(): undefined | string { + return this.postalCodeSuffix; + } + + public addSubLocalityLevel(subLocalityLevel: AdminLevel): void { + this.subLocalityLevels.push(subLocalityLevel); + } + + public getSubLocalityLevels(): AdminLevel[] { + return this.subLocalityLevels; + } +} diff --git a/src/provider/googlemaps/GoogleMapsProvider.ts b/src/provider/googlemaps/GoogleMapsProvider.ts new file mode 100644 index 0000000..3b3edfa --- /dev/null +++ b/src/provider/googlemaps/GoogleMapsProvider.ts @@ -0,0 +1,638 @@ +import { + ExternalLoaderBody, + ExternalLoaderHeaders, + ExternalLoaderInterface, + ExternalLoaderParams, +} from "ExternalLoader"; +import { + ErrorCallback, + GeocodedResultsCallback, + GoogleMapsGeocoded, + GoogleMapsGeocodeQuery, + GoogleMapsGeocodeQueryObject, + GoogleMapsReverseQuery, + GoogleMapsReverseQueryObject, + ProviderHelpers, + ProviderInterface, + ProviderOptionsInterface, + defaultProviderOptions, +} from "provider"; +import AdminLevel from "AdminLevel"; +import { ResponseError } from "error"; +import { + decodeBase64, + decodeUrlSafeBase64, + encodeUrlSafeBase64, + filterUndefinedObjectValues, + getRequireFunc, + isBrowser, +} from "utils"; + +interface GoogleMapsRequestParams { + [param: string]: string | undefined; + readonly key?: string; + readonly client?: string; + readonly signature?: string; + readonly channel?: string; + readonly region?: string; + readonly language?: string; + readonly limit?: string; + readonly address?: string; + readonly components?: string; + readonly bounds?: string; + readonly latlng?: string; + // eslint-disable-next-line camelcase + readonly result_type?: string; + // eslint-disable-next-line camelcase + readonly location_type?: string; +} + +interface GoogleMapsLatLng { + lat: number; + lng: number; +} + +type GoogleMapsPlaceType = + | "airport" + | "administrative_area_level_1" + | "administrative_area_level_2" + | "administrative_area_level_3" + | "administrative_area_level_4" + | "administrative_area_level_5" + | "archipelago" + | "bus_station" + | "colloquial_area" + | "continent" + | "country" + | "establishment" + | "finance" + | "floor" + | "food" + | "general_contractor" + | "geocode" + | "health" + | "intersection" + | "locality" + | "natural_feature" + | "neighborhood" + | "park" + | "parking" + | "place_of_worship" + | "plus_code" + | "point_of_interest" + | "political" + | "post_box" + | "postal_code" + | "postal_code_prefix" + | "postal_code_suffix" + | "postal_town" + | "premise" + | "room" + | "route" + | "street_address" + | "street_number" + | "sublocality" + | "sublocality_level_1" + | "sublocality_level_2" + | "sublocality_level_3" + | "sublocality_level_4" + | "sublocality_level_5" + | "subpremise" + | "town_square" + | "train_station" + | "transit_station" + | "ward"; + +export interface GoogleMapsResult { + geometry: { + location: GoogleMapsLatLng; + // eslint-disable-next-line camelcase + location_type: + | "ROOFTOP" + | "RANGE_INTERPOLATED" + | "GEOMETRIC_CENTER" + | "APPROXIMATE"; + viewport: { + northeast: GoogleMapsLatLng; + southwest: GoogleMapsLatLng; + }; + bounds?: { + northeast: GoogleMapsLatLng; + southwest: GoogleMapsLatLng; + }; + }; + // eslint-disable-next-line camelcase + formatted_address: string; + // eslint-disable-next-line camelcase + address_components: { + types: GoogleMapsPlaceType[]; + // eslint-disable-next-line camelcase + long_name: string; + // eslint-disable-next-line camelcase + short_name: string; + }[]; + // eslint-disable-next-line camelcase + place_id: string; + // eslint-disable-next-line camelcase + plus_code?: { + // eslint-disable-next-line camelcase + global_code: string; + // eslint-disable-next-line camelcase + compound_code?: string; + }; + types: GoogleMapsPlaceType[]; + // eslint-disable-next-line camelcase + postcode_localities?: string[]; + // eslint-disable-next-line camelcase + partial_match?: boolean; +} + +export interface GoogleMapsResponse { + results: GoogleMapsResult[]; + status: + | "OK" + | "ZERO_RESULTS" + | "OVER_DAILY_LIMIT" + | "OVER_QUERY_LIMIT" + | "REQUEST_DENIED" + | "INVALID_REQUEST" + | "UNKNOWN_ERROR"; + // eslint-disable-next-line camelcase + error_message?: string; +} + +export interface GoogleMapsProviderOptionsInterface + extends ProviderOptionsInterface { + readonly apiKey?: string; + readonly secret?: string; + readonly clientId?: string; + readonly countryCodes?: string[]; +} + +type GoogleMapsGeocodedResultsCallback = GeocodedResultsCallback< + GoogleMapsGeocoded +>; + +export default class GoogleMapsProvider + implements ProviderInterface { + private externalLoader: ExternalLoaderInterface; + + private options: GoogleMapsProviderOptionsInterface; + + public constructor( + _externalLoader: ExternalLoaderInterface, + options: GoogleMapsProviderOptionsInterface = defaultProviderOptions + ) { + this.externalLoader = _externalLoader; + this.options = { ...defaultProviderOptions, ...options }; + if (!this.options.apiKey && !this.options.clientId) { + throw new Error( + 'An API key or a client ID is required for the Google Maps provider. Please add it in the "apiKey" or the "clientId" option.' + ); + } + if (this.options.clientId && !this.options.secret) { + throw new Error( + 'An URL signing secret is required if you use a client ID (Premium only). Please add it in the "secret" option.' + ); + } + if (this.options.secret && isBrowser()) { + throw new Error( + 'The "secret" option cannot be used in a browser environment.' + ); + } + if (this.options.countryCodes && this.options.countryCodes.length !== 1) { + throw new Error( + 'The "countryCodes" option must have only one country code top-level domain.' + ); + } + } + + public geocode( + query: string | GoogleMapsGeocodeQuery | GoogleMapsGeocodeQueryObject, + callback: GoogleMapsGeocodedResultsCallback, + errorCallback?: ErrorCallback + ): void { + const geocodeQuery = ProviderHelpers.getGeocodeQueryFromParameter( + query, + GoogleMapsGeocodeQuery + ); + + if (geocodeQuery.getIp()) { + throw new Error( + "The GoogleMaps provider does not support IP geolocation, only location geocoding." + ); + } + + this.externalLoader.setOptions({ + protocol: this.options.useSsl ? "https" : "http", + host: "maps.googleapis.com", + pathname: "maps/api/geocode/json", + }); + + const params: GoogleMapsRequestParams = this.withCommonParams( + { + address: geocodeQuery.getText(), + bounds: geocodeQuery.getBounds() + ? `${geocodeQuery.getBounds()?.latitudeSW},${ + geocodeQuery.getBounds()?.longitudeSW + }|${geocodeQuery.getBounds()?.latitudeNE},${ + geocodeQuery.getBounds()?.longitudeNE + }` + : undefined, + components: (geocodeQuery).getComponents() + ? (geocodeQuery) + .getComponents() + ?.map((component) => `${component.name}:${component.value}`) + .join("|") + : undefined, + region: (geocodeQuery).getCountryCodes() + ? (geocodeQuery).getCountryCodes()?.join(",") + : this.options.countryCodes?.join(","), + }, + geocodeQuery + ); + + this.executeRequest(params, callback, {}, {}, errorCallback); + } + + public geodecode( + latitudeOrQuery: + | number + | string + | GoogleMapsReverseQuery + | GoogleMapsReverseQueryObject, + longitudeOrCallback: number | string | GoogleMapsGeocodedResultsCallback, + callbackOrErrorCallback?: GoogleMapsGeocodedResultsCallback | ErrorCallback, + errorCallback?: ErrorCallback + ): void { + const reverseQuery = ProviderHelpers.getReverseQueryFromParameters( + latitudeOrQuery, + longitudeOrCallback, + GoogleMapsReverseQuery + ); + const reverseCallback = ProviderHelpers.getCallbackFromParameters( + longitudeOrCallback, + callbackOrErrorCallback + ); + const reverseErrorCallback = ProviderHelpers.getErrorCallbackFromParameters( + longitudeOrCallback, + callbackOrErrorCallback, + errorCallback + ); + + this.externalLoader.setOptions({ + protocol: this.options.useSsl ? "https" : "http", + host: "maps.googleapis.com", + pathname: "maps/api/geocode/json", + }); + + const params: GoogleMapsRequestParams = this.withCommonParams( + { + latlng: `${reverseQuery.getCoordinates().latitude},${ + reverseQuery.getCoordinates().longitude + }`, + result_type: (reverseQuery).getResultTypes() + ? (reverseQuery).getResultTypes()?.join("|") + : undefined, + location_type: (reverseQuery).getLocationTypes() + ? (reverseQuery).getLocationTypes()?.join("|") + : undefined, + }, + reverseQuery + ); + + this.executeRequest(params, reverseCallback, {}, {}, reverseErrorCallback); + } + + private withCommonParams( + params: Pick< + GoogleMapsRequestParams, + | "address" + | "bounds" + | "components" + | "latlng" + | "location_type" + | "region" + | "result_type" + >, + query: GoogleMapsGeocodeQuery | GoogleMapsReverseQuery + ): GoogleMapsRequestParams { + let withCommonParams: GoogleMapsRequestParams = { + ...params, + key: this.options.apiKey, + client: this.options.clientId, + channel: query.getChannel(), + language: query.getLocale(), + limit: query.getLimit().toString(), + }; + + if (this.options.secret) { + withCommonParams = { + ...withCommonParams, + signature: GoogleMapsProvider.signQuery( + this.options.secret, + this.externalLoader.getOptions().pathname || "", + withCommonParams + ), + }; + } + + return withCommonParams; + } + + public executeRequest( + params: ExternalLoaderParams, + callback: GoogleMapsGeocodedResultsCallback, + headers?: ExternalLoaderHeaders, + body?: ExternalLoaderBody, + errorCallback?: ErrorCallback + ): void { + const { limit, ...externalLoaderParams } = params; + + this.externalLoader.executeRequest( + externalLoaderParams, + (data: GoogleMapsResponse) => { + let errorMessage: undefined | string; + switch (data.status) { + case "REQUEST_DENIED": + errorMessage = "Request has been denied"; + if (data.error_message) { + errorMessage += `: ${data.error_message}`; + } + break; + case "OVER_QUERY_LIMIT": + errorMessage = + "Exceeded daily quota when attempting geocoding request"; + if (data.error_message) { + errorMessage += `: ${data.error_message}`; + } + break; + case "OVER_DAILY_LIMIT": + errorMessage = "API usage has been limited"; + if (data.error_message) { + errorMessage += `: ${data.error_message}`; + } + break; + case "INVALID_REQUEST": + errorMessage = "The request is invalid"; + if (data.error_message) { + errorMessage += `: ${data.error_message}`; + } + break; + case "UNKNOWN_ERROR": + errorMessage = "Unknown error"; + if (data.error_message) { + errorMessage += `: ${data.error_message}`; + } + break; + default: + // Intentionnaly left empty + } + if (errorMessage && errorCallback) { + errorCallback(new ResponseError(errorMessage, data)); + return; + } + if (errorMessage) { + setTimeout(() => { + throw new Error(errorMessage); + }); + return; + } + + const { results } = data; + const resultsToRemove = + results.length - parseInt(limit || results.length.toString(), 10); + if (resultsToRemove > 0) { + results.splice(-resultsToRemove); + } + + callback( + results.map((result: GoogleMapsResult) => + GoogleMapsProvider.mapToGeocoded(result) + ) + ); + }, + headers, + body, + errorCallback + ); + } + + public static mapToGeocoded(result: GoogleMapsResult): GoogleMapsGeocoded { + const latitude = result.geometry.location.lat; + const longitude = result.geometry.location.lng; + const formattedAddress = result.formatted_address; + let streetNumber; + let streetName; + let subLocality; + let locality; + let postalCode; + let region; + let country; + let countryCode; + const adminLevels: AdminLevel[] = []; + const placeId = result.place_id; + const partialMatch = result.partial_match; + const resultType = result.types; + const locationType = result.geometry.location_type; + let streetAddress; + let intersection; + let political; + let colloquialArea; + let ward; + let neighborhood; + let premise; + let subpremise; + let naturalFeature; + let airport; + let park; + let pointOfInterest; + let establishment; + let postalCodeSuffix; + const subLocalityLevels: AdminLevel[] = []; + + result.address_components.forEach((addressComponent) => { + addressComponent.types.forEach((type) => { + switch (type) { + case "street_number": + streetNumber = addressComponent.long_name; + break; + case "route": + streetName = addressComponent.long_name; + break; + case "sublocality": + subLocality = addressComponent.long_name; + break; + case "locality": + case "postal_town": + locality = addressComponent.long_name; + break; + case "postal_code": + postalCode = addressComponent.long_name; + break; + case "administrative_area_level_1": + case "administrative_area_level_2": + case "administrative_area_level_3": + case "administrative_area_level_4": + case "administrative_area_level_5": + if (type === "administrative_area_level_1") { + region = addressComponent.long_name; + } + + adminLevels.push( + AdminLevel.create({ + level: parseInt(type.substr(-1), 10), + name: addressComponent.long_name, + code: addressComponent.short_name, + }) + ); + break; + case "sublocality_level_1": + case "sublocality_level_2": + case "sublocality_level_3": + case "sublocality_level_4": + case "sublocality_level_5": + subLocalityLevels.push( + AdminLevel.create({ + level: parseInt(type.substr(-1), 10), + name: addressComponent.long_name, + code: addressComponent.short_name, + }) + ); + break; + case "country": + country = addressComponent.long_name; + countryCode = addressComponent.short_name; + break; + case "street_address": + streetAddress = addressComponent.long_name; + break; + case "intersection": + intersection = addressComponent.long_name; + break; + case "political": + political = addressComponent.long_name; + break; + case "colloquial_area": + colloquialArea = addressComponent.long_name; + break; + case "ward": + ward = addressComponent.long_name; + break; + case "neighborhood": + neighborhood = addressComponent.long_name; + break; + case "premise": + premise = addressComponent.long_name; + break; + case "subpremise": + subpremise = addressComponent.long_name; + break; + case "natural_feature": + naturalFeature = addressComponent.long_name; + break; + case "airport": + airport = addressComponent.long_name; + break; + case "park": + park = addressComponent.long_name; + break; + case "point_of_interest": + pointOfInterest = addressComponent.long_name; + break; + case "establishment": + establishment = addressComponent.long_name; + break; + case "postal_code_suffix": + postalCodeSuffix = addressComponent.long_name; + break; + default: + } + }); + }); + + let geocoded = GoogleMapsGeocoded.create({ + coordinates: { + latitude, + longitude, + }, + formattedAddress, + streetNumber, + streetName, + subLocality, + locality, + postalCode, + region, + country, + countryCode, + adminLevels, + placeId, + partialMatch, + resultType, + locationType, + streetAddress, + intersection, + political, + colloquialArea, + ward, + neighborhood, + premise, + subpremise, + naturalFeature, + airport, + park, + pointOfInterest, + establishment, + postalCodeSuffix, + subLocalityLevels, + }); + + if (result.geometry.bounds) { + const { bounds } = result.geometry; + geocoded = geocoded.withBounds({ + latitudeSW: bounds.southwest.lat, + longitudeSW: bounds.southwest.lng, + latitudeNE: bounds.northeast.lat, + longitudeNE: bounds.northeast.lng, + }); + } else if (result.geometry.viewport) { + const { viewport } = result.geometry; + geocoded = geocoded.withBounds({ + latitudeSW: viewport.southwest.lat, + longitudeSW: viewport.southwest.lng, + latitudeNE: viewport.northeast.lat, + longitudeNE: viewport.northeast.lng, + }); + } else if (result.geometry.location_type === "ROOFTOP") { + // Fake bounds + geocoded = geocoded.withBounds({ + latitudeSW: latitude, + longitudeSW: longitude, + latitudeNE: latitude, + longitudeNE: longitude, + }); + } + + return geocoded; + } + + private static signQuery( + secret: string, + pathname: string, + params: GoogleMapsRequestParams + ): string { + const crypto = getRequireFunc()("crypto"); + + const filteredRequestParams = filterUndefinedObjectValues(params); + + const safeSecret = decodeBase64(decodeUrlSafeBase64(secret)); + const toSign = `${pathname}?${new URLSearchParams( + filteredRequestParams + ).toString()}`; + const hashedSignature = encodeUrlSafeBase64( + crypto.createHmac("sha1", safeSecret).update(toSign).digest("base64") + ); + + return hashedSignature; + } +} diff --git a/src/provider/googlemaps/GoogleMapsReverseQuery.ts b/src/provider/googlemaps/GoogleMapsReverseQuery.ts new file mode 100644 index 0000000..bb8a60e --- /dev/null +++ b/src/provider/googlemaps/GoogleMapsReverseQuery.ts @@ -0,0 +1,66 @@ +import { ReverseQuery, ReverseQueryObject } from "query"; + +export interface GoogleMapsReverseQueryObject extends ReverseQueryObject { + readonly resultTypes?: string[]; + readonly locationTypes?: string[]; + readonly channel?: string; +} + +export default class GoogleMapsReverseQuery extends ReverseQuery { + private readonly resultTypes?: string[]; + + private readonly locationTypes?: string[]; + + private readonly channel?: string; + + protected constructor({ + resultTypes, + locationTypes, + channel, + ...reverseQueryObject + }: GoogleMapsReverseQueryObject) { + super(reverseQueryObject); + this.resultTypes = resultTypes; + this.locationTypes = locationTypes; + this.channel = channel; + } + + public static create( + object: GoogleMapsReverseQueryObject + ): GoogleMapsReverseQuery { + return new this(object); + } + + public toObject(): GoogleMapsReverseQueryObject { + return { + ...super.toObject(), + resultTypes: this.resultTypes, + locationTypes: this.locationTypes, + channel: this.channel, + }; + } + + public withResultTypes(resultTypes: string[]): GoogleMapsReverseQuery { + return new GoogleMapsReverseQuery({ ...this.toObject(), resultTypes }); + } + + public getResultTypes(): undefined | string[] { + return this.resultTypes; + } + + public withLocationTypes(locationTypes: string[]): GoogleMapsReverseQuery { + return new GoogleMapsReverseQuery({ ...this.toObject(), locationTypes }); + } + + public getLocationTypes(): undefined | string[] { + return this.locationTypes; + } + + public withChannel(channel: string): GoogleMapsReverseQuery { + return new GoogleMapsReverseQuery({ ...this.toObject(), channel }); + } + + public getChannel(): undefined | string { + return this.channel; + } +} diff --git a/src/provider/googlemaps/index.ts b/src/provider/googlemaps/index.ts new file mode 100644 index 0000000..39df88d --- /dev/null +++ b/src/provider/googlemaps/index.ts @@ -0,0 +1,7 @@ +export { default as GoogleMapsGeocoded } from "provider/googlemaps/GoogleMapsGeocoded"; +export { default as GoogleMapsGeocodeQuery } from "provider/googlemaps/GoogleMapsGeocodeQuery"; +export * from "provider/googlemaps/GoogleMapsGeocodeQuery"; +export { default as GoogleMapsProvider } from "provider/googlemaps/GoogleMapsProvider"; +export * from "provider/googlemaps/GoogleMapsProvider"; +export { default as GoogleMapsReverseQuery } from "provider/googlemaps/GoogleMapsReverseQuery"; +export * from "provider/googlemaps/GoogleMapsReverseQuery"; diff --git a/src/provider/index.ts b/src/provider/index.ts new file mode 100644 index 0000000..a546666 --- /dev/null +++ b/src/provider/index.ts @@ -0,0 +1,13 @@ +export { default as ProviderHelpers } from "provider/ProviderHelpers"; +export { default as ProviderInterface } from "provider/ProviderInterface"; +export * from "provider/ProviderInterface"; +export { default as ChainProvider } from "provider/ChainProvider"; +export * from "provider/ChainProvider"; +export * from "provider/bing"; +export * from "provider/geoplugin"; +export * from "provider/googlemaps"; +export * from "provider/mapbox"; +export * from "provider/mapquest"; +export * from "provider/nominatim"; +export * from "provider/opencage"; +export * from "provider/yandex"; diff --git a/src/provider/mapbox/MapboxGeocodeQuery.ts b/src/provider/mapbox/MapboxGeocodeQuery.ts new file mode 100644 index 0000000..ace9b07 --- /dev/null +++ b/src/provider/mapbox/MapboxGeocodeQuery.ts @@ -0,0 +1,84 @@ +import { GeocodeQuery, GeocodeQueryObject } from "query"; +import { Coordinates } from "types"; + +export interface MapboxGeocodeQueryObject extends GeocodeQueryObject { + readonly countryCodes?: string[]; + readonly proximity?: Coordinates; + readonly locationTypes?: string[]; + readonly fuzzyMatch?: boolean; +} + +export default class MapboxGeocodeQuery extends GeocodeQuery { + private readonly countryCodes?: string[]; + + private readonly proximity?: Coordinates; + + private readonly locationTypes?: string[]; + + private readonly fuzzyMatch?: boolean; + + protected constructor({ + countryCodes, + proximity, + locationTypes, + fuzzyMatch, + ...geocodeQueryObject + }: MapboxGeocodeQueryObject) { + super(geocodeQueryObject); + this.countryCodes = countryCodes; + if (proximity && (!proximity.latitude || !proximity.longitude)) { + throw new Error( + 'The "proximity" parameter must be an object with the keys: "latitude", "longitude".' + ); + } + this.proximity = proximity; + this.locationTypes = locationTypes; + this.fuzzyMatch = fuzzyMatch; + } + + public static create(object: MapboxGeocodeQueryObject): MapboxGeocodeQuery { + return new this(object); + } + + public toObject(): MapboxGeocodeQueryObject { + return { + ...super.toObject(), + countryCodes: this.countryCodes, + proximity: this.proximity, + locationTypes: this.locationTypes, + fuzzyMatch: this.fuzzyMatch, + }; + } + + public withCountryCodes(countryCodes: string[]): MapboxGeocodeQuery { + return new MapboxGeocodeQuery({ ...this.toObject(), countryCodes }); + } + + public getCountryCodes(): undefined | string[] { + return this.countryCodes; + } + + public withProximity(proximity: Coordinates): MapboxGeocodeQuery { + return new MapboxGeocodeQuery({ ...this.toObject(), proximity }); + } + + public getProximity(): undefined | Coordinates { + return this.proximity; + } + + public withLocationTypes(locationTypes: string[]): MapboxGeocodeQuery { + return new MapboxGeocodeQuery({ ...this.toObject(), locationTypes }); + } + + public getLocationTypes(): undefined | string[] { + return this.locationTypes; + } + + public withFuzzyMatch(fuzzyMatch: boolean): MapboxGeocodeQuery { + return new MapboxGeocodeQuery({ ...this.toObject(), fuzzyMatch }); + } + + public getFuzzyMatch(): undefined | boolean { + return this.fuzzyMatch; + } +} diff --git a/src/provider/mapbox/MapboxGeocoded.ts b/src/provider/mapbox/MapboxGeocoded.ts new file mode 100644 index 0000000..47065e1 --- /dev/null +++ b/src/provider/mapbox/MapboxGeocoded.ts @@ -0,0 +1,39 @@ +import Geocoded, { GeocodedObject } from "Geocoded"; + +export interface MapboxGeocodedObject extends GeocodedObject { + readonly resultType?: string[]; +} + +export default class MapboxGeocoded extends Geocoded { + private readonly resultType?: string[]; + + protected constructor({ + resultType, + ...geocodedObject + }: MapboxGeocodedObject) { + super(geocodedObject); + this.resultType = resultType; + } + + public static create(object: MapboxGeocodedObject): MapboxGeocoded { + return new this(object); + } + + public toObject(): MapboxGeocodedObject { + return { + ...super.toObject(), + resultType: this.resultType, + }; + } + + public withResultType(resultType: string[]): MapboxGeocoded { + return new MapboxGeocoded({ + ...this.toObject(), + resultType, + }); + } + + public getResultType(): undefined | string[] { + return this.resultType; + } +} diff --git a/src/provider/mapbox/MapboxProvider.ts b/src/provider/mapbox/MapboxProvider.ts new file mode 100644 index 0000000..1ee1372 --- /dev/null +++ b/src/provider/mapbox/MapboxProvider.ts @@ -0,0 +1,368 @@ +import { + ExternalLoaderBody, + ExternalLoaderHeaders, + ExternalLoaderInterface, + ExternalLoaderParams, +} from "ExternalLoader"; +import { + ErrorCallback, + GeocodedResultsCallback, + MapboxGeocoded, + MapboxGeocodeQuery, + MapboxGeocodeQueryObject, + MapboxReverseQuery, + MapboxReverseQueryObject, + ProviderHelpers, + ProviderInterface, + ProviderOptionsInterface, + defaultProviderOptions, +} from "provider"; +import { FlatBoundingBox, FlatCoordinates } from "types"; +import AdminLevel, { ADMIN_LEVEL_CODES } from "AdminLevel"; + +interface MapboxRequestParams { + [param: string]: string | undefined; + // eslint-disable-next-line camelcase + readonly access_token: string; + readonly country?: string; + readonly language?: string; + readonly limit?: string; + readonly bbox?: string; + readonly fuzzyMatch?: string; + readonly proximity?: string; + readonly reverseMode?: "distance" | "score"; + readonly types?: string; +} + +interface MapboxFeatureContextProperties { + id: string; + text: string; + wikidata?: string; + // eslint-disable-next-line camelcase + short_code?: string; +} + +interface MapboxFeatureProperties { + accuracy?: string; + address?: string; + category?: string; + maki?: string; + wikidata?: string; + // eslint-disable-next-line camelcase + short_code?: string; +} + +export interface MapboxResult { + id: string; + type: "Feature"; + // eslint-disable-next-line camelcase + place_type: ( + | "country" + | "region" + | "postcode" + | "district" + | "place" + | "locality" + | "neighborhood" + | "address" + | "poi" + )[]; + relevance: number; + address?: string; + properties: MapboxFeatureProperties; + text: string; + // eslint-disable-next-line camelcase + place_name: string; + // eslint-disable-next-line camelcase + matching_text?: string; + // eslint-disable-next-line camelcase + matching_place_name?: string; + language?: string; + bbox?: FlatBoundingBox; + center: FlatCoordinates; + geometry: { + type: "Point"; + coordinates: FlatCoordinates; + }; + context?: MapboxFeatureContextProperties[]; + // eslint-disable-next-line camelcase + routable_points?: { + points?: { + coordinates: FlatCoordinates; + }[]; + }; +} + +export interface MapboxResponse { + type: "FeatureCollection"; + query: string[]; + features: MapboxResult[]; + attribution: string; +} + +// eslint-disable-next-line no-shadow +export enum MAPBOX_GEOCODING_MODES { + GEOCODING_MODE_PLACES = "mapbox.places", + GEOCODING_MODE_PLACES_PERMANENT = "mapbox.places-permanent", +} + +export interface MapboxProviderOptionsInterface + extends ProviderOptionsInterface { + readonly apiKey: string; + readonly geocodingMode?: MAPBOX_GEOCODING_MODES; + readonly countryCodes?: string[]; +} + +export const defaultMapboxProviderOptions = { + ...defaultProviderOptions, + apiKey: "", + geocodingMode: MAPBOX_GEOCODING_MODES.GEOCODING_MODE_PLACES, +}; + +type MapboxGeocodedResultsCallback = GeocodedResultsCallback; + +export default class MapboxProvider + implements ProviderInterface { + private externalLoader: ExternalLoaderInterface; + + private options: MapboxProviderOptionsInterface; + + public constructor( + _externalLoader: ExternalLoaderInterface, + options: MapboxProviderOptionsInterface = defaultMapboxProviderOptions + ) { + this.externalLoader = _externalLoader; + this.options = { ...defaultMapboxProviderOptions, ...options }; + if (!this.options.apiKey) { + throw new Error( + 'An API key is required for the Mapbox provider. Please add it in the "apiKey" option.' + ); + } + } + + public geocode( + query: string | MapboxGeocodeQuery | MapboxGeocodeQueryObject, + callback: MapboxGeocodedResultsCallback, + errorCallback?: ErrorCallback + ): void { + const geocodeQuery = ProviderHelpers.getGeocodeQueryFromParameter( + query, + MapboxGeocodeQuery + ); + + if (geocodeQuery.getIp()) { + throw new Error( + "The Mapbox provider does not support IP geolocation, only location geocoding." + ); + } + + this.externalLoader.setOptions({ + protocol: this.options.useSsl ? "https" : "http", + host: "api.mapbox.com", + pathname: `geocoding/v5/${ + this.options.geocodingMode + }/${geocodeQuery.getText()}.json`, + }); + + const fuzzyMatch = (geocodeQuery).getFuzzyMatch() + ? "true" + : "false"; + const params: MapboxRequestParams = this.withCommonParams( + { + bbox: geocodeQuery.getBounds() + ? `${geocodeQuery.getBounds()?.longitudeSW},${ + geocodeQuery.getBounds()?.latitudeSW + },${geocodeQuery.getBounds()?.longitudeNE},${ + geocodeQuery.getBounds()?.latitudeNE + }` + : undefined, + fuzzyMatch: + (geocodeQuery).getFuzzyMatch() !== undefined + ? fuzzyMatch + : undefined, + proximity: (geocodeQuery).getProximity() + ? `${(geocodeQuery).getProximity()?.longitude},${ + (geocodeQuery).getProximity()?.latitude + }` + : undefined, + types: (geocodeQuery).getLocationTypes() + ? (geocodeQuery).getLocationTypes()?.join(",") + : undefined, + }, + geocodeQuery + ); + + this.executeRequest(params, callback, {}, {}, errorCallback); + } + + public geodecode( + latitudeOrQuery: + | number + | string + | MapboxReverseQuery + | MapboxReverseQueryObject, + longitudeOrCallback: number | string | MapboxGeocodedResultsCallback, + callbackOrErrorCallback?: MapboxGeocodedResultsCallback | ErrorCallback, + errorCallback?: ErrorCallback + ): void { + const reverseQuery = ProviderHelpers.getReverseQueryFromParameters( + latitudeOrQuery, + longitudeOrCallback, + MapboxReverseQuery + ); + const reverseCallback = ProviderHelpers.getCallbackFromParameters( + longitudeOrCallback, + callbackOrErrorCallback + ); + const reverseErrorCallback = ProviderHelpers.getErrorCallbackFromParameters( + longitudeOrCallback, + callbackOrErrorCallback, + errorCallback + ); + + this.externalLoader.setOptions({ + protocol: this.options.useSsl ? "https" : "http", + host: "api.mapbox.com", + pathname: `geocoding/v5/${this.options.geocodingMode}/${ + reverseQuery.getCoordinates().longitude + },${reverseQuery.getCoordinates().latitude}.json`, + }); + + const params: MapboxRequestParams = this.withCommonParams( + { + reverseMode: (reverseQuery).getReverseMode() + ? (reverseQuery).getReverseMode() + : undefined, + types: (reverseQuery).getLocationTypes() + ? (reverseQuery).getLocationTypes()?.join(",") + : "address", + }, + reverseQuery + ); + + this.executeRequest(params, reverseCallback, {}, {}, reverseErrorCallback); + } + + private withCommonParams( + params: Pick< + MapboxRequestParams, + "bbox" | "fuzzyMatch" | "proximity" | "reverseMode" | "types" + >, + query: MapboxGeocodeQuery | MapboxReverseQuery + ): MapboxRequestParams { + return { + ...params, + access_token: this.options.apiKey || "", + country: query.getCountryCodes() + ? query.getCountryCodes()?.join(",") + : this.options.countryCodes?.join(","), + language: query.getLocale(), + limit: query.getLimit().toString(), + }; + } + + public executeRequest( + params: ExternalLoaderParams, + callback: MapboxGeocodedResultsCallback, + headers?: ExternalLoaderHeaders, + body?: ExternalLoaderBody, + errorCallback?: ErrorCallback + ): void { + this.externalLoader.executeRequest( + params, + (data: MapboxResponse) => { + callback( + data.features.map((result: MapboxResult) => + MapboxProvider.mapToGeocoded(result) + ) + ); + }, + headers, + body, + errorCallback + ); + } + + public static mapToGeocoded(result: MapboxResult): MapboxGeocoded { + const latitude = result.geometry.coordinates[1]; + const longitude = result.geometry.coordinates[0]; + const formattedAddress = result.place_name; + const streetNumber = result.address; + const streetName = result.text; + let locality; + let postalCode; + let region; + let country; + let countryCode; + const adminLevels: AdminLevel[] = []; + const resultType = result.place_type; + + let adminLevelCode: undefined | string; + (result.context || []).forEach((feature) => { + const type = feature.id.split(".")[0]; + switch (type) { + case "locality": + locality = feature.text; + break; + case "place": + locality = feature.text; + adminLevels.push( + AdminLevel.create({ + level: ADMIN_LEVEL_CODES.COUNTY_CODE, + name: locality, + }) + ); + break; + case "postcode": + postalCode = feature.text; + break; + case "region": + region = feature.text; + adminLevelCode = undefined; + if (feature.short_code && feature.short_code.match(/[A-z]{2}-/)) { + adminLevelCode = feature.short_code.replace(/[A-z]{2}-/, ""); + } + adminLevels.push( + AdminLevel.create({ + level: ADMIN_LEVEL_CODES.STATE_CODE, + name: region, + code: adminLevelCode, + }) + ); + break; + case "country": + country = feature.text; + countryCode = feature.short_code; + break; + default: + } + }); + + let geocoded = MapboxGeocoded.create({ + coordinates: { + latitude, + longitude, + }, + formattedAddress, + streetNumber, + streetName, + locality, + postalCode, + region, + adminLevels, + country, + countryCode, + resultType, + }); + if (result.bbox) { + geocoded = geocoded.withBounds({ + latitudeSW: result.bbox[1], + longitudeSW: result.bbox[0], + latitudeNE: result.bbox[3], + longitudeNE: result.bbox[2], + }); + } + + return geocoded; + } +} diff --git a/src/provider/mapbox/MapboxReverseQuery.ts b/src/provider/mapbox/MapboxReverseQuery.ts new file mode 100644 index 0000000..dd730dd --- /dev/null +++ b/src/provider/mapbox/MapboxReverseQuery.ts @@ -0,0 +1,66 @@ +import { ReverseQuery, ReverseQueryObject } from "query"; + +type ReverseMode = "distance" | "score"; + +export interface MapboxReverseQueryObject extends ReverseQueryObject { + readonly countryCodes?: string[]; + readonly reverseMode?: ReverseMode; + readonly locationTypes?: string[]; +} + +export default class MapboxReverseQuery extends ReverseQuery { + private readonly countryCodes?: string[]; + + private readonly reverseMode?: ReverseMode; + + private readonly locationTypes?: string[]; + + protected constructor({ + countryCodes, + reverseMode, + locationTypes, + ...reverseQueryObject + }: MapboxReverseQueryObject) { + super(reverseQueryObject); + this.countryCodes = countryCodes; + this.reverseMode = reverseMode; + this.locationTypes = locationTypes; + } + + public static create(object: MapboxReverseQueryObject): MapboxReverseQuery { + return new this(object); + } + + public toObject(): MapboxReverseQueryObject { + return { + ...super.toObject(), + countryCodes: this.countryCodes, + reverseMode: this.reverseMode, + locationTypes: this.locationTypes, + }; + } + + public withCountryCodes(countryCodes: string[]): MapboxReverseQuery { + return new MapboxReverseQuery({ ...this.toObject(), countryCodes }); + } + + public getCountryCodes(): undefined | string[] { + return this.countryCodes; + } + + public withReverseMode(reverseMode: ReverseMode): MapboxReverseQuery { + return new MapboxReverseQuery({ ...this.toObject(), reverseMode }); + } + + public getReverseMode(): undefined | ReverseMode { + return this.reverseMode; + } + + public withLocationTypes(locationTypes: string[]): MapboxReverseQuery { + return new MapboxReverseQuery({ ...this.toObject(), locationTypes }); + } + + public getLocationTypes(): undefined | string[] { + return this.locationTypes; + } +} diff --git a/src/provider/mapbox/index.ts b/src/provider/mapbox/index.ts new file mode 100644 index 0000000..acf0bd9 --- /dev/null +++ b/src/provider/mapbox/index.ts @@ -0,0 +1,7 @@ +export { default as MapboxGeocoded } from "provider/mapbox/MapboxGeocoded"; +export { default as MapboxGeocodeQuery } from "provider/mapbox/MapboxGeocodeQuery"; +export * from "provider/mapbox/MapboxGeocodeQuery"; +export { default as MapboxProvider } from "provider/mapbox/MapboxProvider"; +export * from "provider/mapbox/MapboxProvider"; +export { default as MapboxReverseQuery } from "provider/mapbox/MapboxReverseQuery"; +export * from "provider/mapbox/MapboxReverseQuery"; diff --git a/src/provider/mapquest/MapQuestGeocodeQuery.ts b/src/provider/mapquest/MapQuestGeocodeQuery.ts new file mode 100644 index 0000000..34387e7 --- /dev/null +++ b/src/provider/mapquest/MapQuestGeocodeQuery.ts @@ -0,0 +1,133 @@ +import { GeocodeQuery, GeocodeQueryObject } from "query"; +import { MapQuestLocation, MapQuestLocationObject } from "provider"; +import Geocoded, { GeocodedObject } from "Geocoded"; +import { ADMIN_LEVEL_CODES } from "AdminLevel"; + +export interface MapQuestGeocodeQueryObject extends GeocodeQueryObject { + readonly location?: + | MapQuestLocationObject + | MapQuestLocation + | GeocodedObject + | Geocoded; +} + +export default class MapQuestGeocodeQuery extends GeocodeQuery { + private readonly location?: MapQuestLocationObject; + + protected constructor({ + location, + ...geocodeQueryObject + }: MapQuestGeocodeQueryObject) { + super({ + ...geocodeQueryObject, + text: location ? "from_location" : geocodeQueryObject.text, + }); + if (!location) { + return; + } + if ("getLatLng" in location) { + this.location = (location).toObject(); + return; + } + if ("getLocality" in location) { + this.location = MapQuestGeocodeQuery.convertGeocodedToLocationObject( + location + ); + return; + } + let geocodedPropsNumber = 0; + let mapQuestLocationPropsNumber = 0; + const geocodedObject = Geocoded.create({}).toObject(); + Object.keys(geocodedObject).forEach((prop) => { + if (prop in location) { + geocodedPropsNumber += 1; + } + }); + [ + "street", + "adminArea5", + "city", + "adminArea4", + "county", + "adminArea3", + "state", + "adminArea1", + "country", + "postalCode", + "type", + ].forEach((prop) => { + if (prop in location) { + mapQuestLocationPropsNumber += 1; + } + }); + if (geocodedPropsNumber > mapQuestLocationPropsNumber) { + this.location = MapQuestGeocodeQuery.convertGeocodedToLocationObject( + Geocoded.create(location) + ); + return; + } + + this.location = location; + } + + public static create( + object: MapQuestGeocodeQueryObject + ): MapQuestGeocodeQuery { + return new this(object); + } + + public toObject(): MapQuestGeocodeQueryObject { + return { + ...super.toObject(), + location: this.location, + }; + } + + public withLocation( + location: + | MapQuestLocationObject + | MapQuestLocation + | GeocodedObject + | Geocoded + ): MapQuestGeocodeQuery { + return new MapQuestGeocodeQuery({ ...this.toObject(), location }); + } + + public getLocation(): undefined | MapQuestLocationObject { + return this.location; + } + + private static convertGeocodedToLocationObject( + geocoded: Geocoded + ): MapQuestLocationObject { + const location: MapQuestLocationObject = {}; + + const streetParts = [ + geocoded.getStreetNumber() || "", + geocoded.getStreetName() || "", + ]; + const street = streetParts.join(" "); + if (street) { + location.street = street; + } + if (geocoded.getLocality()) { + location.city = geocoded.getLocality(); + } + if (geocoded.getCountry()) { + location.country = geocoded.getCountry(); + } + if (geocoded.getPostalCode()) { + location.postalCode = geocoded.getPostalCode(); + } + geocoded.getAdminLevels().forEach((adminLevel) => { + if (adminLevel.getLevel() === ADMIN_LEVEL_CODES.STATE_CODE) { + location.state = adminLevel.getCode() || adminLevel.getName(); + } + if (adminLevel.getLevel() === ADMIN_LEVEL_CODES.COUNTY_CODE) { + location.county = adminLevel.getCode() || adminLevel.getName(); + } + }); + + return location; + } +} diff --git a/src/provider/mapquest/MapQuestGeocoded.ts b/src/provider/mapquest/MapQuestGeocoded.ts new file mode 100644 index 0000000..794e6de --- /dev/null +++ b/src/provider/mapquest/MapQuestGeocoded.ts @@ -0,0 +1,90 @@ +import Geocoded, { GeocodedObject } from "Geocoded"; + +export interface MapQuestGeocodedObject extends GeocodedObject { + readonly precision?: string; + readonly precisionCode?: string; + readonly mapUrl?: string; + readonly attribution?: string; +} + +export default class MapQuestGeocoded extends Geocoded { + private readonly precision?: string; + + private readonly precisionCode?: string; + + private readonly mapUrl?: string; + + private readonly attribution?: string; + + protected constructor({ + precision, + precisionCode, + mapUrl, + attribution, + ...geocodedObject + }: MapQuestGeocodedObject) { + super(geocodedObject); + this.precision = precision; + this.precisionCode = precisionCode; + this.mapUrl = mapUrl; + this.attribution = attribution; + } + + public static create(object: MapQuestGeocodedObject): MapQuestGeocoded { + return new this(object); + } + + public toObject(): MapQuestGeocodedObject { + return { + ...super.toObject(), + precision: this.precision, + precisionCode: this.precisionCode, + mapUrl: this.mapUrl, + attribution: this.attribution, + }; + } + + public withPrecision(precision: string): MapQuestGeocoded { + return new MapQuestGeocoded({ + ...this.toObject(), + precision, + }); + } + + public getPrecision(): undefined | string { + return this.precision; + } + + public withPrecisionCode(precisionCode: string): MapQuestGeocoded { + return new MapQuestGeocoded({ + ...this.toObject(), + precisionCode, + }); + } + + public getPrecisionCode(): undefined | string { + return this.precisionCode; + } + + public withMapUrl(mapUrl: string): MapQuestGeocoded { + return new MapQuestGeocoded({ + ...this.toObject(), + mapUrl, + }); + } + + public getMapUrl(): undefined | string { + return this.mapUrl; + } + + public withAttribution(attribution: string): MapQuestGeocoded { + return new MapQuestGeocoded({ + ...this.toObject(), + attribution, + }); + } + + public getAttribution(): undefined | string { + return this.attribution; + } +} diff --git a/src/provider/mapquest/MapQuestLocation.ts b/src/provider/mapquest/MapQuestLocation.ts new file mode 100644 index 0000000..a70e2e6 --- /dev/null +++ b/src/provider/mapquest/MapQuestLocation.ts @@ -0,0 +1,139 @@ +import { MapQuestCoordinates } from "provider"; + +export interface MapQuestLocationObject { + latLng?: MapQuestCoordinates; + street?: string; + adminArea1?: string; + country?: string; + adminArea3?: string; + state?: string; + adminArea4?: string; + county?: string; + adminArea5?: string; + city?: string; + postalCode?: string; + type?: "s" | "v"; +} + +export default class MapQuestLocation { + private readonly latLng?: MapQuestCoordinates; + + private readonly street?: string; + + private readonly adminArea1?: string; + + private readonly country?: string; + + private readonly adminArea3?: string; + + private readonly state?: string; + + private readonly adminArea4?: string; + + private readonly county?: string; + + private readonly adminArea5?: string; + + private readonly city?: string; + + private readonly postalCode?: string; + + private readonly type?: "s" | "v"; + + protected constructor({ + latLng, + street, + adminArea1, + country, + adminArea3, + state, + adminArea4, + county, + adminArea5, + city, + postalCode, + type, + }: MapQuestLocationObject) { + this.latLng = latLng; + this.street = street; + this.adminArea1 = adminArea1; + this.country = country; + this.adminArea3 = adminArea3; + this.state = state; + this.adminArea4 = adminArea4; + this.county = county; + this.adminArea5 = adminArea5; + this.city = city; + this.postalCode = postalCode; + this.type = type; + } + + public static create(object: MapQuestLocationObject): MapQuestLocation { + return new this(object); + } + + public toObject(): MapQuestLocationObject { + return { + latLng: this.latLng, + street: this.street, + adminArea1: this.adminArea1, + country: this.country, + adminArea3: this.adminArea3, + state: this.state, + adminArea4: this.adminArea4, + county: this.county, + adminArea5: this.adminArea5, + city: this.city, + postalCode: this.postalCode, + type: this.type, + }; + } + + public getLatLng(): undefined | MapQuestCoordinates { + return this.latLng; + } + + public getStreet(): undefined | string { + return this.street; + } + + public getAdminArea1(): undefined | string { + return this.adminArea1; + } + + public getCountry(): undefined | string { + return this.country; + } + + public getAdminArea3(): undefined | string { + return this.adminArea3; + } + + public getState(): undefined | string { + return this.state; + } + + public getAdminArea4(): undefined | string { + return this.adminArea4; + } + + public getCounty(): undefined | string { + return this.county; + } + + public getAdminArea5(): undefined | string { + return this.adminArea5; + } + + public getCity(): undefined | string { + return this.city; + } + + public getPostalCode(): undefined | string { + return this.postalCode; + } + + public getType(): undefined | "s" | "v" { + return this.type; + } +} diff --git a/src/provider/mapquest/MapQuestProvider.ts b/src/provider/mapquest/MapQuestProvider.ts new file mode 100644 index 0000000..35e9202 --- /dev/null +++ b/src/provider/mapquest/MapQuestProvider.ts @@ -0,0 +1,376 @@ +import { + ExternalLoaderBody, + ExternalLoaderHeaders, + ExternalLoaderInterface, + ExternalLoaderParams, +} from "ExternalLoader"; +import { + ErrorCallback, + GeocodedResultsCallback, + MapQuestGeocoded, + MapQuestGeocodeQuery, + MapQuestGeocodeQueryObject, + ProviderHelpers, + ProviderInterface, + ProviderOptionsInterface, + defaultProviderOptions, +} from "provider"; +import { ReverseQuery, ReverseQueryObject } from "query"; +import AdminLevel, { ADMIN_LEVEL_CODES } from "AdminLevel"; +import { ResponseError } from "error"; + +interface MapQuestRequestParams { + [param: string]: string | undefined; + readonly key: string; + readonly location?: string; + readonly boundingBox?: string; + readonly maxResults?: string; + readonly jsonpCallback?: string; +} + +export interface MapQuestCoordinates { + lat: number; + lng: number; +} + +export interface MapQuestResult { + latLng: MapQuestCoordinates; + displayLatLng: MapQuestCoordinates; + street: string; + sideOfStreet: string; + adminArea1?: string; + adminArea1Type?: string; + adminArea3?: string; + adminArea3Type?: string; + adminArea4?: string; + adminArea4Type?: string; + adminArea5?: string; + adminArea5Type?: string; + adminArea6?: string; + adminArea6Type?: string; + postalCode: string; + type: "s" | "v"; + linkId: string; + dragPoint: boolean; + geocodeQuality: + | "POINT" + | "ADDRESS" + | "INTERSECTION" + | "STREET" + | "COUNTRY" + | "STATE" + | "COUNTY" + | "CITY" + | "NEIGHBORHOOD" + | "ZIP" + | "ZIP_EXTENDED"; + geocodeQualityCode: string; + mapUrl: string; +} + +export interface MapQuestResponse { + info: { + statuscode: 0 | 400 | 403 | 500; + copyright: { + text: string; + imageUrl: string; + imageAltText: string; + }; + messages: string[]; + }; + results: { + providedLocation: { + location?: string; + latLng?: MapQuestCoordinates; + }; + locations: MapQuestResult[]; + }[]; +} + +export interface MapQuestProviderOptionsInterface + extends ProviderOptionsInterface { + readonly apiKey: string; + readonly method?: "GET" | "POST"; + readonly openDomain?: boolean; +} + +export const defaultMapQuestProviderOptions: MapQuestProviderOptionsInterface = { + ...defaultProviderOptions, + apiKey: "", + method: "GET", + openDomain: false, +}; + +type MapQuestGeocodedResultsCallback = GeocodedResultsCallback< + MapQuestGeocoded +>; + +export default class MapQuestProvider + implements ProviderInterface { + private externalLoader: ExternalLoaderInterface; + + private options: MapQuestProviderOptionsInterface; + + public constructor( + _externalLoader: ExternalLoaderInterface, + options: MapQuestProviderOptionsInterface = defaultMapQuestProviderOptions + ) { + this.externalLoader = _externalLoader; + this.options = { ...defaultMapQuestProviderOptions, ...options }; + if (!this.options.apiKey) { + throw new Error( + 'An API key is required for the MapQuest provider. Please add it in the "apiKey" option.' + ); + } + if (this.options.method && !["GET", "POST"].includes(this.options.method)) { + throw new Error('The "method" option can either be "GET" or "POST".'); + } + } + + public geocode( + query: string | MapQuestGeocodeQuery | MapQuestGeocodeQueryObject, + callback: MapQuestGeocodedResultsCallback, + errorCallback?: ErrorCallback + ): void { + const geocodeQuery = ProviderHelpers.getGeocodeQueryFromParameter( + query, + MapQuestGeocodeQuery + ); + + if (geocodeQuery.getIp()) { + throw new Error( + "The MapQuest provider does not support IP geolocation, only location geocoding." + ); + } + + this.externalLoader.setOptions({ + method: this.options.method, + protocol: this.options.useSsl ? "https" : "http", + host: this.options.openDomain + ? "open.mapquestapi.com" + : "www.mapquestapi.com", + pathname: "geocoding/v1/address", + }); + + let requestParams: { + location?: string; + boundingBox?: string; + maxResults?: string; + } = { + boundingBox: geocodeQuery.getBounds() + ? `${geocodeQuery.getBounds()?.latitudeNE},${ + geocodeQuery.getBounds()?.longitudeSW + },${geocodeQuery.getBounds()?.latitudeSW},${ + geocodeQuery.getBounds()?.longitudeNE + }` + : undefined, + maxResults: geocodeQuery.getLimit().toString(), + }; + if ((geocodeQuery).getLocation()) { + requestParams = { + ...(geocodeQuery).getLocation(), + ...requestParams, + }; + } else { + requestParams = { + location: geocodeQuery.getText(), + ...requestParams, + }; + } + + requestParams = this.options.method === "GET" ? requestParams : {}; + const params: MapQuestRequestParams = this.withCommonParams(requestParams); + + const body = + this.options.method === "POST" + ? { + location: (geocodeQuery).getLocation() + ? ( + (geocodeQuery).getLocation() + ) + : geocodeQuery.getText(), + options: { + boundingBox: geocodeQuery.getBounds() + ? { + ul: { + lat: geocodeQuery.getBounds()?.latitudeNE, + lng: geocodeQuery.getBounds()?.longitudeSW, + }, + lr: { + lat: geocodeQuery.getBounds()?.latitudeSW, + lng: geocodeQuery.getBounds()?.longitudeNE, + }, + } + : undefined, + maxResults: geocodeQuery.getLimit().toString(), + }, + } + : {}; + + this.executeRequest(params, callback, {}, body, errorCallback); + } + + public geodecode( + latitudeOrQuery: number | string | ReverseQuery | ReverseQueryObject, + longitudeOrCallback: number | string | MapQuestGeocodedResultsCallback, + callbackOrErrorCallback?: MapQuestGeocodedResultsCallback | ErrorCallback, + errorCallback?: ErrorCallback + ): void { + const reverseQuery = ProviderHelpers.getReverseQueryFromParameters( + latitudeOrQuery, + longitudeOrCallback + ); + const reverseCallback = ProviderHelpers.getCallbackFromParameters( + longitudeOrCallback, + callbackOrErrorCallback + ); + const reverseErrorCallback = ProviderHelpers.getErrorCallbackFromParameters( + longitudeOrCallback, + callbackOrErrorCallback, + errorCallback + ); + + this.externalLoader.setOptions({ + method: this.options.method, + protocol: this.options.useSsl ? "https" : "http", + host: this.options.openDomain + ? "open.mapquestapi.com" + : "www.mapquestapi.com", + pathname: "geocoding/v1/reverse", + }); + + const requestParams = + this.options.method === "GET" + ? { + location: `${reverseQuery.getCoordinates().latitude},${ + reverseQuery.getCoordinates().longitude + }`, + } + : {}; + const params: MapQuestRequestParams = this.withCommonParams(requestParams); + + const body = + this.options.method === "POST" + ? { + location: { + latLng: { + lat: reverseQuery.getCoordinates().latitude, + lng: reverseQuery.getCoordinates().longitude, + }, + }, + } + : {}; + + this.executeRequest( + params, + reverseCallback, + {}, + body, + reverseErrorCallback + ); + } + + private withCommonParams( + params: Pick< + MapQuestRequestParams, + "location" | "boundingBox" | "maxResults" + > + ): MapQuestRequestParams { + return { + ...params, + key: this.options.apiKey || "", + jsonpCallback: this.options.useJsonp ? "callback" : undefined, + }; + } + + public executeRequest( + params: ExternalLoaderParams, + callback: MapQuestGeocodedResultsCallback, + headers?: ExternalLoaderHeaders, + body?: ExternalLoaderBody, + errorCallback?: ErrorCallback + ): void { + this.externalLoader.executeRequest( + params, + (data: MapQuestResponse) => { + if (data.info.statuscode !== 0) { + const errorMessage = `An error has occurred (${ + data.info.statuscode + }): ${data.info.messages.join(" / ")}`; + if (errorCallback) { + errorCallback(new ResponseError(errorMessage, data)); + return; + } + setTimeout(() => { + throw new Error(errorMessage); + }); + return; + } + callback( + data.results[0].locations.map((result: MapQuestResult) => + MapQuestProvider.mapToGeocoded(result, data.info.copyright.text) + ) + ); + }, + headers, + body, + errorCallback + ); + } + + public static mapToGeocoded( + result: MapQuestResult, + attribution?: string + ): MapQuestGeocoded { + const latitude = result.latLng.lat; + const longitude = result.latLng.lng; + const streetName = result.street; + const subLocality = result.adminArea6; + const locality = result.adminArea5; + const { postalCode } = result; + const region = result.adminArea4; + const country = result.adminArea1; + const countryCode = result.adminArea1; + const precision = result.geocodeQuality; + const precisionCode = result.geocodeQualityCode; + const { mapUrl } = result; + + const geocoded = MapQuestGeocoded.create({ + coordinates: { + latitude, + longitude, + }, + streetName, + subLocality, + locality, + postalCode, + region, + country, + countryCode, + attribution, + precision, + precisionCode, + mapUrl, + }); + + if (result.adminArea3) { + geocoded.addAdminLevel( + AdminLevel.create({ + level: ADMIN_LEVEL_CODES.STATE_CODE, + name: result.adminArea3, + code: result.adminArea3.length === 2 ? result.adminArea3 : undefined, + }) + ); + } + if (result.adminArea4) { + geocoded.addAdminLevel( + AdminLevel.create({ + level: ADMIN_LEVEL_CODES.COUNTY_CODE, + name: result.adminArea4, + }) + ); + } + + return geocoded; + } +} diff --git a/src/provider/mapquest/index.ts b/src/provider/mapquest/index.ts new file mode 100644 index 0000000..f499690 --- /dev/null +++ b/src/provider/mapquest/index.ts @@ -0,0 +1,7 @@ +export { default as MapQuestGeocoded } from "provider/mapquest/MapQuestGeocoded"; +export { default as MapQuestGeocodeQuery } from "provider/mapquest/MapQuestGeocodeQuery"; +export * from "provider/mapquest/MapQuestGeocodeQuery"; +export { default as MapQuestLocation } from "provider/mapquest/MapQuestLocation"; +export * from "provider/mapquest/MapQuestLocation"; +export { default as MapQuestProvider } from "provider/mapquest/MapQuestProvider"; +export * from "provider/mapquest/MapQuestProvider"; diff --git a/src/provider/nominatim/NominatimGeocodeQuery.ts b/src/provider/nominatim/NominatimGeocodeQuery.ts new file mode 100644 index 0000000..73f71cd --- /dev/null +++ b/src/provider/nominatim/NominatimGeocodeQuery.ts @@ -0,0 +1,72 @@ +import { GeocodeQuery, GeocodeQueryObject } from "query"; + +export interface NominatimGeocodeQueryObject extends GeocodeQueryObject { + readonly countryCodes?: string[]; + readonly excludePlaceIds?: number[]; + readonly bounded?: boolean; +} + +export default class NominatimGeocodeQuery extends GeocodeQuery { + private readonly countryCodes?: string[]; + + private readonly excludePlaceIds?: number[]; + + private readonly bounded?: boolean; + + protected constructor({ + countryCodes, + excludePlaceIds, + bounded, + bounds, + ...geocodeQueryObject + }: NominatimGeocodeQueryObject) { + super({ bounds, ...geocodeQueryObject }); + this.countryCodes = countryCodes; + this.excludePlaceIds = excludePlaceIds; + if (bounded && !bounds) { + throw new Error( + 'The "bounded" parameter can only be used with the "bounds" parameter.' + ); + } + this.bounded = bounded; + } + + public static create( + object: NominatimGeocodeQueryObject + ): NominatimGeocodeQuery { + return new this(object); + } + + public toObject(): NominatimGeocodeQueryObject { + return { + ...super.toObject(), + countryCodes: this.countryCodes, + excludePlaceIds: this.excludePlaceIds, + bounded: this.bounded, + }; + } + + public withCountryCodes(countryCodes: string[]): NominatimGeocodeQuery { + return new NominatimGeocodeQuery({ ...this.toObject(), countryCodes }); + } + + public getCountryCodes(): undefined | string[] { + return this.countryCodes; + } + + public withExcludePlaceIds(excludePlaceIds: number[]): NominatimGeocodeQuery { + return new NominatimGeocodeQuery({ ...this.toObject(), excludePlaceIds }); + } + + public getExcludePlaceIds(): undefined | number[] { + return this.excludePlaceIds; + } + + public withBounded(bounded: boolean): NominatimGeocodeQuery { + return new NominatimGeocodeQuery({ ...this.toObject(), bounded }); + } + + public getBounded(): undefined | boolean { + return this.bounded; + } +} diff --git a/src/provider/nominatim/NominatimGeocoded.ts b/src/provider/nominatim/NominatimGeocoded.ts new file mode 100644 index 0000000..bb1e0ae --- /dev/null +++ b/src/provider/nominatim/NominatimGeocoded.ts @@ -0,0 +1,124 @@ +import Geocoded, { GeocodedObject } from "Geocoded"; + +export interface NominatimGeocodedObject extends GeocodedObject { + readonly displayName?: string; + readonly osmId?: number; + readonly osmType?: string; + readonly category?: string; + readonly type?: string; + readonly attribution?: string; +} + +export default class NominatimGeocoded extends Geocoded { + private readonly displayName?: string; + + private readonly osmId?: number; + + private readonly osmType?: string; + + private readonly category?: string; + + private readonly type?: string; + + private readonly attribution?: string; + + protected constructor({ + displayName, + osmId, + osmType, + category, + type, + attribution, + ...geocodedObject + }: NominatimGeocodedObject) { + super(geocodedObject); + this.displayName = displayName; + this.osmId = osmId; + this.osmType = osmType; + this.category = category; + this.type = type; + this.attribution = attribution; + } + + public static create(object: NominatimGeocodedObject): NominatimGeocoded { + return new this(object); + } + + public toObject(): NominatimGeocodedObject { + return { + ...super.toObject(), + displayName: this.displayName, + osmId: this.osmId, + osmType: this.osmType, + category: this.category, + type: this.type, + attribution: this.attribution, + }; + } + + public withDisplayName(displayName: string): NominatimGeocoded { + return new NominatimGeocoded({ + ...this.toObject(), + displayName, + }); + } + + public getDisplayName(): undefined | string { + return this.displayName; + } + + public withOsmId(osmId: number): NominatimGeocoded { + return new NominatimGeocoded({ + ...this.toObject(), + osmId, + }); + } + + public getOsmId(): undefined | number { + return this.osmId; + } + + public withOsmType(osmType: string): NominatimGeocoded { + return new NominatimGeocoded({ + ...this.toObject(), + osmType, + }); + } + + public getOsmType(): undefined | string { + return this.osmType; + } + + public withCategory(category: string): NominatimGeocoded { + return new NominatimGeocoded({ + ...this.toObject(), + category, + }); + } + + public getCategory(): undefined | string { + return this.category; + } + + public withType(type: string): NominatimGeocoded { + return new NominatimGeocoded({ + ...this.toObject(), + type, + }); + } + + public getType(): undefined | string { + return this.type; + } + + public withAttribution(attribution: string): NominatimGeocoded { + return new NominatimGeocoded({ + ...this.toObject(), + attribution, + }); + } + + public getAttribution(): undefined | string { + return this.attribution; + } +} diff --git a/src/provider/nominatim/NominatimProvider.ts b/src/provider/nominatim/NominatimProvider.ts new file mode 100644 index 0000000..a590c4f --- /dev/null +++ b/src/provider/nominatim/NominatimProvider.ts @@ -0,0 +1,364 @@ +import { + ExternalLoaderBody, + ExternalLoaderHeaders, + ExternalLoaderInterface, + ExternalLoaderParams, +} from "ExternalLoader"; +import { + ErrorCallback, + GeocodedResultsCallback, + NominatimGeocoded, + NominatimReverseQuery, + NominatimReverseQueryObject, + NominatimGeocodeQueryObject, + NominatimGeocodeQuery, + ProviderHelpers, + ProviderInterface, + ProviderOptionsInterface, + defaultProviderOptions, +} from "provider"; +import AdminLevel from "AdminLevel"; +import { ResponseError } from "error"; + +interface NominatimRequestParams { + [param: string]: string | undefined; + readonly q?: string; + readonly lat?: string; + readonly lon?: string; + readonly format: string; + readonly addressdetails: string; + readonly zoom?: string; + readonly limit?: string; + // eslint-disable-next-line camelcase + readonly "accept-language"?: string; + readonly countrycodes?: string; + // eslint-disable-next-line camelcase + readonly exclude_place_ids?: string; + readonly viewbox?: string; + readonly bounded?: string; + readonly jsonpCallback?: string; +} + +interface NominatimErrorResponse { + error: string; +} + +export interface NominatimResult { + // eslint-disable-next-line camelcase + place_id: number; + licence: string; + // eslint-disable-next-line camelcase + osm_type: string; + // eslint-disable-next-line camelcase + osm_id: number; + boundingbox: [string, string, string, string]; + lat: string; + lon: string; + // eslint-disable-next-line camelcase + display_name: string; + category: string; + type: string; + importance: number; + icon: string; + address: { + attraction?: string; + // eslint-disable-next-line camelcase + house_number?: string; + road?: string; + pedestrian?: string; + neighbourhood?: string; + suburb?: string; + city?: string; + town?: string; + village?: string; + hamlet?: string; + state?: string; + county?: string; + postcode?: string; + country?: string; + // eslint-disable-next-line camelcase + country_code?: string; + }; +} + +export type NominatimResponse = + | NominatimErrorResponse + | NominatimResult + | NominatimResult[]; + +export interface NominatimProviderOptionsInterface + extends ProviderOptionsInterface { + readonly host?: string; + readonly userAgent: string; + readonly referer?: string; +} + +export const defaultNominatimProviderOptions = { + ...defaultProviderOptions, + host: "nominatim.openstreetmap.org", + userAgent: "", +}; + +type NominatimGeocodedResultsCallback = GeocodedResultsCallback< + NominatimGeocoded +>; + +export default class NominatimProvider + implements ProviderInterface { + private externalLoader: ExternalLoaderInterface; + + private options: NominatimProviderOptionsInterface; + + public constructor( + _externalLoader: ExternalLoaderInterface, + options: NominatimProviderOptionsInterface = defaultNominatimProviderOptions + ) { + this.externalLoader = _externalLoader; + this.options = { ...defaultNominatimProviderOptions, ...options }; + if ( + this.options.host === defaultNominatimProviderOptions.host && + !this.options.userAgent + ) { + throw new Error( + 'An User-Agent identifying your application is required for the OpenStreetMap / Nominatim provider when using the default host. Please add it in the "userAgent" option.' + ); + } + } + + public geocode( + query: string | NominatimGeocodeQuery | NominatimGeocodeQueryObject, + callback: NominatimGeocodedResultsCallback, + errorCallback?: ErrorCallback + ): void { + const geocodeQuery = ProviderHelpers.getGeocodeQueryFromParameter( + query, + NominatimGeocodeQuery + ); + + if (geocodeQuery.getIp()) { + throw new Error( + "The OpenStreetMap / Nominatim provider does not support IP geolocation, only location geocoding." + ); + } + + this.externalLoader.setOptions({ + protocol: this.options.useSsl ? "https" : "http", + host: this.options.host, + pathname: "search", + }); + + const params: NominatimRequestParams = this.withCommonParams( + { + q: geocodeQuery.getText(), + limit: geocodeQuery.getLimit().toString(), + countrycodes: (geocodeQuery).getCountryCodes() + ? (geocodeQuery).getCountryCodes()?.join(",") + : undefined, + exclude_place_ids: (( + geocodeQuery + )).getExcludePlaceIds() + ? (geocodeQuery) + .getExcludePlaceIds() + ?.join(",") + : undefined, + viewbox: geocodeQuery.getBounds() + ? `${geocodeQuery.getBounds()?.longitudeSW},${ + geocodeQuery.getBounds()?.latitudeSW + },${geocodeQuery.getBounds()?.longitudeNE},${ + geocodeQuery.getBounds()?.latitudeNE + }` + : undefined, + bounded: (geocodeQuery).getBounded() + ? (geocodeQuery).getBounded()?.toString() + : undefined, + }, + geocodeQuery + ); + + this.executeRequest(params, callback, this.getHeaders(), {}, errorCallback); + } + + public geodecode( + latitudeOrQuery: + | number + | string + | NominatimReverseQuery + | NominatimReverseQueryObject, + longitudeOrCallback: number | string | NominatimGeocodedResultsCallback, + callbackOrErrorCallback?: NominatimGeocodedResultsCallback | ErrorCallback, + errorCallback?: ErrorCallback + ): void { + const reverseQuery = ProviderHelpers.getReverseQueryFromParameters( + latitudeOrQuery, + longitudeOrCallback, + NominatimReverseQuery + ); + const reverseCallback = ProviderHelpers.getCallbackFromParameters( + longitudeOrCallback, + callbackOrErrorCallback + ); + const reverseErrorCallback = ProviderHelpers.getErrorCallbackFromParameters( + longitudeOrCallback, + callbackOrErrorCallback, + errorCallback + ); + + this.externalLoader.setOptions({ + protocol: this.options.useSsl ? "https" : "http", + host: this.options.host, + pathname: "reverse", + }); + + const params: NominatimRequestParams = this.withCommonParams( + { + lat: reverseQuery.getCoordinates().latitude.toString(), + lon: reverseQuery.getCoordinates().longitude.toString(), + zoom: + (reverseQuery).getZoom()?.toString() || "18", + }, + reverseQuery + ); + + this.executeRequest( + params, + reverseCallback, + this.getHeaders(), + {}, + reverseErrorCallback + ); + } + + private withCommonParams( + params: Partial, + query: NominatimGeocodeQuery | NominatimReverseQuery + ): NominatimRequestParams { + return { + ...params, + format: "jsonv2", + addressdetails: "1", + jsonpCallback: this.options.useJsonp ? "json_callback" : undefined, + "accept-language": query.getLocale(), + }; + } + + private getHeaders(): ExternalLoaderHeaders { + return { + "User-Agent": this.options.userAgent || "", + Referer: this.options.referer, + }; + } + + public executeRequest( + params: ExternalLoaderParams, + callback: NominatimGeocodedResultsCallback, + headers?: ExternalLoaderHeaders, + body?: ExternalLoaderBody, + errorCallback?: ErrorCallback + ): void { + this.externalLoader.executeRequest( + params, + (data: NominatimResponse) => { + let results = data; + if (!Array.isArray(data)) { + if ((data).error) { + const errorMessage = `An error has occurred: ${ + (data).error + }`; + if (errorCallback) { + errorCallback(new ResponseError(errorMessage, data)); + return; + } + setTimeout(() => { + throw new Error(errorMessage); + }); + return; + } + results = [data]; + } + callback( + (results).map((result: NominatimResult) => + NominatimProvider.mapToGeocoded(result) + ) + ); + }, + headers, + body, + errorCallback + ); + } + + public static mapToGeocoded(result: NominatimResult): NominatimGeocoded { + const latitude = parseFloat(result.lat); + const longitude = parseFloat(result.lon); + const displayName = result.display_name; + const streetNumber = result.address.house_number; + const streetName = result.address.road || result.address.pedestrian; + const subLocality = result.address.suburb; + let locality: string | undefined; + const postalCode = result.address.postcode + ? result.address.postcode.split(";")[0] + : undefined; + const region = result.address.state; + const { country } = result.address; + const countryCode = result.address.country_code; + const osmId = result.osm_id; + const osmType = result.osm_type; + const { category } = result; + const { type } = result; + const attribution = result.licence; + + const localityTypes: ("city" | "town" | "village" | "hamlet")[] = [ + "city", + "town", + "village", + "hamlet", + ]; + localityTypes.forEach((localityType) => { + if (result.address[localityType] && !locality) { + locality = result.address[localityType]; + } + }); + + let geocoded = NominatimGeocoded.create({ + coordinates: { + latitude, + longitude, + }, + displayName, + streetNumber, + streetName, + subLocality, + locality, + postalCode, + region, + country, + countryCode, + osmId, + osmType, + category, + type, + attribution, + }); + + geocoded = geocoded.withBounds({ + latitudeSW: parseFloat(result.boundingbox[0]), + longitudeSW: parseFloat(result.boundingbox[2]), + latitudeNE: parseFloat(result.boundingbox[1]), + longitudeNE: parseFloat(result.boundingbox[3]), + }); + + const adminLevels: ("state" | "county")[] = ["state", "county"]; + adminLevels.forEach((adminLevel, level) => { + if (result.address[adminLevel]) { + geocoded.addAdminLevel( + AdminLevel.create({ + level: level + 1, + name: result.address[adminLevel] || "", + }) + ); + } + }); + + return geocoded; + } +} diff --git a/src/provider/nominatim/NominatimReverseQuery.ts b/src/provider/nominatim/NominatimReverseQuery.ts new file mode 100644 index 0000000..b0718bf --- /dev/null +++ b/src/provider/nominatim/NominatimReverseQuery.ts @@ -0,0 +1,38 @@ +import { ReverseQuery, ReverseQueryObject } from "query"; + +export interface NominatimReverseQueryObject extends ReverseQueryObject { + readonly zoom?: number; +} + +export default class NominatimReverseQuery extends ReverseQuery { + private readonly zoom?: number; + + protected constructor({ + zoom, + ...reverseQueryObject + }: NominatimReverseQueryObject) { + super(reverseQueryObject); + this.zoom = zoom; + } + + public static create( + object: NominatimReverseQueryObject + ): NominatimReverseQuery { + return new this(object); + } + + public toObject(): NominatimReverseQueryObject { + return { + ...super.toObject(), + zoom: this.zoom, + }; + } + + public withZoom(zoom: number): NominatimReverseQuery { + return new NominatimReverseQuery({ ...this.toObject(), zoom }); + } + + public getZoom(): undefined | number { + return this.zoom; + } +} diff --git a/src/provider/nominatim/index.ts b/src/provider/nominatim/index.ts new file mode 100644 index 0000000..38cc645 --- /dev/null +++ b/src/provider/nominatim/index.ts @@ -0,0 +1,20 @@ +export { default as NominatimGeocoded } from "provider/nominatim/NominatimGeocoded"; +export { default as OpenStreetMapGeocoded } from "provider/nominatim/NominatimGeocoded"; +export * from "provider/nominatim/NominatimGeocoded"; +export { NominatimGeocodedObject as OpenStreetMapGeocodedObject } from "provider/nominatim/NominatimGeocoded"; +export { default as NominatimGeocodeQuery } from "provider/nominatim/NominatimGeocodeQuery"; +export { default as OpenStreetMapGeocodeQuery } from "provider/nominatim/NominatimGeocodeQuery"; +export * from "provider/nominatim/NominatimGeocodeQuery"; +export { NominatimGeocodeQueryObject as OpenStreetMapGeocodeQueryObject } from "provider/nominatim/NominatimGeocodeQuery"; +export { default as NominatimProvider } from "provider/nominatim/NominatimProvider"; +export { default as OpenStreetMapProvider } from "provider/nominatim/NominatimProvider"; +export * from "provider/nominatim/NominatimProvider"; +export { + NominatimProviderOptionsInterface as OpenStreetMapProviderOptionsInterface, + NominatimResult as OpenStreetMapResult, + defaultNominatimProviderOptions as defaultOpenStreetMapProviderOptions, +} from "provider/nominatim/NominatimProvider"; +export { default as NominatimReverseQuery } from "provider/nominatim/NominatimReverseQuery"; +export { default as OpenStreetMapReverseQuery } from "provider/nominatim/NominatimReverseQuery"; +export * from "provider/nominatim/NominatimReverseQuery"; +export { NominatimReverseQueryObject as OpenStreetMapReverseQueryObject } from "provider/nominatim/NominatimReverseQuery"; diff --git a/src/provider/opencage/OpenCageGeocodeQuery.ts b/src/provider/opencage/OpenCageGeocodeQuery.ts new file mode 100644 index 0000000..d6281eb --- /dev/null +++ b/src/provider/opencage/OpenCageGeocodeQuery.ts @@ -0,0 +1,97 @@ +import { GeocodeQuery, GeocodeQueryObject } from "query"; +import { Coordinates } from "types"; + +export interface OpenCageGeocodeQueryObject extends GeocodeQueryObject { + readonly countryCodes?: string[]; + readonly proximity?: Coordinates; + readonly minPrecision?: number; + readonly noRecord?: boolean; +} + +export default class OpenCageGeocodeQuery extends GeocodeQuery { + private readonly countryCodes?: string[]; + + private readonly proximity?: Coordinates; + + private readonly minPrecision?: number; + + private readonly noRecord?: boolean; + + protected constructor({ + countryCodes, + proximity, + minPrecision, + noRecord, + ...geocodeQueryObject + }: OpenCageGeocodeQueryObject) { + super(geocodeQueryObject); + this.countryCodes = countryCodes; + if (proximity && (!proximity.latitude || !proximity.longitude)) { + throw new Error( + 'The "proximity" parameter must be an object with the keys: "latitude", "longitude".' + ); + } + this.proximity = proximity; + if ( + minPrecision && + (minPrecision.toString() !== + parseInt(minPrecision.toString(), 10).toString() || + minPrecision < 1 || + minPrecision > 10) + ) { + throw new Error( + 'The "minPrecision" parameter must be an integer from 1 to 10.' + ); + } + this.minPrecision = minPrecision; + this.noRecord = noRecord; + } + + public static create( + object: OpenCageGeocodeQueryObject + ): OpenCageGeocodeQuery { + return new this(object); + } + + public toObject(): OpenCageGeocodeQueryObject { + return { + ...super.toObject(), + countryCodes: this.countryCodes, + proximity: this.proximity, + minPrecision: this.minPrecision, + noRecord: this.noRecord, + }; + } + + public withCountryCodes(countryCodes: string[]): OpenCageGeocodeQuery { + return new OpenCageGeocodeQuery({ ...this.toObject(), countryCodes }); + } + + public getCountryCodes(): undefined | string[] { + return this.countryCodes; + } + + public withProximity(proximity: Coordinates): OpenCageGeocodeQuery { + return new OpenCageGeocodeQuery({ ...this.toObject(), proximity }); + } + + public getProximity(): undefined | Coordinates { + return this.proximity; + } + + public withMinPrecision(minPrecision: number): OpenCageGeocodeQuery { + return new OpenCageGeocodeQuery({ ...this.toObject(), minPrecision }); + } + + public getMinPrecision(): undefined | number { + return this.minPrecision; + } + + public withNoRecord(noRecord: boolean): OpenCageGeocodeQuery { + return new OpenCageGeocodeQuery({ ...this.toObject(), noRecord }); + } + + public getNoRecord(): undefined | boolean { + return this.noRecord; + } +} diff --git a/src/provider/opencage/OpenCageGeocoded.ts b/src/provider/opencage/OpenCageGeocoded.ts new file mode 100644 index 0000000..98f14d7 --- /dev/null +++ b/src/provider/opencage/OpenCageGeocoded.ts @@ -0,0 +1,141 @@ +import Geocoded, { GeocodedObject } from "Geocoded"; + +export interface OpenCageGeocodedObject extends GeocodedObject { + readonly callingCode?: number; + readonly flag?: string; + readonly precision?: number; + readonly mgrs?: string; + readonly maidenhead?: string; + readonly geohash?: string; + readonly what3words?: string; +} + +export default class OpenCageGeocoded extends Geocoded { + private readonly callingCode?: number; + + private readonly flag?: string; + + private readonly precision?: number; + + private readonly mgrs?: string; + + private readonly maidenhead?: string; + + private readonly geohash?: string; + + private readonly what3words?: string; + + protected constructor({ + callingCode, + flag, + precision, + mgrs, + maidenhead, + geohash, + what3words, + ...geocodedObject + }: OpenCageGeocodedObject) { + super(geocodedObject); + this.callingCode = callingCode; + this.flag = flag; + this.precision = precision; + this.mgrs = mgrs; + this.maidenhead = maidenhead; + this.geohash = geohash; + this.what3words = what3words; + } + + public static create(object: OpenCageGeocodedObject): OpenCageGeocoded { + return new this(object); + } + + public toObject(): OpenCageGeocodedObject { + return { + ...super.toObject(), + callingCode: this.callingCode, + flag: this.flag, + precision: this.precision, + mgrs: this.mgrs, + maidenhead: this.maidenhead, + geohash: this.geohash, + what3words: this.what3words, + }; + } + + public withCallingCode(callingCode: number): OpenCageGeocoded { + return new OpenCageGeocoded({ + ...this.toObject(), + callingCode, + }); + } + + public getCallingCode(): undefined | number { + return this.callingCode; + } + + public withFlag(flag: string): OpenCageGeocoded { + return new OpenCageGeocoded({ + ...this.toObject(), + flag, + }); + } + + public getFlag(): undefined | string { + return this.flag; + } + + public withPrecision(precision: number): OpenCageGeocoded { + return new OpenCageGeocoded({ + ...this.toObject(), + precision, + }); + } + + public getPrecision(): undefined | number { + return this.precision; + } + + public withMgrs(mgrs: string): OpenCageGeocoded { + return new OpenCageGeocoded({ + ...this.toObject(), + mgrs, + }); + } + + public getMgrs(): undefined | string { + return this.mgrs; + } + + public withMaidenhead(maidenhead: string): OpenCageGeocoded { + return new OpenCageGeocoded({ + ...this.toObject(), + maidenhead, + }); + } + + public getMaidenhead(): undefined | string { + return this.maidenhead; + } + + public withGeohash(geohash: string): OpenCageGeocoded { + return new OpenCageGeocoded({ + ...this.toObject(), + geohash, + }); + } + + public getGeohash(): undefined | string { + return this.geohash; + } + + public withWhat3words(what3words: string): OpenCageGeocoded { + return new OpenCageGeocoded({ + ...this.toObject(), + what3words, + }); + } + + public getWhat3words(): undefined | string { + return this.what3words; + } +} diff --git a/src/provider/opencage/OpenCageProvider.ts b/src/provider/opencage/OpenCageProvider.ts new file mode 100644 index 0000000..9ce5b22 --- /dev/null +++ b/src/provider/opencage/OpenCageProvider.ts @@ -0,0 +1,580 @@ +import { + ExternalLoaderBody, + ExternalLoaderHeaders, + ExternalLoaderInterface, + ExternalLoaderParams, +} from "ExternalLoader"; +import { + ErrorCallback, + GeocodedResultsCallback, + OpenCageGeocoded, + OpenCageGeocodeQuery, + OpenCageGeocodeQueryObject, + OpenCageReverseQuery, + OpenCageReverseQueryObject, + ProviderHelpers, + ProviderInterface, + ProviderOptionsInterface, + defaultProviderOptions, +} from "provider"; +import AdminLevel from "AdminLevel"; +import { ResponseError } from "error"; + +interface OpenCageRequestParams { + [param: string]: string | undefined; + readonly key: string; + readonly q: string; + readonly countrycode?: string; + readonly language?: string; + readonly limit?: string; + readonly bounds?: string; + readonly proximity?: string; + // eslint-disable-next-line camelcase + readonly min_confidence?: string; + // eslint-disable-next-line camelcase + readonly no_record?: string; + readonly jsonpCallback?: string; +} + +interface OpenCageCoordinates { + lat: number; + lng: number; +} + +interface OpenCageSun { + apparent: number; + astronomical: number; + civil: number; + nautical: number; +} + +export interface OpenCageResult { + annotations: { + callingcode: number; + currency: { + // eslint-disable-next-line camelcase + alternate_symbols: string[]; + // eslint-disable-next-line camelcase + decimal_mark: string; + // eslint-disable-next-line camelcase + disambiguate_symbol?: string; + // eslint-disable-next-line camelcase + html_entity: string; + // eslint-disable-next-line camelcase + iso_code: string; + // eslint-disable-next-line camelcase + iso_numeric: string; + name: string; + // eslint-disable-next-line camelcase + smallest_denomination: number; + subunit: string; + // eslint-disable-next-line camelcase + subunit_to_unit: number; + symbol: string; + // eslint-disable-next-line camelcase + symbol_first: number; + // eslint-disable-next-line camelcase + thousands_separator: string; + }; + DMS: { + lat: string; + lng: string; + }; + FIPS?: { + state?: string; + county?: string; + }; + flag: string; + geohash?: string; + ITM?: { + easting: string; + northing: string; + }; + Maidenhead?: string; + Mercator: { + x: number; + y: number; + }; + MGRS?: string; + OSM: { + // eslint-disable-next-line camelcase + edit_url?: string; + // eslint-disable-next-line camelcase + note_url: string; + url: string; + }; + qibla: number; + roadinfo: { + // eslint-disable-next-line camelcase + drive_on: "left" | "right"; + road?: string; + // eslint-disable-next-line camelcase + road_type?: string; + // eslint-disable-next-line camelcase + road_reference?: string; + // eslint-disable-next-line camelcase + road_reference_intl?: string; + // eslint-disable-next-line camelcase + speed_in: "km/h" | "mph"; + }; + sun: { + rise: OpenCageSun; + set: OpenCageSun; + }; + timezone: { + name: string; + // eslint-disable-next-line camelcase + now_in_dst: number; + // eslint-disable-next-line camelcase + offset_sec: number; + // eslint-disable-next-line camelcase + offset_string: string; + // eslint-disable-next-line camelcase + short_name: string; + }; + // eslint-disable-next-line camelcase + UN_M49: { + regions: { + [region: string]: string; + }; + // eslint-disable-next-line camelcase + statistical_groupings: ("LDC" | "LEDC" | "LLDC" | "MEDC" | "SIDS")[]; + }; + what3words?: { + words: string; + }; + wikidata?: string; + }; + bounds: { + northeast: OpenCageCoordinates; + southwest: OpenCageCoordinates; + }; + components: { + "ISO_3166-1_alpha-2"?: string; + "ISO_3166-1_alpha-3"?: string; + _category: + | "agriculture" + | "building" + | "castle" + | "commerce" + | "construction" + | "education" + | "financial" + | "government" + | "health" + | "industrial" + | "military" + | "natural/water" + | "outdoors/recreation" + | "place" + | "place_of_worship" + | "postcode" + | "road" + | "social" + | "transportation" + | "travel/tourism" + | "unknown"; + _type: string; + castle?: string; + city?: string; + // eslint-disable-next-line camelcase + city_district?: string; + continent?: + | "Africa" + | "Antarctica" + | "Asia" + | "Europe" + | "Oceania" + | "North America" + | "South America"; + country?: string; + // eslint-disable-next-line camelcase + country_code?: string; + county?: string; + // eslint-disable-next-line camelcase + county_code?: string; + croft?: string; + district?: string; + footway?: string; + hamlet?: string; + // eslint-disable-next-line camelcase + house_number?: string; + houses?: string; + locality?: string; + municipality?: string; + neighbourhood?: string; + path?: string; + pedestrian?: string; + // eslint-disable-next-line camelcase + political_union?: string; + postcode?: string; + quarter?: string; + residential?: string; + road?: string; + // eslint-disable-next-line camelcase + road_reference?: string; + // eslint-disable-next-line camelcase + road_reference_intl?: string; + // eslint-disable-next-line camelcase + road_type?: string; + state?: string; + // eslint-disable-next-line camelcase + state_code?: string; + // eslint-disable-next-line camelcase + state_district?: string; + street?: string; + // eslint-disable-next-line camelcase + street_name?: string; + subdivision?: string; + suburb?: string; + town?: string; + village?: string; + }; + confidence: number; + formatted: string; + geometry: OpenCageCoordinates; +} + +export interface OpenCageResponse { + documentation: string; + licences: { + name: string; + url: string; + }[]; + rate: { + limit: number; + remaining: number; + reset: number; + }; + results: OpenCageResult[]; + status: { + code: 200 | 400 | 401 | 402 | 403 | 404 | 405 | 408 | 410 | 429 | 503; + message: string; + }; + // eslint-disable-next-line camelcase + stay_informed: { + blog: string; + twitter: string; + }; + thanks: string; + timestamp: { + // eslint-disable-next-line camelcase + created_http: string; + // eslint-disable-next-line camelcase + created_unix: number; + }; + // eslint-disable-next-line camelcase + total_results: number; +} + +export interface OpenCageProviderOptionsInterface + extends ProviderOptionsInterface { + readonly apiKey: string; + readonly countryCodes?: string[]; +} + +export const defaultOpenCageProviderOptions = { + ...defaultProviderOptions, + apiKey: "", +}; + +type OpenCageGeocodedResultsCallback = GeocodedResultsCallback< + OpenCageGeocoded +>; + +export default class OpenCageProvider + implements ProviderInterface { + private externalLoader: ExternalLoaderInterface; + + private options: OpenCageProviderOptionsInterface; + + public constructor( + _externalLoader: ExternalLoaderInterface, + options: OpenCageProviderOptionsInterface = defaultOpenCageProviderOptions + ) { + this.externalLoader = _externalLoader; + this.options = { ...defaultOpenCageProviderOptions, ...options }; + if (!this.options.apiKey) { + throw new Error( + 'An API key is required for the OpenCage provider. Please add it in the "apiKey" option.' + ); + } + } + + public geocode( + query: string | OpenCageGeocodeQuery | OpenCageGeocodeQueryObject, + callback: OpenCageGeocodedResultsCallback, + errorCallback?: ErrorCallback + ): void { + const geocodeQuery = ProviderHelpers.getGeocodeQueryFromParameter( + query, + OpenCageGeocodeQuery + ); + + if (geocodeQuery.getIp()) { + throw new Error( + "The OpenCage provider does not support IP geolocation, only location geocoding." + ); + } + + this.externalLoader.setOptions({ + protocol: this.options.useSsl ? "https" : "http", + host: "api.opencagedata.com", + pathname: "geocode/v1/json", + }); + + const params: OpenCageRequestParams = this.withCommonParams( + { + q: geocodeQuery.getText() || "", + bounds: geocodeQuery.getBounds() + ? `${geocodeQuery.getBounds()?.longitudeSW},${ + geocodeQuery.getBounds()?.latitudeSW + },${geocodeQuery.getBounds()?.longitudeNE},${ + geocodeQuery.getBounds()?.latitudeNE + }` + : undefined, + proximity: (geocodeQuery).getProximity() + ? `${(geocodeQuery).getProximity()?.latitude},${ + (geocodeQuery).getProximity()?.longitude + }` + : undefined, + }, + geocodeQuery + ); + + this.executeRequest(params, callback, {}, {}, errorCallback); + } + + public geodecode( + latitudeOrQuery: + | number + | string + | OpenCageReverseQuery + | OpenCageReverseQueryObject, + longitudeOrCallback: number | string | OpenCageGeocodedResultsCallback, + callbackOrErrorCallback?: OpenCageGeocodedResultsCallback | ErrorCallback, + errorCallback?: ErrorCallback + ): void { + const reverseQuery = ProviderHelpers.getReverseQueryFromParameters( + latitudeOrQuery, + longitudeOrCallback, + OpenCageReverseQuery + ); + const reverseCallback = ProviderHelpers.getCallbackFromParameters( + longitudeOrCallback, + callbackOrErrorCallback + ); + const reverseErrorCallback = ProviderHelpers.getErrorCallbackFromParameters( + longitudeOrCallback, + callbackOrErrorCallback, + errorCallback + ); + + this.externalLoader.setOptions({ + protocol: this.options.useSsl ? "https" : "http", + host: "api.opencagedata.com", + pathname: "geocode/v1/json", + }); + + const params: OpenCageRequestParams = this.withCommonParams( + { + q: `${reverseQuery.getCoordinates().latitude},${ + reverseQuery.getCoordinates().longitude + }`, + }, + reverseQuery + ); + + this.executeRequest(params, reverseCallback, {}, {}, reverseErrorCallback); + } + + private withCommonParams( + params: Pick, + query: OpenCageGeocodeQuery | OpenCageReverseQuery + ): OpenCageRequestParams { + return { + ...params, + key: this.options.apiKey || "", + countrycode: query.getCountryCodes() + ? query.getCountryCodes()?.join(",") + : this.options.countryCodes?.join(","), + language: query.getLocale(), + limit: query.getLimit().toString(), + min_confidence: query.getMinPrecision()?.toString(), + no_record: query.getNoRecord()?.toString(), + jsonpCallback: this.options.useJsonp ? "jsonp" : undefined, + }; + } + + public executeRequest( + params: ExternalLoaderParams, + callback: OpenCageGeocodedResultsCallback, + headers?: ExternalLoaderHeaders, + body?: ExternalLoaderBody, + errorCallback?: ErrorCallback + ): void { + this.externalLoader.executeRequest( + params, + (data: OpenCageResponse) => { + callback( + data.results.map((result: OpenCageResult) => + OpenCageProvider.mapToGeocoded(result) + ) + ); + }, + headers, + body, + (error) => { + const response = error.getResponse(); + response.json().then((data: OpenCageResponse) => { + if (data.status) { + let errorMessage: string; + switch (data.status.code) { + case 400: + errorMessage = `Invalid request (400): ${data.status.message}`; + break; + case 401: + errorMessage = `Unable to authenticate (401): ${data.status.message}`; + break; + case 402: + errorMessage = `Quota exceeded (402): ${data.status.message}`; + break; + case 403: + errorMessage = `Forbidden (403): ${data.status.message}`; + break; + case 404: + errorMessage = `Invalid API endpoint (404): ${data.status.message}`; + break; + case 405: + errorMessage = `Method not allowed (405): ${data.status.message}`; + break; + case 408: + errorMessage = `Timeout (408): ${data.status.message}`; + break; + case 410: + errorMessage = `Request too long (410): ${data.status.message}`; + break; + case 429: + errorMessage = `Too many requests (429): ${data.status.message}`; + break; + case 503: + errorMessage = `Internal server error (503): ${data.status.message}`; + break; + default: + errorMessage = `Error (${data.status.code}): ${data.status.message}`; + } + if (errorCallback) { + errorCallback(new ResponseError(errorMessage, data)); + return; + } + setTimeout(() => { + throw new Error(errorMessage); + }); + } + }); + } + ); + } + + public static mapToGeocoded(result: OpenCageResult): OpenCageGeocoded { + const latitude = result.geometry.lat; + const longitude = result.geometry.lng; + const formattedAddress = result.formatted; + const streetNumber = result.components.house_number; + const postalCode = result.components.postcode; + const region = result.components.state; + const { country } = result.components; + const countryCode = result.components.country_code; + const timezone = result.annotations.timezone.name; + const callingCode = result.annotations.callingcode; + const { flag } = result.annotations; + const precision = result.confidence; + const mgrs = result.annotations.MGRS; + const maidenhead = result.annotations.Maidenhead; + const { geohash } = result.annotations; + const what3words = result.annotations.what3words?.words; + + const streetName = + result.components.road || + result.components.footway || + result.components.street || + result.components.street_name || + result.components.residential || + result.components.path || + result.components.pedestrian || + result.components.road_reference || + result.components.road_reference_intl; + + const subLocality = + result.components.neighbourhood || + result.components.suburb || + result.components.city_district || + result.components.district || + result.components.quarter || + result.components.houses || + result.components.subdivision; + + const locality = + result.components.city || + result.components.town || + result.components.municipality || + result.components.village || + result.components.hamlet || + result.components.locality || + result.components.croft; + + let geocoded = OpenCageGeocoded.create({ + coordinates: { + latitude, + longitude, + }, + formattedAddress, + streetNumber, + streetName, + subLocality, + locality, + postalCode, + region, + country, + countryCode, + timezone, + callingCode, + flag, + precision, + mgrs, + maidenhead, + geohash, + what3words, + }); + + if (result.bounds) { + geocoded = geocoded.withBounds({ + latitudeSW: result.bounds.southwest.lat, + longitudeSW: result.bounds.southwest.lng, + latitudeNE: result.bounds.northeast.lat, + longitudeNE: result.bounds.northeast.lng, + }); + } + + const adminLevels: { + nameKey: "state" | "county"; + codeKey: "state_code" | "county_code"; + }[] = [ + { nameKey: "state", codeKey: "state_code" }, + { nameKey: "county", codeKey: "county_code" }, + ]; + adminLevels.forEach(({ nameKey, codeKey }, level) => { + if (result.components[nameKey]) { + geocoded.addAdminLevel( + AdminLevel.create({ + level: level + 1, + name: result.components[nameKey] || "", + code: result.components[codeKey] || undefined, + }) + ); + } + }); + + return geocoded; + } +} diff --git a/src/provider/opencage/OpenCageReverseQuery.ts b/src/provider/opencage/OpenCageReverseQuery.ts new file mode 100644 index 0000000..7cb3193 --- /dev/null +++ b/src/provider/opencage/OpenCageReverseQuery.ts @@ -0,0 +1,77 @@ +import { ReverseQuery, ReverseQueryObject } from "query"; + +export interface OpenCageReverseQueryObject extends ReverseQueryObject { + readonly countryCodes?: string[]; + readonly minPrecision?: number; + readonly noRecord?: boolean; +} + +export default class OpenCageReverseQuery extends ReverseQuery { + private readonly countryCodes?: string[]; + + private readonly minPrecision?: number; + + private readonly noRecord?: boolean; + + protected constructor({ + countryCodes, + minPrecision, + noRecord, + ...reverseQueryObject + }: OpenCageReverseQueryObject) { + super(reverseQueryObject); + this.countryCodes = countryCodes; + if ( + minPrecision && + (minPrecision.toString() !== + parseInt(minPrecision.toString(), 10).toString() || + minPrecision < 1 || + minPrecision > 10) + ) { + throw new Error( + 'The "minPrecision" parameter must be an integer from 1 to 10.' + ); + } + this.minPrecision = minPrecision; + this.noRecord = noRecord; + } + + public static create( + object: OpenCageReverseQueryObject + ): OpenCageReverseQuery { + return new this(object); + } + + public toObject(): OpenCageReverseQueryObject { + return { + ...super.toObject(), + countryCodes: this.countryCodes, + minPrecision: this.minPrecision, + noRecord: this.noRecord, + }; + } + + public withCountryCodes(countryCodes: string[]): OpenCageReverseQuery { + return new OpenCageReverseQuery({ ...this.toObject(), countryCodes }); + } + + public getCountryCodes(): undefined | string[] { + return this.countryCodes; + } + + public withMinPrecision(minPrecision: number): OpenCageReverseQuery { + return new OpenCageReverseQuery({ ...this.toObject(), minPrecision }); + } + + public getMinPrecision(): undefined | number { + return this.minPrecision; + } + + public withNoRecord(noRecord: boolean): OpenCageReverseQuery { + return new OpenCageReverseQuery({ ...this.toObject(), noRecord }); + } + + public getNoRecord(): undefined | boolean { + return this.noRecord; + } +} diff --git a/src/provider/opencage/index.ts b/src/provider/opencage/index.ts new file mode 100644 index 0000000..0c69151 --- /dev/null +++ b/src/provider/opencage/index.ts @@ -0,0 +1,7 @@ +export { default as OpenCageGeocoded } from "provider/opencage/OpenCageGeocoded"; +export { default as OpenCageGeocodeQuery } from "provider/opencage/OpenCageGeocodeQuery"; +export * from "provider/opencage/OpenCageGeocodeQuery"; +export { default as OpenCageProvider } from "provider/opencage/OpenCageProvider"; +export * from "provider/opencage/OpenCageProvider"; +export { default as OpenCageReverseQuery } from "provider/opencage/OpenCageReverseQuery"; +export * from "provider/opencage/OpenCageReverseQuery"; diff --git a/src/provider/yandex/YandexGeocodeQuery.ts b/src/provider/yandex/YandexGeocodeQuery.ts new file mode 100644 index 0000000..d2cb988 --- /dev/null +++ b/src/provider/yandex/YandexGeocodeQuery.ts @@ -0,0 +1,110 @@ +import { GeocodeQuery, GeocodeQueryObject } from "query"; +import { Coordinates } from "types"; + +interface YandexSpan { + spanLatitude: string; + spanLongitude: string; +} + +export interface YandexGeocodeQueryObject extends GeocodeQueryObject { + readonly bounded?: boolean; + readonly proximity?: Coordinates; + readonly span?: YandexSpan; + readonly skip?: number; +} + +export default class YandexGeocodeQuery extends GeocodeQuery { + private readonly bounded?: boolean; + + private readonly proximity?: Coordinates; + + private readonly span?: YandexSpan; + + private readonly skip?: number; + + protected constructor({ + bounded, + proximity, + span, + skip, + bounds, + ...geocodeQueryObject + }: YandexGeocodeQueryObject) { + super({ bounds, ...geocodeQueryObject }); + if (bounded && (!bounds || !proximity)) { + throw new Error( + 'The "bounded" parameter can only be used with the "bounds" or "proximity" parameter.' + ); + } + this.bounded = bounded; + if (proximity && (!proximity.latitude || !proximity.longitude)) { + throw new Error( + 'The "proximity" parameter must be an object with the keys: "latitude", "longitude".' + ); + } + this.proximity = proximity; + if (span && !proximity) { + throw new Error( + 'The "proximity" parameter must be defined to use the "span" parameter.' + ); + } + if (span && (!span.spanLatitude || !span.spanLongitude)) { + throw new Error( + 'The "span" parameter must be an object with the keys: "spanLatitude", "spanLongitude".' + ); + } + this.span = span; + if (bounds && proximity) { + throw new Error( + 'The "bounds" and "proximity" parameters cannot be used at the same time.' + ); + } + this.skip = skip; + } + + public static create(object: YandexGeocodeQueryObject): YandexGeocodeQuery { + return new this(object); + } + + public toObject(): YandexGeocodeQueryObject { + return { + ...super.toObject(), + bounded: this.bounded, + proximity: this.proximity, + span: this.span, + skip: this.skip, + }; + } + + public withBounded(bounded: boolean): YandexGeocodeQuery { + return new YandexGeocodeQuery({ ...this.toObject(), bounded }); + } + + public getBounded(): undefined | boolean { + return this.bounded; + } + + public withProximity(proximity: Coordinates): YandexGeocodeQuery { + return new YandexGeocodeQuery({ ...this.toObject(), proximity }); + } + + public getProximity(): undefined | Coordinates { + return this.proximity; + } + + public withSpan(span: YandexSpan): YandexGeocodeQuery { + return new YandexGeocodeQuery({ ...this.toObject(), span }); + } + + public getSpan(): undefined | YandexSpan { + return this.span; + } + + public withSkip(skip: number): YandexGeocodeQuery { + return new YandexGeocodeQuery({ ...this.toObject(), skip }); + } + + public getSkip(): undefined | number { + return this.skip; + } +} diff --git a/src/provider/yandex/YandexGeocoded.ts b/src/provider/yandex/YandexGeocoded.ts new file mode 100644 index 0000000..883684a --- /dev/null +++ b/src/provider/yandex/YandexGeocoded.ts @@ -0,0 +1,57 @@ +import Geocoded, { GeocodedObject } from "Geocoded"; +import { YandexKind, YandexPrecision } from "provider"; + +export interface YandexGeocodedObject extends GeocodedObject { + readonly locationType?: YandexKind; + readonly precision?: YandexPrecision; +} + +export default class YandexGeocoded extends Geocoded { + private readonly locationType?: YandexKind; + + private readonly precision?: YandexPrecision; + + protected constructor({ + locationType, + precision, + ...geocodedObject + }: YandexGeocodedObject) { + super(geocodedObject); + this.locationType = locationType; + this.precision = precision; + } + + public static create(object: YandexGeocodedObject): YandexGeocoded { + return new this(object); + } + + public toObject(): YandexGeocodedObject { + return { + ...super.toObject(), + locationType: this.locationType, + precision: this.precision, + }; + } + + public withLocationType(locationType: YandexKind): YandexGeocoded { + return new YandexGeocoded({ + ...this.toObject(), + locationType, + }); + } + + public getLocationType(): undefined | YandexKind { + return this.locationType; + } + + public withPrecision(precision: YandexPrecision): YandexGeocoded { + return new YandexGeocoded({ + ...this.toObject(), + precision, + }); + } + + public getPrecision(): undefined | YandexPrecision { + return this.precision; + } +} diff --git a/src/provider/yandex/YandexProvider.ts b/src/provider/yandex/YandexProvider.ts new file mode 100644 index 0000000..bf8ced1 --- /dev/null +++ b/src/provider/yandex/YandexProvider.ts @@ -0,0 +1,407 @@ +import { + ExternalLoaderBody, + ExternalLoaderHeaders, + ExternalLoaderInterface, + ExternalLoaderParams, +} from "ExternalLoader"; +import { + ErrorCallback, + GeocodedResultsCallback, + ProviderHelpers, + ProviderInterface, + ProviderOptionsInterface, + YandexGeocoded, + YandexGeocodeQuery, + YandexGeocodeQueryObject, + YandexReverseQuery, + YandexReverseQueryObject, + defaultProviderOptions, +} from "provider"; +import AdminLevel from "AdminLevel"; + +export type YandexKind = + | "house" + | "street" + | "metro" + | "district" + | "locality" + | "area" + | "province" + | "country" + | "region" + | "hydro" + | "railway_station" + | "station" + | "route" + | "vegetation" + | "airport" + | "entrance" + | "other"; + +export type YandexPrecision = + | "exact" + | "number" + | "near" + | "range" + | "street" + | "other"; + +interface YandexRequestParams { + [param: string]: string | undefined; + readonly apikey?: string; + readonly geocode: string; + readonly format: string; + readonly lang?: string; + readonly kind?: YandexKind; + readonly rspn?: "0" | "1"; + readonly ll?: string; + readonly spn?: string; + readonly bbox?: string; + readonly skip?: string; + readonly results?: string; + readonly jsonpCallback?: string; +} + +export interface YandexResult { + metaDataProperty: { + GeocoderMetaData: { + kind: YandexKind; + text: string; + precision: YandexPrecision; + AddressDetails: { + Country: { + AddressLine: string; + CountryNameCode: string; + CountryName: string; + AdministrativeArea?: { + AdministrativeAreaName: string; + SubAdministrativeArea?: { + SubAdministrativeAreaName: string; + Locality?: { + LocalityName: string; + Thoroughfare?: { + ThoroughfareName: string; + Premise: { + PremiseNumber: string; + }; + }; + }; + }; + }; + }; + }; + }; + }; + description: string; + name: string; + boundedBy: { + Envelope: { + lowerCorner: string; + upperCorner: string; + }; + }; + Point: { + pos: string; + }; +} + +export interface YandexResponse { + response: { + GeoObjectCollection: { + metaDataProperty: { + GeocoderResponseMetaData: { + request: string; + suggest?: { + fix: string; + }; + found: string; + results: string; + skip: string; + }; + }; + featureMember: { + GeoObject: YandexResult; + }[]; + }; + }; +} + +interface YandexFlattenedAddressDetails { + CountryNameCode?: string; + CountryName?: string; + AdministrativeAreaName?: string; + SubAdministrativeAreaName?: string; + LocalityName?: string; + DependentLocalityName?: string; + ThoroughfareName?: string; + PremiseNumber?: string; +} + +export interface YandexProviderOptionsInterface + extends ProviderOptionsInterface { + readonly apiKey: string; +} + +export const defaultYandexProviderOptions = { + ...defaultProviderOptions, + apiKey: "", +}; + +type YandexGeocodedResultsCallback = GeocodedResultsCallback; + +export default class YandexProvider + implements ProviderInterface { + private externalLoader: ExternalLoaderInterface; + + private options: YandexProviderOptionsInterface; + + public constructor( + _externalLoader: ExternalLoaderInterface, + options: YandexProviderOptionsInterface = defaultYandexProviderOptions + ) { + this.externalLoader = _externalLoader; + this.options = { ...defaultYandexProviderOptions, ...options }; + if (!this.options.apiKey) { + throw new Error( + 'An API key is required for the Yandex provider. Please add it in the "apiKey" option.' + ); + } + } + + public geocode( + query: string | YandexGeocodeQuery | YandexGeocodeQueryObject, + callback: YandexGeocodedResultsCallback, + errorCallback?: ErrorCallback + ): void { + const geocodeQuery = ProviderHelpers.getGeocodeQueryFromParameter( + query, + YandexGeocodeQuery + ); + + if (geocodeQuery.getIp()) { + throw new Error( + "The Yandex provider does not support IP geolocation, only location geocoding." + ); + } + + this.externalLoader.setOptions({ + protocol: this.options.useSsl ? "https" : "http", + host: "geocode-maps.yandex.ru", + pathname: "1.x", + }); + + let rspn: "0" | "1" | undefined; + if ((geocodeQuery).getBounded() === false) { + rspn = "0"; + } else if ((geocodeQuery).getBounded() === true) { + rspn = "1"; + } + + const params: YandexRequestParams = this.withCommonParams( + { + geocode: geocodeQuery.getText() || "", + rspn, + ll: (geocodeQuery).getProximity() + ? `${(geocodeQuery).getProximity()?.longitude},${ + (geocodeQuery).getProximity()?.latitude + }` + : undefined, + spn: (geocodeQuery).getSpan() + ? `${(geocodeQuery).getSpan()?.spanLongitude},${ + (geocodeQuery).getSpan()?.spanLatitude + }` + : undefined, + bbox: geocodeQuery.getBounds() + ? `${geocodeQuery.getBounds()?.longitudeSW},${ + geocodeQuery.getBounds()?.latitudeSW + }~${geocodeQuery.getBounds()?.longitudeNE},${ + geocodeQuery.getBounds()?.latitudeNE + }` + : undefined, + }, + geocodeQuery + ); + + this.executeRequest(params, callback, {}, {}, errorCallback); + } + + public geodecode( + latitudeOrQuery: + | number + | string + | YandexReverseQuery + | YandexReverseQueryObject, + longitudeOrCallback: number | string | YandexGeocodedResultsCallback, + callbackOrErrorCallback?: YandexGeocodedResultsCallback | ErrorCallback, + errorCallback?: ErrorCallback + ): void { + const reverseQuery = ProviderHelpers.getReverseQueryFromParameters( + latitudeOrQuery, + longitudeOrCallback, + YandexReverseQuery + ); + const reverseCallback = ProviderHelpers.getCallbackFromParameters( + longitudeOrCallback, + callbackOrErrorCallback + ); + const reverseErrorCallback = ProviderHelpers.getErrorCallbackFromParameters( + longitudeOrCallback, + callbackOrErrorCallback, + errorCallback + ); + + this.externalLoader.setOptions({ + protocol: this.options.useSsl ? "https" : "http", + host: "geocode-maps.yandex.ru", + pathname: "1.x", + }); + + const params: YandexRequestParams = this.withCommonParams( + { + geocode: `${reverseQuery.getCoordinates().longitude},${ + reverseQuery.getCoordinates().latitude + }`, + kind: (reverseQuery).getLocationTypes() + ? (reverseQuery).getLocationTypes()?.[0] + : undefined, + }, + reverseQuery + ); + + this.executeRequest(params, reverseCallback, {}, {}, reverseErrorCallback); + } + + private withCommonParams( + params: Pick< + YandexRequestParams, + "geocode" | "rspn" | "ll" | "spn" | "bbox" | "kind" + >, + query: YandexGeocodeQuery | YandexReverseQuery + ): YandexRequestParams { + return { + ...params, + apikey: this.options.apiKey, + format: "json", + lang: query.getLocale(), + results: query.getLimit().toString(), + skip: query.getSkip()?.toString(), + jsonpCallback: this.options.useJsonp ? "callback" : undefined, + }; + } + + public executeRequest( + params: ExternalLoaderParams, + callback: YandexGeocodedResultsCallback, + headers?: ExternalLoaderHeaders, + body?: ExternalLoaderBody, + errorCallback?: ErrorCallback + ): void { + this.externalLoader.executeRequest( + params, + (data: YandexResponse) => { + callback( + data.response.GeoObjectCollection.featureMember.map((result) => + YandexProvider.mapToGeocoded(result.GeoObject) + ) + ); + }, + headers, + body, + errorCallback + ); + } + + public static mapToGeocoded(result: YandexResult): YandexGeocoded { + const point = result.Point.pos.split(" "); + const latitude = parseFloat(point[1]); + const longitude = parseFloat(point[0]); + + const addressDetails: YandexFlattenedAddressDetails = YandexProvider.flattenObject( + result.metaDataProperty.GeocoderMetaData.AddressDetails + ); + + const streetNumber = addressDetails.PremiseNumber; + const streetName = addressDetails.ThoroughfareName; + const subLocality = addressDetails.DependentLocalityName; + const locality = addressDetails.LocalityName; + const region = addressDetails.AdministrativeAreaName; + const country = addressDetails.CountryName; + const countryCode = addressDetails.CountryNameCode; + const locationType = result.metaDataProperty.GeocoderMetaData.kind; + const { precision } = result.metaDataProperty.GeocoderMetaData; + + let geocoded = YandexGeocoded.create({ + coordinates: { + latitude, + longitude, + }, + streetNumber, + streetName, + subLocality, + locality, + region, + country, + countryCode, + locationType, + precision, + }); + + const adminLevels: ( + | "AdministrativeAreaName" + | "SubAdministrativeAreaName" + )[] = ["AdministrativeAreaName", "SubAdministrativeAreaName"]; + adminLevels.forEach((adminLevel, level) => { + if (addressDetails[adminLevel]) { + geocoded.addAdminLevel( + AdminLevel.create({ + level: level + 1, + name: addressDetails[adminLevel] || "", + }) + ); + } + }); + + const lowerCorner = result.boundedBy.Envelope.lowerCorner.split(" "); + const upperCorner = result.boundedBy.Envelope.upperCorner.split(" "); + geocoded = geocoded.withBounds({ + latitudeSW: parseFloat(lowerCorner[1]), + longitudeSW: parseFloat(lowerCorner[0]), + latitudeNE: parseFloat(upperCorner[1]), + longitudeNE: parseFloat(upperCorner[0]), + }); + + return geocoded; + } + + private static flattenObject< + S extends string | string[], + O extends { [key: string]: O[keyof O] | S } + >(object: O) { + const flattened: { [key: string]: S } = {}; + + const step = (nestedObject: O | O[keyof O]): void => { + Object.keys(nestedObject).forEach((key) => { + const value = (nestedObject)[key]; + const isArray = Array.isArray(value); + const type = Object.prototype.toString.call(value); + const isObject = + type === "[object Object]" || type === "[object Array]"; + + if ( + !isArray && + isObject && + Object.keys(>value).length + ) { + step(value); + return; + } + + flattened[key] = value; + }); + }; + + step(object); + + return flattened; + } +} diff --git a/src/provider/yandex/YandexReverseQuery.ts b/src/provider/yandex/YandexReverseQuery.ts new file mode 100644 index 0000000..d3f7214 --- /dev/null +++ b/src/provider/yandex/YandexReverseQuery.ts @@ -0,0 +1,56 @@ +import { ReverseQuery, ReverseQueryObject } from "query"; +import { YandexKind } from "provider"; + +export interface YandexReverseQueryObject extends ReverseQueryObject { + readonly locationTypes?: YandexKind[]; + readonly skip?: number; +} + +export default class YandexReverseQuery extends ReverseQuery { + private readonly locationTypes?: YandexKind[]; + + private readonly skip?: number; + + protected constructor({ + locationTypes, + skip, + ...reverseQueryObject + }: YandexReverseQueryObject) { + super(reverseQueryObject); + if (locationTypes && locationTypes.length > 1) { + throw new Error( + 'The "locationTypes" parameter must contain only one location type.' + ); + } + this.locationTypes = locationTypes; + this.skip = skip; + } + + public static create(object: YandexReverseQueryObject): YandexReverseQuery { + return new this(object); + } + + public toObject(): YandexReverseQueryObject { + return { + ...super.toObject(), + locationTypes: this.locationTypes, + skip: this.skip, + }; + } + + public withLocationTypes(locationTypes: YandexKind[]): YandexReverseQuery { + return new YandexReverseQuery({ ...this.toObject(), locationTypes }); + } + + public getLocationTypes(): undefined | YandexKind[] { + return this.locationTypes; + } + + public withSkip(skip: number): YandexReverseQuery { + return new YandexReverseQuery({ ...this.toObject(), skip }); + } + + public getSkip(): undefined | number { + return this.skip; + } +} diff --git a/src/provider/yandex/index.ts b/src/provider/yandex/index.ts new file mode 100644 index 0000000..8d69e89 --- /dev/null +++ b/src/provider/yandex/index.ts @@ -0,0 +1,7 @@ +export { default as YandexGeocoded } from "provider/yandex/YandexGeocoded"; +export { default as YandexGeocodeQuery } from "provider/yandex/YandexGeocodeQuery"; +export * from "provider/yandex/YandexGeocodeQuery"; +export { default as YandexProvider } from "provider/yandex/YandexProvider"; +export * from "provider/yandex/YandexProvider"; +export { default as YandexReverseQuery } from "provider/yandex/YandexReverseQuery"; +export * from "provider/yandex/YandexReverseQuery"; diff --git a/src/providers/BingProvider.js b/src/providers/BingProvider.js deleted file mode 100644 index 4533490..0000000 --- a/src/providers/BingProvider.js +++ /dev/null @@ -1,87 +0,0 @@ -if (typeof GeocoderJS === "undefined" && typeof require === "function") { - var GeocoderJS = require("../GeocoderJS.js"); - require("../Geocoded.js"); - require("../providers/ProviderBase.js"); -} - -;(function (GeocoderJS) { - "use strict"; - - var useSSL; - var apiKey; - - GeocoderJS.BingProvider = function(_externalLoader, options) { - if (_externalLoader === undefined) { - throw "No external loader defined."; - } - this.externalLoader = _externalLoader; - - options = (options) ? options : {}; - - useSSL = (options.useSSL) ? options.useSSL : false; - apiKey = (options.apiKey) ? options.apiKey : null; - - if (apiKey) { - useSSL = true; - } - }; - - GeocoderJS.BingProvider.prototype = new GeocoderJS.ProviderBase(); - GeocoderJS.BingProvider.prototype.constructor = GeocoderJS.BingProvider; - - GeocoderJS.BingProvider.prototype.geocode = function(searchString, callback) { - this.externalLoader.setOptions({ - protocol: (useSSL === true) ? 'https' : 'http', - host: 'dev.virtualearth.net', - pathname: 'REST/v1/Locations/' + searchString - }); - - var options = { - key: apiKey, - JSONPCallback: 'jsonp', - }; - - this.executeRequest(options, callback); - }; - - GeocoderJS.BingProvider.prototype.geodecode = function(latitude, longitude, callback) { - this.externalLoader.setOptions({ - protocol: (useSSL === true) ? 'https' : 'http', - host: 'dev.virtualearth.net', - pathname: 'REST/v1/Locations/' + latitude + ',' + longitude - }); - - var options = { - key: apiKey, - JSONPCallback: 'jsonp', - }; - - this.executeRequest(options, callback); - }; - - GeocoderJS.BingProvider.prototype.executeRequest = function(params, callback) { - var _this = this; - - this.externalLoader.executeRequest(params, function(data) { - var results = []; - for (var i in data.resourceSets[0].resources) { - results.push(_this.mapToGeocoded(data.resourceSets[0].resources[i])); - } - callback(results); - }); - }; - - GeocoderJS.BingProvider.prototype.mapToGeocoded = function(result) { - var geocoded = new GeocoderJS.Geocoded() - - geocoded.latitude = result.point.coordinates[0]; - geocoded.longitude = result.point.coordinates[1]; - - geocoded.streetName = result.address.addressLine; - geocoded.city = result.address.locality; - geocoded.region = result.address.adminDistrict; - geocoded.postal_code = result.address.postalCode; - - return geocoded; - }; -})(GeocoderJS); diff --git a/src/providers/GoogleAPIProvider.js b/src/providers/GoogleAPIProvider.js deleted file mode 100644 index fe0dec1..0000000 --- a/src/providers/GoogleAPIProvider.js +++ /dev/null @@ -1,111 +0,0 @@ -if (typeof GeocoderJS === "undefined" && typeof require === "function") { - var GeocoderJS = require("../GeocoderJS.js"); - require("../Geocoded.js"); - require("../providers/ProviderBase.js"); -} - -;(function (GeocoderJS) { - "use strict"; - - var useSSL; - var apiKey; - - GeocoderJS.GoogleAPIProvider = function(_externalLoader, options) { - if (_externalLoader === undefined) { - throw "No external loader defined."; - } - this.externalLoader = _externalLoader; - - options = (options) ? options : {}; - - useSSL = (options.useSSL) ? options.useSSL : false; - apiKey = (options.apiKey) ? options.apiKey : null; - - if (apiKey) { - useSSL = true; - } - }; - - GeocoderJS.GoogleAPIProvider.prototype = new GeocoderJS.ProviderBase(); - GeocoderJS.GoogleAPIProvider.prototype.constructor = GeocoderJS.GoogleAPIProvider; - - GeocoderJS.GoogleAPIProvider.prototype.geocode = function(searchString, callback) { - this.externalLoader.setOptions({ - protocol: (useSSL === true) ? 'https' : 'http', - host: 'maps.googleapis.com', - pathname: 'maps/api/geocode/json' - }); - - var options = { - sensor: false, - address: searchString - }; - - if (apiKey) { - options["key"] = apiKey; - } - - this.executeRequest(options, callback); - }; - - GeocoderJS.GoogleAPIProvider.prototype.geodecode = function(latitude, longitude, callback) { - this.externalLoader.setOptions({ - protocol: (useSSL) ? 'https' : 'http', - host: 'maps.googleapis.com', - pathname: 'maps/api/geocode/json' - }); - - var options = { - "sensor": false, - "latlng": latitude + "," + longitude - }; - - if (apiKey) { - options["key"] = apiKey; - } - - this.executeRequest(options, callback); - }; - - GeocoderJS.GoogleAPIProvider.prototype.executeRequest = function(params, callback) { - var _this = this; - - this.externalLoader.executeRequest(params, function(data) { - var results = []; - for (var i in data.results) { - results.push(_this.mapToGeocoded(data.results[i])); - } - callback(results); - }); - }; - - GeocoderJS.GoogleAPIProvider.prototype.mapToGeocoded = function(result) { - var geocoded = new GeocoderJS.Geocoded(); - geocoded.latitude = result.geometry.location.lat; - geocoded.longitude = result.geometry.location.lng; - - for (var i in result.address_components) { - for (var j in result.address_components[i].types) { - switch (result.address_components[i].types[j]) { - case "street_number": - geocoded.streetNumber = result.address_components[i].long_name; - break; - case "route": - geocoded.streetName = result.address_components[i].long_name; - break; - case "locality": - geocoded.city = result.address_components[i].long_name; - break; - case "administrative_area_level_1": - geocoded.region = result.address_components[i].long_name; - break; - case "postal_code": - geocoded.postal_code = result.address_components[i].long_name; - break; - } - } - } - - return geocoded; - }; -})(GeocoderJS); diff --git a/src/providers/MapquestProvider.js b/src/providers/MapquestProvider.js deleted file mode 100644 index 5cdeccf..0000000 --- a/src/providers/MapquestProvider.js +++ /dev/null @@ -1,95 +0,0 @@ -if (typeof GeocoderJS === "undefined" && typeof require === "function") { - var GeocoderJS = require("../GeocoderJS.js"); -} - -;(function (GeocoderJS) { - "use strict"; - - GeocoderJS.MapquestProvider = function(_externalLoader, options) { - if (_externalLoader === undefined) { - throw "No external loader defined."; - } - this.externalLoader = _externalLoader; - - if (typeof options !== 'object') { - options = {}; - } - - var defaults = { - apiKey: '' - }; - - for (var i in defaults) { - if (options[i] === undefined) { - options[i] = defaults[i]; - } - } - - this.apiKey = options.apiKey; - }; - - GeocoderJS.MapquestProvider.prototype = new GeocoderJS.ProviderBase(); - GeocoderJS.MapquestProvider.prototype.constructor = GeocoderJS.MapquestProvider; - - GeocoderJS.MapquestProvider.prototype.geocode = function(searchString, callback) { - this.externalLoader.setOptions({ - protocol: 'http', - host: 'www.mapquestapi.com', - pathname: 'geocoding/v1/address' - }); - - var params = { - key: this.apiKey, - outputFormat: 'json', - location: encodeURIComponent(searchString), - JSONPCallback: 'callback' - }; - - this.executeRequest(params, callback); - }; - - GeocoderJS.MapquestProvider.prototype.geodecode = function(latitude, longitude, callback) { - this.externalLoader.setOptions({ - protocol: 'http', - host: 'www.mapquestapi.com', - pathname: 'geocoding/v1/reverse' - }); - - var options = { - key: this.apiKey, - outputFormat: 'json', - JSONPCallback: 'callback', - "location": latitude + "," + longitude - }; - - this.executeRequest(options, callback); - }; - - GeocoderJS.MapquestProvider.prototype.mapToGeocoded = function(result) { - var geocoded = new GeocoderJS.Geocoded(); - geocoded.latitude = result.latLng.lat; - geocoded.longitude = result.latLng.lng; - - geocoded.city = result.adminArea5; - geocoded.region = result.adminArea4; - geocoded.streetName = result.street; - geocoded.postal_code = result.postalCode; - - return geocoded; - }; - - GeocoderJS.MapquestProvider.prototype.executeRequest = function(params, callback) { - var _this = this; - - this.externalLoader.executeRequest(params, function(data) { - var results = []; - if (data.results[0].locations.length) { - for (var i in data.results[0].locations) { - results.push(_this.mapToGeocoded(data.results[0].locations[i])); - } - } - - callback(results); - }); - }; -})(GeocoderJS); diff --git a/src/providers/OpenStreetMapProvider.js b/src/providers/OpenStreetMapProvider.js deleted file mode 100644 index dd5d566..0000000 --- a/src/providers/OpenStreetMapProvider.js +++ /dev/null @@ -1,89 +0,0 @@ -if (typeof GeocoderJS === "undefined" && typeof require === "function") { - var GeocoderJS = require("../GeocoderJS.js"); - require("../Geocoded.js"); - require("../ExternalURILoader.js"); - require("../providers/ProviderBase.js"); -} - -;(function (GeocoderJS) { - "use strict"; - - GeocoderJS.OpenStreetMapProvider = function(_externalLoader) { - if (_externalLoader === undefined) { - throw "No external loader defined."; - } - this.externalLoader = _externalLoader; - }; - - GeocoderJS.OpenStreetMapProvider.prototype = new GeocoderJS.ProviderBase(); - GeocoderJS.OpenStreetMapProvider.prototype.constructor = GeocoderJS.OpenStreetMapProvider; - - GeocoderJS.OpenStreetMapProvider.prototype.geocode = function(searchString, callback) { - this.externalLoader.setOptions({ - protocol: 'http', - host: 'nominatim.openstreetmap.org', - pathname: 'search' - }); - - var params = { - format: 'json', - q: searchString, - addressdetails: 1 - }; - - this.executeRequest(params, callback); - }; - - GeocoderJS.OpenStreetMapProvider.prototype.geodecode = function(latitude, longitude, callback) { - this.externalLoader.setOptions({ - protocol: 'http', - host: 'nominatim.openstreetmap.org', - pathname: 'reverse' - }); - - var params = { - format: 'json', - lat: latitude, - lon: longitude, - addressdetails: 1, - zoom: 18 - }; - - var _this = this; - - this.executeRequest(params, callback); - }; - - GeocoderJS.OpenStreetMapProvider.prototype.executeRequest = function(params, callback) { - var _this = this; - - this.externalLoader.executeRequest(params, function(data) { - var results = []; - if (data.length) { - for (var i in data) { - results.push(_this.mapToGeocoded(data[i])); - } - } else { - results.push(_this.mapToGeocoded(data)); - } - - callback(results); - }); - }; - - GeocoderJS.OpenStreetMapProvider.prototype.mapToGeocoded = function(result) { - var geocoded = new GeocoderJS.Geocoded(); - - geocoded.latitude = result.lat * 1; - geocoded.longitude = result.lon * 1; - - geocoded.streetNumber = (result.address.house_number !== undefined) ? result.address.house_number : undefined; - geocoded.streetName = result.address.road; - geocoded.city = result.address.city; - geocoded.region = result.address.state; - geocoded.postal_code = result.address.postcode; - - return geocoded; - }; - -})(GeocoderJS); diff --git a/src/providers/ProviderBase.js b/src/providers/ProviderBase.js deleted file mode 100644 index 06f1823..0000000 --- a/src/providers/ProviderBase.js +++ /dev/null @@ -1,17 +0,0 @@ -if (typeof GeocoderJS === "undefined" && typeof require === "function") { - var GeocoderJS = require("../GeocoderJS.js"); -} - -;(function (GeocoderJS) { - "use strict"; - - GeocoderJS.ProviderBase = function() {}; - - GeocoderJS.ProviderBase.prototype = { - geocode: function(searchString, callback) {}, - geodecode: function(latitude, longitude, callback) {}, - mapToGeocoded: function(result) {}, - executeRequest: function(params, callback) {} - }; - -})(GeocoderJS); diff --git a/src/providers/YandexProvider.js b/src/providers/YandexProvider.js deleted file mode 100644 index 6c68607..0000000 --- a/src/providers/YandexProvider.js +++ /dev/null @@ -1,102 +0,0 @@ -if (typeof GeocoderJS === "undefined" && typeof require === "function") { - var GeocoderJS = require("../GeocoderJS.js"); - require("../Geocoded.js"); - require("../providers/ProviderBase.js"); -} - -;(function (GeocoderJS) { - "use strict"; - - var useSSL; - - GeocoderJS.YandexProvider = function(_externalLoader, options) { - if (_externalLoader === undefined) { - throw "No external loader defined."; - } - this.externalLoader = _externalLoader; - - options = (options) ? options : {}; - - useSSL = (options.useSSL) ? options.useSSL : false; - this.lang = (options.lang) ? options.lang : 'en-US'; - }; - - GeocoderJS.YandexProvider.prototype = new GeocoderJS.ProviderBase(); - GeocoderJS.YandexProvider.prototype.constructor = GeocoderJS.YandexProvider; - - GeocoderJS.YandexProvider.prototype.geocode = function(searchString, callback) { - this.externalLoader.setOptions({ - protocol: (useSSL === true) ? 'https' : 'http', - host: 'geocode-maps.yandex.ru', - pathname: '1.x' - }); - - var options = { - format: 'json', - geocode: searchString, - JSONPCallback: 'callback', - lang: this.lang - }; - - this.executeRequest(options, callback); - }; - - GeocoderJS.YandexProvider.prototype.geodecode = function(latitude, longitude, callback) { - this.externalLoader.setOptions({ - protocol: (useSSL === true) ? 'https' : 'http', - host: 'geocode-maps.yandex.ru', - pathname: '1.x' - }); - - var options = { - format: 'json', - geocode: longitude + ',' + latitude, - JSONPCallback: 'callback', - lang: this.lang - }; - - this.executeRequest(options, callback); - }; - - GeocoderJS.YandexProvider.prototype.executeRequest = function(params, callback) { - var _this = this; - - this.externalLoader.executeRequest(params, function(data) { - var results = []; - - for (var i in data.response.GeoObjectCollection.featureMember) { - results.push(_this.mapToGeocoded(data.response.GeoObjectCollection.featureMember[i].GeoObject)); - } - callback(results); - }); - }; - - GeocoderJS.YandexProvider.prototype.mapToGeocoded = function(result) { - var geocoded = new GeocoderJS.Geocoded(); - - var point = result.Point.pos.split(' '); - geocoded.latitude = point[1] * 1; - geocoded.longitude = point[0] * 1; - - if (result.metaDataProperty.GeocoderMetaData.AddressDetails.Country) { - var tmp = result.metaDataProperty.GeocoderMetaData.AddressDetails.Country; - if (tmp.AdministrativeArea) { - tmp = tmp.AdministrativeArea; - geocoded.region = tmp.AdministrativeAreaName; - if (tmp.SubAdministrativeArea) { - tmp = tmp.SubAdministrativeArea; - if (tmp.Locality) { - tmp = tmp.Locality; - geocoded.city = tmp.LocalityName; - if (tmp.Thoroughfare) { - tmp = tmp.Thoroughfare; - geocoded.streetName = tmp.ThoroughfareName; - } - } - } - } - } - - return geocoded; - }; -})(GeocoderJS); diff --git a/src/query/GeocodeQuery.ts b/src/query/GeocodeQuery.ts new file mode 100644 index 0000000..169cf8a --- /dev/null +++ b/src/query/GeocodeQuery.ts @@ -0,0 +1,119 @@ +import { DEFAULT_RESULT_LIMIT } from "provider"; +import { BoundingBox } from "types"; + +export interface GeocodeQueryObject { + readonly text?: string; + readonly ip?: string; + readonly bounds?: BoundingBox; + readonly locale?: string; + readonly limit?: number; +} + +export default class GeocodeQuery { + private readonly text?: string; + + private readonly ip?: string; + + private readonly bounds?: BoundingBox; + + private readonly locale?: string; + + private readonly limit: number = DEFAULT_RESULT_LIMIT; + + protected constructor({ + text, + ip, + bounds, + locale, + limit = DEFAULT_RESULT_LIMIT, + }: GeocodeQueryObject) { + this.text = text; + this.ip = ip; + if (!text && !ip) { + throw new Error('Either "text" or "ip" parameter is required.'); + } + if ( + bounds && + (!bounds.latitudeSW || + !bounds.longitudeSW || + !bounds.latitudeNE || + !bounds.longitudeNE) + ) { + throw new Error( + 'The "bounds" parameter must be an object with the keys: "latitudeSW", "longitudeSW", "latitudeNE", "longitudeNE".' + ); + } + this.bounds = bounds; + this.locale = locale; + this.limit = limit; + } + + public static create(object: GeocodeQueryObject): GeocodeQuery { + return new this(object); + } + + public toObject(): GeocodeQueryObject { + return { + text: this.text, + ip: this.ip, + bounds: this.bounds, + locale: this.locale, + limit: this.limit, + }; + } + + public withText(text: string): GeocodeQuery { + return (this.constructor).create({ + ...this.toObject(), + text, + }); + } + + public withIp(ip: string): GeocodeQuery { + return (this.constructor).create({ + ...this.toObject(), + ip, + }); + } + + public withBounds(bounds: BoundingBox): GeocodeQuery { + return (this.constructor).create({ + ...this.toObject(), + bounds, + }); + } + + public withLocale(locale: string): GeocodeQuery { + return (this.constructor).create({ + ...this.toObject(), + locale, + }); + } + + public withLimit(limit: number): GeocodeQuery { + return (this.constructor).create({ + ...this.toObject(), + limit, + }); + } + + public getText(): undefined | string { + return this.text; + } + + public getIp(): undefined | string { + return this.ip; + } + + public getBounds(): undefined | BoundingBox { + return this.bounds; + } + + public getLocale(): undefined | string { + return this.locale; + } + + public getLimit(): number { + return this.limit; + } +} diff --git a/src/query/ReverseQuery.ts b/src/query/ReverseQuery.ts new file mode 100644 index 0000000..0bb4179 --- /dev/null +++ b/src/query/ReverseQuery.ts @@ -0,0 +1,76 @@ +import { DEFAULT_RESULT_LIMIT } from "provider"; +import { Coordinates } from "types"; + +export interface ReverseQueryObject { + readonly coordinates: Coordinates; + readonly locale?: string; + readonly limit?: number; +} + +export default class ReverseQuery { + private readonly coordinates: Coordinates; + + private readonly locale?: string; + + private readonly limit: number = DEFAULT_RESULT_LIMIT; + + protected constructor({ + coordinates, + locale, + limit = DEFAULT_RESULT_LIMIT, + }: ReverseQueryObject) { + if (coordinates && (!coordinates.latitude || !coordinates.longitude)) { + throw new Error( + 'The "coordinates" parameter must be an object with the keys: "latitude", "longitude".' + ); + } + this.coordinates = coordinates; + this.locale = locale; + this.limit = limit; + } + + public static create(object: ReverseQueryObject): ReverseQuery { + return new this(object); + } + + public toObject(): ReverseQueryObject { + return { + coordinates: this.coordinates, + locale: this.locale, + limit: this.limit, + }; + } + + public withCoordinates(coordinates: Coordinates): ReverseQuery { + return (this.constructor).create({ + ...this.toObject(), + coordinates, + }); + } + + public withLocale(locale: string): ReverseQuery { + return (this.constructor).create({ + ...this.toObject(), + locale, + }); + } + + public withLimit(limit: number): ReverseQuery { + return (this.constructor).create({ + ...this.toObject(), + limit, + }); + } + + public getCoordinates(): Coordinates { + return this.coordinates; + } + + public getLocale(): undefined | string { + return this.locale; + } + + public getLimit(): number { + return this.limit; + } +} diff --git a/src/query/index.ts b/src/query/index.ts new file mode 100644 index 0000000..5566cb5 --- /dev/null +++ b/src/query/index.ts @@ -0,0 +1,4 @@ +export { default as GeocodeQuery } from "query/GeocodeQuery"; +export * from "query/GeocodeQuery"; +export { default as ReverseQuery } from "query/ReverseQuery"; +export * from "query/ReverseQuery"; diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000..adb719f --- /dev/null +++ b/src/types.ts @@ -0,0 +1,24 @@ +export type PartialSome< + O, + P extends keyof O, + Q = Omit, + R = { [K in P]?: O[P] } +> = Q & R; + +export type FlatCoordinates = [number, number]; + +export type FlatBoundingBox = [number, number, number, number]; + +export type Coordinates = { + readonly latitude: number | string; + readonly longitude: number | string; +}; + +export type BoundingBox = { + // South-West (Lower-Left, minY-minX, minLat-minLon, SouthLatitude-WestLongitude) + readonly latitudeSW: number | string; + readonly longitudeSW: number | string; + // North-East (Upper-Right, maxY-maxX, maxLat-maxLon, NorthLatitude-EastLongitude) + readonly latitudeNE: number | string; + readonly longitudeNE: number | string; +}; diff --git a/src/utils.ts b/src/utils.ts new file mode 100644 index 0000000..ca6cf2d --- /dev/null +++ b/src/utils.ts @@ -0,0 +1,53 @@ +export const isBrowser = (): boolean => typeof window !== "undefined"; + +export const filterUndefinedObjectValues = ( + object: Record +): Record => + Object.keys(object).reduce((acc: Record, key) => { + const filtered = acc; + const value = object[key]; + if (value !== undefined) { + filtered[key] = value; + } + return filtered; + }, {}); + +/** + * Decode from URL-safe base64 to true base64. + */ +export const decodeUrlSafeBase64 = (safe: string): string => + safe.replace(/-/g, "+").replace(/_/g, "/"); + +/** + * Encode from true base64 to URL-safe base64. + */ +export const encodeUrlSafeBase64 = (base64: string): string => + base64.replace(/\+/g, "-").replace(/\//g, "_"); + +export const decodeBase64 = (base64: string): string => { + if (isBrowser()) { + throw new Error("decodeBase64 can only be used in a Node environment."); + } + + return Buffer.from(base64, "base64").toString(); +}; + +// From https://github.com/sindresorhus/ip-regex +const ipv4RegExp = + "(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}"; +export const isIpv4 = (ip: string): boolean => + new RegExp(`^${ipv4RegExp}$`).test(ip); + +// From https://github.com/sindresorhus/ip-regex +const ipv6Seg = "[a-fA-F\\d]{1,4}"; +const ipv6RegExp = `((?:${ipv6Seg}:){7}(?:${ipv6Seg}|:)|(?:${ipv6Seg}:){6}(?:${ipv4RegExp}|:${ipv6Seg}|:)|(?:${ipv6Seg}:){5}(?::${ipv4RegExp}|(:${ipv6Seg}){1,2}|:)|(?:${ipv6Seg}:){4}(?:(:${ipv6Seg}){0,1}:${ipv4RegExp}|(:${ipv6Seg}){1,3}|:)|(?:${ipv6Seg}:){3}(?:(:${ipv6Seg}){0,2}:${ipv4RegExp}|(:${ipv6Seg}){1,4}|:)|(?:${ipv6Seg}:){2}(?:(:${ipv6Seg}){0,3}:${ipv4RegExp}|(:${ipv6Seg}){1,5}|:)|(?:${ipv6Seg}:){1}(?:(:${ipv6Seg}){0,4}:${ipv4RegExp}|(:${ipv6Seg}){1,6}|:)|(?::((?::${ipv6Seg}){0,5}:${ipv4RegExp}|(?::${ipv6Seg}){1,7}|:)))(%[0-9a-zA-Z]{1,})?`; +export const isIpv6 = (ip: string): boolean => + new RegExp(`^${ipv6RegExp}$`).test(ip); + +// eslint-disable-next-line @typescript-eslint/ban-types +export const getRequireFunc = (): Function => + // eslint-disable-next-line camelcase + typeof __non_webpack_require__ === "function" + ? // eslint-disable-next-line camelcase + __non_webpack_require__ + : require; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..7372b85 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "baseUrl": "src", + "declaration": true, + "declarationDir": "types", + "declarationMap": true, + "esModuleInterop": true, + "outDir": "dist", + "sourceMap": true, + "strict": true, + "target": "ES5", + "plugins": [ + { + "transform": "@zerollup/ts-transform-paths", + "exclude": ["*"] + } + ] + }, + "include": [ + "src/**/*" + ] +} diff --git a/webpack.common.js b/webpack.common.js new file mode 100644 index 0000000..e0c41f3 --- /dev/null +++ b/webpack.common.js @@ -0,0 +1,25 @@ +const path = require("path"); + +module.exports = { + entry: ["./src/index.ts", "./src/global.ts"], + module: { + rules: [ + { + test: /\.ts$/, + loader: "ts-loader", + options: { + compiler: 'ttypescript' + }, + exclude: /node_modules/, + }, + ], + }, + resolve: { + extensions: [".ts"], + modules: [path.resolve(__dirname, "src"), "node_modules"], + }, + output: { + path: path.resolve(__dirname, "dist"), + }, + node: false, +}; diff --git a/webpack.dev.js b/webpack.dev.js new file mode 100644 index 0000000..5cd7844 --- /dev/null +++ b/webpack.dev.js @@ -0,0 +1,10 @@ +const { merge } = require("webpack-merge"); +const common = require("./webpack.common.js"); + +module.exports = merge(common, { + devtool: "inline-source-map", + mode: "development", + output: { + filename: "geocoder.js", + }, +}); diff --git a/webpack.prod.js b/webpack.prod.js new file mode 100644 index 0000000..41c7f0a --- /dev/null +++ b/webpack.prod.js @@ -0,0 +1,9 @@ +const { merge } = require("webpack-merge"); +const common = require("./webpack.common.js"); + +module.exports = merge(common, { + mode: "production", + output: { + filename: "geocoder.min.js", + }, +}); diff --git a/website/.nojekyll b/website/.nojekyll new file mode 100644 index 0000000..e69de29 diff --git a/website/copy-vendor.sh b/website/copy-vendor.sh new file mode 100755 index 0000000..c0d4cd1 --- /dev/null +++ b/website/copy-vendor.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +rm -rf vendor webfonts && +mkdir vendor webfonts && +cp node_modules/bootswatch/dist/flatly/bootstrap.min.css vendor/flatly-bootstrap.min.css && +cp node_modules/components-font-awesome/css/all.min.css vendor/font-awesome.min.css && +cp node_modules/components-font-awesome/webfonts/* webfonts && +cp node_modules/@highlightjs/cdn-assets/styles/dracula.min.css vendor/highlight-dracula.min.css && +cp node_modules/jquery/dist/jquery.min.js vendor/jquery.min.js && +cp node_modules/bootstrap/dist/js/bootstrap.min.js vendor/bootstrap.min.js && +cp node_modules/@highlightjs/cdn-assets/highlight.min.js vendor/highlight.min.js && +rm -rf node_modules && +rm -f package-lock.json yarn.lock diff --git a/website/css/main.css b/website/css/main.css new file mode 100644 index 0000000..145c144 --- /dev/null +++ b/website/css/main.css @@ -0,0 +1,123 @@ +html { + position: relative; + min-height: 100%; +} +body { + font-size: 19px; +} + +main { + margin-top: 20px; +} + +section { + margin-bottom: 120px; +} + +h3 { + font-size: 23px; +} + +li { + margin-bottom: 3px; +} + +pre { + padding: 0; + border-radius: 4px; + margin: 15px; + font-size: 15px; +} +pre code { + border: none; +} + +a { + color: #186fbc; +} +a:hover { + color: #0d3b63; +} + +code { + color: #18Bc9c; +} + +img[alt="CI"] { + display: none; +} +img[alt="codecov"] { + display: none; +} +img[alt="npm"] { + display: none; +} +img[alt="minified-size"] { + display: none; +} +img[alt="downloads"] { + display: none; +} + +#content img { + max-width: 100%; + padding: 4px; +} + +header.navbar { + opacity: 0.9; +} +.navbar .navbar-brand { + font-size: 28px; + height: auto; + line-height: 40px; + margin-left: 20px; +} +.navbar .navbar-brand small { + font-size: 18px; + font-weight: 300; + margin-left: 10px; +} + +@media (min-width: 576px) { + #sidebar { + position: fixed; + margin-top: 30px; + } +} +@media (max-width: 960px) { + body { + font-size: 17px; + } + pre { + font-size: 12px; + } +} + +#sidebar .text-muted { + color: #bbbbbb; +} + +.page-header { + margin-top: 0; +} + +footer { + position: absolute; + bottom: 0; + width: 100%; + background-color: #f5f5f5; + padding: 25px 0; + font-size: 15px; + text-align: center; + text-transform: uppercase; + opacity: 0.6; + transition: opacity .2s ease; +} +footer:hover { + opacity: 1.0; + transition: opacity .2s ease; +} +footer p { + margin: 0; +} diff --git a/website/default.twig b/website/default.twig new file mode 100644 index 0000000..1c14584 --- /dev/null +++ b/website/default.twig @@ -0,0 +1,80 @@ + + + + + + + {{ title }} + + + + + + + + + + + +
+
+ + {% if menu is defined %} + + {% endif %} + +
' })|raw }} + + + +
+ + + + + + + + + + + diff --git a/website/package.json b/website/package.json new file mode 100644 index 0000000..8d8714e --- /dev/null +++ b/website/package.json @@ -0,0 +1,23 @@ +{ + "name": "geocoder-js-website", + "description": "Website for GeocoderJS", + "license": "(MIT AND GPL-2.0)", + "author": "Alan Poulain ", + "homepage": "https://geocoder-php.github.io/geocoder-js/", + "repository": { + "type": "git", + "url": "git+https://github.com/geocoder-php/geocoder-js.git" + }, + "private": true, + "files": [], + "dependencies": { + "bootstrap": "^4.5.0", + "bootswatch": "^4.5.0", + "components-font-awesome": "^5.9.0", + "@highlightjs/cdn-assets": "^10.2.0", + "jquery": "^3.5.0" + }, + "scripts": { + "install": "./copy-vendor.sh" + } +}