├── .github └── workflows │ └── node.yml ├── .gitignore ├── LICENSE ├── README.md ├── bench ├── bench-geokdbush.js ├── bench-naive.js ├── bench-sphere-knn.js └── bench-vptree.js ├── eslint.config.js ├── index.js ├── package-lock.json ├── package.json └── test.js /.github/workflows/node.yml: -------------------------------------------------------------------------------- 1 | name: Node 2 | on: [push, pull_request] 3 | jobs: 4 | test: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - name: Checkout 8 | uses: actions/checkout@v4 9 | 10 | - name: Setup Node 11 | uses: actions/setup-node@v4 12 | with: 13 | node-version: 20 14 | 15 | - name: Install dependencies 16 | run: npm ci 17 | 18 | - name: Run tests 19 | run: npm test 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | ISC License 2 | 3 | Copyright (c) 2017, Vladimir Agafonkin 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any purpose 6 | with or without fee is hereby granted, provided that the above copyright notice 7 | and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 10 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 11 | FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 12 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS 13 | OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 14 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 15 | THIS SOFTWARE. 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## geokdbush [![Node](https://github.com/mourner/geokdbush/actions/workflows/node.yml/badge.svg)](https://github.com/mourner/geokdbush/actions/workflows/node.yml) [![Simply Awesome](https://img.shields.io/badge/simply-awesome-brightgreen.svg)](https://github.com/mourner/projects) 2 | 3 | A geographic extension for [KDBush](https://github.com/mourner/kdbush), 4 | the fastest static spatial index for points in JavaScript. 5 | 6 | It implements fast [nearest neighbors](https://en.wikipedia.org/wiki/Nearest_neighbor_search) queries 7 | for locations on Earth, taking Earth curvature and date line wrapping into account. 8 | Inspired by [sphere-knn](https://github.com/darkskyapp/sphere-knn), but uses a different algorithm. 9 | 10 | ### Example 11 | 12 | ```js 13 | import KDBush from 'kdbush'; 14 | import * as geokdbush from 'geokdbush'; 15 | 16 | const index = new KDBush(points.length); 17 | for (const {lon, lat} of points) index.add(lon, lat); 18 | index.finish(); 19 | 20 | const nearestIds = geokdbush.around(index, -119.7051, 34.4363, 1000); 21 | 22 | const nearest = nearestIds.map(id => points[id]); 23 | ``` 24 | 25 | ### API 26 | 27 | #### geokdbush.around(index, longitude, latitude[, maxResults, maxDistance, filterFn]) 28 | 29 | Returns an array of the closest points from a given location in order of increasing distance. 30 | 31 | - `index`: [kdbush](https://github.com/mourner/kdbush) index. 32 | - `longitude`: query point longitude. 33 | - `latitude`: query point latitude. 34 | - `maxResults`: (optional) maximum number of points to return (`Infinity` by default). 35 | - `maxDistance`: (optional) maximum distance in kilometers to search within (`Infinity` by default). 36 | - `filterFn`: (optional) a function to filter the results with. 37 | 38 | #### geokdbush.distance(longitude1, latitude1, longitude2, latitude2) 39 | 40 | Returns great circle distance between two locations in kilometers. 41 | 42 | ### Performance 43 | 44 | This library is incredibly fast. 45 | The results below were obtained with `npm run bench` 46 | (Node v20, Macbook Pro 2020 M1 Pro). 47 | 48 | benchmark | geokdbush | sphere-knn | naive 49 | --- | ---: | ---: | ---: 50 | index 138398 points | 57.6ms | 408ms | n/a 51 | query 1000 closest | 1.6ms | 1.8ms | 72ms 52 | query 50000 closest | 14.7ms | 91.5ms | 72ms 53 | query all 138398 | 33.7ms | 500ms | 72ms 54 | 1000 queries of 1 | 24.7ms | 27.5ms | 11.1s 55 | -------------------------------------------------------------------------------- /bench/bench-geokdbush.js: -------------------------------------------------------------------------------- 1 | import cities from 'all-the-cities'; 2 | import KDBush from 'kdbush'; 3 | import * as geokdbush from '../index.js'; 4 | 5 | console.log('=== geokdbush benchmark ==='); 6 | 7 | const n = cities.length; 8 | const k = 1000; 9 | 10 | const randomPoints = []; 11 | for (let i = 0; i < k; i++) randomPoints.push({ 12 | lon: -180 + 360 * Math.random(), 13 | lat: -60 + 140 * Math.random() 14 | }); 15 | 16 | console.time(`index ${n} points`); 17 | const index = new KDBush(cities.length); 18 | for (const {loc: {coordinates: [lon, lat]}} of cities) index.add(lon, lat); 19 | index.finish(); 20 | console.timeEnd(`index ${n} points`); 21 | 22 | console.time('query 1000 closest'); 23 | geokdbush.around(index, -119.7051, 34.4363, 1000); 24 | console.timeEnd('query 1000 closest'); 25 | 26 | console.time('query 50000 closest'); 27 | geokdbush.around(index, -119.7051, 34.4363, 50000); 28 | console.timeEnd('query 50000 closest'); 29 | 30 | console.time(`query all ${n}`); 31 | geokdbush.around(index, -119.7051, 34.4363); 32 | console.timeEnd(`query all ${n}`); 33 | 34 | console.time(`${k} random queries of 1 closest`); 35 | for (let i = 0; i < k; i++) geokdbush.around(index, randomPoints[i].lon, randomPoints[i].lat, 1); 36 | console.timeEnd(`${k} random queries of 1 closest`); 37 | -------------------------------------------------------------------------------- /bench/bench-naive.js: -------------------------------------------------------------------------------- 1 | import cities from 'all-the-cities'; 2 | 3 | console.log('=== naive benchmark ==='); 4 | 5 | const rad = Math.PI / 180; 6 | 7 | const n = cities.length; 8 | const k = 1000; 9 | const p = {loc: {coordinates: [-119.7051, 34.4363]}}; 10 | 11 | const randomPoints = []; 12 | for (let i = 0; i < k; i++) randomPoints.push({ 13 | loc: { 14 | coordinates: [ 15 | -180 + 360 * Math.random(), 16 | -60 + 140 * Math.random() 17 | ] 18 | } 19 | }); 20 | 21 | console.time(`query (sort) all ${n}`); 22 | const items = cities.map(city => ({p: city, dist: dist(city, p)})); 23 | items.sort((a, b) => a.dist - b.dist); 24 | console.timeEnd(`query (sort) all ${n}`); 25 | 26 | console.time(`${k} random queries of 1 closest`); 27 | for (let i = 0; i < k; i++) findClosest(randomPoints[i]); 28 | console.timeEnd(`${k} random queries of 1 closest`); 29 | 30 | function findClosest(p) { 31 | let minDist = Infinity; 32 | let closest = null; 33 | for (let i = 0; i < cities.length; i++) { 34 | const d = dist(p, cities[i]); 35 | if (d < minDist) { 36 | minDist = d; 37 | closest = cities[i]; 38 | } 39 | } 40 | return closest; 41 | } 42 | 43 | function dist(a, b) { 44 | const [lon1, lat1] = a.loc.coordinates; 45 | const [lon2, lat2] = b.loc.coordinates; 46 | const d = Math.sin(lat1 * rad) * Math.sin(lat2 * rad) + 47 | Math.cos(lat1 * rad) * Math.cos(lat2 * rad) * Math.cos((lon1 - lon2) * rad); 48 | return 6371 * Math.acos(Math.min(d, 1)); 49 | } 50 | -------------------------------------------------------------------------------- /bench/bench-sphere-knn.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | import cities from 'all-the-cities'; 4 | import sphereKnn from 'sphere-knn'; 5 | 6 | console.log('=== sphere-knn benchmark ==='); 7 | 8 | const n = cities.length; 9 | const k = 1000; 10 | 11 | const randomPoints = []; 12 | for (let i = 0; i < k; i++) randomPoints.push({ 13 | lon: -180 + 360 * Math.random(), 14 | lat: -60 + 140 * Math.random() 15 | }); 16 | 17 | const locations = cities.map(c => c.loc.coordinates); 18 | 19 | console.time(`index ${cities.length} points`); 20 | const sphereKnnLookup = sphereKnn(locations); 21 | console.timeEnd(`index ${cities.length} points`); 22 | 23 | console.time('query 1000 closest'); 24 | sphereKnnLookup(34.4363, -119.7051, 1000); 25 | console.timeEnd('query 1000 closest'); 26 | 27 | console.time('query 50000 closest'); 28 | sphereKnnLookup(34.4363, -119.7051, 50000); 29 | console.timeEnd('query 50000 closest'); 30 | 31 | console.time(`query all ${n}`); 32 | sphereKnnLookup(34.4363, -119.7051, Infinity); 33 | console.timeEnd(`query all ${n}`); 34 | 35 | console.time(`${k} random queries of 1 closest`); 36 | for (let i = 0; i < k; i++) sphereKnnLookup(randomPoints[i].lat, randomPoints[i].lon, 1); 37 | console.timeEnd(`${k} random queries of 1 closest`); 38 | -------------------------------------------------------------------------------- /bench/bench-vptree.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | import cities from 'all-the-cities'; 4 | import VPTreeFactory from 'vptree'; 5 | 6 | console.log('=== vptree.js benchmark ==='); 7 | 8 | const rad = Math.PI / 180; 9 | 10 | const n = cities.length; 11 | const k = 1000; 12 | 13 | const randomPoints = []; 14 | for (let i = 0; i < k; i++) randomPoints.push({ 15 | loc: { 16 | coordinates: [ 17 | -180 + 360 * Math.random(), 18 | -60 + 140 * Math.random() 19 | ] 20 | } 21 | }); 22 | 23 | console.time(`index ${n} points`); 24 | const index = VPTreeFactory.build(cities, distance); 25 | console.timeEnd(`index ${n} points`); 26 | 27 | console.time('query 1000 closest'); 28 | index.search({loc: {coordinates: [-119.7051, 34.4363]}}, 1000); 29 | console.timeEnd('query 1000 closest'); 30 | 31 | console.time('query 50000 closest'); 32 | index.search({loc: {coordinates: [-119.7051, 34.4363]}}, 50000); 33 | console.timeEnd('query 50000 closest'); 34 | 35 | console.time(`query all ${n}`); 36 | index.search({loc: {coordinates: [-119.7051, 34.4363]}}, Infinity); 37 | console.timeEnd(`query all ${n}`); 38 | 39 | console.time(`${k} random queries of 1 closest`); 40 | for (let i = 0; i < k; i++) index.search(randomPoints[i], 1); 41 | console.timeEnd(`${k} random queries of 1 closest`); 42 | 43 | function distance(a, b) { 44 | const [lon1, lat1] = a.loc.coordinates; 45 | const [lon2, lat2] = b.loc.coordinates; 46 | const d = Math.sin(lat1 * rad) * Math.sin(lat2 * rad) + 47 | Math.cos(lat1 * rad) * Math.cos(lat2 * rad) * Math.cos((lon1 - lon2) * rad); 48 | return 6371 * Math.acos(Math.min(d, 1)); 49 | } 50 | -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | export {default} from 'eslint-config-mourner'; 2 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 2 | import TinyQueue from 'tinyqueue'; 3 | 4 | const earthRadius = 6371; 5 | const rad = Math.PI / 180; 6 | 7 | export function around(index, lng, lat, maxResults = Infinity, maxDistance = Infinity, predicate) { 8 | let maxHaverSinDist = 1; 9 | const result = []; 10 | 11 | if (maxResults === undefined) maxResults = Infinity; 12 | if (maxDistance !== undefined) maxHaverSinDist = haverSin(maxDistance / earthRadius); 13 | 14 | // a distance-sorted priority queue that will contain both points and kd-tree nodes 15 | const q = new TinyQueue([], compareDist); 16 | 17 | // an object that represents the top kd-tree node (the whole Earth) 18 | let node = { 19 | left: 0, // left index in the kd-tree array 20 | right: index.ids.length - 1, // right index 21 | axis: 0, // 0 for longitude axis and 1 for latitude axis 22 | dist: 0, // will hold the lower bound of children's distances to the query point 23 | minLng: -180, // bounding box of the node 24 | minLat: -90, 25 | maxLng: 180, 26 | maxLat: 90 27 | }; 28 | 29 | const cosLat = Math.cos(lat * rad); 30 | 31 | while (node) { 32 | const right = node.right; 33 | const left = node.left; 34 | 35 | if (right - left <= index.nodeSize) { // leaf node 36 | 37 | // add all points of the leaf node to the queue 38 | for (let i = left; i <= right; i++) { 39 | const id = index.ids[i]; 40 | if (!predicate || predicate(id)) { 41 | const dist = haverSinDist(lng, lat, index.coords[2 * i], index.coords[2 * i + 1], cosLat); 42 | q.push({id, dist}); 43 | } 44 | } 45 | 46 | } else { // not a leaf node (has child nodes) 47 | 48 | const m = (left + right) >> 1; // middle index 49 | const midLng = index.coords[2 * m]; 50 | const midLat = index.coords[2 * m + 1]; 51 | 52 | // add middle point to the queue 53 | const id = index.ids[m]; 54 | if (!predicate || predicate(id)) { 55 | const dist = haverSinDist(lng, lat, midLng, midLat, cosLat); 56 | q.push({id, dist}); 57 | } 58 | 59 | const nextAxis = (node.axis + 1) % 2; 60 | 61 | // first half of the node 62 | const leftNode = { 63 | left, 64 | right: m - 1, 65 | axis: nextAxis, 66 | minLng: node.minLng, 67 | minLat: node.minLat, 68 | maxLng: node.axis === 0 ? midLng : node.maxLng, 69 | maxLat: node.axis === 1 ? midLat : node.maxLat, 70 | dist: 0 71 | }; 72 | // second half of the node 73 | const rightNode = { 74 | left: m + 1, 75 | right, 76 | axis: nextAxis, 77 | minLng: node.axis === 0 ? midLng : node.minLng, 78 | minLat: node.axis === 1 ? midLat : node.minLat, 79 | maxLng: node.maxLng, 80 | maxLat: node.maxLat, 81 | dist: 0 82 | }; 83 | 84 | leftNode.dist = boxDist(lng, lat, cosLat, leftNode); 85 | rightNode.dist = boxDist(lng, lat, cosLat, rightNode); 86 | 87 | // add child nodes to the queue 88 | q.push(leftNode); 89 | q.push(rightNode); 90 | } 91 | 92 | // fetch closest points from the queue; they're guaranteed to be closer 93 | // than all remaining points (both individual and those in kd-tree nodes), 94 | // since each node's distance is a lower bound of distances to its children 95 | while (q.length && q.peek().id != null) { 96 | const candidate = q.pop(); 97 | if (candidate.dist > maxHaverSinDist) return result; 98 | result.push(candidate.id); 99 | if (result.length === maxResults) return result; 100 | } 101 | 102 | // the next closest kd-tree node 103 | node = q.pop(); 104 | } 105 | 106 | return result; 107 | } 108 | 109 | // lower bound for distance from a location to points inside a bounding box 110 | function boxDist(lng, lat, cosLat, node) { 111 | const minLng = node.minLng; 112 | const maxLng = node.maxLng; 113 | const minLat = node.minLat; 114 | const maxLat = node.maxLat; 115 | 116 | // query point is between minimum and maximum longitudes 117 | if (lng >= minLng && lng <= maxLng) { 118 | if (lat < minLat) return haverSin((lat - minLat) * rad); 119 | if (lat > maxLat) return haverSin((lat - maxLat) * rad); 120 | return 0; 121 | } 122 | 123 | // query point is west or east of the bounding box; 124 | // calculate the extremum for great circle distance from query point to the closest longitude; 125 | const haverSinDLng = Math.min(haverSin((lng - minLng) * rad), haverSin((lng - maxLng) * rad)); 126 | const extremumLat = vertexLat(lat, haverSinDLng); 127 | 128 | // if extremum is inside the box, return the distance to it 129 | if (extremumLat > minLat && extremumLat < maxLat) { 130 | return haverSinDistPartial(haverSinDLng, cosLat, lat, extremumLat); 131 | } 132 | // otherwise return the distan e to one of the bbox corners (whichever is closest) 133 | return Math.min( 134 | haverSinDistPartial(haverSinDLng, cosLat, lat, minLat), 135 | haverSinDistPartial(haverSinDLng, cosLat, lat, maxLat) 136 | ); 137 | } 138 | 139 | function compareDist(a, b) { 140 | return a.dist - b.dist; 141 | } 142 | 143 | function haverSin(theta) { 144 | const s = Math.sin(theta / 2); 145 | return s * s; 146 | } 147 | 148 | function haverSinDistPartial(haverSinDLng, cosLat1, lat1, lat2) { 149 | return cosLat1 * Math.cos(lat2 * rad) * haverSinDLng + haverSin((lat1 - lat2) * rad); 150 | } 151 | 152 | function haverSinDist(lng1, lat1, lng2, lat2, cosLat1) { 153 | const haverSinDLng = haverSin((lng1 - lng2) * rad); 154 | return haverSinDistPartial(haverSinDLng, cosLat1, lat1, lat2); 155 | } 156 | 157 | export function distance(lng1, lat1, lng2, lat2) { 158 | const h = haverSinDist(lng1, lat1, lng2, lat2, Math.cos(lat1 * rad)); 159 | return 2 * earthRadius * Math.asin(Math.sqrt(h)); 160 | } 161 | 162 | function vertexLat(lat, haverSinDLng) { 163 | const cosDLng = 1 - 2 * haverSinDLng; 164 | if (cosDLng <= 0) return lat > 0 ? 90 : -90; 165 | return Math.atan(Math.tan(lat * rad) / cosDLng) / rad; 166 | } 167 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "geokdbush", 3 | "version": "2.0.1", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "geokdbush", 9 | "version": "2.0.1", 10 | "license": "ISC", 11 | "dependencies": { 12 | "tinyqueue": "^2.0.3" 13 | }, 14 | "devDependencies": { 15 | "all-the-cities": "3.1.0", 16 | "eslint": "^9.6.0", 17 | "eslint-config-mourner": "^4.0.1", 18 | "kdbush": "^4.0.2", 19 | "sphere-knn": "^1.4.0", 20 | "vptree": "^1.0.0" 21 | } 22 | }, 23 | "node_modules/@eslint-community/eslint-utils": { 24 | "version": "4.4.0", 25 | "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", 26 | "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", 27 | "dev": true, 28 | "license": "MIT", 29 | "dependencies": { 30 | "eslint-visitor-keys": "^3.3.0" 31 | }, 32 | "engines": { 33 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 34 | }, 35 | "peerDependencies": { 36 | "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" 37 | } 38 | }, 39 | "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { 40 | "version": "3.4.3", 41 | "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", 42 | "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", 43 | "dev": true, 44 | "license": "Apache-2.0", 45 | "engines": { 46 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 47 | }, 48 | "funding": { 49 | "url": "https://opencollective.com/eslint" 50 | } 51 | }, 52 | "node_modules/@eslint-community/regexpp": { 53 | "version": "4.11.0", 54 | "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.0.tgz", 55 | "integrity": "sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==", 56 | "dev": true, 57 | "license": "MIT", 58 | "engines": { 59 | "node": "^12.0.0 || ^14.0.0 || >=16.0.0" 60 | } 61 | }, 62 | "node_modules/@eslint/config-array": { 63 | "version": "0.17.0", 64 | "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.17.0.tgz", 65 | "integrity": "sha512-A68TBu6/1mHHuc5YJL0U0VVeGNiklLAL6rRmhTCP2B5XjWLMnrX+HkO+IAXyHvks5cyyY1jjK5ITPQ1HGS2EVA==", 66 | "dev": true, 67 | "license": "Apache-2.0", 68 | "dependencies": { 69 | "@eslint/object-schema": "^2.1.4", 70 | "debug": "^4.3.1", 71 | "minimatch": "^3.1.2" 72 | }, 73 | "engines": { 74 | "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 75 | } 76 | }, 77 | "node_modules/@eslint/eslintrc": { 78 | "version": "3.1.0", 79 | "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz", 80 | "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==", 81 | "dev": true, 82 | "license": "MIT", 83 | "dependencies": { 84 | "ajv": "^6.12.4", 85 | "debug": "^4.3.2", 86 | "espree": "^10.0.1", 87 | "globals": "^14.0.0", 88 | "ignore": "^5.2.0", 89 | "import-fresh": "^3.2.1", 90 | "js-yaml": "^4.1.0", 91 | "minimatch": "^3.1.2", 92 | "strip-json-comments": "^3.1.1" 93 | }, 94 | "engines": { 95 | "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 96 | }, 97 | "funding": { 98 | "url": "https://opencollective.com/eslint" 99 | } 100 | }, 101 | "node_modules/@eslint/js": { 102 | "version": "9.6.0", 103 | "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.6.0.tgz", 104 | "integrity": "sha512-D9B0/3vNg44ZeWbYMpBoXqNP4j6eQD5vNwIlGAuFRRzK/WtT/jvDQW3Bi9kkf3PMDMlM7Yi+73VLUsn5bJcl8A==", 105 | "dev": true, 106 | "license": "MIT", 107 | "engines": { 108 | "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 109 | } 110 | }, 111 | "node_modules/@eslint/object-schema": { 112 | "version": "2.1.4", 113 | "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.4.tgz", 114 | "integrity": "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==", 115 | "dev": true, 116 | "license": "Apache-2.0", 117 | "engines": { 118 | "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 119 | } 120 | }, 121 | "node_modules/@humanwhocodes/module-importer": { 122 | "version": "1.0.1", 123 | "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", 124 | "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", 125 | "dev": true, 126 | "license": "Apache-2.0", 127 | "engines": { 128 | "node": ">=12.22" 129 | }, 130 | "funding": { 131 | "type": "github", 132 | "url": "https://github.com/sponsors/nzakas" 133 | } 134 | }, 135 | "node_modules/@humanwhocodes/retry": { 136 | "version": "0.3.0", 137 | "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.0.tgz", 138 | "integrity": "sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==", 139 | "dev": true, 140 | "license": "Apache-2.0", 141 | "engines": { 142 | "node": ">=18.18" 143 | }, 144 | "funding": { 145 | "type": "github", 146 | "url": "https://github.com/sponsors/nzakas" 147 | } 148 | }, 149 | "node_modules/@nodelib/fs.scandir": { 150 | "version": "2.1.5", 151 | "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", 152 | "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", 153 | "dev": true, 154 | "license": "MIT", 155 | "dependencies": { 156 | "@nodelib/fs.stat": "2.0.5", 157 | "run-parallel": "^1.1.9" 158 | }, 159 | "engines": { 160 | "node": ">= 8" 161 | } 162 | }, 163 | "node_modules/@nodelib/fs.stat": { 164 | "version": "2.0.5", 165 | "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", 166 | "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", 167 | "dev": true, 168 | "license": "MIT", 169 | "engines": { 170 | "node": ">= 8" 171 | } 172 | }, 173 | "node_modules/@nodelib/fs.walk": { 174 | "version": "1.2.8", 175 | "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", 176 | "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", 177 | "dev": true, 178 | "license": "MIT", 179 | "dependencies": { 180 | "@nodelib/fs.scandir": "2.1.5", 181 | "fastq": "^1.6.0" 182 | }, 183 | "engines": { 184 | "node": ">= 8" 185 | } 186 | }, 187 | "node_modules/@stylistic/eslint-plugin-js": { 188 | "version": "2.3.0", 189 | "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-2.3.0.tgz", 190 | "integrity": "sha512-lQwoiYb0Fs6Yc5QS3uT8+T9CPKK2Eoxc3H8EnYJgM26v/DgtW+1lvy2WNgyBflU+ThShZaHm3a6CdD9QeKx23w==", 191 | "dev": true, 192 | "license": "MIT", 193 | "dependencies": { 194 | "@types/eslint": "^8.56.10", 195 | "acorn": "^8.11.3", 196 | "eslint-visitor-keys": "^4.0.0", 197 | "espree": "^10.0.1" 198 | }, 199 | "engines": { 200 | "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 201 | }, 202 | "peerDependencies": { 203 | "eslint": ">=8.40.0" 204 | } 205 | }, 206 | "node_modules/@types/eslint": { 207 | "version": "8.56.10", 208 | "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.10.tgz", 209 | "integrity": "sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ==", 210 | "dev": true, 211 | "license": "MIT", 212 | "dependencies": { 213 | "@types/estree": "*", 214 | "@types/json-schema": "*" 215 | } 216 | }, 217 | "node_modules/@types/estree": { 218 | "version": "1.0.5", 219 | "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", 220 | "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", 221 | "dev": true, 222 | "license": "MIT" 223 | }, 224 | "node_modules/@types/json-schema": { 225 | "version": "7.0.15", 226 | "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", 227 | "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", 228 | "dev": true, 229 | "license": "MIT" 230 | }, 231 | "node_modules/acorn": { 232 | "version": "8.12.0", 233 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.0.tgz", 234 | "integrity": "sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw==", 235 | "dev": true, 236 | "license": "MIT", 237 | "bin": { 238 | "acorn": "bin/acorn" 239 | }, 240 | "engines": { 241 | "node": ">=0.4.0" 242 | } 243 | }, 244 | "node_modules/acorn-jsx": { 245 | "version": "5.3.2", 246 | "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", 247 | "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", 248 | "dev": true, 249 | "license": "MIT", 250 | "peerDependencies": { 251 | "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" 252 | } 253 | }, 254 | "node_modules/ajv": { 255 | "version": "6.12.6", 256 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", 257 | "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", 258 | "dev": true, 259 | "license": "MIT", 260 | "dependencies": { 261 | "fast-deep-equal": "^3.1.1", 262 | "fast-json-stable-stringify": "^2.0.0", 263 | "json-schema-traverse": "^0.4.1", 264 | "uri-js": "^4.2.2" 265 | }, 266 | "funding": { 267 | "type": "github", 268 | "url": "https://github.com/sponsors/epoberezkin" 269 | } 270 | }, 271 | "node_modules/all-the-cities": { 272 | "version": "3.1.0", 273 | "resolved": "https://registry.npmjs.org/all-the-cities/-/all-the-cities-3.1.0.tgz", 274 | "integrity": "sha512-Jx+mZR7lUVSX+qk785T3MRkAJYMvUpOmeNQSCbUl1WcCNpBUKJsQJ6uZzOl3/bWXVIUx1UhDFs8ASDHSI3rvzg==", 275 | "dev": true, 276 | "license": "MIT", 277 | "dependencies": { 278 | "pbf": "^3.2.1" 279 | } 280 | }, 281 | "node_modules/ansi-regex": { 282 | "version": "5.0.1", 283 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 284 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", 285 | "dev": true, 286 | "license": "MIT", 287 | "engines": { 288 | "node": ">=8" 289 | } 290 | }, 291 | "node_modules/ansi-styles": { 292 | "version": "4.3.0", 293 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 294 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 295 | "dev": true, 296 | "license": "MIT", 297 | "dependencies": { 298 | "color-convert": "^2.0.1" 299 | }, 300 | "engines": { 301 | "node": ">=8" 302 | }, 303 | "funding": { 304 | "url": "https://github.com/chalk/ansi-styles?sponsor=1" 305 | } 306 | }, 307 | "node_modules/argparse": { 308 | "version": "2.0.1", 309 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", 310 | "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", 311 | "dev": true, 312 | "license": "Python-2.0" 313 | }, 314 | "node_modules/balanced-match": { 315 | "version": "1.0.2", 316 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 317 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", 318 | "dev": true, 319 | "license": "MIT" 320 | }, 321 | "node_modules/brace-expansion": { 322 | "version": "1.1.11", 323 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 324 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 325 | "dev": true, 326 | "license": "MIT", 327 | "dependencies": { 328 | "balanced-match": "^1.0.0", 329 | "concat-map": "0.0.1" 330 | } 331 | }, 332 | "node_modules/callsites": { 333 | "version": "3.1.0", 334 | "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", 335 | "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", 336 | "dev": true, 337 | "license": "MIT", 338 | "engines": { 339 | "node": ">=6" 340 | } 341 | }, 342 | "node_modules/chalk": { 343 | "version": "4.1.2", 344 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", 345 | "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", 346 | "dev": true, 347 | "license": "MIT", 348 | "dependencies": { 349 | "ansi-styles": "^4.1.0", 350 | "supports-color": "^7.1.0" 351 | }, 352 | "engines": { 353 | "node": ">=10" 354 | }, 355 | "funding": { 356 | "url": "https://github.com/chalk/chalk?sponsor=1" 357 | } 358 | }, 359 | "node_modules/color-convert": { 360 | "version": "2.0.1", 361 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 362 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 363 | "dev": true, 364 | "license": "MIT", 365 | "dependencies": { 366 | "color-name": "~1.1.4" 367 | }, 368 | "engines": { 369 | "node": ">=7.0.0" 370 | } 371 | }, 372 | "node_modules/color-name": { 373 | "version": "1.1.4", 374 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 375 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 376 | "dev": true, 377 | "license": "MIT" 378 | }, 379 | "node_modules/concat-map": { 380 | "version": "0.0.1", 381 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 382 | "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", 383 | "dev": true, 384 | "license": "MIT" 385 | }, 386 | "node_modules/cross-spawn": { 387 | "version": "7.0.3", 388 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", 389 | "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", 390 | "dev": true, 391 | "license": "MIT", 392 | "dependencies": { 393 | "path-key": "^3.1.0", 394 | "shebang-command": "^2.0.0", 395 | "which": "^2.0.1" 396 | }, 397 | "engines": { 398 | "node": ">= 8" 399 | } 400 | }, 401 | "node_modules/debug": { 402 | "version": "4.3.5", 403 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", 404 | "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", 405 | "dev": true, 406 | "license": "MIT", 407 | "dependencies": { 408 | "ms": "2.1.2" 409 | }, 410 | "engines": { 411 | "node": ">=6.0" 412 | }, 413 | "peerDependenciesMeta": { 414 | "supports-color": { 415 | "optional": true 416 | } 417 | } 418 | }, 419 | "node_modules/deep-is": { 420 | "version": "0.1.4", 421 | "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", 422 | "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", 423 | "dev": true, 424 | "license": "MIT" 425 | }, 426 | "node_modules/escape-string-regexp": { 427 | "version": "4.0.0", 428 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", 429 | "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", 430 | "dev": true, 431 | "license": "MIT", 432 | "engines": { 433 | "node": ">=10" 434 | }, 435 | "funding": { 436 | "url": "https://github.com/sponsors/sindresorhus" 437 | } 438 | }, 439 | "node_modules/eslint": { 440 | "version": "9.6.0", 441 | "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.6.0.tgz", 442 | "integrity": "sha512-ElQkdLMEEqQNM9Njff+2Y4q2afHk7JpkPvrd7Xh7xefwgQynqPxwf55J7di9+MEibWUGdNjFF9ITG9Pck5M84w==", 443 | "dev": true, 444 | "license": "MIT", 445 | "dependencies": { 446 | "@eslint-community/eslint-utils": "^4.2.0", 447 | "@eslint-community/regexpp": "^4.6.1", 448 | "@eslint/config-array": "^0.17.0", 449 | "@eslint/eslintrc": "^3.1.0", 450 | "@eslint/js": "9.6.0", 451 | "@humanwhocodes/module-importer": "^1.0.1", 452 | "@humanwhocodes/retry": "^0.3.0", 453 | "@nodelib/fs.walk": "^1.2.8", 454 | "ajv": "^6.12.4", 455 | "chalk": "^4.0.0", 456 | "cross-spawn": "^7.0.2", 457 | "debug": "^4.3.2", 458 | "escape-string-regexp": "^4.0.0", 459 | "eslint-scope": "^8.0.1", 460 | "eslint-visitor-keys": "^4.0.0", 461 | "espree": "^10.1.0", 462 | "esquery": "^1.5.0", 463 | "esutils": "^2.0.2", 464 | "fast-deep-equal": "^3.1.3", 465 | "file-entry-cache": "^8.0.0", 466 | "find-up": "^5.0.0", 467 | "glob-parent": "^6.0.2", 468 | "ignore": "^5.2.0", 469 | "imurmurhash": "^0.1.4", 470 | "is-glob": "^4.0.0", 471 | "is-path-inside": "^3.0.3", 472 | "json-stable-stringify-without-jsonify": "^1.0.1", 473 | "levn": "^0.4.1", 474 | "lodash.merge": "^4.6.2", 475 | "minimatch": "^3.1.2", 476 | "natural-compare": "^1.4.0", 477 | "optionator": "^0.9.3", 478 | "strip-ansi": "^6.0.1", 479 | "text-table": "^0.2.0" 480 | }, 481 | "bin": { 482 | "eslint": "bin/eslint.js" 483 | }, 484 | "engines": { 485 | "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 486 | }, 487 | "funding": { 488 | "url": "https://eslint.org/donate" 489 | } 490 | }, 491 | "node_modules/eslint-config-mourner": { 492 | "version": "4.0.1", 493 | "resolved": "https://registry.npmjs.org/eslint-config-mourner/-/eslint-config-mourner-4.0.1.tgz", 494 | "integrity": "sha512-seylu4qdUc7kabx42zEedJiCyRDYqwsZLFPPnMRntmOFOQv6cKoM+FqhYpLKZ5dF7A7wW6mCympQSkg6RVxGlg==", 495 | "dev": true, 496 | "license": "ISC", 497 | "dependencies": { 498 | "@eslint/js": "^9.5.0", 499 | "@stylistic/eslint-plugin-js": "^2.2.2" 500 | } 501 | }, 502 | "node_modules/eslint-scope": { 503 | "version": "8.0.1", 504 | "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.0.1.tgz", 505 | "integrity": "sha512-pL8XjgP4ZOmmwfFE8mEhSxA7ZY4C+LWyqjQ3o4yWkkmD0qcMT9kkW3zWHOczhWcjTSgqycYAgwSlXvZltv65og==", 506 | "dev": true, 507 | "license": "BSD-2-Clause", 508 | "dependencies": { 509 | "esrecurse": "^4.3.0", 510 | "estraverse": "^5.2.0" 511 | }, 512 | "engines": { 513 | "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 514 | }, 515 | "funding": { 516 | "url": "https://opencollective.com/eslint" 517 | } 518 | }, 519 | "node_modules/eslint-visitor-keys": { 520 | "version": "4.0.0", 521 | "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz", 522 | "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==", 523 | "dev": true, 524 | "license": "Apache-2.0", 525 | "engines": { 526 | "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 527 | }, 528 | "funding": { 529 | "url": "https://opencollective.com/eslint" 530 | } 531 | }, 532 | "node_modules/espree": { 533 | "version": "10.1.0", 534 | "resolved": "https://registry.npmjs.org/espree/-/espree-10.1.0.tgz", 535 | "integrity": "sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA==", 536 | "dev": true, 537 | "license": "BSD-2-Clause", 538 | "dependencies": { 539 | "acorn": "^8.12.0", 540 | "acorn-jsx": "^5.3.2", 541 | "eslint-visitor-keys": "^4.0.0" 542 | }, 543 | "engines": { 544 | "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 545 | }, 546 | "funding": { 547 | "url": "https://opencollective.com/eslint" 548 | } 549 | }, 550 | "node_modules/esquery": { 551 | "version": "1.5.0", 552 | "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", 553 | "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", 554 | "dev": true, 555 | "license": "BSD-3-Clause", 556 | "dependencies": { 557 | "estraverse": "^5.1.0" 558 | }, 559 | "engines": { 560 | "node": ">=0.10" 561 | } 562 | }, 563 | "node_modules/esrecurse": { 564 | "version": "4.3.0", 565 | "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", 566 | "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", 567 | "dev": true, 568 | "license": "BSD-2-Clause", 569 | "dependencies": { 570 | "estraverse": "^5.2.0" 571 | }, 572 | "engines": { 573 | "node": ">=4.0" 574 | } 575 | }, 576 | "node_modules/estraverse": { 577 | "version": "5.3.0", 578 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", 579 | "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", 580 | "dev": true, 581 | "license": "BSD-2-Clause", 582 | "engines": { 583 | "node": ">=4.0" 584 | } 585 | }, 586 | "node_modules/esutils": { 587 | "version": "2.0.3", 588 | "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", 589 | "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", 590 | "dev": true, 591 | "license": "BSD-2-Clause", 592 | "engines": { 593 | "node": ">=0.10.0" 594 | } 595 | }, 596 | "node_modules/fast-deep-equal": { 597 | "version": "3.1.3", 598 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", 599 | "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", 600 | "dev": true, 601 | "license": "MIT" 602 | }, 603 | "node_modules/fast-json-stable-stringify": { 604 | "version": "2.1.0", 605 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", 606 | "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", 607 | "dev": true, 608 | "license": "MIT" 609 | }, 610 | "node_modules/fast-levenshtein": { 611 | "version": "2.0.6", 612 | "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", 613 | "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", 614 | "dev": true, 615 | "license": "MIT" 616 | }, 617 | "node_modules/fastq": { 618 | "version": "1.17.1", 619 | "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", 620 | "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", 621 | "dev": true, 622 | "license": "ISC", 623 | "dependencies": { 624 | "reusify": "^1.0.4" 625 | } 626 | }, 627 | "node_modules/file-entry-cache": { 628 | "version": "8.0.0", 629 | "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", 630 | "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", 631 | "dev": true, 632 | "license": "MIT", 633 | "dependencies": { 634 | "flat-cache": "^4.0.0" 635 | }, 636 | "engines": { 637 | "node": ">=16.0.0" 638 | } 639 | }, 640 | "node_modules/find-up": { 641 | "version": "5.0.0", 642 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", 643 | "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", 644 | "dev": true, 645 | "license": "MIT", 646 | "dependencies": { 647 | "locate-path": "^6.0.0", 648 | "path-exists": "^4.0.0" 649 | }, 650 | "engines": { 651 | "node": ">=10" 652 | }, 653 | "funding": { 654 | "url": "https://github.com/sponsors/sindresorhus" 655 | } 656 | }, 657 | "node_modules/flat-cache": { 658 | "version": "4.0.1", 659 | "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", 660 | "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", 661 | "dev": true, 662 | "license": "MIT", 663 | "dependencies": { 664 | "flatted": "^3.2.9", 665 | "keyv": "^4.5.4" 666 | }, 667 | "engines": { 668 | "node": ">=16" 669 | } 670 | }, 671 | "node_modules/flatted": { 672 | "version": "3.3.1", 673 | "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", 674 | "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", 675 | "dev": true, 676 | "license": "ISC" 677 | }, 678 | "node_modules/glob-parent": { 679 | "version": "6.0.2", 680 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", 681 | "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", 682 | "dev": true, 683 | "license": "ISC", 684 | "dependencies": { 685 | "is-glob": "^4.0.3" 686 | }, 687 | "engines": { 688 | "node": ">=10.13.0" 689 | } 690 | }, 691 | "node_modules/globals": { 692 | "version": "14.0.0", 693 | "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", 694 | "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", 695 | "dev": true, 696 | "license": "MIT", 697 | "engines": { 698 | "node": ">=18" 699 | }, 700 | "funding": { 701 | "url": "https://github.com/sponsors/sindresorhus" 702 | } 703 | }, 704 | "node_modules/has-flag": { 705 | "version": "4.0.0", 706 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 707 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 708 | "dev": true, 709 | "license": "MIT", 710 | "engines": { 711 | "node": ">=8" 712 | } 713 | }, 714 | "node_modules/ieee754": { 715 | "version": "1.2.1", 716 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", 717 | "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", 718 | "dev": true, 719 | "funding": [ 720 | { 721 | "type": "github", 722 | "url": "https://github.com/sponsors/feross" 723 | }, 724 | { 725 | "type": "patreon", 726 | "url": "https://www.patreon.com/feross" 727 | }, 728 | { 729 | "type": "consulting", 730 | "url": "https://feross.org/support" 731 | } 732 | ], 733 | "license": "BSD-3-Clause" 734 | }, 735 | "node_modules/ignore": { 736 | "version": "5.3.1", 737 | "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", 738 | "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", 739 | "dev": true, 740 | "license": "MIT", 741 | "engines": { 742 | "node": ">= 4" 743 | } 744 | }, 745 | "node_modules/import-fresh": { 746 | "version": "3.3.0", 747 | "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", 748 | "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", 749 | "dev": true, 750 | "license": "MIT", 751 | "dependencies": { 752 | "parent-module": "^1.0.0", 753 | "resolve-from": "^4.0.0" 754 | }, 755 | "engines": { 756 | "node": ">=6" 757 | }, 758 | "funding": { 759 | "url": "https://github.com/sponsors/sindresorhus" 760 | } 761 | }, 762 | "node_modules/imurmurhash": { 763 | "version": "0.1.4", 764 | "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", 765 | "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", 766 | "dev": true, 767 | "license": "MIT", 768 | "engines": { 769 | "node": ">=0.8.19" 770 | } 771 | }, 772 | "node_modules/is-extglob": { 773 | "version": "2.1.1", 774 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 775 | "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", 776 | "dev": true, 777 | "license": "MIT", 778 | "engines": { 779 | "node": ">=0.10.0" 780 | } 781 | }, 782 | "node_modules/is-glob": { 783 | "version": "4.0.3", 784 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 785 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 786 | "dev": true, 787 | "license": "MIT", 788 | "dependencies": { 789 | "is-extglob": "^2.1.1" 790 | }, 791 | "engines": { 792 | "node": ">=0.10.0" 793 | } 794 | }, 795 | "node_modules/is-path-inside": { 796 | "version": "3.0.3", 797 | "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", 798 | "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", 799 | "dev": true, 800 | "license": "MIT", 801 | "engines": { 802 | "node": ">=8" 803 | } 804 | }, 805 | "node_modules/isexe": { 806 | "version": "2.0.0", 807 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 808 | "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", 809 | "dev": true, 810 | "license": "ISC" 811 | }, 812 | "node_modules/js-yaml": { 813 | "version": "4.1.0", 814 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", 815 | "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", 816 | "dev": true, 817 | "license": "MIT", 818 | "dependencies": { 819 | "argparse": "^2.0.1" 820 | }, 821 | "bin": { 822 | "js-yaml": "bin/js-yaml.js" 823 | } 824 | }, 825 | "node_modules/json-buffer": { 826 | "version": "3.0.1", 827 | "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", 828 | "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", 829 | "dev": true, 830 | "license": "MIT" 831 | }, 832 | "node_modules/json-schema-traverse": { 833 | "version": "0.4.1", 834 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", 835 | "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", 836 | "dev": true, 837 | "license": "MIT" 838 | }, 839 | "node_modules/json-stable-stringify-without-jsonify": { 840 | "version": "1.0.1", 841 | "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", 842 | "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", 843 | "dev": true, 844 | "license": "MIT" 845 | }, 846 | "node_modules/kdbush": { 847 | "version": "4.0.2", 848 | "resolved": "https://registry.npmjs.org/kdbush/-/kdbush-4.0.2.tgz", 849 | "integrity": "sha512-WbCVYJ27Sz8zi9Q7Q0xHC+05iwkm3Znipc2XTlrnJbsHMYktW4hPhXUE8Ys1engBrvffoSCqbil1JQAa7clRpA==", 850 | "dev": true, 851 | "license": "ISC" 852 | }, 853 | "node_modules/keyv": { 854 | "version": "4.5.4", 855 | "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", 856 | "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", 857 | "dev": true, 858 | "license": "MIT", 859 | "dependencies": { 860 | "json-buffer": "3.0.1" 861 | } 862 | }, 863 | "node_modules/levn": { 864 | "version": "0.4.1", 865 | "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", 866 | "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", 867 | "dev": true, 868 | "license": "MIT", 869 | "dependencies": { 870 | "prelude-ls": "^1.2.1", 871 | "type-check": "~0.4.0" 872 | }, 873 | "engines": { 874 | "node": ">= 0.8.0" 875 | } 876 | }, 877 | "node_modules/locate-path": { 878 | "version": "6.0.0", 879 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", 880 | "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", 881 | "dev": true, 882 | "license": "MIT", 883 | "dependencies": { 884 | "p-locate": "^5.0.0" 885 | }, 886 | "engines": { 887 | "node": ">=10" 888 | }, 889 | "funding": { 890 | "url": "https://github.com/sponsors/sindresorhus" 891 | } 892 | }, 893 | "node_modules/lodash.merge": { 894 | "version": "4.6.2", 895 | "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", 896 | "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", 897 | "dev": true, 898 | "license": "MIT" 899 | }, 900 | "node_modules/minimatch": { 901 | "version": "3.1.2", 902 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 903 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 904 | "dev": true, 905 | "license": "ISC", 906 | "dependencies": { 907 | "brace-expansion": "^1.1.7" 908 | }, 909 | "engines": { 910 | "node": "*" 911 | } 912 | }, 913 | "node_modules/ms": { 914 | "version": "2.1.2", 915 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 916 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", 917 | "dev": true, 918 | "license": "MIT" 919 | }, 920 | "node_modules/natural-compare": { 921 | "version": "1.4.0", 922 | "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", 923 | "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", 924 | "dev": true, 925 | "license": "MIT" 926 | }, 927 | "node_modules/optionator": { 928 | "version": "0.9.4", 929 | "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", 930 | "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", 931 | "dev": true, 932 | "license": "MIT", 933 | "dependencies": { 934 | "deep-is": "^0.1.3", 935 | "fast-levenshtein": "^2.0.6", 936 | "levn": "^0.4.1", 937 | "prelude-ls": "^1.2.1", 938 | "type-check": "^0.4.0", 939 | "word-wrap": "^1.2.5" 940 | }, 941 | "engines": { 942 | "node": ">= 0.8.0" 943 | } 944 | }, 945 | "node_modules/p-limit": { 946 | "version": "3.1.0", 947 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", 948 | "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", 949 | "dev": true, 950 | "license": "MIT", 951 | "dependencies": { 952 | "yocto-queue": "^0.1.0" 953 | }, 954 | "engines": { 955 | "node": ">=10" 956 | }, 957 | "funding": { 958 | "url": "https://github.com/sponsors/sindresorhus" 959 | } 960 | }, 961 | "node_modules/p-locate": { 962 | "version": "5.0.0", 963 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", 964 | "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", 965 | "dev": true, 966 | "license": "MIT", 967 | "dependencies": { 968 | "p-limit": "^3.0.2" 969 | }, 970 | "engines": { 971 | "node": ">=10" 972 | }, 973 | "funding": { 974 | "url": "https://github.com/sponsors/sindresorhus" 975 | } 976 | }, 977 | "node_modules/parent-module": { 978 | "version": "1.0.1", 979 | "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", 980 | "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", 981 | "dev": true, 982 | "license": "MIT", 983 | "dependencies": { 984 | "callsites": "^3.0.0" 985 | }, 986 | "engines": { 987 | "node": ">=6" 988 | } 989 | }, 990 | "node_modules/path-exists": { 991 | "version": "4.0.0", 992 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", 993 | "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", 994 | "dev": true, 995 | "license": "MIT", 996 | "engines": { 997 | "node": ">=8" 998 | } 999 | }, 1000 | "node_modules/path-key": { 1001 | "version": "3.1.1", 1002 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", 1003 | "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", 1004 | "dev": true, 1005 | "license": "MIT", 1006 | "engines": { 1007 | "node": ">=8" 1008 | } 1009 | }, 1010 | "node_modules/pbf": { 1011 | "version": "3.2.1", 1012 | "resolved": "https://registry.npmjs.org/pbf/-/pbf-3.2.1.tgz", 1013 | "integrity": "sha512-ClrV7pNOn7rtmoQVF4TS1vyU0WhYRnP92fzbfF75jAIwpnzdJXf8iTd4CMEqO4yUenH6NDqLiwjqlh6QgZzgLQ==", 1014 | "dev": true, 1015 | "license": "BSD-3-Clause", 1016 | "dependencies": { 1017 | "ieee754": "^1.1.12", 1018 | "resolve-protobuf-schema": "^2.1.0" 1019 | }, 1020 | "bin": { 1021 | "pbf": "bin/pbf" 1022 | } 1023 | }, 1024 | "node_modules/prelude-ls": { 1025 | "version": "1.2.1", 1026 | "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", 1027 | "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", 1028 | "dev": true, 1029 | "license": "MIT", 1030 | "engines": { 1031 | "node": ">= 0.8.0" 1032 | } 1033 | }, 1034 | "node_modules/protocol-buffers-schema": { 1035 | "version": "3.6.0", 1036 | "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz", 1037 | "integrity": "sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw==", 1038 | "dev": true, 1039 | "license": "MIT" 1040 | }, 1041 | "node_modules/punycode": { 1042 | "version": "2.3.1", 1043 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", 1044 | "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", 1045 | "dev": true, 1046 | "license": "MIT", 1047 | "engines": { 1048 | "node": ">=6" 1049 | } 1050 | }, 1051 | "node_modules/queue-microtask": { 1052 | "version": "1.2.3", 1053 | "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", 1054 | "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", 1055 | "dev": true, 1056 | "funding": [ 1057 | { 1058 | "type": "github", 1059 | "url": "https://github.com/sponsors/feross" 1060 | }, 1061 | { 1062 | "type": "patreon", 1063 | "url": "https://www.patreon.com/feross" 1064 | }, 1065 | { 1066 | "type": "consulting", 1067 | "url": "https://feross.org/support" 1068 | } 1069 | ], 1070 | "license": "MIT" 1071 | }, 1072 | "node_modules/resolve-from": { 1073 | "version": "4.0.0", 1074 | "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", 1075 | "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", 1076 | "dev": true, 1077 | "license": "MIT", 1078 | "engines": { 1079 | "node": ">=4" 1080 | } 1081 | }, 1082 | "node_modules/resolve-protobuf-schema": { 1083 | "version": "2.1.0", 1084 | "resolved": "https://registry.npmjs.org/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz", 1085 | "integrity": "sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==", 1086 | "dev": true, 1087 | "license": "MIT", 1088 | "dependencies": { 1089 | "protocol-buffers-schema": "^3.3.1" 1090 | } 1091 | }, 1092 | "node_modules/reusify": { 1093 | "version": "1.0.4", 1094 | "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", 1095 | "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", 1096 | "dev": true, 1097 | "license": "MIT", 1098 | "engines": { 1099 | "iojs": ">=1.0.0", 1100 | "node": ">=0.10.0" 1101 | } 1102 | }, 1103 | "node_modules/run-parallel": { 1104 | "version": "1.2.0", 1105 | "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", 1106 | "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", 1107 | "dev": true, 1108 | "funding": [ 1109 | { 1110 | "type": "github", 1111 | "url": "https://github.com/sponsors/feross" 1112 | }, 1113 | { 1114 | "type": "patreon", 1115 | "url": "https://www.patreon.com/feross" 1116 | }, 1117 | { 1118 | "type": "consulting", 1119 | "url": "https://feross.org/support" 1120 | } 1121 | ], 1122 | "license": "MIT", 1123 | "dependencies": { 1124 | "queue-microtask": "^1.2.2" 1125 | } 1126 | }, 1127 | "node_modules/shebang-command": { 1128 | "version": "2.0.0", 1129 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", 1130 | "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", 1131 | "dev": true, 1132 | "license": "MIT", 1133 | "dependencies": { 1134 | "shebang-regex": "^3.0.0" 1135 | }, 1136 | "engines": { 1137 | "node": ">=8" 1138 | } 1139 | }, 1140 | "node_modules/shebang-regex": { 1141 | "version": "3.0.0", 1142 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", 1143 | "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", 1144 | "dev": true, 1145 | "license": "MIT", 1146 | "engines": { 1147 | "node": ">=8" 1148 | } 1149 | }, 1150 | "node_modules/sphere-knn": { 1151 | "version": "1.4.0", 1152 | "resolved": "https://registry.npmjs.org/sphere-knn/-/sphere-knn-1.4.0.tgz", 1153 | "integrity": "sha512-sFdsABoSClBxz3DzO2yvHwbbV4zIRvgrIeC1qqWPYzKMtT26xCF/r5xG9Sj7iLFG9GJIt1WnT6VpYaQeRvIzPA==", 1154 | "dev": true, 1155 | "engines": { 1156 | "node": ">=6.0.0" 1157 | } 1158 | }, 1159 | "node_modules/strip-ansi": { 1160 | "version": "6.0.1", 1161 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", 1162 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", 1163 | "dev": true, 1164 | "license": "MIT", 1165 | "dependencies": { 1166 | "ansi-regex": "^5.0.1" 1167 | }, 1168 | "engines": { 1169 | "node": ">=8" 1170 | } 1171 | }, 1172 | "node_modules/strip-json-comments": { 1173 | "version": "3.1.1", 1174 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", 1175 | "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", 1176 | "dev": true, 1177 | "license": "MIT", 1178 | "engines": { 1179 | "node": ">=8" 1180 | }, 1181 | "funding": { 1182 | "url": "https://github.com/sponsors/sindresorhus" 1183 | } 1184 | }, 1185 | "node_modules/supports-color": { 1186 | "version": "7.2.0", 1187 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", 1188 | "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 1189 | "dev": true, 1190 | "license": "MIT", 1191 | "dependencies": { 1192 | "has-flag": "^4.0.0" 1193 | }, 1194 | "engines": { 1195 | "node": ">=8" 1196 | } 1197 | }, 1198 | "node_modules/text-table": { 1199 | "version": "0.2.0", 1200 | "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", 1201 | "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", 1202 | "dev": true, 1203 | "license": "MIT" 1204 | }, 1205 | "node_modules/tinyqueue": { 1206 | "version": "2.0.3", 1207 | "resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-2.0.3.tgz", 1208 | "integrity": "sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA==", 1209 | "license": "ISC" 1210 | }, 1211 | "node_modules/type-check": { 1212 | "version": "0.4.0", 1213 | "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", 1214 | "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", 1215 | "dev": true, 1216 | "license": "MIT", 1217 | "dependencies": { 1218 | "prelude-ls": "^1.2.1" 1219 | }, 1220 | "engines": { 1221 | "node": ">= 0.8.0" 1222 | } 1223 | }, 1224 | "node_modules/uri-js": { 1225 | "version": "4.4.1", 1226 | "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", 1227 | "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", 1228 | "dev": true, 1229 | "license": "BSD-2-Clause", 1230 | "dependencies": { 1231 | "punycode": "^2.1.0" 1232 | } 1233 | }, 1234 | "node_modules/vptree": { 1235 | "version": "1.0.0", 1236 | "resolved": "https://registry.npmjs.org/vptree/-/vptree-1.0.0.tgz", 1237 | "integrity": "sha512-BbaipsfTQxxkmDVVoSdiC6Dkl4NLGIuz+DmzBpoByfbKw6TLtBQkvBykkK2xUa2yzsXt6iMlh0rS/a6W02Hw3A==", 1238 | "dev": true, 1239 | "license": "ISC" 1240 | }, 1241 | "node_modules/which": { 1242 | "version": "2.0.2", 1243 | "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", 1244 | "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", 1245 | "dev": true, 1246 | "license": "ISC", 1247 | "dependencies": { 1248 | "isexe": "^2.0.0" 1249 | }, 1250 | "bin": { 1251 | "node-which": "bin/node-which" 1252 | }, 1253 | "engines": { 1254 | "node": ">= 8" 1255 | } 1256 | }, 1257 | "node_modules/word-wrap": { 1258 | "version": "1.2.5", 1259 | "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", 1260 | "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", 1261 | "dev": true, 1262 | "license": "MIT", 1263 | "engines": { 1264 | "node": ">=0.10.0" 1265 | } 1266 | }, 1267 | "node_modules/yocto-queue": { 1268 | "version": "0.1.0", 1269 | "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", 1270 | "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", 1271 | "dev": true, 1272 | "license": "MIT", 1273 | "engines": { 1274 | "node": ">=10" 1275 | }, 1276 | "funding": { 1277 | "url": "https://github.com/sponsors/sindresorhus" 1278 | } 1279 | } 1280 | } 1281 | } 1282 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "geokdbush", 3 | "version": "2.0.1", 4 | "type": "module", 5 | "main": "index.js", 6 | "author": "Vladimir Agafonkin ", 7 | "license": "ISC", 8 | "dependencies": { 9 | "tinyqueue": "^2.0.3" 10 | }, 11 | "files": [], 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/mourner/geokdbush.git" 15 | }, 16 | "devDependencies": { 17 | "all-the-cities": "3.1.0", 18 | "eslint": "^9.6.0", 19 | "eslint-config-mourner": "^4.0.1", 20 | "kdbush": "^4.0.2", 21 | "sphere-knn": "^1.4.0", 22 | "vptree": "^1.0.0" 23 | }, 24 | "scripts": { 25 | "pretest": "eslint index.js test.js bench", 26 | "test": "node test.js", 27 | "bench": "cd bench && node bench-geokdbush && node bench-sphere-knn && node bench-naive && node bench-vptree" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | import test from 'node:test'; 2 | import assert from 'node:assert/strict'; 3 | import KDBush from 'kdbush'; 4 | import cities from 'all-the-cities'; 5 | import * as geokdbush from './index.js'; 6 | 7 | const index = new KDBush(cities.length); 8 | for (const {loc: {coordinates: [lon, lat]}} of cities) index.add(lon, lat); 9 | index.finish(); 10 | 11 | test('performs search according to maxResults', () => { 12 | const points = geokdbush.around(index, -119.7051, 34.4363, 5); 13 | 14 | assert.equal(points.map(id => cities[id].name).join(', '), 'Mission Canyon, Santa Barbara, Montecito, Summerland, Goleta'); 15 | }); 16 | 17 | test('performs search within maxDistance', () => { 18 | const points = geokdbush.around(index, 30.5, 50.5, Infinity, 20); 19 | 20 | assert.equal(points.map(id => cities[id].name).join(', '), 21 | 'Kyiv, Vyshhorod, Pohreby, Kotsyubyns’ke, Horenka, Sofiyivska Borschagivka, Novi Petrivtsi, Vyshneve, Kriukivschina, Irpin, Hostomel, Chabany, Khotiv, Pukhivka'); 22 | }); 23 | 24 | test('performs search using filter function', () => { 25 | const points = geokdbush.around(index, 30.5, 50.5, 10, Infinity, id => cities[id].population > 200000 && cities[id].country === 'UA'); 26 | 27 | assert.equal(points.map(id => cities[id].name).join(', '), 28 | 'Kyiv, Chernihiv, Zhytomyr, Cherkasy, Vinnytsia, Kropyvnytskyi, Kremenchuk, Khmelnytskyi, Rivne, Poltava'); 29 | }); 30 | 31 | test('performs exhaustive search in correct order', () => { 32 | const points = geokdbush.around(index, 30.5, 50.5); 33 | 34 | const lon = 30.5; 35 | const lat = 50.5; 36 | const sorted = cities 37 | .map(({loc: {coordinates: [plon, plat]}}, id) => ({id, dist: geokdbush.distance(lon, lat, plon, plat)})) 38 | .sort((a, b) => a.dist - b.dist); 39 | 40 | for (let i = 0; i < sorted.length; i++) { 41 | const [plon, plat] = cities[points[i]].loc.coordinates; 42 | const dist = geokdbush.distance(plon, plat, lon, lat); 43 | if (dist !== sorted[i].dist) { 44 | assert.fail(`${cities[points[i]].name } vs ${ cities[sorted[i].id].name}`); 45 | break; 46 | } 47 | } 48 | // all points in correct order 49 | }); 50 | 51 | test('calculates great circle distance', () => { 52 | assert.equal(10131.7396, Math.round(1e4 * geokdbush.distance(30.5, 50.5, -119.7, 34.4)) / 1e4); 53 | }); 54 | --------------------------------------------------------------------------------