From 14e35846d5916d91fd720a5d543bd41049a2b056 Mon Sep 17 00:00:00 2001 From: Buddy Date: Sat, 22 Aug 2026 11:41:58 -0700 Subject: [PATCH] refactor: complete WithProps mixin, generic event forwarding, and full prop coverage Finishes the WithProps(PROPS, options) mixin (src/core/with-props.ts): every component gets reactive attributes, live-value property getters that read through to the Leaflet object where possible, and every Leaflet event re-emitted on the element as `leaflet:` via a generic fire() patch (no per-component or per-event registration needed). Prop coverage: - Centralized pathProps in shared-props.ts (stroke, lineCap, lineJoin, dashArray, dashOffset, fillRule, interactive, className, bubblingMouseEvents, pane), deduped leaflet-geojson against it. - Added live getters backed by Leaflet's own accessors: radius (circle, circle-marker), bounds (rectangle, image/video/svg-overlay), url (image/video-overlay). - Filled gaps: smoothFactor/noClip on polyline; a shared tileLayerProps fragment now used by both tile-layer and tile-layer-wms (WMS previously exposed none of its base tile options); crs on WMS; zIndex/className/ keepAspectRatio/errorOverlayUrl on video-overlay; fixed tile-layer's zIndex default and div-icon's className default to match Leaflet. Tooling: - Removed dead src/core/events.ts (broken, unused, superseded by the generic event forwarding above) and src/core/attributes.ts (emptied by an earlier rename, nothing imported it). - Swapped ESLint + @typescript-eslint for oxlint: no released @typescript-eslint version supports the pinned typescript@7, even for parsing alone. oxlint has its own parser and lints clean. - Dropped the Rollup CJS/UMD bundle step; dist/ is ESM-only from tsc now. Updated package.json's main/module/exports/unpkg accordingly. - Updated CLAUDE.md to match: build/lint commands, ESM-only output, the event-forwarding mechanism, and layer-group/feature-group now going through WithProps({}) instead of raw HTMLElement. --- .oxlintrc.json | 17 + CLAUDE.md | 14 +- eslint.config.js | 43 - package-lock.json | 2390 ++++------------- package.json | 24 +- rollup.config.mjs | 25 - src/components/leaflet-circle-marker.ts | 77 +- src/components/leaflet-circle.ts | 79 +- src/components/leaflet-control-attribution.ts | 39 +- src/components/leaflet-control-layers.ts | 103 +- src/components/leaflet-control-scale.ts | 49 +- src/components/leaflet-control-zoom.ts | 50 +- src/components/leaflet-div-icon.ts | 89 +- src/components/leaflet-feature-group.ts | 35 +- src/components/leaflet-geojson.ts | 87 +- src/components/leaflet-icon.ts | 74 +- src/components/leaflet-image-overlay.ts | 75 +- src/components/leaflet-layer-group.ts | 36 +- src/components/leaflet-map.ts | 436 ++- src/components/leaflet-marker.ts | 103 +- src/components/leaflet-polygon.ts | 58 +- src/components/leaflet-polyline.ts | 63 +- src/components/leaflet-popup.ts | 90 +- src/components/leaflet-rectangle.ts | 49 +- src/components/leaflet-svg-overlay.ts | 65 +- src/components/leaflet-tile-layer-wms.ts | 97 +- src/components/leaflet-tile-layer.ts | 54 +- src/components/leaflet-tooltip.ts | 91 +- src/components/leaflet-video-overlay.ts | 103 +- src/core/attributes.ts | 97 - src/core/props.ts | 262 +- src/core/register.ts | 67 +- src/core/shared-props.ts | 156 ++ src/core/with-props.ts | 307 +++ src/index.ts | 1 - 35 files changed, 1792 insertions(+), 3613 deletions(-) create mode 100644 .oxlintrc.json delete mode 100644 eslint.config.js delete mode 100644 rollup.config.mjs delete mode 100644 src/core/attributes.ts create mode 100644 src/core/shared-props.ts create mode 100644 src/core/with-props.ts diff --git a/.oxlintrc.json b/.oxlintrc.json new file mode 100644 index 0000000..76d5208 --- /dev/null +++ b/.oxlintrc.json @@ -0,0 +1,17 @@ +{ + "$schema": "./node_modules/oxlint/configuration_schema.json", + "plugins": ["typescript", "unicorn", "oxc"], + "categories": { + "correctness": "error", + "suspicious": "warn", + "pedantic": "warn" + }, + "rules": { + "eslint/max-lines": "off", + "eslint/max-lines-per-function": "off" + }, + "env": { + "builtin": true + }, + "ignorePatterns": ["dist/"] +} diff --git a/CLAUDE.md b/CLAUDE.md index 056f032..569e3fa 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -5,12 +5,14 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ## Commands ```bash -npm run build # tsc emits individual ESM modules, then Rollup bundles ESM/CJS/UMD +npm run build # tsc emits individual ESM modules to dist/ (no bundling) npm run typecheck # tsc --noEmit -npm run lint # ESLint over src/**/*.{ts,js} +npm run lint # oxlint over src/ npm run format # Prettier formatting ``` +Linting runs on `oxlint`, not ESLint/`@typescript-eslint`. This project pins `typescript@^7.0.2`, and `@typescript-eslint` has no released version that supports it (peer range caps at `<6.1.0`, and even loading `@typescript-eslint/parser` crashes against TS 7's package shape). `oxlint` has its own parser and doesn't touch the `typescript` package, so it works regardless of TS version — the tradeoff is no type-aware rules (no `no-floating-promises`, `no-unnecessary-condition`, etc.). Revisit once `@typescript-eslint` supports TS 7. + There are no tests in this project. To preview components locally, open `index.html` in a browser with any static-file server. ## Architecture @@ -23,9 +25,9 @@ The `WithProps(Base, PROPS)` mixin factory replaces the old `LeafletElement` bas - `observedAttributes` getter derived from the PROPS table keys. - `definePropAccessors` — property getters/setters on the prototype that sync attributes. -- `parseAttributeValue` for boolean, number, and JSON coersion in `attributeChangedCallback`. - On connect, `initOptions()` builds a Leaflet options object from current attributes + PROPS defaults, then calls `createLeafletObject()`. - On attribute change, `updateLeafletObject()` by default dispatches to the matching Leaflet setter (e.g. `setOpacity`, `setRadius`). Components override this for custom attribute handling (e.g. lat/lng pairs). +- Every Leaflet event the created object fires is re-emitted on the element as `leaflet:` (e.g. `leaflet:zoomend`, `leaflet:dragend`), carrying the original Leaflet event object as `event.detail`. This is generic and automatic: `WithProps` wraps the object's own `fire()` method, so no per-component or per-event-type registration is needed. Not bubbling — Leaflet already propagates layer events up to the map, so `` would otherwise see each one twice. ### `leaflet-map`: `src/components/leaflet-map.ts` @@ -41,14 +43,12 @@ All non-map components extend `WithProps(HTMLElement, PROPS)`. To add a new comp 4. Call `customElements.define('leaflet-foo', LeafletFoo)` at the bottom. 5. Export from `src/index.ts`. -For components that accept children (layers, popups, tooltips), use the `registerChildren`/`unregisterChildren`/`getChildren` WeakMap-backed helpers from `src/core/register.ts` in `connectedCallback`/`disconnectedCallback` instead of maintaining private fields. - ### Special cases - **`leaflet-polygon`** uses `` children for vertices. The polygon collects lat/lng from child `leaflet-line` elements rather than having them as direct attributes. - **`leaflet-popup`** / **`leaflet-tooltip`**: content comes from `innerHTML`, not attributes. `leaflet-popup` watches for DOM mutations to keep Leaflet in sync. -- **`leaflet-layer-group`** / **`leaflet-feature-group`**: passthrough containers extending `HTMLElement` directly (not via `WithProps`). Children register themselves into them via the standard bubble mechanism. +- **`leaflet-layer-group`** / **`leaflet-feature-group`**: passthrough containers built with `WithProps({})` (an empty props table) — they have no options of their own, but still get the standard lifecycle, child registration, and `leaflet:` event forwarding for free. Children register themselves into them via the standard bubble mechanism. ### Output -`tsc` compiles `src/` → `dist/` as individual ESM modules (`.js` + `.d.ts` + `.d.ts.map`), then `rollup -c` bundles `dist/index.js` (ESM), `dist/index.cjs` (CJS), and `dist/index.umd.js` (UMD) — each with minified variants. Leaflet is always external (never bundled). Imports within source use `.ts` extensions; `rewriteRelativeImportExtensions` in tsconfig strips them to `.js` in the tsc output (`tsconfig.json:16`). +`tsc` compiles `src/` → `dist/` as individual ESM modules (`.js` + `.d.ts` + `.d.ts.map`) — no bundling step. Consumers import `dist/index.js` (or any individual module) directly; there is no CJS or UMD build. Leaflet is always external (never bundled). Imports within source use `.ts` extensions; `rewriteRelativeImportExtensions` in tsconfig strips them to `.js` in the tsc output (`tsconfig.json:16`). diff --git a/eslint.config.js b/eslint.config.js deleted file mode 100644 index eb37214..0000000 --- a/eslint.config.js +++ /dev/null @@ -1,43 +0,0 @@ -import { defineConfig } from 'eslint/config'; -import tsPlugin from '@typescript-eslint/eslint-plugin'; -import prettierPlugin from 'eslint-plugin-prettier'; -import prettierConfig from 'eslint-config-prettier'; - -export default defineConfig([ - { - ignores: ['dist/'], - }, - - ...tsPlugin.configs['flat/strict-type-checked'].map(c => ({ - ...c, - files: ['**/*.ts'], - })), - - ...tsPlugin.configs['flat/stylistic-type-checked'].map(c => ({ - ...c, - files: ['**/*.ts'], - })), - - - - { - files: ['**/*.ts'], - languageOptions: { - parserOptions: { project: true }, - }, - plugins: { - prettier: prettierPlugin, - }, - rules: { - ...prettierConfig.rules, - 'prettier/prettier': 'error', - }, - }, - - { - files: ['**/*.ts'], - rules: { - '@typescript-eslint/restrict-template-expressions': ['error', { allowNumber: true }], - }, - }, -]); diff --git a/package-lock.json b/package-lock.json index 04e2312..41ecd42 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,381 +12,16 @@ "leaflet": "1.9.4" }, "devDependencies": { - "@eslint/js": "^10.0.1", - "@rollup/plugin-terser": "^1.0.0", - "@rollup/plugin-typescript": "^12.3.0", "@types/leaflet": "1.9.21", - "@typescript-eslint/eslint-plugin": "8.60.1", - "@typescript-eslint/parser": "8.60.1", - "eslint": "10.4.1", - "eslint-config-prettier": "10.1.8", - "eslint-plugin-prettier": "5.5.6", + "oxlint": "^1.79.0", "prettier": "3.8.3", - "rollup": "^4.61.1", - "rollup-plugin-dts": "^6.4.1", - "tslib": "^2.8.1", - "typescript": "6.0.3" + "typescript": "^7.0.2" } }, - "node_modules/@babel/code-frame": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", - "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.29.7", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", - "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", - "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "eslint-visitor-keys": "^3.4.3" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.12.2", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", - "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/config-array": { - "version": "0.23.5", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.5.tgz", - "integrity": "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/object-schema": "^3.0.5", - "debug": "^4.3.1", - "minimatch": "^10.2.4" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - } - }, - "node_modules/@eslint/config-helpers": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.6.0.tgz", - "integrity": "sha512-ii6Bw9jJ2zi2cWA2Z+9/QZ/+3DX6kwaV5Q986D/CdP3Lap3w/pgQZ373FV7byY/i7L4IRH/G43I5dz1ClsCbpA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^1.2.1" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - } - }, - "node_modules/@eslint/core": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz", - "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - } - }, - "node_modules/@eslint/js": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-10.0.1.tgz", - "integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - }, - "funding": { - "url": "https://eslint.org/donate" - }, - "peerDependencies": { - "eslint": "^10.0.0" - }, - "peerDependenciesMeta": { - "eslint": { - "optional": true - } - } - }, - "node_modules/@eslint/object-schema": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz", - "integrity": "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - } - }, - "node_modules/@eslint/plugin-kit": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.2.tgz", - "integrity": "sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^1.2.1", - "levn": "^0.4.1" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - } - }, - "node_modules/@humanfs/core": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", - "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@humanfs/types": "^0.15.0" - }, - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/node": { - "version": "0.16.8", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", - "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@humanfs/core": "^0.19.2", - "@humanfs/types": "^0.15.0", - "@humanwhocodes/retry": "^0.4.0" - }, - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/types": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", - "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/retry": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", - "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/remapping": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", - "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/source-map": { - "version": "0.3.11", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", - "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.31", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", - "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@pkgr/core": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.3.6.tgz", - "integrity": "sha512-SEeaJLb3qBNF/OaXnaR1NmmBbFYk1zC0ZH/52fATcRPLFg/p791YrcyFFy44Bo9sLaGuSuLp5Q6axbb/O+v/RA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/pkgr" - } - }, - "node_modules/@rollup/plugin-terser": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@rollup/plugin-terser/-/plugin-terser-1.0.0.tgz", - "integrity": "sha512-FnCxhTBx6bMOYQrar6C8h3scPt8/JwIzw3+AJ2K++6guogH5fYaIFia+zZuhqv0eo1RN7W1Pz630SyvLbDjhtQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "serialize-javascript": "^7.0.3", - "smob": "^1.0.0", - "terser": "^5.17.4" - }, - "engines": { - "node": ">=20.0.0" - }, - "peerDependencies": { - "rollup": "^2.0.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, - "node_modules/@rollup/plugin-typescript": { - "version": "12.3.0", - "resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-12.3.0.tgz", - "integrity": "sha512-7DP0/p7y3t67+NabT9f8oTBFE6gGkto4SA6Np2oudYmZE/m1dt8RB0SjL1msMxFpLo631qjRCcBlAbq1ml/Big==", - "dev": true, - "license": "MIT", - "dependencies": { - "@rollup/pluginutils": "^5.1.0", - "resolve": "^1.22.1" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^2.14.0||^3.0.0||^4.0.0", - "tslib": "*", - "typescript": ">=3.7.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - }, - "tslib": { - "optional": true - } - } - }, - "node_modules/@rollup/pluginutils": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.4.0.tgz", - "integrity": "sha512-MfPp06CjRLfXQ3wY0R8vJDYBy/MvVcc9OulEfR0B8Iv9ko+GCNaRZ+EpJYFl27LhKsZK0o420sYCRHCjfCgeUg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "estree-walker": "^2.0.2", - "picomatch": "^4.0.2" - }, - "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/rollup-android-arm-eabi": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.61.1.tgz", - "integrity": "sha512-JnBB8MdXj45cajvTuO5FmPlvFVJRQgvrz1uSEl3NwqFnReAPGwb8EanbGi4z2nRaqLzjJSv5/JmycoTKlRZxHA==", + "node_modules/@oxlint/binding-android-arm-eabi": { + "version": "1.79.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-android-arm-eabi/-/binding-android-arm-eabi-1.79.0.tgz", + "integrity": "sha512-TebFaaMklO/RXzTv7PucaCq9l3X6D1gA+C8H6K4njtjFOV+zWE9MKLpulcJZN9bzytbUbQIY0mZuz12nQ5Kv4Q==", "cpu": [ "arm" ], @@ -395,12 +30,15 @@ "optional": true, "os": [ "android" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.61.1.tgz", - "integrity": "sha512-Jx2g7iSjw4AOT0HDPHM9RV3GNjRXwybWtSFZiZAYUTjUwjVrYIwq3kBf+LnhqJlzXFAqTAh2F7IGI+O568exPw==", + "node_modules/@oxlint/binding-android-arm64": { + "version": "1.79.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-android-arm64/-/binding-android-arm64-1.79.0.tgz", + "integrity": "sha512-KqqnOtAVgNsPPF0YSodkFZA1O80jcKoCZCTu3bgsszxA+MrMP9TLzfXitKjEj1FmrPprKDMdRDMmY3weESO9sg==", "cpu": [ "arm64" ], @@ -409,12 +47,15 @@ "optional": true, "os": [ "android" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.61.1.tgz", - "integrity": "sha512-0F1L/Z3Eqv8mT2n3dCpeO8GcTvHvVqkP5/t6DMsn0KzhYVcg+s7Ncl5DS8qjKYEeio6Az0Gt6nyBORay5qIlCA==", + "node_modules/@oxlint/binding-darwin-arm64": { + "version": "1.79.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-darwin-arm64/-/binding-darwin-arm64-1.79.0.tgz", + "integrity": "sha512-BVC2nsMzqQzRDPc5RhixkZ+m1p7iH4bxRRvqkbwDXX0PlQKm1BPy8J8cRjnAFafOq2QzI+BfO3vE8w2GZ3CBag==", "cpu": [ "arm64" ], @@ -423,12 +64,15 @@ "optional": true, "os": [ "darwin" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.61.1.tgz", - "integrity": "sha512-qLttcH871ujY4YcVfUSShhOw+CsoTatYz8gRbHO7Bb92QH059/P0y5do1KMs41fY0BpD2x4AJH/gID0zFiqVKQ==", + "node_modules/@oxlint/binding-darwin-x64": { + "version": "1.79.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-darwin-x64/-/binding-darwin-x64-1.79.0.tgz", + "integrity": "sha512-p6Lm+snmhGuLKL1+CpCV8L6ijkE/qJzK2H2jG9+eKJT0n31RbY4FLsdhexekgP3bLpw4Kgde+9DZuDZQ4yIInA==", "cpu": [ "x64" ], @@ -437,26 +81,15 @@ "optional": true, "os": [ "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.61.1.tgz", - "integrity": "sha512-fUI4RapGE0Oh3mb8mgfvC1O2nU1RpDZUKnDQm3xB1Ipg7C2wTs5Kstz7G2uWK99a8S2yTMq8/P4uycwNa0nJyw==", - "cpu": [ - "arm64" ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.61.1.tgz", - "integrity": "sha512-H5YrdvJaDtI/U9/emrD4b++xkvp3y/JvOe4rizHbxvkyMfRS/CiRYdji+Pl8D0brEaNFWUh1drQxgAGIl6Xudw==", + "node_modules/@oxlint/binding-freebsd-x64": { + "version": "1.79.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-freebsd-x64/-/binding-freebsd-x64-1.79.0.tgz", + "integrity": "sha512-qDMm0dXZnoHyRqSL4N4xUq82T4sqK5cbKSjvd/dF/YbMUXc2R1wEPf+vmA5S0qUmi0nwXfNbjXBtZaIqzQLIMg==", "cpu": [ "x64" ], @@ -465,12 +98,15 @@ "optional": true, "os": [ "freebsd" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.61.1.tgz", - "integrity": "sha512-Q8CBCCQtDFrYtXoeUXSrnFXKOnyUhx6bz+SkL6A0E7V8kAiCJ5pamq1WtbfpVGhR5TSpXY6ak3avmDc5fHTyJA==", + "node_modules/@oxlint/binding-linux-arm-gnueabihf": { + "version": "1.79.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.79.0.tgz", + "integrity": "sha512-2od7s0nuKPzqyUZAWk9KkCyGg7eI9dwFPZg+20lB15fKFkVZ0c9ZFxqPfiBAyDTlTkh9stPI0t+JlPCqMbItVA==", "cpu": [ "arm" ], @@ -479,12 +115,15 @@ "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.61.1.tgz", - "integrity": "sha512-nwnhk1581l0FBVellGcVCAT0Oi06onEA3WB53sf01VO3I0UPBkMH9sXONYME2K0ovXcNayJfNtHfm6mpJElatQ==", + "node_modules/@oxlint/binding-linux-arm-musleabihf": { + "version": "1.79.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-1.79.0.tgz", + "integrity": "sha512-ZOQUjkzDnvlhSE3+tWC3YXx94MMl+sYMlwH+u1+YGApGHOJP/YAc8ZBRFOXZ6eOBmxtXAWuS/fBcdZr8qqNO1A==", "cpu": [ "arm" ], @@ -493,180 +132,175 @@ "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.61.1.tgz", - "integrity": "sha512-x5Xr49hwt3hdW75UOZm3395YwwzPyauktslv29KpWL/T+vVAzoT3azLcTWv0eMciBNrx+DYjH4paehHoLpPvpg==", + "node_modules/@oxlint/binding-linux-arm64-gnu": { + "version": "1.79.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.79.0.tgz", + "integrity": "sha512-lu158FR4nGqGeRS3BQvtG85wRgU/Fy4MD5Cxp1hzJXizGiLo6u2742wJSCDKh8cFcZntvX7fcxlq4mMmfryH1g==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.61.1.tgz", - "integrity": "sha512-unMS3H73DpaoPyyEVPjGKleM/s0mkmsauTENpw4INQY8y4+IuLNjkueQ5QCtC0D3N38Y38yhAU8OoZ20S2Tm6w==", + "node_modules/@oxlint/binding-linux-arm64-musl": { + "version": "1.79.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.79.0.tgz", + "integrity": "sha512-mbpKQeE2aflTjddaHK7MP8KP/OFbUM++lt5M635ENM8IyIdK0jm2t9pb+2v9mVVIvhF6TqA4l7F79Pll1mi+uw==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.61.1.tgz", - "integrity": "sha512-zNZzGRnAhwjFEYmvphJRV5XaQGjs62cCmeYYHUT//NbvEnHauw+I85nGG+SiVg5ld4GX8D1IbKIX+ozITQnhMQ==", + "node_modules/@oxlint/binding-linux-ppc64-gnu": { + "version": "1.79.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.79.0.tgz", + "integrity": "sha512-WpGNua7gaxaHnpSDeog2ji8IDHn/QLPl9LPzwkR/FvVv58vT5BcXjRXnU+wbu3N75cpeha8CdC7ho/U2OIsB4g==", "cpu": [ - "loong64" + "ppc64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.61.1.tgz", - "integrity": "sha512-LdpWGL8X209B2SIvWjqlc8VZgM6PKfontSerGepuldQmHYrAOtnMCXeJkxXGbC+PPZVOuu5czJo7fNV6aeW8rQ==", + "node_modules/@oxlint/binding-linux-riscv64-gnu": { + "version": "1.79.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-1.79.0.tgz", + "integrity": "sha512-tK1E93A5LVzISg4ngpKJnfTs7EqtIUceGI7MQ4GyDjJiLi8wPCkEyKlj2xkyKWZ1yzkDJyLHTBJ5/iFWRdnJvg==", "cpu": [ - "loong64" + "riscv64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.61.1.tgz", - "integrity": "sha512-EC5kTtNaNGOmbMGqar8dvJy6y/hg99GAwjfBz++pxZhQATXGcRjd6c5en5wcbru0vkRmiMGsQKdMJOOf6sza4g==", + "node_modules/@oxlint/binding-linux-riscv64-musl": { + "version": "1.79.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-1.79.0.tgz", + "integrity": "sha512-qhQvUIrngXivA2A9pQ+xPCychztn/5qUv7yS3gDwXv3w7Rag+eTeeXWmRyx+t7XsW5x6LuY/8AsTq36UgFIblg==", "cpu": [ - "ppc64" + "riscv64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.61.1.tgz", - "integrity": "sha512-8hiwp6D4acEcNK78I4rP0/XtS1sknWIAMJBPdR4l6zUtyTm5KiTDr5bXmWt4foY7nAN7AThDHgkLIEZOWKbzWw==", + "node_modules/@oxlint/binding-linux-s390x-gnu": { + "version": "1.79.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.79.0.tgz", + "integrity": "sha512-sv6AaVgU/eE6u+6WFiQVDcPPwTxP6IJMSB9k701W2r/r6Tx465e8vPvVyRxquNH4Vy6KwRNu90mVbxXJN8+5gg==", "cpu": [ - "ppc64" + "s390x" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.61.1.tgz", - "integrity": "sha512-10dh/h/BqA7DuMPWSxkR8uks18FRwnwOEqr5zOTEl+NOwP/OMzKX8OFR/Of9xxDA7D5qef1Nzar5WDD2kCCr1g==", + "node_modules/@oxlint/binding-linux-x64-gnu": { + "version": "1.79.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.79.0.tgz", + "integrity": "sha512-iFZL02deziHslb3jEX9KdqlAkYoo4fGyotchKDzdfK1f5mxlIBeiQeHhvK3iFpuEJSB4ma/qeFn9oxPiwnhUPQ==", "cpu": [ - "riscv64" + "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.61.1.tgz", - "integrity": "sha512-YKJ5lg35DP17gcAOggnihe+APw9HLyj1Xn7gsmGumBJAUDa6NGXNixJzmkWLhcK9TOuuyQjdamzvJefkO7qHZQ==", + "node_modules/@oxlint/binding-linux-x64-musl": { + "version": "1.79.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-x64-musl/-/binding-linux-x64-musl-1.79.0.tgz", + "integrity": "sha512-3DtZR2raqObnh7wXZoFYFd0Fw7skBvcb3f7A+/lkEiDuh8hrE6vv9b/62Qxao1a9/OeHLw/FcXlXzgsW9wTRFg==", "cpu": [ - "riscv64" + "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.61.1.tgz", - "integrity": "sha512-Mlil5G2Jj6a7B3LWGctg+XPL9vdXYuzCtNXfxOQ0nPjc2m6ueUktocPGH9bnAM0bNRKb/bAWTujUU7IJQdQA+g==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.61.1.tgz", - "integrity": "sha512-bVWIOIk6pV01p4CdUbPP7CJ/434z+OooYjDuFcR+44N35YvKUC66G8MGnvcWx5mWKW3g61J+t74l3Kj15Kwn2Q==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.61.1.tgz", - "integrity": "sha512-qy5pBvZbqNFheBz61R1rzsezjm0J7O2oNGoWtGoY89SZYLUfxAJTBAqDChqAIdB4rCiIbi9nF7yZ83GnNiLwSw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.61.1.tgz", - "integrity": "sha512-E83TXjI4zm0+5f2qO+UOudaCYIhYwpJ5jq6YCZNIZ+6CbfhKrkAGezeiASBL9ElxAxFsRS9ZhESv8mfnj6TKeg==", - "cpu": [ - "x64" ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ] + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.61.1.tgz", - "integrity": "sha512-fbWnKqVkjrJN38vNe3ahkbk6iejS/3b0Nt7EEtPpE6RBacZcGXNKbzfHN3GUUlXOPghUg0j6XUGrtjX9z1sIvA==", + "node_modules/@oxlint/binding-openharmony-arm64": { + "version": "1.79.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-openharmony-arm64/-/binding-openharmony-arm64-1.79.0.tgz", + "integrity": "sha512-Oatt4GuA1WJkqzk2ozx4HrWROOi7opV3AKDw/U8qDIqeTqzsjn5K2x3REJMNjU3/KU/Bkq96Zi3CknaiDTaC/Q==", "cpu": [ "arm64" ], @@ -675,12 +309,15 @@ "optional": true, "os": [ "openharmony" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.61.1.tgz", - "integrity": "sha512-ArMl38iVAbk0New1ogihQNY6iphLi4ZaRsa037gUzv5yeKPY8TD3Dmy4x2RNC1VztU/uqm+G+/RwFrSka3Oy2g==", + "node_modules/@oxlint/binding-win32-arm64-msvc": { + "version": "1.79.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.79.0.tgz", + "integrity": "sha512-NAgZr9Qp8nIA9rpo0JEvwiabTF/2UVqBNnupBG9X4kxXcQoScJUTi+qHhvabb9s/thgj5wQ4XcIaJvb+ZMgoKw==", "cpu": [ "arm64" ], @@ -689,12 +326,15 @@ "optional": true, "os": [ "win32" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.61.1.tgz", - "integrity": "sha512-0mYtjHS9ucAbcATycCNK9IGBk/cCe/ma7EmSLGZdsxnOA8cjRIyU04wDpVAD9NiOfLUR9KTxdiO53uOkherqjQ==", + "node_modules/@oxlint/binding-win32-ia32-msvc": { + "version": "1.79.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-1.79.0.tgz", + "integrity": "sha512-+KyXjIvcpaXmWW/j9NNY5yWjrIVxaX18VyIheQy3jwc2GSYgpCr7MGI/HxIGQ/shAL5IWEKbhsqoMpAO5Stiog==", "cpu": [ "ia32" ], @@ -703,26 +343,15 @@ "optional": true, "os": [ "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.61.1.tgz", - "integrity": "sha512-gK1iCEPfpoSG9wfBihXxvBMi8ZfcWffYkEsC/Eih+iFENTaewvNcrEQ69lIOWYO5pePHKLHHO7nq5AILGO/HQQ==", - "cpu": [ - "x64" ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.61.1.tgz", - "integrity": "sha512-X+zaP2x+j4RXGfbp/seSoRHWnPxzApilDszisZxbYH5C/jTxFhCtDNdPGZb9lJyYPs24wGxruPF7Y+sIXt9Gzw==", + "node_modules/@oxlint/binding-win32-x64-msvc": { + "version": "1.79.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.79.0.tgz", + "integrity": "sha512-mEelcCMMBS57sIXh2veGMNy+pQwuGtcMxHxGIZWQ5Ba9pJ5jCCUFOZB9E2JhBaxGsURe+WGe0zJp4RVre52gpQ==", "cpu": [ "x64" ], @@ -731,21 +360,10 @@ "optional": true, "os": [ "win32" - ] - }, - "node_modules/@types/esrecurse": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", - "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/estree": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", - "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", - "dev": true, - "license": "MIT" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, "node_modules/@types/geojson": { "version": "7946.0.16", @@ -754,13 +372,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/leaflet": { "version": "1.9.21", "resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.9.21.tgz", @@ -771,1063 +382,399 @@ "@types/geojson": "*" } }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.60.1.tgz", - "integrity": "sha512-JQ4S5GB0tfjO8BuJ4fcX+HodkzJjYBV+7OJ+wLygaX7OGQ7FudyHL4NSCA6ob+w3Yn+5MkKIozOwQhXeM7opVg==", + "node_modules/@typescript/typescript-aix-ppc64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-aix-ppc64/-/typescript-aix-ppc64-7.0.2.tgz", + "integrity": "sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ==", + "cpu": [ + "ppc64" + ], "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.60.1", - "@typescript-eslint/type-utils": "8.60.1", - "@typescript-eslint/utils": "8.60.1", - "@typescript-eslint/visitor-keys": "8.60.1", - "ignore": "^7.0.5", - "natural-compare": "^1.4.0", - "ts-api-utils": "^2.5.0" - }, + "license": "Apache-2.0", + "optional": true, + "os": [ + "aix" + ], "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^8.60.1", - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" + "node": ">=16.20.0" } }, - "node_modules/@typescript-eslint/parser": { - "version": "8.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.60.1.tgz", - "integrity": "sha512-A0M6ua6H252bVjPvvtSgl2QA4+ET9S5Mtkb2GDyTxIhH/C4qDItT7RQNO5PhMC6NXGYXOR9dIalcDDgBKT7oFA==", + "node_modules/@typescript/typescript-darwin-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-darwin-arm64/-/typescript-darwin-arm64-7.0.2.tgz", + "integrity": "sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/scope-manager": "8.60.1", - "@typescript-eslint/types": "8.60.1", - "@typescript-eslint/typescript-estree": "8.60.1", - "@typescript-eslint/visitor-keys": "8.60.1", - "debug": "^4.4.3" - }, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" + "node": ">=16.20.0" } }, - "node_modules/@typescript-eslint/project-service": { - "version": "8.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.60.1.tgz", - "integrity": "sha512-eXkTH2bxmXlqD1RnOPmLZ9ZM9D3VwSx04JOwBnP9RQ+yUA5a2Mu7SfW8uaV2Aon53NJzZlZYuX7tn91Izf+xaw==", + "node_modules/@typescript/typescript-darwin-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-darwin-x64/-/typescript-darwin-x64-7.0.2.tgz", + "integrity": "sha512-SZ9xZInqApNlNGc9s0W1VSsktYSOe9cFqNOIqmN1Gs8SmkjKZYFt017G4VwPxASInODuAdbTW7sXiFUf893RgA==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.60.1", - "@typescript-eslint/types": "^8.60.1", - "debug": "^4.4.3" - }, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" + "node": ">=16.20.0" } }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "8.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.60.1.tgz", - "integrity": "sha512-gvI5OQoptnxQnchOirukCuQ55svJSTuD/4k5+pC267xyBtYry748R9/c3tYUzb/iE6RZfllRz2lVulLCHkTm4w==", + "node_modules/@typescript/typescript-freebsd-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-freebsd-arm64/-/typescript-freebsd-arm64-7.0.2.tgz", + "integrity": "sha512-W5NH4y/J0plIIS5b2xvTEkU7JFxyqdMAOgf+Ilhl0vHQXKO5dZoxd+C/jEtq56c4F3wk71RB4BMRQ2XdI+bwYQ==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.60.1", - "@typescript-eslint/visitor-keys": "8.60.1" - }, + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": ">=16.20.0" } }, - "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.60.1.tgz", - "integrity": "sha512-nh8w4qAteiKuZu3pSSzG/yGKpw0OlkrKnzFmbVRenKaD4qc+7i1GrmZaLVkr8rk4uipiPGMOW4YsM6WmKZ5CvA==", + "node_modules/@typescript/typescript-freebsd-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-freebsd-x64/-/typescript-freebsd-x64-7.0.2.tgz", + "integrity": "sha512-UMGDx5sTpzNw3WiPebH7l90IWfJggEd+egHt/q6p7/Cm3zqoV7VxkGXt+3DxPIw8CcmvAB0j3sVVfbhX+M4Tpw==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT", + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" + "node": ">=16.20.0" } }, - "node_modules/@typescript-eslint/type-utils": { - "version": "8.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.60.1.tgz", - "integrity": "sha512-sdwTrpjosW7ANQYJ39ZBF1ZyEMEGVB2UsikrserVM/30a/F1dTLnu9bGxEdosugyu5caigjLrR2qiD11asjI1A==", + "node_modules/@typescript/typescript-linux-arm": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-arm/-/typescript-linux-arm-7.0.2.tgz", + "integrity": "sha512-gffT3xPz9sR7j/YJExkyPntrI0P2EP9XbOyWzth2/Gs0RstK+90RBcO0ncXoXy/beYll1SXw846Nf2zdnEz0QQ==", + "cpu": [ + "arm" + ], "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.60.1", - "@typescript-eslint/typescript-estree": "8.60.1", - "@typescript-eslint/utils": "8.60.1", - "debug": "^4.4.3", - "ts-api-utils": "^2.5.0" - }, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" + "node": ">=16.20.0" } }, - "node_modules/@typescript-eslint/types": { - "version": "8.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.60.1.tgz", - "integrity": "sha512-4h0tY8ppCkdCzcrl2YM5M3my0xsE1Tf8om3owEu5oPWmXwkKRmk0j0LGDzYBGUcAlesEbxBhazqu/K4cu3Ug7w==", + "node_modules/@typescript/typescript-linux-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-arm64/-/typescript-linux-arm64-7.0.2.tgz", + "integrity": "sha512-Qh4eU4/y3yDjnfjjyPYihMj5/ODIlmt+Bzu17OI+fiSRDW57QmU5SiN63exPRNJPKUzcc1INa1NXdrJ+MqHjUQ==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT", + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": ">=16.20.0" } }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.60.1.tgz", - "integrity": "sha512-alpRkfG8hlVE5kdJW2GkfgDgXxold3e8e4l6EnmhRmRLbekgAPCCGDVD++sABy9FcgPFroq+uFcCSM1vR57Cew==", + "node_modules/@typescript/typescript-linux-loong64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-loong64/-/typescript-linux-loong64-7.0.2.tgz", + "integrity": "sha512-uEHck9i8hoAzXPiYRib1O7miOnz23SxIeVl6F4LXox+qov1K35jHcEW6VHKvZI+pyvl7fZEP4MCU5LYvIq1GuQ==", + "cpu": [ + "loong64" + ], "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/project-service": "8.60.1", - "@typescript-eslint/tsconfig-utils": "8.60.1", - "@typescript-eslint/types": "8.60.1", - "@typescript-eslint/visitor-keys": "8.60.1", - "debug": "^4.4.3", - "minimatch": "^10.2.2", - "semver": "^7.7.3", - "tinyglobby": "^0.2.15", - "ts-api-utils": "^2.5.0" - }, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" + "node": ">=16.20.0" } }, - "node_modules/@typescript-eslint/utils": { - "version": "8.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.60.1.tgz", - "integrity": "sha512-h2MPBLoNtjc3qZWfY3Tl51yPorQ2McHn8pJfcMNTcIvrrZrr90Ykffit0yjrPFWQcRcUxzH20+6OcVdW4yHtUg==", + "node_modules/@typescript/typescript-linux-mips64el": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-mips64el/-/typescript-linux-mips64el-7.0.2.tgz", + "integrity": "sha512-R4KvAMnE43W5Qeqb0Ly56O3mWMWIAgsMyz36DCaycd5nbg/9kzm0liw3JocfRqyJY0KPmzFjbswozXyW0DnIYA==", + "cpu": [ + "mips64el" + ], "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.60.1", - "@typescript-eslint/types": "8.60.1", - "@typescript-eslint/typescript-estree": "8.60.1" - }, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" + "node": ">=16.20.0" } }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.60.1.tgz", - "integrity": "sha512-EbGRQg4FhrmwLodl+t3JNAnXHWVr9Vp+Zl1QBZVPY4ByfkzIT8cX3K6QWODHtkIZqqJVEWvhHSx3v5PDHsaQag==", + "node_modules/@typescript/typescript-linux-ppc64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-ppc64/-/typescript-linux-ppc64-7.0.2.tgz", + "integrity": "sha512-DORx5b3sd/4S7eayxm4FQv+A7CrkUIGRaHiwI8oiHTAI1fAPWhF4J0vAlkC8biAlHSVVwxMQ3tjZ2/DVbnQiiA==", + "cpu": [ + "ppc64" + ], "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.60.1", - "eslint-visitor-keys": "^5.0.0" - }, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": ">=16.20.0" } }, - "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", - "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "node_modules/@typescript/typescript-linux-riscv64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-riscv64/-/typescript-linux-riscv64-7.0.2.tgz", + "integrity": "sha512-wf0jqEDOjrPRnKwYRyyJDRo11KMbvMFrU+q4zqKyChODBzvlkbhNQfKvLxQCcwTpdDaXSHZTVuh0JoCrKCUMHQ==", + "cpu": [ + "riscv64" + ], "dev": true, "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": ">=16.20.0" } }, - "node_modules/acorn": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", - "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "node_modules/@typescript/typescript-linux-s390x": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-s390x/-/typescript-linux-s390x-7.0.2.tgz", + "integrity": "sha512-IkwJc3L7yhytWd/ewjyxNDfOmswCm9GWMJT/ue/dU4aZNbwZeYAetq42VyLmsmSjvoX7z74X6ZaYCtzAr0EuGw==", + "cpu": [ + "s390x" + ], "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=0.4.0" + "node": ">=16.20.0" } }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "node_modules/@typescript/typescript-linux-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-x64/-/typescript-linux-x64-7.0.2.tgz", + "integrity": "sha512-EYdf2cNg7rgCWJnxCdJ+F3V39O8ihb37eHAu1LK8oAFizgTQbPOK7zHHXbPt8rX24COqODXeI3sIf0fCXG7H/A==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" } }, - "node_modules/ajv": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", - "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "node_modules/@typescript/typescript-netbsd-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-netbsd-arm64/-/typescript-netbsd-arm64-7.0.2.tgz", + "integrity": "sha512-+polYF4MF04aPpO5FTkHran9yUQDSXqy5GiSDKpsll5jy3l3+g9QLhpf39T+ePtefhXLOGrLl0QIjkQP6VnelA==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "license": "Apache-2.0", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=16.20.0" } }, - "node_modules/balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "node_modules/@typescript/typescript-netbsd-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-netbsd-x64/-/typescript-netbsd-x64-7.0.2.tgz", + "integrity": "sha512-8YIT0EHM/3dq10ZOVF/A7pc/YSMtbcecct4rWtexrnSCHOPcpC2KTLXfTCR6vDpnSiY12heNb1GiN/wu+T/FyA==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT", + "license": "Apache-2.0", + "optional": true, + "os": [ + "netbsd" + ], "engines": { - "node": "18 || 20 || >=22" + "node": ">=16.20.0" } }, - "node_modules/brace-expansion": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", - "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^4.0.2" - }, - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true, - "license": "MIT" - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "10.4.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.4.1.tgz", - "integrity": "sha512-AyIKhnOBuOAdueD7RB3xB+YeAWScb9jHsJBgH2Hcde8InP5JYhqrRR6iTMHyTEwgENK54Cp44e4v8BwNhsuHuw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.8.0", - "@eslint-community/regexpp": "^4.12.2", - "@eslint/config-array": "^0.23.5", - "@eslint/config-helpers": "^0.6.0", - "@eslint/core": "^1.2.1", - "@eslint/plugin-kit": "^0.7.2", - "@humanfs/node": "^0.16.6", - "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.4.2", - "@types/estree": "^1.0.6", - "ajv": "^6.14.0", - "cross-spawn": "^7.0.6", - "debug": "^4.3.2", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^9.1.2", - "eslint-visitor-keys": "^5.0.1", - "espree": "^11.2.0", - "esquery": "^1.7.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^8.0.0", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "minimatch": "^10.2.4", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - }, - "funding": { - "url": "https://eslint.org/donate" - }, - "peerDependencies": { - "jiti": "*" - }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - } - } - }, - "node_modules/eslint-config-prettier": { - "version": "10.1.8", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", - "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", - "dev": true, - "license": "MIT", - "bin": { - "eslint-config-prettier": "bin/cli.js" - }, - "funding": { - "url": "https://opencollective.com/eslint-config-prettier" - }, - "peerDependencies": { - "eslint": ">=7.0.0" - } - }, - "node_modules/eslint-plugin-prettier": { - "version": "5.5.6", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.6.tgz", - "integrity": "sha512-ifetmTcxWfz+4qRW3pH/ujdTq2jQIj59AxJMIN26K5avYgU8dxycUETQonWiW+wPrYXA0j3Try0l1CnwVQtDqQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "prettier-linter-helpers": "^1.0.1", - "synckit": "^0.11.13" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint-plugin-prettier" - }, - "peerDependencies": { - "@types/eslint": ">=8.0.0", - "eslint": ">=8.0.0", - "eslint-config-prettier": ">= 7.0.0 <10.0.0 || >=10.1.0", - "prettier": ">=3.0.0" - }, - "peerDependenciesMeta": { - "@types/eslint": { - "optional": true - }, - "eslint-config-prettier": { - "optional": true - } - } - }, - "node_modules/eslint-scope": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", - "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "@types/esrecurse": "^4.3.1", - "@types/estree": "^1.0.8", - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "node_modules/@typescript/typescript-openbsd-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-openbsd-arm64/-/typescript-openbsd-arm64-7.0.2.tgz", + "integrity": "sha512-APT8+ClYnuYm1u9+kgGXoMj2VzWzcymwh2gNSQVySHfkRDGOTVkoWLjCmOQSaO+PoqQ57B0flRp9SA+7GnnkzQ==", + "cpu": [ + "arm64" + ], "dev": true, "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/eslint-visitor-keys": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", - "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/espree": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", - "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "acorn": "^8.16.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^5.0.1" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/espree/node_modules/eslint-visitor-keys": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", - "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esquery": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", - "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", - "dev": true, - "license": "MIT" - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-diff": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", - "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/file-entry-cache": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", - "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "flat-cache": "^4.0.0" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat-cache": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", - "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", - "dev": true, - "license": "MIT", - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.4" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/flatted": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", - "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", - "dev": true, - "license": "ISC" - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", "optional": true, "os": [ - "darwin" + "openbsd" ], "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/hasown": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", - "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", - "dev": true, - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/ignore": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", - "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/is-core-module": { - "version": "2.16.2", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", - "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", - "dev": true, - "license": "MIT", - "dependencies": { - "hasown": "^2.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "license": "ISC" - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true, - "license": "MIT", - "optional": true - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, - "license": "MIT", - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/leaflet": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.4.tgz", - "integrity": "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==", - "license": "BSD-2-Clause" - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" + "node": ">=16.20.0" } }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "node_modules/@typescript/typescript-openbsd-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-openbsd-x64/-/typescript-openbsd-x64-7.0.2.tgz", + "integrity": "sha512-yX7s+Q0Dln0Dt9tEzZsAjXXR/+ytBM7AlglaqyeMPxQszJ1JhlJdZ6jLA+IzldHtflX81em7lDao1xXu+aRRkg==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^5.0.0" - }, + "license": "Apache-2.0", + "optional": true, + "os": [ + "openbsd" + ], "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=16.20.0" } }, - "node_modules/magic-string": { - "version": "0.30.21", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", - "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "node_modules/@typescript/typescript-sunos-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-sunos-x64/-/typescript-sunos-x64-7.0.2.tgz", + "integrity": "sha512-dLJDGaLZ1D4HPQn62u1n8mBDkJREwMsAkCdkwd4Ieqw+x3TUyTsqY0YiBCtE6H6OzzgGk3iuZ3vFWRS+E8/d1g==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" + "license": "Apache-2.0", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=16.20.0" } }, - "node_modules/minimatch": { - "version": "10.2.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", - "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "node_modules/@typescript/typescript-win32-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-win32-arm64/-/typescript-win32-arm64-7.0.2.tgz", + "integrity": "sha512-Gyl1Vy6OsWesLzmq+EP0Fb7b4Nid5232AvcA2SFcdYreldpNtYFFofPjnt62y9hQy7VTaZp65ICJjuAQRaVcIQ==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "brace-expansion": "^5.0.5" - }, + "license": "Apache-2.0", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=16.20.0" } }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true, - "license": "MIT" - }, - "node_modules/optionator": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "node_modules/@typescript/typescript-win32-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-win32-x64/-/typescript-win32-x64-7.0.2.tgz", + "integrity": "sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT", - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.5" - }, + "license": "Apache-2.0", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">= 0.8.0" + "node": ">=16.20.0" } }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "node_modules/leaflet": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.4.tgz", + "integrity": "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==", + "license": "BSD-2-Clause" + }, + "node_modules/oxlint": { + "version": "1.79.0", + "resolved": "https://registry.npmjs.org/oxlint/-/oxlint-1.79.0.tgz", + "integrity": "sha512-hVJ9hq9m2unPS+Of4eJJgCPdIeCC+3DHEUX3tkmrPJr3OK2hz7PhXwgC+ZP71ZcYu8cCDEtQrqLxWNvxBppBVg==", "dev": true, "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" + "bin": { + "oxlint": "bin/oxlint" }, "engines": { - "node": ">=10" + "node": "^20.19.0 || >=22.12.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^3.0.2" + "url": "https://github.com/sponsors/Boshen" }, - "engines": { - "node": ">=10" + "optionalDependencies": { + "@oxlint/binding-android-arm-eabi": "1.79.0", + "@oxlint/binding-android-arm64": "1.79.0", + "@oxlint/binding-darwin-arm64": "1.79.0", + "@oxlint/binding-darwin-x64": "1.79.0", + "@oxlint/binding-freebsd-x64": "1.79.0", + "@oxlint/binding-linux-arm-gnueabihf": "1.79.0", + "@oxlint/binding-linux-arm-musleabihf": "1.79.0", + "@oxlint/binding-linux-arm64-gnu": "1.79.0", + "@oxlint/binding-linux-arm64-musl": "1.79.0", + "@oxlint/binding-linux-ppc64-gnu": "1.79.0", + "@oxlint/binding-linux-riscv64-gnu": "1.79.0", + "@oxlint/binding-linux-riscv64-musl": "1.79.0", + "@oxlint/binding-linux-s390x-gnu": "1.79.0", + "@oxlint/binding-linux-x64-gnu": "1.79.0", + "@oxlint/binding-linux-x64-musl": "1.79.0", + "@oxlint/binding-openharmony-arm64": "1.79.0", + "@oxlint/binding-win32-arm64-msvc": "1.79.0", + "@oxlint/binding-win32-ia32-msvc": "1.79.0", + "@oxlint/binding-win32-x64-msvc": "1.79.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true, - "license": "MIT" - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, - "license": "ISC", - "optional": true - }, - "node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" + "peerDependencies": { + "oxlint-tsgolint": ">=7.0.2001", + "vite-plus": "*" }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8.0" + "peerDependenciesMeta": { + "oxlint-tsgolint": { + "optional": true + }, + "vite-plus": { + "optional": true + } } }, "node_modules/prettier": { @@ -1846,358 +793,39 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "node_modules/prettier-linter-helpers": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.1.tgz", - "integrity": "sha512-SxToR7P8Y2lWmv/kTzVLC1t/GDI2WGjMwNhLLE9qtH8Q13C+aEmuRlzDst4Up4s0Wc8sF2M+J57iB3cMLqftfg==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-diff": "^1.1.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/resolve": { - "version": "1.22.12", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", - "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "is-core-module": "^2.16.1", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/rollup": { - "version": "4.61.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.61.1.tgz", - "integrity": "sha512-I4KW6iuRpuu2uHBLraZ1wNZe0DP7lnRha+VJ9tNaYVaVgKhW0aI3h4RYnoRPeql0flHm/Co55b7snEDcOfOJrA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "1.0.9" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.61.1", - "@rollup/rollup-android-arm64": "4.61.1", - "@rollup/rollup-darwin-arm64": "4.61.1", - "@rollup/rollup-darwin-x64": "4.61.1", - "@rollup/rollup-freebsd-arm64": "4.61.1", - "@rollup/rollup-freebsd-x64": "4.61.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.61.1", - "@rollup/rollup-linux-arm-musleabihf": "4.61.1", - "@rollup/rollup-linux-arm64-gnu": "4.61.1", - "@rollup/rollup-linux-arm64-musl": "4.61.1", - "@rollup/rollup-linux-loong64-gnu": "4.61.1", - "@rollup/rollup-linux-loong64-musl": "4.61.1", - "@rollup/rollup-linux-ppc64-gnu": "4.61.1", - "@rollup/rollup-linux-ppc64-musl": "4.61.1", - "@rollup/rollup-linux-riscv64-gnu": "4.61.1", - "@rollup/rollup-linux-riscv64-musl": "4.61.1", - "@rollup/rollup-linux-s390x-gnu": "4.61.1", - "@rollup/rollup-linux-x64-gnu": "4.61.1", - "@rollup/rollup-linux-x64-musl": "4.61.1", - "@rollup/rollup-openbsd-x64": "4.61.1", - "@rollup/rollup-openharmony-arm64": "4.61.1", - "@rollup/rollup-win32-arm64-msvc": "4.61.1", - "@rollup/rollup-win32-ia32-msvc": "4.61.1", - "@rollup/rollup-win32-x64-gnu": "4.61.1", - "@rollup/rollup-win32-x64-msvc": "4.61.1", - "fsevents": "~2.3.2" - } - }, - "node_modules/rollup-plugin-dts": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/rollup-plugin-dts/-/rollup-plugin-dts-6.4.1.tgz", - "integrity": "sha512-l//F3Zf7ID5GoOfLfD8kroBjQKEKpy1qfhtAdnpibFZMffPaylrg1CoDC2vGkPeTeyxUe4bVFCln2EFuL7IGGg==", - "dev": true, - "license": "LGPL-3.0-only", - "dependencies": { - "@jridgewell/remapping": "^2.3.5", - "@jridgewell/sourcemap-codec": "^1.5.5", - "convert-source-map": "^2.0.0", - "magic-string": "^0.30.21" - }, - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/Swatinem" - }, - "optionalDependencies": { - "@babel/code-frame": "^7.29.0" - }, - "peerDependencies": { - "rollup": "^3.29.4 || ^4", - "typescript": "^4.5 || ^5.0 || ^6.0" - } - }, - "node_modules/semver": { - "version": "7.8.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.2.tgz", - "integrity": "sha512-c8jsqUZm3omBOI66G90z1Dyw5z622G8oLG+omfsHBJf3CWQTlOcwOjvOG6wtiNfW6anKm/eA39LMwMtMez2TiQ==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/serialize-javascript": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-7.0.5.tgz", - "integrity": "sha512-F4LcB0UqUl1zErq+1nYEEzSHJnIwb3AF2XWB94b+afhrekOUijwooAYqFyRbjYkm2PAKBabx6oYv/xDxNi8IBw==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/smob": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/smob/-/smob-1.6.2.tgz", - "integrity": "sha512-RQsvleCbF8cVHEv+xuDGaA4pOizFqJ0GgjtMSRo6oP8pnN7WsigHgVGey6aILRBKv4W2YOMHLqbKdnB6hpB9fw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/synckit": { - "version": "0.11.13", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.13.tgz", - "integrity": "sha512-eNRKgb3z66Yp3D2CixVujOUvXLFUTij/zVnV8KRyvFdQwpz7I5DS8UfRkTeLzb64u+dkzDSdelE24izu+zSSUg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@pkgr/core": "^0.3.6" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/synckit" - } - }, - "node_modules/terser": { - "version": "5.48.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.48.0.tgz", - "integrity": "sha512-J/9An6vs9Us6wKRriSFXBWdRZapREHqFzdNUKk0pmu804EMR6dr6winwo7e5JDxN4xahxQsuysyYFwlwj4XN/Q==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.15.0", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "bin": { - "terser": "bin/terser" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/tinyglobby": { - "version": "0.2.17", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", - "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fdir": "^6.5.0", - "picomatch": "^4.0.4" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" - } - }, - "node_modules/ts-api-utils": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", - "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18.12" - }, - "peerDependencies": { - "typescript": ">=4.8.4" - } - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, - "license": "0BSD" - }, - "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, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/typescript": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", - "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-7.0.2.tgz", + "integrity": "sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA==", "dev": true, "license": "Apache-2.0", "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" + "tsc": "bin/tsc" }, "engines": { - "node": ">= 8" - } - }, - "node_modules/word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" + "node": ">=16.20.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "optionalDependencies": { + "@typescript/typescript-aix-ppc64": "7.0.2", + "@typescript/typescript-darwin-arm64": "7.0.2", + "@typescript/typescript-darwin-x64": "7.0.2", + "@typescript/typescript-freebsd-arm64": "7.0.2", + "@typescript/typescript-freebsd-x64": "7.0.2", + "@typescript/typescript-linux-arm": "7.0.2", + "@typescript/typescript-linux-arm64": "7.0.2", + "@typescript/typescript-linux-loong64": "7.0.2", + "@typescript/typescript-linux-mips64el": "7.0.2", + "@typescript/typescript-linux-ppc64": "7.0.2", + "@typescript/typescript-linux-riscv64": "7.0.2", + "@typescript/typescript-linux-s390x": "7.0.2", + "@typescript/typescript-linux-x64": "7.0.2", + "@typescript/typescript-netbsd-arm64": "7.0.2", + "@typescript/typescript-netbsd-x64": "7.0.2", + "@typescript/typescript-openbsd-arm64": "7.0.2", + "@typescript/typescript-openbsd-x64": "7.0.2", + "@typescript/typescript-sunos-x64": "7.0.2", + "@typescript/typescript-win32-arm64": "7.0.2", + "@typescript/typescript-win32-x64": "7.0.2" } } } diff --git a/package.json b/package.json index cf5d993..8d8d08c 100644 --- a/package.json +++ b/package.json @@ -2,15 +2,13 @@ "name": "leaflet-components", "version": "0.1.0", "description": "LeafletJS as Web Components", - "main": "dist/index.cjs", + "main": "dist/index.js", "module": "dist/index.js", "types": "dist/index.d.ts", - "unpkg": "dist/index.umd.js", "exports": { ".": { "types": "./dist/index.d.ts", - "import": "./dist/index.js", - "require": "./dist/index.cjs" + "import": "./dist/index.js" }, "./dist/*": "./dist/*", "./package.json": "./package.json" @@ -21,9 +19,9 @@ "README.md" ], "scripts": { - "build": "rm -rf dist && tsc --outDir dist && rollup -c rollup.config.mjs", + "build": "rm -rf dist && tsc --outDir dist", "typecheck": "tsc --noEmit", - "lint": "eslint 'src/**/*.{ts,js}'", + "lint": "oxlint src", "format": "prettier --write 'src/**/*.{ts,js,json,md}'", "prepublishOnly": "npm run build" }, @@ -39,19 +37,9 @@ "leaflet": "1.9.4" }, "devDependencies": { - "@eslint/js": "^10.0.1", - "@rollup/plugin-terser": "^1.0.0", - "@rollup/plugin-typescript": "^12.3.0", "@types/leaflet": "1.9.21", - "@typescript-eslint/eslint-plugin": "8.60.1", - "@typescript-eslint/parser": "8.60.1", - "eslint": "10.4.1", - "eslint-config-prettier": "10.1.8", - "eslint-plugin-prettier": "5.5.6", + "oxlint": "^1.79.0", "prettier": "3.8.3", - "rollup": "^4.61.1", - "rollup-plugin-dts": "^6.4.1", - "tslib": "^2.8.1", - "typescript": "6.0.3" + "typescript": "^7.0.2" } } diff --git a/rollup.config.mjs b/rollup.config.mjs deleted file mode 100644 index 644ed2e..0000000 --- a/rollup.config.mjs +++ /dev/null @@ -1,25 +0,0 @@ -import typescript from '@rollup/plugin-typescript'; -import terser from '@rollup/plugin-terser'; -import dts from 'rollup-plugin-dts'; - -export default [ - { - input: 'src/index.ts', - external: ['leaflet'], - plugins: [typescript({ declaration: false, declarationMap: false })], - output: [ - { format: 'es', file: 'dist/index.js' }, - { format: 'es', file: 'dist/index.min.js', plugins: [terser()] }, - { format: 'cjs', file: 'dist/index.cjs' }, - { format: 'cjs', file: 'dist/index.min.cjs', plugins: [terser()] }, - { format: 'umd', name: 'LeafletComponents', file: 'dist/index.umd.js', globals: { leaflet: 'L' } }, - { format: 'umd', name: 'LeafletComponents', file: 'dist/index.umd.min.js', globals: { leaflet: 'L' }, plugins: [terser()] }, - ], - }, - { - input: 'src/index.ts', - external: ['leaflet'], - plugins: [dts()], - output: { format: 'es', file: 'dist/index.d.ts' }, - }, -]; diff --git a/src/components/leaflet-circle-marker.ts b/src/components/leaflet-circle-marker.ts index cb74791..618ebe8 100644 --- a/src/components/leaflet-circle-marker.ts +++ b/src/components/leaflet-circle-marker.ts @@ -1,66 +1,17 @@ -import { CircleMarker } from 'leaflet'; -import { WithProps, defineProps, num, str, on, buildOptions } from '../core/props.ts'; -import { registerChildren, unregisterChildren } from '../core/register.ts'; -import { isPathStyleAttr, numAttr, updatePathStyle } from '../core/attributes.ts'; - -const PROPS = defineProps({ - lat: num(), - lng: num(), - radius: num(10), - color: str('#3388ff'), - weight: num(3), - opacity: num(1.0), - fill: on(), - fillColor: str('#3388ff'), - fillOpacity: num(0.2), -}); - -export class LeafletCircleMarker extends WithProps(HTMLElement, PROPS) { - #obj?: CircleMarker; - #syncing = false; - - connectedCallback() { - this.#obj = new CircleMarker( - [numAttr(this, PROPS, 'lat'), numAttr(this, PROPS, 'lng')], - buildOptions(this, PROPS, ['lat', 'lng']), - ); - // The `on` method binds the function to `this`, we can ignore the error - // eslint-disable-next-line @typescript-eslint/unbound-method - this.#obj.on('move', this.#onMove, this); - registerChildren(this, this.#obj); - } - - disconnectedCallback() { - // The `off` method binds the function to `this`, we can ignore the error - // eslint-disable-next-line @typescript-eslint/unbound-method - this.#obj?.off('move', this.#onMove, this); - unregisterChildren(this); - this.#obj?.remove(); - this.#obj = undefined; - } - - get leafletObject() { - return this.#obj; - } - - attributeChangedCallback(name: string, _old: string | null, val: string | null) { - if (!this.#obj || this.#syncing) return; - if (name === 'lat' || name === 'lng') { - this.#obj.setLatLng([numAttr(this, PROPS, 'lat'), numAttr(this, PROPS, 'lng')]); - } else if (name === 'radius') { - this.#obj.setRadius(numAttr(this, PROPS, 'radius')); - } else if (isPathStyleAttr(name)) { - updatePathStyle(this.#obj, name, val); - } - } - - #onMove() { - if (!this.#obj || this.#syncing) return; - this.#syncing = true; - const pos = this.#obj.getLatLng(); - this.setAttribute('lat', `${pos.lat}`); - this.setAttribute('lng', `${pos.lng}`); - this.#syncing = false; +import { CircleMarker, type CircleMarkerOptions } from 'leaflet'; +import { num } from '../core/props.ts'; +import { latLngProps, pathProps } from '../core/shared-props.ts'; +import { WithProps } from '../core/with-props.ts'; + +export class LeafletCircleMarker extends WithProps({ + ...latLngProps, + radius: num(10, { get: (obj) => obj.getRadius() }), + ...pathProps, +}) { + declare readonly leafletObject?: CircleMarker; + + createLeafletObject(options: CircleMarkerOptions): CircleMarker { + return new CircleMarker([this.lat, this.lng], options); } } diff --git a/src/components/leaflet-circle.ts b/src/components/leaflet-circle.ts index 4a0b142..e1c51cd 100644 --- a/src/components/leaflet-circle.ts +++ b/src/components/leaflet-circle.ts @@ -1,66 +1,19 @@ -import { Circle } from 'leaflet'; -import { WithProps, defineProps, num, str, on, buildOptions } from '../core/props.ts'; -import { registerChildren, unregisterChildren } from '../core/register.ts'; -import { isPathStyleAttr, numAttr, updatePathStyle } from '../core/attributes.ts'; - -const PROPS = defineProps({ - lat: num(), - lng: num(), - radius: num(1000), - color: str('#3388ff'), - weight: num(3), - opacity: num(1.0), - fill: on(), - fillColor: str('#3388ff'), - fillOpacity: num(0.2), -}); - -export class LeafletCircle extends WithProps(HTMLElement, PROPS) { - #obj?: Circle; - #syncing = false; - - connectedCallback() { - this.#obj = new Circle( - [numAttr(this, PROPS, 'lat'), numAttr(this, PROPS, 'lng')], - buildOptions(this, PROPS, ['lat', 'lng']), - ); - // the on method binds the function to `this`, we can ignore the error - // eslint-disable-next-line @typescript-eslint/unbound-method - this.#obj.on('move', this.#onMove, this); - registerChildren(this, this.#obj); - } - - disconnectedCallback() { - // the off method binds the function to `this`, we can ignore the error - // eslint-disable-next-line @typescript-eslint/unbound-method - this.#obj?.off('move', this.#onMove, this); - unregisterChildren(this); - this.#obj?.remove(); - this.#obj = undefined; - } - - get leafletObject() { - return this.#obj; - } - - attributeChangedCallback(name: string, _old: string | null, val: string | null) { - if (!this.#obj || this.#syncing) return; - if (name === 'lat' || name === 'lng') { - this.#obj.setLatLng([numAttr(this, PROPS, 'lat'), numAttr(this, PROPS, 'lng')]); - } else if (name === 'radius') { - this.#obj.setRadius(numAttr(this, PROPS, 'radius')); - } else if (isPathStyleAttr(name)) { - updatePathStyle(this.#obj, name, val); - } - } - - #onMove() { - if (!this.#obj || this.#syncing) return; - this.#syncing = true; - const pos = this.#obj.getLatLng(); - this.setAttribute('lat', `${pos.lat}`); - this.setAttribute('lng', `${pos.lng}`); - this.#syncing = false; +import { Circle, type CircleOptions } from 'leaflet'; +import { num, positional } from '../core/props.ts'; +import { latLngProps, pathProps } from '../core/shared-props.ts'; +import { WithProps } from '../core/with-props.ts'; + +export class LeafletCircle extends WithProps({ + ...latLngProps, + ...pathProps, + // Leaflet has no default radius -- it throws without one -- so unlike every + // other option, this one is always passed, from the default when unset. + radius: positional(num(1000, { get: (obj) => obj.getRadius() })), +}) { + declare readonly leafletObject?: Circle; + + createLeafletObject(options: CircleOptions): Circle { + return new Circle([this.lat, this.lng], { ...options, radius: this.radius }); } } diff --git a/src/components/leaflet-control-attribution.ts b/src/components/leaflet-control-attribution.ts index 210ae10..fa5e122 100644 --- a/src/components/leaflet-control-attribution.ts +++ b/src/components/leaflet-control-attribution.ts @@ -1,31 +1,18 @@ -import { Control, ControlPosition } from 'leaflet'; -import { WithProps, defineProps, str } from '../core/props.ts'; +import { Control, type ControlPosition } from 'leaflet'; +import { choice, str } from '../core/props.ts'; +import { WithProps } from '../core/with-props.ts'; -import { registerWithParent } from '../core/register.ts'; +export class LeafletControlAttribution extends WithProps( + { + position: choice('bottomright'), + prefix: str(), + }, + { attach: 'self' }, +) { + declare readonly leafletObject?: Control.Attribution; -const PROPS = defineProps({ - position: str('bottomright'), - prefix: str(), -}); - -export class LeafletControlAttribution extends WithProps(HTMLElement, PROPS) { - #obj?: Control.Attribution; - - connectedCallback() { - this.#obj = new Control.Attribution({ - position: this.getAttribute('position') as ControlPosition | undefined, - prefix: this.getAttribute('prefix') ?? undefined, - }); - registerWithParent(this, this.#obj); - } - - disconnectedCallback() { - this.#obj?.remove(); - this.#obj = undefined; - } - - get leafletObject() { - return this.#obj; + createLeafletObject(options: Control.AttributionOptions): Control.Attribution { + return new Control.Attribution(options); } } diff --git a/src/components/leaflet-control-layers.ts b/src/components/leaflet-control-layers.ts index 7e9d9be..17c7d35 100644 --- a/src/components/leaflet-control-layers.ts +++ b/src/components/leaflet-control-layers.ts @@ -1,78 +1,81 @@ -import { Control, ControlPosition, Layer } from 'leaflet'; -import { WithProps, defineProps, str } from '../core/props.ts'; -import { LeafletRegisterEvent, registerWithParent } from '../core/register.ts'; +import { Control, type ControlPosition, type Layer } from 'leaflet'; +import { bool, choice } from '../core/props.ts'; +import { WithProps } from '../core/with-props.ts'; +import type { LeafletRegisterEvent } from '../core/register.ts'; -const PROPS = defineProps({ - position: str('topright'), -}); +interface ChildLayer { + layer: Layer; + name: string; + base: boolean; + active: boolean; +} -export class LeafletControlLayers extends WithProps(HTMLElement, PROPS) { - #obj?: Control.Layers; +export class LeafletControlLayers extends WithProps( + { + position: choice('topright'), + collapsed: bool(true), + autoZIndex: bool(true), + hideSingleBase: bool(), + sortLayers: bool(), + }, + { attach: 'self' }, +) { + declare readonly leafletObject?: Control.Layers; - connectedCallback() { + // Children are read straight off the DOM rather than through registration: + // any that already exist are listed as base layers or overlays by their + // `name`, and `type="base"` / `active` decide how. + createLeafletObject(options: Control.LayersOptions): Control.Layers { const baseLayers: Record = {}; const overlays: Record = {}; - const inactiveLayers: Layer[] = []; - - for (const child of this.querySelectorAll(':scope > *')) { - const layer = (child as unknown as { leafletObject?: Layer }).leafletObject; - const name = child.getAttribute('name'); - if (!name || !layer) continue; - const base = child.getAttribute('type') === 'base'; - const active = child.hasAttribute('active'); - (base ? baseLayers : overlays)[name] = layer; - if (!active) inactiveLayers.push(layer); + for (const child of this.#childLayers()) { + (child.base ? baseLayers : overlays)[child.name] = child.layer; } + return new Control.Layers(baseLayers, overlays, options); + } - for (const layer of inactiveLayers) { + connectedCallback(): void { + super.connectedCallback(); + for (const child of this.#childLayers()) { + if (child.active) continue; this.dispatchEvent( - new CustomEvent('leaflet-remove-layer', { bubbles: true, detail: { layer } }), + new CustomEvent('leaflet-remove-layer', { bubbles: true, detail: { layer: child.layer } }), ); } - - this.#obj = new Control.Layers(baseLayers, overlays, { - position: this.getAttribute('position') as ControlPosition | undefined, - collapsed: !this.hasAttribute('collapsed') || this.getAttribute('collapsed') !== 'false', - autoZIndex: - !this.hasAttribute('auto-z-index') || this.getAttribute('auto-z-index') !== 'false', - hideSingleBase: this.hasAttribute('hide-single-base'), - sortLayers: this.hasAttribute('sort-layers'), - }); - - registerWithParent(this, this.#obj); this.addEventListener('leaflet-register', this.#onChildRegister); } - disconnectedCallback() { + disconnectedCallback(): void { this.removeEventListener('leaflet-register', this.#onChildRegister); - this.#obj?.remove(); - this.#obj = undefined; + super.disconnectedCallback(); } - attributeChangedCallback(name: string) { - if (name === 'position' && this.#obj) { - this.#obj.setPosition(this.getAttribute('position') as ControlPosition); + #childLayers(): ChildLayer[] { + const children: ChildLayer[] = []; + for (const child of this.querySelectorAll(':scope > *')) { + const layer = (child as { leafletObject?: Layer }).leafletObject; + const name = child.getAttribute('name'); + if (!name || !layer) continue; + children.push({ + layer, + name, + base: child.getAttribute('type') === 'base', + active: child.hasAttribute('active'), + }); } + return children; } - get leafletObject() { - return this.#obj; - } - + // Children that connect after us announce themselves instead. #onChildRegister = (e: LeafletRegisterEvent) => { e.stopPropagation(); const el = e.detail.element; const layer = e.detail.leafletObject; const name = el.getAttribute('name'); if (!name) return; - const base = el.getAttribute('type') === 'base'; - const active = el.hasAttribute('active'); - if (base) { - this.#obj?.addBaseLayer(layer, name); - } else { - this.#obj?.addOverlay(layer, name); - } - if (active) { + if (el.getAttribute('type') === 'base') this.leafletObject?.addBaseLayer(layer, name); + else this.leafletObject?.addOverlay(layer, name); + if (el.hasAttribute('active')) { this.dispatchEvent( new CustomEvent('leaflet-add-layer', { bubbles: true, detail: { layer } }), ); diff --git a/src/components/leaflet-control-scale.ts b/src/components/leaflet-control-scale.ts index 298b8c2..d753aef 100644 --- a/src/components/leaflet-control-scale.ts +++ b/src/components/leaflet-control-scale.ts @@ -1,38 +1,21 @@ -import { Control, ControlPosition } from 'leaflet'; -import { WithProps, defineProps, num, str, on } from '../core/props.ts'; +import { Control, type ControlPosition } from 'leaflet'; +import { bool, choice, num } from '../core/props.ts'; +import { WithProps } from '../core/with-props.ts'; -import { registerWithParent } from '../core/register.ts'; +export class LeafletControlScale extends WithProps( + { + position: choice('bottomleft'), + maxWidth: num(100), + metric: bool(true), + imperial: bool(true), + updateWhenIdle: bool(), + }, + { attach: 'self' }, +) { + declare readonly leafletObject?: Control.Scale; -const PROPS = defineProps({ - position: str('bottomleft'), - maxWidth: num(100), - metric: on(), - imperial: on(), - updateWhenIdle: on(), -}); - -export class LeafletControlScale extends WithProps(HTMLElement, PROPS) { - #obj?: Control.Scale; - - connectedCallback() { - this.#obj = new Control.Scale({ - position: this.getAttribute('position') as ControlPosition | undefined, - maxWidth: +(this.getAttribute('max-width') ?? 100), - metric: !this.hasAttribute('metric') || this.getAttribute('metric') !== 'false', - imperial: !this.hasAttribute('imperial') || this.getAttribute('imperial') !== 'false', - updateWhenIdle: - !this.hasAttribute('update-when-idle') || this.getAttribute('update-when-idle') !== 'false', - }); - registerWithParent(this, this.#obj); - } - - disconnectedCallback() { - this.#obj?.remove(); - this.#obj = undefined; - } - - get leafletObject() { - return this.#obj; + createLeafletObject(options: Control.ScaleOptions): Control.Scale { + return new Control.Scale(options); } } diff --git a/src/components/leaflet-control-zoom.ts b/src/components/leaflet-control-zoom.ts index 7b464e7..ef60c99 100644 --- a/src/components/leaflet-control-zoom.ts +++ b/src/components/leaflet-control-zoom.ts @@ -1,37 +1,23 @@ -import { Control, ControlPosition } from 'leaflet'; -import { WithProps, defineProps, str } from '../core/props.ts'; +import { Control, type ControlPosition } from 'leaflet'; +import { choice, str } from '../core/props.ts'; +import { WithProps } from '../core/with-props.ts'; -import { registerWithParent } from '../core/register.ts'; +// The button labels default to Leaflet's own markup, which hides the glyph +// from screen readers in favour of the title. +export class LeafletControlZoom extends WithProps( + { + position: choice('topleft'), + zoomInText: str(''), + zoomInTitle: str('Zoom in'), + zoomOutText: str(''), + zoomOutTitle: str('Zoom out'), + }, + { attach: 'self' }, +) { + declare readonly leafletObject?: Control.Zoom; -const PROPS = defineProps({ - position: str('topleft'), - zoomInText: str('+'), - zoomInTitle: str('Zoom in'), - zoomOutText: str('-'), - zoomOutTitle: str('Zoom out'), -}); - -export class LeafletControlZoom extends WithProps(HTMLElement, PROPS) { - #obj?: Control.Zoom; - - connectedCallback() { - this.#obj = new Control.Zoom({ - position: this.getAttribute('position') as ControlPosition | undefined, - zoomInText: this.getAttribute('zoom-in-text') ?? '+', - zoomInTitle: this.getAttribute('zoom-in-title') ?? 'Zoom in', - zoomOutText: this.getAttribute('zoom-out-text') ?? '-', - zoomOutTitle: this.getAttribute('zoom-out-title') ?? 'Zoom out', - }); - registerWithParent(this, this.#obj); - } - - disconnectedCallback() { - this.#obj?.remove(); - this.#obj = undefined; - } - - get leafletObject() { - return this.#obj; + createLeafletObject(options: Control.ZoomOptions): Control.Zoom { + return new Control.Zoom(options); } } diff --git a/src/components/leaflet-div-icon.ts b/src/components/leaflet-div-icon.ts index 09548dd..2759cac 100644 --- a/src/components/leaflet-div-icon.ts +++ b/src/components/leaflet-div-icon.ts @@ -1,76 +1,47 @@ -import { DivIcon, DivIconOptions } from 'leaflet'; -import { defineProps, str } from '../core/props.ts'; +import { DivIcon, type DivIconOptions, type PointExpression } from 'leaflet'; +import { json, str } from '../core/props.ts'; import { emitIconChanged } from '../core/register.ts'; +import { WithProps } from '../core/with-props.ts'; + +// Like leaflet-icon, rebuilt on every change -- including changes to the +// markup, which is what the icon renders when `html` isn't set. +export class LeafletDivIcon extends WithProps( + { + iconSize: json([0, 0]), + iconAnchor: json([0, 0]), + popupAnchor: json([0, 0]), + tooltipAnchor: json([0, 0]), + className: str('leaflet-div-icon'), + html: str(), + bgPos: json([0, 0]), + }, + { attach: 'none', recreate: true }, +) { + declare readonly leafletObject?: DivIcon; -const PROPS = defineProps({ - iconSize: str(), - iconAnchor: str(), - popupAnchor: str(), - tooltipAnchor: str(), - className: str(), - html: str(), - bgPos: str(), -}); - -const JSON_KEYS = new Set([ - 'iconSize', - 'iconAnchor', - 'popupAnchor', - 'tooltipAnchor', - 'bgPos', -]); - -export class LeafletDivIcon extends HTMLElement { - #obj?: DivIcon; #observer?: MutationObserver; - connectedCallback() { - this.#applyIcon(); - emitIconChanged(this, this.#obj); + createLeafletObject(options: DivIconOptions): DivIcon { + return new DivIcon(this.innerHTML ? { ...options, html: this.innerHTML } : options); + } + connectedCallback(): void { + super.connectedCallback(); this.#observer = new MutationObserver(() => { - this.#applyIcon(); - emitIconChanged(this, this.#obj); - }); - this.#observer.observe(this, { - childList: true, - characterData: true, - subtree: true, + this.recreateLeafletObject(); }); + this.#observer.observe(this, { childList: true, characterData: true, subtree: true }); } - disconnectedCallback() { + disconnectedCallback(): void { this.#observer?.disconnect(); this.#observer = undefined; + super.disconnectedCallback(); emitIconChanged(this, null); } - static get observedAttributes() { - return Object.values(PROPS).map((s) => s.attr); - } - - attributeChangedCallback() { - this.#applyIcon(); - if (!this.#obj) return; - emitIconChanged(this, this.#obj); - } - - get leafletObject() { - return this.#obj; - } - - #applyIcon() { - const opts: Partial = {}; - for (const [key, spec] of Object.entries(PROPS)) { - const v = this.getAttribute(spec.attr); - if (v === null) continue; - const typedKey = key as keyof typeof PROPS; - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - opts[typedKey as keyof DivIconOptions] = JSON_KEYS.has(typedKey) ? JSON.parse(v) : v; - } - const content = this.innerHTML; - if (content) opts.html = content; - this.#obj = new DivIcon(opts); + leafletObjectCreated(): void { + emitIconChanged(this, this.leafletObject); } } diff --git a/src/components/leaflet-feature-group.ts b/src/components/leaflet-feature-group.ts index 5ff3cd4..4d87f58 100644 --- a/src/components/leaflet-feature-group.ts +++ b/src/components/leaflet-feature-group.ts @@ -1,33 +1,12 @@ -import { FeatureGroup, Layer } from 'leaflet'; -import { registerChildren, unregisterChildren, getChildren } from '../core/register.ts'; +import { FeatureGroup } from 'leaflet'; +import { WithProps } from '../core/with-props.ts'; -import { buildOptions } from '../core/props.ts'; +// Like leaflet-layer-group, but its children share events and a bounding box. +export class LeafletFeatureGroup extends WithProps({}) { + declare readonly leafletObject?: FeatureGroup; -export class LeafletFeatureGroup extends HTMLElement { - #obj?: FeatureGroup; - - static get observedAttributes() { - return []; - } - - connectedCallback() { - this.#obj = new FeatureGroup([], buildOptions(this, {})); - registerChildren(this, this.#obj); - } - - disconnectedCallback() { - for (const [el, type] of getChildren(this) ?? []) { - if (type === 'popup') this.#obj?.unbindPopup(); - else if (type === 'tooltip') this.#obj?.unbindTooltip(); - else this.#obj?.removeLayer(el as unknown as Layer); - } - unregisterChildren(this); - this.#obj?.remove(); - this.#obj = undefined; - } - - get leafletObject() { - return this.#obj; + createLeafletObject(): FeatureGroup { + return new FeatureGroup([]); } } diff --git a/src/components/leaflet-geojson.ts b/src/components/leaflet-geojson.ts index 087e793..2d9b2aa 100644 --- a/src/components/leaflet-geojson.ts +++ b/src/components/leaflet-geojson.ts @@ -1,69 +1,26 @@ -import { GeoJSON, PathOptions, Layer } from 'leaflet'; -import { WithProps, defineProps, num, str, on, buildOptions } from '../core/props.ts'; -import { registerChildren, unregisterChildren, getChildren } from '../core/register.ts'; +import { GeoJSON, type PathOptions } from 'leaflet'; import type { GeoJsonObject } from 'geojson'; - -const PROPS = defineProps({ - data: str(), - stroke: str(), - color: str('#3388ff'), - weight: num(3), - opacity: num(1.0), - lineCap: str('round'), - lineJoin: str('round'), - dashArray: str(), - dashOffset: str(), - fill: on(), - fillColor: str('#3388ff'), - fillOpacity: num(0.2), - fillRule: str('evenodd'), -}); - -const PROP_BY_ATTR = new Map( - Object.entries(PROPS).map(([name, spec]) => [spec.attr, name]), -); - -export class LeafletGeoJSON extends WithProps(HTMLElement, PROPS) { - #obj?: GeoJSON; - - connectedCallback() { - const raw = this.getAttribute('data'); - const data = raw ? (JSON.parse(raw) as GeoJsonObject) : null; - const styleOpts = Object.fromEntries( - Object.entries(buildOptions(this, PROPS, ['data'])).filter(([, v]) => v !== ''), - ); - this.#obj = new GeoJSON(data, { style: styleOpts as PathOptions }); - registerChildren(this, this.#obj); - } - - disconnectedCallback() { - for (const [el, type] of getChildren(this) ?? []) { - if (type === 'popup') this.#obj?.unbindPopup(); - else if (type === 'tooltip') this.#obj?.unbindTooltip(); - else this.#obj?.removeLayer(el as unknown as Layer); - } - unregisterChildren(this); - this.#obj?.remove(); - this.#obj = undefined; - } - - get leafletObject() { - return this.#obj; - } - - attributeChangedCallback(name: string, _old: string | null, val: string | null) { - if (!this.#obj) return; - if (name === 'data') { - this.#obj.clearLayers(); - // eslint-disable-next-line @typescript-eslint/no-unsafe-argument - if (val) this.#obj.addData(JSON.parse(val)); - } else { - const propName = PROP_BY_ATTR.get(name); - if (!propName) return; - const spec = PROPS[propName as keyof typeof PROPS]; - const styleVal = spec.kind === 'bool-on' ? val !== null : val; - this.#obj.setStyle({ [propName]: styleVal } as PathOptions); - } +import { json, positional } from '../core/props.ts'; +import { pathProps } from '../core/shared-props.ts'; +import { WithProps } from '../core/with-props.ts'; + +export class LeafletGeoJSON extends WithProps({ + data: positional( + json(null, { + set(obj: GeoJSON, value) { + obj.clearLayers(); + if (value) obj.addData(value); + }, + }), + ), + ...pathProps, +}) { + declare readonly leafletObject?: GeoJSON; + + // Every prop but `data` is a style option, and GeoJSON takes those nested + // under `style` so they apply to each feature it builds. + createLeafletObject(options: PathOptions): GeoJSON { + return new GeoJSON(this.data, { style: options }); } } diff --git a/src/components/leaflet-icon.ts b/src/components/leaflet-icon.ts index bcbc1d0..4b522dd 100644 --- a/src/components/leaflet-icon.ts +++ b/src/components/leaflet-icon.ts @@ -1,68 +1,38 @@ -import { Icon, IconOptions } from 'leaflet'; -import { defineProps, str } from '../core/props.ts'; +import { Icon, type IconOptions, type PointExpression } from 'leaflet'; +import { json, str } from '../core/props.ts'; import { emitIconChanged } from '../core/register.ts'; +import { WithProps } from '../core/with-props.ts'; -const PROPS = defineProps({ +const PROPS = { iconUrl: str(), iconRetinaUrl: str(), - iconSize: str(), - iconAnchor: str(), - popupAnchor: str(), - tooltipAnchor: str(), + iconSize: json([0, 0]), + iconAnchor: json([0, 0]), + popupAnchor: json([0, 0]), + tooltipAnchor: json([0, 0]), shadowUrl: str(), shadowRetinaUrl: str(), - shadowSize: str(), - shadowAnchor: str(), + shadowSize: json([0, 0]), + shadowAnchor: json([0, 0]), className: str(), -}); +} as const; -const JSON_KEYS = new Set([ - 'iconSize', - 'iconAnchor', - 'popupAnchor', - 'tooltipAnchor', - 'shadowSize', - 'shadowAnchor', -]); +// An Icon has no setters, so every attribute change builds a new one and the +// parent marker is told to swap it in. +export class LeafletIcon extends WithProps(PROPS, { attach: 'none', recreate: true }) { + declare readonly leafletObject?: Icon; -export class LeafletIcon extends HTMLElement { - #obj?: Icon; - - connectedCallback() { - this.#applyIcon(); - emitIconChanged(this, this.#obj); + createLeafletObject(options: Partial): Icon | undefined { + return options.iconUrl ? new Icon(options as IconOptions) : undefined; } - disconnectedCallback() { - emitIconChanged(this, null); + leafletObjectCreated(): void { + emitIconChanged(this, this.leafletObject); } - static get observedAttributes() { - return Object.values(PROPS).map((s) => s.attr); - } - - attributeChangedCallback() { - this.#applyIcon(); - if (!this.#obj) return; - emitIconChanged(this, this.#obj); - } - - get leafletObject() { - return this.#obj; - } - - #applyIcon() { - const opts: Record = {}; - for (const [key, spec] of Object.entries(PROPS)) { - const v = this.getAttribute(spec.attr); - if (v === null) continue; - opts[key] = JSON_KEYS.has(key) ? JSON.parse(v) : v; - } - if (opts.iconUrl) { - this.#obj = new Icon(opts as unknown as IconOptions); - } else { - this.#obj = undefined; - } + disconnectedCallback(): void { + super.disconnectedCallback(); + emitIconChanged(this, null); } } diff --git a/src/components/leaflet-image-overlay.ts b/src/components/leaflet-image-overlay.ts index 108169e..4651648 100644 --- a/src/components/leaflet-image-overlay.ts +++ b/src/components/leaflet-image-overlay.ts @@ -1,58 +1,33 @@ -import { ImageOverlay, LatLngBounds, LatLngExpression } from 'leaflet'; -import type { ImageOverlayOptions } from 'leaflet'; -import { WithProps, defineProps, num, str, on, buildOptions } from '../core/props.ts'; -import { registerChildren, unregisterChildren } from '../core/register.ts'; -import { buildAttrMap, parseBoundsAttr, setLayerAttr } from '../core/attributes.ts'; - -const PROPS = defineProps({ - url: str(), - bounds: str(), +import { + ImageOverlay, + type CrossOrigin, + type ImageOverlayOptions, + type LatLngBoundsExpression, +} from 'leaflet'; +import { bool, choice, json, num, positional, str } from '../core/props.ts'; +import { getBounds, urlProp } from '../core/shared-props.ts'; +import { WithProps } from '../core/with-props.ts'; + +export class LeafletImageOverlay extends WithProps({ + url: urlProp, + bounds: positional(json([], { get: getBounds })), opacity: num(1.0), - alt: str(), - interactive: on(), - crossOrigin: str(), + alt: str('', { + set(obj: ImageOverlay, value) { + const el = obj.getElement(); + if (el) el.alt = value; + }, + }), + interactive: bool(), + crossOrigin: choice(''), errorOverlayUrl: str(), zIndex: num(), className: str(), -}); - -const PROP_BY_ATTR = buildAttrMap(PROPS); - -export class LeafletImageOverlay extends WithProps(HTMLElement, PROPS) { - #obj?: ImageOverlay; - - connectedCallback() { - const url = this.getAttribute('url') ?? ''; - this.#obj = new ImageOverlay( - url, - parseBoundsAttr(this), - buildOptions(this, PROPS, ['url', 'bounds']) as ImageOverlayOptions, - ); - registerChildren(this, this.#obj); - } - - disconnectedCallback() { - unregisterChildren(this); - this.#obj?.remove(); - this.#obj = undefined; - } - - get leafletObject() { - return this.#obj; - } +}) { + declare readonly leafletObject?: ImageOverlay; - attributeChangedCallback(name: string, _old: string | null, val: string | null) { - if (!this.#obj) return; - if (name === 'url') { - if (val) this.#obj.setUrl(val); - } else if (name === 'bounds') { - this.#obj.setBounds(new LatLngBounds(parseBoundsAttr(this) as LatLngExpression[])); - } else if (name === 'alt') { - const el = this.#obj.getElement(); - if (el) el.alt = val ?? ''; - } else { - setLayerAttr(this.#obj, PROPS, PROP_BY_ATTR, name, val); - } + createLeafletObject(options: ImageOverlayOptions): ImageOverlay { + return new ImageOverlay(this.url, this.bounds, options); } } diff --git a/src/components/leaflet-layer-group.ts b/src/components/leaflet-layer-group.ts index 181bc80..c7adc92 100644 --- a/src/components/leaflet-layer-group.ts +++ b/src/components/leaflet-layer-group.ts @@ -1,33 +1,13 @@ -import { LayerGroup, Layer } from 'leaflet'; -import { registerChildren, unregisterChildren, getChildren } from '../core/register.ts'; +import { LayerGroup } from 'leaflet'; +import { WithProps } from '../core/with-props.ts'; -import { buildOptions } from '../core/props.ts'; +// A passthrough container: it has no options of its own, and children add +// themselves to it through the standard registration bubble. +export class LeafletLayerGroup extends WithProps({}) { + declare readonly leafletObject?: LayerGroup; -export class LeafletLayerGroup extends HTMLElement { - #obj?: LayerGroup; - - static get observedAttributes() { - return []; - } - - connectedCallback() { - this.#obj = new LayerGroup([], buildOptions(this, {})); - registerChildren(this, this.#obj); - } - - disconnectedCallback() { - for (const [el, type] of getChildren(this) ?? []) { - if (type === 'popup') this.#obj?.unbindPopup(); - else if (type === 'tooltip') this.#obj?.unbindTooltip(); - else this.#obj?.removeLayer(el as unknown as Layer); - } - unregisterChildren(this); - this.#obj?.remove(); - this.#obj = undefined; - } - - get leafletObject() { - return this.#obj; + createLeafletObject(): LayerGroup { + return new LayerGroup([]); } } diff --git a/src/components/leaflet-map.ts b/src/components/leaflet-map.ts index ec16ba1..f6735d8 100644 --- a/src/components/leaflet-map.ts +++ b/src/components/leaflet-map.ts @@ -1,259 +1,172 @@ -import { Icon, Layer, Map as LMap, MapOptions } from 'leaflet'; -import { defineProps, num, off, on, type NumProp, type PropDef } from '../core/props.ts'; -import { LeafletRegisterEvent } from '../core/register.ts'; +import { Icon, Map as LMap, type MapOptions } from 'leaflet'; +import { bool, disabled, num, positional, str } from '../core/props.ts'; +import { WithProps } from '../core/with-props.ts'; +import type { LeafletLayerEvent, LeafletRegisterEvent } from '../core/register.ts'; const DEFAULT_CSS_URL = 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.css'; const DEFAULT_CSS_INTEGRITY = 'sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY='; -const CSS_ATTRS = ['css-url', 'css-integrity', 'css-crossorigin'] as const; - -const PROPS = defineProps({ - // View state — excluded from #buildOptions, initialised via setView() - lat: num(0, { - viewState: true, - event: 'moveend', - mapGet: (m: LMap) => m.getCenter().lat, - mapSet: (m: LMap, v: number) => m.setView([v, m.getCenter().lng], m.getZoom()), - }), - lng: num(0, { - viewState: true, - event: 'moveend', - mapGet: (m: LMap) => m.getCenter().lng, - mapSet: (m: LMap, v: number) => m.setView([m.getCenter().lat, v], m.getZoom()), - }), - zoom: num(2, { - viewState: true, - event: 'zoomend', - mapGet: (m: LMap) => m.getZoom(), - mapSet: (m: LMap, v: number) => m.setZoom(v), - }), - - // Live numeric options — have Leaflet setters - minZoom: num(0, { - mapGet: (m: LMap) => m.getMinZoom(), - mapSet: (m: LMap, v: number) => m.setMinZoom(v), - }), - maxZoom: num(Infinity, { - mapGet: (m: LMap) => m.getMaxZoom(), - mapSet: (m: LMap, v: number) => m.setMaxZoom(v), - }), - - // Constructor-only numeric options - zoomSnap: num(1), - zoomDelta: num(1), - keyboardPanDelta: num(80), - wheelDebounceTime: num(40), - wheelPxPerZoomLevel: num(60), - inertiaDeceleration: num(3000), - inertiaMaxSpeed: num(Infinity), - easeLinearity: num(0.2), - maxBoundsViscosity: num(0), - tapTolerance: num(15), - zoomAnimationThreshold: num(4), - transform3DLimit: num(8388608), - - // Boolean defaults-true → disable-* attribute; handler-based options have live mapSet - scrollWheelZoom: off((m: LMap, v: boolean) => - v ? m.scrollWheelZoom.enable() : m.scrollWheelZoom.disable(), - ), - dragging: off((m: LMap, v: boolean) => (v ? m.dragging.enable() : m.dragging.disable())), - touchZoom: off((m: LMap, v: boolean) => (v ? m.touchZoom.enable() : m.touchZoom.disable())), - doubleClickZoom: off((m: LMap, v: boolean) => - v ? m.doubleClickZoom.enable() : m.doubleClickZoom.disable(), - ), - boxZoom: off((m: LMap, v: boolean) => (v ? m.boxZoom.enable() : m.boxZoom.disable())), - keyboard: off((m: LMap, v: boolean) => (v ? m.keyboard.enable() : m.keyboard.disable())), - closePopupOnClick: off(), - trackResize: off(), - zoomControl: off(), - attributionControl: off(), - inertia: off(), - zoomAnimation: off(), - fadeAnimation: off(), - markerZoomAnimation: off(), - bounceAtZoomLimits: off(), - tapHold: off(), - - // Boolean defaults-false → normal attribute - preferCanvas: on(), - worldCopyJump: on(), -}); - -type PropName = keyof typeof PROPS; - -const ATTR_TO_PROP = new Map( - (Object.entries(PROPS) as [PropName, PropDef][]).map(([name, spec]) => [spec.attr, name]), -); - -// Derive property types directly from the PROPS table so adding a prop to the -// table automatically makes it part of the LeafletMap instance type. -type PropTypes = { - [K in keyof typeof PROPS]: (typeof PROPS)[K] extends { kind: 'num' } ? number : boolean; -}; +interface CssHost extends HTMLElement { + applyCss(): void; +} -const TypedBase = HTMLElement as unknown as new () => HTMLElement & PropTypes; +// The css-* attributes describe the stylesheet in the shadow root rather than +// anything about the Leaflet map, so a change just re-links it. +function relinkCss(_map: LMap, _value: string, el: HTMLElement): void { + (el as CssHost).applyCss(); +} -export class LeafletMap extends TypedBase { - #map?: LMap; - #container!: HTMLDivElement; +// The root of the component tree. Every other component bubbles a +// `leaflet-register` event up to here, which is where it stops. +export class LeafletMap extends WithProps( + { + // View state. Not constructor options -- the map is positioned with setView + // once it exists -- and written back whenever the user pans or zooms. + lat: positional( + num(0, { + event: 'moveend', + get: (map) => map.getCenter().lat, + set: (map, value) => { + map.setView([value, map.getCenter().lng], map.getZoom()); + }, + }), + ), + lng: positional( + num(0, { + event: 'moveend', + get: (map) => map.getCenter().lng, + set: (map, value) => { + map.setView([map.getCenter().lat, value], map.getZoom()); + }, + }), + ), + zoom: positional( + num(2, { + event: 'zoomend', + get: (map) => map.getZoom(), + set: (map, value) => { + map.setZoom(value); + }, + }), + ), + + // Numeric options Leaflet lets us change after construction. + minZoom: num(0, { + get: (map) => map.getMinZoom(), + set: (map, value) => { + map.setMinZoom(value); + }, + }), + maxZoom: num(Infinity, { + get: (map) => map.getMaxZoom(), + set: (map, value) => { + map.setMaxZoom(value); + }, + }), + + // Constructor-only numeric options. + zoomSnap: num(1), + zoomDelta: num(1), + keyboardPanDelta: num(80), + wheelDebounceTime: num(40), + wheelPxPerZoomLevel: num(60), + inertiaDeceleration: num(3000), + inertiaMaxSpeed: num(Infinity), + easeLinearity: num(0.2), + maxBoundsViscosity: num(0), + tapTolerance: num(15), + zoomAnimationThreshold: num(4), + transform3DLimit: num(8388608), + + // Options Leaflet defaults to true, turned off with disable-* attributes. + // The interaction handlers can be toggled after construction. + scrollWheelZoom: disabled({ + set: (map, on) => (on ? map.scrollWheelZoom.enable() : map.scrollWheelZoom.disable()), + }), + dragging: disabled({ + set: (map, on) => (on ? map.dragging.enable() : map.dragging.disable()), + }), + touchZoom: disabled({ + set: (map, on) => (on ? map.touchZoom.enable() : map.touchZoom.disable()), + }), + doubleClickZoom: disabled({ + set: (map, on) => (on ? map.doubleClickZoom.enable() : map.doubleClickZoom.disable()), + }), + boxZoom: disabled({ + set: (map, on) => (on ? map.boxZoom.enable() : map.boxZoom.disable()), + }), + keyboard: disabled({ + set: (map, on) => (on ? map.keyboard.enable() : map.keyboard.disable()), + }), + closePopupOnClick: disabled(), + trackResize: disabled(), + zoomControl: disabled(), + attributionControl: disabled(), + inertia: disabled(), + zoomAnimation: disabled(), + fadeAnimation: disabled(), + markerZoomAnimation: disabled(), + bounceAtZoomLimits: disabled(), + tapHold: disabled(), + + // Options Leaflet defaults to false. + preferCanvas: bool(), + worldCopyJump: bool(), + + // Not Leaflet options at all -- see relinkCss above. + cssUrl: positional(str(DEFAULT_CSS_URL, { set: relinkCss })), + cssIntegrity: positional(str(DEFAULT_CSS_INTEGRITY, { set: relinkCss })), + cssCrossorigin: positional(str('', { set: relinkCss })), + }, + { attach: 'none' }, +) { + declare readonly leafletObject?: LMap; + + #container?: HTMLDivElement; #cssLink?: HTMLLinkElement; - #syncing = false; - #mapEventHandlers = new Map void>(); #resizeObserver?: ResizeObserver; - static get observedAttributes(): string[] { - return [...Object.values(PROPS).map((s) => s.attr), ...CSS_ATTRS]; - } - - // Generates a getter/setter on the prototype for every entry in PROPS. - // The functions are defined inside the class body so they can access private fields. - static { - for (const [propName, spec] of Object.entries(PROPS) as [PropName, PropDef][]) { - if (spec.kind === 'num') { - Object.defineProperty(LeafletMap.prototype, propName, { - get(this: LeafletMap) { - const val = spec.mapGet && this.#map ? spec.mapGet(this.#map) : undefined; - return val ?? +(this.getAttribute(spec.attr) ?? spec.default); - }, - set(this: LeafletMap, v: number) { - if (spec.event) { - this.#syncAttr(spec.attr, `${v}`); - } else { - this.setAttribute(spec.attr, `${v}`); - } - }, - configurable: true, - enumerable: true, - }); - } else if (spec.kind === 'bool-off') { - Object.defineProperty(LeafletMap.prototype, propName, { - get(this: LeafletMap) { - return !this.hasAttribute(spec.attr); - }, - set(this: LeafletMap, v: boolean) { - this.toggleAttribute(spec.attr, !v); - }, - configurable: true, - enumerable: true, - }); - } else { - Object.defineProperty(LeafletMap.prototype, propName, { - get(this: LeafletMap) { - return this.hasAttribute(spec.attr); - }, - set(this: LeafletMap, v: boolean) { - this.toggleAttribute(spec.attr, v); - }, - configurable: true, - enumerable: true, - }); - } - } + createLeafletObject(options: MapOptions): LMap { + const map = new LMap(this.#container ?? this.#buildShadowRoot(), options); + // getCenter() and getZoom() throw until the view is set. Reading this.lat, + // this.lng and this.zoom here is safe for the same reason: the object + // doesn't exist yet, so the getters fall back to the attributes. + map.setView([this.lat, this.lng], this.zoom); + return map; } - connectedCallback() { - if (!this.shadowRoot) { - this.attachShadow({ mode: 'open' }); - this.#container = document.createElement('div'); - this.#container.style.width = '100%'; - this.#container.style.height = '100%'; - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - this.shadowRoot!.appendChild(this.#container); - const style = document.createElement('style'); - style.textContent = ':host { display: block; width: 100%; height: 400px; }'; - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - this.shadowRoot!.appendChild(style); - } - this.#applyCss(); - - // Read view-state from attributes directly — the getters call getCenter()/getZoom() - // which throw if invoked before setView(), so we can't use them here. - const lat = +(this.getAttribute('lat') ?? 0); - const lng = +(this.getAttribute('lng') ?? 0); - const zoom = +(this.getAttribute('zoom') ?? 2); - - // Assign #map only after setView so the getters' `this.#map` guard is - // equivalent to "map is ready" — getCenter()/getZoom() throw before setView. - const map = new LMap(this.#container, this.#buildOptions()); - map.setView([lat, lng], zoom); - this.#map = map; + connectedCallback(): void { + this.#buildShadowRoot(); + this.applyCss(); + super.connectedCallback(); - this.#resizeObserver = new ResizeObserver(() => this.#map?.invalidateSize()); + this.#resizeObserver = new ResizeObserver(() => this.leafletObject?.invalidateSize()); this.#resizeObserver.observe(this); - // Register one handler per unique map event, updating all props that share it - const groups = new Map(); - for (const spec of Object.values(PROPS) as PropDef[]) { - if (spec.kind === 'num' && spec.event && spec.mapGet) { - const g = groups.get(spec.event) ?? []; - g.push(spec); - groups.set(spec.event, g); - } - } - for (const [event, specs] of groups) { - const handler = () => { - for (const s of specs) { - if (this.#map && s.mapGet) { - const v = s.mapGet(this.#map); - if (v !== undefined) this.#syncAttr(s.attr, `${v}`); - } - } - }; - this.#mapEventHandlers.set(event, handler); - this.#map.on(event, handler); - } - - this.addEventListener( - 'leaflet-register', - this.#handleLeafletRegister as EventListenerOrEventListenerObject, - ); + this.addEventListener('leaflet-register', this.#onRegister); this.addEventListener('leaflet-add-layer', this.#onAddLayer); this.addEventListener('leaflet-remove-layer', this.#onRemoveLayer); } - disconnectedCallback() { + disconnectedCallback(): void { this.#resizeObserver?.disconnect(); this.#resizeObserver = undefined; - this.removeEventListener( - 'leaflet-register', - this.#handleLeafletRegister as EventListenerOrEventListenerObject, - ); + this.removeEventListener('leaflet-register', this.#onRegister); this.removeEventListener('leaflet-add-layer', this.#onAddLayer); this.removeEventListener('leaflet-remove-layer', this.#onRemoveLayer); - if (!this.#map) return; - for (const [event, handler] of this.#mapEventHandlers) { - this.#map.off(event, handler); - } - this.#mapEventHandlers.clear(); - this.#map.remove(); - this.#map = undefined; - } - - attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null) { - if (oldValue === newValue || !this.#map || this.#syncing) return; - if (CSS_ATTRS.includes(name as (typeof CSS_ATTRS)[number])) { - this.#applyCss(); - return; - } - const propName = ATTR_TO_PROP.get(name); - if (!propName) return; - const spec = PROPS[propName] as PropDef; - if (spec.kind === 'num') { - if (spec.mapSet && newValue !== null) spec.mapSet(this.#map, +newValue); - } else if (spec.kind === 'bool-off') { - spec.mapSet?.(this.#map, newValue === null); - } - // skip spec.kind === 'bool-on': constructor-only, no live update + super.disconnectedCallback(); } - #applyCss() { - const sr = this.shadowRoot; - if (!sr) return; + // Replaces the in the shadow root, and derives the marker icon path + // from the same URL. Called on connect and whenever a css-* attribute + // changes. Absence and emptiness mean different things here, so this reads + // the attributes rather than the properties. + applyCss(): void { + const root = this.shadowRoot; + if (!root) return; - if (this.#cssLink) { - this.#cssLink.remove(); - this.#cssLink = undefined; - } + this.#cssLink?.remove(); + this.#cssLink = undefined; const customUrl = this.hasAttribute('css-url'); const url = customUrl ? this.getAttribute('css-url') : DEFAULT_CSS_URL; @@ -267,8 +180,7 @@ export class LeafletMap extends TypedBase { let crossorigin: string | undefined; if (this.hasAttribute('css-crossorigin')) { - const val = this.getAttribute('css-crossorigin'); - crossorigin = val ?? undefined; + crossorigin = this.getAttribute('css-crossorigin') ?? undefined; } else if (integrity) { crossorigin = 'anonymous'; } @@ -278,54 +190,42 @@ export class LeafletMap extends TypedBase { link.href = url ?? ''; if (integrity) link.setAttribute('integrity', integrity); if (crossorigin !== undefined) link.setAttribute('crossorigin', crossorigin); - sr.appendChild(link); + root.append(link); this.#cssLink = link; - // Derive marker icon path from the CSS URL (replaces leaflet.css → images/) - Icon.Default.imagePath = url?.replace(/\/[^/]+$/, '/images/'); + Icon.Default.imagePath = url?.replace(/\/[^/]+$/u, '/images/'); } - #syncAttr(name: string, value: string) { - if (this.#syncing || this.getAttribute(name) === value) return; - this.#syncing = true; - this.setAttribute(name, value); - this.#syncing = false; - } + #buildShadowRoot(): HTMLDivElement { + if (this.#container) return this.#container; - #buildOptions(): MapOptions { - const o: Record = {}; - for (const [propName, spec] of Object.entries(PROPS) as [PropName, PropDef][]) { - if (spec.kind === 'num') { - if (spec.viewState) continue; - const v = this.getAttribute(spec.attr); - if (v !== null) o[propName] = +v; - } else if (spec.kind === 'bool-off') { - if (this.hasAttribute(spec.attr)) o[propName] = false; - } else { - if (this.hasAttribute(spec.attr)) o[propName] = true; - } - } - return o; + const root = this.shadowRoot ?? this.attachShadow({ mode: 'open' }); + const container = document.createElement('div'); + container.style.width = '100%'; + container.style.height = '100%'; + root.append(container); + + const style = document.createElement('style'); + style.textContent = ':host { display: block; width: 100%; height: 400px; }'; + root.append(style); + + this.#container = container; + return container; } - #handleLeafletRegister = (e: LeafletRegisterEvent) => { + #onRegister = (e: LeafletRegisterEvent) => { e.stopPropagation(); - if (this.#map) { - e.detail.leafletObject.addTo(this.#map); - } + const map = this.leafletObject; + if (map) e.detail.leafletObject.addTo(map); }; - #onAddLayer = (e: Event) => { - this.#map?.addLayer((e as CustomEvent<{ layer: Layer }>).detail.layer); + #onAddLayer = (e: LeafletLayerEvent) => { + this.leafletObject?.addLayer(e.detail.layer); }; - #onRemoveLayer = (e: Event) => { - this.#map?.removeLayer((e as CustomEvent<{ layer: Layer }>).detail.layer); + #onRemoveLayer = (e: LeafletLayerEvent) => { + this.leafletObject?.removeLayer(e.detail.layer); }; - - get leafletObject() { - return this.#map; - } } customElements.define('leaflet-map', LeafletMap); diff --git a/src/components/leaflet-marker.ts b/src/components/leaflet-marker.ts index a468f96..a86c508 100644 --- a/src/components/leaflet-marker.ts +++ b/src/components/leaflet-marker.ts @@ -1,79 +1,54 @@ -import { Icon, Marker } from 'leaflet'; -import { WithProps, defineProps, num, str, on, buildOptions } from '../core/props.ts'; -import { LeafletIconChangedEvent, registerChildren, unregisterChildren } from '../core/register.ts'; -import { buildAttrMap, numAttr, setLayerAttr } from '../core/attributes.ts'; - -const PROPS = defineProps({ - lat: num(), - lng: num(), - title: str(), - alt: str(), - draggable: on(), +import { Icon, Marker, type MarkerOptions } from 'leaflet'; +import { bool, num, str } from '../core/props.ts'; +import { latLngProps } from '../core/shared-props.ts'; +import { WithProps } from '../core/with-props.ts'; +import type { LeafletIconChangedEvent } from '../core/register.ts'; + +const PROPS = { + ...latLngProps, + // title and alt end up on the Leaflet renders, so live updates go there. + title: str('', { + set: (obj: Marker, value) => { + const el = obj.getElement(); + if (el) el.title = value; + }, + }), + alt: str('', { + set: (obj: Marker, value) => { + const el = obj.getElement() as HTMLImageElement | undefined; + if (el) el.alt = value; + }, + }), + draggable: bool(false, { + set(obj: Marker, value) { + if (value) obj.dragging?.enable(); + else obj.dragging?.disable(); + }, + }), opacity: num(1.0), zIndexOffset: num(), -}); +} as const; -const PROP_BY_ATTR = buildAttrMap(PROPS); +export class LeafletMarker extends WithProps(PROPS) { + declare readonly leafletObject?: Marker; -export class LeafletMarker extends WithProps(HTMLElement, PROPS) { - #obj?: Marker; - #syncing = false; + createLeafletObject(options: MarkerOptions): Marker { + return new Marker([this.lat, this.lng], options); + } - connectedCallback() { - this.#obj = new Marker( - [numAttr(this, PROPS, 'lat'), numAttr(this, PROPS, 'lng')], - buildOptions(this, PROPS, ['lat', 'lng']), - ); - // eslint-disable-next-line @typescript-eslint/unbound-method - this.#obj.on('dragend move', this.#onChange, this); - registerChildren(this, this.#obj); + connectedCallback(): void { + super.connectedCallback(); this.addEventListener('icon-changed', this.#onIconChanged); } - disconnectedCallback() { - // eslint-disable-next-line @typescript-eslint/unbound-method - this.#obj?.off('dragend move', this.#onChange, this); + disconnectedCallback(): void { this.removeEventListener('icon-changed', this.#onIconChanged); - unregisterChildren(this); - this.#obj?.remove(); - this.#obj = undefined; - } - - get leafletObject() { - return this.#obj; - } - - attributeChangedCallback(name: string, _old: string | null, val: string | null) { - if (!this.#obj || this.#syncing) return; - if (name === 'lat' || name === 'lng') { - this.#obj.setLatLng([numAttr(this, PROPS, 'lat'), numAttr(this, PROPS, 'lng')]); - } else if (name === 'draggable') { - if (val !== null) this.#obj.dragging?.enable(); - else this.#obj.dragging?.disable(); - } else if (name === 'title') { - const el = this.#obj.getElement(); - if (el) (el as HTMLImageElement).title = val ?? ''; - } else if (name === 'alt') { - const el = this.#obj.getElement(); - if (el) (el as HTMLImageElement).alt = val ?? ''; - } else { - setLayerAttr(this.#obj, PROPS, PROP_BY_ATTR, name, val); - } - } - - #onChange() { - if (!this.#obj || this.#syncing) return; - this.#syncing = true; - const pos = this.#obj.getLatLng(); - this.setAttribute('lat', `${pos.lat}`); - this.setAttribute('lng', `${pos.lng}`); - this.#syncing = false; + super.disconnectedCallback(); } + // A or child announces itself this way. #onIconChanged = (e: LeafletIconChangedEvent) => { - if (!this.#obj) return; - if (e.detail.icon) this.#obj.setIcon(e.detail.icon); - else this.#obj.setIcon(new Icon.Default()); + this.leafletObject?.setIcon(e.detail.icon ?? new Icon.Default()); }; } diff --git a/src/components/leaflet-polygon.ts b/src/components/leaflet-polygon.ts index 52b2c77..03ad657 100644 --- a/src/components/leaflet-polygon.ts +++ b/src/components/leaflet-polygon.ts @@ -1,59 +1,39 @@ -import { Polygon } from 'leaflet'; -import { registerChildren, unregisterChildren } from '../core/register.ts'; -import { WithProps, defineProps, num, str, on, buildOptions } from '../core/props.ts'; -import { isPathStyleAttr, updatePathStyle } from '../core/attributes.ts'; +import { Polygon, type PolylineOptions } from 'leaflet'; +import { pathProps } from '../core/shared-props.ts'; +import { WithProps } from '../core/with-props.ts'; import type { LeafletLine } from './leaflet-line.ts'; -const PROPS = defineProps({ - color: str('#3388ff'), - weight: num(3), - opacity: num(1.0), - fill: on(), - fillColor: str('#3388ff'), - fillOpacity: num(0.2), -}); - -export class LeafletPolygon extends WithProps(HTMLElement, PROPS) { - #obj?: Polygon; +export class LeafletPolygon extends WithProps({ ...pathProps }) { + declare readonly leafletObject?: Polygon; + #observer?: MutationObserver; - connectedCallback() { - this.#obj = new Polygon(this.#getCoords(), buildOptions(this, PROPS)); - registerChildren(this, this.#obj); + createLeafletObject(options: PolylineOptions): Polygon { + return new Polygon(this.#coords(), options); + } + // Vertices come from children rather than an attribute, so we + // re-read them whenever one is added, removed or moved. + connectedCallback(): void { + super.connectedCallback(); this.addEventListener('line-updated', this.#syncCoords); - this.#observer = new MutationObserver(() => { - this.#syncCoords(); - }); + this.#observer = new MutationObserver(this.#syncCoords); this.#observer.observe(this, { childList: true }); } - disconnectedCallback() { + disconnectedCallback(): void { this.#observer?.disconnect(); this.#observer = undefined; this.removeEventListener('line-updated', this.#syncCoords); - unregisterChildren(this); - this.#obj?.remove(); - this.#obj = undefined; - } - - get leafletObject() { - return this.#obj; - } - - attributeChangedCallback(name: string, _old: string | null, val: string | null) { - if (!this.#obj) return; - if (isPathStyleAttr(name)) { - updatePathStyle(this.#obj, name, val); - } + super.disconnectedCallback(); } #syncCoords = () => { - this.#obj?.setLatLngs(this.#getCoords()); + this.leafletObject?.setLatLngs(this.#coords()); }; - #getCoords(): [number, number][] { - const lines: LeafletLine[] = Array.from(this.querySelectorAll('leaflet-line')); + #coords(): [number, number][] { + const lines = Array.from(this.querySelectorAll('leaflet-line')); return lines.map((line) => line.latlng); } } diff --git a/src/components/leaflet-polyline.ts b/src/components/leaflet-polyline.ts index 09a1fa3..ebd2834 100644 --- a/src/components/leaflet-polyline.ts +++ b/src/components/leaflet-polyline.ts @@ -1,58 +1,45 @@ -import { Polyline } from 'leaflet'; -import { WithProps, defineProps, num, str, on, buildOptions } from '../core/props.ts'; -import { registerChildren, unregisterChildren } from '../core/register.ts'; -import { isPathStyleAttr, updatePathStyle } from '../core/attributes.ts'; +import { Polyline, type PolylineOptions } from 'leaflet'; +import { bool, num } from '../core/props.ts'; +import { pathProps, style } from '../core/shared-props.ts'; +import { WithProps } from '../core/with-props.ts'; import type { LeafletLine } from './leaflet-line.ts'; -const PROPS = defineProps({ - color: str('#3388ff'), - weight: num(3), - opacity: num(1.0), - fill: on(), - fillColor: str('#3388ff'), - fillOpacity: num(0.2), -}); - -export class LeafletPolyline extends WithProps(HTMLElement, PROPS) { - #obj?: Polyline; +export class LeafletPolyline extends WithProps({ + ...pathProps, + // Unlike closed shapes, a polyline is unfilled by default. + fill: bool(false, { set: style('fill') }), + smoothFactor: num(1.0), + noClip: bool(), +}) { + declare readonly leafletObject?: Polyline; + #observer?: MutationObserver; - connectedCallback() { - this.#obj = new Polyline(this.#getCoords(), buildOptions(this, PROPS)); - registerChildren(this, this.#obj); + createLeafletObject(options: PolylineOptions): Polyline { + return new Polyline(this.#coords(), options); + } + // Vertices come from children rather than an attribute, so we + // re-read them whenever one is added, removed or moved. + connectedCallback(): void { + super.connectedCallback(); this.addEventListener('line-updated', this.#syncCoords); - this.#observer = new MutationObserver(() => { - this.#syncCoords(); - }); + this.#observer = new MutationObserver(this.#syncCoords); this.#observer.observe(this, { childList: true }); } - disconnectedCallback() { + disconnectedCallback(): void { this.#observer?.disconnect(); this.#observer = undefined; this.removeEventListener('line-updated', this.#syncCoords); - unregisterChildren(this); - this.#obj?.remove(); - this.#obj = undefined; - } - - get leafletObject() { - return this.#obj; - } - - attributeChangedCallback(name: string, _old: string | null, val: string | null) { - if (!this.#obj) return; - if (isPathStyleAttr(name)) { - updatePathStyle(this.#obj, name, val); - } + super.disconnectedCallback(); } #syncCoords = () => { - this.#obj?.setLatLngs(this.#getCoords()); + this.leafletObject?.setLatLngs(this.#coords()); }; - #getCoords(): [number, number][] { + #coords(): [number, number][] { const lines = Array.from(this.querySelectorAll('leaflet-line')); return lines.map((line) => line.latlng); } diff --git a/src/components/leaflet-popup.ts b/src/components/leaflet-popup.ts index c676d06..3525e82 100644 --- a/src/components/leaflet-popup.ts +++ b/src/components/leaflet-popup.ts @@ -1,74 +1,46 @@ -import { Popup } from 'leaflet'; -import { WithProps, defineProps, num, on, buildOptions } from '../core/props.ts'; -import { numAttr } from '../core/attributes.ts'; -import { registerWithParent } from '../core/register.ts'; +import { Popup, type PopupOptions } from 'leaflet'; +import { bool, num } from '../core/props.ts'; +import { latLngProps } from '../core/shared-props.ts'; +import { WithProps } from '../core/with-props.ts'; + +export class LeafletPopup extends WithProps( + { + ...latLngProps, + maxWidth: num(300), + minWidth: num(50), + maxHeight: num(), + autoPan: bool(true), + closeButton: bool(true), + autoClose: bool(true), + }, + { attach: 'self' }, +) { + declare readonly leafletObject?: Popup; -const PROPS = defineProps({ - lat: num(), - lng: num(), - maxWidth: num(300), - minWidth: num(50), - maxHeight: num(), - autoPan: on(), - closeButton: on(), - autoClose: on(), -}); - -export class LeafletPopup extends WithProps(HTMLElement, PROPS) { - #obj?: Popup; #observer?: MutationObserver; - #syncing = false; - connectedCallback() { - this.#obj = new Popup({ - ...buildOptions(this, PROPS, ['lat', 'lng']), - content: this.innerHTML, - }); + // Content is the element's markup, not an attribute. A popup only carries a + // position of its own when it isn't bound to a parent layer. + createLeafletObject(options: PopupOptions): Popup { + const popup = new Popup({ ...options, content: this.innerHTML }); if (this.hasAttribute('lat') && this.hasAttribute('lng')) { - this.#obj.setLatLng([numAttr(this, PROPS, 'lat'), numAttr(this, PROPS, 'lng')]); + popup.setLatLng([this.lat, this.lng]); } - // eslint-disable-next-line @typescript-eslint/unbound-method - this.#obj.on('move', this.#onChange, this); - registerWithParent(this, this.#obj); + return popup; + } + connectedCallback(): void { + super.connectedCallback(); this.#observer = new MutationObserver(() => { - this.#obj?.setContent(this.innerHTML); - }); - this.#observer.observe(this, { - childList: true, - characterData: true, - subtree: true, + this.leafletObject?.setContent(this.innerHTML); }); + this.#observer.observe(this, { childList: true, characterData: true, subtree: true }); } - disconnectedCallback() { - // eslint-disable-next-line @typescript-eslint/unbound-method - this.#obj?.off('move', this.#onChange, this); + disconnectedCallback(): void { this.#observer?.disconnect(); this.#observer = undefined; - this.#obj?.remove(); - this.#obj = undefined; - } - - get leafletObject() { - return this.#obj; - } - - attributeChangedCallback(name: string) { - if (!this.#obj || this.#syncing) return; - if (name === 'lat' || name === 'lng') { - this.#obj.setLatLng([numAttr(this, PROPS, 'lat'), numAttr(this, PROPS, 'lng')]); - } - } - - #onChange() { - if (!this.#obj || this.#syncing) return; - const pos = this.#obj.getLatLng(); - if (!pos) return; - this.#syncing = true; - this.setAttribute('lat', `${pos.lat}`); - this.setAttribute('lng', `${pos.lng}`); - this.#syncing = false; + super.disconnectedCallback(); } } diff --git a/src/components/leaflet-rectangle.ts b/src/components/leaflet-rectangle.ts index dd02bd4..f1d7e37 100644 --- a/src/components/leaflet-rectangle.ts +++ b/src/components/leaflet-rectangle.ts @@ -1,43 +1,16 @@ -import { Rectangle } from 'leaflet'; -import { WithProps, defineProps, num, str, on, buildOptions } from '../core/props.ts'; -import { registerChildren, unregisterChildren } from '../core/register.ts'; -import { isPathStyleAttr, parseBoundsAttr, updatePathStyle } from '../core/attributes.ts'; +import { Rectangle, type LatLngBoundsExpression, type PolylineOptions } from 'leaflet'; +import { json, positional } from '../core/props.ts'; +import { pathProps, getBounds } from '../core/shared-props.ts'; +import { WithProps } from '../core/with-props.ts'; -const PROPS = defineProps({ - bounds: str(), - color: str('#3388ff'), - weight: num(3), - opacity: num(1.0), - fill: on(), - fillColor: str('#3388ff'), - fillOpacity: num(0.2), -}); +export class LeafletRectangle extends WithProps({ + bounds: positional(json([], { get: getBounds })), + ...pathProps, +}) { + declare readonly leafletObject?: Rectangle; -export class LeafletRectangle extends WithProps(HTMLElement, PROPS) { - #obj?: Rectangle; - - connectedCallback() { - this.#obj = new Rectangle(parseBoundsAttr(this), buildOptions(this, PROPS, ['bounds'])); - registerChildren(this, this.#obj); - } - - disconnectedCallback() { - unregisterChildren(this); - this.#obj?.remove(); - this.#obj = undefined; - } - - get leafletObject() { - return this.#obj; - } - - attributeChangedCallback(name: string, _old: string | null, val: string | null) { - if (!this.#obj) return; - if (name === 'bounds') { - this.#obj.setBounds(parseBoundsAttr(this)); - } else if (isPathStyleAttr(name)) { - updatePathStyle(this.#obj, name, val); - } + createLeafletObject(options: PolylineOptions): Rectangle { + return new Rectangle(this.bounds, options); } } diff --git a/src/components/leaflet-svg-overlay.ts b/src/components/leaflet-svg-overlay.ts index d50b8b3..ece04f6 100644 --- a/src/components/leaflet-svg-overlay.ts +++ b/src/components/leaflet-svg-overlay.ts @@ -1,52 +1,27 @@ -import { SVGOverlay, LatLngBounds, LatLngExpression } from 'leaflet'; -import type { ImageOverlayOptions } from 'leaflet'; -import { WithProps, defineProps, num, str, on, buildOptions } from '../core/props.ts'; -import { registerChildren, unregisterChildren } from '../core/register.ts'; -import { buildAttrMap, parseBoundsAttr, setLayerAttr } from '../core/attributes.ts'; - -const PROPS = defineProps({ - bounds: str(), +import { + SVGOverlay, + type CrossOrigin, + type ImageOverlayOptions, + type LatLngBoundsExpression, +} from 'leaflet'; +import { bool, choice, json, num, positional, str } from '../core/props.ts'; +import { getBounds } from '../core/shared-props.ts'; +import { WithProps } from '../core/with-props.ts'; + +export class LeafletSVGOverlay extends WithProps({ + bounds: positional(json([], { get: getBounds })), opacity: num(1.0), - interactive: on(), - crossOrigin: str(), + interactive: bool(), + crossOrigin: choice(''), zIndex: num(), className: str(), -}); - -const PROP_BY_ATTR = buildAttrMap(PROPS); - -export class LeafletSVGOverlay extends WithProps(HTMLElement, PROPS) { - #obj?: SVGOverlay; - - connectedCallback() { - const svg = this.querySelector('svg'); - const dummy = !svg ? document.createElementNS('http://www.w3.org/2000/svg', 'svg') : undefined; - this.#obj = new SVGOverlay( - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - svg ?? dummy!, - parseBoundsAttr(this), - buildOptions(this, PROPS, ['bounds']) as ImageOverlayOptions, - ); - registerChildren(this, this.#obj); - } - - disconnectedCallback() { - unregisterChildren(this); - this.#obj?.remove(); - this.#obj = undefined; - } - - get leafletObject() { - return this.#obj; - } +}) { + declare readonly leafletObject?: SVGOverlay; - attributeChangedCallback(name: string, _old: string | null, val: string | null) { - if (!this.#obj) return; - if (name === 'bounds') { - this.#obj.setBounds(new LatLngBounds(parseBoundsAttr(this) as LatLngExpression[])); - } else { - setLayerAttr(this.#obj, PROPS, PROP_BY_ATTR, name, val); - } + createLeafletObject(options: ImageOverlayOptions): SVGOverlay { + const svg = + this.querySelector('svg') ?? document.createElementNS('http://www.w3.org/2000/svg', 'svg'); + return new SVGOverlay(svg, this.bounds, options); } } diff --git a/src/components/leaflet-tile-layer-wms.ts b/src/components/leaflet-tile-layer-wms.ts index d413bc6..2cc016c 100644 --- a/src/components/leaflet-tile-layer-wms.ts +++ b/src/components/leaflet-tile-layer-wms.ts @@ -1,54 +1,51 @@ -import { TileLayer } from 'leaflet'; -import { WithProps, defineProps, str, on, buildOptions } from '../core/props.ts'; -import { registerChildren, unregisterChildren } from '../core/register.ts'; -import { buildAttrMap, parseAttributeValue, setLayerAttr } from '../core/attributes.ts'; - -const PROPS = defineProps({ - url: str(), - layers: str(), - styles: str(), - format: str('image/jpeg'), - transparent: on(), - version: str('1.1.1'), - uppercase: on(), -}); - -const PROP_BY_ATTR = buildAttrMap(PROPS); - -export class LeafletTileLayerWMS extends WithProps(HTMLElement, PROPS) { - #obj?: TileLayer.WMS; - - connectedCallback() { - const url = this.getAttribute('url') ?? ''; - this.#obj = new TileLayer.WMS(url, buildOptions(this, PROPS, ['url'])); - registerChildren(this, this.#obj); - } - - disconnectedCallback() { - unregisterChildren(this); - this.#obj?.remove(); - this.#obj = undefined; - } - - get leafletObject() { - return this.#obj; - } +import { CRS, TileLayer, type WMSOptions, type WMSParams } from 'leaflet'; +import { bool, str, type PropDef } from '../core/props.ts'; +import { tileLayerProps, urlProp } from '../core/shared-props.ts'; +import { WithProps } from '../core/with-props.ts'; + +// WMS request parameters have no individual setters -- they're merged into the +// query string through setParams(). +function param(key: keyof WMSParams): (obj: TileLayer.WMS, value: T) => void { + return (obj, value) => { + obj.setParams({ [key]: value } as unknown as WMSParams); + }; +} - attributeChangedCallback(name: string, _old: string | null, val: string | null) { - if (!this.#obj) return; - if (name === 'url') { - if (val) this.#obj.setUrl(val); - } else { - const propName = PROP_BY_ATTR.get(name); - if (!propName) return; - const spec = PROPS[propName]; - if (!setLayerAttr(this.#obj, PROPS, PROP_BY_ATTR, name, val)) { - const value = spec.kind === 'bool-on' ? val !== null : parseAttributeValue(val); - (this.#obj.setParams as unknown as (params: Record) => void)({ - [propName]: value, - }); - } - } +// `crs` takes a CRS instance, not a primitive, so it's looked up by name from +// Leaflet's built-in set rather than decoded like a plain value. Constructor- +// only, like the rest of tileLayerProps -- Leaflet has no setter for it. +const NAMED_CRS = { + EPSG3857: CRS.EPSG3857, + EPSG4326: CRS.EPSG4326, + EPSG3395: CRS.EPSG3395, + Simple: CRS.Simple, +}; +type CrsName = keyof typeof NAMED_CRS; + +const crsProp: PropDef = { + default: CRS.EPSG3857, + decode: (raw) => NAMED_CRS[raw as CrsName] ?? CRS.EPSG3857, + encode: (value) => + value === CRS.EPSG3857 + ? null + : ((Object.keys(NAMED_CRS) as CrsName[]).find((name) => NAMED_CRS[name] === value) ?? null), +}; + +export class LeafletTileLayerWMS extends WithProps({ + url: urlProp, + ...tileLayerProps, + layers: str('', { set: param('layers') }), + styles: str('', { set: param('styles') }), + format: str('image/jpeg', { set: param('format') }), + transparent: bool(false, { set: param('transparent') }), + version: str('1.1.1', { set: param('version') }), + uppercase: bool(), + crs: crsProp, +}) { + declare readonly leafletObject?: TileLayer.WMS; + + createLeafletObject(options: WMSOptions): TileLayer.WMS { + return new TileLayer.WMS(this.url, options); } } diff --git a/src/components/leaflet-tile-layer.ts b/src/components/leaflet-tile-layer.ts index e726659..08632e7 100644 --- a/src/components/leaflet-tile-layer.ts +++ b/src/components/leaflet-tile-layer.ts @@ -1,45 +1,15 @@ -import { TileLayer } from 'leaflet'; -import { registerChildren, unregisterChildren } from '../core/register.ts'; -import { WithProps, defineProps, num, str, buildOptions } from '../core/props.ts'; -import { buildAttrMap, setLayerAttr } from '../core/attributes.ts'; - -const PROPS = defineProps({ - url: str(), - attribution: str(), - minZoom: num(), - maxZoom: num(18), - opacity: num(1.0), - zIndex: num(), -}); - -const PROP_BY_ATTR = buildAttrMap(PROPS); - -export class LeafletTileLayer extends WithProps(HTMLElement, PROPS) { - #obj?: TileLayer; - - connectedCallback() { - const url = this.getAttribute('url') ?? ''; - this.#obj = new TileLayer(url, buildOptions(this, PROPS, ['url'])); - registerChildren(this, this.#obj); - } - - disconnectedCallback() { - unregisterChildren(this); - this.#obj?.remove(); - this.#obj = undefined; - } - - get leafletObject() { - return this.#obj; - } - - attributeChangedCallback(name: string, _old: string | null, val: string | null) { - if (!this.#obj) return; - if (name === 'url') { - if (val) this.#obj.setUrl(val); - } else { - setLayerAttr(this.#obj, PROPS, PROP_BY_ATTR, name, val); - } +import { TileLayer, type TileLayerOptions } from 'leaflet'; +import { tileLayerProps, urlProp } from '../core/shared-props.ts'; +import { WithProps } from '../core/with-props.ts'; + +export class LeafletTileLayer extends WithProps({ + url: urlProp, + ...tileLayerProps, +}) { + declare readonly leafletObject?: TileLayer; + + createLeafletObject(options: TileLayerOptions): TileLayer { + return new TileLayer(this.url, options); } } diff --git a/src/components/leaflet-tooltip.ts b/src/components/leaflet-tooltip.ts index 83a66fd..da39c46 100644 --- a/src/components/leaflet-tooltip.ts +++ b/src/components/leaflet-tooltip.ts @@ -1,75 +1,46 @@ -import { Tooltip } from 'leaflet'; -import type { TooltipOptions } from 'leaflet'; -import { WithProps, defineProps, num, str, on, buildOptions } from '../core/props.ts'; -import { numAttr } from '../core/attributes.ts'; -import { registerWithParent } from '../core/register.ts'; +import { Tooltip, type Direction, type PointExpression, type TooltipOptions } from 'leaflet'; +import { bool, choice, json, num, str } from '../core/props.ts'; +import { latLngProps } from '../core/shared-props.ts'; +import { WithProps } from '../core/with-props.ts'; + +export class LeafletTooltip extends WithProps( + { + ...latLngProps, + pane: str(), + offset: json([0, 0]), + direction: choice('auto'), + permanent: bool(), + sticky: bool(), + opacity: num(1.0), + }, + { attach: 'self' }, +) { + declare readonly leafletObject?: Tooltip; -const PROPS = defineProps({ - lat: num(), - lng: num(), - pane: str(), - offset: str(), - direction: str('auto'), - permanent: on(), - sticky: on(), - opacity: num(1.0), -}); - -export class LeafletTooltip extends WithProps(HTMLElement, PROPS) { - #obj?: Tooltip; #observer?: MutationObserver; - #syncing = false; - connectedCallback() { - this.#obj = new Tooltip({ - ...buildOptions(this, PROPS, ['lat', 'lng']), - content: this.innerHTML, - } as unknown as TooltipOptions); + // Content is the element's markup, not an attribute. A tooltip only carries a + // position of its own when it isn't bound to a parent layer. + createLeafletObject(options: TooltipOptions): Tooltip { + const tooltip = new Tooltip({ ...options, content: this.innerHTML }); if (this.hasAttribute('lat') && this.hasAttribute('lng')) { - this.#obj.setLatLng([numAttr(this, PROPS, 'lat'), numAttr(this, PROPS, 'lng')]); + tooltip.setLatLng([this.lat, this.lng]); } - // eslint-disable-next-line @typescript-eslint/unbound-method - this.#obj.on('move', this.#onChange, this); - registerWithParent(this, this.#obj); + return tooltip; + } + connectedCallback(): void { + super.connectedCallback(); this.#observer = new MutationObserver(() => { - this.#obj?.setContent(this.innerHTML); - }); - this.#observer.observe(this, { - childList: true, - characterData: true, - subtree: true, + this.leafletObject?.setContent(this.innerHTML); }); + this.#observer.observe(this, { childList: true, characterData: true, subtree: true }); } - disconnectedCallback() { - // eslint-disable-next-line @typescript-eslint/unbound-method - this.#obj?.off('move', this.#onChange, this); + disconnectedCallback(): void { this.#observer?.disconnect(); this.#observer = undefined; - this.#obj?.remove(); - this.#obj = undefined; - } - - get leafletObject() { - return this.#obj; - } - - attributeChangedCallback(name: string) { - if (!this.#obj || this.#syncing) return; - if (name === 'lat' || name === 'lng') { - this.#obj.setLatLng([numAttr(this, PROPS, 'lat'), numAttr(this, PROPS, 'lng')]); - } - } - - #onChange() { - if (!this.#obj || this.#syncing) return; - const pos = this.#obj.getLatLng(); - if (!pos) return; - this.#syncing = true; - this.setAttribute('lat', `${pos.lat}`); - this.setAttribute('lng', `${pos.lng}`); - this.#syncing = false; + super.disconnectedCallback(); } } diff --git a/src/components/leaflet-video-overlay.ts b/src/components/leaflet-video-overlay.ts index e6fc1d3..91d9e17 100644 --- a/src/components/leaflet-video-overlay.ts +++ b/src/components/leaflet-video-overlay.ts @@ -1,73 +1,48 @@ -import { VideoOverlay, LatLngBounds, LatLngExpression } from 'leaflet'; -import type { VideoOverlayOptions } from 'leaflet'; -import { WithProps, defineProps, num, str, on, buildOptions } from '../core/props.ts'; -import { registerChildren, unregisterChildren } from '../core/register.ts'; -import { buildAttrMap, parseBoundsAttr, setLayerAttr } from '../core/attributes.ts'; +import { + VideoOverlay, + type CrossOrigin, + type LatLngBoundsExpression, + type VideoOverlayOptions, +} from 'leaflet'; +import { bool, choice, json, num, positional, str } from '../core/props.ts'; +import { getBounds, urlProp } from '../core/shared-props.ts'; +import { WithProps } from '../core/with-props.ts'; + +// Playback options live on the