diff --git a/.circleci/config.yml b/.circleci/config.yml index 1108be06c2d..5d52de54e0a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -357,8 +357,14 @@ jobs: - attach_workspace: at: ~/ - run: - command: npm run test-unit + name: Run unit tests + command: | + npm run test-unit no_output_timeout: 5m + - store_artifacts: + path: test/unit/vitest + - store_test_results: + path: test/unit/test-results.xml test-query: <<: *linux-defaults diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index f4cc9f2aa28..ad71e380a55 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1 +1 @@ -* @mapbox/gl-js +* @mapbox/gl-js @mapbox/gl-native @mapbox/mapbox-3d-team diff --git a/3d-style/data/bucket/tiled_3d_model_bucket.ts b/3d-style/data/bucket/tiled_3d_model_bucket.ts index 3db534b627a..859a313fb66 100644 --- a/3d-style/data/bucket/tiled_3d_model_bucket.ts +++ b/3d-style/data/bucket/tiled_3d_model_bucket.ts @@ -95,8 +95,9 @@ export class Tiled3dModelFeature { this.emissionHeightBasedParams = []; // Needs to calculate geometry this.feature = {type: 'Point', id: node.id, geometry: [], properties: {'height' : getNodeHeight(node)}}; + this.aabb = this._getLocalBounds(); } - getLocalBounds(): Aabb { + _getLocalBounds(): Aabb { if (!this.node.meshes) { return new Aabb([Infinity, Infinity, Infinity], [-Infinity, -Infinity, -Infinity]); } @@ -105,11 +106,12 @@ export class Tiled3dModelFeature { const aabb = new Aabb([Infinity, Infinity, Infinity], [-Infinity, -Infinity, -Infinity]); for (const mesh of this.node.meshes) { if (this.node.lightMeshIndex !== i) { - aabb.encapsulate(mesh.aabb); + mesh.transformedAabb = Aabb.applyTransform(mesh.aabb, this.node.matrix); + aabb.encapsulate(mesh.transformedAabb); } i++; } - this.aabb = Aabb.applyTransform(aabb, this.node.matrix); + this.aabb = aabb; } return this.aabb; } @@ -551,7 +553,7 @@ class Tiled3dModelBucket implements Bucket { const nodeInfo = nodesInfo[i]; assert(nodeInfo.node.meshes.length > 0); const mesh = nodeInfo.node.meshes[0]; - const meshAabb = Aabb.applyTransform(mesh.aabb, nodeInfo.node.matrix); + const meshAabb = mesh.transformedAabb; if (x < meshAabb.min[0] || y < meshAabb.min[1] || x > meshAabb.max[0] || y > meshAabb.max[1]) continue; if (nodeInfo.node.hidden === true) return {height: 0.0, maxHeight: nodeInfo.feature.properties["height"], hidden: false, verticalScale: nodeInfo.evaluatedScale[2]}; diff --git a/3d-style/data/model.ts b/3d-style/data/model.ts index 455298fddd9..dc8712ee8f5 100644 --- a/3d-style/data/model.ts +++ b/3d-style/data/model.ts @@ -75,6 +75,7 @@ export type Mesh = { pbrBuffer: VertexBuffer; material: Material; aabb: Aabb; + transformedAabb: Aabb; segments: SegmentVector; centroid: vec3; heightmap: Float32Array; diff --git a/3d-style/render/draw_model.ts b/3d-style/render/draw_model.ts index a4e024275c9..fbcce715513 100644 --- a/3d-style/render/draw_model.ts +++ b/3d-style/render/draw_model.ts @@ -824,7 +824,7 @@ function drawBatchedModels(painter: Painter, source: SourceCache, layer: ModelSt } const nodeAabb = () => { - const localBounds = nodeInfo.getLocalBounds(); + const localBounds = nodeInfo.aabb; // @ts-expect-error - TS2322 - Type 'number[]' is not assignable to type 'vec3'. aabb.min = [...localBounds.min]; // @ts-expect-error - TS2322 - Type 'number[]' is not assignable to type 'vec3'. @@ -874,8 +874,7 @@ function drawBatchedModels(painter: Painter, source: SourceCache, layer: ModelSt let opacity = layerOpacity; if (!isShadowPass) { if (frontCutoffEnabled) { - - opacity *= calculateFrontCutoffOpacity(nodeModelMatrix, tr, nodeInfo.getLocalBounds(), frontCutoffParams); + opacity *= calculateFrontCutoffOpacity(tileModelMatrix as any, tr, nodeInfo.aabb, frontCutoffParams); } opacity *= calculateFarCutoffOpacity(cutoffParams, depth); @@ -1120,7 +1119,7 @@ function calculateFarCutoffOpacity(cutoffParams: CutoffParams, depth: number): n return clamp((linearDepth - cutoffStart) / (cutoffEnd - cutoffStart), 0.0, 1.0); } -function calculateFrontCutoffOpacity(nodeModelMatrix: mat4, tr: Transform, aabb: Aabb, cutoffParams: [number, number, number]) { +function calculateFrontCutoffOpacity(tileModelMatrix: mat4, tr: Transform, aabb: Aabb, cutoffParams: [number, number, number]) { // The cutoff opacity is completely disabled when pitch is lower than 20. const fullyOpaquePitch = 20.0; const fullyTransparentPitch = 40.0; @@ -1131,7 +1130,7 @@ function calculateFrontCutoffOpacity(nodeModelMatrix: mat4, tr: Transform, aabb: } const worldToCamera = tr.getWorldToCameraMatrix(); - const mat = mat4.multiply([] as any, worldToCamera, nodeModelMatrix); + const mat = mat4.multiply([] as any, worldToCamera, tileModelMatrix); // The cutoff opacity is calculated based on how much below the view the AABB bottom corners are. // For this, we find the AABB points with the highest and lowest y value in the view space. diff --git a/3d-style/render/lights.ts b/3d-style/render/lights.ts index b5f134b6196..65f60fec6a7 100644 --- a/3d-style/render/lights.ts +++ b/3d-style/render/lights.ts @@ -73,10 +73,10 @@ function calculateGroundRadiance(dir: vec3, dirColor: vec3, ambientColor: vec3): export const lightsUniformValues = (directional: Lights, ambient: Lights, style: Style): UniformValues => { const direction = directional.properties.get('direction'); - const directionalColor = directional.properties.get('color').toRenderColor(style._luts[directional.scope]).toArray01(); + const directionalColor = directional.properties.get('color').toRenderColor(style.getLut(directional.scope)).toArray01(); const directionalIntensity = directional.properties.get('intensity'); - const ambientColor = ambient.properties.get('color').toRenderColor(style._luts[ambient.scope]).toArray01(); + const ambientColor = ambient.properties.get('color').toRenderColor(style.getLut(ambient.scope)).toArray01(); const ambientIntensity = ambient.properties.get('intensity'); const dirVec: [number, number, number] = [direction.x, direction.y, direction.z]; diff --git a/3d-style/render/shadow_renderer.ts b/3d-style/render/shadow_renderer.ts index 250e003b831..968767a98fc 100644 --- a/3d-style/render/shadow_renderer.ts +++ b/3d-style/render/shadow_renderer.ts @@ -621,10 +621,10 @@ export function calculateGroundShadowFactor( const dirDirectionalFactor = Math.max(vec3.dot(groundNormal as [number, number, number], directionVec as [number, number, number]), 0.0); const ambStrength = [0, 0, 0]; // @ts-expect-error - TS2339 - Property 'toRenderColor' does not exist on type 'unknown'. | TS2345 - Argument of type 'unknown' is not assignable to parameter of type 'number'. - vec3.scale(ambStrength as [number, number, number], ambientColor.toRenderColor(style._luts[directionalLight.scope]).toArray01Linear().slice(0, 3), ambientIntensity); + vec3.scale(ambStrength as [number, number, number], ambientColor.toRenderColor(style.getLut(directionalLight.scope)).toArray01Linear().slice(0, 3), ambientIntensity); const dirStrength = [0, 0, 0]; // @ts-expect-error - TS2339 - Property 'toRenderColor' does not exist on type 'unknown'. | TS2363 - The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. - vec3.scale(dirStrength as [number, number, number], dirColor.toRenderColor(style._luts[ambientLight.scope]).toArray01Linear().slice(0, 3), dirDirectionalFactor * dirIntensity); + vec3.scale(dirStrength as [number, number, number], dirColor.toRenderColor(style.getLut(ambientLight.scope)).toArray01Linear().slice(0, 3), dirDirectionalFactor * dirIntensity); // Multiplier X to get from lit surface color L to shadowed surface color S // X = A / (A + D) diff --git a/3d-style/shaders/model.fragment.glsl b/3d-style/shaders/model.fragment.glsl index 8fc9b5ca3bb..4a75e36f9f8 100644 --- a/3d-style/shaders/model.fragment.glsl +++ b/3d-style/shaders/model.fragment.glsl @@ -494,6 +494,10 @@ vec4 finalColor; distance += mix(0.5, 0.0, clamp(resEmission - 1.0, 0.0, 1.0)); opacity *= v_roughness_metallic_emissive_alpha.w * saturate(1.0 - distance * distance); #endif +#else +#ifdef APPLY_LUT_ON_GPU + color = applyLUT(u_lutTexture, color); +#endif #endif // Use emissive strength as interpolation between lit and unlit color // for coherence with other layer types. diff --git a/3d-style/source/replacement_source.ts b/3d-style/source/replacement_source.ts index f96c5f878e2..5c6eafe16e3 100644 --- a/3d-style/source/replacement_source.ts +++ b/3d-style/source/replacement_source.ts @@ -51,7 +51,7 @@ class ReplacementSource { this._sourceIds = []; this._activeRegions = []; this._prevRegions = []; - this._globalClipBounds = {min: new Point(0, 0), max: new Point(0, 0)}; + this._globalClipBounds = {min: new Point(Infinity, Infinity), max: new Point(-Infinity, -Infinity)}; } clear() { diff --git a/CHANGELOG.md b/CHANGELOG.md index aa7e7f8c17a..7d3019ea7a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,20 +1,30 @@ -## 3.5.0-beta.1 +## 3.5.0 ### Breaking changes ⚠️ - This release marks a significant transition for GL JS, moving from [Flow](https://flow.org/) to [TypeScript](https://www.typescriptlang.org/). While we have maintained backward compatibility where possible, the community typings `@types/mapbox-gl` are not fully compatible with the new first-class typings. Users relying on the community typings may experience breaking changes. Please remove the `@types/mapbox-gl` dependency and refer to the [v3.5.0 migration guide](https://github.com/mapbox/mapbox-gl-js/issues/13203) for instructions on upgrading, resolving common issues, and asking questions regarding the migration to the first-class typings. We welcome your feedback and contributions to make Mapbox GL JS better. ### Features and improvements ✨ -- Add `color-theme` property to enable colorization with a lookup table (LUT) images. +- Add `color-theme` property and `Map` `setColorTheme` method to enable colorization with a lookup table (LUT) images. +- Significantly improve performance of `updateData` for `GeoJSON` sources in `dynamic` mode. - Add `icon-occlusion-opacity` and `text-occlusion-opacity` properties to fade symbols behind models and landmarks. - Add `line-occlusion-opacity` property to fade lines behind 3D objects. - Add experimental `clip` layer to filter out rendering data. - Add experimental `line-z-offset` property for a non-globe view. -- Add `model-cutoff-fade-range` property support to control fade out of faraway 3D buildings. +- Add `model-cutoff-fade-range` property to control fade out of faraway 3D buildings. +- Improve precision of `line-pattern` on long lines and higher zooms. +- Add experimental `line-trim-color` and `line-trim-fade-range` properties to customize rendering of lines trimmed with `line-trim-offset`. +- Add `Map` `getSlots` method for listing available slots of a style. ### Bug fixes 🐞 +- Fix a performance regression in Standard style introduced in v3.4.0. - Fix icon rotation during globe transition. - Fix GeoJSON data loss due to frequent `updateData` calls. - Improve `raster-particle` layer animation. +- Fix `model-front-cutoff` property for Meshopt-encoded models. +- Fix errors in the console on empty 3D tiles. +- Fix not properly detecting fingerprinting protection when adding terrain through `setTerrain`. +- Fix `style.load` event missing `style` property. +- Fix errors when using `queryRenderedFeatures` on areas with missing DEM tiles when terrain is enabled. ## 3.4.0 diff --git a/build/check-ts-suppressions.js b/build/check-ts-suppressions.js index 7827f6e9110..6c958596ccb 100755 --- a/build/check-ts-suppressions.js +++ b/build/check-ts-suppressions.js @@ -8,7 +8,7 @@ import {Octokit} from '@octokit/rest'; import {execSync} from 'child_process'; const owner = 'mapbox'; -const repo = 'mapbox-gl-js-internal'; +const repo = 'mapbox-gl-js'; const branch = process.env['CIRCLE_BRANCH'] || execSync('git rev-parse --abbrev-ref HEAD').toString().trim(); async function getBannedTsComments() { diff --git a/build/generate-typed-style-spec.ts b/build/generate-typed-style-spec.ts index be8f1fec48f..fa4064e0d1f 100644 --- a/build/generate-typed-style-spec.ts +++ b/build/generate-typed-style-spec.ts @@ -253,6 +253,21 @@ export type TransitionSpecification = { // Note: doesn't capture interpolatable vs. non-interpolatable types. +export type PropertyFunctionStop = [number, T]; +export type ZoomAndPropertyFunctionStop = [{zoom: number; value: string | number | boolean}, T]; + +/** + * @deprecated Use [Expressions](https://docs.mapbox.com/style-spec/reference/expressions/) syntax instead. +*/ +export type FunctionSpecification = { + stops: Array | ZoomAndPropertyFunctionStop>; + base?: number; + property?: string; + type?: 'identity' | 'exponential' | 'interval' | 'categorical'; + colorSpace?: 'rgb' | 'lab' | 'hcl'; + default?: T; +}; + export type CameraFunctionSpecification = | { type: 'exponential', stops: Array<[number, T]> } | { type: 'interval', stops: Array<[number, T]> }; @@ -277,6 +292,7 @@ export type PropertyValueSpecification = export type DataDrivenPropertyValueSpecification = | T + | FunctionSpecification | CameraFunctionSpecification | SourceFunctionSpecification | CompositeFunctionSpecification diff --git a/debug/line-pattern.html b/debug/line-pattern.html index c5fd68f8c7e..1dfff38ef9e 100644 --- a/debug/line-pattern.html +++ b/debug/line-pattern.html @@ -148,6 +148,28 @@ ] }; +const longRoute = { + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": {}, + "geometry": { + "coordinates": [ + [ 48.884680, 16.085282 ], [ 49.061463, 16.515979 ], [ 48.891005, 16.562687 ], [ 48.974135, 16.763008 ], + [ 49.268492, 16.718297 ], [ 49.331357, 16.902966 ], [ 49.386120, 16.986536 ], [ 49.587155, 17.011794 ], + [ 49.936453, 17.009859 ], [ 50.180135, 17.013721 ], [ 50.236977, 16.961266 ], [ 49.676524, 16.889389 ], + [ 49.528352, 16.714410 ], [ 49.408444, 16.321152 ], [ 49.556546, 16.118124 ], [ 49.802388, 16.008736 ], + [ 50.056386, 15.991148 ], [ 50.415946, 16.075220 ], [ 50.674048, 16.215688 ], [ 50.180274, 16.204027 ], + [ 50.178172, 16.317207 ], [ 49.885689, 16.315238 ], [ 49.875532, 16.399057 ], [ 49.674499, 16.445862 ], + [ 50.186082, 16.722162 ], [ 50.699853, 16.720252 ] + ], + "type": "LineString" + } + } + ] +}; + map.on('load', () => { map.setFog({ 'horizon-blend': 0.0 @@ -160,6 +182,13 @@ 'maxzoom': 19, }); + map.addSource('longRoute', { + 'type': 'geojson', + 'data': longRoute, + 'lineMetrics': true, + 'maxzoom': 16, + }); + map.addSource('mapbox-dem', { 'type': 'raster-dem', 'url': 'mapbox://mapbox.mapbox-terrain-dem-v1', @@ -216,6 +245,20 @@ } map.setPaintProperty('route', 'line-trim-offset', [0, progress]); }, 100); + + map.addLayer({ + 'id': 'longRoute', + 'slot': 'top', + 'source': 'longRoute', + 'type': 'line', + 'layout': { + 'line-join': 'none' + }, + 'paint': { + 'line-pattern': 'bluecircle', + 'line-width': zoomConstExpression + } + }); }); }); }); diff --git a/debug/lut.html b/debug/lut.html index 65c96924d28..db6ea2bff46 100644 --- a/debug/lut.html +++ b/debug/lut.html @@ -36,9 +36,8 @@ contrast: 1.0, saturation: 0.5, hue: 0.01, - lightPreset: 'day', lutPreset: 'custom', - styleURL: 'mapbox://styles/mapbox/standard', + styleURL: 'mapbox://styles/mapbox/streets-v12', firstLoadDone: false }; @@ -47,7 +46,6 @@ let lutData = ""; let standard = '{}'; - const lightPresets = ['dawn', 'day', 'dusk', 'night']; const lutPresets = ['custom', 'almost-bw', 'bright', 'bw-2', 'identity', 'red']; var map = window.map = new mapboxgl.Map({ @@ -61,25 +59,6 @@ style: PARAMS.styleURL }); - function updateStyle() { - map.setStyle({ - "version": 8, - "imports": [ - { - "id": "standard", - "url": "", - "data": standard, - "config": { - "lightPreset": PARAMS.lightPreset - } - } - ], - "color-theme": standard["color-theme"], - "sources": {}, - "layers": [] - }); - } - async function fetchJSON(url) { try { const response = await fetch(url); @@ -93,17 +72,6 @@ } } - function downloadStyle() { - fetchJSON(`https://api.mapbox.com/styles/v1/${PARAMS.styleURL.replace("mapbox://styles/", "")}?sdk=js-3.4.0-beta.1&access_token=${mapboxgl.accessToken}`).then(data => { - standard = data; - updateStyle(); - if (!PARAMS.firstLoadDone) { - updateLUT(); - PARAMS.firstLoadDone = true; - } - }); - } - const query = Object.fromEntries(location.search.substring(1).split('&').map(v => v.split('='))); const name = query.lut ? query.lut : 'red'; @@ -218,10 +186,9 @@ drawLUTOnCanvas(canvas, lut, resolution); document.getElementById('canvasImageHolder').src = canvas.toDataURL("image/png"); lutData = canvas.toDataURL(); - standard['color-theme'] = { + map.setColorTheme({ "data": lutData - }; - updateStyle(); + }); } else { // Load from preset const img = document.getElementById('canvasImageHolder'); @@ -230,10 +197,9 @@ const ctx = canvas.getContext('2d'); ctx.drawImage(img, 0, 0); lutData = canvas.toDataURL(); - standard['color-theme'] = { + map.setColorTheme({ "data": lutData - }; - updateStyle(); + }); }; img.onerror = function (e) { throw new Error(e.message); @@ -248,10 +214,9 @@ if (file) { const reader = new FileReader(); reader.onload = function(e) { - standard['color-theme'] = { - "data": e.target.result - }; - updateStyle(); + map.setColorTheme({ + "data": e.target.result + }); const img = document.getElementById('canvasImageHolder'); img.src = e.target.result; img.style.display = 'block'; @@ -307,17 +272,10 @@ updateLUT(); }); - pane.addBinding(PARAMS, 'lightPreset', { - label: 'lightPreset', - options: lightPresets.reduce((acc, item) => ({...acc, [item]: item}), {}) - }).on('change', (e) => { - updateStyle(); - }); - pane.addBinding(PARAMS, 'styleURL', { label: 'styleURL' }).on('change', (e) => { - downloadStyle(); + map.setStyle(PARAMS.styleURL); }); map.on('style.load', () => { @@ -383,9 +341,6 @@ } }); }); - map.once('load', () => { - downloadStyle(); - }); diff --git a/package-lock.json b/package-lock.json index a20cd8993d8..1cd1b81e912 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "mapbox-gl", - "version": "3.5.0-beta.1", + "version": "3.5.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "mapbox-gl", - "version": "3.5.0-beta.1", + "version": "3.5.0", "license": "SEE LICENSE IN LICENSE.txt", "workspaces": [ "src/style-spec" @@ -19,14 +19,14 @@ "@mapbox/unitbezier": "^0.0.1", "@mapbox/vector-tile": "^1.3.1", "@mapbox/whoots-js": "^3.1.0", - "cheap-ruler": "^3.0.1", + "cheap-ruler": "^4.0.0", "csscolorparser": "~1.0.3", - "earcut": "^2.2.4", + "earcut": "^3.0.0", "fflate": "^0.8.1", "geojson-vt": "^4.0.2", "gl-matrix": "^3.4.3", "grid-index": "^1.1.0", - "kdbush": "^4.0.1", + "kdbush": "^4.0.2", "lodash.clonedeep": "^4.5.0", "murmurhash-js": "^1.0.0", "pbf": "^3.2.1", @@ -34,54 +34,53 @@ "quickselect": "^2.0.0", "rw": "^1.3.3", "serialize-to-js": "^3.1.2", - "supercluster": "^8.0.0", + "supercluster": "^8.0.1", "tiny-lru": "^11.2.6", "tinyqueue": "^2.0.3", - "tweakpane": "^4.0.3", + "tweakpane": "^4.0.4", "vt-pbf": "^3.1.3" }, "devDependencies": { "@mapbox/mvt-fixtures": "^3.10.0", - "@octokit/rest": "^20.1.1", - "@rollup/plugin-alias": "^5.1.0", + "@octokit/rest": "^21.0.0", "@rollup/plugin-commonjs": "^26.0.1", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-replace": "^5.0.7", "@rollup/plugin-strip": "^3.0.4", "@rollup/plugin-terser": "^0.4.4", - "@tweakpane/core": "^2.0.3", + "@tweakpane/core": "^2.0.4", "@types/geojson": "^7946.0.14", "@types/geojson-vt": "^3.2.5", "@types/jest": "^29.5.12", "@types/mapbox__point-geometry": "^0.1.4", "@types/mapbox__vector-tile": "^1.3.4", - "@types/node": "^20.14.3", + "@types/node": "^20.14.9", "@types/offscreencanvas": "^2019.7.3", "@types/pbf": "^3.0.5", - "@typescript-eslint/eslint-plugin": "^7.13.1", - "@typescript-eslint/parser": "^7.13.1", + "@typescript-eslint/eslint-plugin": "^7.15.0", + "@typescript-eslint/parser": "^7.15.0", "@vitest/browser": "^1.6.0", "@vitest/ui": "^1.6.0", - "address": "^2.0.2", + "address": "^2.0.3", "browserify": "^17.0.0", "chalk": "^5.0.1", "chokidar": "^3.6.0", "cross-env": "^7.0.3", - "cssnano": "^7.0.2", + "cssnano": "^7.0.3", "d3-queue": "^3.0.7", "diff": "^5.2.0", "dts-bundle-generator": "^9.5.1", "ejs": "^3.1.10", "envify": "^4.1.0", - "esbuild": "^0.21.5", + "esbuild": "^0.23.0", "eslint": "^8.57.0", "eslint-config-mourner": "^3.0.0", "eslint-import-resolver-typescript": "^3.6.1", "eslint-plugin-html": "^8.1.1", "eslint-plugin-import": "^2.29.1", - "eslint-plugin-jsdoc": "^48.2.12", - "glob": "^10.4.1", + "eslint-plugin-jsdoc": "^48.5.0", + "glob": "^10.4.2", "is-builtin-module": "^4.0.0", "jest-extended": "^4.0.2", "json-stringify-pretty-compact": "^4.0.0", @@ -93,9 +92,9 @@ "node-notifier": "^10.0.1", "npm-font-open-sans": "^1.1.0", "npm-run-all": "^4.1.5", - "pixelmatch": "^5.3.0", - "playwright": "^1.44.1", - "postcss": "^8.4.38", + "pixelmatch": "^6.0.0", + "playwright": "^1.45.0", + "postcss": "^8.4.39", "postcss-cli": "^11.0.0", "postcss-inline-svg": "^6.0.0", "pretty-bytes": "^6.0.0", @@ -107,15 +106,15 @@ "shuffle-seed": "^1.1.6", "st": "^3.0.0", "stylelint": "^16.6.1", - "stylelint-config-standard": "^36.0.0", + "stylelint-config-standard": "^36.0.1", "tape": "^5.8.1", "tape-filter": "^1.0.4", - "testem": "^3.14.0", - "tsx": "^4.15.6", - "typescript": "^5.4.5", - "typescript-eslint": "^7.13.1", + "testem": "^3.15.0", + "tsx": "^4.16.0", + "typescript": "^5.5.3", + "typescript-eslint": "^7.15.0", "utility-types": "^3.11.0", - "vite-plugin-arraybuffer": "^0.0.7", + "vite-plugin-arraybuffer": "^0.0.8", "vitest": "^1.6.0" } }, @@ -341,9 +340,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", - "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.0.tgz", + "integrity": "sha512-3sG8Zwa5fMcA9bgqB8AfWPQ+HFke6uD3h1s3RIwUNK8EG7a4buxvuFTs3j1IMs2NXAk9F30C/FF4vxRgQCcmoQ==", "cpu": [ "ppc64" ], @@ -353,13 +352,13 @@ "aix" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", - "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.0.tgz", + "integrity": "sha512-+KuOHTKKyIKgEEqKbGTK8W7mPp+hKinbMBeEnNzjJGyFcWsfrXjSTNluJHCY1RqhxFurdD8uNXQDei7qDlR6+g==", "cpu": [ "arm" ], @@ -369,13 +368,13 @@ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", - "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.0.tgz", + "integrity": "sha512-EuHFUYkAVfU4qBdyivULuu03FhJO4IJN9PGuABGrFy4vUuzk91P2d+npxHcFdpUnfYKy0PuV+n6bKIpHOB3prQ==", "cpu": [ "arm64" ], @@ -385,13 +384,13 @@ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", - "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.0.tgz", + "integrity": "sha512-WRrmKidLoKDl56LsbBMhzTTBxrsVwTKdNbKDalbEZr0tcsBgCLbEtoNthOW6PX942YiYq8HzEnb4yWQMLQuipQ==", "cpu": [ "x64" ], @@ -401,13 +400,13 @@ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", - "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.0.tgz", + "integrity": "sha512-YLntie/IdS31H54Ogdn+v50NuoWF5BDkEUFpiOChVa9UnKpftgwzZRrI4J132ETIi+D8n6xh9IviFV3eXdxfow==", "cpu": [ "arm64" ], @@ -417,13 +416,13 @@ "darwin" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", - "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.0.tgz", + "integrity": "sha512-IMQ6eme4AfznElesHUPDZ+teuGwoRmVuuixu7sv92ZkdQcPbsNHzutd+rAfaBKo8YK3IrBEi9SLLKWJdEvJniQ==", "cpu": [ "x64" ], @@ -433,13 +432,13 @@ "darwin" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", - "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.0.tgz", + "integrity": "sha512-0muYWCng5vqaxobq6LB3YNtevDFSAZGlgtLoAc81PjUfiFz36n4KMpwhtAd4he8ToSI3TGyuhyx5xmiWNYZFyw==", "cpu": [ "arm64" ], @@ -449,13 +448,13 @@ "freebsd" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", - "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.0.tgz", + "integrity": "sha512-XKDVu8IsD0/q3foBzsXGt/KjD/yTKBCIwOHE1XwiXmrRwrX6Hbnd5Eqn/WvDekddK21tfszBSrE/WMaZh+1buQ==", "cpu": [ "x64" ], @@ -465,13 +464,13 @@ "freebsd" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", - "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.0.tgz", + "integrity": "sha512-SEELSTEtOFu5LPykzA395Mc+54RMg1EUgXP+iw2SJ72+ooMwVsgfuwXo5Fn0wXNgWZsTVHwY2cg4Vi/bOD88qw==", "cpu": [ "arm" ], @@ -481,13 +480,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", - "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.0.tgz", + "integrity": "sha512-j1t5iG8jE7BhonbsEg5d9qOYcVZv/Rv6tghaXM/Ug9xahM0nX/H2gfu6X6z11QRTMT6+aywOMA8TDkhPo8aCGw==", "cpu": [ "arm64" ], @@ -497,13 +496,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", - "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.0.tgz", + "integrity": "sha512-P7O5Tkh2NbgIm2R6x1zGJJsnacDzTFcRWZyTTMgFdVit6E98LTxO+v8LCCLWRvPrjdzXHx9FEOA8oAZPyApWUA==", "cpu": [ "ia32" ], @@ -513,13 +512,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", - "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.0.tgz", + "integrity": "sha512-InQwepswq6urikQiIC/kkx412fqUZudBO4SYKu0N+tGhXRWUqAx+Q+341tFV6QdBifpjYgUndV1hhMq3WeJi7A==", "cpu": [ "loong64" ], @@ -529,13 +528,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", - "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.0.tgz", + "integrity": "sha512-J9rflLtqdYrxHv2FqXE2i1ELgNjT+JFURt/uDMoPQLcjWQA5wDKgQA4t/dTqGa88ZVECKaD0TctwsUfHbVoi4w==", "cpu": [ "mips64el" ], @@ -545,13 +544,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", - "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.0.tgz", + "integrity": "sha512-cShCXtEOVc5GxU0fM+dsFD10qZ5UpcQ8AM22bYj0u/yaAykWnqXJDpd77ublcX6vdDsWLuweeuSNZk4yUxZwtw==", "cpu": [ "ppc64" ], @@ -561,13 +560,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", - "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.0.tgz", + "integrity": "sha512-HEtaN7Y5UB4tZPeQmgz/UhzoEyYftbMXrBCUjINGjh3uil+rB/QzzpMshz3cNUxqXN7Vr93zzVtpIDL99t9aRw==", "cpu": [ "riscv64" ], @@ -577,13 +576,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", - "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.0.tgz", + "integrity": "sha512-WDi3+NVAuyjg/Wxi+o5KPqRbZY0QhI9TjrEEm+8dmpY9Xir8+HE/HNx2JoLckhKbFopW0RdO2D72w8trZOV+Wg==", "cpu": [ "s390x" ], @@ -593,13 +592,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", - "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.0.tgz", + "integrity": "sha512-a3pMQhUEJkITgAw6e0bWA+F+vFtCciMjW/LPtoj99MhVt+Mfb6bbL9hu2wmTZgNd994qTAEw+U/r6k3qHWWaOQ==", "cpu": [ "x64" ], @@ -609,13 +608,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", - "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.0.tgz", + "integrity": "sha512-cRK+YDem7lFTs2Q5nEv/HHc4LnrfBCbH5+JHu6wm2eP+d8OZNoSMYgPZJq78vqQ9g+9+nMuIsAO7skzphRXHyw==", "cpu": [ "x64" ], @@ -625,13 +624,29 @@ "netbsd" ], "engines": { - "node": ">=12" + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.0.tgz", + "integrity": "sha512-suXjq53gERueVWu0OKxzWqk7NxiUWSUlrxoZK7usiF50C6ipColGR5qie2496iKGYNLhDZkPxBI3erbnYkU0rQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", - "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.0.tgz", + "integrity": "sha512-6p3nHpby0DM/v15IFKMjAaayFhqnXV52aEmv1whZHX56pdkK+MEaLoQWj+H42ssFarP1PcomVhbsR4pkz09qBg==", "cpu": [ "x64" ], @@ -641,13 +656,13 @@ "openbsd" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", - "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.0.tgz", + "integrity": "sha512-BFelBGfrBwk6LVrmFzCq1u1dZbG4zy/Kp93w2+y83Q5UGYF1d8sCzeLI9NXjKyujjBBniQa8R8PzLFAUrSM9OA==", "cpu": [ "x64" ], @@ -657,13 +672,13 @@ "sunos" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", - "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.0.tgz", + "integrity": "sha512-lY6AC8p4Cnb7xYHuIxQ6iYPe6MfO2CC43XXKo9nBXDb35krYt7KGhQnOkRGar5psxYkircpCqfbNDB4uJbS2jQ==", "cpu": [ "arm64" ], @@ -673,13 +688,13 @@ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", - "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.0.tgz", + "integrity": "sha512-7L1bHlOTcO4ByvI7OXVI5pNN6HSu6pUQq9yodga8izeuB1KcT2UkHaH6118QJwopExPn0rMHIseCTx1CRo/uNA==", "cpu": [ "ia32" ], @@ -689,13 +704,13 @@ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", - "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.0.tgz", + "integrity": "sha512-Arm+WgUFLUATuoxCJcahGuk6Yj9Pzxd6l11Zb/2aAuv5kWWvvfhLFo2fni4uSK5vzlUdCGZ/BdV5tH8klj8p8g==", "cpu": [ "x64" ], @@ -705,7 +720,7 @@ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@eslint-community/eslint-utils": { @@ -1416,6 +1431,7 @@ "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-4.0.0.tgz", "integrity": "sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==", "dev": true, + "peer": true, "engines": { "node": ">= 18" } @@ -1425,6 +1441,7 @@ "resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.1.0.tgz", "integrity": "sha512-BDa2VAMLSh3otEiaMJ/3Y36GU4qf6GI+VivQ/P41NC6GHcdxpKlqV0ikSZ5gdQsmS3ojXeRx5vasgNTinF0Q4g==", "dev": true, + "peer": true, "dependencies": { "@octokit/auth-token": "^4.0.0", "@octokit/graphql": "^7.0.0", @@ -1443,6 +1460,7 @@ "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.4.tgz", "integrity": "sha512-DWPLtr1Kz3tv8L0UvXTDP1fNwM0S+z6EJpRcvH66orY6Eld4XBMCSYsaWp4xIm61jTWxK68BrR7ibO+vSDnZqw==", "dev": true, + "peer": true, "dependencies": { "@octokit/types": "^12.0.0", "universal-user-agent": "^6.0.0" @@ -1456,6 +1474,7 @@ "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.0.2.tgz", "integrity": "sha512-OJ2iGMtj5Tg3s6RaXH22cJcxXRi7Y3EBqbHTBRq+PQAqfaS8f/236fUrWhfSn8P4jovyzqucxme7/vWSSZBX2Q==", "dev": true, + "peer": true, "dependencies": { "@octokit/request": "^8.0.1", "@octokit/types": "^12.0.0", @@ -1469,7 +1488,8 @@ "version": "20.0.0", "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-20.0.0.tgz", "integrity": "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA==", - "dev": true + "dev": true, + "peer": true }, "node_modules/@octokit/plugin-paginate-rest": { "version": "11.3.1", @@ -1501,18 +1521,6 @@ "@octokit/openapi-types": "^22.2.0" } }, - "node_modules/@octokit/plugin-request-log": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-4.0.1.tgz", - "integrity": "sha512-GihNqNpGHorUrO7Qa9JbAl0dbLnqJVrV8OXe2Zm5/Y4wFkZQDfTreBzVmiRfJVfE4mClXdihHnbpyyO9FSX4HA==", - "dev": true, - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "@octokit/core": "5" - } - }, "node_modules/@octokit/plugin-rest-endpoint-methods": { "version": "13.2.2", "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-13.2.2.tgz", @@ -1548,6 +1556,7 @@ "resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.2.0.tgz", "integrity": "sha512-exPif6x5uwLqv1N1irkLG1zZNJkOtj8bZxuVHd71U5Ftuxf2wGNvAJyNBcPbPC+EBzwYEbBDdSFb8EPcjpYxPQ==", "dev": true, + "peer": true, "dependencies": { "@octokit/endpoint": "^9.0.0", "@octokit/request-error": "^5.0.0", @@ -1563,6 +1572,7 @@ "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.0.1.tgz", "integrity": "sha512-X7pnyTMV7MgtGmiXBwmO6M5kIPrntOXdyKZLigNfQWSEQzVxR4a4vo49vJjTWX70mPndj8KhfT4Dx+2Ng3vnBQ==", "dev": true, + "peer": true, "dependencies": { "@octokit/types": "^12.0.0", "deprecation": "^2.0.0", @@ -1573,25 +1583,146 @@ } }, "node_modules/@octokit/rest": { - "version": "20.1.1", - "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-20.1.1.tgz", - "integrity": "sha512-MB4AYDsM5jhIHro/dq4ix1iWTLGToIGk6cWF5L6vanFaMble5jTX/UBQyiv05HsWnwUtY8JrfHy2LWfKwihqMw==", + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-21.0.0.tgz", + "integrity": "sha512-XudXXOmiIjivdjNZ+fN71NLrnDM00sxSZlhqmPR3v0dVoJwyP628tSlc12xqn8nX3N0965583RBw5GPo6r8u4Q==", + "dev": true, + "dependencies": { + "@octokit/core": "^6.1.2", + "@octokit/plugin-paginate-rest": "^11.0.0", + "@octokit/plugin-request-log": "^5.1.0", + "@octokit/plugin-rest-endpoint-methods": "^13.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/rest/node_modules/@octokit/auth-token": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-5.1.1.tgz", + "integrity": "sha512-rh3G3wDO8J9wSjfI436JUKzHIxq8NaiL0tVeB2aXmG6p/9859aUOAjA9pmSPNGGZxfwmaJ9ozOJImuNVJdpvbA==", + "dev": true, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/rest/node_modules/@octokit/core": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-6.1.2.tgz", + "integrity": "sha512-hEb7Ma4cGJGEUNOAVmyfdB/3WirWMg5hDuNFVejGEDFqupeOysLc2sG6HJxY2etBp5YQu5Wtxwi020jS9xlUwg==", + "dev": true, + "dependencies": { + "@octokit/auth-token": "^5.0.0", + "@octokit/graphql": "^8.0.0", + "@octokit/request": "^9.0.0", + "@octokit/request-error": "^6.0.1", + "@octokit/types": "^13.0.0", + "before-after-hook": "^3.0.2", + "universal-user-agent": "^7.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/rest/node_modules/@octokit/endpoint": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-10.1.1.tgz", + "integrity": "sha512-JYjh5rMOwXMJyUpj028cu0Gbp7qe/ihxfJMLc8VZBMMqSwLgOxDI1911gV4Enl1QSavAQNJcwmwBF9M0VvLh6Q==", + "dev": true, + "dependencies": { + "@octokit/types": "^13.0.0", + "universal-user-agent": "^7.0.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/rest/node_modules/@octokit/graphql": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-8.1.1.tgz", + "integrity": "sha512-ukiRmuHTi6ebQx/HFRCXKbDlOh/7xEV6QUXaE7MJEKGNAncGI/STSbOkl12qVXZrfZdpXctx5O9X1AIaebiDBg==", + "dev": true, + "dependencies": { + "@octokit/request": "^9.0.0", + "@octokit/types": "^13.0.0", + "universal-user-agent": "^7.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/rest/node_modules/@octokit/openapi-types": { + "version": "22.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-22.2.0.tgz", + "integrity": "sha512-QBhVjcUa9W7Wwhm6DBFu6ZZ+1/t/oYxqc2tp81Pi41YNuJinbFRx8B133qVOrAaBbF7D/m0Et6f9/pZt9Rc+tg==", + "dev": true + }, + "node_modules/@octokit/rest/node_modules/@octokit/plugin-request-log": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-5.3.0.tgz", + "integrity": "sha512-FiGcyjdtYPlr03ExBk/0ysIlEFIFGJQAVoPPMxL19B24bVSEiZQnVGBunNtaAF1YnvE/EFoDpXmITtRnyCiypQ==", + "dev": true, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": ">=6" + } + }, + "node_modules/@octokit/rest/node_modules/@octokit/request": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-9.1.1.tgz", + "integrity": "sha512-pyAguc0p+f+GbQho0uNetNQMmLG1e80WjkIaqqgUkihqUp0boRU6nKItXO4VWnr+nbZiLGEyy4TeKRwqaLvYgw==", + "dev": true, + "dependencies": { + "@octokit/endpoint": "^10.0.0", + "@octokit/request-error": "^6.0.1", + "@octokit/types": "^13.1.0", + "universal-user-agent": "^7.0.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/rest/node_modules/@octokit/request-error": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-6.1.1.tgz", + "integrity": "sha512-1mw1gqT3fR/WFvnoVpY/zUM2o/XkMs/2AszUUG9I69xn0JFLv6PGkPhNk5lbfvROs79wiS0bqiJNxfCZcRJJdg==", "dev": true, "dependencies": { - "@octokit/core": "^5.0.2", - "@octokit/plugin-paginate-rest": "11.3.1", - "@octokit/plugin-request-log": "^4.0.0", - "@octokit/plugin-rest-endpoint-methods": "13.2.2" + "@octokit/types": "^13.0.0" }, "engines": { "node": ">= 18" } }, + "node_modules/@octokit/rest/node_modules/@octokit/types": { + "version": "13.5.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.5.0.tgz", + "integrity": "sha512-HdqWTf5Z3qwDVlzCrP8UJquMwunpDiMPt5er+QjGzL4hqr/vBVY/MauQgS1xWxCDT1oMx1EULyqxncdCY/NVSQ==", + "dev": true, + "dependencies": { + "@octokit/openapi-types": "^22.2.0" + } + }, + "node_modules/@octokit/rest/node_modules/before-after-hook": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-3.0.2.tgz", + "integrity": "sha512-Nik3Sc0ncrMK4UUdXQmAnRtzmNQTAAXmXIopizwZ1W1t8QmfJj+zL4OA2I7XPTPW5z5TDqv4hRo/JzouDJnX3A==", + "dev": true + }, + "node_modules/@octokit/rest/node_modules/universal-user-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.2.tgz", + "integrity": "sha512-0JCqzSKnStlRRQfCdowvqy3cy0Dvtlb8xecj/H8JFZuCze4rwjPZQOgvFvn0Ws/usCHQFGpyr+pB9adaGwXn4Q==", + "dev": true + }, "node_modules/@octokit/types": { "version": "12.6.0", "resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.6.0.tgz", "integrity": "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==", "dev": true, + "peer": true, "dependencies": { "@octokit/openapi-types": "^20.0.0" } @@ -1628,32 +1759,24 @@ "node": ">=14" } }, + "node_modules/@pkgr/core": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", + "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, "node_modules/@polka/url": { "version": "1.0.0-next.25", "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.25.tgz", "integrity": "sha512-j7P6Rgr3mmtdkeDGTe0E/aYyWEWVtc5yFXtHCRHs28/jptDEWfaVOc5T7cblqy1XKPPfCxJc/8DwQ5YgLOZOVQ==", "dev": true }, - "node_modules/@rollup/plugin-alias": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@rollup/plugin-alias/-/plugin-alias-5.1.0.tgz", - "integrity": "sha512-lpA3RZ9PdIG7qqhEfv79tBffNaoDuukFDrmhLqg9ifv99u/ehn+lOg30x2zmhf8AQqQUZaMk/B9fZraQ6/acDQ==", - "dev": true, - "dependencies": { - "slash": "^4.0.0" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, "node_modules/@rollup/plugin-commonjs": { "version": "26.0.1", "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-26.0.1.tgz", @@ -2080,9 +2203,9 @@ } }, "node_modules/@tweakpane/core": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@tweakpane/core/-/core-2.0.3.tgz", - "integrity": "sha512-qHci4XA1Wngpwy8IzsLh5JEdscz8aDti/9YhyOaq01si+cgNDaZfwzTtXdn1+xTxSnCM+pW4Zb2/4eqn+K1ATw==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@tweakpane/core/-/core-2.0.4.tgz", + "integrity": "sha512-0P3xcmvjBr8AmqMOEDNYIbkiaPwvQPkj8VeJX+8ZYkpRgWWbNp1HLbld0MDI0WJHdom89osH3MmCDLnWEXKI2w==", "dev": true }, "node_modules/@types/cookie": { @@ -2205,9 +2328,9 @@ } }, "node_modules/@types/node": { - "version": "20.14.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.3.tgz", - "integrity": "sha512-Nuzqa6WAxeGnve6SXqiPAM9rA++VQs+iLZ1DDd56y0gdvygSZlQvZuvdFPR3yLqkVxPu4WrO02iDEyH1g+wazw==", + "version": "20.14.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.9.tgz", + "integrity": "sha512-06OCtnTXtWOZBJlRApleWndH4JsRVs1pDCc8dLSQp+7PpUpX3ePdHyeNSFTeSe7FtKyQkrlPvHwJOW3SLd8Oyg==", "dev": true, "dependencies": { "undici-types": "~5.26.4" @@ -2265,16 +2388,17 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "7.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.13.1.tgz", - "integrity": "sha512-kZqi+WZQaZfPKnsflLJQCz6Ze9FFSMfXrrIOcyargekQxG37ES7DJNpJUE9Q/X5n3yTIP/WPutVNzgknQ7biLg==", + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.15.0.tgz", + "integrity": "sha512-uiNHpyjZtFrLwLDpHnzaDlP3Tt6sGMqTCiqmxaN4n4RP0EfYZDODJyddiFDF44Hjwxr5xAcaYxVKm9QKQFJFLA==", "dev": true, + "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "7.13.1", - "@typescript-eslint/type-utils": "7.13.1", - "@typescript-eslint/utils": "7.13.1", - "@typescript-eslint/visitor-keys": "7.13.1", + "@typescript-eslint/scope-manager": "7.15.0", + "@typescript-eslint/type-utils": "7.15.0", + "@typescript-eslint/utils": "7.15.0", + "@typescript-eslint/visitor-keys": "7.15.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -2298,15 +2422,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "7.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.13.1.tgz", - "integrity": "sha512-1ELDPlnLvDQ5ybTSrMhRTFDfOQEOXNM+eP+3HT/Yq7ruWpciQw+Avi73pdEbA4SooCawEWo3dtYbF68gN7Ed1A==", + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.15.0.tgz", + "integrity": "sha512-k9fYuQNnypLFcqORNClRykkGOMOj+pV6V91R4GO/l1FDGwpqmSwoOQrOHo3cGaH63e+D3ZiCAOsuS/D2c99j/A==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/scope-manager": "7.13.1", - "@typescript-eslint/types": "7.13.1", - "@typescript-eslint/typescript-estree": "7.13.1", - "@typescript-eslint/visitor-keys": "7.13.1", + "@typescript-eslint/scope-manager": "7.15.0", + "@typescript-eslint/types": "7.15.0", + "@typescript-eslint/typescript-estree": "7.15.0", + "@typescript-eslint/visitor-keys": "7.15.0", "debug": "^4.3.4" }, "engines": { @@ -2326,13 +2451,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "7.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.13.1.tgz", - "integrity": "sha512-adbXNVEs6GmbzaCpymHQ0MB6E4TqoiVbC0iqG3uijR8ZYfpAXMGttouQzF4Oat3P2GxDVIrg7bMI/P65LiQZdg==", + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.15.0.tgz", + "integrity": "sha512-Q/1yrF/XbxOTvttNVPihxh1b9fxamjEoz2Os/Pe38OHwxC24CyCqXxGTOdpb4lt6HYtqw9HetA/Rf6gDGaMPlw==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "7.13.1", - "@typescript-eslint/visitor-keys": "7.13.1" + "@typescript-eslint/types": "7.15.0", + "@typescript-eslint/visitor-keys": "7.15.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -2343,13 +2469,14 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "7.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.13.1.tgz", - "integrity": "sha512-aWDbLu1s9bmgPGXSzNCxELu+0+HQOapV/y+60gPXafR8e2g1Bifxzevaa+4L2ytCWm+CHqpELq4CSoN9ELiwCg==", + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.15.0.tgz", + "integrity": "sha512-SkgriaeV6PDvpA6253PDVep0qCqgbO1IOBiycjnXsszNTVQe5flN5wR5jiczoEoDEnAqYFSFFc9al9BSGVltkg==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "7.13.1", - "@typescript-eslint/utils": "7.13.1", + "@typescript-eslint/typescript-estree": "7.15.0", + "@typescript-eslint/utils": "7.15.0", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, @@ -2370,10 +2497,11 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "7.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.13.1.tgz", - "integrity": "sha512-7K7HMcSQIAND6RBL4kDl24sG/xKM13cA85dc7JnmQXw2cBDngg7c19B++JzvJHRG3zG36n9j1i451GBzRuHchw==", + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.15.0.tgz", + "integrity": "sha512-aV1+B1+ySXbQH0pLK0rx66I3IkiZNidYobyfn0WFsdGhSXw+P3YOqeTq5GED458SfB24tg+ux3S+9g118hjlTw==", "dev": true, + "license": "MIT", "engines": { "node": "^18.18.0 || >=20.0.0" }, @@ -2383,13 +2511,14 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "7.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.13.1.tgz", - "integrity": "sha512-uxNr51CMV7npU1BxZzYjoVz9iyjckBduFBP0S5sLlh1tXYzHzgZ3BR9SVsNed+LmwKrmnqN3Kdl5t7eZ5TS1Yw==", + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.15.0.tgz", + "integrity": "sha512-gjyB/rHAopL/XxfmYThQbXbzRMGhZzGw6KpcMbfe8Q3nNQKStpxnUKeXb0KiN/fFDR42Z43szs6rY7eHk0zdGQ==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/types": "7.13.1", - "@typescript-eslint/visitor-keys": "7.13.1", + "@typescript-eslint/types": "7.15.0", + "@typescript-eslint/visitor-keys": "7.15.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -2415,6 +2544,7 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } @@ -2424,6 +2554,7 @@ "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dev": true, + "license": "MIT", "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", @@ -2440,10 +2571,11 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", - "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -2459,6 +2591,7 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", "dev": true, + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -2471,20 +2604,22 @@ "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/@typescript-eslint/utils": { - "version": "7.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.13.1.tgz", - "integrity": "sha512-h5MzFBD5a/Gh/fvNdp9pTfqJAbuQC4sCN2WzuXme71lqFJsZtLbjxfSk4r3p02WIArOF9N94pdsLiGutpDbrXQ==", + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.15.0.tgz", + "integrity": "sha512-hfDMDqaqOqsUVGiEPSMLR/AjTSCsmJwjpKkYQRo1FNbmW4tBwBspYDwO9eh7sKSTwMQgBw9/T4DHudPaqshRWA==", "dev": true, + "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "7.13.1", - "@typescript-eslint/types": "7.13.1", - "@typescript-eslint/typescript-estree": "7.13.1" + "@typescript-eslint/scope-manager": "7.15.0", + "@typescript-eslint/types": "7.15.0", + "@typescript-eslint/typescript-estree": "7.15.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -2498,12 +2633,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "7.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.13.1.tgz", - "integrity": "sha512-k/Bfne7lrP7hcb7m9zSsgcBmo+8eicqqfNAJ7uUY+jkTFpKeH2FSkWpFRtimBxgkyvqfu9jTPRbYOvud6isdXA==", + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.15.0.tgz", + "integrity": "sha512-Hqgy/ETgpt2L5xueA/zHHIl4fJI2O4XUE9l4+OIfbJIRSnTJb/QscncdqqZzofQegIJugRIF57OJea1khw2SDw==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "7.13.1", + "@typescript-eslint/types": "7.15.0", "eslint-visitor-keys": "^3.4.3" }, "engines": { @@ -2740,9 +2876,9 @@ } }, "node_modules/address": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/address/-/address-2.0.2.tgz", - "integrity": "sha512-u6nFssvaX9RHQmjMSqgT7b7QJbf/5/U8+ntbTL8vgABfIiEmm02ZSM5MwljKjCrIrm7iIbgYEya2YW6AaRccVA==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/address/-/address-2.0.3.tgz", + "integrity": "sha512-XNAb/a6TCqou+TufU8/u11HCu9x1gYvOoxLwtlXgIqmkrYQADVv6ljyW2zwiPhHz9R1gItAWpuDrdJMmrOBFEA==", "dev": true, "engines": { "node": ">= 16.0.0" @@ -3188,7 +3324,8 @@ "version": "2.2.3", "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==", - "dev": true + "dev": true, + "peer": true }, "node_modules/binary-extensions": { "version": "2.3.0", @@ -3531,9 +3668,9 @@ } }, "node_modules/browserslist": { - "version": "4.23.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", - "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", + "version": "4.23.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.1.tgz", + "integrity": "sha512-TUfofFo/KsK/bWZ9TWQ5O26tsWW4Uhmt8IYklbnUa70udB6P2wA7w7o4PY4muaEPBQaAX+CEnmmIA41NVHtPVw==", "dev": true, "funding": [ { @@ -3550,10 +3687,10 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001587", - "electron-to-chromium": "^1.4.668", + "caniuse-lite": "^1.0.30001629", + "electron-to-chromium": "^1.4.796", "node-releases": "^2.0.14", - "update-browserslist-db": "^1.0.13" + "update-browserslist-db": "^1.0.16" }, "bin": { "browserslist": "cli.js" @@ -3673,9 +3810,9 @@ "dev": true }, "node_modules/caniuse-lite": { - "version": "1.0.30001629", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001629.tgz", - "integrity": "sha512-c3dl911slnQhmxUIT4HhYzT7wnBK/XYpGnYLOj4nJBaRiw52Ibe7YxlDaAeRECvA786zCuExhxIUJ2K7nHMrBw==", + "version": "1.0.30001636", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001636.tgz", + "integrity": "sha512-bMg2vmr8XBsbL6Lr0UHXy/21m84FTxDLWn2FSqMd5PrlbMxwJlQnC2YWYxVgp66PZE+BBNF2jYQUBKCo1FDeZg==", "dev": true, "funding": [ { @@ -3732,9 +3869,10 @@ } }, "node_modules/cheap-ruler": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/cheap-ruler/-/cheap-ruler-3.0.2.tgz", - "integrity": "sha512-02T332h1/HTN6cDSufLP8x4JzDs2+VC+8qZ/N0kWIVPyc2xUkWwWh3B2fJxR7raXkL4Mq7k554mfuM9ofv/vGg==" + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cheap-ruler/-/cheap-ruler-4.0.0.tgz", + "integrity": "sha512-0BJa8f4t141BYKQyn9NSQt1PguFQXMXwZiA5shfoaBYHAb2fFk2RAX+tiWMoQU+Agtzt3mdt0JtuyshAXqZ+Vw==", + "license": "ISC" }, "node_modules/check-error": { "version": "1.0.3", @@ -4377,13 +4515,13 @@ } }, "node_modules/cssnano": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-7.0.2.tgz", - "integrity": "sha512-LXm/Xx6TNLzfHM2lBaIQHfvtdW5QfdbyLzfJAWZrclCAb47yVa0/yJG69+amcw3Lq0YZ+kyU40rbsMPLcMt9aw==", + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-7.0.3.tgz", + "integrity": "sha512-lsekJctOTqdCn4cNrtrSwsuMR/fHC+oiVMHkp/OugBWtwjH8XJag1/OtGaYJGtz0un1fQcRy4ryfYTQsfh+KSQ==", "dev": true, "dependencies": { - "cssnano-preset-default": "^7.0.2", - "lilconfig": "^3.1.1" + "cssnano-preset-default": "^7.0.3", + "lilconfig": "^3.1.2" }, "engines": { "node": "^18.12.0 || ^20.9.0 || >=22.0" @@ -4397,38 +4535,38 @@ } }, "node_modules/cssnano-preset-default": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-7.0.2.tgz", - "integrity": "sha512-z95kGKZx8VWHfERj7LFzuiTxylbvEp07ZEYaFu+t6bFyNOXLd/+3oPyNaY7ISwcrfHFCkt8OfRo4IZxVRJZ7dg==", + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-7.0.3.tgz", + "integrity": "sha512-dQ3Ba1p/oewICp/szF1XjFFgql8OlOBrI2YNBUUwhHQnJNoMOcQTa+Bi7jSJN8r/eM1egW0Ud1se/S7qlduWKA==", "dev": true, "dependencies": { - "browserslist": "^4.23.0", + "browserslist": "^4.23.1", "css-declaration-sorter": "^7.2.0", "cssnano-utils": "^5.0.0", "postcss-calc": "^10.0.0", - "postcss-colormin": "^7.0.0", - "postcss-convert-values": "^7.0.0", - "postcss-discard-comments": "^7.0.0", + "postcss-colormin": "^7.0.1", + "postcss-convert-values": "^7.0.1", + "postcss-discard-comments": "^7.0.1", "postcss-discard-duplicates": "^7.0.0", "postcss-discard-empty": "^7.0.0", "postcss-discard-overridden": "^7.0.0", - "postcss-merge-longhand": "^7.0.1", - "postcss-merge-rules": "^7.0.1", + "postcss-merge-longhand": "^7.0.2", + "postcss-merge-rules": "^7.0.2", "postcss-minify-font-values": "^7.0.0", "postcss-minify-gradients": "^7.0.0", - "postcss-minify-params": "^7.0.0", - "postcss-minify-selectors": "^7.0.1", + "postcss-minify-params": "^7.0.1", + "postcss-minify-selectors": "^7.0.2", "postcss-normalize-charset": "^7.0.0", "postcss-normalize-display-values": "^7.0.0", "postcss-normalize-positions": "^7.0.0", "postcss-normalize-repeat-style": "^7.0.0", "postcss-normalize-string": "^7.0.0", "postcss-normalize-timing-functions": "^7.0.0", - "postcss-normalize-unicode": "^7.0.0", + "postcss-normalize-unicode": "^7.0.1", "postcss-normalize-url": "^7.0.0", "postcss-normalize-whitespace": "^7.0.0", - "postcss-ordered-values": "^7.0.0", - "postcss-reduce-initial": "^7.0.0", + "postcss-ordered-values": "^7.0.1", + "postcss-reduce-initial": "^7.0.1", "postcss-reduce-transforms": "^7.0.0", "postcss-svgo": "^7.0.1", "postcss-unique-selectors": "^7.0.1" @@ -4701,7 +4839,8 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==", - "dev": true + "dev": true, + "peer": true }, "node_modules/deps-sort": { "version": "2.0.1", @@ -4926,9 +5065,10 @@ } }, "node_modules/earcut": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/earcut/-/earcut-2.2.4.tgz", - "integrity": "sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ==" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/earcut/-/earcut-3.0.0.tgz", + "integrity": "sha512-41Fs7Q/PLq1SDbqjsgcY7GA42T0jvaCNGXgGtsNdvg+Yv8eIu06bxv4/PoREkZ9nMDNwnUSG9OFB9+yv8eKhDg==", + "license": "ISC" }, "node_modules/eastasianwidth": { "version": "0.2.0", @@ -4958,9 +5098,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.791", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.791.tgz", - "integrity": "sha512-6FlqP0NSWvxFf1v+gHu+LCn5wjr1pmkj5nPr7BsxPnj41EDR4EWhK/KmQN0ytHUqgTR1lkpHRYxvHBLZFQtkKw==", + "version": "1.4.811", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.811.tgz", + "integrity": "sha512-CDyzcJ5XW78SHzsIOdn27z8J4ist8eaFLhdto2hSMSJQgsiwvbv2fbizcKUICryw1Wii1TI/FEkvzvJsR3awrA==", "dev": true }, "node_modules/elliptic": { @@ -5238,9 +5378,9 @@ "dev": true }, "node_modules/es-module-lexer": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.2.tgz", - "integrity": "sha512-l60ETUTmLqbVbVHv1J4/qj+M8nq7AwMzEcg3kmJDt9dCNrTk+yHcYFf/Kw75pMDwd9mPcIGCG5LcS20SxYRzFA==", + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.4.tgz", + "integrity": "sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==", "dev": true }, "node_modules/es-object-atoms": { @@ -5296,41 +5436,42 @@ } }, "node_modules/esbuild": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", - "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.0.tgz", + "integrity": "sha512-1lvV17H2bMYda/WaFb2jLPeHU3zml2k4/yagNMG8Q/YtfMjCwEUZa2eXXMgZTVSL5q1n4H7sQ0X6CdJDqqeCFA==", "dev": true, "hasInstallScript": true, "bin": { "esbuild": "bin/esbuild" }, "engines": { - "node": ">=12" + "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.21.5", - "@esbuild/android-arm": "0.21.5", - "@esbuild/android-arm64": "0.21.5", - "@esbuild/android-x64": "0.21.5", - "@esbuild/darwin-arm64": "0.21.5", - "@esbuild/darwin-x64": "0.21.5", - "@esbuild/freebsd-arm64": "0.21.5", - "@esbuild/freebsd-x64": "0.21.5", - "@esbuild/linux-arm": "0.21.5", - "@esbuild/linux-arm64": "0.21.5", - "@esbuild/linux-ia32": "0.21.5", - "@esbuild/linux-loong64": "0.21.5", - "@esbuild/linux-mips64el": "0.21.5", - "@esbuild/linux-ppc64": "0.21.5", - "@esbuild/linux-riscv64": "0.21.5", - "@esbuild/linux-s390x": "0.21.5", - "@esbuild/linux-x64": "0.21.5", - "@esbuild/netbsd-x64": "0.21.5", - "@esbuild/openbsd-x64": "0.21.5", - "@esbuild/sunos-x64": "0.21.5", - "@esbuild/win32-arm64": "0.21.5", - "@esbuild/win32-ia32": "0.21.5", - "@esbuild/win32-x64": "0.21.5" + "@esbuild/aix-ppc64": "0.23.0", + "@esbuild/android-arm": "0.23.0", + "@esbuild/android-arm64": "0.23.0", + "@esbuild/android-x64": "0.23.0", + "@esbuild/darwin-arm64": "0.23.0", + "@esbuild/darwin-x64": "0.23.0", + "@esbuild/freebsd-arm64": "0.23.0", + "@esbuild/freebsd-x64": "0.23.0", + "@esbuild/linux-arm": "0.23.0", + "@esbuild/linux-arm64": "0.23.0", + "@esbuild/linux-ia32": "0.23.0", + "@esbuild/linux-loong64": "0.23.0", + "@esbuild/linux-mips64el": "0.23.0", + "@esbuild/linux-ppc64": "0.23.0", + "@esbuild/linux-riscv64": "0.23.0", + "@esbuild/linux-s390x": "0.23.0", + "@esbuild/linux-x64": "0.23.0", + "@esbuild/netbsd-x64": "0.23.0", + "@esbuild/openbsd-arm64": "0.23.0", + "@esbuild/openbsd-x64": "0.23.0", + "@esbuild/sunos-x64": "0.23.0", + "@esbuild/win32-arm64": "0.23.0", + "@esbuild/win32-ia32": "0.23.0", + "@esbuild/win32-x64": "0.23.0" } }, "node_modules/escalade": { @@ -5557,10 +5698,11 @@ } }, "node_modules/eslint-plugin-jsdoc": { - "version": "48.2.12", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-48.2.12.tgz", - "integrity": "sha512-sO9sKkJx5ovWoRk9hV0YiNzXQ4Z6j27CqE/po2E3wddZVuy9wvKPSTiIhpxMTrP/qURvKayJIDB2+o9kyCW1Fw==", + "version": "48.5.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-48.5.0.tgz", + "integrity": "sha512-ukXPNpGby3KjCveCizIS8t1EbuJEHYEu/tBg8GCbn/YbHcXwphyvYCdvRZ/oMRfTscGSSzfsWoZ+ZkAP0/6YMQ==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "@es-joy/jsdoccomment": "~0.43.1", "are-docs-informative": "^0.0.2", @@ -5568,8 +5710,10 @@ "debug": "^4.3.4", "escape-string-regexp": "^4.0.0", "esquery": "^1.5.0", + "parse-imports": "^2.1.0", "semver": "^7.6.2", - "spdx-expression-parse": "^4.0.0" + "spdx-expression-parse": "^4.0.0", + "synckit": "^0.9.0" }, "engines": { "node": ">=18" @@ -6581,15 +6725,16 @@ "integrity": "sha512-wcCp8vu8FT22BnvKVPjXa/ICBWRq/zjFfdofZy1WSpQZpphblv12/bOQLBC1rMM7SGOFS9ltVmKOHil5+Ml7gA==" }, "node_modules/glob": { - "version": "10.4.1", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.1.tgz", - "integrity": "sha512-2jelhlq3E4ho74ZyVLN03oKdAZVUa6UDZzFLVH1H7dnoax+y9qyaq8zBkfDIggjniU19z0wU18y16jMB2eyVIw==", + "version": "10.4.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.2.tgz", + "integrity": "sha512-GwMlUF6PkPo3Gk21UxkCohOv0PLcIXVtKyLlpEI28R/cO/4eNOdmLk3CMW1wROV/WR/EsZOWAfBbBOqYvs88/w==", "dev": true, "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { @@ -8166,9 +8311,9 @@ } }, "node_modules/lilconfig": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.1.tgz", - "integrity": "sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz", + "integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==", "dev": true, "engines": { "node": ">=14" @@ -9428,6 +9573,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/package-json-from-dist": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz", + "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==", + "dev": true + }, "node_modules/pako": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", @@ -9472,6 +9623,19 @@ "node": ">= 0.10" } }, + "node_modules/parse-imports": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/parse-imports/-/parse-imports-2.1.0.tgz", + "integrity": "sha512-JQWgmK2o4w8leUkZeZPatWdAny6vXGU/3siIUvMF6J2rDCud9aTt8h/px9oZJ6U3EcfhngBJ635uPFI0q0VAeA==", + "dev": true, + "dependencies": { + "es-module-lexer": "^1.5.3", + "slashes": "^3.0.12" + }, + "engines": { + "node": ">= 18" + } + }, "node_modules/parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", @@ -9662,12 +9826,13 @@ } }, "node_modules/pixelmatch": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/pixelmatch/-/pixelmatch-5.3.0.tgz", - "integrity": "sha512-o8mkY4E/+LNUf6LzX96ht6k6CEDi65k9G2rjMtBe9Oo+VPKSvl+0GKHuH/AlG+GA5LPG/i5hrekkxUc3s2HU+Q==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/pixelmatch/-/pixelmatch-6.0.0.tgz", + "integrity": "sha512-FYpL4XiIWakTnIqLqvt3uN4L9B3TsuHIvhLILzTiJZMJUsGvmKNeL4H3b6I99LRyerK9W4IuOXw+N28AtRgK2g==", "dev": true, + "license": "ISC", "dependencies": { - "pngjs": "^6.0.0" + "pngjs": "^7.0.0" }, "bin": { "pixelmatch": "bin/pixelmatch" @@ -9685,33 +9850,33 @@ } }, "node_modules/playwright": { - "version": "1.44.1", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.44.1.tgz", - "integrity": "sha512-qr/0UJ5CFAtloI3avF95Y0L1xQo6r3LQArLIg/z/PoGJ6xa+EwzrwO5lpNr/09STxdHuUoP2mvuELJS+hLdtgg==", + "version": "1.45.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.45.0.tgz", + "integrity": "sha512-4z3ac3plDfYzGB6r0Q3LF8POPR20Z8D0aXcxbJvmfMgSSq1hkcgvFRXJk9rUq5H/MJ0Ktal869hhOdI/zUTeLA==", "dev": true, "dependencies": { - "playwright-core": "1.44.1" + "playwright-core": "1.45.0" }, "bin": { "playwright": "cli.js" }, "engines": { - "node": ">=16" + "node": ">=18" }, "optionalDependencies": { "fsevents": "2.3.2" } }, "node_modules/playwright-core": { - "version": "1.44.1", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.44.1.tgz", - "integrity": "sha512-wh0JWtYTrhv1+OSsLPgFzGzt67Y7BE/ZS3jEqgGBlp2ppp1ZDj8c+9IARNW4dwf1poq5MgHreEM2KV/GuR4cFA==", + "version": "1.45.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.45.0.tgz", + "integrity": "sha512-lZmHlFQ0VYSpAs43dRq1/nJ9G/6SiTI7VPqidld9TDefL9tX87bTKExWZZUF5PeRyqtXqd8fQi2qmfIedkwsNQ==", "dev": true, "bin": { "playwright-core": "cli.js" }, "engines": { - "node": ">=16" + "node": ">=18" } }, "node_modules/playwright/node_modules/fsevents": { @@ -9729,12 +9894,13 @@ } }, "node_modules/pngjs": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-6.0.0.tgz", - "integrity": "sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-7.0.0.tgz", + "integrity": "sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==", "dev": true, + "license": "MIT", "engines": { - "node": ">=12.13.0" + "node": ">=14.19.0" } }, "node_modules/possible-typed-array-names": { @@ -9747,9 +9913,9 @@ } }, "node_modules/postcss": { - "version": "8.4.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", - "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", + "version": "8.4.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.39.tgz", + "integrity": "sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==", "dev": true, "funding": [ { @@ -9767,7 +9933,7 @@ ], "dependencies": { "nanoid": "^3.3.7", - "picocolors": "^1.0.0", + "picocolors": "^1.0.1", "source-map-js": "^1.2.0" }, "engines": { @@ -9832,12 +9998,12 @@ } }, "node_modules/postcss-colormin": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-7.0.0.tgz", - "integrity": "sha512-5CN6fqtsEtEtwf3mFV3B4UaZnlYljPpzmGeDB4yCK067PnAtfLe9uX2aFZaEwxHE7HopG5rUkW8gyHrNAesHEg==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-7.0.1.tgz", + "integrity": "sha512-uszdT0dULt3FQs47G5UHCduYK+FnkLYlpu1HpWu061eGsKZ7setoG7kA+WC9NQLsOJf69D5TxGHgnAdRgylnFQ==", "dev": true, "dependencies": { - "browserslist": "^4.23.0", + "browserslist": "^4.23.1", "caniuse-api": "^3.0.0", "colord": "^2.9.3", "postcss-value-parser": "^4.2.0" @@ -9850,12 +10016,12 @@ } }, "node_modules/postcss-convert-values": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-7.0.0.tgz", - "integrity": "sha512-bMuzDgXBbFbByPgj+/r6va8zNuIDUaIIbvAFgdO1t3zdgJZ77BZvu6dfWyd6gHEJnYzmeVr9ayUsAQL3/qLJ0w==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-7.0.1.tgz", + "integrity": "sha512-9x2ofb+hYPwHWMlWAzyWys2yMDZYGfkX9LodbaVTmLdlupmtH2AGvj8Up95wzzNPRDEzPIxQIkUaPJew3bT6xA==", "dev": true, "dependencies": { - "browserslist": "^4.23.0", + "browserslist": "^4.23.1", "postcss-value-parser": "^4.2.0" }, "engines": { @@ -9866,10 +10032,13 @@ } }, "node_modules/postcss-discard-comments": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-7.0.0.tgz", - "integrity": "sha512-xpSdzRqYmy4YIVmjfGyYXKaI1SRnK6CTr+4Zmvyof8ANwvgfZgGdVtmgAvzh59gJm808mJCWQC9tFN0KF5dEXA==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-7.0.1.tgz", + "integrity": "sha512-GVrQxUOhmle1W6jX2SvNLt4kmN+JYhV7mzI6BMnkAWR9DtVvg8e67rrV0NfdWhn7x1zxvzdWkMBPdBDCls+uwQ==", "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.1.0" + }, "engines": { "node": "^18.12.0 || ^20.9.0 || >=22.0" }, @@ -9983,13 +10152,13 @@ } }, "node_modules/postcss-merge-longhand": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-7.0.1.tgz", - "integrity": "sha512-qZlD26hnqSTMxSSOMS8+QCeRWtqOdMKeQHvHcBhjL3mJxKUs47cvO1Y1x3iTdYIk3ioMcRHTiy229TT0mEMH/A==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-7.0.2.tgz", + "integrity": "sha512-06vrW6ZWi9qeP7KMS9fsa9QW56+tIMW55KYqF7X3Ccn+NI2pIgPV6gFfvXTMQ05H90Y5DvnCDPZ2IuHa30PMUg==", "dev": true, "dependencies": { "postcss-value-parser": "^4.2.0", - "stylehacks": "^7.0.1" + "stylehacks": "^7.0.2" }, "engines": { "node": "^18.12.0 || ^20.9.0 || >=22.0" @@ -9999,12 +10168,12 @@ } }, "node_modules/postcss-merge-rules": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-7.0.1.tgz", - "integrity": "sha512-bb8McYQbo2etgs0uVt6AfngajACK3FHSVP3sGLhprrjbtHJWgG03JZ4KKBlJ8/5Fb8/Rr+mMKaybMYeoYrAg0A==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-7.0.2.tgz", + "integrity": "sha512-VAR47UNvRsdrTHLe7TV1CeEtF9SJYR5ukIB9U4GZyZOptgtsS20xSxy+k5wMrI3udST6O1XuIn7cjQkg7sDAAw==", "dev": true, "dependencies": { - "browserslist": "^4.23.0", + "browserslist": "^4.23.1", "caniuse-api": "^3.0.0", "cssnano-utils": "^5.0.0", "postcss-selector-parser": "^6.1.0" @@ -10049,12 +10218,12 @@ } }, "node_modules/postcss-minify-params": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-7.0.0.tgz", - "integrity": "sha512-XOJAuX8Q/9GT1sGxlUvaFEe2H9n50bniLZblXXsAT/BwSfFYvzSZeFG7uupwc0KbKpTnflnQ7aMwGzX6JUWliQ==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-7.0.1.tgz", + "integrity": "sha512-e+Xt8xErSRPgSRFxHeBCSxMiO8B8xng7lh8E0A5ep1VfwYhY8FXhu4Q3APMjgx9YDDbSp53IBGENrzygbUvgUQ==", "dev": true, "dependencies": { - "browserslist": "^4.23.0", + "browserslist": "^4.23.1", "cssnano-utils": "^5.0.0", "postcss-value-parser": "^4.2.0" }, @@ -10066,11 +10235,12 @@ } }, "node_modules/postcss-minify-selectors": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-7.0.1.tgz", - "integrity": "sha512-YfIbGtcgMFquPxV2L/ASs36ZS4DsgfcDX9tQ8cTEIvBTv+0GXFKtcvvpi9tCKto/+DWGWYKMCESFG3Pnan0Feg==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-7.0.2.tgz", + "integrity": "sha512-dCzm04wqW1uqLmDZ41XYNBJfjgps3ZugDpogAmJXoCb5oCiTzIX4oPXXKxDpTvWOnKxQKR4EbV4ZawJBLcdXXA==", "dev": true, "dependencies": { + "cssesc": "^3.0.0", "postcss-selector-parser": "^6.1.0" }, "engines": { @@ -10168,12 +10338,12 @@ } }, "node_modules/postcss-normalize-unicode": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-7.0.0.tgz", - "integrity": "sha512-OnKV52/VFFDAim4n0pdI+JAhsolLBdnCKxE6VV5lW5Q/JeVGFN8UM8ur6/A3EAMLsT1ZRm3fDHh/rBoBQpqi2w==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-7.0.1.tgz", + "integrity": "sha512-PTPGdY9xAkTw+8ZZ71DUePb7M/Vtgkbbq+EoI33EuyQEzbKemEQMhe5QSr0VP5UfZlreANDPxSfcdSprENcbsg==", "dev": true, "dependencies": { - "browserslist": "^4.23.0", + "browserslist": "^4.23.1", "postcss-value-parser": "^4.2.0" }, "engines": { @@ -10214,9 +10384,9 @@ } }, "node_modules/postcss-ordered-values": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-7.0.0.tgz", - "integrity": "sha512-KROvC63A8UQW1eYDljQe1dtwc1E/M+mMwDT6z7khV/weHYLWTghaLRLunU7x1xw85lWFwVZOAGakxekYvKV+0w==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-7.0.1.tgz", + "integrity": "sha512-irWScWRL6nRzYmBOXReIKch75RRhNS86UPUAxXdmW/l0FcAsg0lvAXQCby/1lymxn/o0gVa6Rv/0f03eJOwHxw==", "dev": true, "dependencies": { "cssnano-utils": "^5.0.0", @@ -10230,12 +10400,12 @@ } }, "node_modules/postcss-reduce-initial": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-7.0.0.tgz", - "integrity": "sha512-iqGgmBxY9LrblZ0BKLjmrA1mC/cf9A/wYCCqSmD6tMi+xAyVl0+DfixZIHSVDMbCPRPjNmVF0DFGth/IDGelFQ==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-7.0.1.tgz", + "integrity": "sha512-0JDUSV4bGB5FGM5g8MkS+rvqKukJZ7OTHw/lcKn7xPNqeaqJyQbUO8/dJpvyTpaVwPsd3Uc33+CfNzdVowp2WA==", "dev": true, "dependencies": { - "browserslist": "^4.23.0", + "browserslist": "^4.23.1", "caniuse-api": "^3.0.0" }, "engines": { @@ -11340,17 +11510,11 @@ "node": ">= 10" } }, - "node_modules/slash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", - "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "node_modules/slashes": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/slashes/-/slashes-3.0.12.tgz", + "integrity": "sha512-Q9VME8WyGkc7pJf6QEkj3wE+2CnvZMI+XJhwdTPR8Z/kWQRXi7boAWLDibRPyHRTUTPx5FaU7MsyrjI3yLB4HA==", + "dev": true }, "node_modules/slice-ansi": { "version": "4.0.0", @@ -11955,12 +12119,12 @@ "dev": true }, "node_modules/stylehacks": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-7.0.1.tgz", - "integrity": "sha512-PnrT4HzajnxbjfChpeBKLSpSykilnGBlD+pIffCoT5KbLur9fcL8uKRQJJap85byR2wCYZl/4Otk5eq76qeZxQ==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-7.0.2.tgz", + "integrity": "sha512-HdkWZS9b4gbgYTdMg4gJLmm7biAUug1qTqXjS+u8X+/pUd+9Px1E+520GnOW3rST9MNsVOVpsJG+mPHNosxjOQ==", "dev": true, "dependencies": { - "browserslist": "^4.23.0", + "browserslist": "^4.23.1", "postcss-selector-parser": "^6.1.0" }, "engines": { @@ -12034,24 +12198,44 @@ } }, "node_modules/stylelint-config-recommended": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-14.0.0.tgz", - "integrity": "sha512-jSkx290CglS8StmrLp2TxAppIajzIBZKYm3IxT89Kg6fGlxbPiTiyH9PS5YUuVAFwaJLl1ikiXX0QWjI0jmgZQ==", + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-14.0.1.tgz", + "integrity": "sha512-bLvc1WOz/14aPImu/cufKAZYfXs/A/owZfSMZ4N+16WGXLoX5lOir53M6odBxvhgmgdxCVnNySJmZKx73T93cg==", "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/stylelint" + }, + { + "type": "github", + "url": "https://github.com/sponsors/stylelint" + } + ], "engines": { "node": ">=18.12.0" }, "peerDependencies": { - "stylelint": "^16.0.0" + "stylelint": "^16.1.0" } }, "node_modules/stylelint-config-standard": { - "version": "36.0.0", - "resolved": "https://registry.npmjs.org/stylelint-config-standard/-/stylelint-config-standard-36.0.0.tgz", - "integrity": "sha512-3Kjyq4d62bYFp/Aq8PMKDwlgUyPU4nacXsjDLWJdNPRUgpuxALu1KnlAHIj36cdtxViVhXexZij65yM0uNIHug==", + "version": "36.0.1", + "resolved": "https://registry.npmjs.org/stylelint-config-standard/-/stylelint-config-standard-36.0.1.tgz", + "integrity": "sha512-8aX8mTzJ6cuO8mmD5yon61CWuIM4UD8Q5aBcWKGSf6kg+EC3uhB+iOywpTK4ca6ZL7B49en8yanOFtUW0qNzyw==", "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/stylelint" + }, + { + "type": "github", + "url": "https://github.com/sponsors/stylelint" + } + ], "dependencies": { - "stylelint-config-recommended": "^14.0.0" + "stylelint-config-recommended": "^14.0.1" }, "engines": { "node": ">=18.12.0" @@ -12273,6 +12457,22 @@ "url": "https://opencollective.com/svgo" } }, + "node_modules/synckit": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.0.tgz", + "integrity": "sha512-7RnqIMq572L8PeEzKeBINYEJDDxpcH8JEgLwUqBd3TkofhFRbkq4QLR0u+36avGAhCRbk2nnmjcW9SE531hPDg==", + "dev": true, + "dependencies": { + "@pkgr/core": "^0.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, "node_modules/syntax-error": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/syntax-error/-/syntax-error-1.4.0.tgz", @@ -12492,9 +12692,9 @@ "dev": true }, "node_modules/testem": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/testem/-/testem-3.14.0.tgz", - "integrity": "sha512-hpybTZhio6DXUM7s0HsE8EOnN8zuA6LdNcz3EsTpQSnD56Cj6gSuFQx82wDKZQ6OmM1kvIBebxP+rEoOYBgCOA==", + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/testem/-/testem-3.15.0.tgz", + "integrity": "sha512-vI1oQsjJW4QdVaH6ZmfNErzH7nzs0KzHJluocnfvbz1XRYGJKkIMGKWfsbD8MGGJOg+uzXcEek0/2W7BmGR4ug==", "dev": true, "dependencies": { "@xmldom/xmldom": "^0.8.0", @@ -12506,7 +12706,7 @@ "consolidate": "^0.16.0", "execa": "^1.0.0", "express": "^4.10.7", - "fireworm": "^0.7.0", + "fireworm": "^0.7.2", "glob": "^7.0.4", "http-proxy": "^1.13.1", "js-yaml": "^3.2.5", @@ -12715,13 +12915,19 @@ "json5": "lib/cli.js" } }, + "node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", + "dev": true + }, "node_modules/tsx": { - "version": "4.15.6", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.15.6.tgz", - "integrity": "sha512-is0VQQlfNZRHEuSSTKA6m4xw74IU4AizmuB6lAYLRt9XtuyeQnyJYexhNZOPCB59SqC4JzmSzPnHGBXxf3k0hA==", + "version": "4.16.0", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.16.0.tgz", + "integrity": "sha512-MPgN+CuY+4iKxGoJNPv+1pyo5YWZAQ5XfsyobUG+zoKG7IkvCPLZDEyoIb8yLS2FcWci1nlxAqmvPlFWD5AFiQ==", "dev": true, "dependencies": { - "esbuild": "~0.21.4", + "esbuild": "~0.21.5", "get-tsconfig": "^4.7.5" }, "bin": { @@ -12734,84 +12940,490 @@ "fsevents": "~2.3.3" } }, - "node_modules/tty-browserify": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.1.tgz", - "integrity": "sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==", - "dev": true - }, - "node_modules/tweakpane": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/tweakpane/-/tweakpane-4.0.3.tgz", - "integrity": "sha512-BlcWOAe8oe4c+k9pmLBARGdWB6MVZMszayekkixQXTgkxTaYoTUpHpwVEp+3HkoamZkomodpbBf0CkguIHTgLg==", - "funding": { - "url": "https://github.com/sponsors/cocopon" + "node_modules/tsx/node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" } }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "node_modules/tsx/node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1" - }, + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">= 0.8.0" + "node": ">=12" } }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "node_modules/tsx/node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], "dev": true, + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=4" + "node": ">=12" } }, - "node_modules/type-fest": { - "version": "4.13.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.13.1.tgz", - "integrity": "sha512-ASMgM+Vf2cLwDMt1KXSkMUDSYCxtckDJs8zsaVF/mYteIsiARKCVtyXtcK38mIKbLTctZP8v6GMqdNaeI3fo7g==", + "node_modules/tsx/node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], "dev": true, + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=12" } }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "node_modules/tsx/node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">= 0.6" + "node": ">=12" } }, - "node_modules/typed-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", - "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", + "node_modules/tsx/node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "is-typed-array": "^1.1.13" - }, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">= 0.4" + "node": ">=12" } }, - "node_modules/typed-array-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", - "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", + "node_modules/tsx/node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/tty-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.1.tgz", + "integrity": "sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==", + "dev": true + }, + "node_modules/tweakpane": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/tweakpane/-/tweakpane-4.0.4.tgz", + "integrity": "sha512-RkWD54zDlEbnN01wQPk0ANHGbdCvlJx/E8A1QxhTfCbX+ROWos1Ws2MnhOm39aUGMOh+36TjUwpDmLfmwTr1Fg==", + "funding": { + "url": "https://github.com/sponsors/cocopon" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "4.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.13.1.tgz", + "integrity": "sha512-ASMgM+Vf2cLwDMt1KXSkMUDSYCxtckDJs8zsaVF/mYteIsiARKCVtyXtcK38mIKbLTctZP8v6GMqdNaeI3fo7g==", + "dev": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", + "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", + "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", "dev": true, "dependencies": { "call-bind": "^1.0.7", @@ -12874,9 +13486,9 @@ "dev": true }, "node_modules/typescript": { - "version": "5.4.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", - "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz", + "integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -12887,14 +13499,15 @@ } }, "node_modules/typescript-eslint": { - "version": "7.13.1", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-7.13.1.tgz", - "integrity": "sha512-pvLEuRs8iS9s3Cnp/Wt//hpK8nKc8hVa3cLljHqzaJJQYP8oys8GUyIFqtlev+2lT/fqMPcyQko+HJ6iYK3nFA==", + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-7.15.0.tgz", + "integrity": "sha512-Ta40FhMXBCwHura4X4fncaCVkVcnJ9jnOq5+Lp4lN8F4DzHZtOwZdRvVBiNUGznUDHPwdGnrnwxmUOU2fFQqFA==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "7.13.1", - "@typescript-eslint/parser": "7.13.1", - "@typescript-eslint/utils": "7.13.1" + "@typescript-eslint/eslint-plugin": "7.15.0", + "@typescript-eslint/parser": "7.15.0", + "@typescript-eslint/utils": "7.15.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -13004,7 +13617,8 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.1.tgz", "integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==", - "dev": true + "dev": true, + "peer": true }, "node_modules/universalify": { "version": "2.0.1", @@ -13251,9 +13865,9 @@ } }, "node_modules/vite-plugin-arraybuffer": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/vite-plugin-arraybuffer/-/vite-plugin-arraybuffer-0.0.7.tgz", - "integrity": "sha512-c4Egxj7NUGco2Ggw9KUBToOxuc7Ws7mWm0hz/QnaT5Ph8ycC7ypMBOD31NuhPSx+wdUvgIbS1XpMvJLSdHakPA==", + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/vite-plugin-arraybuffer/-/vite-plugin-arraybuffer-0.0.8.tgz", + "integrity": "sha512-F+InDQuxd93YDVRdXTjBr3lydgjyVHSpgRZS4izK/i85Anl5kmbvf2NwJ//XHAccHh1TScfX70MFJfBx/rf3cg==", "dev": true }, "node_modules/vite/node_modules/@esbuild/aix-ppc64": { @@ -14191,13 +14805,13 @@ }, "src/style-spec": { "name": "@mapbox/mapbox-gl-style-spec", - "version": "14.5.0-beta.1", + "version": "14.5.0", "license": "SEE LICENSE IN LICENSE.txt", "dependencies": { "@mapbox/jsonlint-lines-primitives": "~2.0.2", "@mapbox/point-geometry": "^0.1.0", "@mapbox/unitbezier": "^0.0.1", - "cheap-ruler": "^3.0.1", + "cheap-ruler": "^4.0.0", "csscolorparser": "~1.0.2", "json-stringify-pretty-compact": "^4.0.0", "minimist": "^1.2.6", diff --git a/package.json b/package.json index 83e573f1407..f9a8a0ab268 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "mapbox-gl", "description": "A WebGL interactive maps library", - "version": "3.5.0-beta.1", + "version": "3.5.0", "main": "dist/mapbox-gl.js", "style": "dist/mapbox-gl.css", "types": "dist/mapbox-gl.d.ts", @@ -22,14 +22,14 @@ "@mapbox/unitbezier": "^0.0.1", "@mapbox/vector-tile": "^1.3.1", "@mapbox/whoots-js": "^3.1.0", - "cheap-ruler": "^3.0.1", + "cheap-ruler": "^4.0.0", "csscolorparser": "~1.0.3", - "earcut": "^2.2.4", + "earcut": "^3.0.0", "fflate": "^0.8.1", "geojson-vt": "^4.0.2", "gl-matrix": "^3.4.3", "grid-index": "^1.1.0", - "kdbush": "^4.0.1", + "kdbush": "^4.0.2", "lodash.clonedeep": "^4.5.0", "murmurhash-js": "^1.0.0", "pbf": "^3.2.1", @@ -37,54 +37,53 @@ "quickselect": "^2.0.0", "rw": "^1.3.3", "serialize-to-js": "^3.1.2", - "supercluster": "^8.0.0", + "supercluster": "^8.0.1", "tiny-lru": "^11.2.6", "tinyqueue": "^2.0.3", - "tweakpane": "^4.0.3", + "tweakpane": "^4.0.4", "vt-pbf": "^3.1.3" }, "devDependencies": { "@mapbox/mvt-fixtures": "^3.10.0", - "@octokit/rest": "^20.1.1", - "@rollup/plugin-alias": "^5.1.0", + "@octokit/rest": "^21.0.0", "@rollup/plugin-commonjs": "^26.0.1", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-replace": "^5.0.7", "@rollup/plugin-strip": "^3.0.4", "@rollup/plugin-terser": "^0.4.4", - "@tweakpane/core": "^2.0.3", + "@tweakpane/core": "^2.0.4", "@types/geojson": "^7946.0.14", "@types/geojson-vt": "^3.2.5", "@types/jest": "^29.5.12", "@types/mapbox__point-geometry": "^0.1.4", "@types/mapbox__vector-tile": "^1.3.4", - "@types/node": "^20.14.3", + "@types/node": "^20.14.9", "@types/offscreencanvas": "^2019.7.3", "@types/pbf": "^3.0.5", - "@typescript-eslint/eslint-plugin": "^7.13.1", - "@typescript-eslint/parser": "^7.13.1", + "@typescript-eslint/eslint-plugin": "^7.15.0", + "@typescript-eslint/parser": "^7.15.0", "@vitest/browser": "^1.6.0", "@vitest/ui": "^1.6.0", - "address": "^2.0.2", + "address": "^2.0.3", "browserify": "^17.0.0", "chalk": "^5.0.1", "chokidar": "^3.6.0", "cross-env": "^7.0.3", - "cssnano": "^7.0.2", + "cssnano": "^7.0.3", "d3-queue": "^3.0.7", "diff": "^5.2.0", "dts-bundle-generator": "^9.5.1", "ejs": "^3.1.10", "envify": "^4.1.0", - "esbuild": "^0.21.5", + "esbuild": "^0.23.0", "eslint": "^8.57.0", "eslint-config-mourner": "^3.0.0", "eslint-import-resolver-typescript": "^3.6.1", "eslint-plugin-html": "^8.1.1", "eslint-plugin-import": "^2.29.1", - "eslint-plugin-jsdoc": "^48.2.12", - "glob": "^10.4.1", + "eslint-plugin-jsdoc": "^48.5.0", + "glob": "^10.4.2", "is-builtin-module": "^4.0.0", "jest-extended": "^4.0.2", "json-stringify-pretty-compact": "^4.0.0", @@ -96,9 +95,9 @@ "node-notifier": "^10.0.1", "npm-font-open-sans": "^1.1.0", "npm-run-all": "^4.1.5", - "pixelmatch": "^5.3.0", - "playwright": "^1.44.1", - "postcss": "^8.4.38", + "pixelmatch": "^6.0.0", + "playwright": "^1.45.0", + "postcss": "^8.4.39", "postcss-cli": "^11.0.0", "postcss-inline-svg": "^6.0.0", "pretty-bytes": "^6.0.0", @@ -110,15 +109,15 @@ "shuffle-seed": "^1.1.6", "st": "^3.0.0", "stylelint": "^16.6.1", - "stylelint-config-standard": "^36.0.0", + "stylelint-config-standard": "^36.0.1", "tape": "^5.8.1", "tape-filter": "^1.0.4", - "testem": "^3.14.0", - "tsx": "^4.15.6", - "typescript": "^5.4.5", - "typescript-eslint": "^7.13.1", + "testem": "^3.15.0", + "tsx": "^4.16.0", + "typescript": "^5.5.3", + "typescript-eslint": "^7.15.0", "utility-types": "^3.11.0", - "vite-plugin-arraybuffer": "^0.0.7", + "vite-plugin-arraybuffer": "^0.0.8", "vitest": "^1.6.0" }, "scripts": { diff --git a/src/data/array_types.ts b/src/data/array_types.ts index 008d56e12b5..c1111df343f 100644 --- a/src/data/array_types.ts +++ b/src/data/array_types.ts @@ -219,11 +219,11 @@ register(StructArrayLayout4f16, 'StructArrayLayout4f16'); /** * Implementation of the StructArray layout: - * [0]: Float32[2] + * [0]: Float32[3] * * @private */ -class StructArrayLayout2f8 extends StructArray implements IStructArrayLayout { +class StructArrayLayout3f12 extends StructArray implements IStructArrayLayout { uint8: Uint8Array; float32: Float32Array; @@ -232,22 +232,23 @@ class StructArrayLayout2f8 extends StructArray implements IStructArrayLayout { this.float32 = new Float32Array(this.arrayBuffer); } - emplaceBack(v0: number, v1: number): number { + emplaceBack(v0: number, v1: number, v2: number): number { const i = this.length; this.resize(i + 1); - return this.emplace(i, v0, v1); + return this.emplace(i, v0, v1, v2); } - emplace(i: number, v0: number, v1: number): number { - const o4 = i * 2; + emplace(i: number, v0: number, v1: number, v2: number): number { + const o4 = i * 3; this.float32[o4 + 0] = v0; this.float32[o4 + 1] = v1; + this.float32[o4 + 2] = v2; return i; } } -StructArrayLayout2f8.prototype.bytesPerElement = 8; -register(StructArrayLayout2f8, 'StructArrayLayout2f8'); +StructArrayLayout3f12.prototype.bytesPerElement = 12; +register(StructArrayLayout3f12, 'StructArrayLayout3f12'); /** * Implementation of the StructArray layout: @@ -957,6 +958,38 @@ class StructArrayLayout7f28 extends StructArray implements IStructArrayLayout { StructArrayLayout7f28.prototype.bytesPerElement = 28; register(StructArrayLayout7f28, 'StructArrayLayout7f28'); +/** + * Implementation of the StructArray layout: + * [0]: Float32[2] + * + * @private + */ +class StructArrayLayout2f8 extends StructArray implements IStructArrayLayout { + uint8: Uint8Array; + float32: Float32Array; + + _refreshViews() { + this.uint8 = new Uint8Array(this.arrayBuffer); + this.float32 = new Float32Array(this.arrayBuffer); + } + + emplaceBack(v0: number, v1: number): number { + const i = this.length; + this.resize(i + 1); + return this.emplace(i, v0, v1); + } + + emplace(i: number, v0: number, v1: number): number { + const o4 = i * 2; + this.float32[o4 + 0] = v0; + this.float32[o4 + 1] = v1; + return i; + } +} + +StructArrayLayout2f8.prototype.bytesPerElement = 8; +register(StructArrayLayout2f8, 'StructArrayLayout2f8'); + /** * Implementation of the StructArray layout: * [0]: Uint32[1] @@ -1026,39 +1059,6 @@ class StructArrayLayout1ui2 extends StructArray implements IStructArrayLayout { StructArrayLayout1ui2.prototype.bytesPerElement = 2; register(StructArrayLayout1ui2, 'StructArrayLayout1ui2'); -/** - * Implementation of the StructArray layout: - * [0]: Float32[3] - * - * @private - */ -class StructArrayLayout3f12 extends StructArray implements IStructArrayLayout { - uint8: Uint8Array; - float32: Float32Array; - - _refreshViews() { - this.uint8 = new Uint8Array(this.arrayBuffer); - this.float32 = new Float32Array(this.arrayBuffer); - } - - emplaceBack(v0: number, v1: number, v2: number): number { - const i = this.length; - this.resize(i + 1); - return this.emplace(i, v0, v1, v2); - } - - emplace(i: number, v0: number, v1: number, v2: number): number { - const o4 = i * 3; - this.float32[o4 + 0] = v0; - this.float32[o4 + 1] = v1; - this.float32[o4 + 2] = v2; - return i; - } -} - -StructArrayLayout3f12.prototype.bytesPerElement = 12; -register(StructArrayLayout3f12, 'StructArrayLayout3f12'); - /** * Implementation of the StructArray layout: * [0]: Float32[16] @@ -1430,7 +1430,7 @@ export { StructArrayLayout5i10, StructArrayLayout2i4ub1f12, StructArrayLayout4f16, - StructArrayLayout2f8, + StructArrayLayout3f12, StructArrayLayout4ui1f12, StructArrayLayout4ui8, StructArrayLayout6i12, @@ -1448,9 +1448,9 @@ export { StructArrayLayout2f9i15ui1ul6f1ub88, StructArrayLayout5f20, StructArrayLayout7f28, + StructArrayLayout2f8, StructArrayLayout1ul3ui12, StructArrayLayout1ui2, - StructArrayLayout3f12, StructArrayLayout16f64, StructArrayLayout4ui3f20, StructArrayLayout1i2, @@ -1465,7 +1465,7 @@ export { StructArrayLayout2i4 as HeatmapLayoutArray, StructArrayLayout2i4ub1f12 as LineLayoutArray, StructArrayLayout4f16 as LineExtLayoutArray, - StructArrayLayout2f8 as LinePatternLayoutArray, + StructArrayLayout3f12 as LinePatternLayoutArray, StructArrayLayout4ui1f12 as PatternLayoutArray, StructArrayLayout4ui8 as DashLayoutArray, StructArrayLayout6i12 as FillExtrusionExtArray, diff --git a/src/data/bucket/line_attributes_pattern.ts b/src/data/bucket/line_attributes_pattern.ts index 9cabe4f0559..dabd187dc96 100644 --- a/src/data/bucket/line_attributes_pattern.ts +++ b/src/data/bucket/line_attributes_pattern.ts @@ -3,7 +3,7 @@ import {createLayout} from '../../util/struct_array'; import type {StructArrayLayout} from '../../util/struct_array'; const lineLayoutAttributesPattern: StructArrayLayout = createLayout([ - {name: 'a_pattern_data', components: 2, type: 'Float32'} + {name: 'a_pattern_data', components: 3, type: 'Float32'} ]); export default lineLayoutAttributesPattern; diff --git a/src/data/bucket/line_bucket.ts b/src/data/bucket/line_bucket.ts index 3c631b81e2a..aa16ed13880 100644 --- a/src/data/bucket/line_bucket.ts +++ b/src/data/bucket/line_bucket.ts @@ -110,6 +110,7 @@ class LineBucket implements Bucket { patternJoinNone: boolean; segmentStart: number; + segmentStartf32: number; segmentPoints: Array; index: number; @@ -401,6 +402,7 @@ class LineBucket implements Bucket { const joinNone = join === 'none'; this.patternJoinNone = this.hasPattern && joinNone; this.segmentStart = 0; + this.segmentStartf32 = 0; this.segmentPoints = []; if (this.lineClips) { @@ -511,13 +513,12 @@ class LineBucket implements Bucket { // Integer part contains position in tile units // Fractional part has offset sign 0.25 = -1, 0.5 = 0, 0.75 = 1 const posAndOffset = Math.round(pos) + 0.5 + offsetSign * 0.25; - bucket.patternVertexArray.emplaceBack(posAndOffset, segmentLength); - bucket.patternVertexArray.emplaceBack(posAndOffset, segmentLength); + bucket.patternVertexArray.emplaceBack(posAndOffset, segmentLength, bucket.segmentStart); + bucket.patternVertexArray.emplaceBack(posAndOffset, segmentLength, bucket.segmentStart); } // Reset line segment - bucket.segmentPoints = []; - bucket.segmentStart = bucket.lineSoFar; + bucket.segmentPoints.length = 0; assert(bucket.zOffsetVertexArray.length === bucket.patternVertexArray.length || !bucket.hasZOffset); } @@ -815,6 +816,18 @@ class LineBucket implements Bucket { x, y, }: Point, extrudeX: number, extrudeY: number, round: boolean, up: boolean, dir: number, segment: Segment, fixedElevation?: number | null) { + if (this.patternJoinNone) { + if (this.segmentPoints.length === 0) { + this.segmentStart = this.lineSoFar; + this.segmentStartf32 = Math.fround(this.lineSoFar); + } + + if (!up) { // Only add one segment point for each line point + // Real vertex data is inserted after each line segment is finished + this.segmentPoints.push(this.lineSoFar - this.segmentStart, dir); + } + } + this.layoutVertexArray.emplaceBack( // a_pos_normal // Encode round/up the least significant bits @@ -827,7 +840,7 @@ class LineBucket implements Bucket { ((dir === 0 ? 0 : (dir < 0 ? -1 : 1)) + 1), 0, // unused // a_linesofar - this.lineSoFar); + this.lineSoFar - this.segmentStartf32); // Constructs a second vertex buffer with higher precision line progress if (this.lineClips) { @@ -847,10 +860,6 @@ class LineBucket implements Bucket { if (fixedElevation != null) { this.zOffsetVertexArray.emplaceBack(fixedElevation); } - if (!up && this.patternJoinNone) { - // Real data is inserted after each line segment is finished - this.segmentPoints.push(this.lineSoFar - this.segmentStart, dir); - } assert(this.zOffsetVertexArray.length === this.layoutVertexArray.length || !this.hasZOffset); } diff --git a/src/geo/projection/projection.ts b/src/geo/projection/projection.ts index bdd22b8151c..0e9ed12ca92 100644 --- a/src/geo/projection/projection.ts +++ b/src/geo/projection/projection.ts @@ -112,7 +112,7 @@ export default class Projection { } isPointAboveHorizon(tr: Transform, p: Point): boolean { - if (tr.elevation) { + if (tr.elevation && tr.elevation.visibleDemTiles.length) { const raycastOnTerrain = this.pointCoordinate3D(tr, p.x, p.y); return !raycastOnTerrain; } diff --git a/src/render/draw_atmosphere.ts b/src/render/draw_atmosphere.ts index 1a61ef3ef0b..67286317d04 100644 --- a/src/render/draw_atmosphere.ts +++ b/src/render/draw_atmosphere.ts @@ -137,7 +137,7 @@ class Atmosphere { const transitionT = globeToMercatorTransition(tr.zoom); - const fogLUT = painter.style._luts[fog.scope]; + const fogLUT = painter.style.getLut(fog.scope); const fogColor = fog.properties.get('color').toRenderColor(fogLUT).toArray01(); diff --git a/src/render/draw_background.ts b/src/render/draw_background.ts index c9b81758689..da80d12f663 100644 --- a/src/render/draw_background.ts +++ b/src/render/draw_background.ts @@ -33,7 +33,7 @@ function drawBackground(painter: Painter, sourceCache: SourceCache, layer: Backg if (image === null) { return; } - patternPosition = painter.imageManager.getPattern(image.toString(), layer.scope, painter.style._luts[layer.scope]); + patternPosition = painter.imageManager.getPattern(image.toString(), layer.scope, painter.style.getLut(layer.scope)); if (!patternPosition) { return; } diff --git a/src/render/fog.ts b/src/render/fog.ts index 5a262662ec7..5313712910c 100644 --- a/src/render/fog.ts +++ b/src/render/fog.ts @@ -59,7 +59,7 @@ export const fogUniformValues = ( ): UniformValues => { const tr = painter.transform; - const fogColor = fog.properties.get('color').toRenderColor(painter.style._luts[fog.scope]).toArray01(); + const fogColor = fog.properties.get('color').toRenderColor(painter.style.getLut(fog.scope)).toArray01(); fogColor[3] = fogOpacity; // Update Alpha const temporalOffset = (painter.frameCounter / 1000.0) % 1; diff --git a/src/render/painter.ts b/src/render/painter.ts index a0b2d3c3889..0733efeaded 100644 --- a/src/render/painter.ts +++ b/src/render/painter.ts @@ -981,7 +981,7 @@ class Painter { const fog = this.style.fog; if (fog && this.transform.projection.supportsFog) { - const fogLUT = this.style._luts[fog.scope]; + const fogLUT = this.style.getLut(fog.scope); if (!shouldRenderAtmosphere) { const fogColor = fog.properties.get('color').toRenderColor(fogLUT).toArray01(); diff --git a/src/render/program/line_program.ts b/src/render/program/line_program.ts index 378a5ecd3e2..7c8bf5c69a6 100644 --- a/src/render/program/line_program.ts +++ b/src/render/program/line_program.ts @@ -1,4 +1,4 @@ -import {Uniform1i, Uniform1f, Uniform2f, UniformMatrix2f, UniformMatrix4f} from '../uniform_binding'; +import {Uniform1i, Uniform1f, Uniform2f, Uniform4f, UniformMatrix2f, UniformMatrix4f} from '../uniform_binding'; import pixelsToTileUnits from '../../source/pixels_to_tile_units'; import type Context from '../../gl/context'; @@ -20,6 +20,8 @@ export type LineUniformsType = { ['u_tile_units_to_pixels']: Uniform1f; ['u_alpha_discard_threshold']: Uniform1f; ['u_trim_offset']: Uniform2f; + ['u_trim_fade_range']: Uniform2f; + ['u_trim_color']: Uniform4f; ['u_emissive_strength']: Uniform1f; }; @@ -49,6 +51,8 @@ const lineUniforms = (context: Context): LineUniformsType => ({ 'u_tile_units_to_pixels': new Uniform1f(context), 'u_alpha_discard_threshold': new Uniform1f(context), 'u_trim_offset': new Uniform2f(context), + 'u_trim_fade_range': new Uniform2f(context), + 'u_trim_color': new Uniform4f(context), 'u_emissive_strength': new Uniform1f(context) }); @@ -90,6 +94,8 @@ const lineUniformValues = ( 'u_tile_units_to_pixels': calculateTileRatio(tile, painter.transform), 'u_alpha_discard_threshold': 0.0, 'u_trim_offset': trimOffset, + 'u_trim_fade_range': layer.paint.get('line-trim-fade-range'), + 'u_trim_color': layer.paint.get('line-trim-color').toRenderColor(layer.lut).toArray01(), 'u_emissive_strength': layer.paint.get('line-emissive-strength') }; }; diff --git a/src/render/texture.ts b/src/render/texture.ts index 80dfd213b77..7c2b3722be5 100644 --- a/src/render/texture.ts +++ b/src/render/texture.ts @@ -185,7 +185,9 @@ export class Texture3D { gl.bindTexture(gl.TEXTURE_3D, this.texture); + context.pixelStoreUnpackFlipY.set(false); context.pixelStoreUnpack.set(1); + context.pixelStoreUnpackPremultiplyAlpha.set(false); let internalFormat = this.format; let type: TextureType = gl.UNSIGNED_BYTE; diff --git a/src/shaders/line.fragment.glsl b/src/shaders/line.fragment.glsl index 6b7b6fbae96..0e7f721ac47 100644 --- a/src/shaders/line.fragment.glsl +++ b/src/shaders/line.fragment.glsl @@ -4,6 +4,8 @@ uniform lowp float u_device_pixel_ratio; uniform float u_alpha_discard_threshold; uniform highp vec2 u_trim_offset; +uniform lowp vec2 u_trim_fade_range; +uniform lowp vec4 u_trim_color; in vec2 v_width2; in vec2 v_normal; @@ -69,7 +71,7 @@ void main() { out_color = color; #endif - float trimmed = 1.0; + float trim_alpha = 1.0; #ifdef RENDER_LINE_TRIM_OFFSET // v_uv[2] and v_uv[3] are specifying the original clip range that the vertex is located in. highp float start = v_uv[2]; @@ -86,10 +88,11 @@ void main() { // Nested conditionals fixes the issue // https://github.com/mapbox/mapbox-gl-js/issues/12013 if (trim_end > trim_start) { - if (line_progress <= trim_end && line_progress >= trim_start) { - out_color = vec4(0, 0, 0, 0); - trimmed = 0.0; - } + float start_transition = max(0.0, min(1.0, (line_progress - trim_start) / clamp(u_trim_fade_range[0], 1e-4, 1.0))); + float end_transition = max(0.0, min(1.0, (trim_end - line_progress) / clamp(u_trim_fade_range[1], 1e-4, 1.0))); + float transition_factor = min(start_transition, end_transition); + out_color = mix(out_color, u_trim_color, transition_factor); + trim_alpha = out_color.a; } #endif @@ -114,7 +117,7 @@ void main() { out_color.rgb *= (0.6 + 0.4 * smoothAlpha); } } else { - out_color.rgb = mix(border_color.rgb * border_color.a * trimmed, out_color.rgb, smoothAlpha); + out_color.rgb = mix(border_color.rgb * border_color.a * trim_alpha, out_color.rgb, smoothAlpha); } } #endif diff --git a/src/shaders/line_pattern.fragment.glsl b/src/shaders/line_pattern.fragment.glsl index 84ae3013103..65bfc6d4df1 100644 --- a/src/shaders/line_pattern.fragment.glsl +++ b/src/shaders/line_pattern.fragment.glsl @@ -1,17 +1,17 @@ #include "_prelude_fog.fragment.glsl" #include "_prelude_lighting.glsl" -uniform lowp float u_device_pixel_ratio; -uniform float u_alpha_discard_threshold; -uniform vec2 u_texsize; -uniform mediump float u_tile_units_to_pixels; +uniform highp float u_device_pixel_ratio; +uniform highp float u_alpha_discard_threshold; +uniform highp vec2 u_texsize; +uniform highp float u_tile_units_to_pixels; uniform highp vec2 u_trim_offset; uniform sampler2D u_image; in vec2 v_normal; in vec2 v_width2; -in float v_linesofar; +in highp float v_linesofar; in float v_gamma_scale; in float v_width; #ifdef RENDER_LINE_TRIM_OFFSET @@ -22,24 +22,23 @@ in highp vec4 v_uv; in vec2 v_pattern_data; // [pos_in_segment, segment_length]; #endif -#pragma mapbox: define lowp vec4 pattern -#pragma mapbox: define lowp float pixel_ratio -#pragma mapbox: define lowp float blur -#pragma mapbox: define lowp float opacity +#pragma mapbox: define mediump vec4 pattern +#pragma mapbox: define mediump float pixel_ratio +#pragma mapbox: define mediump float blur +#pragma mapbox: define mediump float opacity void main() { #pragma mapbox: initialize mediump vec4 pattern - #pragma mapbox: initialize lowp float pixel_ratio - - #pragma mapbox: initialize lowp float blur - #pragma mapbox: initialize lowp float opacity + #pragma mapbox: initialize mediump float pixel_ratio + #pragma mapbox: initialize mediump float blur + #pragma mapbox: initialize mediump float opacity vec2 pattern_tl = pattern.xy; vec2 pattern_br = pattern.zw; vec2 display_size = (pattern_br - pattern_tl) / pixel_ratio; - vec2 pattern_size = vec2(display_size.x / u_tile_units_to_pixels, display_size.y); + float pattern_size = display_size.x / u_tile_units_to_pixels; float aspect = display_size.y / v_width; @@ -52,7 +51,7 @@ void main() { float blur2 = (blur + 1.0 / u_device_pixel_ratio) * v_gamma_scale; float alpha = clamp(min(dist - (v_width2.t - blur2), v_width2.s - dist) / blur2, 0.0, 1.0); - float pattern_x = v_linesofar / pattern_size.x * aspect; + highp float pattern_x = v_linesofar / pattern_size * aspect; float x = mod(pattern_x, 1.0); float y = 0.5 * v_normal.y + 0.5; @@ -91,8 +90,8 @@ void main() { // negative). v_pattern_data.y is not modified because we can't access overlap info for other end of the segment. // All units are tile units. // Distance from segment start point to start of first pattern instance - float pattern_len = pattern_size.x / aspect; - float segment_phase = pattern_len - mod((v_linesofar - v_pattern_data.x), pattern_len); + float pattern_len = pattern_size / aspect; + float segment_phase = pattern_len - mod(v_linesofar - v_pattern_data.x + pattern_len, pattern_len); // Step is used to check if we can fit an extra pattern cycle when considering the segment overlap at the corner float visible_start = segment_phase - step(pattern_len * 0.5, segment_phase) * pattern_len; float visible_end = floor((v_pattern_data.y - segment_phase) / pattern_len) * pattern_len + segment_phase; diff --git a/src/shaders/line_pattern.vertex.glsl b/src/shaders/line_pattern.vertex.glsl index 89a1c96672c..dcb81ea1775 100644 --- a/src/shaders/line_pattern.vertex.glsl +++ b/src/shaders/line_pattern.vertex.glsl @@ -20,46 +20,46 @@ in float a_z_offset; #ifdef RENDER_LINE_TRIM_OFFSET in highp vec4 a_packed; #endif -in float a_linesofar; +in highp float a_linesofar; #ifdef LINE_JOIN_NONE -in vec2 a_pattern_data; // [position_in_segment & offset_sign, segment_length]; +in highp vec3 a_pattern_data; // [position_in_segment & offset_sign, segment_length, linesofar_hi]; out vec2 v_pattern_data; // [position_in_segment, segment_length] #endif uniform mat4 u_matrix; -uniform mediump float u_tile_units_to_pixels; +uniform float u_tile_units_to_pixels; uniform vec2 u_units_to_pixels; uniform mat2 u_pixels_to_tile_units; -uniform lowp float u_device_pixel_ratio; +uniform float u_device_pixel_ratio; out vec2 v_normal; out vec2 v_width2; -out float v_linesofar; +out highp float v_linesofar; out float v_gamma_scale; out float v_width; #ifdef RENDER_LINE_TRIM_OFFSET out highp vec4 v_uv; #endif -#pragma mapbox: define lowp float blur -#pragma mapbox: define lowp float opacity -#pragma mapbox: define lowp float offset +#pragma mapbox: define mediump float blur +#pragma mapbox: define mediump float opacity +#pragma mapbox: define mediump float offset #pragma mapbox: define mediump float gapwidth #pragma mapbox: define mediump float width -#pragma mapbox: define lowp float floorwidth -#pragma mapbox: define lowp vec4 pattern -#pragma mapbox: define lowp float pixel_ratio +#pragma mapbox: define mediump float floorwidth +#pragma mapbox: define mediump vec4 pattern +#pragma mapbox: define mediump float pixel_ratio void main() { - #pragma mapbox: initialize lowp float blur - #pragma mapbox: initialize lowp float opacity - #pragma mapbox: initialize lowp float offset + #pragma mapbox: initialize mediump float blur + #pragma mapbox: initialize mediump float opacity + #pragma mapbox: initialize mediump float offset #pragma mapbox: initialize mediump float gapwidth #pragma mapbox: initialize mediump float width - #pragma mapbox: initialize lowp float floorwidth + #pragma mapbox: initialize mediump float floorwidth #pragma mapbox: initialize mediump vec4 pattern - #pragma mapbox: initialize lowp float pixel_ratio + #pragma mapbox: initialize mediump float pixel_ratio // the distance over which the line edge fades out. // Retina devices need a smaller distance to avoid aliasing. @@ -73,7 +73,7 @@ void main() { // x is 1 if it's a round cap, 0 otherwise // y is 1 if the normal points up, and -1 if it points down // We store these in the least significant bit of a_pos_normal - mediump vec2 normal = a_pos_normal - 2.0 * pos; + vec2 normal = a_pos_normal - 2.0 * pos; normal.y = normal.y * 2.0 - 1.0; v_normal = normal; @@ -88,15 +88,15 @@ void main() { // Scale the extrusion vector down to a normal and then up by the line width // of this vertex. - mediump vec2 dist = outset * a_extrude * scale; + vec2 dist = outset * a_extrude * scale; // Calculate the offset when drawing a line that is to the side of the actual line. // We do this by creating a vector that points towards the extrude, but rotate // it when we're drawing round end points (a_direction = -1 or 1) since their // extrude vector points in another direction. - mediump float u = 0.5 * a_direction; - mediump float t = 1.0 - abs(u); - mediump vec2 offset2 = offset * a_extrude * scale * normal.y * mat2(t, -u, u, t); + float u = 0.5 * a_direction; + float t = 1.0 - abs(u); + vec2 offset2 = offset * a_extrude * scale * normal.y * mat2(t, -u, u, t); vec4 projected_extrude = u_matrix * vec4(dist * u_pixels_to_tile_units, 0.0, 0.0); #if defined(ELEVATED) @@ -141,11 +141,19 @@ void main() { #ifdef LINE_JOIN_NONE // Needs to consider antialiasing width extension to get accurate pattern aspect ratio - v_width += ANTIALIASING; + v_width = floorwidth + ANTIALIASING; + + mediump float pixels_to_tile_units = 1.0 / u_tile_units_to_pixels; + mediump float pixel_ratio_inverse = 1.0 / pixel_ratio; + mediump float aspect = v_width / ((pattern.w - pattern.y) * pixel_ratio_inverse); + // Pattern length * 32 is chosen experimentally, seems to provide good quality + highp float subt_multiple = (pattern.z - pattern.x) * pixel_ratio_inverse * pixels_to_tile_units * aspect * 32.0; + highp float subt = floor(a_pattern_data.z / subt_multiple) * subt_multiple; + // Offset caused by vertices extended forward or backward from line point float offset_sign = (fract(a_pattern_data.x) - 0.5) * 4.0; - float line_progress_offset = offset_sign * v_width * 0.5 / u_tile_units_to_pixels; - v_linesofar += line_progress_offset; + float line_progress_offset = offset_sign * v_width * 0.5 * pixels_to_tile_units; + v_linesofar = (a_pattern_data.z - subt) + a_linesofar + line_progress_offset; v_pattern_data = vec2(a_pattern_data.x + line_progress_offset, a_pattern_data.y); #endif diff --git a/src/source/geojson_rt.ts b/src/source/geojson_rt.ts new file mode 100644 index 00000000000..86150078eff --- /dev/null +++ b/src/source/geojson_rt.ts @@ -0,0 +1,230 @@ + +import EXTENT from '../style-spec/data/extent'; +import {mercatorXfromLng, mercatorYfromLat} from '../geo/mercator_coordinate'; + +import type WorkerTile from './worker_tile'; +import type {Feature} from './geojson_wrapper'; + +type BBox = { + minX: number, + minY: number, + maxX: number, + maxY: number +} + +type InternalFeature = BBox & { + id: string | number, + tags: {[_: string]: string | number | boolean}, + type: 1 | 2 | 3, + geometry: number[] | number[][] +}; + +const PAD = 64 / 4096; // geojson-vt default tile buffer + +/* + * A GeoJSON index tailored to "small data, updated frequently" use cases + * which gets used with GeoJSON sources in `dynamic` mode + */ +export default class GeoJSONRT { + features: Map; + + constructor() { + this.features = new Map(); + } + + clear() { + this.features.clear(); + } + + load(features: GeoJSON.Feature[] = [], cache: {[_: number]: WorkerTile}) { + for (const feature of features) { + const id = feature.id; + if (id == null) continue; + + let updated = this.features.get(id); + + // update tile cache for the old position + if (updated) this.updateCache(updated, cache); + + if (!feature.geometry) { + this.features.delete(id); + } else { + updated = convertFeature(feature); + // update tile cache for the new position + this.updateCache(updated, cache); + this.features.set(id, updated); + } + + this.updateCache(updated, cache); + } + } + + // clear all tiles that contain a given feature from the tile cache + updateCache(feature: InternalFeature, cache: {[_: number]: WorkerTile}) { + for (const {canonical, uid} of Object.values(cache)) { + const {z, x, y} = canonical; + const z2 = Math.pow(2, z); + + if (intersectsTile(feature, z2, x, y)) { + delete cache[uid]; + } + } + } + + // return all features that fit in the tile (plus a small padding) by bbox; since dynamic mode is + // for "small data, frequently updated" case, linear loop through all features should be fast enough + getTile(z: number, tx: number, ty: number) { + const z2 = Math.pow(2, z); + const features = []; + for (const feature of this.features.values()) { + if (intersectsTile(feature, z2, tx, ty)) { + features.push(outputFeature(feature, z2, tx, ty)); + } + } + return {features}; + } + + getFeatures() { + return [...this.features.values()]; + } +} + +function intersectsTile({minX, minY, maxX, maxY}: BBox, z2: number, tx: number, ty: number) { + const x0 = (tx - PAD) / z2; + const y0 = (ty - PAD) / z2; + const x1 = (tx + 1 + PAD) / z2; + const y1 = (ty + 1 + PAD) / z2; + return minX < x1 && minY < y1 && maxX > x0 && maxY > y0; +} + +function convertFeature(originalFeature: GeoJSON.Feature): InternalFeature { + const {id, geometry, properties} = originalFeature; + if (!geometry) return; + if (geometry.type === 'GeometryCollection') { + throw new Error('GeometryCollection not supported in dynamic mode.'); + } + const {type, coordinates} = geometry; + + const feature: InternalFeature = { + id, + type: 1, + geometry: [], + tags: properties, + minX: Infinity, + minY: Infinity, + maxX: -Infinity, + maxY: -Infinity + }; + const geom = feature.geometry; + + if (type === 'Point') { + convertPoint(coordinates, geom as number[], feature); + + } else if (type === 'MultiPoint') { + for (const p of coordinates) { + convertPoint(p, geom as number[], feature); + } + + } else if (type === 'LineString') { + feature.type = 2; + convertLine(coordinates, geom as number[][], feature); + + } else if (type === 'MultiLineString') { + feature.type = 2; + convertLines(coordinates, geom as number[][], feature); + + } else if (type === 'Polygon') { + feature.type = 3; + convertLines(coordinates, geom as number[][], feature, true); + + } else if (type === 'MultiPolygon') { + feature.type = 3; + for (const polygon of coordinates) { + convertLines(polygon, geom as number[][], feature, true); + } + + } else { + throw new Error('Input data is not a valid GeoJSON object.'); + } + + return feature; +} + +function convertPoint([lng, lat]: GeoJSON.Position, out: number[], bbox: BBox) { + const x = mercatorXfromLng(lng); + let y = mercatorYfromLat(lat); + y = y < 0 ? 0 : y > 1 ? 1 : y; + out.push(x, y); + + bbox.minX = Math.min(bbox.minX, x); + bbox.minY = Math.min(bbox.minY, y); + bbox.maxX = Math.max(bbox.maxX, x); + bbox.maxY = Math.max(bbox.maxY, y); +} + +function convertLine(ring: GeoJSON.Position[], out: number[][], bbox: BBox, isPolygon: boolean = false, isOuter: boolean = false) { + const newLine: number[] = []; + for (const p of ring) { + convertPoint(p, newLine, bbox); + } + out.push(newLine); + if (isPolygon) rewind(newLine, isOuter); +} + +function convertLines(lines: GeoJSON.Position[][], out: number[][], bbox: BBox, isPolygon: boolean = false) { + for (let i = 0; i < lines.length; i++) { + convertLine(lines[i], out, bbox, isPolygon, i === 0); + } +} + +function outputFeature(feature: InternalFeature, z2: number, tx: number, ty: number): Feature { + const {id, type, geometry, tags} = feature; + const tileGeom = []; + + if (type === 1) { + transformLine(geometry as number[], z2, tx, ty, tileGeom); + } else { + for (const ring of geometry) { + tileGeom.push(transformLine(ring as number[], z2, tx, ty)); + } + } + + return { + id, + type, + geometry: tileGeom, + tags + }; +} + +function transformLine(line: number[], z2: number, tx: number, ty: number, out: [number, number][] = []) { + for (let i = 0; i < line.length; i += 2) { + out.push(transformPoint(line[i], line[i + 1], z2, tx, ty)); + } + return out; +} + +function transformPoint(x: number, y: number, z2: number, tx: number, ty: number): [number, number] { + return [ + Math.round(EXTENT * (x * z2 - tx)), + Math.round(EXTENT * (y * z2 - ty)) + ]; +} + +// rewind a polygon ring to a given winding order (clockwise or anti-clockwise) +function rewind(ring: number[], clockwise: boolean) { + let area = 0; + for (let i = 0, len = ring.length, j = len - 2; i < len; j = i, i += 2) { + area += (ring[i] - ring[j]) * (ring[i + 1] + ring[j + 1]); + } + if (area > 0 === clockwise) { + for (let i = 0, len = ring.length; i < len / 2; i += 2) { + const x = ring[i]; + const y = ring[i + 1]; + ring[i] = ring[len - 2 - i]; + ring[i + 1] = ring[len - 1 - i]; + ring[len - 2 - i] = x; + ring[len - 1 - i] = y; + } + } +} diff --git a/src/source/geojson_source.ts b/src/source/geojson_source.ts index ce5a4b5fa86..3a1ad598c45 100644 --- a/src/source/geojson_source.ts +++ b/src/source/geojson_source.ts @@ -88,6 +88,7 @@ class GeoJSONSource extends Evented implements ISource { _metadataFired: boolean | null | undefined; _collectResourceTiming: boolean; _pendingLoad: Cancelable | null | undefined; + _partialReload: boolean; reload: undefined; hasTile: undefined; @@ -404,7 +405,9 @@ class GeoJSONSource extends Evented implements ISource { if (this._collectResourceTiming && result && result.resourceTiming && result.resourceTiming[this.id]) { data.resourceTiming = result.resourceTiming[this.id]; } + if (append) this._partialReload = true; this.fire(new Event('data', data)); + this._partialReload = false; this._metadataFired = true; } @@ -423,6 +426,7 @@ class GeoJSONSource extends Evented implements ISource { const message = !tile.actor ? 'loadTile' : 'reloadTile'; tile.actor = this.actor; const lutForScope = this.map.style ? this.map.style.getLut(this.scope) : null; + const partial = this._partialReload; const params = { type: this.type, uid: tile.uid, @@ -439,10 +443,17 @@ class GeoJSONSource extends Evented implements ISource { pixelRatio: browser.devicePixelRatio, showCollisionBoxes: this.map.showCollisionBoxes, promoteId: this.promoteId, - brightness: this.map.style ? (this.map.style.getBrightness() || 0.0) : 0.0 + brightness: this.map.style ? (this.map.style.getBrightness() || 0.0) : 0.0, + partial }; tile.request = this.actor.send(message, params, (err, data) => { + if (partial && !data) { + // if we did a partial reload and the tile didn't change, do nothing and treat the tile as loaded + tile.state = 'loaded'; + return callback(null); + } + delete tile.request; tile.destroy(); diff --git a/src/source/geojson_worker_source.ts b/src/source/geojson_worker_source.ts index 51bf3bb69bf..f4fc1df9c25 100644 --- a/src/source/geojson_worker_source.ts +++ b/src/source/geojson_worker_source.ts @@ -2,6 +2,7 @@ import {getJSON} from '../util/ajax'; import {getPerformanceMeasurement} from '../util/performance'; import GeoJSONWrapper from './geojson_wrapper'; +import GeoJSONRT from './geojson_rt'; import vtpbf from 'vt-pbf'; import Supercluster from 'supercluster'; import geojsonvt from 'geojson-vt'; @@ -43,9 +44,9 @@ export type LoadGeoJSON = (params: LoadGeoJSONParameters, callback: ResponseCall export interface GeoJSONIndex { getTile(z: number, x: number, y: number): any; // supercluster methods - getClusterExpansionZoom(clusterId: number): number; - getChildren(clusterId: number): Array; - getLeaves(clusterId: number, limit: number, offset: number): Array; + getClusterExpansionZoom?(clusterId: number): number; + getChildren?(clusterId: number): Array; + getLeaves?(clusterId: number, limit: number, offset: number): Array; } function loadGeoJSONTile(params: RequestedTileParameters, callback: LoadVectorDataCallback) { @@ -89,7 +90,7 @@ function loadGeoJSONTile(params: RequestedTileParameters, callback: LoadVectorDa */ class GeoJSONWorkerSource extends VectorTileWorkerSource { _geoJSONIndex: GeoJSONIndex; - _featureMap: Map; + _dynamicIndex: GeoJSONRT; /** * @param [loadGeoJSON] Optional method for custom loading/parsing of @@ -103,7 +104,7 @@ class GeoJSONWorkerSource extends VectorTileWorkerSource { if (loadGeoJSON) { this.loadGeoJSON = loadGeoJSON; } - this._featureMap = new Map(); + this._dynamicIndex = new GeoJSONRT(); } /** @@ -155,32 +156,27 @@ class GeoJSONWorkerSource extends VectorTileWorkerSource { if (data.type === 'Feature') data = {type: 'FeatureCollection', features: [data]}; if (!params.append) { - this._featureMap.clear(); + this._dynamicIndex.clear(); + this.loaded = {}; } - for (const feature of (data.features || [])) { - const id = feature.id; - if (id !== undefined) { - if (!feature.geometry) { - this._featureMap.delete(id); - } else { - this._featureMap.set(id, feature); - } - } - } - data.features = [...this._featureMap.values()]; + this._dynamicIndex.load(data.features, this.loaded); + + if (params.cluster) data.features = this._dynamicIndex.getFeatures(); + + } else { + this.loaded = {}; } - this._geoJSONIndex = params.cluster ? - new Supercluster(getSuperclusterOptions(params)).load(data.features) : + this._geoJSONIndex = + params.cluster ? new Supercluster(getSuperclusterOptions(params)).load(data.features) : + params.dynamic ? this._dynamicIndex : geojsonvt(data, params.geojsonVtOptions); } catch (err: any) { return callback(err); } - this.loaded = {}; - const result: Record = {}; if (perf) { const resourceTimingData = getPerformanceMeasurement(requestParam); @@ -211,6 +207,9 @@ class GeoJSONWorkerSource extends VectorTileWorkerSource { uid = params.uid; if (loaded && loaded[uid]) { + if (params.partial) { + return callback(null, undefined); + } return super.reloadTile(params, callback); } else { return this.loadTile(params, callback); diff --git a/src/source/geojson_wrapper.ts b/src/source/geojson_wrapper.ts index 62b9494c372..44da0264af0 100644 --- a/src/source/geojson_wrapper.ts +++ b/src/source/geojson_wrapper.ts @@ -8,7 +8,7 @@ import type {VectorTile, VectorTileLayer} from '@mapbox/vector-tile'; // The feature type used by geojson-vt and supercluster. Should be extracted to // global type and used in module definitions for those two modules. -type Feature = { +export type Feature = { type: 1; id: unknown; tags: { diff --git a/src/source/load_tilejson.ts b/src/source/load_tilejson.ts index 13c1bee8bba..faf45dfc553 100644 --- a/src/source/load_tilejson.ts +++ b/src/source/load_tilejson.ts @@ -33,10 +33,18 @@ export default function( } else if (tileJSON) { // Prefer TileJSON tiles, if both URL and tiles options are set if (options.url && tileJSON.tiles && options.tiles) delete options.tiles; - // check if we have variants and merge with the original TileJson if (tileJSON.variants) { + if (!Array.isArray(tileJSON.variants)) { + return callback(new Error("variants must be an array")); + } for (const variant of tileJSON.variants) { + if (variant == null || typeof variant !== 'object' || variant.constructor !== Object) { + return callback(new Error("variant must be an object")); + } + if (!Array.isArray(variant.capabilities)) { + return callback(new Error("capabilities must be an array")); + } // in this version we only support meshopt, we check there is no more different capabilities // so future tileJsons with more capabilities won't break existing sdk's if (variant.capabilities.length === 1 && variant.capabilities[0] === "meshopt") { diff --git a/src/source/query_features.ts b/src/source/query_features.ts index 3f709d0ee30..c0fc680ea4e 100644 --- a/src/source/query_features.ts +++ b/src/source/query_features.ts @@ -9,7 +9,7 @@ import type Transform from '../geo/transform'; import type {OverscaledTileID} from './tile_id'; import type {RetainedQueryData} from '../symbol/placement'; import type {QueryGeometry, TilespaceQueryGeometry} from '../style/query_geometry'; -import type {LayerSpecification, FilterSpecification} from '../style-spec/types'; +import type {LayerSpecification, FilterSpecification, ExpressionSpecification} from '../style-spec/types'; // we augment GeoJSON with custom properties in query*Features results export interface QueryFeature extends GeoJSON.Feature { @@ -196,7 +196,11 @@ export function queryRenderedSymbols( return result; } -export function querySourceFeatures(sourceCache: SourceCache, params: any): Array { +export function querySourceFeatures(sourceCache: SourceCache, params?: { + sourceLayer?: string; + filter?: FilterSpecification | ExpressionSpecification; + validate?: boolean; +}): Array { const tiles = sourceCache.getRenderableIds().map((id) => { return sourceCache.getTileByID(id); }); diff --git a/src/source/tile.ts b/src/source/tile.ts index c5f358b4711..5e333e0563a 100644 --- a/src/source/tile.ts +++ b/src/source/tile.ts @@ -45,7 +45,7 @@ import type Framebuffer from '../gl/framebuffer'; import type Transform from '../geo/transform'; import type {LayerFeatureStates} from './source_state'; import type {Cancelable} from '../types/cancelable'; -import type {FilterSpecification} from '../style-spec/types'; +import type {FilterSpecification, ExpressionSpecification} from '../style-spec/types'; import type {TilespaceQueryGeometry} from '../style/query_geometry'; import type VertexBuffer from '../gl/vertex_buffer'; import type IndexBuffer from '../gl/index_buffer'; @@ -480,7 +480,11 @@ class Tile { }, layers, serializedLayers, sourceFeatureState); } - querySourceFeatures(result: Array, params: any) { + querySourceFeatures(result: Array, params?: { + sourceLayer?: string; + filter?: FilterSpecification | ExpressionSpecification; + validate?: boolean; + }) { const featureIndex = this.latestFeatureIndex; if (!featureIndex || !featureIndex.rawTileData) return; diff --git a/src/source/worker.ts b/src/source/worker.ts index f13ae8455bd..162cc69dba0 100644 --- a/src/source/worker.ts +++ b/src/source/worker.ts @@ -164,11 +164,10 @@ export default class Worker { for (const workerSource in this.workerSources[mapId][scope]) { const ws = this.workerSources[mapId][scope][workerSource]; for (const source in ws) { - if (ws[source] instanceof VectorTileWorkerSource) { - // @ts-expect-error - TS2339 - Property 'isSpriteLoaded' does not exist on type 'WorkerSource'. - ws[source].isSpriteLoaded = isLoaded; - // @ts-expect-error - TS2339 - Property 'fire' does not exist on type 'WorkerSource'. - ws[source].fire(new Event('isSpriteLoaded')); + const workerSource = ws[source]; + if (workerSource instanceof VectorTileWorkerSource) { + workerSource.isSpriteLoaded = isLoaded; + workerSource.fire(new Event('isSpriteLoaded')); } } } diff --git a/src/source/worker_source.ts b/src/source/worker_source.ts index 60e6d669ca1..0576e7e0353 100644 --- a/src/source/worker_source.ts +++ b/src/source/worker_source.ts @@ -41,6 +41,7 @@ export type WorkerTileParameters = RequestedTileParameters & { brightness: number; extraShadowCaster?: boolean; tessellationStep?: number // test purpose only; + partial?: boolean; }; export type DEMSourceEncoding = 'mapbox' | 'terrarium'; diff --git a/src/style-spec/feature_filter/index.ts b/src/style-spec/feature_filter/index.ts index a517beeae96..a04d6cffcd2 100644 --- a/src/style-spec/feature_filter/index.ts +++ b/src/style-spec/feature_filter/index.ts @@ -1,10 +1,12 @@ +import type Point from '@mapbox/point-geometry'; +import latest from '../reference/latest'; + +import {deepUnbundle} from '../util/unbundle_jsonlint'; import {createExpression} from '../expression/index'; import {isFeatureConstant} from '../expression/is_constant'; -import {deepUnbundle} from '../util/unbundle_jsonlint'; -import latest from '../reference/latest'; -import type {GlobalProperties, Feature} from '../expression/index'; import type {CanonicalTileID} from '../types/tile_id'; -import type Point from '@mapbox/point-geometry'; +import type {GlobalProperties, Feature} from '../expression/index'; +import type {FilterSpecification, ExpressionSpecification} from '../types'; export type FeatureDistanceData = { bearing: [number, number]; @@ -28,7 +30,7 @@ export type FeatureFilter = { export default createFilter; export {isExpressionFilter, isDynamicFilter, extractStaticFilter}; -function isExpressionFilter(filter: any): boolean { +function isExpressionFilter(filter: unknown): boolean { if (filter === true || filter === false) { return true; } @@ -80,14 +82,15 @@ function isExpressionFilter(filter: any): boolean { * @param {string} layerType the type of the layer this filter will be applied to. * @returns {Function} filter-evaluating function */ -function createFilter(filter: any, layerType: string = 'fill'): FeatureFilter { +function createFilter(filter?: FilterSpecification | ExpressionSpecification, layerType: string = 'fill'): FeatureFilter { if (filter === null || filter === undefined) { return {filter: () => true, needGeometry: false, needFeature: false}; } if (!isExpressionFilter(filter)) { - filter = convertFilter(filter); + filter = convertFilter(filter) as ExpressionSpecification; } + const filterExp = (filter as string[] | string | boolean); let staticFilter = true; diff --git a/src/style-spec/function/convert.ts b/src/style-spec/function/convert.ts index c6a1f703f9d..fefb15f2c8e 100644 --- a/src/style-spec/function/convert.ts +++ b/src/style-spec/function/convert.ts @@ -1,27 +1,18 @@ import assert from 'assert'; import type {StylePropertySpecification} from '../style-spec'; -import type {ExpressionSpecification} from '../types'; - -type Stop = [{ - zoom: number; - value: string | number | boolean; -}, unknown]; - -type FunctionParameters = { - stops: Array; - base: number; - property: string; - type: 'identity' | 'exponential' | 'interval' | 'categorical'; - colorSpace: 'rgb' | 'lab' | 'hcl'; - default: unknown; -}; +import type { + FunctionSpecification, + PropertyFunctionStop, + ZoomAndPropertyFunctionStop, + ExpressionSpecification, +} from '../types'; function convertLiteral(value: unknown) { return typeof value === 'object' ? ['literal', value] : value; } -export default function convertFunction(parameters: FunctionParameters, propertySpec: StylePropertySpecification): ExpressionSpecification { +export default function convertFunction(parameters: FunctionSpecification, propertySpec: StylePropertySpecification): ExpressionSpecification { let stops = parameters.stops; if (!stops) { // identity function @@ -33,23 +24,22 @@ export default function convertFunction(parameters: FunctionParameters, property const zoomDependent = zoomAndFeatureDependent || !featureDependent; stops = stops.map((stop) => { - // @ts-expect-error - TS2339 - Property 'tokens' does not exist on type 'StylePropertySpecification'. if (!featureDependent && propertySpec.tokens && typeof stop[1] === 'string') { return [stop[0], convertTokenString(stop[1])]; } return [stop[0], convertLiteral(stop[1])]; - }); + }) as FunctionSpecification['stops']; if (zoomAndFeatureDependent) { - return convertZoomAndPropertyFunction(parameters, propertySpec, stops); + return convertZoomAndPropertyFunction(parameters, propertySpec, stops as Array>); } else if (zoomDependent) { - return convertZoomFunction(parameters, propertySpec, stops); + return convertZoomFunction(parameters, propertySpec, stops as PropertyFunctionStop[]); } else { - return convertPropertyFunction(parameters, propertySpec, stops); + return convertPropertyFunction(parameters, propertySpec, stops as PropertyFunctionStop[]); } } -function convertIdentityFunction(parameters: FunctionParameters, propertySpec: StylePropertySpecification): ExpressionSpecification { +function convertIdentityFunction(parameters: FunctionSpecification, propertySpec: StylePropertySpecification): ExpressionSpecification { const get: ExpressionSpecification = ['get', parameters.property]; if (parameters.default === undefined) { @@ -73,7 +63,7 @@ function convertIdentityFunction(parameters: FunctionParameters, propertySpec: S } } -function getInterpolateOperator(parameters: FunctionParameters) { +function getInterpolateOperator(parameters: FunctionSpecification) { switch (parameters.colorSpace) { case 'hcl': return 'interpolate-hcl'; case 'lab': return 'interpolate-lab'; @@ -81,10 +71,10 @@ function getInterpolateOperator(parameters: FunctionParameters) { } } -function convertZoomAndPropertyFunction( - parameters: FunctionParameters, +function convertZoomAndPropertyFunction( + parameters: FunctionSpecification, propertySpec: StylePropertySpecification, - stops: Array, + stops: Array>, ): ExpressionSpecification { const featureFunctionParameters: Record = {}; const featureFunctionStops: Record = {}; @@ -109,8 +99,7 @@ function convertZoomAndPropertyFunction( // function is determined directly from the style property specification // for which it's being used: linear for interpolatable properties, step // otherwise. - // @ts-expect-error - TS2345 - Argument of type '{}' is not assignable to parameter of type 'FunctionParameters'. - const functionType = getFunctionType({}, propertySpec); + const functionType = getFunctionType({} as FunctionSpecification, propertySpec); if (functionType === 'exponential') { const expression: ExpressionSpecification = [getInterpolateOperator(parameters), ['linear'], ['zoom']]; @@ -139,7 +128,7 @@ function coalesce(a: unknown, b: unknown) { if (b !== undefined) return b; } -function getFallback(parameters: FunctionParameters, propertySpec: StylePropertySpecification) { +function getFallback(parameters: FunctionSpecification, propertySpec: StylePropertySpecification) { const defaultValue = convertLiteral(coalesce(parameters.default, propertySpec.default)); /* @@ -154,10 +143,10 @@ function getFallback(parameters: FunctionParameters, propertySpec: StyleProperty return defaultValue; } -function convertPropertyFunction( - parameters: FunctionParameters, +function convertPropertyFunction( + parameters: FunctionSpecification, propertySpec: StylePropertySpecification, - stops: Array, + stops: Array>, ): ExpressionSpecification { const type = getFunctionType(parameters, propertySpec); const get: ExpressionSpecification = ['get', parameters.property]; @@ -211,7 +200,7 @@ function convertPropertyFunction( } } -function convertZoomFunction(parameters: FunctionParameters, propertySpec: StylePropertySpecification, stops: Array, input: Array = ['zoom']) { +function convertZoomFunction(parameters: FunctionSpecification, propertySpec: StylePropertySpecification, stops: Array>, input: Array = ['zoom']) { const type = getFunctionType(parameters, propertySpec); let expression; let isStep = false; @@ -256,7 +245,7 @@ function appendStopPair(curve: ExpressionSpecification, input: unknown, output: curve.push(output); } -function getFunctionType(parameters: FunctionParameters, propertySpec: StylePropertySpecification): string { +function getFunctionType(parameters: FunctionSpecification, propertySpec: StylePropertySpecification): string { if (parameters.type) { return parameters.type; } else { diff --git a/src/style-spec/migrate/expressions.ts b/src/style-spec/migrate/expressions.ts index ecc174b2218..f718237008c 100644 --- a/src/style-spec/migrate/expressions.ts +++ b/src/style-spec/migrate/expressions.ts @@ -3,7 +3,7 @@ import {isExpression} from '../expression/index'; import convertFunction, {convertTokenString} from '../function/convert'; import convertFilter from '../feature_filter/convert'; -import type {StyleSpecification} from '../types'; +import type {StyleSpecification, FunctionSpecification} from '../types'; /** * Migrate the given style object in place to use expressions. Specifically, @@ -22,10 +22,8 @@ export default function(style: StyleSpecification): StyleSpecification { eachProperty(style, {paint: true, layout: true}, ({path, value, reference, set}) => { if (isExpression(value)) return; if (typeof value === 'object' && !Array.isArray(value)) { - // @ts-expect-error - TS2345 - Argument of type 'object' is not assignable to parameter of type 'FunctionParameters'. - set(convertFunction(value, reference)); + set(convertFunction(value as FunctionSpecification, reference)); converted.push(path.join('.')); - // @ts-expect-error - TS2339 - Property 'tokens' does not exist on type 'StylePropertySpecification'. } else if (reference.tokens && typeof value === 'string') { set(convertTokenString(value)); } @@ -33,4 +31,3 @@ export default function(style: StyleSpecification): StyleSpecification { return style; } - diff --git a/src/style-spec/package.json b/src/style-spec/package.json index 6f5fe5a4950..911f83e8929 100644 --- a/src/style-spec/package.json +++ b/src/style-spec/package.json @@ -1,6 +1,6 @@ { "name": "@mapbox/mapbox-gl-style-spec", - "version": "14.5.0-beta.1", + "version": "14.5.0", "description": "a specification for mapbox gl styles", "author": "Mapbox", "license": "SEE LICENSE IN LICENSE.txt", @@ -46,7 +46,7 @@ "@mapbox/jsonlint-lines-primitives": "~2.0.2", "@mapbox/point-geometry": "^0.1.0", "@mapbox/unitbezier": "^0.0.1", - "cheap-ruler": "^3.0.1", + "cheap-ruler": "^4.0.0", "csscolorparser": "~1.0.2", "json-stringify-pretty-compact": "^4.0.0", "minimist": "^1.2.6", diff --git a/src/style-spec/reference/v8.json b/src/style-spec/reference/v8.json index e15675ff75c..0b3cc522967 100644 --- a/src/style-spec/reference/v8.json +++ b/src/style-spec/reference/v8.json @@ -5162,6 +5162,7 @@ "paint_fill-extrusion", "paint_symbol", "paint_raster", + "paint_raster-particle", "paint_hillshade", "paint_background", "paint_sky", @@ -6315,19 +6316,19 @@ "line-trim-offset": { "type": "array", "value": "number", - "doc": "The line part between [trim-start, trim-end] will be marked as transparent to make a route vanishing effect. The line trim-off offset is based on the whole line range [0.0, 1.0].", + "doc": "The line part between [trim-start, trim-end] will be painted using `line-trim-color,` which is transparent by default to produce a route vanishing effect. The line trim-off offset is based on the whole line range [0.0, 1.0].", "length": 2, "default": [0.0, 0.0], "minimum": [0.0, 0.0], "maximum": [1.0, 1.0], "transition": false, "requires": [ - { - "source": "geojson", - "has": { - "lineMetrics": true - } - } + { + "source": "geojson", + "has": { + "lineMetrics": true + } + } ], "sdk-support": { "basic functionality": { @@ -6338,6 +6339,72 @@ }, "property-type": "constant" }, + "line-trim-fade-range": { + "type": "array", + "value": "number", + "doc": "The fade range for the trim-start and trim-end points is defined by the `line-trim-offset` property. The first element of the array represents the fade range from the trim-start point toward the end of the line, while the second element defines the fade range from the trim-end point toward the beginning of the line. The fade result is achieved by interpolating between `line-trim-color` and the color specified by the `line-color` or the `line-gradient` property.", + "experimental": true, + "length": 2, + "default": [0.0, 0.0], + "minimum": [0.0, 0.0], + "maximum": [1.0, 1.0], + "transition": false, + "requires": [ + "line-trim-offset", + { + "source": "geojson", + "has": { + "lineMetrics": true + } + } + ], + "expression": { + "interpolated": true, + "parameters": [ + "zoom", + "measure-light" + ] + }, + "sdk-support": { + "basic functionality": { + "js": "3.6.0", + "android": "11.6.0", + "ios": "11.6.0" + } + }, + "property-type": "data-constant" + }, + "line-trim-color": { + "type": "color", + "doc": "The color to be used for rendering the trimmed line section that is defined by the `line-trim-offset` property.", + "experimental": true, + "default": "transparent", + "transition": true, + "requires": [ + "line-trim-offset", + { + "source": "geojson", + "has": { + "lineMetrics": true + } + } + ], + "sdk-support": { + "basic functionality": { + "js": "3.6.0", + "android": "11.6.0", + "ios": "11.6.0" + } + }, + "expression": { + "interpolated": true, + "parameters": [ + "zoom", + "measure-light" + ] + }, + "property-type": "data-constant" + }, "line-emissive-strength": { "type": "number", "default": 0.0, diff --git a/src/style-spec/style-spec.ts b/src/style-spec/style-spec.ts index 23af16c7b87..df0f9e94cca 100644 --- a/src/style-spec/style-spec.ts +++ b/src/style-spec/style-spec.ts @@ -12,7 +12,8 @@ export type StylePropertySpecification = { 'property-type': ExpressionType, expression?: ExpressionSpecification, transition?: boolean, - default?: number + default?: number, + tokens: never } | { type: 'string', 'property-type': ExpressionType, @@ -25,20 +26,23 @@ export type StylePropertySpecification = { 'property-type': ExpressionType, expression?: ExpressionSpecification, transition?: boolean, - default?: boolean + default?: boolean, + tokens: never } | { type: 'enum', 'property-type': ExpressionType, expression?: ExpressionSpecification, values: {[_: string]: unknown}, transition?: boolean, - default?: string + default?: string, + tokens: never } | { type: 'color', 'property-type': ExpressionType, expression?: ExpressionSpecification, transition?: boolean, default?: string, + tokens: never, overridable: boolean } | { type: 'array', @@ -47,7 +51,8 @@ export type StylePropertySpecification = { expression?: ExpressionSpecification, length?: number, transition?: boolean, - default?: Array + default?: Array, + tokens: never } | { type: 'array', value: 'string', @@ -55,13 +60,15 @@ export type StylePropertySpecification = { expression?: ExpressionSpecification, length?: number, transition?: boolean, - default?: Array + default?: Array, + tokens: never } | { type: 'resolvedImage', 'property-type': ExpressionType, expression?: ExpressionSpecification, transition?: boolean, - default?: string + default?: string, + tokens: never }; import v8 from './reference/v8.json'; diff --git a/src/style-spec/types.ts b/src/style-spec/types.ts index c2e6ff836dc..0f18b191f13 100644 --- a/src/style-spec/types.ts +++ b/src/style-spec/types.ts @@ -27,6 +27,21 @@ export type TransitionSpecification = { // Note: doesn't capture interpolatable vs. non-interpolatable types. +export type PropertyFunctionStop = [number, T]; +export type ZoomAndPropertyFunctionStop = [{zoom: number; value: string | number | boolean}, T]; + +/** + * @deprecated Use [Expressions](https://docs.mapbox.com/style-spec/reference/expressions/) syntax instead. +*/ +export type FunctionSpecification = { + stops: Array | ZoomAndPropertyFunctionStop>; + base?: number; + property?: string; + type?: 'identity' | 'exponential' | 'interval' | 'categorical'; + colorSpace?: 'rgb' | 'lab' | 'hcl'; + default?: T; +}; + export type CameraFunctionSpecification = | { type: 'exponential', stops: Array<[number, T]> } | { type: 'interval', stops: Array<[number, T]> }; @@ -51,6 +66,7 @@ export type PropertyValueSpecification = export type DataDrivenPropertyValueSpecification = | T + | FunctionSpecification | CameraFunctionSpecification | SourceFunctionSpecification | CompositeFunctionSpecification @@ -403,6 +419,12 @@ export type LineLayerSpecification = { "line-pattern"?: DataDrivenPropertyValueSpecification, "line-gradient"?: ExpressionSpecification, "line-trim-offset"?: [number, number], + /** + * @experimental This property is experimental and subject to change in future versions. + */ + "line-trim-fade-range"?: PropertyValueSpecification<[number, number]>, + "line-trim-color"?: PropertyValueSpecification, + "line-trim-color-transition"?: TransitionSpecification, "line-emissive-strength"?: PropertyValueSpecification, "line-emissive-strength-transition"?: TransitionSpecification, "line-border-width"?: DataDrivenPropertyValueSpecification, diff --git a/src/style/style.ts b/src/style/style.ts index 6e1bdce881f..b71dee72b07 100644 --- a/src/style/style.ts +++ b/src/style/style.ts @@ -70,6 +70,7 @@ import {RGBAImage} from '../util/image'; import type {ColorThemeSpecification, LayerSpecification, FilterSpecification, + ExpressionSpecification, StyleSpecification, ImportSpecification, LightSpecification, @@ -84,7 +85,7 @@ import type {ColorThemeSpecification, ConfigSpecification, SchemaSpecification, CameraSpecification -} from "../style-spec/types"; +} from '../style-spec/types'; import {evaluateColorThemeProperties} from '../util/lut'; // We're skipping validation errors with the `source.canvas` identifier in order @@ -181,6 +182,12 @@ export type Fragment = { config?: ConfigSpecification | null | undefined; }; +type StyleColorTheme = { + lut: LUT | null; + lutLoading: boolean; + colorTheme: ColorThemeSpecification | null; +}; + const MAX_IMPORT_DEPTH = 5; const defaultTransition = {duration: 300, delay: 0}; @@ -201,7 +208,10 @@ class Style extends Evented { disableElevatedTerrain: boolean | null | undefined; fog: Fog | null | undefined; camera: CameraSpecification; - lut: LUT | null; + _styleColorTheme: StyleColorTheme; + _styleColorThemeForScope: { + [_: string]: StyleColorTheme; + }; transition: TransitionSpecification; projection: ProjectionSpecification; @@ -220,22 +230,12 @@ class Style extends Evented { // Merged layers and sources _mergedOrder: Array; - _mergedLayers: { - [_: string]: StyleLayer; - }; - _mergedSourceCaches: { - [_: string]: SourceCache; - }; - _mergedOtherSourceCaches: { - [_: string]: SourceCache; - }; - _mergedSymbolSourceCaches: { - [_: string]: SourceCache; - }; + _mergedLayers: Record; + _mergedSlots: Array; + _mergedSourceCaches: Record; + _mergedOtherSourceCaches: Record; + _mergedSymbolSourceCaches: Record; _clipLayerIndices: Array; - _luts: { - [_: string]: LUT; - }; _request: Cancelable | null | undefined; _spriteRequest: Cancelable | null | undefined; @@ -364,7 +364,12 @@ class Style extends Evented { this._availableImages = []; this._order = []; this._markersNeedUpdate = false; - this._luts = {}; + this._styleColorTheme = { + lut: null, + lutLoading: false, + colorTheme: null + }; + this._styleColorThemeForScope = {}; this.options = options.configOptions ? options.configOptions : new Map(); this._configDependentLayers = options.configDependentLayers ? options.configDependentLayers : new Set(); @@ -722,7 +727,7 @@ class Style extends Evented { this._layers = {}; this._serializedLayers = {}; for (const layer of layers) { - const styleLayer = createStyleLayer(layer, this.scope, this.lut, this.options); + const styleLayer = createStyleLayer(layer, this.scope, this._styleColorTheme.lut, this.options); if (styleLayer.isConfigDependent) this._configDependentLayers.add(styleLayer.fqid); styleLayer.setEventedParent(this, {layer: {id: styleLayer.id}}); this._layers[styleLayer.id] = styleLayer; @@ -742,14 +747,8 @@ class Style extends Evented { const terrain = this.stylesheet.terrain; if (terrain) { - // This workaround disables terrain and hillshade - // if there is noise in the Canvas2D operations used for image decoding. - if (this.disableElevatedTerrain === undefined) - this.disableElevatedTerrain = browser.hasCanvasFingerprintNoise(); - - if (this.disableElevatedTerrain) { - warnOnce('Terrain and hillshade are disabled because of Canvas2D limitations when fingerprinting protection is enabled (e.g. in private browsing mode).'); - } else if (!this.terrainSetForDrapingOnly()) { + this.checkCanvasFingerprintNoise(); + if (!this.terrainSetForDrapingOnly()) { this._createTerrain(terrain, DrapeRenderMode.elevated); } } @@ -777,14 +776,18 @@ class Style extends Evented { } }; - if (this.stylesheet['color-theme']) { - this._loadColorTheme(this.stylesheet['color-theme']).then(() => { + const colorTheme = this.stylesheet['color-theme']; + this._styleColorTheme.colorTheme = colorTheme; + if (colorTheme) { + const data = this._evaluateColorThemeData(colorTheme); + this._loadColorTheme(data).then(() => { proceedWithStyleLoad(); }).catch((e) => { warnOnce(`Couldn\'t load color theme from the stylesheet: ${e}`); proceedWithStyleLoad(); }); } else { + this._styleColorTheme.lut = null; proceedWithStyleLoad(); } } @@ -802,6 +805,9 @@ class Style extends Evented { let projection; let transition; let camera; + const styleColorThemeForScope: { + [_: string]: StyleColorTheme; + } = {}; // Reset terrain that might have been set by a previous merge if (this.terrain && this.terrain.scope !== this.scope) { @@ -841,12 +847,15 @@ class Style extends Evented { if (style.stylesheet.transition != null) transition = style.stylesheet.transition; + + styleColorThemeForScope[style.scope] = style._styleColorTheme; }); this.light = light; this.ambientLight = ambientLight; this.directionalLight = directionalLight; this.fog = fog; + this._styleColorThemeForScope = styleColorThemeForScope; if (terrain === null) { delete this.terrain; @@ -969,25 +978,16 @@ class Style extends Evented { } mergeLayers() { - const slots: { - [key: string]: StyleLayer[]; - } = {}; + const slots: Record = {}; const mergedOrder: StyleLayer[] = []; - const mergedLayers: { - [key: string]: StyleLayer; - } = {}; - const luts: { - [key: string]: LUT; - } = {}; + const mergedLayers: Record = {}; + this._mergedSlots = []; this._has3DLayers = false; this._hasCircleLayers = false; this._hasSymbolLayers = false; this.forEachFragmentStyle((style: Style) => { - if (style.lut) { - luts[style.scope] = style.lut; - } for (const layerId of style._order) { const layer = style._layers[layerId]; if (layer.type === 'slot') { @@ -1014,6 +1014,7 @@ class Style extends Evented { if (layer.type === 'slot') { const slotName = getNameFromFQID(layer.id); if (slots[slotName]) sort(slots[slotName]); + this._mergedSlots.push(slotName); } else { const fqid = makeFQID(layer.id, layer.scope); this._mergedOrder.push(fqid); @@ -1031,7 +1032,6 @@ class Style extends Evented { sort(mergedOrder); this._mergedLayers = mergedLayers; - this._luts = luts; this.updateDrapeFirstLayers(); this._buildingIndex.processLayersChanged(); } @@ -1050,36 +1050,47 @@ class Style extends Evented { return this; } - _loadColorTheme(colorization?: ColorThemeSpecification | null): Promise { - if (!colorization) { - return Promise.resolve(); - } - if (!colorization.data) { - return Promise.resolve(); + _evaluateColorThemeData(theme: ColorThemeSpecification): string | null { + if (!theme.data) { + return null; } + const properties = evaluateColorThemeProperties(this.scope, theme, this.options); + return properties.get('data'); + } - const properties = evaluateColorThemeProperties(this.scope, colorization, this.options); - let data = properties.get('data'); - const dataURLPrefix = 'data:image/png;base64,'; + _loadColorTheme(colorThemeData: string): Promise { + this._styleColorTheme.lutLoading = true; + return new Promise((resolve, reject) => { + const dataURLPrefix = 'data:image/png;base64,'; - if (!data.startsWith(dataURLPrefix)) { - data = dataURLPrefix + data; - } - // Reserved image name, which references the LUT in the image manager - const styleLutName = 'mapbox-reserved-lut'; + if (colorThemeData.length === 0) { + this._styleColorTheme.lut = null; + this._styleColorTheme.lutLoading = false; + resolve(); + return; + } - return new Promise((resolve, reject) => { + if (!colorThemeData.startsWith(dataURLPrefix)) { + colorThemeData = dataURLPrefix + colorThemeData; + } + // Reserved image name, which references the LUT in the image manager + const styleLutName = 'mapbox-reserved-lut'; - this.map.loadImage(data, (err, bitmap) => { - if (err) { - reject(new Error(`${err.message}`)); - return; - } - if (bitmap.height > 32) { + const lutImage = new Image(); + lutImage.src = colorThemeData; + lutImage.onerror = () => { + this._styleColorTheme.lutLoading = false; + reject(new Error('Failed to load image data')); + + }; + lutImage.onload = () => { + this._styleColorTheme.lutLoading = false; + const {width, height, data} = browser.getImageData(lutImage); + if (height > 32) { reject(new Error('The height of the image must be less than or equal to 32 pixels.')); return; } - if (bitmap.width !== bitmap.height * bitmap.height) { + if (width !== height * height) { reject(new Error('The width of the image must be equal to the height squared.')); return; } @@ -1087,27 +1098,25 @@ class Style extends Evented { if (this.getImage(styleLutName)) { this.removeImage(styleLutName); } - const {width, height, data} = browser.getImageData(bitmap); this.addImage(styleLutName, {data: new RGBAImage({width, height}, data), pixelRatio: 1, sdf: false, version: 0}); const image = this.imageManager.getImage(styleLutName, this.scope); if (!image) { reject(new Error('Missing LUT image.')); } else { - this.lut = { - // @ts-expect-error - TS2353 - Object literal may only specify known properties, and 'name' does not exist in type 'LUT'. - name: styleLutName, - image: image.data + this._styleColorTheme.lut = { + image: image.data, + data: colorThemeData }; - // @ts-expect-error - TS2794 - Expected 1 arguments, but got 0. Did you forget to include 'void' in your type argument to 'Promise'? resolve(); } - }); + }; }); } getLut(scope: string): LUT | null { - return this._luts ? this._luts[scope] : null; + const styleColorTheme = this._styleColorThemeForScope[scope]; + return styleColorTheme ? styleColorTheme.lut : null; } setProjection(projection?: ProjectionSpecification | null) { @@ -1205,6 +1214,9 @@ class Style extends Evented { if (!this.modelManager.isLoaded()) return false; + if (this._styleColorTheme.lutLoading) + return false; + for (const {style} of this.fragments) { if (!style.loaded()) return false; } @@ -1423,7 +1435,7 @@ class Style extends Evented { if (!programIds) continue; for (const programId of programIds) { - const params = layer.getDefaultProgramParams(programId, parameters.zoom, this.lut); + const params = layer.getDefaultProgramParams(programId, parameters.zoom, this._styleColorTheme.lut); if (params) { painter.style = this; if (this.fog) { @@ -2049,6 +2061,15 @@ class Style extends Evented { this.fog.updateConfig(this.options); } + this.forEachFragmentStyle((style: Style) => { + if (style._styleColorTheme.colorTheme) { + const data = style._evaluateColorThemeData(style._styleColorTheme.colorTheme); + if (!style._styleColorTheme.lut || (style._styleColorTheme.lut && data !== style._styleColorTheme.lut.data)) { + style.setColorTheme(style._styleColorTheme.colorTheme); + } + } + }); + this._changes.setDirty(); } @@ -2073,7 +2094,7 @@ class Style extends Evented { let layer; if (layerObject.type === 'custom') { if (emitValidationErrors(this, validateCustomStyleLayer(layerObject))) return; - layer = createStyleLayer(layerObject, this.scope, this.lut, this.options); + layer = createStyleLayer(layerObject, this.scope, this._styleColorTheme.lut, this.options); } else { if (typeof layerObject.source === 'object') { this.addSource(id, layerObject.source); @@ -2085,7 +2106,7 @@ class Style extends Evented { if (this._validate(validateLayer, `layers.${id}`, layerObject, {arrayIndex: -1}, options)) return; - layer = createStyleLayer(layerObject, this.scope, this.lut, this.options); + layer = createStyleLayer(layerObject, this.scope, this._styleColorTheme.lut, this.options); this._validateLayer(layer); layer.setEventedParent(this, {layer: {id}}); @@ -2297,6 +2318,11 @@ class Style extends Evented { this._updateLayer(layer); } + getSlots(): string[] { + this._checkLoaded(); + return this._mergedSlots; + } + setSlot(layerId: string, slot?: string | null) { this._checkLoaded(); @@ -2720,10 +2746,10 @@ class Style extends Evented { querySourceFeatures( sourceID: string, params?: { - sourceLayer: string | null | undefined; - filter?: Array | null | undefined; + sourceLayer?: string; + filter?: FilterSpecification | ExpressionSpecification; validate?: boolean; - } | null, + }, ): Array { if (params && params.filter) { this._validate(validateFilter, 'querySourceFeatures.filter', params.filter, null, params); @@ -2785,6 +2811,15 @@ class Style extends Evented { this.setTerrain(mockTerrainOptions, DrapeRenderMode.deferred); } + checkCanvasFingerprintNoise() { + // This workaround disables terrain and hillshade + // if there is noise in the Canvas2D operations used for image decoding. + if (this.disableElevatedTerrain === undefined) { + this.disableElevatedTerrain = browser.hasCanvasFingerprintNoise(); + if (this.disableElevatedTerrain) warnOnce('Terrain and hillshade are disabled because of Canvas2D limitations when fingerprinting protection is enabled (e.g. in private browsing mode).'); + } + } + // eslint-disable-next-line no-warning-comments // TODO: generic approach for root level property: light, terrain, skybox. // It is not done here to prevent rebasing issues. @@ -2809,9 +2844,13 @@ class Style extends Evented { return; } + this.checkCanvasFingerprintNoise(); + let options: TerrainSpecification = terrainOptions; const isUpdating = terrainOptions.source == null; if (drapeRenderMode === DrapeRenderMode.elevated) { + if (this.disableElevatedTerrain) return; + // Input validation and source object unrolling if (typeof options.source === 'object') { const id = 'terrain-dem-src'; @@ -2919,6 +2958,34 @@ class Style extends Evented { this._markersNeedUpdate = true; } + setColorTheme(colorTheme?: ColorThemeSpecification) { + this._checkLoaded(); + + const updateStyle = () => { + for (const layerId in this._layers) { + const layer = this._layers[layerId]; + layer.lut = this._styleColorTheme.lut; + } + for (const id in this._sourceCaches) { + this._sourceCaches[id].clearTiles(); + } + }; + + this._styleColorTheme.colorTheme = colorTheme; + if (!colorTheme) { + this._styleColorTheme.lut = null; + updateStyle(); + return; + } + + const data = this._evaluateColorThemeData(colorTheme); + this._loadColorTheme(data).then(() => { + updateStyle(); + }).catch((e) => { + warnOnce(`Couldn\'t set color theme: ${e}`); + }); + } + _getTransitionParameters(transition?: TransitionSpecification | null): TransitionParameters { return { now: browser.now(), diff --git a/src/style/style_layer/line_style_layer_properties.ts b/src/style/style_layer/line_style_layer_properties.ts index a9d45224f99..6f5a9a3cf0d 100644 --- a/src/style/style_layer/line_style_layer_properties.ts +++ b/src/style/style_layer/line_style_layer_properties.ts @@ -49,6 +49,8 @@ export type PaintProps = { "line-pattern": DataDrivenProperty; "line-gradient": ColorRampProperty; "line-trim-offset": DataConstantProperty<[number, number]>; + "line-trim-fade-range": DataConstantProperty<[number, number]>; + "line-trim-color": DataConstantProperty; "line-emissive-strength": DataConstantProperty; "line-border-width": DataDrivenProperty; "line-border-color": DataDrivenProperty; @@ -68,6 +70,8 @@ const paint: Properties = new Properties({ "line-pattern": new DataDrivenProperty(styleSpec["paint_line"]["line-pattern"]), "line-gradient": new ColorRampProperty(styleSpec["paint_line"]["line-gradient"]), "line-trim-offset": new DataConstantProperty(styleSpec["paint_line"]["line-trim-offset"]), + "line-trim-fade-range": new DataConstantProperty(styleSpec["paint_line"]["line-trim-fade-range"]), + "line-trim-color": new DataConstantProperty(styleSpec["paint_line"]["line-trim-color"]), "line-emissive-strength": new DataConstantProperty(styleSpec["paint_line"]["line-emissive-strength"]), "line-border-width": new DataDrivenProperty(styleSpec["paint_line"]["line-border-width"]), "line-border-color": new DataDrivenProperty(styleSpec["paint_line"]["line-border-color"]), diff --git a/src/ui/map.ts b/src/ui/map.ts index d02b67749fc..3b20b62aa47 100644 --- a/src/ui/map.ts +++ b/src/ui/map.ts @@ -81,10 +81,12 @@ import type { CameraSpecification, ImportSpecification, ConfigSpecification, - SchemaSpecification + SchemaSpecification, + ColorThemeSpecification, + ExpressionSpecification } from '../style-spec/types'; import type StyleLayer from '../style/style_layer'; -import type {Source} from '../source/source'; +import type {Source, SourceClass} from '../source/source'; import type {EasingOptions} from './camera'; import type {ContextOptions} from '../gl/context'; import type {QueryFeature, QueryRenderedFeaturesParams} from '../source/query_features'; @@ -92,6 +94,7 @@ import type {QueryFeature, QueryRenderedFeaturesParams} from '../source/query_fe import {TrackedParameters} from '../tracked-parameters/tracked_parameters'; import {TrackedParametersMock} from '../tracked-parameters/tracked_parameters_base'; import type {ITrackedParameters} from '../tracked-parameters/tracked_parameters_base'; +import type {Callback} from 'src/types/callback'; export type ControlPosition = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'; /* eslint-disable no-use-before-define */ @@ -114,6 +117,12 @@ export type SetStyleOptions = { localIdeographFontFamily: StyleOptions['localIdeographFontFamily']; }; +type DelegatedListener = { + layers: Set; + listener: Listener; + delegates: {[K in MapEvent]?: Listener}; +}; + export type FeatureSelector = { id: string | number; source: string; @@ -126,7 +135,7 @@ export const AVERAGE_ELEVATION_EASE_THRESHOLD = 1; // meters export const AVERAGE_ELEVATION_CHANGE_THRESHOLD = 1e-4; // meters export type MapOptions = { - style?: StyleSpecification | string | undefined; + style?: StyleSpecification | string; config?: { [key: string]: ConfigSpecification; }; @@ -147,10 +156,10 @@ export type MapOptions = { maxBounds?: LngLatBoundsLike; fitBoundsOptions?: EasingOptions; scrollZoom?: boolean; - minZoom?: number | null | undefined; - maxZoom?: number | null | undefined; - minPitch?: number | null | undefined; - maxPitch?: number | null | undefined; + minZoom?: number; + maxZoom?: number; + minPitch?: number; + maxPitch?: number; boxZoom?: boolean; dragRotate?: boolean; dragPan?: boolean | DragPanOptions; @@ -170,8 +179,8 @@ export type MapOptions = { maxTileCacheSize?: number; transformRequest?: RequestTransformFunction; accessToken?: string; - testMode?: boolean | null | undefined; - locale?: any; + testMode?: boolean; + locale?: Partial; language?: string; worldview?: string; crossSourceCollisions?: boolean; @@ -443,7 +452,7 @@ export class Map extends Camera { _antialias: boolean; _refreshExpiredTiles: boolean; _hash: Hash; - _delegatedListeners: any; + _delegatedListeners: {[type: string]: DelegatedListener[]}; _fullscreenchangeEvent: 'fullscreenchange' | 'webkitfullscreenchange'; _isInitialLoad: boolean; _shouldCheckAccess: boolean; @@ -460,7 +469,7 @@ export class Map extends Camera { _localIdeographFontFamily: string; _localFontFamily: string | null | undefined; _requestManager: RequestManager; - _locale: any; + _locale: Partial; _removed: boolean; _speedIndexTiming: boolean; _clickTolerance: number; @@ -752,7 +761,7 @@ export class Map extends Camera { this.on('style.load', () => { if (this.transform.unmodified) { - this.jumpTo((this.style.stylesheet as any)); + this.jumpTo((this.style.stylesheet as unknown)); } this._postStyleLoadEvent(); }); @@ -919,7 +928,7 @@ export class Map extends Camera { * const mapDiv = document.getElementById('map'); * if (mapDiv.style.visibility === true) map.resize(); */ - resize(eventData?: any): this { + resize(eventData?: object): this { this._updateContainerDimensions(); // do nothing if container remained the same size @@ -1504,7 +1513,7 @@ export class Map extends Camera { return (this.handlers && this.handlers._isDragging()) || false; } - _createDelegatedListener(type: MapEvent, layers: Array, listener: any): any { + _createDelegatedListener(type: MapEvent, layers: Array, listener: Listener): DelegatedListener { if (type === 'mouseenter' || type === 'mouseover') { let mousein = false; const mousemove = (e: MapMouseEvent) => { @@ -1673,9 +1682,12 @@ export class Map extends Camera { * @see [Example: Create a hover effect](https://docs.mapbox.com/mapbox-gl-js/example/hover-styles/) * @see [Example: Display popup on click](https://docs.mapbox.com/mapbox-gl-js/example/popup-on-click/) */ - on(type: MapEvent, layerIds: any, listener?: Listener): this { - if (listener === undefined) { - return super.on(type, layerIds); + on(type: MapEvent, listener: Listener): this; + on(type: MapEvent, layerIds: string | string[], listener: Listener): this; + + on(type: MapEvent, layerIds: string | string[] | Listener, listener?: Listener): this { + if (typeof layerIds === 'function' || listener === undefined) { + return super.on(type, layerIds as Listener); } if (!Array.isArray(layerIds)) { @@ -1697,7 +1709,7 @@ export class Map extends Camera { this._delegatedListeners[type].push(delegatedListener); for (const event in delegatedListener.delegates) { - this.on((event as any), delegatedListener.delegates[event]); + this.on(event as MapEvent, delegatedListener.delegates[event]); } return this; @@ -1747,9 +1759,9 @@ export class Map extends Camera { once(type: MapEvent, layerIds: string | string[]): Promise; once(type: MapEvent, layerIds: string | string[], listener: Listener): this; - once(type: MapEvent, layerIds?: any, listener?: Listener): this | Promise { - if (listener === undefined) { - return super.once(type, layerIds); + once(type: MapEvent, layerIds?: string | string[] | Listener, listener?: Listener): this | Promise { + if (typeof layerIds === 'function' || listener === undefined) { + return super.once(type, layerIds as Listener); } if (!Array.isArray(layerIds)) { @@ -1767,7 +1779,7 @@ export class Map extends Camera { const delegatedListener = this._createDelegatedListener(type, layerIds, listener); for (const event in delegatedListener.delegates) { - this.once((event as any), delegatedListener.delegates[event]); + this.once(event as MapEvent, delegatedListener.delegates[event]); } return this; @@ -1798,19 +1810,23 @@ export class Map extends Camera { * }); * @see [Example: Create a draggable point](https://docs.mapbox.com/mapbox-gl-js/example/drag-a-point/) */ - off(type: MapEvent, layerIds: any, listener?: Listener): this { - if (listener === undefined) { - return super.off(type, layerIds); + off(type: MapEvent, listener: Listener): this; + off(type: MapEvent, layerIds: string | string[], listener: Listener): this; + + off(type: MapEvent, layerIds: string | string[] | Listener, listener?: Listener): this { + if (typeof layerIds === 'function' || listener === undefined) { + return super.off(type, layerIds as Listener); } - layerIds = new Set(Array.isArray(layerIds) ? layerIds : [layerIds]); - for (const layerId of layerIds) { + const uniqLayerIds = new Set(Array.isArray(layerIds) ? layerIds : [layerIds]); + + for (const layerId of uniqLayerIds) { if (!this._isValidId(layerId)) { return this; } } - const areLayerArraysEqual = (hash1: Set, hash2: Set) => { + const areLayerIdsEqual = (hash1: Set, hash2: Set) => { if (hash1.size !== hash2.size) { return false; // at-least 1 arr has duplicate value(s) } @@ -1822,12 +1838,12 @@ export class Map extends Camera { return true; }; - const removeDelegatedListeners = (listeners: Array) => { + const removeDelegatedListeners = (listeners: Array) => { for (let i = 0; i < listeners.length; i++) { const delegatedListener = listeners[i]; - if (delegatedListener.listener === listener && areLayerArraysEqual(delegatedListener.layers, layerIds)) { + if (delegatedListener.listener === listener && areLayerIdsEqual(delegatedListener.layers, uniqLayerIds)) { for (const event in delegatedListener.delegates) { - this.off((event as any), delegatedListener.delegates[event]); + this.off(event as MapEvent, delegatedListener.delegates[event]); } listeners.splice(i, 1); return this; @@ -1938,7 +1954,7 @@ export class Map extends Camera { } if (options === undefined && geometry !== undefined && !(geometry instanceof Point) && !Array.isArray(geometry)) { - options = (geometry as any); + options = geometry; geometry = undefined; } @@ -1996,10 +2012,10 @@ export class Map extends Camera { querySourceFeatures( sourceId: string, parameters?: { - sourceLayer: string | null | undefined; - filter?: Array | null | undefined; + sourceLayer?: string; + filter?: FilterSpecification | ExpressionSpecification; validate?: boolean; - } | null, + }, ): Array { if (!this._isValidId(sourceId)) { return []; @@ -2107,7 +2123,7 @@ export class Map extends Camera { return str; } - _updateStyle(style: StyleSpecification | string | null, options?: SetStyleOptions): this { + _updateStyle(style?: StyleSpecification | string, options?: SetStyleOptions): this { if (this.style) { this.style.setEventedParent(null); this.style._remove(); @@ -2123,9 +2139,8 @@ export class Map extends Camera { delete styleOptions.config; } - this.style = new Style(this, styleOptions) - .setEventedParent(this, {style: this.style}) - .load(style); + this.style = new Style(this, styleOptions).load(style); + this.style.setEventedParent(this, {style: this.style}); } this._updateTerrain(); @@ -2274,7 +2289,7 @@ export class Map extends Camera { * @param {Function} SourceType A {@link Source} constructor. * @param {Function} callback Called when the source type is ready or with an error argument if there is an error. */ - addSourceType(name: string, SourceType: any, callback: any) { + addSourceType(name: string, SourceType: SourceClass, callback: Callback) { this._lazyInitEmptyStyle(); this.style.addSourceType(name, SourceType, callback); } @@ -2453,10 +2468,8 @@ export class Map extends Camera { 'The map has no image with that id. If you are adding a new image use `map.addImage(...)` instead.'))); return; } - const imageData = (image instanceof HTMLImageElement || (ImageBitmap && image instanceof ImageBitmap)) ? browser.getImageData(image) : image; - const {width, height} = imageData; - // Flow can't refine the type enough to exclude ImageBitmap - const data = ((imageData as any).data as Uint8Array | Uint8ClampedArray); + const imageData = (image instanceof HTMLImageElement || (ImageBitmap && image instanceof ImageBitmap)) ? browser.getImageData(image) : image as ImageData; + const {width, height, data} = imageData; if (width === undefined || height === undefined) { this.fire(new ErrorEvent(new Error( @@ -2536,7 +2549,7 @@ export class Map extends Camera { * * @see [Example: Add an icon to the map](https://www.mapbox.com/mapbox-gl-js/example/add-image/) */ - loadImage(url: string, callback: any) { + loadImage(url: string, callback: Callback) { // @ts-expect-error - TS2345 - Argument of type 'string' is not assignable to parameter of type '"Unknown" | "Style" | "Source" | "Tile" | "Glyphs" | "SpriteImage" | "SpriteJSON" | "Image" | "Model"'. getImage(this._requestManager.transformRequest(url, ResourceType.Image), (err, img) => { callback(err, img instanceof HTMLImageElement ? browser.getImageData(img) : img); @@ -3034,6 +3047,18 @@ export class Map extends Camera { return this.style.getOwnLayer(id); } + /** + * Returns the IDs of all slots in the map's style. + * + * @returns {Array} The IDs of all slots in the map's style. + * + * @example + * const slots = map.getSlots(); + */ + getSlots(): Array { + return this.style.getSlots(); + } + /** * Sets the zoom extent for the specified style layer. The zoom extent includes the * [minimum zoom level](https://docs.mapbox.com/mapbox-gl-js/style-spec/#layer-minzoom) @@ -3474,6 +3499,24 @@ export class Map extends Camera { return this.style ? this.style.getFog() : null; } + /** + * Sets the color-theme property of the style. + * + * @param {ColorThemeSpecification} colorTheme The color-theme properties to set. + * If `null` or `undefined` is provided, this function call removes the color-theme from the map. + * Note: Calling this function triggers a full reload of tiles. + * @returns {Map} Returns itself to allow for method chaining. + * @example + * map.setColorTheme({ + * "data": "iVBORw0KGgoAA..." + * }); + */ + setColorTheme(colorTheme?: ColorThemeSpecification): this { + this._lazyInitEmptyStyle(); + this.style.setColorTheme(colorTheme); + return this._update(true); + } + /** * Sets the camera property of the style. * diff --git a/src/util/evented.ts b/src/util/evented.ts index 4b5df1c8983..cffcd86377e 100644 --- a/src/util/evented.ts +++ b/src/util/evented.ts @@ -24,9 +24,10 @@ function _removeEventListener(type: string, listener: Listener, listenerList: Li } export class Event { + target: unknown; readonly type: string; - constructor(type: string, data: any = {}) { + constructor(type: string, data: object = {}) { extend(this, data); this.type = type; } @@ -56,8 +57,8 @@ export class ErrorEvent extends Event { export class Evented { _listeners: Listeners; _oneTimeListeners: Listeners; - _eventedParent: Evented | null | undefined; - _eventedParentData: any | (() => any) | null | undefined; + _eventedParent?: Evented; + _eventedParentData?: unknown | (() => unknown); /** * Adds a listener to a specified event type. @@ -113,7 +114,7 @@ export class Evented { return this; } - fire(event: Event | string, properties?: any): this { + fire(event: Event | string, properties?: object): this { // Compatibility with (type: string, properties: Object) signature from previous versions. // See https://github.com/mapbox/mapbox-gl-js/issues/6522, // https://github.com/mapbox/mapbox-gl-draw/issues/766 @@ -124,7 +125,7 @@ export class Evented { const type = event.type; if (this.listens(type)) { - (event as any).target = this; + event.target = this; // make sure adding or removing listeners inside other listeners won't cause an infinite loop const listeners = this._listeners && this._listeners[type] ? this._listeners[type].slice() : []; @@ -141,10 +142,11 @@ export class Evented { const parent = this._eventedParent; if (parent) { - extend( - event, - typeof this._eventedParentData === 'function' ? this._eventedParentData() : this._eventedParentData - ); + const eventedParentData = typeof this._eventedParentData === 'function' ? + this._eventedParentData() : + this._eventedParentData; + + extend(event, eventedParentData); parent.fire(event); } @@ -178,7 +180,7 @@ export class Evented { * @returns {Object} `this` * @private */ - setEventedParent(parent?: Evented | null, data?: any | (() => any)): this { + setEventedParent(parent?: Evented, data?: unknown | (() => unknown)): this { this._eventedParent = parent; this._eventedParentData = data; diff --git a/src/util/lut.ts b/src/util/lut.ts index a2faf9bd5d0..8190c051cdc 100644 --- a/src/util/lut.ts +++ b/src/util/lut.ts @@ -9,6 +9,7 @@ import type {ConfigOptions} from '../../src/style/properties'; export type LUT = { image: RGBAImage; + data: string; texture?: Texture3D; }; diff --git a/src/util/tile_request_cache.ts b/src/util/tile_request_cache.ts index acdab5e5411..7ae5b54a695 100644 --- a/src/util/tile_request_cache.ts +++ b/src/util/tile_request_cache.ts @@ -62,6 +62,15 @@ function prepareBody(response: Response, callback: (body?: Blob | ReadableStream } } +// https://fetch.spec.whatwg.org/#null-body-status +function isNullBodyStatus(status: Response["status"]): boolean { + if (status === 200 || status === 404) { + return false; + } + + return [101, 103, 204, 205, 304].includes(status); +} + export function cachePut(request: Request, response: Response, requestTime: number) { cacheOpen(); if (!sharedCache) return; @@ -100,7 +109,7 @@ export function cachePut(request: Request, response: Response, requestTime: numb } prepareBody(response, body => { - const clonedResponse = new Response(body, options); + const clonedResponse = new Response(isNullBodyStatus(response.status) ? null : body, options); cacheOpen(); if (!sharedCache) return; diff --git a/test/build/typings/compatibility-test.ts b/test/build/typings/compatibility-test.ts index a9065febb33..fee6fcb784b 100644 --- a/test/build/typings/compatibility-test.ts +++ b/test/build/typings/compatibility-test.ts @@ -190,7 +190,6 @@ map.on("load", function () { layout: { "icon-image": "marker-15", "text-field": ["get", "property"], - // @ts-expect-error - incompatible "text-max-width": { stops: [ [10, 2], @@ -617,7 +616,6 @@ var mapStyle: mapboxgl.Style = { layout: { "text-field": "{name_en}", "text-font": ["DIN Offc Pro Bold", "Arial Unicode MS Bold"], - // @ts-expect-error - incompatible "text-size": { stops: [ [4, 9], @@ -671,7 +669,6 @@ var mapStyle: mapboxgl.Style = { ], "text-letter-spacing": 0.15, "text-max-width": 7, - // @ts-expect-error - incompatible "text-size": { stops: [ [4, 10], diff --git a/test/build/typings/index.ts b/test/build/typings/index.ts index a102fe5e6fa..c1619d762f7 100644 --- a/test/build/typings/index.ts +++ b/test/build/typings/index.ts @@ -144,8 +144,8 @@ map.setTerrain({'source': 'mapbox-dem', 'exaggeration': 1.5}); // Query features // -const features1 = map.queryRenderedFeatures([0, 0], {layers: ['layer-id']}); -const features2 = map.querySourceFeatures('sourceId', {sourceLayer: 'sourceLayer', filter: [], validate: true}); +const features1 = map.queryRenderedFeatures([0, 0], {layers: ['layer-id'], filter: ['>=', 'area', 80000], validate: true}); +const features2 = map.querySourceFeatures('sourceId', {sourceLayer: 'sourceLayer', filter: ['>=', 'area', 80000], validate: true}); // // Set state diff --git a/test/build/typings/package-lock.json b/test/build/typings/package-lock.json index c0ba19fc8de..08c04ac5f96 100644 --- a/test/build/typings/package-lock.json +++ b/test/build/typings/package-lock.json @@ -15,7 +15,7 @@ }, "../../..": { "name": "@mapbox/mapbox-gl-private", - "version": "3.5.0-alpha.1", + "version": "3.5.0-beta.1", "license": "SEE LICENSE IN LICENSE.txt", "workspaces": [ "src/style-spec" @@ -32,7 +32,7 @@ "csscolorparser": "~1.0.3", "earcut": "^2.2.4", "fflate": "^0.8.1", - "geojson-vt": "^3.2.1", + "geojson-vt": "^4.0.2", "gl-matrix": "^3.4.3", "grid-index": "^1.1.0", "kdbush": "^4.0.1", @@ -61,14 +61,15 @@ "@rollup/plugin-terser": "^0.4.4", "@tweakpane/core": "^2.0.3", "@types/geojson": "^7946.0.14", + "@types/geojson-vt": "^3.2.5", "@types/jest": "^29.5.12", "@types/mapbox__point-geometry": "^0.1.4", "@types/mapbox__vector-tile": "^1.3.4", - "@types/node": "^20.14.2", + "@types/node": "^20.14.3", "@types/offscreencanvas": "^2019.7.3", "@types/pbf": "^3.0.5", - "@typescript-eslint/eslint-plugin": "^7.12.0", - "@typescript-eslint/parser": "^7.12.0", + "@typescript-eslint/eslint-plugin": "^7.13.1", + "@typescript-eslint/parser": "^7.13.1", "@vitest/browser": "^1.6.0", "@vitest/ui": "^1.6.0", "address": "^2.0.2", @@ -82,13 +83,13 @@ "dts-bundle-generator": "^9.5.1", "ejs": "^3.1.10", "envify": "^4.1.0", - "esbuild": "^0.21.4", + "esbuild": "^0.21.5", "eslint": "^8.57.0", "eslint-config-mourner": "^3.0.0", "eslint-import-resolver-typescript": "^3.6.1", "eslint-plugin-html": "^8.1.1", "eslint-plugin-import": "^2.29.1", - "eslint-plugin-jsdoc": "^48.2.8", + "eslint-plugin-jsdoc": "^48.2.12", "glob": "^10.4.1", "is-builtin-module": "^4.0.0", "jest-extended": "^4.0.2", @@ -107,7 +108,6 @@ "postcss-cli": "^11.0.0", "postcss-inline-svg": "^6.0.0", "pretty-bytes": "^6.0.0", - "puppeteer-core": "^22.10.0", "qrcode-terminal": "^0.12.0", "rollup": "^4.18.0", "rollup-plugin-esbuild": "^6.1.1", @@ -117,12 +117,13 @@ "st": "^3.0.0", "stylelint": "^16.6.1", "stylelint-config-standard": "^36.0.0", - "tape": "^5.7.5", + "tape": "^5.8.1", "tape-filter": "^1.0.4", "testem": "^3.14.0", - "tsx": "^4.12.0", - "typescript": "^5.4.5", - "typescript-eslint": "^7.12.0", + "tsx": "^4.15.6", + "typescript": "^5.5.2", + "typescript-eslint": "^7.13.1", + "utility-types": "^3.11.0", "vite-plugin-arraybuffer": "^0.0.7", "vitest": "^1.6.0" } diff --git a/test/ignores/all.js b/test/ignores/all.js index 8c789552664..3e77daa6903 100644 --- a/test/ignores/all.js +++ b/test/ignores/all.js @@ -2,6 +2,8 @@ const todo = [ // "https://github.com/mapbox/mapbox-gl-js/issues/2716 "query-tests/regressions/mapbox-gl-js#4494", + // To be ported: https://mapbox.atlassian.net/browse/GLJS-892 + "query-tests/symbol/above-horizon", // https://github.com/mapbox/mapbox-gl-js/issues/7207 "render-tests/fill-pattern/update-feature-state", diff --git a/test/integration/lib/operation-handlers.js b/test/integration/lib/operation-handlers.js index 3d958c25613..94dc62c5583 100644 --- a/test/integration/lib/operation-handlers.js +++ b/test/integration/lib/operation-handlers.js @@ -216,15 +216,6 @@ export const operationHandlers = { updateGeoJSONData(map, [sourceId, data], doneCb) { map.getSource(sourceId).updateData(data); doneCb(); - }, - setColorTheme(map, params, doneCb) { - // Update this to setColorTheme after - // https://mapbox.atlassian.net/browse/GLJS-842 is implemented - const style = map.getStyle(); - style["color-theme"] = params[0]; - this.setStyle(map, [style], () => { - doneCb(); - }); } }; diff --git a/test/integration/query-tests/symbol/above-horizon/expected.json b/test/integration/query-tests/symbol/above-horizon/expected.json new file mode 100644 index 00000000000..850ba34f733 --- /dev/null +++ b/test/integration/query-tests/symbol/above-horizon/expected.json @@ -0,0 +1,44 @@ +[ + { + "type": "Feature", + "id": "feature3", + "geometry": { + "type": "Point", + "coordinates": [ + -0.0789642333984375, + 52.75000935103216 + ] + }, + "properties": {}, + "source": "geo", + "state": {} + }, + { + "type": "Feature", + "id": "feature2", + "geometry": { + "type": "Point", + "coordinates": [ + -0.17500877380371095, + 51.99999569006238 + ] + }, + "properties": {}, + "source": "geo", + "state": {} + }, + { + "type": "Feature", + "id": "feature1", + "geometry": { + "type": "Point", + "coordinates": [ + -0.12765169143676759, + 51.50732011286516 + ] + }, + "properties": {}, + "source": "geo", + "state": {} + } +] \ No newline at end of file diff --git a/test/integration/query-tests/symbol/above-horizon/style.json b/test/integration/query-tests/symbol/above-horizon/style.json new file mode 100644 index 00000000000..04c78819485 --- /dev/null +++ b/test/integration/query-tests/symbol/above-horizon/style.json @@ -0,0 +1,82 @@ +{ + "version": 8, + "metadata": { + "test": { + "width": 200, + "height": 700, + "collisionDebug": true, + "operations": [ + ["wait"], + ["wait"] + ], + "queryGeometry": [ + [ + 0, + 0 + ], + [ + 200, + 700 + ] + ] + } + }, + "glyphs": "local://glyphs/{fontstack}/{range}.pbf", + "sprite": "local://sprites/sprite", + "sources": { + "geo": { + "type": "geojson", + "data": { + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "id": "feature1", + "geometry": { + "type": "Point", + "coordinates": [ -0.1276474, 51.5073219] + } + }, + { + "type": "Feature", + "id": "feature2", + "geometry": { + "type": "Point", + "coordinates": [ -0.175, 52.00] + } + }, + { + "type": "Feature", + "id": "feature3", + "geometry": { + "type": "Point", + "coordinates": [ -0.079, 52.75] + } + } + ] + } + } + }, + "pitch": 85, + "zoom": 12.0, + "bearing": 0, + "center": [ + -0.1276474, + 51.5073219 + ], + "layers": [ + { + "id": "testing", + "layout": { + "icon-image": "building-12", + "icon-size": 3.0, + "icon-anchor": "bottom", + "icon-allow-overlap": true, + "icon-pitch-alignment": "viewport" + }, + "paint": {}, + "source": "geo", + "type": "symbol" + } + ] +} diff --git a/test/integration/query-tests/terrain/missed-tiles/expected.json b/test/integration/query-tests/terrain/missed-tiles/expected.json new file mode 100644 index 00000000000..1b4764d0c80 --- /dev/null +++ b/test/integration/query-tests/terrain/missed-tiles/expected.json @@ -0,0 +1,25 @@ +[ + { + "geometry": { + "coordinates": [ + [ + -113.3269464969635, + 35.933558016730615 + ], + [ + -113.33341598510742, + 35.929422840516594 + ], + [ + -113.32208633422852, + 35.94187983750328 + ] + ], + "type": "MultiPoint" + }, + "properties": {}, + "source": "geojson", + "state": {}, + "type": "Feature" + } +] diff --git a/test/integration/query-tests/terrain/missed-tiles/style.json b/test/integration/query-tests/terrain/missed-tiles/style.json new file mode 100644 index 00000000000..59be4e4e2a4 --- /dev/null +++ b/test/integration/query-tests/terrain/missed-tiles/style.json @@ -0,0 +1,75 @@ +{ + "version": 8, + "metadata": { + "test": { + "height": 100, + "width": 200, + "queryGeometry": [98, 57] + } + }, + "center": [-113.32694547094238, 35.93455626259847], + "zoom": 12, + "pitch": 0, + "bearing": -20, + "terrain": { + "source": "rgbterrain" + }, + "sources": { + "rgbterrain": { + "type": "raster-dem", + "tiles": [ + "local://tiles-missed/{z}-{x}-{y}.terrain.png" + ], + "maxzoom": 12, + "tileSize": 256 + }, + "satellite": { + "type": "raster", + "tiles": [ + "local://tiles/{z}-{x}-{y}.satellite.png" + ], + "maxzoom": 17, + "tileSize": 256 + }, + "geojson": { + "type": "geojson", + "data": { + "type": "MultiPoint", + "coordinates": [ + [ + -113.32694547094238, + 35.93355626259847 + ], + [ + -113.33341462261518, + 35.9294218694216 + ], + [ + -113.3220882006336, + 35.9418831745696 + ] + ] + } + } + }, + "layers": [ + { + "id": "raster", + "type": "raster", + "source": "satellite", + "paint": { + "raster-fade-duration": 0 + } + }, + { + "id": "circle", + "type": "circle", + "source": "geojson", + "paint": { + "circle-radius": 5, + "circle-color": "#ff0000", + "circle-pitch-alignment": "map" + } + } + ] +} diff --git a/test/integration/render-tests/color-theme/add/expected.png b/test/integration/render-tests/color-theme/add/expected.png new file mode 100644 index 00000000000..94761627a85 Binary files /dev/null and b/test/integration/render-tests/color-theme/add/expected.png differ diff --git a/test/integration/render-tests/color-theme/add/style.json b/test/integration/render-tests/color-theme/add/style.json new file mode 100644 index 00000000000..60c80b286f3 --- /dev/null +++ b/test/integration/render-tests/color-theme/add/style.json @@ -0,0 +1,77 @@ +{ + "version": 8, + "metadata": { + "test": { + "height": 64, + "width": 64, + "operations": [ + [ + "wait" + ], + [ + "setColorTheme", + { + "data": "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" + } + ], + [ + "wait" + ] + ] + } + }, + "center": [ + 0, + 0 + ], + "zoom": 0, + "transition": { + "duration": 0 + }, + "glyphs": "local://glyphs/{fontstack}/{range}.pbf", + "sources": { + "point": { + "type": "geojson", + "data": { + "type": "Point", + "coordinates": [ + 0, + 0 + ] + } + } + }, + "layers": [ + { + "id": "background", + "type": "background", + "paint": { + "background-color": "violet" + } + }, + { + "id": "circle", + "type": "circle", + "source": "point", + "paint": { + "circle-radius": 18, + "circle-color": "white" + } + }, + { + "id": "text", + "type": "symbol", + "source": "point", + "layout": { + "text-field": "Test", + "text-font": [ + "Open Sans Semibold", + "Arial Unicode MS Bold" + ] + }, + "paint": { + "text-color": "black" + } + } + ] +} \ No newline at end of file diff --git a/test/integration/render-tests/color-theme/emission-bw/expected.png b/test/integration/render-tests/color-theme/emission-bw/expected.png new file mode 100644 index 00000000000..91f6603cd36 Binary files /dev/null and b/test/integration/render-tests/color-theme/emission-bw/expected.png differ diff --git a/test/integration/render-tests/color-theme/emission-bw/style.json b/test/integration/render-tests/color-theme/emission-bw/style.json new file mode 100644 index 00000000000..41fa971916a --- /dev/null +++ b/test/integration/render-tests/color-theme/emission-bw/style.json @@ -0,0 +1,49 @@ +{ + "version": 8, + "metadata": { + "test": { + "width": 256, + "height": 256, + "allowed": 0.0005 + } + }, + "center": [ + 0, + 0 + ], + "pitch": 80, + "bearing": 0, + "zoom": 25, + "sources": { + "model": { + "type": "model", + "models": { + "model-1" : { + "uri": "local://models//BoomBox.glb", + "position": [0, 0], + "orientation": [0, 0, 0] + } + } + } + }, + "color-theme": { + "data": "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" + }, + "layers": [ + { + "id": "background", + "type": "background", + "paint": { + "background-color": "white" + } + }, + { + "id": "model", + "type": "model", + "source": "model", + "paint": { + "model-scale" : [440.0, 440.0, 440.0] + } + } + ] +} diff --git a/test/integration/render-tests/color-theme/remove/expected.png b/test/integration/render-tests/color-theme/remove/expected.png new file mode 100644 index 00000000000..2ea30a8675b Binary files /dev/null and b/test/integration/render-tests/color-theme/remove/expected.png differ diff --git a/test/integration/render-tests/color-theme/remove/style.json b/test/integration/render-tests/color-theme/remove/style.json new file mode 100644 index 00000000000..6becf011f20 --- /dev/null +++ b/test/integration/render-tests/color-theme/remove/style.json @@ -0,0 +1,78 @@ +{ + "version": 8, + "metadata": { + "test": { + "height": 64, + "width": 64, + "operations": [ + [ + "wait" + ], + [ + "setColorTheme", + null + ], + [ + "wait" + ] + ] + } + }, + "color-theme": { + "data": "iVBORw0KGgoAAAANSUhEUgAABAAAAAAgCAYAAACM/gqmAAAAAXNSR0IArs4c6QAABSFJREFUeF7t3cFO40AQAFHnBv//wSAEEgmJPeUDsid5h9VqtcMiZsfdPdXVzmVZlo+3ZVm+fr3//L7257Lm778x+prL1ff0/b//H+z/4/M4OkuP/n70Nc7f+nnb+yzb//sY6vxt5xXPn+dP/aH+GsXJekb25izxR/ypZ6ucUefv9g4z2jPP3/HPHwAAgABAABgACIACkAAsAL1SD4yKWQAUAHUBdAG8buKNYoYL8PEX4FcHQAAAAAAAAAAAAAAAAAAAAAAA8LAeGF1mABAABAABQACQbZP7+hk5AwACAAAAAAAAAAAAAAAAAAAAAAAA4EE9AICMx4QBAAAAAAAANgvJsxGQV1dA/PxmMEtxU9YoABQACoC5CgDxX/wvsb2sEf/Ff/Ff/N96l5n73+/5YAB4CeBqx2VvMqXgUfD2npkzBCAXEBeQcrkoa5x/FxAXEBcQF5A2Wy3/t32qNYr8I//Mln+MABgBMAJgBMAIgBEAIwBGAIwAGAEwAmAE4K4eAGCNQIw+qQ0AmQ+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB/6gEABAB5RgACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAN/UAAPKcAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgEFNODICRtDkDO/gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOhvlPUWem+h9xKQ+V4CUt9wO6KZnn/Pv+ff8z/bW5DFP59CUnJbWSP+iX/iX78znqED/urxnwHAAGAAMAAYAAwABgADgAHAAGAAMAAYAAwABgADoNMcHUAdQAQcAUfAe8xEwH0O86t3IPz8OvClu17WqD/UH+oP9cf1Gdia01d/LQsDgAHAAGAAMAAYAAwABgADgAHAAGAAMAAYAAwABkCnSQwABgACj8Aj8D1mItAMAB1wHfDS3S5r5F/5V/6Vf3XAW12h/mIArHY89iZTAAQA2XtmBKAWqOslyf4rgBXACmAFcIur8k/bJ/mnQTr5V/6Vf+fKv0YAjAAYATACYATACIARACMARgCMABgBMAJgBMAIgBEAIwCdZuiA64AjwAgwAtxjpg6cDlztLlLA7/Pr1gueyr56/jx/5ZzUNeof9Y/6R/0zk4HGAGAAMAAYAAwABgADgAHAAGAAMAAYAAwABgADgAHQaQ4DgAGAgCPgCHiPmTqQOpC1u8gAYACMjAf5V/6Vf+XfmTrQ8l97v8Z/5X8GAAOAAcAAYAAwABgADAAGAAOAAcAAYAAwABgADIBO0xgADAAdCB0IHYgeMxkADAAdkGM7IPbf/pfuWlmj/lH/qH/UPzMZGAwABgADgAHAAGAAMAAYAAwABgADgAHAAGAAMAAYAJ3mMAAYAAg4Ao6A95jJAGAA6EDrQJfuclkj/8q/8q/8O1MHWv47Nv8xABgADAAGAAOAAcAAYAAwABgADAAGAAOAAcAAYAB0msYAYADoQOhA6ED0mMkAYADogBzbAbH/9r/YFWWN+kf9o/5R/8xkYDAAGAAMAAYAA4ABwABgADAAGAAMAAYAA4ABwABgAHSawwBgACDgCDgC3mMmA4ABoAOtA126y2WN/Cv/yr/y70wdaPnv2PzHAGAAMAAYAAwABgADgAHAAGAAMAAYAAwABgADgAHQaRoDgAGgA6EDoQPRYyYDgAGgA3JsB8T+2/9iV5Q16h/1j/pH/TOTgcEAYAAwABgADAAGAAOAAcAAYAAwABgADAAGAAPgyQ2AT4NBIB3ew5dkAAAAAElFTkSuQmCC" + }, + "center": [ + 0, + 0 + ], + "zoom": 0, + "transition": { + "duration": 0 + }, + "glyphs": "local://glyphs/{fontstack}/{range}.pbf", + "sources": { + "point": { + "type": "geojson", + "data": { + "type": "Point", + "coordinates": [ + 0, + 0 + ] + } + } + }, + "layers": [ + { + "id": "background", + "type": "background", + "paint": { + "background-color": "violet" + } + }, + { + "id": "circle", + "type": "circle", + "source": "point", + "paint": { + "circle-radius": 18, + "circle-color": "white" + } + }, + { + "id": "text", + "type": "symbol", + "source": "point", + "layout": { + "text-field": "Test", + "text-font": [ + "Open Sans Semibold", + "Arial Unicode MS Bold" + ] + }, + "paint": { + "text-color": "black" + } + } + ] +} \ No newline at end of file diff --git a/test/integration/render-tests/color-theme/update-config-base/expected.png b/test/integration/render-tests/color-theme/update-config-base/expected.png new file mode 100644 index 00000000000..94761627a85 Binary files /dev/null and b/test/integration/render-tests/color-theme/update-config-base/expected.png differ diff --git a/test/integration/render-tests/color-theme/update-config-base/style.json b/test/integration/render-tests/color-theme/update-config-base/style.json new file mode 100644 index 00000000000..2e11fd55d47 --- /dev/null +++ b/test/integration/render-tests/color-theme/update-config-base/style.json @@ -0,0 +1,106 @@ +{ + "version": 8, + "metadata": { + "test": { + "height": 64, + "width": 64, + "operations": [ + [ + "wait" + ], + [ + "setConfigProperty", + "basemap", + "colorTheme", + "bw" + ], + [ + "wait" + ] + ] + } + }, + "color-theme": { + "data": [ + "match", + ["config", "colorTheme"], + "red", + "iVBORw0KGgoAAAANSUhEUgAABAAAAAAgCAYAAACM/gqmAAAAAXNSR0IArs4c6QAABSFJREFUeF7t3cFO40AQAFHnBv//wSAEEgmJPeUDsid5h9VqtcMiZsfdPdXVzmVZlo+3ZVm+fr3//L7257Lm778x+prL1ff0/b//H+z/4/M4OkuP/n70Nc7f+nnb+yzb//sY6vxt5xXPn+dP/aH+GsXJekb25izxR/ypZ6ucUefv9g4z2jPP3/HPHwAAgABAABgACIACkAAsAL1SD4yKWQAUAHUBdAG8buKNYoYL8PEX4FcHQAAAAAAAAAAAAAAAAAAAAAAA8LAeGF1mABAABAABQACQbZP7+hk5AwACAAAAAAAAAAAAAAAAAAAAAAAA4EE9AICMx4QBAAAAAAAANgvJsxGQV1dA/PxmMEtxU9YoABQACoC5CgDxX/wvsb2sEf/Ff/Ff/N96l5n73+/5YAB4CeBqx2VvMqXgUfD2npkzBCAXEBeQcrkoa5x/FxAXEBcQF5A2Wy3/t32qNYr8I//Mln+MABgBMAJgBMAIgBEAIwBGAIwAGAEwAmAE4K4eAGCNQIw+qQ0AmQ+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB/6gEABAB5RgACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAN/UAAPKcAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgEFNODICRtDkDO/gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOhvlPUWem+h9xKQ+V4CUt9wO6KZnn/Pv+ff8z/bW5DFP59CUnJbWSP+iX/iX78znqED/urxnwHAAGAAMAAYAAwABgADgAHAAGAAMAAYAAwABgADoNMcHUAdQAQcAUfAe8xEwH0O86t3IPz8OvClu17WqD/UH+oP9cf1Gdia01d/LQsDgAHAAGAAMAAYAAwABgADgAHAAGAAMAAYAAwABkCnSQwABgACj8Aj8D1mItAMAB1wHfDS3S5r5F/5V/6Vf3XAW12h/mIArHY89iZTAAQA2XtmBKAWqOslyf4rgBXACmAFcIur8k/bJ/mnQTr5V/6Vf+fKv0YAjAAYATACYATACIARACMARgCMABgBMAJgBMAIgBEAIwCdZuiA64AjwAgwAtxjpg6cDlztLlLA7/Pr1gueyr56/jx/5ZzUNeof9Y/6R/0zk4HGAGAAMAAYAAwABgADgAHAAGAAMAAYAAwABgADgAHQaQ4DgAGAgCPgCHiPmTqQOpC1u8gAYACMjAf5V/6Vf+XfmTrQ8l97v8Z/5X8GAAOAAcAAYAAwABgADAAGAAOAAcAAYAAwABgADIBO0xgADAAdCB0IHYgeMxkADAAdkGM7IPbf/pfuWlmj/lH/qH/UPzMZGAwABgADgAHAAGAAMAAYAAwABgADgAHAAGAAMAAYAJ3mMAAYAAg4Ao6A95jJAGAA6EDrQJfuclkj/8q/8q/8O1MHWv47Nv8xABgADAAGAAOAAcAAYAAwABgADAAGAAOAAcAAYAB0msYAYADoQOhA6ED0mMkAYADogBzbAbH/9r/YFWWN+kf9o/5R/8xkYDAAGAAMAAYAA4ABwABgADAAGAAMAAYAA4ABwABgAHSawwBgACDgCDgC3mMmA4ABoAOtA126y2WN/Cv/yr/y70wdaPnv2PzHAGAAMAAYAAwABgADgAHAAGAAMAAYAAwABgADgAHQaRoDgAGgA6EDoQPRYyYDgAGgA3JsB8T+2/9iV5Q16h/1j/pH/TOTgcEAYAAwABgADAAGAAOAAcAAYAAwABgADAAGAAPgyQ2AT4NBIB3ew5dkAAAAAElFTkSuQmCC", + "bw", + "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", + "" + ] + }, + "schema": { + "colorTheme": { + "default": "red" + }, + "lightPreset": { + "default": [ + "format", + "abc", + { + "text-color": "red", + "text-font": [ + "Open Sans Semibold", + "Arial Unicode MS Bold" + ] + } + ] + } + }, + "center": [ + 0, + 0 + ], + "zoom": 0, + "transition": { + "duration": 0 + }, + "glyphs": "local://glyphs/{fontstack}/{range}.pbf", + "sources": { + "point": { + "type": "geojson", + "data": { + "type": "Point", + "coordinates": [ + 0, + 0 + ] + } + } + }, + "layers": [ + { + "id": "background", + "type": "background", + "paint": { + "background-color": "violet" + } + }, + { + "id": "circle", + "type": "circle", + "source": "point", + "paint": { + "circle-radius": 18, + "circle-color": "white" + } + }, + { + "id": "text", + "type": "symbol", + "source": "point", + "layout": { + "text-field": "Test", + "text-font": [ + "Open Sans Semibold", + "Arial Unicode MS Bold" + ] + }, + "paint": { + "text-color": "black" + } + } + ] +} \ No newline at end of file diff --git a/test/integration/render-tests/color-theme/update-config-import/expected.png b/test/integration/render-tests/color-theme/update-config-import/expected.png new file mode 100644 index 00000000000..cb1098be459 Binary files /dev/null and b/test/integration/render-tests/color-theme/update-config-import/expected.png differ diff --git a/test/integration/render-tests/color-theme/update-config-import/style.json b/test/integration/render-tests/color-theme/update-config-import/style.json new file mode 100644 index 00000000000..1c7568711f0 --- /dev/null +++ b/test/integration/render-tests/color-theme/update-config-import/style.json @@ -0,0 +1,116 @@ +{ + "version": 8, + "metadata": { + "test": { + "height": 64, + "width": 64, + "operations": [ + [ + "wait" + ], + [ + "setConfigProperty", + "basemap", + "colorTheme", + "bw" + ], + [ + "wait" + ] + ] + } + }, + "center": [ + 0, + 0 + ], + "zoom": 0, + "transition": { + "duration": 0 + }, + "sources": {}, + "layers": [], + "imports": [ + { + "id": "basemap", + "url": "", + "data": { + "version": 8, + "color-theme": { + "data": [ + "match", + ["config", "colorTheme"], + "red", + "iVBORw0KGgoAAAANSUhEUgAABAAAAAAgCAYAAACM/gqmAAAAAXNSR0IArs4c6QAABSFJREFUeF7t3cFO40AQAFHnBv//wSAEEgmJPeUDsid5h9VqtcMiZsfdPdXVzmVZlo+3ZVm+fr3//L7257Lm778x+prL1ff0/b//H+z/4/M4OkuP/n70Nc7f+nnb+yzb//sY6vxt5xXPn+dP/aH+GsXJekb25izxR/ypZ6ucUefv9g4z2jPP3/HPHwAAgABAABgACIACkAAsAL1SD4yKWQAUAHUBdAG8buKNYoYL8PEX4FcHQAAAAAAAAAAAAAAAAAAAAAAA8LAeGF1mABAABAABQACQbZP7+hk5AwACAAAAAAAAAAAAAAAAAAAAAAAA4EE9AICMx4QBAAAAAAAANgvJsxGQV1dA/PxmMEtxU9YoABQACoC5CgDxX/wvsb2sEf/Ff/Ff/N96l5n73+/5YAB4CeBqx2VvMqXgUfD2npkzBCAXEBeQcrkoa5x/FxAXEBcQF5A2Wy3/t32qNYr8I//Mln+MABgBMAJgBMAIgBEAIwBGAIwAGAEwAmAE4K4eAGCNQIw+qQ0AmQ+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB/6gEABAB5RgACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAN/UAAPKcAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgEFNODICRtDkDO/gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOhvlPUWem+h9xKQ+V4CUt9wO6KZnn/Pv+ff8z/bW5DFP59CUnJbWSP+iX/iX78znqED/urxnwHAAGAAMAAYAAwABgADgAHAAGAAMAAYAAwABgADoNMcHUAdQAQcAUfAe8xEwH0O86t3IPz8OvClu17WqD/UH+oP9cf1Gdia01d/LQsDgAHAAGAAMAAYAAwABgADgAHAAGAAMAAYAAwABkCnSQwABgACj8Aj8D1mItAMAB1wHfDS3S5r5F/5V/6Vf3XAW12h/mIArHY89iZTAAQA2XtmBKAWqOslyf4rgBXACmAFcIur8k/bJ/mnQTr5V/6Vf+fKv0YAjAAYATACYATACIARACMARgCMABgBMAJgBMAIgBEAIwCdZuiA64AjwAgwAtxjpg6cDlztLlLA7/Pr1gueyr56/jx/5ZzUNeof9Y/6R/0zk4HGAGAAMAAYAAwABgADgAHAAGAAMAAYAAwABgADgAHQaQ4DgAGAgCPgCHiPmTqQOpC1u8gAYACMjAf5V/6Vf+XfmTrQ8l97v8Z/5X8GAAOAAcAAYAAwABgADAAGAAOAAcAAYAAwABgADIBO0xgADAAdCB0IHYgeMxkADAAdkGM7IPbf/pfuWlmj/lH/qH/UPzMZGAwABgADgAHAAGAAMAAYAAwABgADgAHAAGAAMAAYAJ3mMAAYAAg4Ao6A95jJAGAA6EDrQJfuclkj/8q/8q/8O1MHWv47Nv8xABgADAAGAAOAAcAAYAAwABgADAAGAAOAAcAAYAB0msYAYADoQOhA6ED0mMkAYADogBzbAbH/9r/YFWWN+kf9o/5R/8xkYDAAGAAMAAYAA4ABwABgADAAGAAMAAYAA4ABwABgAHSawwBgACDgCDgC3mMmA4ABoAOtA126y2WN/Cv/yr/y70wdaPnv2PzHAGAAMAAYAAwABgADgAHAAGAAMAAYAAwABgADgAHQaRoDgAGgA6EDoQPRYyYDgAGgA3JsB8T+2/9iV5Q16h/1j/pH/TOTgcEAYAAwABgADAAGAAOAAcAAYAAwABgADAAGAAPgyQ2AT4NBIB3ew5dkAAAAAElFTkSuQmCC", + "bw", + "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", + "" + ] + }, + "glyphs": "local://glyphs/{fontstack}/{range}.pbf", + "schema": { + "colorTheme": { + "default": "red" + }, + "lightPreset": { + "default": [ + "format", + "abc", + { + "text-color": "red", + "text-font": [ + "Open Sans Semibold", + "Arial Unicode MS Bold" + ] + } + ] + } + }, + "sources": { + "point": { + "type": "geojson", + "data": { + "type": "Point", + "coordinates": [ + 0, + 0 + ] + } + } + }, + "layers": [ + { + "id": "background", + "type": "background", + "paint": { + "background-color": "violet" + } + }, + { + "id": "circle", + "type": "circle", + "source": "point", + "paint": { + "circle-radius": 18, + "circle-color": "white" + } + }, + { + "id": "text", + "type": "symbol", + "source": "point", + "layout": { + "text-field": [ + "config", + "lightPreset" + ] + }, + "paint": { + "text-color": "black" + } + } + ] + } + } + ] +} \ No newline at end of file diff --git a/test/integration/render-tests/color-theme/update-with-api/expected.png b/test/integration/render-tests/color-theme/update-with-api/expected.png new file mode 100644 index 00000000000..94761627a85 Binary files /dev/null and b/test/integration/render-tests/color-theme/update-with-api/expected.png differ diff --git a/test/integration/render-tests/color-theme/update-with-api/style.json b/test/integration/render-tests/color-theme/update-with-api/style.json new file mode 100644 index 00000000000..2a5f7684a65 --- /dev/null +++ b/test/integration/render-tests/color-theme/update-with-api/style.json @@ -0,0 +1,80 @@ +{ + "version": 8, + "metadata": { + "test": { + "height": 64, + "width": 64, + "operations": [ + [ + "wait" + ], + [ + "setColorTheme", + { + "data": "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" + } + ], + [ + "wait" + ] + ] + } + }, + "color-theme": { + "data": "iVBORw0KGgoAAAANSUhEUgAABAAAAAAgCAYAAACM/gqmAAAAAXNSR0IArs4c6QAABSFJREFUeF7t3cFO40AQAFHnBv//wSAEEgmJPeUDsid5h9VqtcMiZsfdPdXVzmVZlo+3ZVm+fr3//L7257Lm778x+prL1ff0/b//H+z/4/M4OkuP/n70Nc7f+nnb+yzb//sY6vxt5xXPn+dP/aH+GsXJekb25izxR/ypZ6ucUefv9g4z2jPP3/HPHwAAgABAABgACIACkAAsAL1SD4yKWQAUAHUBdAG8buKNYoYL8PEX4FcHQAAAAAAAAAAAAAAAAAAAAAAA8LAeGF1mABAABAABQACQbZP7+hk5AwACAAAAAAAAAAAAAAAAAAAAAAAA4EE9AICMx4QBAAAAAAAANgvJsxGQV1dA/PxmMEtxU9YoABQACoC5CgDxX/wvsb2sEf/Ff/Ff/N96l5n73+/5YAB4CeBqx2VvMqXgUfD2npkzBCAXEBeQcrkoa5x/FxAXEBcQF5A2Wy3/t32qNYr8I//Mln+MABgBMAJgBMAIgBEAIwBGAIwAGAEwAmAE4K4eAGCNQIw+qQ0AmQ+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB/6gEABAB5RgACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAN/UAAPKcAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgEFNODICRtDkDO/gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOhvlPUWem+h9xKQ+V4CUt9wO6KZnn/Pv+ff8z/bW5DFP59CUnJbWSP+iX/iX78znqED/urxnwHAAGAAMAAYAAwABgADgAHAAGAAMAAYAAwABgADoNMcHUAdQAQcAUfAe8xEwH0O86t3IPz8OvClu17WqD/UH+oP9cf1Gdia01d/LQsDgAHAAGAAMAAYAAwABgADgAHAAGAAMAAYAAwABkCnSQwABgACj8Aj8D1mItAMAB1wHfDS3S5r5F/5V/6Vf3XAW12h/mIArHY89iZTAAQA2XtmBKAWqOslyf4rgBXACmAFcIur8k/bJ/mnQTr5V/6Vf+fKv0YAjAAYATACYATACIARACMARgCMABgBMAJgBMAIgBEAIwCdZuiA64AjwAgwAtxjpg6cDlztLlLA7/Pr1gueyr56/jx/5ZzUNeof9Y/6R/0zk4HGAGAAMAAYAAwABgADgAHAAGAAMAAYAAwABgADgAHQaQ4DgAGAgCPgCHiPmTqQOpC1u8gAYACMjAf5V/6Vf+XfmTrQ8l97v8Z/5X8GAAOAAcAAYAAwABgADAAGAAOAAcAAYAAwABgADIBO0xgADAAdCB0IHYgeMxkADAAdkGM7IPbf/pfuWlmj/lH/qH/UPzMZGAwABgADgAHAAGAAMAAYAAwABgADgAHAAGAAMAAYAJ3mMAAYAAg4Ao6A95jJAGAA6EDrQJfuclkj/8q/8q/8O1MHWv47Nv8xABgADAAGAAOAAcAAYAAwABgADAAGAAOAAcAAYAB0msYAYADoQOhA6ED0mMkAYADogBzbAbH/9r/YFWWN+kf9o/5R/8xkYDAAGAAMAAYAA4ABwABgADAAGAAMAAYAA4ABwABgAHSawwBgACDgCDgC3mMmA4ABoAOtA126y2WN/Cv/yr/y70wdaPnv2PzHAGAAMAAYAAwABgADgAHAAGAAMAAYAAwABgADgAHQaRoDgAGgA6EDoQPRYyYDgAGgA3JsB8T+2/9iV5Q16h/1j/pH/TOTgcEAYAAwABgADAAGAAOAAcAAYAAwABgADAAGAAPgyQ2AT4NBIB3ew5dkAAAAAElFTkSuQmCC" + }, + "center": [ + 0, + 0 + ], + "zoom": 0, + "transition": { + "duration": 0 + }, + "glyphs": "local://glyphs/{fontstack}/{range}.pbf", + "sources": { + "point": { + "type": "geojson", + "data": { + "type": "Point", + "coordinates": [ + 0, + 0 + ] + } + } + }, + "layers": [ + { + "id": "background", + "type": "background", + "paint": { + "background-color": "violet" + } + }, + { + "id": "circle", + "type": "circle", + "source": "point", + "paint": { + "circle-radius": 18, + "circle-color": "white" + } + }, + { + "id": "text", + "type": "symbol", + "source": "point", + "layout": { + "text-field": "Test", + "text-font": [ + "Open Sans Semibold", + "Arial Unicode MS Bold" + ] + }, + "paint": { + "text-color": "black" + } + } + ] +} \ No newline at end of file diff --git a/test/integration/render-tests/line-pattern/3x-on-2x-add-image/style.json b/test/integration/render-tests/line-pattern/3x-on-2x-add-image/style.json index 425c467c887..d2524de78bf 100644 --- a/test/integration/render-tests/line-pattern/3x-on-2x-add-image/style.json +++ b/test/integration/render-tests/line-pattern/3x-on-2x-add-image/style.json @@ -5,6 +5,7 @@ "width": 64, "height": 32, "pixelRatio": 2, + "allowed": 0.0005, "operations": [ [ "wait" diff --git a/test/integration/render-tests/line-pattern/int-zoom-constant-width/style.json b/test/integration/render-tests/line-pattern/int-zoom-constant-width/style.json index d5543915f96..6cd86e058f5 100644 --- a/test/integration/render-tests/line-pattern/int-zoom-constant-width/style.json +++ b/test/integration/render-tests/line-pattern/int-zoom-constant-width/style.json @@ -2,7 +2,8 @@ "version": 8, "metadata": { "test": { - "height": 256 + "height": 256, + "allowed": 0.0012 } }, "center": [ diff --git a/test/integration/render-tests/line-pattern/line-join-none-fract-zoom/style.json b/test/integration/render-tests/line-pattern/line-join-none-fract-zoom/style.json index b41fc20e195..629a5861e77 100644 --- a/test/integration/render-tests/line-pattern/line-join-none-fract-zoom/style.json +++ b/test/integration/render-tests/line-pattern/line-join-none-fract-zoom/style.json @@ -2,7 +2,8 @@ "version": 8, "metadata": { "test": { - "height": 256 + "height": 256, + "allowed": 0.0003 } }, "center": [ diff --git a/test/integration/render-tests/line-pattern/line-join-none-high-zoom/expected.png b/test/integration/render-tests/line-pattern/line-join-none-high-zoom/expected.png new file mode 100644 index 00000000000..fb180dcc328 Binary files /dev/null and b/test/integration/render-tests/line-pattern/line-join-none-high-zoom/expected.png differ diff --git a/test/integration/render-tests/line-pattern/line-join-none-high-zoom/style.json b/test/integration/render-tests/line-pattern/line-join-none-high-zoom/style.json new file mode 100644 index 00000000000..28a38005853 --- /dev/null +++ b/test/integration/render-tests/line-pattern/line-join-none-high-zoom/style.json @@ -0,0 +1,93 @@ +{ + "version": 8, + "metadata": { + "test": { + "width": 360, + "height": 120, + "allowed": 0.0008 + } + }, + "center": [ + 50.6990, + 16.7202 + ], + "zoom": 17.5, + "sources": { + "geojson": { + "type": "geojson", + "data": "local://data/pattern-line.geojson", + "lineMetrics": true + } + }, + "sprite": "local://sprites/emerald", + "layers": [ + { + "id": "background", + "type": "background", + "paint": { + "background-color": "#FFF" + } + }, + { + "id": "line", + "type": "line", + "source": "geojson", + "paint": { + "line-pattern": "dot", + "line-width": { + "type": "exponential", + "base": 2, + "stops": [ + [0, 18], + [0.99999, 36], + [1, 18], + [1.99999, 36], + [2, 18], + [2.99999, 36], + [3, 18], + [3.99999, 36], + [4, 18], + [4.99999, 36], + [5, 18], + [5.99999, 36], + [6, 18], + [6.99999, 36], + [7, 18], + [7.99999, 36], + [8, 18], + [8.99999, 36], + [9, 18], + [9.99999, 36], + [10, 18], + [10.99999, 36], + [11, 18], + [11.99999, 36], + [12, 18], + [12.99999, 36], + [13, 18], + [13.99999, 36], + [14, 18], + [14.99999, 36], + [15, 18], + [15.99999, 36], + [16, 18], + [16.99999, 36], + [17, 18], + [17.99999, 36], + [18, 18], + [18.99999, 36], + [19, 18], + [19.99999, 36], + [20, 18], + [20.99999, 36], + [21, 18], + [22, 36] + ] + } + }, + "layout": { + "line-join": "none" + } + } + ] +} diff --git a/test/integration/render-tests/line-pattern/line-join-none-runtime-pattern/style.json b/test/integration/render-tests/line-pattern/line-join-none-runtime-pattern/style.json index 49f4a5ba441..a4339e92d15 100644 --- a/test/integration/render-tests/line-pattern/line-join-none-runtime-pattern/style.json +++ b/test/integration/render-tests/line-pattern/line-join-none-runtime-pattern/style.json @@ -3,6 +3,7 @@ "metadata": { "test": { "height": 256, + "allowed": 0.0008, "operations": [ ["wait"], ["setPaintProperty", "line", "line-pattern", "dot"], diff --git a/test/integration/render-tests/line-pattern/line-join-none-with-offset/style.json b/test/integration/render-tests/line-pattern/line-join-none-with-offset/style.json index fa3dc80155e..75f6fceeee4 100644 --- a/test/integration/render-tests/line-pattern/line-join-none-with-offset/style.json +++ b/test/integration/render-tests/line-pattern/line-join-none-with-offset/style.json @@ -2,7 +2,8 @@ "version": 8, "metadata": { "test": { - "height": 256 + "height": 256, + "allowed": 0.0012 } }, "center": [ diff --git a/test/integration/render-tests/line-pattern/line-join-none/style.json b/test/integration/render-tests/line-pattern/line-join-none/style.json index 4621c9b6c6a..0344081f89c 100644 --- a/test/integration/render-tests/line-pattern/line-join-none/style.json +++ b/test/integration/render-tests/line-pattern/line-join-none/style.json @@ -2,7 +2,8 @@ "version": 8, "metadata": { "test": { - "height": 256 + "height": 256, + "allowed": 0.0008 } }, "center": [ diff --git a/test/integration/render-tests/line-pattern/line-metrics/style.json b/test/integration/render-tests/line-pattern/line-metrics/style.json index 9e263009dce..692114ae454 100644 --- a/test/integration/render-tests/line-pattern/line-metrics/style.json +++ b/test/integration/render-tests/line-pattern/line-metrics/style.json @@ -2,7 +2,8 @@ "version": 8, "metadata": { "test": { - "height": 256 + "height": 256, + "allowed": 0.0003 } }, "center": [ diff --git a/test/integration/render-tests/line-trim-offset/trim-color-fade/expected.png b/test/integration/render-tests/line-trim-offset/trim-color-fade/expected.png new file mode 100644 index 00000000000..a852552e5d7 Binary files /dev/null and b/test/integration/render-tests/line-trim-offset/trim-color-fade/expected.png differ diff --git a/test/integration/render-tests/line-trim-offset/trim-color-fade/style.json b/test/integration/render-tests/line-trim-offset/trim-color-fade/style.json new file mode 100644 index 00000000000..14fb20c57de --- /dev/null +++ b/test/integration/render-tests/line-trim-offset/trim-color-fade/style.json @@ -0,0 +1,201 @@ +{ + "version": 8, + "metadata": { + "test": { + "width": 150, + "height": 150 + } + }, + "zoom": 6.5, + "center": [0.4, 0.5], + "sources": { + "trim_offset": { + "type": "geojson", + "data": { + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": {}, + "id": 1, + "geometry": { + "coordinates": [ + [ + 0.0, + 0.0 + ], + [ + 0.0, + 1.0 + ] + ], + "type": "LineString" + } + }, + { + "type": "Feature", + "properties": {}, + "id": 2, + "geometry": { + "coordinates": [ + [ + 0.2, + 0.0 + ], + [ + 0.2, + 1.0 + ] + ], + "type": "LineString" + } + }, + { + "type": "Feature", + "properties": {}, + "id": 3, + "geometry": { + "coordinates": [ + [ + 0.4, + 0.0 + ], + [ + 0.4, + 1.0 + ] + ], + "type": "LineString" + } + }, + { + "type": "Feature", + "properties": {}, + "id": 4, + "geometry": { + "coordinates": [ + [ + 0.6, + 0.0 + ], + [ + 0.6, + 1.0 + ] + ], + "type": "LineString" + } + }, + { + "type": "Feature", + "properties": {}, + "id": 5, + "geometry": { + "coordinates": [ + [ + 0.8, + 0.0 + ], + [ + 0.8, + 1.0 + ] + ], + "type": "LineString" + } + } + ] + }, + "lineMetrics": true + } + }, + "layers": [ + { + "id": "default_color_fade_begin", + "type": "line", + "source": "trim_offset", + "filter": ["==", ["id"], 1], + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-width": 10, + "line-trim-offset": [0.0, 0.2], + "line-trim-fade-range": [0.0, 0.1] + } + }, + { + "id": "default_color_fade_end", + "type": "line", + "source": "trim_offset", + "filter": ["==", ["id"], 2], + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-width": 10, + "line-trim-offset": [0.8, 1.0], + "line-trim-fade-range": [0.1, 0.0] + } + }, + { + "id": "trim-color", + "type": "line", + "source": "trim_offset", + "filter": ["==", ["id"], 3], + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-width": 10, + "line-trim-offset": [0.2, 0.6], + "line-trim-fade-range": [0.0, 0.2], + "line-trim-color": "orange", + "line-color": "transparent" + } + }, + { + "id": "trim-fade-both-sides", + "type": "line", + "source": "trim_offset", + "filter": ["==", ["id"], 4], + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-width": 10, + "line-trim-offset": [0.2, 0.9], + "line-trim-fade-range": [0.1, 0.1], + "line-trim-color": "green", + "line-color": "transparent" + } + }, + { + "id": "trim-fade-gradient", + "type": "line", + "source": "trim_offset", + "filter": ["==", ["id"], 5], + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-width": 10, + "line-trim-offset": [0.1, 0.3], + "line-trim-fade-range": [0.02, 0.02], + "line-trim-color": "aquamarine", + "line-gradient": ["interpolate", + ["linear"], + ["line-progress"], + 0.0, "red", + 0.7, "green", + 1.0, "transparent" + ] + } + } + ] +} diff --git a/test/integration/render-tests/model-layer/landmark-front-cutoff-meshopt/expected.png b/test/integration/render-tests/model-layer/landmark-front-cutoff-meshopt/expected.png new file mode 100644 index 00000000000..b66bee6400e Binary files /dev/null and b/test/integration/render-tests/model-layer/landmark-front-cutoff-meshopt/expected.png differ diff --git a/test/integration/render-tests/model-layer/landmark-front-cutoff-meshopt/style.json b/test/integration/render-tests/model-layer/landmark-front-cutoff-meshopt/style.json new file mode 100644 index 00000000000..37a59435463 --- /dev/null +++ b/test/integration/render-tests/model-layer/landmark-front-cutoff-meshopt/style.json @@ -0,0 +1,626 @@ +{ + "version": 8, + "metadata": { + "test": { + "allowed": 0.0012, + "width": 1024, + "height": 1024 + } + }, + "lights": [ + { + "type": "ambient", + "id": "environment" + }, + { + "type": "directional", + "id": "sun_light", + "properties": { + "cast-shadows": true, + "intensity": 0.8599999904632569, + "direction": [ + 311.9219970703125, + 82.37799835205078 + ] + } + } + ], + "glyphs": "local://glyphs/{fontstack}/{range}.pbf", + "sprite": "local://sprites/sprite", + "sources": { + "composite": { + "type": "vector", + "maxzoom": 15, + "tiles": [ + "local://models/vector/{z}-{x}-{y}.vector.pbf" + ] + }, + "file-system-tiles": { + "type": "batched-model", + "maxzoom": 14, + "tiles": [ + "local://models/landmark/mbx-meshopt/{x}-{y}-{z}.glb" + ] + } + }, + "transition": { + "duration": 0 + }, + "pitch": 55, + "zoom": 17.3, + "bearing": 0, + "center": [ + 11.582243, + 48.131199 + ], + "layers": [ + { + "type": "background", + "paint": { + "background-color": [ + "interpolate", + [ + "linear" + ], + [ + "zoom" + ], + 11.0, + [ + "rgba", + 239.00001525878907, + 233.00001525878907, + 225.00001525878907, + 1.0 + ], + 13.0, + [ + "rgba", + 230.00001525878907, + 228.00001525878907, + 224.00001525878907, + 1.0 + ] + ] + }, + "id": "land" + }, + { + "type": "fill", + "id": "landcover", + "source": "composite", + "maxzoom": 7.0, + "paint": { + "fill-antialias": false, + "fill-opacity": [ + "interpolate", + [ + "exponential", + 1.5 + ], + [ + "zoom" + ], + 2.0, + 0.3, + 7.0, + 0.0 + ], + "fill-color": [ + "match", + [ + "get", + "class" + ], + "snow", + [ + "rgba", + 255.0, + 255.0, + 255.0, + 1.0 + ], + [ + "rgba", + 222.00001525878907, + 237.00001525878907, + 177.0, + 1.0 + ] + ] + }, + "source-layer": "landcover" + }, + { + "type": "fill", + "source": "composite", + "paint": { + "fill-color": [ + "rgba", + 117.00000762939453, + 207.00001525878907, + 240.00001525878907, + 1.0 + ] + }, + "source-layer": "water", + "id": "water" + }, + { + "minzoom": 11.0, + "type": "line", + "paint": { + "line-opacity": [ + "step", + [ + "zoom" + ], + 0.0, + 14.0, + 1.0 + ], + "line-color": [ + "match", + [ + "get", + "class" + ], + "street_limited", + [ + "rgba", + 240.00001525878907, + 238.00001525878907, + 235.00001525878907, + 1.0 + ], + [ + "rgba", + 255.0, + 255.0, + 255.0, + 1.0 + ] + ], + "line-width": [ + "interpolate", + [ + "exponential", + 1.5 + ], + [ + "zoom" + ], + 12.0, + 0.5, + 14.0, + 2.0, + 18.0, + 18.0 + ] + }, + "source-layer": "road", + "filter": [ + "all", + [ + "match", + [ + "get", + "class" + ], + [ + "primary_link", + "street", + "street_limited" + ], + true, + false + ], + [ + "match", + [ + "get", + "structure" + ], + [ + "ford", + "none" + ], + true, + false + ], + [ + "==", + [ + "geometry-type" + ], + "LineString" + ] + ], + "layout": { + "line-join": "round", + "line-cap": "round" + }, + "source": "composite", + "id": "road-street" + }, + { + "minzoom": 15.0, + "type": "fill-extrusion", + "paint": { + "fill-extrusion-height": [ + "number", + [ + "get", + "height" + ] + ], + "fill-extrusion-ambient-occlusion-intensity": 0.3499999940395355, + "fill-extrusion-ambient-occlusion-ground-radius": 5, + "fill-extrusion-ambient-occlusion-wall-radius": 5, + "fill-extrusion-opacity": 1.0, + "fill-extrusion-base": [ + "number", + [ + "get", + "min_height" + ] + ], + "fill-extrusion-color": [ + "rgba", + 255.0, + 255.0, + 255.0, + 1.0 + ], + "fill-extrusion-vertical-gradient": false, + "fill-extrusion-vertical-scale": [ + "interpolate", + ["linear"], + ["zoom"], + 16, 0, + 17, 2.0 + ] + }, + "source-layer": "building", + "filter": [ + "==", + [ + "get", + "extrude" + ], + "true" + ], + "source": "composite", + "id": "3d-buildings" + }, + { + "minzoom": 10.0, + "type": "symbol", + "paint": { + "text-halo-width": 1.0, + "text-halo-blur": 1.0, + "text-halo-color": [ + "match", + [ + "get", + "class" + ], + "ferry", + [ + "rgba", + 117.00000762939453, + 207.00001525878907, + 240.00001525878907, + 1.0 + ], + [ + "motorway", + "trunk" + ], + [ + "rgba", + 255.0, + 255.0, + 255.0, + 0.75 + ], + [ + "rgba", + 255.0, + 255.0, + 255.0, + 1.0 + ] + ], + "text-color": [ + "match", + [ + "get", + "class" + ], + "ferry", + [ + "rgba", + 58.000003814697269, + 76.0, + 166.0, + 1.0 + ], + [ + "rgba", + 0.0, + 0.0, + 0.0, + 1.0 + ] + ] + }, + "source-layer": "road", + "filter": [ + "step", + [ + "zoom" + ], + [ + "match", + [ + "get", + "class" + ], + [ + "motorway", + "primary", + "secondary", + "tertiary", + "trunk" + ], + true, + false + ], + 12.0, + [ + "match", + [ + "get", + "class" + ], + [ + "motorway", + "pedestrian", + "primary", + "secondary", + "street", + "street_limited", + "tertiary", + "trunk" + ], + true, + false + ], + 15.0, + [ + "match", + [ + "get", + "class" + ], + "golf", + false, + true + ] + ], + "layout": { + "text-font": [ "NotoCJK" ], + "text-max-angle": 30.0, + "text-size": [ + "interpolate", + [ + "linear" + ], + [ + "zoom" + ], + 10.0, + [ + "match", + [ + "get", + "class" + ], + [ + "motorway", + "primary", + "secondary", + "tertiary", + "trunk" + ], + 10.0, + [ + "motorway_link", + "pedestrian", + "primary_link", + "secondary_link", + "street", + "street_limited", + "tertiary_link", + "trunk_link" + ], + 9.0, + 6.5 + ], + 18.0, + [ + "match", + [ + "get", + "class" + ], + [ + "motorway", + "primary", + "secondary", + "tertiary", + "trunk" + ], + 16.0, + [ + "motorway_link", + "pedestrian", + "primary_link", + "secondary_link", + "street", + "street_limited", + "tertiary_link", + "trunk_link" + ], + 14.0, + 13.0 + ] + ], + "text-field": [ + "format", + [ + "coalesce", + [ + "get", + "name_en" + ], + [ + "get", + "name" + ] + ], + {} + ], + "symbol-placement": "line", + "text-rotation-alignment": "map", + "text-pitch-alignment": "viewport" + }, + "source": "composite", + "id": "road-label" + }, + { + "id": "building-models", + "minzoom": 14.0, + "paint": { + "model-ambient-occlusion-intensity": 0.75, + "model-color": "red", + "model-color-mix-intensity": [ + "match", + [ + "get", + "part" + ], + "logo", + [ + "interpolate", + [ "linear" ], + [ "measure-light", "brightness" ], + 0.2, + 0, + 0.4, + 0.3 + ], + 1.0 + ], + "model-emissive-strength": [ + "match", + [ + "get", + "part" + ], + "door", + [ + "interpolate", + [ "linear" ], + [ "measure-light", "brightness" ], + 0.2, + 1.5, + 0.4, + 2.5 + ], + "logo", + 0.6, + "window", + [ + "random", + 0.5, + 0.8, + [ + "id" + ] + ], + 0.0 + ], + "model-height-based-emissive-strength-multiplier": [ + "match", + [ + "get", + "part" + ], + "window", + [ + "literal", + [ + 0.0, + 0.9, + 0, + 1, + 0.5 + ] + ], + [ + "literal", + [ + 1.0, + 1.0, + 1.0, + 1.0, + 1.0 + ] + ] + ], + "model-opacity": [ + "interpolate", + [ "linear" ], + [ "zoom" ], + 14.2, + 0.0, + 14.5, + 1.0 + ], + "model-roughness": [ + "match", + [ + "get", + "part" + ], + "window", + 0.0, + 1.0 + ], + "model-scale": [ + "interpolate", + [ "linear" ], + [ "zoom" ], + 16.0, + [ + 1.0, + 1.0, + 0.0 + ], + 17.0, + [ + 1.0, + 1.0, + 3.5 + ] + ], + "model-front-cutoff": [0.1, 0.5, 0.0], + "model-type": "common-3d" + }, + "source": "file-system-tiles", + "type": "model" + } + ] +} diff --git a/test/unit/setup.ts b/test/unit/setup.ts index a3bca01cca9..59c2a3f7968 100644 --- a/test/unit/setup.ts +++ b/test/unit/setup.ts @@ -3,11 +3,19 @@ import {toHaveBeenCalledBefore, toHaveBeenCalledAfter} from 'jest-extended'; import {expect} from '../util/vitest'; + +// Load Error Handling +// https://vitejs.dev/guide/build#load-error-handling +window.addEventListener('vite:preloadError', (event) => { + console.log('vite:preloadError', event); + window.location.reload(); +}); + import mapboxgl from '../../src/index'; if (!globalThis.defined) { mapboxgl.workerParams = { - type: "module" + type: 'module' }; mapboxgl.workerUrl = '/src/source/worker.ts'; diff --git a/test/unit/source/load_tilejson.test.ts b/test/unit/source/load_tilejson.test.ts new file mode 100644 index 00000000000..5261c09db6f --- /dev/null +++ b/test/unit/source/load_tilejson.test.ts @@ -0,0 +1,101 @@ +// @ts-nocheck +import {describe, test, expect} from '../../util/vitest'; +import {Evented} from '../../../src/util/evented'; +import loadTileJSON from '../../../src/source/load_tilejson'; + +class StubMap extends Evented { + constructor() { + super(); + this._requestManager = { + canonicalizeTileset: tileJSON => tileJSON.tiles + }; + } +} + + +describe('LoadTileJson#variants', () => { + const map = new StubMap(); + + test('returns error if variants is not an array', () => { + // variants should be an array + let tileJSON = { + "tiles": ["http://dataset1"], + "variants": "variants should not be a string" + }; + let tileJSONRequest = loadTileJSON(tileJSON, map._requestManager, null, null, (err, result) => { + expect(err.message).toEqual("variants must be an array"); + }); + // variants elements should be objects + tileJSON = { + "tiles": ["http://dataset1"], + "variants": ["variants elements should be objects"] + }; + tileJSONRequest = loadTileJSON(tileJSON, map._requestManager, null, null, (err, result) => { + expect(err.message).toEqual("variant must be an object"); + }); + // capabilities should be an array + tileJSON = { + "tiles": ["http://dataset1"], + "variants": [ + { + "capabilities" : "capabilities should be an array" + } + ] + }; + tileJSONRequest = loadTileJSON(tileJSON, map._requestManager, null, null, (err, result) => { + expect(err.message).toEqual("capabilities must be an array"); + }); + // tiles should be replaced if capabilities.lenght == 1 and capabilities[0]== "meshopt" + tileJSON = { + "tiles": ["http://dataset1"], + "variants": [ + { + "capabilities" : ["meshopt"], + "tiles": ["http://dataset2"] + } + ] + }; + tileJSONRequest = loadTileJSON(tileJSON, map._requestManager, null, null, (err, result) => { + expect(err).toEqual(null); + expect(result.tiles).toEqual(["http://dataset2"]); + }); + // tiles should be replaced even when the above condition is met, and it happens in a different variant. + tileJSON = { + "tiles": ["http://dataset1"], + "variants": [ + { + "capabilities" : ["customcapability"], + "tiles": ["http://dataset2"] + }, + { + "capabilities" : ["meshopt"], + "tiles": ["http://dataset3"] + } + ] + }; + tileJSONRequest = loadTileJSON(tileJSON, map._requestManager, null, null, (err, result) => { + expect(err).toEqual(null); + expect(result.tiles).toEqual(["http://dataset3"]); + }); + // meshopt variants should replace other fields as well + tileJSON = { + "tiles": ["http://dataset1"], + "minzoom": 1, + "variants": [ + { + "capabilities" : ["customcapability"], + "tiles": ["http://dataset2"] + }, + { + "minzoom": 14, + "capabilities" : ["meshopt"], + "tiles": ["http://dataset3"] + } + ] + }; + tileJSONRequest = loadTileJSON(tileJSON, map._requestManager, null, null, (err, result) => { + expect(err).toEqual(null); + expect(result.minzoom).toEqual(14); + }); + }); +}); diff --git a/test/unit/style/style_imports.test.ts b/test/unit/style/style_imports.test.ts index 561e714d22e..a189212f4c0 100644 --- a/test/unit/style/style_imports.test.ts +++ b/test/unit/style/style_imports.test.ts @@ -1115,6 +1115,7 @@ describe('Style#addLayer', () => { }); }); }); + describe('Style#removeLayer', () => { test('same id in different scope is intact', async () => { const style = new Style(new StubMap()); @@ -1162,6 +1163,7 @@ describe('Style#removeLayer', () => { }); }); }); + describe('Style#moveLayer', () => { test('reorders layers', async () => { const style = new Style(new StubMap()); @@ -1225,6 +1227,7 @@ describe('Style#moveLayer', () => { }); }); }); + describe('Style#_mergeLayers', () => { test('supports slots', async () => { const style = new Style(new StubMap()); @@ -1556,6 +1559,46 @@ describe('Style#_mergeLayers', () => { ]); }); }); + +test('Style#getSlots', async () => { + const style = new Style(new StubMap()); + + const initialStyle = createStyleJSON({ + imports: [ + { + id: 'basemap', + url: '', + data: { + version: 8, + sources: {}, + layers: [ + {id: 'layer-0', type: 'background'}, + {id: 'bottom', type: 'slot'}, + {id: 'layer-1', type: 'background'}, + {id: 'middle', type: 'slot'}, + {id: 'layer-2', type: 'background'}, + {id: 'top', type: 'slot'}, + {id: 'layer-3', type: 'background'} + ] + } + } + ], + sources: {}, + layers: [ + {id: 'user-slot-1', type: 'slot', 'slot': 'middle'}, + {id: 'user-slot-2', type: 'slot'}, + {id: 'user-layer-1', type: 'background'} + ] + }); + + style.loadJSON(initialStyle); + await waitFor(style, 'style.load'); + expect(style.getSlots()).toEqual(['bottom', 'user-slot-1', 'middle', 'top', 'user-slot-2']); + + style.addLayer({id: 'runtime-slot-1', type: 'slot', slot: 'top'}); + expect(style.getSlots()).toEqual(['bottom', 'user-slot-1', 'middle', 'runtime-slot-1', 'top', 'user-slot-2']); +}); + describe('Style#getLights', () => { test('root style resolves lights from import', async () => { const style = new Style(new StubMap()); @@ -1966,6 +2009,7 @@ describe('Terrain', () => { expect(style.getTerrain().exaggeration).toEqual(2); }); }); + describe('Style#getFog', () => { test('resolves fog from import', async () => { const style = new Style(new StubMap()); @@ -2032,6 +2076,7 @@ describe('Style#getFog', () => { expect(fog['horizon-blend']).toEqual(0); }); }); + describe('Camera', () => { test('resolves camera from import', async () => { const map = new StubMap(); @@ -2136,6 +2181,7 @@ describe('Camera', () => { } ); }); + describe('Projection', () => { test('resolves projection from import', async () => { const map = new StubMap(); @@ -2199,6 +2245,7 @@ describe('Projection', () => { } ); }); + describe('Transition', () => { test('resolves transition from import', async () => { const style = new Style(new StubMap()); @@ -2230,6 +2277,7 @@ describe('Transition', () => { expect(style.transition).toEqual({duration: 600, delay: 100}); }); }); + describe('Glyphs', () => { test('fallbacks to the default glyphs URL', async () => { const style = new Style(new StubMap()); @@ -2445,6 +2493,7 @@ test('Style#setPaintProperty', () => { expect(spy).toHaveBeenCalledTimes(2); }); }); + test('Style#setLayerZoomRange', () => { const style = new Style(new StubMap()); @@ -2590,6 +2639,7 @@ describe('Style#setConfigProperty', () => { expect(layer.getLayoutProperty('visibility')).toEqual('visible'); }); }); + describe('Style#setState', () => { test('Adds fragment', async () => { const map = new StubMap(); @@ -2894,6 +2944,7 @@ describe('Style#setState', () => { ]); }); }); + test('Style#serialize', async () => { const style = new Style(new StubMap()); diff --git a/test/unit/ui/map_events.test.ts b/test/unit/ui/map_events.test.ts index 65f1630f877..513dadcf316 100644 --- a/test/unit/ui/map_events.test.ts +++ b/test/unit/ui/map_events.test.ts @@ -1077,3 +1077,13 @@ test('Map#off distinguishes distinct listeners - multiple layers', () => { expect(spy).not.toHaveBeenCalled(); }); }); + +test('Map#on style.load event has style property', async () => { + const map = createMap(); + await new Promise(resolve => { + map.once('style.load', (event) => { + expect(event.style).toEqual(map.style); + resolve(); + }); + }); +}); diff --git a/vitest.config.base.ts b/vitest.config.base.ts index 7034baea9e2..07a098e6620 100644 --- a/vitest.config.base.ts +++ b/vitest.config.base.ts @@ -1,10 +1,8 @@ // @ts-nocheck import {readFile} from 'node:fs/promises'; -import {fileURLToPath} from 'node:url'; import {defineConfig} from 'vite'; import {createFilter} from '@rollup/pluginutils'; -import alias from '@rollup/plugin-alias'; import arraybuffer from 'vite-plugin-arraybuffer'; function glsl(include: string[]) { @@ -38,6 +36,7 @@ function fixAssertUtil(regexp = /node_modules\/assert/) { } export default defineConfig({ + retry: 2, pool: 'threads', poolOptions: { threads: { @@ -57,7 +56,11 @@ export default defineConfig({ fileParallelism: false, }, restoreMocks: true, - unstubGlobals: true + unstubGlobals: true, + reporters: process.env.CI ? [ + ['html', {outputFile: './test/unit/vitest/index.html'}], + ['junit', {outputFile: './test/unit/test-results.xml'}], + ] : ['basic'], }, optimizeDeps: { esbuildOptions: {