├── .github └── workflows │ └── node.yml ├── .gitignore ├── LICENSE.txt ├── README.md ├── bench.js ├── eslint.config.js ├── index.js ├── package-lock.json ├── package.json ├── proto └── vector_tile.proto ├── test ├── fixtures │ ├── 12-1143-1497.vector.pbf │ ├── 14-8801-5371.vector.pbf │ ├── lots-of-tags.vector.pbf │ ├── multi-line.pbf │ ├── multi-point.pbf │ ├── multi-polygon.pbf │ ├── multipolygon-with-closepath.pbf │ ├── multipolygon.pbf │ ├── polygon-with-inner.pbf │ ├── singleton-multi-line.pbf │ ├── singleton-multi-point.pbf │ ├── singleton-multi-polygon.pbf │ ├── stacked-multipolygon.pbf │ ├── zero-line.pbf │ ├── zero-point.pbf │ └── zero-polygon.pbf └── parse.test.js └── tsconfig.json /.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 | index.d.ts 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2024, Mapbox 2 | 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, 9 | this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | * Neither the name of Mapbox nor the names of its contributors 14 | may be used to endorse or promote products derived from this software 15 | without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 21 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 22 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 23 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 24 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 25 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 26 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vector-tile 2 | 3 | This library reads [Mapbox Vector Tiles](https://github.com/mapbox/vector-tile-spec) and allows access to the layers and features. 4 | 5 | ## Example 6 | 7 | ```js 8 | import {VectorTile} from '@mapbox/vector-tile'; 9 | import Protobuf from 'pbf'; 10 | 11 | const tile = new VectorTile(new Protobuf(data)); 12 | 13 | // Contains a map of all layers 14 | tile.layers; 15 | 16 | const landuse = tile.layers.landuse; 17 | 18 | // Amount of features in this layer 19 | landuse.length; 20 | 21 | // Returns the first feature 22 | landuse.feature(0); 23 | ``` 24 | 25 | Vector tiles contained in [serialtiles-spec](https://github.com/mapbox/serialtiles-spec) 26 | are gzip-encoded, so a complete example of parsing them with the native 27 | zlib module would be: 28 | 29 | ```js 30 | import {VectorTile} from '@mapbox/vector-tile'; 31 | import Protobuf from 'pbf'; 32 | import {gunzipSync} from 'zlib'; 33 | 34 | const buffer = gunzipSync(data); 35 | const tile = new VectorTile(new Protobuf(buffer)); 36 | ``` 37 | 38 | ## Install 39 | 40 | To install: 41 | 42 | npm install @mapbox/vector-tile 43 | 44 | 45 | ## API Reference 46 | 47 | ### VectorTile 48 | 49 | An object that parses vector tile data and makes it readable. 50 | 51 | #### Constructor 52 | 53 | - **new VectorTile(protobuf[, end])** — 54 | parses the vector tile data contained in the given [Protobuf](https://github.com/mapbox/pbf) object, 55 | saving resulting layers in the created object as a `layers` property. Optionally accepts end index. 56 | 57 | #### Properties 58 | 59 | - **layers** (Object) — an object containing parsed layers in the form of `{: , ...}`, 60 | where each layer is a `VectorTileLayer` object. 61 | 62 | 63 | ### VectorTileLayer 64 | 65 | An object that contains the data for a single vector tile layer. 66 | 67 | #### Properties 68 | 69 | - **version** (`Number`, default: `1`) 70 | - **name** (`String`) — layer name 71 | - **extent** (`Number`, default: `4096`) — tile extent size 72 | - **length** (`Number`) — number of features in the layer 73 | 74 | #### Methods 75 | 76 | - **feature(i)** — get a feature (`VectorTileFeature`) by the given index from the layer. 77 | 78 | 79 | ### VectorTileFeature 80 | 81 | An object that contains the data for a single feature. 82 | 83 | #### Properties 84 | 85 | - **type** (`Number`) — type of the feature (also see `VectorTileFeature.types`) 86 | - **extent** (`Number`) — feature extent size 87 | - **id** (`Number`) — feature identifier, if present 88 | - **properties** (`Object`) — object literal with feature properties 89 | 90 | #### Methods 91 | 92 | - **loadGeometry()** — parses feature geometry and returns an array of 93 | [Point](https://github.com/mapbox/point-geometry) arrays (with each point having `x` and `y` properties) 94 | - **bbox()** — calculates and returns the bounding box of the feature in the form `[x1, y1, x2, y2]` 95 | - **toGeoJSON(x, y, z)** — returns a GeoJSON representation of the feature. (`x`, `y`, and `z` refer to the containing tile's index.) 96 | -------------------------------------------------------------------------------- /bench.js: -------------------------------------------------------------------------------- 1 | import Pbf from 'pbf'; 2 | import {VectorTile} from './index.js'; 3 | import Benchmark from 'benchmark'; 4 | import fs from 'fs'; 5 | 6 | const suite = new Benchmark.Suite(); 7 | const data = fs.readFileSync(new URL('test/fixtures/14-8801-5371.vector.pbf', import.meta.url)); 8 | 9 | readTile(); // output any errors before running the suite 10 | readTile(true); 11 | 12 | suite 13 | .add('read tile with geometries', () => { 14 | readTile(true); 15 | }) 16 | .add('read tile without geometries', () => { 17 | readTile(); 18 | }) 19 | .on('cycle', (event) => { 20 | console.log(String(event.target)); 21 | }) 22 | .run(); 23 | 24 | 25 | function readTile(loadGeom) { 26 | const buf = new Pbf(data), 27 | vt = new VectorTile(buf); 28 | 29 | for (const id in vt.layers) { 30 | const layer = vt.layers[id]; 31 | for (let i = 0; i < layer.length; i++) { 32 | const feature = layer.feature(i); 33 | if (loadGeom) feature.loadGeometry(); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | export {default} from 'eslint-config-mourner'; 2 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 2 | import Point from '@mapbox/point-geometry'; 3 | 4 | /** @import Pbf from 'pbf' */ 5 | /** @import {Feature} from 'geojson' */ 6 | 7 | export class VectorTileFeature { 8 | /** 9 | * @param {Pbf} pbf 10 | * @param {number} end 11 | * @param {number} extent 12 | * @param {string[]} keys 13 | * @param {unknown[]} values 14 | */ 15 | constructor(pbf, end, extent, keys, values) { 16 | // Public 17 | 18 | /** @type {Record} */ 19 | this.properties = {}; 20 | 21 | this.extent = extent; 22 | /** @type {0 | 1 | 2 | 3} */ 23 | this.type = 0; 24 | 25 | /** @type {number | undefined} */ 26 | this.id = undefined; 27 | 28 | this._pbf = pbf; 29 | this._geometry = -1; 30 | this._keys = keys; 31 | this._values = values; 32 | 33 | pbf.readFields(readFeature, this, end); 34 | } 35 | 36 | loadGeometry() { 37 | const pbf = this._pbf; 38 | pbf.pos = this._geometry; 39 | 40 | const end = pbf.readVarint() + pbf.pos; 41 | 42 | /** @type Point[][] */ 43 | const lines = []; 44 | 45 | /** @type Point[] | undefined */ 46 | let line; 47 | 48 | let cmd = 1; 49 | let length = 0; 50 | let x = 0; 51 | let y = 0; 52 | 53 | while (pbf.pos < end) { 54 | if (length <= 0) { 55 | const cmdLen = pbf.readVarint(); 56 | cmd = cmdLen & 0x7; 57 | length = cmdLen >> 3; 58 | } 59 | 60 | length--; 61 | 62 | if (cmd === 1 || cmd === 2) { 63 | x += pbf.readSVarint(); 64 | y += pbf.readSVarint(); 65 | 66 | if (cmd === 1) { // moveTo 67 | if (line) lines.push(line); 68 | line = []; 69 | } 70 | 71 | if (line) line.push(new Point(x, y)); 72 | 73 | } else if (cmd === 7) { 74 | 75 | // Workaround for https://github.com/mapbox/mapnik-vector-tile/issues/90 76 | if (line) { 77 | line.push(line[0].clone()); // closePolygon 78 | } 79 | 80 | } else { 81 | throw new Error(`unknown command ${cmd}`); 82 | } 83 | } 84 | 85 | if (line) lines.push(line); 86 | 87 | return lines; 88 | } 89 | 90 | bbox() { 91 | const pbf = this._pbf; 92 | pbf.pos = this._geometry; 93 | 94 | const end = pbf.readVarint() + pbf.pos; 95 | let cmd = 1, 96 | length = 0, 97 | x = 0, 98 | y = 0, 99 | x1 = Infinity, 100 | x2 = -Infinity, 101 | y1 = Infinity, 102 | y2 = -Infinity; 103 | 104 | while (pbf.pos < end) { 105 | if (length <= 0) { 106 | const cmdLen = pbf.readVarint(); 107 | cmd = cmdLen & 0x7; 108 | length = cmdLen >> 3; 109 | } 110 | 111 | length--; 112 | 113 | if (cmd === 1 || cmd === 2) { 114 | x += pbf.readSVarint(); 115 | y += pbf.readSVarint(); 116 | if (x < x1) x1 = x; 117 | if (x > x2) x2 = x; 118 | if (y < y1) y1 = y; 119 | if (y > y2) y2 = y; 120 | 121 | } else if (cmd !== 7) { 122 | throw new Error(`unknown command ${cmd}`); 123 | } 124 | } 125 | 126 | return [x1, y1, x2, y2]; 127 | } 128 | 129 | /** 130 | * @param {number} x 131 | * @param {number} y 132 | * @param {number} z 133 | * @return {Feature} 134 | */ 135 | toGeoJSON(x, y, z) { 136 | const size = this.extent * Math.pow(2, z), 137 | x0 = this.extent * x, 138 | y0 = this.extent * y, 139 | vtCoords = this.loadGeometry(); 140 | 141 | /** @param {Point} p */ 142 | function projectPoint(p) { 143 | return [ 144 | (p.x + x0) * 360 / size - 180, 145 | 360 / Math.PI * Math.atan(Math.exp((1 - (p.y + y0) * 2 / size) * Math.PI)) - 90 146 | ]; 147 | } 148 | 149 | /** @param {Point[]} line */ 150 | function projectLine(line) { 151 | return line.map(projectPoint); 152 | } 153 | 154 | /** @type {Feature["geometry"]} */ 155 | let geometry; 156 | 157 | if (this.type === 1) { 158 | const points = []; 159 | for (const line of vtCoords) { 160 | points.push(line[0]); 161 | } 162 | const coordinates = projectLine(points); 163 | geometry = points.length === 1 ? 164 | {type: 'Point', coordinates: coordinates[0]} : 165 | {type: 'MultiPoint', coordinates}; 166 | 167 | } else if (this.type === 2) { 168 | 169 | const coordinates = vtCoords.map(projectLine); 170 | geometry = coordinates.length === 1 ? 171 | {type: 'LineString', coordinates: coordinates[0]} : 172 | {type: 'MultiLineString', coordinates}; 173 | 174 | } else if (this.type === 3) { 175 | const polygons = classifyRings(vtCoords); 176 | const coordinates = []; 177 | for (const polygon of polygons) { 178 | coordinates.push(polygon.map(projectLine)); 179 | } 180 | geometry = coordinates.length === 1 ? 181 | {type: 'Polygon', coordinates: coordinates[0]} : 182 | {type: 'MultiPolygon', coordinates}; 183 | } else { 184 | 185 | throw new Error('unknown feature type'); 186 | } 187 | 188 | /** @type {Feature} */ 189 | const result = { 190 | type: 'Feature', 191 | geometry, 192 | properties: this.properties 193 | }; 194 | 195 | if (this.id != null) { 196 | result.id = this.id; 197 | } 198 | 199 | return result; 200 | } 201 | } 202 | 203 | /** @type {['Unknown', 'Point', 'LineString', 'Polygon']} */ 204 | VectorTileFeature.types = ['Unknown', 'Point', 'LineString', 'Polygon']; 205 | 206 | /** 207 | * @param {number} tag 208 | * @param {VectorTileFeature} feature 209 | * @param {Pbf} pbf 210 | */ 211 | function readFeature(tag, feature, pbf) { 212 | if (tag === 1) feature.id = pbf.readVarint(); 213 | else if (tag === 2) readTag(pbf, feature); 214 | else if (tag === 3) feature.type = /** @type {0 | 1 | 2 | 3} */ (pbf.readVarint()); 215 | else if (tag === 4) feature._geometry = pbf.pos; 216 | } 217 | 218 | /** 219 | * @param {Pbf} pbf 220 | * @param {VectorTileFeature} feature 221 | */ 222 | function readTag(pbf, feature) { 223 | const end = pbf.readVarint() + pbf.pos; 224 | 225 | while (pbf.pos < end) { 226 | const key = feature._keys[pbf.readVarint()], 227 | value = feature._values[pbf.readVarint()]; 228 | feature.properties[key] = value; 229 | } 230 | } 231 | 232 | /** classifies an array of rings into polygons with outer rings and holes 233 | * @param {Point[][]} rings 234 | */ 235 | export function classifyRings(rings) { 236 | const len = rings.length; 237 | 238 | if (len <= 1) return [rings]; 239 | 240 | const polygons = []; 241 | let polygon, ccw; 242 | 243 | for (let i = 0; i < len; i++) { 244 | const area = signedArea(rings[i]); 245 | if (area === 0) continue; 246 | 247 | if (ccw === undefined) ccw = area < 0; 248 | 249 | if (ccw === area < 0) { 250 | if (polygon) polygons.push(polygon); 251 | polygon = [rings[i]]; 252 | 253 | } else if (polygon) { 254 | polygon.push(rings[i]); 255 | } 256 | } 257 | if (polygon) polygons.push(polygon); 258 | 259 | return polygons; 260 | } 261 | 262 | /** @param {Point[]} ring */ 263 | function signedArea(ring) { 264 | let sum = 0; 265 | for (let i = 0, len = ring.length, j = len - 1, p1, p2; i < len; j = i++) { 266 | p1 = ring[i]; 267 | p2 = ring[j]; 268 | sum += (p2.x - p1.x) * (p1.y + p2.y); 269 | } 270 | return sum; 271 | } 272 | 273 | export class VectorTileLayer { 274 | /** 275 | * @param {Pbf} pbf 276 | * @param {number} [end] 277 | */ 278 | constructor(pbf, end) { 279 | // Public 280 | this.version = 1; 281 | this.name = ''; 282 | this.extent = 4096; 283 | this.length = 0; 284 | 285 | // Private 286 | this._pbf = pbf; 287 | 288 | /** @type {string[]} */ 289 | this._keys = []; 290 | 291 | /** @type {unknown[]} */ 292 | this._values = []; 293 | 294 | /** @type {number[]} */ 295 | this._features = []; 296 | 297 | pbf.readFields(readLayer, this, end); 298 | 299 | this.length = this._features.length; 300 | } 301 | 302 | /** return feature `i` from this layer as a `VectorTileFeature` 303 | * @param {number} i 304 | */ 305 | feature(i) { 306 | if (i < 0 || i >= this._features.length) throw new Error('feature index out of bounds'); 307 | 308 | this._pbf.pos = this._features[i]; 309 | 310 | const end = this._pbf.readVarint() + this._pbf.pos; 311 | return new VectorTileFeature(this._pbf, end, this.extent, this._keys, this._values); 312 | } 313 | } 314 | 315 | /** 316 | * @param {number} tag 317 | * @param {VectorTileLayer} layer 318 | * @param {Pbf} pbf 319 | */ 320 | function readLayer(tag, layer, pbf) { 321 | if (tag === 15) layer.version = pbf.readVarint(); 322 | else if (tag === 1) layer.name = pbf.readString(); 323 | else if (tag === 5) layer.extent = pbf.readVarint(); 324 | else if (tag === 2) layer._features.push(pbf.pos); 325 | else if (tag === 3) layer._keys.push(pbf.readString()); 326 | else if (tag === 4) layer._values.push(readValueMessage(pbf)); 327 | } 328 | 329 | /** 330 | * @param {Pbf} pbf 331 | */ 332 | function readValueMessage(pbf) { 333 | let value = null; 334 | const end = pbf.readVarint() + pbf.pos; 335 | 336 | while (pbf.pos < end) { 337 | const tag = pbf.readVarint() >> 3; 338 | 339 | value = tag === 1 ? pbf.readString() : 340 | tag === 2 ? pbf.readFloat() : 341 | tag === 3 ? pbf.readDouble() : 342 | tag === 4 ? pbf.readVarint64() : 343 | tag === 5 ? pbf.readVarint() : 344 | tag === 6 ? pbf.readSVarint() : 345 | tag === 7 ? pbf.readBoolean() : null; 346 | } 347 | 348 | return value; 349 | } 350 | 351 | export class VectorTile { 352 | /** 353 | * @param {Pbf} pbf 354 | * @param {number} [end] 355 | */ 356 | constructor(pbf, end) { 357 | /** @type {Record} */ 358 | this.layers = pbf.readFields(readTile, {}, end); 359 | } 360 | } 361 | 362 | /** 363 | * @param {number} tag 364 | * @param {Record} layers 365 | * @param {Pbf} pbf 366 | */ 367 | function readTile(tag, layers, pbf) { 368 | if (tag === 3) { 369 | const layer = new VectorTileLayer(pbf, pbf.readVarint() + pbf.pos); 370 | if (layer.length) layers[layer.name] = layer; 371 | } 372 | } 373 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@mapbox/vector-tile", 3 | "version": "2.0.3", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "@mapbox/vector-tile", 9 | "version": "2.0.3", 10 | "license": "BSD-3-Clause", 11 | "dependencies": { 12 | "@mapbox/point-geometry": "~1.1.0", 13 | "@types/geojson": "^7946.0.14", 14 | "pbf": "^4.0.1" 15 | }, 16 | "devDependencies": { 17 | "benchmark": "^2.1.4", 18 | "eslint": "^9.7.0", 19 | "eslint-config-mourner": "^4.0.2", 20 | "typescript": "^5.5.3" 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.7.0", 103 | "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.7.0.tgz", 104 | "integrity": "sha512-ChuWDQenef8OSFnvuxv0TCVxEwmu3+hPNKvM9B34qpM0rDRbjL8t5QkQeHHeAfsKQjuH9wS82WeCi1J/owatng==", 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/@mapbox/point-geometry": { 150 | "version": "1.1.0", 151 | "resolved": "https://registry.npmjs.org/@mapbox/point-geometry/-/point-geometry-1.1.0.tgz", 152 | "integrity": "sha512-YGcBz1cg4ATXDCM/71L9xveh4dynfGmcLDqufR+nQQy3fKwsAZsWd/x4621/6uJaeB9mwOHE6hPeDgXz9uViUQ==", 153 | "license": "ISC" 154 | }, 155 | "node_modules/@nodelib/fs.scandir": { 156 | "version": "2.1.5", 157 | "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", 158 | "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", 159 | "dev": true, 160 | "license": "MIT", 161 | "dependencies": { 162 | "@nodelib/fs.stat": "2.0.5", 163 | "run-parallel": "^1.1.9" 164 | }, 165 | "engines": { 166 | "node": ">= 8" 167 | } 168 | }, 169 | "node_modules/@nodelib/fs.stat": { 170 | "version": "2.0.5", 171 | "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", 172 | "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", 173 | "dev": true, 174 | "license": "MIT", 175 | "engines": { 176 | "node": ">= 8" 177 | } 178 | }, 179 | "node_modules/@nodelib/fs.walk": { 180 | "version": "1.2.8", 181 | "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", 182 | "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", 183 | "dev": true, 184 | "license": "MIT", 185 | "dependencies": { 186 | "@nodelib/fs.scandir": "2.1.5", 187 | "fastq": "^1.6.0" 188 | }, 189 | "engines": { 190 | "node": ">= 8" 191 | } 192 | }, 193 | "node_modules/@stylistic/eslint-plugin-js": { 194 | "version": "2.3.0", 195 | "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-2.3.0.tgz", 196 | "integrity": "sha512-lQwoiYb0Fs6Yc5QS3uT8+T9CPKK2Eoxc3H8EnYJgM26v/DgtW+1lvy2WNgyBflU+ThShZaHm3a6CdD9QeKx23w==", 197 | "dev": true, 198 | "license": "MIT", 199 | "dependencies": { 200 | "@types/eslint": "^8.56.10", 201 | "acorn": "^8.11.3", 202 | "eslint-visitor-keys": "^4.0.0", 203 | "espree": "^10.0.1" 204 | }, 205 | "engines": { 206 | "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 207 | }, 208 | "peerDependencies": { 209 | "eslint": ">=8.40.0" 210 | } 211 | }, 212 | "node_modules/@types/eslint": { 213 | "version": "8.56.10", 214 | "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.10.tgz", 215 | "integrity": "sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ==", 216 | "dev": true, 217 | "license": "MIT", 218 | "dependencies": { 219 | "@types/estree": "*", 220 | "@types/json-schema": "*" 221 | } 222 | }, 223 | "node_modules/@types/estree": { 224 | "version": "1.0.5", 225 | "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", 226 | "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", 227 | "dev": true, 228 | "license": "MIT" 229 | }, 230 | "node_modules/@types/geojson": { 231 | "version": "7946.0.14", 232 | "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.14.tgz", 233 | "integrity": "sha512-WCfD5Ht3ZesJUsONdhvm84dmzWOiOzOAqOncN0++w0lBw1o8OuDNJF2McvvCef/yBqb/HYRahp1BYtODFQ8bRg==", 234 | "license": "MIT" 235 | }, 236 | "node_modules/@types/json-schema": { 237 | "version": "7.0.15", 238 | "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", 239 | "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", 240 | "dev": true, 241 | "license": "MIT" 242 | }, 243 | "node_modules/acorn": { 244 | "version": "8.12.1", 245 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", 246 | "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", 247 | "dev": true, 248 | "license": "MIT", 249 | "bin": { 250 | "acorn": "bin/acorn" 251 | }, 252 | "engines": { 253 | "node": ">=0.4.0" 254 | } 255 | }, 256 | "node_modules/acorn-jsx": { 257 | "version": "5.3.2", 258 | "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", 259 | "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", 260 | "dev": true, 261 | "license": "MIT", 262 | "peerDependencies": { 263 | "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" 264 | } 265 | }, 266 | "node_modules/ajv": { 267 | "version": "6.12.6", 268 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", 269 | "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", 270 | "dev": true, 271 | "license": "MIT", 272 | "dependencies": { 273 | "fast-deep-equal": "^3.1.1", 274 | "fast-json-stable-stringify": "^2.0.0", 275 | "json-schema-traverse": "^0.4.1", 276 | "uri-js": "^4.2.2" 277 | }, 278 | "funding": { 279 | "type": "github", 280 | "url": "https://github.com/sponsors/epoberezkin" 281 | } 282 | }, 283 | "node_modules/ansi-regex": { 284 | "version": "5.0.1", 285 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 286 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", 287 | "dev": true, 288 | "license": "MIT", 289 | "engines": { 290 | "node": ">=8" 291 | } 292 | }, 293 | "node_modules/ansi-styles": { 294 | "version": "4.3.0", 295 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 296 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 297 | "dev": true, 298 | "license": "MIT", 299 | "dependencies": { 300 | "color-convert": "^2.0.1" 301 | }, 302 | "engines": { 303 | "node": ">=8" 304 | }, 305 | "funding": { 306 | "url": "https://github.com/chalk/ansi-styles?sponsor=1" 307 | } 308 | }, 309 | "node_modules/argparse": { 310 | "version": "2.0.1", 311 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", 312 | "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", 313 | "dev": true, 314 | "license": "Python-2.0" 315 | }, 316 | "node_modules/balanced-match": { 317 | "version": "1.0.2", 318 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 319 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", 320 | "dev": true, 321 | "license": "MIT" 322 | }, 323 | "node_modules/benchmark": { 324 | "version": "2.1.4", 325 | "resolved": "https://registry.npmjs.org/benchmark/-/benchmark-2.1.4.tgz", 326 | "integrity": "sha512-l9MlfN4M1K/H2fbhfMy3B7vJd6AGKJVQn2h6Sg/Yx+KckoUA7ewS5Vv6TjSq18ooE1kS9hhAlQRH3AkXIh/aOQ==", 327 | "dev": true, 328 | "license": "MIT", 329 | "dependencies": { 330 | "lodash": "^4.17.4", 331 | "platform": "^1.3.3" 332 | } 333 | }, 334 | "node_modules/benchmark/node_modules/lodash": { 335 | "version": "4.17.21", 336 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", 337 | "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", 338 | "dev": true, 339 | "license": "MIT" 340 | }, 341 | "node_modules/brace-expansion": { 342 | "version": "1.1.11", 343 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 344 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 345 | "dev": true, 346 | "license": "MIT", 347 | "dependencies": { 348 | "balanced-match": "^1.0.0", 349 | "concat-map": "0.0.1" 350 | } 351 | }, 352 | "node_modules/callsites": { 353 | "version": "3.1.0", 354 | "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", 355 | "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", 356 | "dev": true, 357 | "license": "MIT", 358 | "engines": { 359 | "node": ">=6" 360 | } 361 | }, 362 | "node_modules/chalk": { 363 | "version": "4.1.2", 364 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", 365 | "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", 366 | "dev": true, 367 | "license": "MIT", 368 | "dependencies": { 369 | "ansi-styles": "^4.1.0", 370 | "supports-color": "^7.1.0" 371 | }, 372 | "engines": { 373 | "node": ">=10" 374 | }, 375 | "funding": { 376 | "url": "https://github.com/chalk/chalk?sponsor=1" 377 | } 378 | }, 379 | "node_modules/color-convert": { 380 | "version": "2.0.1", 381 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 382 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 383 | "dev": true, 384 | "license": "MIT", 385 | "dependencies": { 386 | "color-name": "~1.1.4" 387 | }, 388 | "engines": { 389 | "node": ">=7.0.0" 390 | } 391 | }, 392 | "node_modules/color-name": { 393 | "version": "1.1.4", 394 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 395 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 396 | "dev": true, 397 | "license": "MIT" 398 | }, 399 | "node_modules/concat-map": { 400 | "version": "0.0.1", 401 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 402 | "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", 403 | "dev": true, 404 | "license": "MIT" 405 | }, 406 | "node_modules/cross-spawn": { 407 | "version": "7.0.3", 408 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", 409 | "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", 410 | "dev": true, 411 | "license": "MIT", 412 | "dependencies": { 413 | "path-key": "^3.1.0", 414 | "shebang-command": "^2.0.0", 415 | "which": "^2.0.1" 416 | }, 417 | "engines": { 418 | "node": ">= 8" 419 | } 420 | }, 421 | "node_modules/debug": { 422 | "version": "4.3.5", 423 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", 424 | "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", 425 | "dev": true, 426 | "license": "MIT", 427 | "dependencies": { 428 | "ms": "2.1.2" 429 | }, 430 | "engines": { 431 | "node": ">=6.0" 432 | }, 433 | "peerDependenciesMeta": { 434 | "supports-color": { 435 | "optional": true 436 | } 437 | } 438 | }, 439 | "node_modules/deep-is": { 440 | "version": "0.1.4", 441 | "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", 442 | "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", 443 | "dev": true, 444 | "license": "MIT" 445 | }, 446 | "node_modules/escape-string-regexp": { 447 | "version": "4.0.0", 448 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", 449 | "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", 450 | "dev": true, 451 | "license": "MIT", 452 | "engines": { 453 | "node": ">=10" 454 | }, 455 | "funding": { 456 | "url": "https://github.com/sponsors/sindresorhus" 457 | } 458 | }, 459 | "node_modules/eslint": { 460 | "version": "9.7.0", 461 | "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.7.0.tgz", 462 | "integrity": "sha512-FzJ9D/0nGiCGBf8UXO/IGLTgLVzIxze1zpfA8Ton2mjLovXdAPlYDv+MQDcqj3TmrhAGYfOpz9RfR+ent0AgAw==", 463 | "dev": true, 464 | "license": "MIT", 465 | "dependencies": { 466 | "@eslint-community/eslint-utils": "^4.2.0", 467 | "@eslint-community/regexpp": "^4.11.0", 468 | "@eslint/config-array": "^0.17.0", 469 | "@eslint/eslintrc": "^3.1.0", 470 | "@eslint/js": "9.7.0", 471 | "@humanwhocodes/module-importer": "^1.0.1", 472 | "@humanwhocodes/retry": "^0.3.0", 473 | "@nodelib/fs.walk": "^1.2.8", 474 | "ajv": "^6.12.4", 475 | "chalk": "^4.0.0", 476 | "cross-spawn": "^7.0.2", 477 | "debug": "^4.3.2", 478 | "escape-string-regexp": "^4.0.0", 479 | "eslint-scope": "^8.0.2", 480 | "eslint-visitor-keys": "^4.0.0", 481 | "espree": "^10.1.0", 482 | "esquery": "^1.5.0", 483 | "esutils": "^2.0.2", 484 | "fast-deep-equal": "^3.1.3", 485 | "file-entry-cache": "^8.0.0", 486 | "find-up": "^5.0.0", 487 | "glob-parent": "^6.0.2", 488 | "ignore": "^5.2.0", 489 | "imurmurhash": "^0.1.4", 490 | "is-glob": "^4.0.0", 491 | "is-path-inside": "^3.0.3", 492 | "json-stable-stringify-without-jsonify": "^1.0.1", 493 | "levn": "^0.4.1", 494 | "lodash.merge": "^4.6.2", 495 | "minimatch": "^3.1.2", 496 | "natural-compare": "^1.4.0", 497 | "optionator": "^0.9.3", 498 | "strip-ansi": "^6.0.1", 499 | "text-table": "^0.2.0" 500 | }, 501 | "bin": { 502 | "eslint": "bin/eslint.js" 503 | }, 504 | "engines": { 505 | "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 506 | }, 507 | "funding": { 508 | "url": "https://eslint.org/donate" 509 | } 510 | }, 511 | "node_modules/eslint-config-mourner": { 512 | "version": "4.0.2", 513 | "resolved": "https://registry.npmjs.org/eslint-config-mourner/-/eslint-config-mourner-4.0.2.tgz", 514 | "integrity": "sha512-gaXR2jyDbu51PhUNQXuLZmyHYgFztdLfAz7EtfdmBXw0Ok97H6/39hKb1nLBwQa+8+QHXCCgqG8Sviu/Z5GSzQ==", 515 | "dev": true, 516 | "license": "ISC", 517 | "dependencies": { 518 | "@eslint/js": "^9.6.0", 519 | "@stylistic/eslint-plugin-js": "^2.3.0", 520 | "globals": "^15.8.0" 521 | } 522 | }, 523 | "node_modules/eslint-config-mourner/node_modules/globals": { 524 | "version": "15.8.0", 525 | "resolved": "https://registry.npmjs.org/globals/-/globals-15.8.0.tgz", 526 | "integrity": "sha512-VZAJ4cewHTExBWDHR6yptdIBlx9YSSZuwojj9Nt5mBRXQzrKakDsVKQ1J63sklLvzAJm0X5+RpO4i3Y2hcOnFw==", 527 | "dev": true, 528 | "license": "MIT", 529 | "engines": { 530 | "node": ">=18" 531 | }, 532 | "funding": { 533 | "url": "https://github.com/sponsors/sindresorhus" 534 | } 535 | }, 536 | "node_modules/eslint-scope": { 537 | "version": "8.0.2", 538 | "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.0.2.tgz", 539 | "integrity": "sha512-6E4xmrTw5wtxnLA5wYL3WDfhZ/1bUBGOXV0zQvVRDOtrR8D0p6W7fs3JweNYhwRYeGvd/1CKX2se0/2s7Q/nJA==", 540 | "dev": true, 541 | "license": "BSD-2-Clause", 542 | "dependencies": { 543 | "esrecurse": "^4.3.0", 544 | "estraverse": "^5.2.0" 545 | }, 546 | "engines": { 547 | "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 548 | }, 549 | "funding": { 550 | "url": "https://opencollective.com/eslint" 551 | } 552 | }, 553 | "node_modules/eslint-visitor-keys": { 554 | "version": "4.0.0", 555 | "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz", 556 | "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==", 557 | "dev": true, 558 | "license": "Apache-2.0", 559 | "engines": { 560 | "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 561 | }, 562 | "funding": { 563 | "url": "https://opencollective.com/eslint" 564 | } 565 | }, 566 | "node_modules/espree": { 567 | "version": "10.1.0", 568 | "resolved": "https://registry.npmjs.org/espree/-/espree-10.1.0.tgz", 569 | "integrity": "sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA==", 570 | "dev": true, 571 | "license": "BSD-2-Clause", 572 | "dependencies": { 573 | "acorn": "^8.12.0", 574 | "acorn-jsx": "^5.3.2", 575 | "eslint-visitor-keys": "^4.0.0" 576 | }, 577 | "engines": { 578 | "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 579 | }, 580 | "funding": { 581 | "url": "https://opencollective.com/eslint" 582 | } 583 | }, 584 | "node_modules/esquery": { 585 | "version": "1.6.0", 586 | "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", 587 | "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", 588 | "dev": true, 589 | "license": "BSD-3-Clause", 590 | "dependencies": { 591 | "estraverse": "^5.1.0" 592 | }, 593 | "engines": { 594 | "node": ">=0.10" 595 | } 596 | }, 597 | "node_modules/esrecurse": { 598 | "version": "4.3.0", 599 | "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", 600 | "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", 601 | "dev": true, 602 | "license": "BSD-2-Clause", 603 | "dependencies": { 604 | "estraverse": "^5.2.0" 605 | }, 606 | "engines": { 607 | "node": ">=4.0" 608 | } 609 | }, 610 | "node_modules/estraverse": { 611 | "version": "5.3.0", 612 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", 613 | "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", 614 | "dev": true, 615 | "license": "BSD-2-Clause", 616 | "engines": { 617 | "node": ">=4.0" 618 | } 619 | }, 620 | "node_modules/esutils": { 621 | "version": "2.0.3", 622 | "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", 623 | "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", 624 | "dev": true, 625 | "license": "BSD-2-Clause", 626 | "engines": { 627 | "node": ">=0.10.0" 628 | } 629 | }, 630 | "node_modules/fast-deep-equal": { 631 | "version": "3.1.3", 632 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", 633 | "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", 634 | "dev": true, 635 | "license": "MIT" 636 | }, 637 | "node_modules/fast-json-stable-stringify": { 638 | "version": "2.1.0", 639 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", 640 | "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", 641 | "dev": true, 642 | "license": "MIT" 643 | }, 644 | "node_modules/fast-levenshtein": { 645 | "version": "2.0.6", 646 | "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", 647 | "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", 648 | "dev": true, 649 | "license": "MIT" 650 | }, 651 | "node_modules/fastq": { 652 | "version": "1.17.1", 653 | "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", 654 | "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", 655 | "dev": true, 656 | "license": "ISC", 657 | "dependencies": { 658 | "reusify": "^1.0.4" 659 | } 660 | }, 661 | "node_modules/file-entry-cache": { 662 | "version": "8.0.0", 663 | "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", 664 | "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", 665 | "dev": true, 666 | "license": "MIT", 667 | "dependencies": { 668 | "flat-cache": "^4.0.0" 669 | }, 670 | "engines": { 671 | "node": ">=16.0.0" 672 | } 673 | }, 674 | "node_modules/find-up": { 675 | "version": "5.0.0", 676 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", 677 | "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", 678 | "dev": true, 679 | "license": "MIT", 680 | "dependencies": { 681 | "locate-path": "^6.0.0", 682 | "path-exists": "^4.0.0" 683 | }, 684 | "engines": { 685 | "node": ">=10" 686 | }, 687 | "funding": { 688 | "url": "https://github.com/sponsors/sindresorhus" 689 | } 690 | }, 691 | "node_modules/flat-cache": { 692 | "version": "4.0.1", 693 | "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", 694 | "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", 695 | "dev": true, 696 | "license": "MIT", 697 | "dependencies": { 698 | "flatted": "^3.2.9", 699 | "keyv": "^4.5.4" 700 | }, 701 | "engines": { 702 | "node": ">=16" 703 | } 704 | }, 705 | "node_modules/flatted": { 706 | "version": "3.3.1", 707 | "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", 708 | "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", 709 | "dev": true, 710 | "license": "ISC" 711 | }, 712 | "node_modules/glob-parent": { 713 | "version": "6.0.2", 714 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", 715 | "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", 716 | "dev": true, 717 | "license": "ISC", 718 | "dependencies": { 719 | "is-glob": "^4.0.3" 720 | }, 721 | "engines": { 722 | "node": ">=10.13.0" 723 | } 724 | }, 725 | "node_modules/globals": { 726 | "version": "14.0.0", 727 | "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", 728 | "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", 729 | "dev": true, 730 | "license": "MIT", 731 | "engines": { 732 | "node": ">=18" 733 | }, 734 | "funding": { 735 | "url": "https://github.com/sponsors/sindresorhus" 736 | } 737 | }, 738 | "node_modules/has-flag": { 739 | "version": "4.0.0", 740 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 741 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 742 | "dev": true, 743 | "license": "MIT", 744 | "engines": { 745 | "node": ">=8" 746 | } 747 | }, 748 | "node_modules/ignore": { 749 | "version": "5.3.1", 750 | "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", 751 | "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", 752 | "dev": true, 753 | "license": "MIT", 754 | "engines": { 755 | "node": ">= 4" 756 | } 757 | }, 758 | "node_modules/import-fresh": { 759 | "version": "3.3.0", 760 | "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", 761 | "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", 762 | "dev": true, 763 | "license": "MIT", 764 | "dependencies": { 765 | "parent-module": "^1.0.0", 766 | "resolve-from": "^4.0.0" 767 | }, 768 | "engines": { 769 | "node": ">=6" 770 | }, 771 | "funding": { 772 | "url": "https://github.com/sponsors/sindresorhus" 773 | } 774 | }, 775 | "node_modules/imurmurhash": { 776 | "version": "0.1.4", 777 | "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", 778 | "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", 779 | "dev": true, 780 | "license": "MIT", 781 | "engines": { 782 | "node": ">=0.8.19" 783 | } 784 | }, 785 | "node_modules/is-extglob": { 786 | "version": "2.1.1", 787 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 788 | "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", 789 | "dev": true, 790 | "license": "MIT", 791 | "engines": { 792 | "node": ">=0.10.0" 793 | } 794 | }, 795 | "node_modules/is-glob": { 796 | "version": "4.0.3", 797 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 798 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 799 | "dev": true, 800 | "license": "MIT", 801 | "dependencies": { 802 | "is-extglob": "^2.1.1" 803 | }, 804 | "engines": { 805 | "node": ">=0.10.0" 806 | } 807 | }, 808 | "node_modules/is-path-inside": { 809 | "version": "3.0.3", 810 | "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", 811 | "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", 812 | "dev": true, 813 | "license": "MIT", 814 | "engines": { 815 | "node": ">=8" 816 | } 817 | }, 818 | "node_modules/isexe": { 819 | "version": "2.0.0", 820 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 821 | "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", 822 | "dev": true, 823 | "license": "ISC" 824 | }, 825 | "node_modules/js-yaml": { 826 | "version": "4.1.0", 827 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", 828 | "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", 829 | "dev": true, 830 | "license": "MIT", 831 | "dependencies": { 832 | "argparse": "^2.0.1" 833 | }, 834 | "bin": { 835 | "js-yaml": "bin/js-yaml.js" 836 | } 837 | }, 838 | "node_modules/json-buffer": { 839 | "version": "3.0.1", 840 | "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", 841 | "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", 842 | "dev": true, 843 | "license": "MIT" 844 | }, 845 | "node_modules/json-schema-traverse": { 846 | "version": "0.4.1", 847 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", 848 | "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", 849 | "dev": true, 850 | "license": "MIT" 851 | }, 852 | "node_modules/json-stable-stringify-without-jsonify": { 853 | "version": "1.0.1", 854 | "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", 855 | "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", 856 | "dev": true, 857 | "license": "MIT" 858 | }, 859 | "node_modules/keyv": { 860 | "version": "4.5.4", 861 | "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", 862 | "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", 863 | "dev": true, 864 | "license": "MIT", 865 | "dependencies": { 866 | "json-buffer": "3.0.1" 867 | } 868 | }, 869 | "node_modules/levn": { 870 | "version": "0.4.1", 871 | "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", 872 | "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", 873 | "dev": true, 874 | "license": "MIT", 875 | "dependencies": { 876 | "prelude-ls": "^1.2.1", 877 | "type-check": "~0.4.0" 878 | }, 879 | "engines": { 880 | "node": ">= 0.8.0" 881 | } 882 | }, 883 | "node_modules/locate-path": { 884 | "version": "6.0.0", 885 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", 886 | "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", 887 | "dev": true, 888 | "license": "MIT", 889 | "dependencies": { 890 | "p-locate": "^5.0.0" 891 | }, 892 | "engines": { 893 | "node": ">=10" 894 | }, 895 | "funding": { 896 | "url": "https://github.com/sponsors/sindresorhus" 897 | } 898 | }, 899 | "node_modules/lodash.merge": { 900 | "version": "4.6.2", 901 | "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", 902 | "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", 903 | "dev": true, 904 | "license": "MIT" 905 | }, 906 | "node_modules/minimatch": { 907 | "version": "3.1.2", 908 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 909 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 910 | "dev": true, 911 | "license": "ISC", 912 | "dependencies": { 913 | "brace-expansion": "^1.1.7" 914 | }, 915 | "engines": { 916 | "node": "*" 917 | } 918 | }, 919 | "node_modules/ms": { 920 | "version": "2.1.2", 921 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 922 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", 923 | "dev": true, 924 | "license": "MIT" 925 | }, 926 | "node_modules/natural-compare": { 927 | "version": "1.4.0", 928 | "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", 929 | "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", 930 | "dev": true, 931 | "license": "MIT" 932 | }, 933 | "node_modules/optionator": { 934 | "version": "0.9.4", 935 | "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", 936 | "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", 937 | "dev": true, 938 | "license": "MIT", 939 | "dependencies": { 940 | "deep-is": "^0.1.3", 941 | "fast-levenshtein": "^2.0.6", 942 | "levn": "^0.4.1", 943 | "prelude-ls": "^1.2.1", 944 | "type-check": "^0.4.0", 945 | "word-wrap": "^1.2.5" 946 | }, 947 | "engines": { 948 | "node": ">= 0.8.0" 949 | } 950 | }, 951 | "node_modules/p-limit": { 952 | "version": "3.1.0", 953 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", 954 | "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", 955 | "dev": true, 956 | "license": "MIT", 957 | "dependencies": { 958 | "yocto-queue": "^0.1.0" 959 | }, 960 | "engines": { 961 | "node": ">=10" 962 | }, 963 | "funding": { 964 | "url": "https://github.com/sponsors/sindresorhus" 965 | } 966 | }, 967 | "node_modules/p-locate": { 968 | "version": "5.0.0", 969 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", 970 | "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", 971 | "dev": true, 972 | "license": "MIT", 973 | "dependencies": { 974 | "p-limit": "^3.0.2" 975 | }, 976 | "engines": { 977 | "node": ">=10" 978 | }, 979 | "funding": { 980 | "url": "https://github.com/sponsors/sindresorhus" 981 | } 982 | }, 983 | "node_modules/parent-module": { 984 | "version": "1.0.1", 985 | "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", 986 | "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", 987 | "dev": true, 988 | "license": "MIT", 989 | "dependencies": { 990 | "callsites": "^3.0.0" 991 | }, 992 | "engines": { 993 | "node": ">=6" 994 | } 995 | }, 996 | "node_modules/path-exists": { 997 | "version": "4.0.0", 998 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", 999 | "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", 1000 | "dev": true, 1001 | "license": "MIT", 1002 | "engines": { 1003 | "node": ">=8" 1004 | } 1005 | }, 1006 | "node_modules/path-key": { 1007 | "version": "3.1.1", 1008 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", 1009 | "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", 1010 | "dev": true, 1011 | "license": "MIT", 1012 | "engines": { 1013 | "node": ">=8" 1014 | } 1015 | }, 1016 | "node_modules/pbf": { 1017 | "version": "4.0.1", 1018 | "resolved": "https://registry.npmjs.org/pbf/-/pbf-4.0.1.tgz", 1019 | "integrity": "sha512-SuLdBvS42z33m8ejRbInMapQe8n0D3vN/Xd5fmWM3tufNgRQFBpaW2YVJxQZV4iPNqb0vEFvssMEo5w9c6BTIA==", 1020 | "license": "BSD-3-Clause", 1021 | "dependencies": { 1022 | "resolve-protobuf-schema": "^2.1.0" 1023 | }, 1024 | "bin": { 1025 | "pbf": "bin/pbf" 1026 | } 1027 | }, 1028 | "node_modules/platform": { 1029 | "version": "1.3.6", 1030 | "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.6.tgz", 1031 | "integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==", 1032 | "dev": true, 1033 | "license": "MIT" 1034 | }, 1035 | "node_modules/prelude-ls": { 1036 | "version": "1.2.1", 1037 | "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", 1038 | "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", 1039 | "dev": true, 1040 | "license": "MIT", 1041 | "engines": { 1042 | "node": ">= 0.8.0" 1043 | } 1044 | }, 1045 | "node_modules/protocol-buffers-schema": { 1046 | "version": "3.6.0", 1047 | "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz", 1048 | "integrity": "sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw==", 1049 | "license": "MIT" 1050 | }, 1051 | "node_modules/punycode": { 1052 | "version": "2.3.1", 1053 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", 1054 | "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", 1055 | "dev": true, 1056 | "license": "MIT", 1057 | "engines": { 1058 | "node": ">=6" 1059 | } 1060 | }, 1061 | "node_modules/queue-microtask": { 1062 | "version": "1.2.3", 1063 | "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", 1064 | "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", 1065 | "dev": true, 1066 | "funding": [ 1067 | { 1068 | "type": "github", 1069 | "url": "https://github.com/sponsors/feross" 1070 | }, 1071 | { 1072 | "type": "patreon", 1073 | "url": "https://www.patreon.com/feross" 1074 | }, 1075 | { 1076 | "type": "consulting", 1077 | "url": "https://feross.org/support" 1078 | } 1079 | ], 1080 | "license": "MIT" 1081 | }, 1082 | "node_modules/resolve-from": { 1083 | "version": "4.0.0", 1084 | "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", 1085 | "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", 1086 | "dev": true, 1087 | "license": "MIT", 1088 | "engines": { 1089 | "node": ">=4" 1090 | } 1091 | }, 1092 | "node_modules/resolve-protobuf-schema": { 1093 | "version": "2.1.0", 1094 | "resolved": "https://registry.npmjs.org/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz", 1095 | "integrity": "sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==", 1096 | "license": "MIT", 1097 | "dependencies": { 1098 | "protocol-buffers-schema": "^3.3.1" 1099 | } 1100 | }, 1101 | "node_modules/reusify": { 1102 | "version": "1.0.4", 1103 | "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", 1104 | "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", 1105 | "dev": true, 1106 | "license": "MIT", 1107 | "engines": { 1108 | "iojs": ">=1.0.0", 1109 | "node": ">=0.10.0" 1110 | } 1111 | }, 1112 | "node_modules/run-parallel": { 1113 | "version": "1.2.0", 1114 | "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", 1115 | "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", 1116 | "dev": true, 1117 | "funding": [ 1118 | { 1119 | "type": "github", 1120 | "url": "https://github.com/sponsors/feross" 1121 | }, 1122 | { 1123 | "type": "patreon", 1124 | "url": "https://www.patreon.com/feross" 1125 | }, 1126 | { 1127 | "type": "consulting", 1128 | "url": "https://feross.org/support" 1129 | } 1130 | ], 1131 | "license": "MIT", 1132 | "dependencies": { 1133 | "queue-microtask": "^1.2.2" 1134 | } 1135 | }, 1136 | "node_modules/shebang-command": { 1137 | "version": "2.0.0", 1138 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", 1139 | "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", 1140 | "dev": true, 1141 | "license": "MIT", 1142 | "dependencies": { 1143 | "shebang-regex": "^3.0.0" 1144 | }, 1145 | "engines": { 1146 | "node": ">=8" 1147 | } 1148 | }, 1149 | "node_modules/shebang-regex": { 1150 | "version": "3.0.0", 1151 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", 1152 | "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", 1153 | "dev": true, 1154 | "license": "MIT", 1155 | "engines": { 1156 | "node": ">=8" 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/type-check": { 1206 | "version": "0.4.0", 1207 | "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", 1208 | "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", 1209 | "dev": true, 1210 | "license": "MIT", 1211 | "dependencies": { 1212 | "prelude-ls": "^1.2.1" 1213 | }, 1214 | "engines": { 1215 | "node": ">= 0.8.0" 1216 | } 1217 | }, 1218 | "node_modules/typescript": { 1219 | "version": "5.5.3", 1220 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz", 1221 | "integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==", 1222 | "dev": true, 1223 | "license": "Apache-2.0", 1224 | "bin": { 1225 | "tsc": "bin/tsc", 1226 | "tsserver": "bin/tsserver" 1227 | }, 1228 | "engines": { 1229 | "node": ">=14.17" 1230 | } 1231 | }, 1232 | "node_modules/uri-js": { 1233 | "version": "4.4.1", 1234 | "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", 1235 | "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", 1236 | "dev": true, 1237 | "license": "BSD-2-Clause", 1238 | "dependencies": { 1239 | "punycode": "^2.1.0" 1240 | } 1241 | }, 1242 | "node_modules/which": { 1243 | "version": "2.0.2", 1244 | "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", 1245 | "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", 1246 | "dev": true, 1247 | "license": "ISC", 1248 | "dependencies": { 1249 | "isexe": "^2.0.0" 1250 | }, 1251 | "bin": { 1252 | "node-which": "bin/node-which" 1253 | }, 1254 | "engines": { 1255 | "node": ">= 8" 1256 | } 1257 | }, 1258 | "node_modules/word-wrap": { 1259 | "version": "1.2.5", 1260 | "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", 1261 | "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", 1262 | "dev": true, 1263 | "license": "MIT", 1264 | "engines": { 1265 | "node": ">=0.10.0" 1266 | } 1267 | }, 1268 | "node_modules/yocto-queue": { 1269 | "version": "0.1.0", 1270 | "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", 1271 | "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", 1272 | "dev": true, 1273 | "license": "MIT", 1274 | "engines": { 1275 | "node": ">=10" 1276 | }, 1277 | "funding": { 1278 | "url": "https://github.com/sponsors/sindresorhus" 1279 | } 1280 | } 1281 | } 1282 | } 1283 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@mapbox/vector-tile", 3 | "description": "Parses vector tiles", 4 | "repository": { 5 | "url": "git+https://github.com/mapbox/vector-tile-js.git" 6 | }, 7 | "version": "2.0.3", 8 | "type": "module", 9 | "exports": "./index.js", 10 | "license": "BSD-3-Clause", 11 | "main": "index.js", 12 | "types": "index.d.ts", 13 | "dependencies": { 14 | "@mapbox/point-geometry": "~1.1.0", 15 | "@types/geojson": "^7946.0.14", 16 | "pbf": "^4.0.1" 17 | }, 18 | "devDependencies": { 19 | "benchmark": "^2.1.4", 20 | "eslint": "^9.7.0", 21 | "eslint-config-mourner": "^4.0.2", 22 | "typescript": "^5.5.3" 23 | }, 24 | "scripts": { 25 | "lint": "eslint index.js test/*.js", 26 | "pretest": "npm run lint", 27 | "test": "tsc && node --test", 28 | "cov": "node --test --experimental-test-coverage", 29 | "prepublishOnly": "npm test" 30 | }, 31 | "files": [ 32 | "index.d.ts" 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /proto/vector_tile.proto: -------------------------------------------------------------------------------- 1 | // Protocol Version 1 2 | 3 | message tile { 4 | enum GeomType { 5 | Unknown = 0; 6 | Point = 1; 7 | LineString = 2; 8 | Polygon = 3; 9 | } 10 | 11 | // Variant type encoding 12 | message value { 13 | // Exactly one of these values may be present in a valid message 14 | optional string string_value = 1; 15 | optional float float_value = 2; 16 | optional double double_value = 3; 17 | optional int64 int_value = 4; 18 | optional uint64 uint_value = 5; 19 | optional sint64 sint_value = 6; 20 | optional bool bool_value = 7; 21 | 22 | extensions 8 to max; 23 | } 24 | 25 | message feature { 26 | optional uint64 id = 1; 27 | 28 | // Tags of this feature. Even numbered values refer to the nth 29 | // value in the keys list on the tile message, odd numbered 30 | // values refer to the nth value in the values list on the tile 31 | // message. 32 | repeated uint32 tags = 2 [ packed = true ]; 33 | 34 | // The type of geometry stored in this feature. 35 | optional GeomType type = 3 [ default = Unknown ]; 36 | 37 | // Contains a stream of commands and parameters (vertices). The 38 | // repeat count is shifted to the left by 3 bits. This means 39 | // that the command has 3 bits (0-7). The repeat count 40 | // indicates how often this command is to be repeated. Defined 41 | // commands are: 42 | // - MoveTo: 1 (2 parameters follow) 43 | // - LineTo: 2 (2 parameters follow) 44 | // - ClosePath: 7 (no parameters follow) 45 | // 46 | // Ex.: MoveTo(3, 6), LineTo(8, 12), LineTo(20, 34), ClosePath 47 | // Encoded as: [ 9 3 6 18 5 6 12 22 15 ] 48 | // == command type 7 (ClosePath), length 1 49 | // ===== relative LineTo(+12, +22) == LineTo(20, 34) 50 | // === relative LineTo(+5, +6) == LineTo(8, 12) 51 | // == [00010 010] = command type 2 (LineTo), length 2 52 | // === relative MoveTo(+3, +6) 53 | // == [00001 001] = command type 1 (MoveTo), length 1 54 | // Commands are encoded as uint32 varints, vertex parameters are 55 | // encoded as sint32 varints (zigzag). Vertex parameters are 56 | // also encoded as deltas to the previous position. The original 57 | // position is (0,0) 58 | repeated uint32 geometry = 4 [ packed = true ]; 59 | } 60 | 61 | message layer { 62 | // Any compliant implementation must first read the version 63 | // number encoded in this message and choose the correct 64 | // implementation for this version number before proceeding to 65 | // decode other parts of this message. 66 | required uint32 version = 15 [ default = 1 ]; 67 | 68 | required string name = 1; 69 | 70 | // The actual features in this tile. 71 | repeated feature features = 2; 72 | 73 | // Dictionary encoding for keys 74 | repeated string keys = 3; 75 | 76 | // Dictionary encoding for values 77 | repeated value values = 4; 78 | 79 | // The bounding box in this tile spans from 0..4095 units 80 | optional uint32 extent = 5 [ default = 4096 ]; 81 | 82 | extensions 16 to max; 83 | } 84 | 85 | repeated layer layers = 3; 86 | 87 | extensions 16 to 8191; 88 | } 89 | -------------------------------------------------------------------------------- /test/fixtures/12-1143-1497.vector.pbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapbox/vector-tile-js/a85415eca60c01a6a5c836b2cbc244a08c1f8284/test/fixtures/12-1143-1497.vector.pbf -------------------------------------------------------------------------------- /test/fixtures/14-8801-5371.vector.pbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapbox/vector-tile-js/a85415eca60c01a6a5c836b2cbc244a08c1f8284/test/fixtures/14-8801-5371.vector.pbf -------------------------------------------------------------------------------- /test/fixtures/lots-of-tags.vector.pbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapbox/vector-tile-js/a85415eca60c01a6a5c836b2cbc244a08c1f8284/test/fixtures/lots-of-tags.vector.pbf -------------------------------------------------------------------------------- /test/fixtures/multi-line.pbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapbox/vector-tile-js/a85415eca60c01a6a5c836b2cbc244a08c1f8284/test/fixtures/multi-line.pbf -------------------------------------------------------------------------------- /test/fixtures/multi-point.pbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapbox/vector-tile-js/a85415eca60c01a6a5c836b2cbc244a08c1f8284/test/fixtures/multi-point.pbf -------------------------------------------------------------------------------- /test/fixtures/multi-polygon.pbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapbox/vector-tile-js/a85415eca60c01a6a5c836b2cbc244a08c1f8284/test/fixtures/multi-polygon.pbf -------------------------------------------------------------------------------- /test/fixtures/multipolygon-with-closepath.pbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapbox/vector-tile-js/a85415eca60c01a6a5c836b2cbc244a08c1f8284/test/fixtures/multipolygon-with-closepath.pbf -------------------------------------------------------------------------------- /test/fixtures/multipolygon.pbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapbox/vector-tile-js/a85415eca60c01a6a5c836b2cbc244a08c1f8284/test/fixtures/multipolygon.pbf -------------------------------------------------------------------------------- /test/fixtures/polygon-with-inner.pbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapbox/vector-tile-js/a85415eca60c01a6a5c836b2cbc244a08c1f8284/test/fixtures/polygon-with-inner.pbf -------------------------------------------------------------------------------- /test/fixtures/singleton-multi-line.pbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapbox/vector-tile-js/a85415eca60c01a6a5c836b2cbc244a08c1f8284/test/fixtures/singleton-multi-line.pbf -------------------------------------------------------------------------------- /test/fixtures/singleton-multi-point.pbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapbox/vector-tile-js/a85415eca60c01a6a5c836b2cbc244a08c1f8284/test/fixtures/singleton-multi-point.pbf -------------------------------------------------------------------------------- /test/fixtures/singleton-multi-polygon.pbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapbox/vector-tile-js/a85415eca60c01a6a5c836b2cbc244a08c1f8284/test/fixtures/singleton-multi-polygon.pbf -------------------------------------------------------------------------------- /test/fixtures/stacked-multipolygon.pbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapbox/vector-tile-js/a85415eca60c01a6a5c836b2cbc244a08c1f8284/test/fixtures/stacked-multipolygon.pbf -------------------------------------------------------------------------------- /test/fixtures/zero-line.pbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapbox/vector-tile-js/a85415eca60c01a6a5c836b2cbc244a08c1f8284/test/fixtures/zero-line.pbf -------------------------------------------------------------------------------- /test/fixtures/zero-point.pbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapbox/vector-tile-js/a85415eca60c01a6a5c836b2cbc244a08c1f8284/test/fixtures/zero-point.pbf -------------------------------------------------------------------------------- /test/fixtures/zero-polygon.pbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapbox/vector-tile-js/a85415eca60c01a6a5c836b2cbc244a08c1f8284/test/fixtures/zero-polygon.pbf -------------------------------------------------------------------------------- /test/parse.test.js: -------------------------------------------------------------------------------- 1 | import test from 'node:test'; 2 | import assert from 'node:assert/strict'; 3 | import fs from 'fs'; 4 | import Protobuf from 'pbf'; 5 | import {VectorTile, VectorTileLayer, VectorTileFeature} from '../index.js'; 6 | import Point from '@mapbox/point-geometry'; 7 | 8 | function getFixtureTile(name) { 9 | const data = fs.readFileSync(new URL(`fixtures/${name}.pbf`, import.meta.url)); 10 | return new VectorTile(new Protobuf(data)); 11 | } 12 | 13 | const tile = getFixtureTile('14-8801-5371.vector'); 14 | 15 | function approximateDeepEqual(a, b, epsilon) { 16 | epsilon = epsilon || 1e-6; 17 | 18 | if (typeof a !== typeof b) 19 | return false; 20 | if (typeof a === 'number') 21 | return Math.abs(a - b) < epsilon; 22 | if (a === null || typeof a !== 'object') 23 | return a === b; 24 | 25 | const ka = Object.keys(a); 26 | const kb = Object.keys(b); 27 | 28 | if (ka.length !== kb.length) 29 | return false; 30 | 31 | ka.sort(); 32 | kb.sort(); 33 | 34 | for (let i = 0; i < ka.length; i++) 35 | if (ka[i] !== kb[i] || !approximateDeepEqual(a[ka[i]], b[ka[i]], epsilon)) 36 | return false; 37 | 38 | return true; 39 | } 40 | 41 | test('should have all layers', () => { 42 | assert.deepEqual(Object.keys(tile.layers), [ 43 | 'landuse', 'waterway', 'water', 'barrier_line', 'building', 44 | 'landuse_overlay', 'tunnel', 'road', 'bridge', 'place_label', 45 | 'water_label', 'poi_label', 'road_label', 'waterway_label']); 46 | }); 47 | 48 | test('should extract the tags of a feature', () => { 49 | assert.equal(tile.layers.poi_label.length, 558); 50 | 51 | const park = tile.layers.poi_label.feature(11); 52 | 53 | assert.deepEqual(park.bbox(), [3898, 1731, 3898, 1731]); 54 | 55 | assert.throws(() => { 56 | tile.layers.poi_label.feature(1e9); 57 | }, 'throws on reading a feature out of bounds'); 58 | 59 | assert.equal(park.id, 3000003150561); 60 | 61 | assert.equal(park.properties.name, 'Mauerpark'); 62 | assert.equal(park.properties.type, 'Park'); 63 | 64 | // Check point geometry 65 | assert.deepEqual(park.loadGeometry(), [[new Point(3898, 1731)]]); 66 | 67 | // Check line geometry 68 | assert.deepEqual(tile.layers.road.feature(656).loadGeometry(), [[new Point(1988, 306), new Point(1808, 321), new Point(1506, 347)]]); 69 | }); 70 | 71 | test('changing first point of a polygon should not change last point', () => { 72 | const building = tile.layers.building.feature(0).loadGeometry(); 73 | assert.deepEqual(building, [[new Point(2039, -32), new Point(2035, -31), new Point(2032, -31), new Point(2032, -32), new Point(2039, -32)]]); 74 | building[0][0].x = 1; 75 | building[0][0].y = 2; 76 | building[0][1].x = 3; 77 | building[0][1].y = 4; 78 | assert.deepEqual(building, [[new Point(1, 2), new Point(3, 4), new Point(2032, -31), new Point(2032, -32), new Point(2039, -32)]]); 79 | }); 80 | 81 | test('toGeoJSON', () => { 82 | assert.ok(approximateDeepEqual(tile.layers.poi_label.feature(11).toGeoJSON(8801, 5371, 14), { 83 | type: 'Feature', 84 | id: 3000003150561, 85 | properties: { 86 | localrank: 1, 87 | maki: 'park', 88 | name: 'Mauerpark', 89 | 'name_de': 'Mauerpark', 90 | 'name_en': 'Mauerpark', 91 | 'name_es': 'Mauerpark', 92 | 'name_fr': 'Mauerpark', 93 | 'osm_id': 3000003150561, 94 | ref: '', 95 | scalerank: 2, 96 | type: 'Park' 97 | }, 98 | geometry: { 99 | type: 'Point', 100 | coordinates: [13.402258157730103, 52.54398925380624] 101 | } 102 | })); 103 | 104 | assert.ok(approximateDeepEqual(tile.layers.bridge.feature(0).toGeoJSON(8801, 5371, 14), { 105 | type: 'Feature', 106 | id: 238162948, 107 | properties: { 108 | class: 'service', 109 | oneway: 0, 110 | 'osm_id': 238162948, 111 | type: 'service' 112 | }, 113 | geometry: { 114 | type: 'LineString', 115 | coordinates: [[13.399457931518555, 52.546334844036416], [13.399441838264465, 52.546504478525016]] 116 | } 117 | })); 118 | 119 | assert.ok(approximateDeepEqual(tile.layers.building.feature(0).toGeoJSON(8801, 5371, 14), { 120 | type: 'Feature', 121 | id: 1000267229912, 122 | properties: { 123 | 'osm_id': 1000267229912 124 | }, 125 | geometry: { 126 | type: 'Polygon', 127 | coordinates: [[[13.392285704612732, 52.54974045706258], [13.392264246940613, 52.549737195107554], 128 | [13.392248153686523, 52.549737195107554], [13.392248153686523, 52.54974045706258], 129 | [13.392285704612732, 52.54974045706258]]] 130 | } 131 | })); 132 | 133 | function geoJSONFromFixture(name) { 134 | const tile = getFixtureTile(name); 135 | return tile.layers.geojson.feature(0).toGeoJSON(0, 0, 0); 136 | } 137 | 138 | // https://github.com/mapbox/vector-tile-spec/issues/30 139 | assert.ok(approximateDeepEqual(geoJSONFromFixture('singleton-multi-point').geometry, { 140 | type: 'Point', 141 | coordinates: [1, 2] 142 | }, 1e-1)); 143 | assert.ok(approximateDeepEqual(geoJSONFromFixture('singleton-multi-line').geometry, { 144 | type: 'LineString', 145 | coordinates: [[1, 2], [3, 4]] 146 | }, 1e-1)); 147 | assert.ok(approximateDeepEqual(geoJSONFromFixture('singleton-multi-polygon').geometry, { 148 | type: 'Polygon', 149 | coordinates: [[[1, 0], [0, 0], [1, 1], [1, 0]]] 150 | }, 1e-1)); 151 | 152 | assert.ok(approximateDeepEqual(geoJSONFromFixture('multi-point').geometry, { 153 | type: 'MultiPoint', 154 | coordinates: [[1, 2], [3, 4]] 155 | }, 1e-1)); 156 | assert.ok(approximateDeepEqual(geoJSONFromFixture('multi-line').geometry, { 157 | type: 'MultiLineString', 158 | coordinates: [[[1, 2], [3, 4]], [[5, 6], [7, 8]]] 159 | }, 1e-1)); 160 | assert.ok(approximateDeepEqual(geoJSONFromFixture('multi-polygon').geometry, { 161 | type: 'MultiPolygon', 162 | coordinates: [[[[1, 0], [0, 0], [1, 1], [1, 0]]], [[[-1, -1], [-1, 0], [0, 0], [-1, -1]]]] 163 | }, 1e-1)); 164 | 165 | // https://github.com/mapbox/vector-tile-js/issues/32 166 | assert.ok(approximateDeepEqual(geoJSONFromFixture('polygon-with-inner').geometry, { 167 | type: 'Polygon', 168 | coordinates: [[[2, -2], [-2, -2], [-2, 2], [2, 2], [2, -2]], [[-1, 1], [-1, -1], [1, -1], [1, 1], [-1, 1]]] 169 | }, 1e-1)); 170 | assert.ok(approximateDeepEqual(geoJSONFromFixture('stacked-multipolygon').geometry, { 171 | type: 'MultiPolygon', 172 | coordinates: [[[[2, -2], [-2, -2], [-2, 2], [2, 2], [2, -2]]], [[[1, -1], [-1, -1], [-1, 1], [1, 1], [1, -1]]]] 173 | }, 1e-1)); 174 | 175 | }); 176 | 177 | test('VectorTileLayer', () => { 178 | const emptyLayer = new VectorTileLayer(new Protobuf(Buffer.alloc(0))); 179 | assert.ok(emptyLayer, 'can be created with no values'); 180 | }); 181 | 182 | test('VectorTileFeature', () => { 183 | const emptyFeature = new VectorTileFeature(new Protobuf(Buffer.alloc(0))); 184 | assert.ok(emptyFeature, 'can be created with no values'); 185 | assert.ok(Array.isArray(VectorTileFeature.types)); 186 | assert.deepEqual(VectorTileFeature.types, ['Unknown', 'Point', 'LineString', 'Polygon']); 187 | }); 188 | 189 | test('https://github.com/mapbox/vector-tile-js/issues/15', () => { 190 | const tile = getFixtureTile('lots-of-tags.vector'); 191 | assert.ok(tile.layers['stuttgart-rails'].feature(0)); 192 | }); 193 | 194 | test('https://github.com/mapbox/mapbox-gl-js/issues/1019', () => { 195 | const tile = getFixtureTile('12-1143-1497.vector'); 196 | assert.ok(tile.layers.water.feature(1).loadGeometry()); 197 | }); 198 | 199 | test('https://github.com/mapbox/vector-tile-js/issues/60', () => { 200 | const tile = getFixtureTile('multipolygon-with-closepath'); 201 | for (const id in tile.layers) { 202 | const layer = tile.layers[id]; 203 | for (let i = 0; i < layer.length; i++) { 204 | layer.feature(i).loadGeometry(); 205 | } 206 | } 207 | }); 208 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true, 4 | "checkJs": true, 5 | "strict": true, 6 | "emitDeclarationOnly": true, 7 | "declaration": true, 8 | "target": "es2020", 9 | "module": "nodenext", 10 | "moduleResolution": "nodenext" 11 | }, 12 | "files": [ 13 | "index.js" 14 | ] 15 | } 16 | --------------------------------------------------------------------------------