From 2067f91632b3bdc9d305da58111581405a40353e Mon Sep 17 00:00:00 2001 From: Harlan Wilton Date: Tue, 18 Aug 2026 14:39:09 +1000 Subject: [PATCH] feat(maplibre)!: require MapLibre GL JS v6 MapLibre v6 dropped its UMD and CSP bundles, so the pinned unpkg script tag no longer resolves. The registry entry now runs in npm mode and imports the `maplibre-gl` package, matching how PostHog loads. - Peer dependency: `^5.24.0` -> `^6.0.0` - Registry `src` is `false`; the bundle capability is removed - Stylesheet comes from `maplibre-gl/dist/maplibre-gl.css`, so the pinned CDN URL and its integrity hash are gone - The worker URL is set from `maplibre-gl-worker.mjs?worker&url`, which MapLibre requires under any bundler. `workerUrl` still overrides it. Closes #857 --- .../3.performance-csp-and-accessibility.md | 20 +- docs/content/scripts/maplibre/index.md | 4 +- packages/script/package.json | 3 +- packages/script/src/registry-types.json | 11 +- packages/script/src/registry.ts | 7 +- .../components/MapLibre/ScriptMapLibreMap.vue | 2 +- .../script/src/runtime/maplibre-styles.ts | 36 ++-- .../script/src/runtime/registry/maplibre.ts | 55 ++++-- .../script/src/runtime/registry/schemas.ts | 12 +- packages/script/src/script-meta.ts | 3 +- pnpm-lock.yaml | 171 ++++-------------- test/unit/maplibre-registry.test.ts | 89 +++++++-- test/unit/maplibre-styles.test.ts | 28 +-- 13 files changed, 202 insertions(+), 239 deletions(-) diff --git a/docs/content/scripts/maplibre/1.guides/3.performance-csp-and-accessibility.md b/docs/content/scripts/maplibre/1.guides/3.performance-csp-and-accessibility.md index 966c0e7b..a8402f6a 100644 --- a/docs/content/scripts/maplibre/1.guides/3.performance-csp-and-accessibility.md +++ b/docs/content/scripts/maplibre/1.guides/3.performance-csp-and-accessibility.md @@ -75,7 +75,7 @@ For a decorative map, set `:interactive="false"`. The component disables input, ## Stylesheet Loading -Nuxt Scripts injects its version-pinned MapLibre stylesheet when the SDK starts loading. To bundle the stylesheet with your app instead: +Nuxt Scripts loads the stylesheet from the `maplibre-gl` package when the SDK starts loading. To control the stylesheet through Nuxt instead: ```ts [nuxt.config.ts] export default defineNuxtConfig({ @@ -93,27 +93,15 @@ export default defineNuxtConfig({ ## Content Security Policy -The standard MapLibre build creates a Blob worker. Its documented CSP includes `worker-src blob:`, `child-src blob:`, and `img-src data: blob:` alongside the origins used by your style. +MapLibre v6 loads its worker from a real URL served by your own build, so `worker-src blob:` is no longer required. The map still needs `img-src data: blob:` alongside the origins used by your style. -For a policy that does not allow Blob workers, self-host MapLibre's CSP build and worker: - -```ts [nuxt.config.ts] -export default defineNuxtConfig({ - scripts: { - registry: { - maplibre: { - scriptInput: { src: '/maplibre-gl-csp.js' }, - }, - }, - }, -}) -``` +To serve the worker from a different path, set `worker-url`: ```vue ``` diff --git a/docs/content/scripts/maplibre/index.md b/docs/content/scripts/maplibre/index.md index 68fa7c8a..a5231a80 100644 --- a/docs/content/scripts/maplibre/index.md +++ b/docs/content/scripts/maplibre/index.md @@ -14,14 +14,14 @@ links: [MapLibre GL JS](https://maplibre.org/maplibre-gl-js/docs/) is an open-source WebGL renderer for interactive vector maps. It renders the map and handles interaction; you choose the style, tile source, attribution, geocoding, and routing services separately. -Nuxt Scripts supports MapLibre GL JS 5.24 through the [`useScriptMapLibre()`{lang="ts"}](/scripts/maplibre/api/use-script-maplibre) composable and declarative components for common map resources. First-party mode can bundle the JavaScript SDK. Nuxt Scripts loads the required MapLibre stylesheet separately by default. +Nuxt Scripts supports MapLibre GL JS v6 through the [`useScriptMapLibre()`{lang="ts"}](/scripts/maplibre/api/use-script-maplibre) composable and declarative components for common map resources. MapLibre v6 is ESM-only, so Nuxt Scripts loads it from the `maplibre-gl` package rather than a CDN script tag. The stylesheet comes from the same package. ::script-types{exclude-components} :: ## Setup -Install MapLibre for its TypeScript definitions, then enable the registry entry: +Install MapLibre v6, then enable the registry entry: ```bash pnpm add -D maplibre-gl diff --git a/packages/script/package.json b/packages/script/package.json index 5fb95547..f4b0b661 100644 --- a/packages/script/package.json +++ b/packages/script/package.json @@ -69,6 +69,7 @@ "unimport", "#nuxt-scripts/types", "posthog-js", + "maplibre-gl", "@nuxt/devtools-kit", "sirv" ] @@ -84,7 +85,7 @@ "@types/vimeo__player": "^2.18.3", "@types/youtube": "^0.1.0", "@unhead/vue": "^3.3.1", - "maplibre-gl": "^5.24.0", + "maplibre-gl": "^6.0.0", "posthog-js": "^1.0.0", "unhead": "^3.3.1" }, diff --git a/packages/script/src/registry-types.json b/packages/script/src/registry-types.json index 67ab823c..e9a7be70 100644 --- a/packages/script/src/registry-types.json +++ b/packages/script/src/registry-types.json @@ -822,7 +822,7 @@ { "name": "MapLibreOptions", "kind": "const", - "code": "export const MapLibreOptions = object({\n /**\n * Inject the MapLibre GL JS 5.24.0 stylesheet when the script begins\n * loading. Disable this when supplying the stylesheet through Nuxt.\n * @default true\n */\n injectStyles: optional(boolean()),\n /**\n * Stylesheet URL used when `injectStyles` is enabled.\n * @default 'https://unpkg.com/maplibre-gl@5.24.0/dist/maplibre-gl.css'\n */\n stylesheetUrl: optional(string()),\n /**\n * Worker URL for the CSP-compatible MapLibre build. Pair this with a custom\n * `scriptInput.src` that loads `maplibre-gl-csp.js`.\n */\n workerUrl: optional(string()),\n})" + "code": "export const MapLibreOptions = object({\n /**\n * Inject the MapLibre stylesheet when the script begins loading. Disable\n * this when supplying the stylesheet through Nuxt.\n * @default true\n */\n injectStyles: optional(boolean()),\n /**\n * Stylesheet URL used when `injectStyles` is enabled. Defaults to the\n * stylesheet shipped in the `maplibre-gl` package.\n */\n stylesheetUrl: optional(string()),\n /**\n * Worker URL passed to `maplibregl.setWorkerUrl()`. Set this when serving\n * the MapLibre worker from your own origin.\n */\n workerUrl: optional(string()),\n})" }, { "name": "MapLibreApi", @@ -837,7 +837,7 @@ { "name": "ScriptMapLibreMapProps", "kind": "interface", - "code": "interface ScriptMapLibreMapProps {\n /**\n * Defines when the MapLibre script loads.\n * @default 'visible'\n */\n trigger?: ElementScriptTrigger\n /** MapLibre style URL or inline style specification. */\n mapStyle: string | MapLibre.StyleSpecification\n /** Initial and reactively controlled map center. */\n center: MapLibre.LngLatLike\n /** Initial and reactively controlled zoom level. @default 12 */\n zoom?: number\n /** Initial and reactively controlled bearing in degrees. @default 0 */\n bearing?: number\n /** Initial and reactively controlled pitch in degrees. @default 0 */\n pitch?: number\n /** Options passed to `new maplibregl.Map()`. Dedicated props take precedence. */\n options?: Omit\n /** Inject MapLibre's stylesheet when the script begins loading. @default true */\n injectStyles?: boolean\n /** Custom MapLibre stylesheet URL. */\n stylesheetUrl?: string\n /** Worker URL used with MapLibre's CSP-compatible build. */\n workerUrl?: string\n /** Width reserved before the map loads. @default 640 */\n width?: number | string\n /** Height reserved before the map loads. @default 400 */\n height?: number | string\n /** Accessible name for an interactive map. @default 'Interactive map' */\n ariaLabel?: string\n /** Disable map input and remove it from the accessibility tree when decorative. @default true */\n interactive?: boolean\n /** Attributes applied to the outer layout container. */\n rootAttrs?: HTMLAttributes & ReservedProps & Record\n}" + "code": "interface ScriptMapLibreMapProps {\n /**\n * Defines when the MapLibre script loads.\n * @default 'visible'\n */\n trigger?: ElementScriptTrigger\n /** MapLibre style URL or inline style specification. */\n mapStyle: string | MapLibre.StyleSpecification\n /** Initial and reactively controlled map center. */\n center: MapLibre.LngLatLike\n /** Initial and reactively controlled zoom level. @default 12 */\n zoom?: number\n /** Initial and reactively controlled bearing in degrees. @default 0 */\n bearing?: number\n /** Initial and reactively controlled pitch in degrees. @default 0 */\n pitch?: number\n /** Options passed to `new maplibregl.Map()`. Dedicated props take precedence. */\n options?: Omit\n /** Inject MapLibre's stylesheet when the script begins loading. @default true */\n injectStyles?: boolean\n /** Custom MapLibre stylesheet URL. */\n stylesheetUrl?: string\n /** Worker URL passed to `maplibregl.setWorkerUrl()`. */\n workerUrl?: string\n /** Width reserved before the map loads. @default 640 */\n width?: number | string\n /** Height reserved before the map loads. @default 400 */\n height?: number | string\n /** Accessible name for an interactive map. @default 'Interactive map' */\n ariaLabel?: string\n /** Disable map input and remove it from the accessibility tree when decorative. @default true */\n interactive?: boolean\n /** Attributes applied to the outer layout container. */\n rootAttrs?: HTMLAttributes & ReservedProps & Record\n}" }, { "name": "ScriptMapLibreMapEvents", @@ -1482,21 +1482,20 @@ "name": "injectStyles", "type": "boolean", "required": false, - "description": "Inject the MapLibre GL JS 5.24.0 stylesheet when the script begins loading. Disable this when supplying the stylesheet through Nuxt.", + "description": "Inject the MapLibre stylesheet when the script begins loading. Disable this when supplying the stylesheet through Nuxt.", "defaultValue": "true" }, { "name": "stylesheetUrl", "type": "string", "required": false, - "description": "Stylesheet URL used when `injectStyles` is enabled.", - "defaultValue": "'https://unpkg.com/maplibre-gl@5.24.0/dist/maplibre-gl.css'" + "description": "Stylesheet URL used when `injectStyles` is enabled. Defaults to the stylesheet shipped in the `maplibre-gl` package." }, { "name": "workerUrl", "type": "string", "required": false, - "description": "Worker URL for the CSP-compatible MapLibre build. Pair this with a custom `scriptInput.src` that loads `maplibre-gl-csp.js`." + "description": "Worker URL passed to `maplibregl.setWorkerUrl()`. Set this when serving the MapLibre worker from your own origin." } ], "AhrefsAnalyticsOptions": [ diff --git a/packages/script/src/registry.ts b/packages/script/src/registry.ts index ed79bd24..b1c84d3e 100644 --- a/packages/script/src/registry.ts +++ b/packages/script/src/registry.ts @@ -162,7 +162,7 @@ export const registryMeta: RegistryScriptMeta[] = [ // content m('googleMaps', 'Google Maps', 'content', 'useScriptGoogleMaps', {}, null), m('leaflet', 'Leaflet', 'content', 'useScriptLeaflet', { bundle: true }, null), - m('maplibre', 'MapLibre GL JS', 'content', 'useScriptMapLibre', { bundle: true }, null), + m('maplibre', 'MapLibre GL JS', 'content', 'useScriptMapLibre', {}, null), m('instagramEmbed', 'Instagram Embed', 'content', false, {}, null), m('xEmbed', 'X Embed', 'content', false, {}, null), m('blueskyEmbed', 'Bluesky Embed', 'content', false, {}, null), @@ -708,9 +708,10 @@ export async function registry(resolve?: (path: string) => Promise): Pro composableName: 'useScriptMapLibre', schema: MapLibreOptions, label: 'MapLibre GL JS', - src: 'https://unpkg.com/maplibre-gl@5.24.0/dist/maplibre-gl.js', + // MapLibre GL JS v6 is ESM-only, so it loads from the `maplibre-gl` + // package rather than a CDN script tag. + src: false, category: 'content', - bundle: true, }), def('blueskyEmbed', { composableName: false, diff --git a/packages/script/src/runtime/components/MapLibre/ScriptMapLibreMap.vue b/packages/script/src/runtime/components/MapLibre/ScriptMapLibreMap.vue index 0642dc8f..0c7722dd 100644 --- a/packages/script/src/runtime/components/MapLibre/ScriptMapLibreMap.vue +++ b/packages/script/src/runtime/components/MapLibre/ScriptMapLibreMap.vue @@ -27,7 +27,7 @@ export interface ScriptMapLibreMapProps { injectStyles?: boolean /** Custom MapLibre stylesheet URL. */ stylesheetUrl?: string - /** Worker URL used with MapLibre's CSP-compatible build. */ + /** Worker URL passed to `maplibregl.setWorkerUrl()`. */ workerUrl?: string /** Width reserved before the map loads. @default 640 */ width?: number | string diff --git a/packages/script/src/runtime/maplibre-styles.ts b/packages/script/src/runtime/maplibre-styles.ts index f18944e1..39ab888e 100644 --- a/packages/script/src/runtime/maplibre-styles.ts +++ b/packages/script/src/runtime/maplibre-styles.ts @@ -1,15 +1,32 @@ -export const MAPLIBRE_STYLESHEET_URL = 'https://unpkg.com/maplibre-gl@5.24.0/dist/maplibre-gl.css' -export const MAPLIBRE_STYLESHEET_INTEGRITY = 'sha384-uTttxo/aOKbdE5RlD/SPzSDoDmNvGlUYPjONi2MN/b7c9HPSvW07OIuyP7uL6jxK' - const MAPLIBRE_STYLE_ID = 'nuxt-scripts-maplibre-styles' -export function configureMapLibreWorker(maplibre: { setWorkerUrl: (value: string) => void } | undefined, workerUrl?: string): void { - if (maplibre && workerUrl) +interface MapLibreWorkerApi { + setWorkerUrl: (value: string) => void +} + +/** + * Points MapLibre at its worker. + * + * MapLibre v6 resolves the worker from `import.meta.url`, which does not + * survive bundling, so the URL has to be set explicitly. `?worker&url` is + * required over plain `?url`: the worker imports its sibling + * `maplibre-gl-shared.mjs`. + * + * @see https://maplibre.org/maplibre-gl-js/docs/#installation + */ +export async function configureMapLibreWorker(maplibre: MapLibreWorkerApi | undefined, workerUrl?: string): Promise { + if (!maplibre) + return + if (workerUrl) { maplibre.setWorkerUrl(workerUrl) + return + } + const { default: bundledWorkerUrl } = await import('maplibre-gl/dist/maplibre-gl-worker.mjs?worker&url') + maplibre.setWorkerUrl(bundledWorkerUrl) } -/** Injects MapLibre's required control and marker stylesheet once. */ -export function ensureMapLibreStyles(stylesheetUrl = MAPLIBRE_STYLESHEET_URL): void { +/** Injects a custom MapLibre control and marker stylesheet once. */ +export function ensureMapLibreStyles(stylesheetUrl: string): void { if (typeof document === 'undefined') return @@ -25,10 +42,5 @@ export function ensureMapLibreStyles(stylesheetUrl = MAPLIBRE_STYLESHEET_URL): v link.id = injectedStylesheets.length ? `${MAPLIBRE_STYLE_ID}-${injectedStylesheets.length + 1}` : MAPLIBRE_STYLE_ID link.dataset.nuxtScriptsMaplibre = '1' - if (stylesheetUrl === MAPLIBRE_STYLESHEET_URL) { - link.integrity = MAPLIBRE_STYLESHEET_INTEGRITY - link.crossOrigin = 'anonymous' - } - document.head.append(link) } diff --git a/packages/script/src/runtime/registry/maplibre.ts b/packages/script/src/runtime/registry/maplibre.ts index 22196119..f85bb8ef 100644 --- a/packages/script/src/runtime/registry/maplibre.ts +++ b/packages/script/src/runtime/registry/maplibre.ts @@ -1,6 +1,6 @@ import type * as MapLibre from 'maplibre-gl' import type { RegistryScriptInput } from '#nuxt-scripts/types' -import { configureMapLibreWorker, ensureMapLibreStyles, MAPLIBRE_STYLESHEET_URL } from '../maplibre-styles' +import { configureMapLibreWorker, ensureMapLibreStyles } from '../maplibre-styles' import { useRegistryScript } from '../utils' import { MapLibreOptions } from './schemas' @@ -13,34 +13,53 @@ export interface MapLibreApi { } declare global { - interface Window extends MapLibreApi {} + interface Window { + maplibregl?: typeof MapLibre + } } +/** + * MapLibre GL JS v6 is an ESM-only distribution. It has no UMD build, and its + * worker resolves against `import.meta.url`, so it is loaded from the + * `maplibre-gl` package instead of a CDN script tag. + */ export function useScriptMapLibre(_options?: MapLibreInput) { - return useRegistryScript('maplibre', (options, context) => { + return useRegistryScript('maplibre', (options) => { const injectStyles = options?.injectStyles !== false - const stylesheetUrl = options?.stylesheetUrl || MAPLIBRE_STYLESHEET_URL - const usesDefaultSource = !context.scriptInput?.src + const stylesheetUrl = options?.stylesheetUrl return { - scriptInput: { - src: 'https://unpkg.com/maplibre-gl@5.24.0/dist/maplibre-gl.js', - ...(usesDefaultSource - ? { - integrity: 'sha384-5+cfbwT0iiub6VsQAdn6yz16nr6sDiQoHx6tm4O8OVYXHYOxcffFmCJBL0dgdvGp', - crossorigin: 'anonymous', - } - : {}), - }, - clientInit: injectStyles ? () => ensureMapLibreStyles(stylesheetUrl) : undefined, + scriptMode: 'npm', schema: import.meta.dev ? MapLibreOptions : undefined, scriptOptions: { use() { - const maplibregl = window.maplibregl - configureMapLibreWorker(maplibregl, options?.workerUrl) - return { maplibregl } + return import.meta.client && window.maplibregl + ? { maplibregl: window.maplibregl } + : undefined }, }, + clientInit: import.meta.server + ? undefined + : async (ctx) => { + const throwIfAborted = () => { + if (ctx?.signal.aborted) + throw ctx.signal.reason || new Error('Loading MapLibre was aborted') + } + throwIfAborted() + + if (injectStyles) { + if (stylesheetUrl) + ensureMapLibreStyles(stylesheetUrl) + else + await import('maplibre-gl/dist/maplibre-gl.css') + } + + const maplibregl = window.maplibregl || await import('maplibre-gl') + throwIfAborted() + await configureMapLibreWorker(maplibregl, options?.workerUrl) + window.maplibregl = maplibregl + return { maplibregl } + }, } }, _options) } diff --git a/packages/script/src/runtime/registry/schemas.ts b/packages/script/src/runtime/registry/schemas.ts index fc5b13b6..815800a1 100644 --- a/packages/script/src/runtime/registry/schemas.ts +++ b/packages/script/src/runtime/registry/schemas.ts @@ -30,19 +30,19 @@ export const LeafletOptions = object({ export const MapLibreOptions = object({ /** - * Inject the MapLibre GL JS 5.24.0 stylesheet when the script begins - * loading. Disable this when supplying the stylesheet through Nuxt. + * Inject the MapLibre stylesheet when the script begins loading. Disable + * this when supplying the stylesheet through Nuxt. * @default true */ injectStyles: optional(boolean()), /** - * Stylesheet URL used when `injectStyles` is enabled. - * @default 'https://unpkg.com/maplibre-gl@5.24.0/dist/maplibre-gl.css' + * Stylesheet URL used when `injectStyles` is enabled. Defaults to the + * stylesheet shipped in the `maplibre-gl` package. */ stylesheetUrl: optional(string()), /** - * Worker URL for the CSP-compatible MapLibre build. Pair this with a custom - * `scriptInput.src` that loads `maplibre-gl-csp.js`. + * Worker URL passed to `maplibregl.setWorkerUrl()`. Set this when serving + * the MapLibre worker from your own origin. */ workerUrl: optional(string()), }) diff --git a/packages/script/src/script-meta.ts b/packages/script/src/script-meta.ts index 1a96ebf1..59c490a6 100644 --- a/packages/script/src/script-meta.ts +++ b/packages/script/src/script-meta.ts @@ -188,7 +188,8 @@ export const scriptMeta = { trackedData: [], }, maplibre: { - urls: ['https://unpkg.com/maplibre-gl@5.24.0/dist/maplibre-gl.js'], + // Loaded from the `maplibre-gl` package, not a CDN. + urls: [], trackedData: [], }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 21ae433e..d272220a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -203,7 +203,7 @@ importers: devDependencies: '@antfu/eslint-config': specifier: 'catalog:' - version: 9.2.0(@typescript-eslint/typescript-estree@8.65.0(supports-color@10.2.2)(typescript@6.0.3))(@typescript-eslint/utils@8.65.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(@vue/compiler-sfc@3.5.40)(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)(vitest@4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.2)(happy-dom@20.11.1)(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))) + version: 9.2.0(@typescript-eslint/typescript-estree@8.65.0(supports-color@10.2.2)(typescript@6.0.3))(@typescript-eslint/utils@8.65.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(@vue/compiler-sfc@3.5.41)(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)(vitest@4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.2)(happy-dom@20.11.1)(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))) '@googlemaps/markerclusterer': specifier: 'catalog:' version: 2.6.2 @@ -275,7 +275,7 @@ importers: version: 6.1.0 nuxt: specifier: 'catalog:' - version: 4.5.1(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@oxc-project/types@0.143.0)(@parcel/watcher@2.5.6)(@types/node@26.1.2)(@vue/compiler-sfc@3.5.40)(cac@6.7.14)(db0@0.3.4)(esbuild@0.28.1)(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(ioredis@5.11.1(supports-color@10.2.2))(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(oxc-parser@0.143.0)(rollup-plugin-visualizer@7.0.1(rolldown@1.2.0)(rollup@4.62.4))(rollup@4.62.4)(srvx@0.11.22)(supports-color@10.2.2)(terser@5.48.0)(typescript@6.0.3)(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue-tsc@3.3.9(typescript@6.0.3))(yaml@2.9.0) + version: 4.5.1(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@oxc-project/types@0.143.0)(@parcel/watcher@2.5.6)(@types/node@26.1.2)(@vue/compiler-sfc@3.5.41)(cac@6.7.14)(db0@0.3.4)(esbuild@0.28.1)(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(ioredis@5.11.1(supports-color@10.2.2))(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(oxc-parser@0.143.0)(rollup-plugin-visualizer@7.0.1(rolldown@1.2.0)(rollup@4.62.4))(rollup@4.62.4)(srvx@0.11.22)(supports-color@10.2.2)(terser@5.48.0)(typescript@6.0.3)(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue-tsc@3.3.9(typescript@6.0.3))(yaml@2.9.0) ofetch: specifier: 'catalog:' version: 1.5.1 @@ -425,8 +425,8 @@ importers: specifier: 'catalog:' version: 1.1.0 maplibre-gl: - specifier: ^5.24.0 - version: 5.24.0 + specifier: ^6.0.0 + version: 6.1.0 ofetch: specifier: 'catalog:' version: 1.5.1 @@ -1479,29 +1479,15 @@ packages: '@mapbox/tiny-sdf@2.2.0': resolution: {integrity: sha512-LVL4wgI9YAum5V+LNVQO6QgFBPw7/MIIY4XJPNsPDMrjEwcE+JfKk1LuIl8GnF197ejVdC9QdPaxrx5gfgdGXg==} - '@mapbox/unitbezier@0.0.1': - resolution: {integrity: sha512-nMkuDXFv60aBr9soUG5q+GvZYL+2KZHVvsqFCzqnkGEf46U2fvmytHaEVc1/YZbiLn8X+eR3QzX1+dwDO1lxlw==} - '@mapbox/unitbezier@1.0.0': resolution: {integrity: sha512-fqd515fjBmANKGGsQ286E2Wvj/XvDFpGzwJxq4CI6jMQue6Oy04uCKp+JWKF00xRTmk6cEu1jPJ9p3xqH8YWqQ==} - '@mapbox/vector-tile@2.0.5': - resolution: {integrity: sha512-pXj8m7KTsqZt+1jsE0xIpGvqTSbblfkuEJL/NJmNePMtEwxO8V3XMDo9WMSfDeqHvCtBI9Lmt4mGcGR10zecmw==} - '@mapbox/vector-tile@3.0.0': resolution: {integrity: sha512-Qf10S1uIHMk20ri/IVBnpS+esUEkVaR5Hftmz88jTInrpmWgPGJfPe3LVjjlE77trLx8tH6qjTG7uWH9hIq/0Q==} - '@mapbox/whoots-js@3.1.0': - resolution: {integrity: sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q==} - engines: {node: '>=6.0.0'} - '@maplibre/geojson-vt@6.1.1': resolution: {integrity: sha512-FVMOcmSP/yqol45t7StApEyTL5/vmqBCuFhH9n+fFuINenhaX+YgHHIt1yJ86S8kln3uJLcMvmEU2cfn6E2eCQ==} - '@maplibre/maplibre-gl-style-spec@24.10.0': - resolution: {integrity: sha512-lichxSiagMEBBrqHF0trtMQH9RKh+9jUlIJl0qW0QHvt2H/tbvUWdE+ZzI2Jd0/pT7j/iavLonlPu7EQ/ixTOw==} - hasBin: true - '@maplibre/maplibre-gl-style-spec@26.2.1': resolution: {integrity: sha512-QFKCXkOeSzOr8jF75jm6kySOg+dUvOehPhRi68gcOYPHb7U5JloUq0dJW0Y5/fZV8ygfT0Vp2RWodvq+fyxFWA==} hasBin: true @@ -5142,10 +5128,6 @@ packages: resolution: {integrity: sha512-ayF7iT+44LXdxJLTrTd3TLQpFDDvPCBxXxbv+pMUSuHA5Q8zyAfwkRP6aHHwNVFBUFWtxAHqwNJxF8vMZLAbVg==} engines: {node: '>=18'} - maplibre-gl@5.24.0: - resolution: {integrity: sha512-ALyFxgtd5R+65UqZ/++lOqwWcC0SNho9c27fYSyLmG7AfnAul2o46F05aDJGPbFU57wos9dgcIySHs0Xe6ia3A==} - engines: {node: '>=16.14.0', npm: '>=8.1.0'} - maplibre-gl@6.1.0: resolution: {integrity: sha512-vLRukjvbUai4SXW2/jKo8rPsw6YMXuA/b4fKFVSulZKFVRHkOvk4ZmNlDSVZpTYPV0/7/iTqq8ltYdyr764b7w==} engines: {node: '>=16.14.0', npm: '>=8.1.0'} @@ -5657,10 +5639,6 @@ packages: pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} - pbf@4.0.2: - resolution: {integrity: sha512-J0ajxARhZfpUEebxYs1vhMGMuLSXtBe1e+fFPDrf2uA2hgo+UshKfNUWOz92HJNz6/NFEXseQPddnHkTreWRqg==} - hasBin: true - pbf@5.1.2: resolution: {integrity: sha512-mnvGdvOrIvJOBGUEdGkrVXjN8E/VkIJCkf2eS1DH2yv82ORUlLttmDt0rWY38yYZmVwciZwBUvHM20qxBZf40w==} hasBin: true @@ -7379,7 +7357,7 @@ snapshots: '@alloc/quick-lru@5.2.0': {} - '@antfu/eslint-config@9.2.0(@typescript-eslint/typescript-estree@8.65.0(supports-color@10.2.2)(typescript@6.0.3))(@typescript-eslint/utils@8.65.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(@vue/compiler-sfc@3.5.40)(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)(vitest@4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.2)(happy-dom@20.11.1)(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)))': + '@antfu/eslint-config@9.2.0(@typescript-eslint/typescript-estree@8.65.0(supports-color@10.2.2)(typescript@6.0.3))(@typescript-eslint/utils@8.65.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(@vue/compiler-sfc@3.5.41)(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)(vitest@4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.1.2)(happy-dom@20.11.1)(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)))': dependencies: '@antfu/install-pkg': 1.1.0 '@clack/prompts': 1.7.0 @@ -7411,7 +7389,7 @@ snapshots: eslint-plugin-unused-imports: 4.4.1(@typescript-eslint/eslint-plugin@8.65.0(@typescript-eslint/parser@8.65.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2)) eslint-plugin-vue: 10.10.0(@stylistic/eslint-plugin@5.10.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2)))(@typescript-eslint/parser@8.65.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(vue-eslint-parser@10.4.1(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)) eslint-plugin-yml: 3.7.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2)) - eslint-processor-vue-blocks: 2.0.0(@vue/compiler-sfc@3.5.40)(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2)) + eslint-processor-vue-blocks: 2.0.0(@vue/compiler-sfc@3.5.41)(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2)) globals: 17.7.0 local-pkg: 1.2.1 parse-gitignore: 2.0.0 @@ -7449,10 +7427,10 @@ snapshots: '@babel/helper-compilation-targets': 7.29.7 '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) '@babel/helpers': 7.29.7 - '@babel/parser': 7.29.7 + '@babel/parser': 7.29.8 '@babel/template': 7.29.7 '@babel/traverse': 7.29.7(supports-color@10.2.2) - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 '@jridgewell/remapping': 2.3.5 convert-source-map: 2.0.0 debug: 4.4.3(supports-color@10.2.2) @@ -7464,8 +7442,8 @@ snapshots: '@babel/generator@7.29.7': dependencies: - '@babel/parser': 7.29.7 - '@babel/types': 7.29.7 + '@babel/parser': 7.29.8 + '@babel/types': 7.29.8 '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 jsesc: 3.1.0 @@ -7516,7 +7494,7 @@ snapshots: '@babel/helper-module-imports@7.29.7(supports-color@10.2.2)': dependencies: '@babel/traverse': 7.29.7(supports-color@10.2.2) - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 transitivePeerDependencies: - supports-color @@ -7564,7 +7542,7 @@ snapshots: '@babel/helpers@7.29.7': dependencies: '@babel/template': 7.29.7 - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 '@babel/parser@7.29.7': dependencies: @@ -7602,17 +7580,17 @@ snapshots: '@babel/template@7.29.7': dependencies: '@babel/code-frame': 7.29.7 - '@babel/parser': 7.29.7 - '@babel/types': 7.29.7 + '@babel/parser': 7.29.8 + '@babel/types': 7.29.8 '@babel/traverse@7.29.7(supports-color@10.2.2)': dependencies: '@babel/code-frame': 7.29.7 '@babel/generator': 7.29.7 '@babel/helper-globals': 7.29.7 - '@babel/parser': 7.29.7 + '@babel/parser': 7.29.8 '@babel/template': 7.29.7 - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 debug: 4.4.3(supports-color@10.2.2) transitivePeerDependencies: - supports-color @@ -8244,37 +8222,18 @@ snapshots: '@mapbox/tiny-sdf@2.2.0': {} - '@mapbox/unitbezier@0.0.1': {} - '@mapbox/unitbezier@1.0.0': {} - '@mapbox/vector-tile@2.0.5': - dependencies: - '@mapbox/point-geometry': 1.1.0 - '@types/geojson': 7946.0.16 - pbf: 4.0.2 - '@mapbox/vector-tile@3.0.0': dependencies: '@mapbox/point-geometry': 1.1.0 '@types/geojson': 7946.0.16 pbf: 5.1.2 - '@mapbox/whoots-js@3.1.0': {} - '@maplibre/geojson-vt@6.1.1': dependencies: kdbush: 4.1.0 - '@maplibre/maplibre-gl-style-spec@24.10.0': - dependencies: - '@mapbox/jsonlint-lines-primitives': 2.0.3 - '@mapbox/unitbezier': 1.0.0 - json-stringify-pretty-compact: 4.0.0 - minimist: 1.2.8 - quickselect: 3.0.0 - tinyqueue: 3.0.0 - '@maplibre/maplibre-gl-style-spec@26.2.1': dependencies: '@mapbox/jsonlint-lines-primitives': 2.0.3 @@ -8915,7 +8874,7 @@ snapshots: - vue-tsc - webpack - '@nuxt/nitro-server@4.5.1(186d28e6bb39350865619c9a810edaaa)': + '@nuxt/nitro-server@4.5.1(b2e46168f2ef9884385715bfcac8a509)': dependencies: '@nuxt/devalue': 2.0.2 '@nuxt/kit': 4.5.1(magic-string@1.0.0)(magicast@0.5.3)(oxc-parser@0.143.0)(rolldown@1.2.0)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.0)(rollup@4.62.4)(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))) @@ -8934,7 +8893,7 @@ snapshots: mocked-exports: 0.1.1 nitropack: 2.13.4(oxc-parser@0.143.0)(rolldown@1.2.0)(srvx@0.11.22)(supports-color@10.2.2)(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) nostics: 1.2.0 - nuxt: 4.5.1(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@oxc-project/types@0.143.0)(@parcel/watcher@2.5.6)(@types/node@26.1.2)(@vue/compiler-sfc@3.5.40)(cac@6.7.14)(db0@0.3.4)(esbuild@0.28.1)(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(ioredis@5.11.1(supports-color@10.2.2))(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(oxc-parser@0.143.0)(rollup-plugin-visualizer@7.0.1(rolldown@1.2.0)(rollup@4.62.4))(rollup@4.62.4)(srvx@0.11.22)(supports-color@10.2.2)(terser@5.48.0)(typescript@6.0.3)(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue-tsc@3.3.9(typescript@6.0.3))(yaml@2.9.0) + nuxt: 4.5.1(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@oxc-project/types@0.143.0)(@parcel/watcher@2.5.6)(@types/node@26.1.2)(@vue/compiler-sfc@3.5.41)(cac@6.7.14)(db0@0.3.4)(esbuild@0.28.1)(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(ioredis@5.11.1(supports-color@10.2.2))(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(oxc-parser@0.143.0)(rollup-plugin-visualizer@7.0.1(rolldown@1.2.0)(rollup@4.62.4))(rollup@4.62.4)(srvx@0.11.22)(supports-color@10.2.2)(terser@5.48.0)(typescript@6.0.3)(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue-tsc@3.3.9(typescript@6.0.3))(yaml@2.9.0) nypm: 0.6.8 ohash: 2.0.11 pathe: 2.0.3 @@ -9096,7 +9055,7 @@ snapshots: '@nuxt/schema@4.5.1': dependencies: - '@vue/shared': 3.5.40 + '@vue/shared': 3.5.41 defu: 6.1.7 nostics: 1.2.0 pathe: 2.0.3 @@ -9402,7 +9361,7 @@ snapshots: - vue-tsc - yaml - '@nuxt/vite-builder@4.5.1(d3bd8e16a3e4c74d511b3b43451dca2f)': + '@nuxt/vite-builder@4.5.1(3fceaf98b1915314d813404cfbf6a16e)': dependencies: '@nuxt/kit': 4.5.1(magic-string@1.0.0)(magicast@0.5.3)(oxc-parser@0.143.0)(rolldown@1.2.0)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.0)(rollup@4.62.4)(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))) '@vitejs/plugin-vue': 6.0.8(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3)) @@ -9420,7 +9379,7 @@ snapshots: knitwork: 1.3.0 mlly: 1.8.2 mocked-exports: 0.1.1 - nuxt: 4.5.1(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@oxc-project/types@0.143.0)(@parcel/watcher@2.5.6)(@types/node@26.1.2)(@vue/compiler-sfc@3.5.40)(cac@6.7.14)(db0@0.3.4)(esbuild@0.28.1)(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(ioredis@5.11.1(supports-color@10.2.2))(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(oxc-parser@0.143.0)(rollup-plugin-visualizer@7.0.1(rolldown@1.2.0)(rollup@4.62.4))(rollup@4.62.4)(srvx@0.11.22)(supports-color@10.2.2)(terser@5.48.0)(typescript@6.0.3)(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue-tsc@3.3.9(typescript@6.0.3))(yaml@2.9.0) + nuxt: 4.5.1(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@oxc-project/types@0.143.0)(@parcel/watcher@2.5.6)(@types/node@26.1.2)(@vue/compiler-sfc@3.5.41)(cac@6.7.14)(db0@0.3.4)(esbuild@0.28.1)(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(ioredis@5.11.1(supports-color@10.2.2))(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(oxc-parser@0.143.0)(rollup-plugin-visualizer@7.0.1(rolldown@1.2.0)(rollup@4.62.4))(rollup@4.62.4)(srvx@0.11.22)(supports-color@10.2.2)(terser@5.48.0)(typescript@6.0.3)(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue-tsc@3.3.9(typescript@6.0.3))(yaml@2.9.0) nypm: 0.6.8 pathe: 2.0.3 pkg-types: 2.3.1 @@ -10751,7 +10710,7 @@ snapshots: '@vue-macros/common@3.1.3(vue@3.5.41(typescript@6.0.3))': dependencies: - '@vue/compiler-sfc': 3.5.40 + '@vue/compiler-sfc': 3.5.41 ast-kit: 2.2.0 local-pkg: 1.2.1 magic-string-ast: 1.0.3 @@ -10870,8 +10829,8 @@ snapshots: '@vue/language-core@3.3.9': dependencies: '@volar/language-core': 2.4.28 - '@vue/compiler-dom': 3.5.40 - '@vue/shared': 3.5.40 + '@vue/compiler-dom': 3.5.41 + '@vue/shared': 3.5.41 alien-signals: 3.2.1 muggle-string: 0.4.1 path-browserify: 1.0.1 @@ -11052,13 +11011,13 @@ snapshots: ast-kit@2.2.0: dependencies: - '@babel/parser': 7.29.7 + '@babel/parser': 7.29.8 pathe: 2.0.3 ast-walker-scope@0.9.0: dependencies: - '@babel/parser': 7.29.7 - '@babel/types': 7.29.7 + '@babel/parser': 7.29.8 + '@babel/types': 7.29.8 ast-kit: 2.2.0 async-sema@3.1.1: {} @@ -11937,9 +11896,9 @@ snapshots: natural-compare: 1.4.0 yaml-eslint-parser: 2.0.0 - eslint-processor-vue-blocks@2.0.0(@vue/compiler-sfc@3.5.40)(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2)): + eslint-processor-vue-blocks@2.0.0(@vue/compiler-sfc@3.5.41)(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2)): dependencies: - '@vue/compiler-sfc': 3.5.40 + '@vue/compiler-sfc': 3.5.41 eslint: 10.8.0(jiti@2.7.0)(supports-color@10.2.2) eslint-scope@9.1.2: @@ -12799,8 +12758,8 @@ snapshots: magicast@0.5.4: dependencies: - '@babel/parser': 7.29.7 - '@babel/types': 7.29.7 + '@babel/parser': 7.29.8 + '@babel/types': 7.29.8 source-map-js: 1.2.1 make-asynchronous@1.1.0: @@ -12809,28 +12768,6 @@ snapshots: type-fest: 4.41.0 web-worker: 1.5.0 - maplibre-gl@5.24.0: - dependencies: - '@mapbox/jsonlint-lines-primitives': 2.0.3 - '@mapbox/point-geometry': 1.1.0 - '@mapbox/tiny-sdf': 2.2.0 - '@mapbox/unitbezier': 0.0.1 - '@mapbox/vector-tile': 2.0.5 - '@mapbox/whoots-js': 3.1.0 - '@maplibre/geojson-vt': 6.1.1 - '@maplibre/maplibre-gl-style-spec': 24.10.0 - '@maplibre/mlt': 1.1.12 - '@maplibre/vt-pbf': 4.3.2 - '@types/geojson': 7946.0.16 - earcut: 3.2.3 - gl-matrix: 3.4.4 - kdbush: 4.1.0 - murmurhash-js: 1.0.0 - pbf: 4.0.2 - potpack: 2.1.0 - quickselect: 3.0.0 - tinyqueue: 3.0.0 - maplibre-gl@6.1.0: dependencies: '@mapbox/point-geometry': 1.1.0 @@ -13466,16 +13403,16 @@ snapshots: dependencies: boolbase: 1.0.0 - nuxt@4.5.1(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@oxc-project/types@0.143.0)(@parcel/watcher@2.5.6)(@types/node@26.1.2)(@vue/compiler-sfc@3.5.40)(cac@6.7.14)(db0@0.3.4)(esbuild@0.28.1)(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(ioredis@5.11.1(supports-color@10.2.2))(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(oxc-parser@0.143.0)(rollup-plugin-visualizer@7.0.1(rolldown@1.2.0)(rollup@4.62.4))(rollup@4.62.4)(srvx@0.11.22)(supports-color@10.2.2)(terser@5.48.0)(typescript@6.0.3)(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue-tsc@3.3.9(typescript@6.0.3))(yaml@2.9.0): + nuxt@4.5.1(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.2.2)))(@oxc-project/types@0.143.0)(@parcel/watcher@2.5.6)(@types/node@26.1.2)(@vue/compiler-sfc@3.5.41)(cac@6.7.14)(db0@0.3.4)(esbuild@0.28.1)(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(ioredis@5.11.1(supports-color@10.2.2))(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(oxc-parser@0.143.0)(rollup-plugin-visualizer@7.0.1(rolldown@1.2.0)(rollup@4.62.4))(rollup@4.62.4)(srvx@0.11.22)(supports-color@10.2.2)(terser@5.48.0)(typescript@6.0.3)(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue-tsc@3.3.9(typescript@6.0.3))(yaml@2.9.0): dependencies: '@dxup/nuxt': 0.5.3(esbuild@0.28.1)(magicast@0.5.3)(oxc-parser@0.143.0)(rolldown@1.2.0)(rollup@4.62.4)(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) '@nuxt/cli': 3.37.0(@nuxt/schema@4.5.1)(cac@6.7.14)(magicast@0.5.3)(supports-color@10.2.2) '@nuxt/devtools': 3.4.0(db0@0.3.4)(ioredis@5.11.1(supports-color@10.2.2))(magic-string@1.0.0)(oxc-parser@0.143.0)(rolldown@1.2.0)(supports-color@10.2.2)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.0)(rollup@4.62.4)(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)))(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3)) '@nuxt/kit': 4.5.1(magic-string@1.0.0)(magicast@0.5.3)(oxc-parser@0.143.0)(rolldown@1.2.0)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.0)(rollup@4.62.4)(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))) - '@nuxt/nitro-server': 4.5.1(186d28e6bb39350865619c9a810edaaa) + '@nuxt/nitro-server': 4.5.1(b2e46168f2ef9884385715bfcac8a509) '@nuxt/schema': 4.5.1 '@nuxt/telemetry': 2.8.0(@nuxt/kit@4.5.1(magic-string@1.0.0)(magicast@0.5.3)(oxc-parser@0.143.0)(rolldown@1.2.0)(unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.2.0)(rollup@4.62.4)(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)))) - '@nuxt/vite-builder': 4.5.1(d3bd8e16a3e4c74d511b3b43451dca2f) + '@nuxt/vite-builder': 4.5.1(3fceaf98b1915314d813404cfbf6a16e) '@unhead/vue': 3.3.1(@oxc-project/types@0.143.0)(esbuild@0.28.1)(lightningcss@1.32.0)(oxc-parser@0.143.0)(rolldown@1.2.0)(rollup@4.62.4)(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3)) '@vue/shared': 3.5.40 chokidar: 5.0.0 @@ -13526,7 +13463,7 @@ snapshots: untyped: 2.0.0 verkit: 0.2.0 vue: 3.5.41(typescript@6.0.3) - vue-router: 5.2.0(@vue/compiler-sfc@3.5.40)(esbuild@0.28.1)(rolldown@1.2.0)(rollup@4.62.4)(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3)) + vue-router: 5.2.0(@vue/compiler-sfc@3.5.41)(esbuild@0.28.1)(rolldown@1.2.0)(rollup@4.62.4)(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3)) optionalDependencies: '@parcel/watcher': 2.5.6 '@types/node': 26.1.2 @@ -13908,10 +13845,6 @@ snapshots: pathe@2.0.3: {} - pbf@4.0.2: - dependencies: - resolve-protobuf-schema: 2.1.0 - pbf@5.1.2: dependencies: resolve-protobuf-schema: 2.1.0 @@ -15544,40 +15477,6 @@ snapshots: transitivePeerDependencies: - supports-color - vue-router@5.2.0(@vue/compiler-sfc@3.5.40)(esbuild@0.28.1)(rolldown@1.2.0)(rollup@4.62.4)(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3)): - dependencies: - '@babel/generator': 8.0.0 - '@vue-macros/common': 3.1.3(vue@3.5.41(typescript@6.0.3)) - '@vue/devtools-api': 8.1.5 - ast-walker-scope: 0.9.0 - chokidar: 5.0.0 - json5: 2.2.3 - local-pkg: 1.2.1 - magic-string: 0.30.21 - mlly: 1.8.2 - muggle-string: 0.4.1 - nostics: 1.2.0 - pathe: 2.0.3 - picomatch: 4.0.5 - scule: 1.3.0 - tinyglobby: 0.2.17 - unplugin: 3.3.0(esbuild@0.28.1)(rolldown@1.2.0)(rollup@4.62.4)(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) - unplugin-utils: 0.3.2 - vue: 3.5.41(typescript@6.0.3) - yaml: 2.9.0 - optionalDependencies: - '@vue/compiler-sfc': 3.5.40 - vite: 8.1.5(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) - transitivePeerDependencies: - - '@farmfe/core' - - '@rspack/core' - - bun-types-no-globals - - esbuild - - rolldown - - rollup - - unloader - - webpack - vue-router@5.2.0(@vue/compiler-sfc@3.5.41)(esbuild@0.28.1)(rolldown@1.2.0)(rollup@4.62.4)(vite@8.1.5(@types/node@26.1.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.41(typescript@6.0.3)): dependencies: '@babel/generator': 8.0.0 diff --git a/test/unit/maplibre-registry.test.ts b/test/unit/maplibre-registry.test.ts index facd3c25..5275eeb9 100644 --- a/test/unit/maplibre-registry.test.ts +++ b/test/unit/maplibre-registry.test.ts @@ -1,5 +1,10 @@ +/** + * @vitest-environment happy-dom + */ import { beforeEach, describe, expect, it, vi } from 'vitest' +const setWorkerUrl = vi.fn() + vi.mock('nuxt/app', () => ({ createError: (input: { message: string }) => new Error(input.message), useRuntimeConfig: () => ({ public: { scripts: {} } }), @@ -9,32 +14,82 @@ vi.mock('../../packages/script/src/runtime/composables/useScript', () => ({ useScript: vi.fn((input, options) => ({ input, options })), })) -describe('useScriptMapLibre', () => { - beforeEach(() => vi.clearAllMocks()) +vi.mock('maplibre-gl', () => ({ setWorkerUrl, Map: class {} })) - it('uses the pinned stable build with integrity metadata', async () => { - const { useScriptMapLibre } = await import('../../packages/script/src/runtime/registry/maplibre') - const { useScript } = await import('../../packages/script/src/runtime/composables/useScript') +async function loadMapLibre(options?: Record) { + const { useScriptMapLibre } = await import('../../packages/script/src/runtime/registry/maplibre') + const { useScript } = await import('../../packages/script/src/runtime/composables/useScript') + + useScriptMapLibre(options as never) + + const input = vi.mocked(useScript).mock.calls.at(-1)?.[0] as { + key: string + loader: (ctx: { signal: AbortSignal }) => Promise + } + return { input, api: await input.loader({ signal: new AbortController().signal }) } +} - useScriptMapLibre() +describe('useScriptMapLibre', () => { + let injectedStyles: HTMLLinkElement[] = [] - expect(vi.mocked(useScript).mock.calls.at(-1)?.[0]).toMatchObject({ - src: 'https://unpkg.com/maplibre-gl@5.24.0/dist/maplibre-gl.js', - integrity: 'sha384-5+cfbwT0iiub6VsQAdn6yz16nr6sDiQoHx6tm4O8OVYXHYOxcffFmCJBL0dgdvGp', - crossorigin: 'anonymous', + beforeEach(() => { + vi.restoreAllMocks() + vi.clearAllMocks() + delete window.maplibregl + injectedStyles = [] + // Capture the stylesheet without connecting it, so happy-dom does not fetch it. + vi.spyOn(document.head, 'append').mockImplementation((node) => { + injectedStyles.push(node as HTMLLinkElement) }) }) - it('does not apply the pinned integrity hash to a custom source', async () => { + it('loads MapLibre from the package instead of a script tag', async () => { + const { input, api } = await loadMapLibre({ injectStyles: false }) + + expect(input).not.toHaveProperty('src') + expect(input.key).toBe('maplibre') + expect(api).toMatchObject({ maplibregl: window.maplibregl }) + expect(window.maplibregl?.Map).toBeTypeOf('function') + }) + + it('injects a custom stylesheet when one is configured', async () => { + await loadMapLibre({ stylesheetUrl: 'https://cdn.example.com/maplibre.css' }) + + expect(injectedStyles.map(link => link.href)).toEqual(['https://cdn.example.com/maplibre.css']) + }) + + it('does not inject a stylesheet when injectStyles is disabled', async () => { + await loadMapLibre({ injectStyles: false, stylesheetUrl: 'https://cdn.example.com/maplibre.css' }) + + expect(injectedStyles).toHaveLength(0) + }) + + it('points MapLibre at the bundled worker by default', async () => { + await loadMapLibre({ injectStyles: false }) + + expect(setWorkerUrl).toHaveBeenCalledOnce() + expect(setWorkerUrl.mock.calls[0]?.[0]).toContain('maplibre-gl-worker') + }) + + it('applies a custom worker URL', async () => { + await loadMapLibre({ injectStyles: false, workerUrl: '/maplibre-worker.js' }) + + expect(setWorkerUrl).toHaveBeenCalledWith('/maplibre-worker.js') + }) + + it('aborts before touching the package when the signal is already aborted', async () => { const { useScriptMapLibre } = await import('../../packages/script/src/runtime/registry/maplibre') const { useScript } = await import('../../packages/script/src/runtime/composables/useScript') - useScriptMapLibre({ - scriptInput: { src: 'https://cdn.example.com/maplibre.js' }, - }) + useScriptMapLibre({ injectStyles: false } as never) + const { loader } = vi.mocked(useScript).mock.calls.at(-1)?.[0] as { + loader: (ctx: { signal: AbortSignal }) => Promise + } + + const controller = new AbortController() + controller.abort() - const input = vi.mocked(useScript).mock.calls.at(-1)?.[0] as Record - expect(input.src).toBe('https://cdn.example.com/maplibre.js') - expect(input).not.toHaveProperty('integrity') + await expect(loader({ signal: controller.signal })).rejects.toThrow() + expect(window.maplibregl).toBeUndefined() }) }) diff --git a/test/unit/maplibre-styles.test.ts b/test/unit/maplibre-styles.test.ts index df6233e3..07926174 100644 --- a/test/unit/maplibre-styles.test.ts +++ b/test/unit/maplibre-styles.test.ts @@ -5,8 +5,6 @@ import { afterEach, describe, expect, it, vi } from 'vitest' import { configureMapLibreWorker, ensureMapLibreStyles, - MAPLIBRE_STYLESHEET_INTEGRITY, - MAPLIBRE_STYLESHEET_URL, } from '../../packages/script/src/runtime/maplibre-styles' describe('mapLibre styles', () => { @@ -27,23 +25,13 @@ describe('mapLibre styles', () => { return { append, styles } } - it('injects the pinned stylesheet once with integrity metadata', () => { + it('injects a custom stylesheet once', () => { const { append, styles } = mockStylesheetInsertion() - ensureMapLibreStyles() - ensureMapLibreStyles() - - expect(append).toHaveBeenCalledOnce() - expect(styles[0]?.href).toBe(MAPLIBRE_STYLESHEET_URL) - expect(styles[0]?.integrity).toBe(MAPLIBRE_STYLESHEET_INTEGRITY) - expect(styles[0]?.crossOrigin).toBe('anonymous') - }) - - it('does not apply pinned integrity metadata to a custom stylesheet', () => { - const { styles } = mockStylesheetInsertion() - + ensureMapLibreStyles('https://cdn.example.com/maplibre.css') ensureMapLibreStyles('https://cdn.example.com/maplibre.css') + expect(append).toHaveBeenCalledOnce() expect(styles[0]?.href).toBe('https://cdn.example.com/maplibre.css') expect(styles[0]?.integrity).toBeFalsy() }) @@ -51,14 +39,14 @@ describe('mapLibre styles', () => { it('injects distinct stylesheet URLs once each', () => { const { append, styles } = mockStylesheetInsertion() - ensureMapLibreStyles() - ensureMapLibreStyles('https://cdn.example.com/maplibre.css') - ensureMapLibreStyles('https://cdn.example.com/maplibre.css') + ensureMapLibreStyles('https://cdn.example.com/a.css') + ensureMapLibreStyles('https://cdn.example.com/b.css') + ensureMapLibreStyles('https://cdn.example.com/b.css') expect(append).toHaveBeenCalledTimes(2) expect(styles.map(style => style.href)).toEqual([ - MAPLIBRE_STYLESHEET_URL, - 'https://cdn.example.com/maplibre.css', + 'https://cdn.example.com/a.css', + 'https://cdn.example.com/b.css', ]) })