diff --git a/.dockerignore b/.dockerignore index 078aaa7d..a968be31 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,38 +1,7 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -# dependencies -/node_modules -/.pnp -.pnp.js - -# testing -/coverage - -# next.js -/.next/ -/out/ - -# production -/build - -# misc/sensitive -.DS_Store -.env -.env.local -secrets - -# debug -npm-debug.log* -yarn-debug.log* -yarn-error.log* -pnpm-debug.log* - -# typescript -*.tsbuildinfo - -# editor config -.vscode/ - -# generated types -src/generated/ -cache/ +node_modules +dist +dist-cache +.astro +.astro-incremental +.image-cache +.git diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..3537cec4 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,11 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 4 + +[*.{yml,yaml}] +indent_size = 2 diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index 9ef18a4b..00000000 --- a/.eslintrc.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "extends": [ - "next/core-web-vitals", - "airbnb", - "airbnb-typescript" - ], - "parser": "@typescript-eslint/parser", - "parserOptions": { - "project": "./tsconfig.json" - }, - "ignorePatterns": [ - "./server/", - "src/generated/**", - "**/*.generated.ts", - "**/*.gen.ts" - ], - "rules": { - "strict": 1, - - "@typescript-eslint/indent": ["error", 4, { "SwitchCase": 1 }], - "react/destructuring-assignment": "off", - "react/jsx-indent": [2, 4], - "react/jsx-indent-props": [2, 4], - "react/jsx-filename-extension": ["error", { "extensions": [".js", ".ts", ".jsx", ".tsx"] }], - "react/react-in-jsx-scope": "off", - "react/require-default-props": "off" - } -} diff --git a/.gitignore b/.gitignore index 8d60ed8b..49529fbd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,19 +1,27 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -# dependencies -/node_modules -/.pnp -.pnp.js - -# testing -/coverage - -# next.js -/.next/ -/out/ - -# production -/build +# deps +node_modules/ + +# all build artifacts: +# build/out — the site (astro outDir; merged tree on incremental runs) +# build/out-cache — incremental merge staging (transient) +# build/incremental — incremental state (code-hash, manifest, shards) +# build/image-cache — remote-image webp encodes +# build/full-data — site data from `pnpm fetch-data` (also the incremental-fetch cache) +# In CI, restore/save build/out + build/incremental as ONE cache entry (the +# manifest describes that exact tree) and build/image-cache as another. +build/ +.astro/ + +# generated optimized images (emitted into build/out at build time) +public/_img/ + +# scratch build logs / measurements +build*.log +measure_*.txt + +# core dumps (a crashed dev-server node once dumped 9.7 GiB of these into +# two commits — root-anchored so a hypothetical src core.* file stays safe) +/core.[0-9]* # misc .DS_Store @@ -22,9 +30,6 @@ # debug npm-debug.log* -yarn-debug.log* -yarn-error.log* -pnpm-debug.log* # typescript *.tsbuildinfo @@ -32,9 +37,9 @@ pnpm-debug.log* # editor config .vscode/ -# generated types -generated -fullData/ +# playwright +test-results/ +playwright-report/ -cache/ -server/ +# generated from backend/schema.graphql via `pnpm generate:type` +generated/ diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 00000000..2bd5a0a9 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +22 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c3ac9346..6ff033b9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,26 +1,21 @@ repos: - repo: local hooks: - - id: check-unused - name: check-unused - language: system - pass_filenames: false - entry: yarn check-unused - - id: lint - name: lint + name: eslint + entry: pnpm --silent lint language: system + types_or: [ts, tsx, javascript] pass_filenames: false - entry: yarn lint - - id: css-lint - name: CSS Lint + name: stylelint + entry: pnpm --silent css-lint language: system + types: [css] pass_filenames: false - entry: yarn css-lint - - - id: type-check - name: Typecheck + - id: typecheck + name: astro check + entry: pnpm --silent typecheck language: system + types_or: [ts, tsx, astro] pass_filenames: false - entry: yarn typecheck diff --git a/.stylelintrc.json b/.stylelintrc.json index 7d7a6560..4503a259 100644 --- a/.stylelintrc.json +++ b/.stylelintrc.json @@ -1,11 +1,13 @@ { - "plugins": [], + "plugins": [ + "@stylistic/stylelint-plugin" + ], "extends": [ "stylelint-config-recommended", - "stylelint-config-concentric" + "stylelint-config-concentric-order" ], "rules": { - "indentation": 4, + "@stylistic/indentation": 4, "selector-pseudo-class-no-unknown": [ true, { diff --git a/.unimportedrc.json b/.unimportedrc.json deleted file mode 100644 index 20a02fd4..00000000 --- a/.unimportedrc.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "entry": ["./src/pages/**/*.{jsx,tsx,js,ts}"], - "extensions": [".ts", ".js", ".tsx", ".jsx"], - "ignorePatterns": ["**/node_modules/**", "out/**", ".next/"], - "ignoreUnresolved": [], - "ignoreUnimported": ["**/*.d.ts"], - "ignoreUnused": [], - "rootDir": ".", - "typescript": true, - "aliases": { - "components/*": ["src/components/*"], - "fullData/*": ["fullData/*"], - "utils/*": ["src/utils/*"], - "hooks/*": ["src/hooks/*"], - "styles/*": ["src/styles/*"], - "generated/*": ["src/generated/*"] - } -} diff --git a/.yarn/install-state.gz b/.yarn/install-state.gz deleted file mode 100644 index 4550ad87..00000000 Binary files a/.yarn/install-state.gz and /dev/null differ diff --git a/.yarnrc.yml b/.yarnrc.yml deleted file mode 100644 index 3186f3f0..00000000 --- a/.yarnrc.yml +++ /dev/null @@ -1 +0,0 @@ -nodeLinker: node-modules diff --git a/Dockerfile b/Dockerfile index 3c17aa50..bd83081a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,11 +1,7 @@ -FROM node:18-buster-slim +# Dev-only image; the app is mounted by docker-compose and built there. +FROM node:22-bookworm-slim -RUN apt-get update -y \ - && apt-get install -y --no-install-recommends \ - git bash g++ make \ - # Clean-up - && rm -rf /var/lib/apt/lists/* \ - # Add /code as safe directory - && git config --global --add safe.directory /code +RUN apt-get update && apt-get install -y --no-install-recommends git \ + && rm -rf /var/lib/apt/lists/* WORKDIR /code diff --git a/README.md b/README.md index c5b194d1..bce0d4a5 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Mapswipe Website -NextJs application for [Mapswipe community website](https://mapswipe.org). +Astro application for [Mapswipe community website](https://mapswipe.org). ## Development @@ -21,51 +21,53 @@ Set these environment variables: ```env APP_ENVIRONMENT=PROD # Use DEV if you are running your own server instance MAPSWIPE_API_ENDPOINT=https://backend.mapswipe.org/ -NEXT_PUBLIC_POSTHOG_KEY= -NEXT_PUBLIC_POSTHOG_HOST_API= +MAPSWIPE_REFERER_ENDPOINT=https://mapswipe.org/ ``` ### Running ```bash # Install dependencies -yarn install +pnpm install # Generate typescript types from graphql schema -yarn generate:type +pnpm generate:type # Fetch latest data from MapSwipe database for projects -yarn fetch-data:local +pnpm fetch-data:local # Run -yarn dev +pnpm dev ``` > [!NOTE] -> Currently the platform runs smoothly in node 16, so developers might have to switch to node 16 for development. +> Requires node >= 22.18 (TypeScript files run under node's type stripping). -Whenever new texts for translation are added, translation files need to be generated. - -```bash -yarn generate:i18n -``` +Translation keys live directly in [locales/en](locales/en); add new keys +there (and their uses via `t('key')`) — no generation step. Before creating a pull request, all lint and type issues must be fixed. To check for issues: ```bash -yarn lint -yarn css-lint -yarn typecheck -yarn unimported +pnpm lint +pnpm css-lint +pnpm typecheck +pnpm check-unused +pnpm test:e2e # needs a built build/out/ (see Building) ``` ### Building ```bash -yarn build +pnpm build # full static build -> build/out/ +pnpm verify-dist # post-build invariant checks ``` +For data-only refreshes there is also `pnpm build:incremental`, which +re-renders just the pages whose data changed and merges the rest from the +previous `build/out/` (see packages/astro-incremental-static). + ### Staging Deployment The **staging environment** is used to test new changes before they are deployed to production. @@ -108,7 +110,7 @@ latest data from MapSwipe database. ### Edit Source Strings - Pull the latest changes from the `main` branch - Checkout to a new branch -- Navigate to the source string files [here](https://github.com/mapswipe/website/tree/main/public/locales/en) +- Navigate to the source string files [here](https://github.com/mapswipe/website/tree/main/locales/en) - Open appropriate file(s) and edit string(s) as per requirement - Push the changes to the local branch - Create a pull request to the main branch @@ -134,26 +136,14 @@ latest data from MapSwipe database. ## Supported Languages -Languages listed in [i18next-parser.config.js](https://github.com/mapswipe/community-website/blob/main/i18next-parser.config.js) -are listed as options to view the website in that particular language. - -To add a new language option, user should add [ISO_639-1](https://en.wikipedia.org/wiki/ISO_639-1) -code of that language to the list. - -```js -module.exports = { - locales: ['en', 'ne'] // NOTE: add ISO code in this list, -}; -``` - -Language's title and abbreviation in the selected language, needs to be added -in [languages.ts](https://github.com/mapswipe/community-website/blob/main/src/utils/languages.ts). - -After the language settings are added, user should generate the language files. - -```bash -yarn generate:i18n -``` +Supported languages are defined in two places that must agree: +[astro.config.mjs](astro.config.mjs) (`i18n.locales` — drives routing) and +[src/i18n/index.ts](src/i18n/index.ts) (`LOCALES` — drives translation +loading). To add a language, add its +[ISO_639-1](https://en.wikipedia.org/wiki/ISO_639-1) code to both, add the +language name to the switcher in +[src/components/Navbar.astro](src/components/Navbar.astro), and add its +translation files under `locales//`. ## Adding 'News & Updates' or Blogs @@ -175,7 +165,7 @@ title: This is a blog publishedDate: 2022-08-17 author: John Doe description: Lorem Ipsum -coverImage: /img/blogImages/example-image.png +coverImage: ./images/example-image.png featured: true --- @@ -200,7 +190,28 @@ Aldus PageMaker including versions of Lorem Ipsum. #### Rules - `publishedDate` should be in `YYYY-MM-DD` format. Any other format is not supported. -- Project images `coverImage` should be uploaded in the [/img/blogImages](https://github.com/mapswipe/community-website/tree/main/public/img/blogImages) -folder. +- Post images (`coverImage` and in-body) should be added to the +[blogs/images](https://github.com/mapswipe/community-website/tree/main/blogs/images) +folder and referenced relatively (`./images/`), so they are optimized +at build time. - The value for `featured` determines whether to highlight the posts on News & Updates section of home page + +## Build Internals + +| Cache / state (gitignored) | What | Cold cost if lost | +| --- | --- | --- | +| `build/image-cache/` | optimized remote project covers + negative cache | ~70 s refetch/re-encode | +| `build/full-data/staticData.json` | site data AND the incremental-fetch cache | ~50 s full refetch | +| `build/incremental/` | manifest for `pnpm build:incremental` (cache with `build/out/`) | one full render | + +| Env knob | Effect | +| --- | --- | +| `SLICE_LIMIT=N` | build only N projects (fast smoke builds) | +| `FORCE_FULL_FETCH=1` | fetch-data: ignore the incremental cache | +| `MAPSWIPE_DATA_FILE=/path` | build against an alternate data file | +| `PREFETCH_CONCURRENCY=N` | image prefetch pool size (default 24) | +| `BAD_IMAGE_TEST=1` | inject a broken cover URL (tests the error-image path) | + +A failed remote cover can never break the build: it renders +`/_img/image-error.svg` and warns (`[remote-images] ...`). diff --git a/astro.config.mjs b/astro.config.mjs new file mode 100644 index 00000000..643c5ccf --- /dev/null +++ b/astro.config.mjs @@ -0,0 +1,132 @@ +import { createHash } from 'node:crypto'; +import { writeFileSync } from 'node:fs'; + +import { defineConfig } from 'astro/config'; +import react from '@astrojs/react'; +import sitemap from '@astrojs/sitemap'; +import remarkGfm from 'remark-gfm'; +// Deduplicates the byte-identical inline island-hydration runtimes (~3.9 KB on +// each of ~14.6k pages, ~56 MB total) into external cached, content-hashed +// /_astro/dedup-.js files at build:done. Registered LAST so it +// post-processes the final HTML after the other build:done hooks. +import hydrationDedup from './packages/astro-hydration-dedup/src/index.ts'; + +const SITE = 'https://mapswipe.org'; + +const CLASS_ALPHA = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; +const CLASS_ALNUM = `${CLASS_ALPHA}0123456789-_`; +const assignedClassNames = new Map(); +// Debug aid: CLASS_MAP_OUT= dumps {shortName -> source} after the build, +// so two builds' HTML can be compared structurally when class hashes shift +// (e.g. after moving a CSS module). +if (process.env.CLASS_MAP_OUT) { + process.on('exit', () => { + try { + writeFileSync(process.env.CLASS_MAP_OUT, JSON.stringify(Object.fromEntries(assignedClassNames))); + } catch { /* best-effort */ } + }); +} +function shortClassName(name, filename) { + const rel = filename.includes('website-astro') || filename.startsWith(process.cwd()) + ? filename.slice(process.cwd().length) + : filename; + const digest = createHash('sha1').update(`${rel}:${name}`).digest(); + let out = CLASS_ALPHA[digest[0] % CLASS_ALPHA.length]; + for (let i = 1; i < 4; i += 1) { + out += CLASS_ALNUM[digest[i] % CLASS_ALNUM.length]; + } + const source = `${rel}:${name}`; + const existing = assignedClassNames.get(out); + if (existing && existing !== source) { + throw new Error(`css module class-name collision: '${out}' (${existing} vs ${source}) — widen the hash`); + } + assignedClassNames.set(out, source); + return out; +} + +export default defineConfig({ + // All build artifacts live under build/ (out + incremental state + + // image cache) so CI caches one tree. + outDir: './build/out', + site: SITE, + vite: { + css: { + modules: { + // dev keeps readable names; prod ships 4-char names (class + // attributes were ~22% of every page with the default + // pattern). Content-hashed so names are stable across builds + // (the incremental merge relies on that); first char forced + // alphabetic (leading digits get CSS-escaped, which grows + // output); collisions fail the build instead of silently + // merging two components' styles. + generateScopedName: process.env.NODE_ENV === 'production' + ? shortClassName + : '[local]_[hash:base64:4]', + }, + }, + }, + output: 'static', + trailingSlash: 'always', + build: { + format: 'directory', + // Pages rendered concurrently. Rendering is single-threaded JS — + // concurrency only overlaps the async waits — so gains saturate + // fast: measured 55s/48s/45s at 1/4/8 (peak RSS 1.8/2.0/2.4 GiB), + // byte-identical output. 4 is the balance; override per-run via env. + concurrency: Number(process.env.ASTRO_BUILD_CONCURRENCY ?? 4), + // 'auto' inlines small stylesheets into EVERY page — ~10 KB of + // identical CSS duplicated across 14.6k pages (~150 MB of dist). + // External files are fetched once and cached. + inlineStylesheets: 'never', + }, + // Root/locale-less redirect stubs (parity with the Next app's src/pages/*.tsx + // Redirect stubs). Next did client-side language detection; for a static + // export we redirect the locale-less path to the default-locale (en) variant. + // Emitted as meta-refresh HTML pages in dist. `/` is handled by + // src/pages/index.astro. + redirects: { + '/blogs': '/en/blogs/', + '/data': '/en/data/', + '/get-involved': '/en/get-involved/', + '/privacy': '/en/privacy/', + '/projects': '/en/projects/', + }, + integrations: [ + react(), + // Emit a sitemap with per-locale alternate links (hreflang). The i18n option + // groups the 6 locale-prefixed variants of each page and cross-links them, + // matching the hreflang alternates we emit in . + sitemap({ + i18n: { + defaultLocale: 'en', + locales: { en: 'en', ne: 'ne', hu: 'hu', de: 'de', cs: 'cs', pt: 'pt' }, + }, + }), + // Default minOccurrences (10): the 3 sitewide runtimes are externalized; + // the data pages' 6-page client:load loader stays inline by design. + hydrationDedup({ minOccurrences: 10 }), + ], + // Blog markdown is rendered by Astro's built-in pipeline. Next used remark-gfm + // (remark().use(remarkGfm) in its Pages-Router detail page), so we enable it + // here for GFM parity (tables, strikethrough, autolinks, etc.). + // In-body relative images (./images/*) are optimized natively by astro:assets. + markdown: { + remarkPlugins: [remarkGfm], + }, + i18n: { + defaultLocale: 'en', + locales: ['en', 'ne', 'hu', 'de', 'cs', 'pt'], + // redirectToDefaultLocale off: src/pages/index.astro owns the root + // redirect (remembered-locale aware); astro's auto-stub would shadow it. + routing: { prefixDefaultLocale: true, redirectToDefaultLocale: false }, + }, + // NOTE: Remote project cover images are optimized by our build-time, + // fail-soft resolver (the astro-remote-images workspace package, configured + // in src/lib/remoteImages.ts + wrapped by components/RemoteImage.astro), + // NOT by Astro's astro:assets pipeline — its optimize phase is all-or-nothing + // and one malformed remote image aborts the whole build. LOCAL blog images + // (blogs/images/*, a small trusted set) DO go through astro:assets (default + // sharp service): covers via the image() schema helper + , in-body + // relative markdown images natively. We don't declare remotePatterns, so + // astro:assets never touches a remote URL. +}); diff --git a/blogs/2016-07-19-mapswipe-app.md b/blogs/2016-07-19-mapswipe-app.md index d15eb95f..f0fdcfbf 100644 --- a/blogs/2016-07-19-mapswipe-app.md +++ b/blogs/2016-07-19-mapswipe-app.md @@ -3,7 +3,7 @@ title: MapSwipe app lets public help deliver medical care to most vulnerable publishedDate: 2016-07-19 author: Médecins Sans Frontières (MSF) description: Médecins Sans Frontières (MSF) unveils a first-of-its-kind mobile phone app in which people across the globe can locate communities in remote parts of the world affected by natural disasters, disease outbreaks or conflict. -coverImage: /img/blogImages/2016-07-19-MapSwipe-user.jpg +coverImage: "./images/2016-07-19-MapSwipe-user.jpg" featured: false --- diff --git a/blogs/2022-02-15-Mali-solid-waste.md b/blogs/2022-02-15-Mali-solid-waste.md index b05c2848..01f6821c 100644 --- a/blogs/2022-02-15-Mali-solid-waste.md +++ b/blogs/2022-02-15-Mali-solid-waste.md @@ -3,7 +3,7 @@ title: Solid waste management in Mali - Sustaining livelihoods amidst COVID-19 s publishedDate: 2022-02-15 author: Jessica Bergmann description: To support economic livelihoods and resilience, the World Bank partnered with MapSwipe to identify solid waste in Bamako, Mali. -coverImage: /img/blogImages/2022-02-15-Mali-solid-waste.jpg +coverImage: "./images/2022-02-15-Mali-solid-waste.jpg" featured: false --- diff --git a/blogs/2022-08-15-Madagascar-change-detection.md b/blogs/2022-08-15-Madagascar-change-detection.md index c4fa5a97..8357878d 100644 --- a/blogs/2022-08-15-Madagascar-change-detection.md +++ b/blogs/2022-08-15-Madagascar-change-detection.md @@ -3,7 +3,7 @@ title: Change detection analysis in response to Cyclone Batsirai Madagascar publishedDate: 2022-08-15 author: Laurie Boobier description: To respond to the cyclone emergency in Madagascar, Médecins Sans Frontières (MSF) used a new MapSwipe method developed by the Heidelberg Institute of Geoinformation Technology (HeiGIT) and Laurent Savaete, a long-time MapSwipe software engineer. -coverImage: /img/blogImages/2022-08-15-Madagascar-change-detection.jpg +coverImage: "./images/2022-08-15-Madagascar-change-detection.jpg" featured: false --- diff --git a/blogs/2023-01-31-tallying-community-contributions.md b/blogs/2023-01-31-tallying-community-contributions.md index a303e335..34e77be6 100644 --- a/blogs/2023-01-31-tallying-community-contributions.md +++ b/blogs/2023-01-31-tallying-community-contributions.md @@ -3,7 +3,7 @@ title: Tallying the contributions of the MapSwipe community publishedDate: 2023-01-31 author: Dan Joseph and Kat Kinzer description: We are excited that the American Red Cross has supported the development and launch of new functionalities for contribution analytics including the concept of groups which allow users to aggregate tallies of their efforts. -coverImage: /img/blogImages/2023-01-31-tallying-community-contributions.jpg +coverImage: "./images/2023-01-31-tallying-community-contributions.jpg" featured: false --- diff --git a/blogs/2023-08-22-investing-in-mapswipe.md b/blogs/2023-08-22-investing-in-mapswipe.md index 4977576e..2b4c4831 100644 --- a/blogs/2023-08-22-investing-in-mapswipe.md +++ b/blogs/2023-08-22-investing-in-mapswipe.md @@ -3,7 +3,7 @@ title: "Investing in MapSwipe: Humanitarian technology, virtual volunteering, an publishedDate: 2023-08-22 author: Kat Kinzer description: To bolster and expand the impact of MapSwipe, the American Red Cross has supported upgrades to the app. -coverImage: /img/blogImages/2023-08-22-cover.jpg +coverImage: "./images/2023-08-22-cover.jpg" featured: false --- @@ -29,13 +29,13 @@ The MapSwipe app is open source, which means that its code is made public under Glad you asked! While artificial intelligence and machine learning hold great promise for accelerating mapping efforts, we have not yet reached the point where human insight and oversight is no longer needed. There is still a significant need for human judgment and discretion when validating map data, assessing quality, and ensuring responsible data practices. As AI and ML progress, our goal is to augment human abilities rather than replace them entirely. -![generic icon to represent artificial intelligence](/img/blogImages/2023-08-22-noun-5960267.png) +![generic icon to represent artificial intelligence](./images/2023-08-22-noun-5960267.png) ## MapSwipe Data Outputs MapSwipe provides volunteers with satellite imagery of specific areas of interest from around the world. Depending on a project's objectives, volunteers are asked to identify and locate various features on the ground, detect changes in environments or structures, or validate the accuracy of existing mapped data. Each completed project creates data that can be used for humanitarian mapping or research purposes. -![screen grab of the data page on the MapSwipe website](/img/blogImages/2023-08-22-data-page.png) +![screen grab of the data page on the MapSwipe website](./images/2023-08-22-data-page.png) All MapSwipe data outputs are open and available. They can be searched and downloaded from MapSwipe’s [data page](https://mapswipe.org/en/data/). @@ -49,19 +49,19 @@ To bolster and expand that impact, the American Red Cross has supported upgrades As a community-driven platform that crowdsources results, MapSwipe relies on the dedication of its volunteers. MapSwipe volunteers and partners can now easily track their contribution stats, as well as see how their efforts fit into the bigger picture on the new [Community page](https://community.mapswipe.org/) launched earlier this year. Details such as the number of tasks completed, number of organizations supported, and other impact metrics are refreshed daily. In addition, a new functionality allows groups of volunteers to aggregate their collective contributions to app projects. -![screen grab of the community page on the MapSwipe website](/img/blogImages/2023-08-22-community-page.jpg) +![screen grab of the community page on the MapSwipe website](./images/2023-08-22-community-page.jpg) ### MapSwipe website refresh The [MapSwipe website](https://mapswipe.org/en/) has been given a modern, visually-appealing makeover. The new design makes it easier for new volunteers to learn about the app and improved navigation and intuitive layouts help visitors find information relevant to them whether they want to get involved at an individual or community level, partner as an organization with MapSwipe, or access all app data outputs. -![screen grab of the landing page on the MapSwipe website](/img/blogImages/2023-08-22-website.png) +![screen grab of the landing page on the MapSwipe website](./images/2023-08-22-website.png) ### Enhanced customization of projects Behind the scenes, project creators can add more information into project tutorials, better supporting volunteers in their learning journey. Additionally, the Validate project type now has configurable answer options and sub-options, allowing more detailed quality assessment of mapped areas. These increases in flexibility expand the types of mapping MapSwipe supports. -![screen grabs from app showing validate project type with new sub options](/img/blogImages/2023-08-22-validate.png) +![screen grabs from app showing validate project type with new sub options](./images/2023-08-22-validate.png) ## How you can get involved! diff --git a/blogs/2023-10-19-nigeria-building-data.md b/blogs/2023-10-19-nigeria-building-data.md index 411ae922..bf280b4e 100644 --- a/blogs/2023-10-19-nigeria-building-data.md +++ b/blogs/2023-10-19-nigeria-building-data.md @@ -3,7 +3,7 @@ title: Using MapSwipe to validate OSM building quality data in Nigeria publishedDate: 2023-10-19 author: Paul Knight description: Using the MapSwipe data, the Nigerian Red Cross are now able to see the accuracy of the previous mapping and any edits that need to be made to the community map as it is today. -coverImage: /img/blogImages/2023-10-19-cover.jpg +coverImage: "./images/2023-10-19-cover.jpg" featured: false --- @@ -17,7 +17,7 @@ We have previously written ([1](https://medium.com/digital-and-innovation-at-bri With new states and communities selected this year, the Nigerian Red Cross is leading on similar activities conducted previously. One of which is through Digital Community Mapping for the Enhanced Vulnerability Capacity Assessment (EVCA). More information about what this is can be found in a [past blog](https://medium.com/digital-and-innovation-at-british-red-cross/first-time-this-community-has-been-on-a-map-nigeria-f592906b7be1). -![A woman standing outside and wearing a red British Red Cross ball cap and branded t-shirt looks at her phone.](/img/blogImages/2023-10-19-volunteer.jpg) +![A woman standing outside and wearing a red British Red Cross ball cap and branded t-shirt looks at her phone.](./images/2023-10-19-volunteer.jpg) _Digital Community Mapping — Carter Olayemi/Nigerian Red Cross_ Previously, the Nigerian Red Cross followed the Missing Maps methodology as presented on the [Missing Maps website](https://www.missingmaps.org/). First, digital volunteers, trace aerial imagery to create a basic digital map on OpenStreetMap. Next, using various data collection tools, community members with local knowledge add detail to the map, such as medical facilities or schools, and rank their capacity or vulnerability, with any public data added to OpenStreetMap. Lastly, the map data is used in products for local disaster risk management planning, for advocacy with stakeholders, and as reference maps during emergency responses. @@ -30,7 +30,7 @@ In one of the new states that the programme is expanding to, we saw that there w But how? We used MapSwipe — a mobile app, which allows users to **Find** and flag features such as building or roads in imagery, which then speeds up the mapping process as remote mappers map areas which are known to have a features in them. In the past year the project types have diversified and now include **Compare** imagery and **Validate** quality of mapping. For the Nigeria Programme where there was existing map data, we opted to use the Validate project type. -![Graphic showing different MapSwipe project types: find, compare, and validate.](/img/blogImages/2023-10-19-project-types.jpg) +![Graphic showing different MapSwipe project types: find, compare, and validate.](./images/2023-10-19-project-types.jpg) _Different MapSwipe projects — MapSwipe_ For communities without any previous mapping Nigerian Red Cross conducted a mapathon, as in the previous phase of the programme. @@ -40,7 +40,7 @@ In September 2023, the Nigerian Red Cross held a mapathon and mapswipe-athon, in - **5,733** buildings were validated using the Validate project type on Mapswipe. - **1,766** Buildings were mapped at the mapathon using the [HOT Tasking Manager](https://tasks.hotosm.org/). -![The screens of a tutorial for the MapSwipe validate project type.](/img/blogImages/2023-10-19-tutorial.jpg) +![The screens of a tutorial for the MapSwipe validate project type.](./images/2023-10-19-tutorial.jpg) _Tutorial for a MapSwipe Validate project type — MapSwipe_ Using the MapSwipe data, the Nigerian Red Cross are now able to see the accuracy of the previous mapping and any edits that need to be made to the community map as it is today. diff --git a/blogs/2023-11-14-early-history-of-mapswipe.md b/blogs/2023-11-14-early-history-of-mapswipe.md index 597ad44b..0b57fd7f 100644 --- a/blogs/2023-11-14-early-history-of-mapswipe.md +++ b/blogs/2023-11-14-early-history-of-mapswipe.md @@ -3,7 +3,7 @@ title: "The early history of MapSwipe" publishedDate: 2023-11-14 author: Ivan Gayton, Pete Masters, and Dan Joseph description: "The real story of MapSwipe is the volunteers who give their time to improve humanitarian mapping, the agencies that use the data to save and improve lives, and the incredible developers who continue to build and maintain MapSwipe and make it a key tool in the open mapping ecosystem. However, we hope people sometimes remember how we originally built MapSwipe." -coverImage: /img/blogImages/2023-11-14-cover.jpeg +coverImage: "./images/2023-11-14-cover.jpeg" featured: true --- diff --git a/blogs/2024-08-05-accessibility-improvements.md b/blogs/2024-08-05-accessibility-improvements.md index 0d90286d..f768df02 100644 --- a/blogs/2024-08-05-accessibility-improvements.md +++ b/blogs/2024-08-05-accessibility-improvements.md @@ -3,7 +3,7 @@ title: "Enhancing Accessibility in MapSwipe" publishedDate: 2024-08-05 author: Jenny Chan description: "The American Red Cross is excited to announce the launch of new functionality in the MapSwipe app. We supported an enhancement to make MapSwipe more accessible and easy to use for a wider community of users." -coverImage: /img/blogImages/2024-08-05-cover.png +coverImage: "./images/2024-08-05-cover.png" featured: false --- @@ -15,7 +15,7 @@ The MapSwipe 'Find' project type has users swipe through satellite images to sea In the current version of the MapSwipe app, the only indication of the number of taps is through transparent color overlays. However, feedback provided by users noted that this format posed challenges for users with color blindness or when color filters, such as night mode, are turned on, making it difficult to swipe and contribute to projects effectively. -![A screen grab from the MapSwipe app showing the 'Find' type project with only a color overlay on tiles. It is hard to distinguish between the different overlays on different tiles.](/img/blogImages/2024-08-05-color-overlays.jpg) +![A screen grab from the MapSwipe app showing the 'Find' type project with only a color overlay on tiles. It is hard to distinguish between the different overlays on different tiles.](./images/2024-08-05-color-overlays.jpg) _The 'Find' type project with only a color overlay on tiles. Top right: green, middle right: yellow, middle left: red._ @@ -31,7 +31,7 @@ Our recent MapSwipe [update v2.2.10](https://github.com/mapswipe/mapswipe/releas - A question mark icon for areas that need further review. - A bad imagery icon for areas that cannot be assessed due to poor image quality. -![A screen grab from the MapSwipe app showing the updated 'Find' type project with both a color overlay and descriptive icons.](/img/blogImages/2024-08-05-with-icons.png) +![A screen grab from the MapSwipe app showing the updated 'Find' type project with both a color overlay and descriptive icons.](./images/2024-08-05-with-icons.png) _The updated 'Find' type project with both color overlay and descriptive icons. Top right: check mark, top left: question mark, and the rest of the tiles: bad imagery._ diff --git a/blogs/2025-01-24-mapswipe-belgium-meetup.md b/blogs/2025-01-24-mapswipe-belgium-meetup.md index 18d3d0cc..c84fbdfc 100644 --- a/blogs/2025-01-24-mapswipe-belgium-meetup.md +++ b/blogs/2025-01-24-mapswipe-belgium-meetup.md @@ -3,7 +3,7 @@ title: MapSwipe Belgium Meetup publishedDate: 2025-02-20 author: Amar Shahi description: This article summarizes the key highlights of Mapswipe Belgium meetup 2024. -coverImage: /img/blogImages/2025-01-24-mapswipe-belgium-meetup-1.jpg +coverImage: "./images/2025-01-24-mapswipe-belgium-meetup-1.jpg" featured: true --- @@ -13,7 +13,7 @@ In mid-November 2024, the MapSwipe community gathered in Brussels for a four-day ## Day 1: Reflecting on Technology and Challenges The meetup kicked off with a deep dive into MapSwipe’s history and memories. From its beginnings in 2015 as a crowdsourcing tool to its current role in global humanitarian mapping, The group then revisited the platform’s history, reflecting on milestones such as its 2015 prototype, the 2016 app launch, and the evolution of the app, website refresh and now [MapSwipe for Web](https://web.mapswipe.org/#/en). -![Meetup Image One](/img/blogImages/2025-01-24-mapswipe-belgium-meetup-1.jpg) +![Meetup Image One](./images/2025-01-24-mapswipe-belgium-meetup-1.jpg) After reflecting on Mapswipe’s history from a prototype in 2015 to the mobile and web apps it is today, the latter half of the day was dedicated to examining the technical aspects of MapSwipe. The group reflected on the platform’s evolution from a web-based prototype in 2015 to the current app and web versions. The group also discussed various technical challenges that currently exist. Key technical topics included: * **System Complexity:** Participants discussed how the platform’s growing features have increased complexity, creating challenges in onboarding developers and maintaining the ecosystem. @@ -40,7 +40,7 @@ The third day of the meetup was dedicated to MapSwipe’s sustainability—fundi The day began with Amar presenting a recap of the financial overview of MapSwipe for the year 2024. The presentation also focused on the detailed contributions made by each organization and a check on MapSwipe financial health. There was also a discussion on any potential funding that the participants are willing to make in 2025. Check the detailed presentation [here](https://docs.google.com/presentation/d/1lWwv82gemDybxFjAF_vUJ4H58tpI1Vwh/edit#slide=id.g3145f004ab7_0_172). -![Meetup Image Two](/img/blogImages/2025-01-24-mapswipe-belgium-meetup-2.jpg) +![Meetup Image Two](./images/2025-01-24-mapswipe-belgium-meetup-2.jpg) The second half of Day 3 focused on a brainstorming session on funding ideas. The participants were divided into 3 groups: @@ -81,12 +81,12 @@ The roadmap was divided into 2 sections: Through rigorous discussion and voting, the participants decided on the following [roadmap](https://app.mural.co/t/katkinzer9715/m/katkinzer9715/1731585514072/0423daffb9fc8a287f3591a4db711e8c90474c8f): -![Meetup Image Three](/img/blogImages/2025-01-24-mapswipe-belgium-meetup-3.jpg) -![Meetup Image Four](/img/blogImages/2025-01-24-mapswipe-belgium-meetup-4.jpg) +![Meetup Image Three](./images/2025-01-24-mapswipe-belgium-meetup-3.jpg) +![Meetup Image Four](./images/2025-01-24-mapswipe-belgium-meetup-4.jpg) ## In Conclusion As we look to the future, the insights and plans generated during this workshop will help shape MapSwipe’s trajectory. Whether you’re a mapper, an organization, or a supporter, there’s a place for you in this journey. Together, we can continue to map what matters and make every swipe count. -![Meetup Image Five](/img/blogImages/2025-01-24-mapswipe-belgium-meetup-5.jpg) +![Meetup Image Five](./images/2025-01-24-mapswipe-belgium-meetup-5.jpg) #### Important Links from the MapSwipe Meetup 2024 Here are all the key resources and links shared during the meetup: diff --git a/blogs/2025-04-03-papua-new-guinea-swiping-to-find-airstrips.md b/blogs/2025-04-03-papua-new-guinea-swiping-to-find-airstrips.md index a0d3713f..9c132d82 100644 --- a/blogs/2025-04-03-papua-new-guinea-swiping-to-find-airstrips.md +++ b/blogs/2025-04-03-papua-new-guinea-swiping-to-find-airstrips.md @@ -3,7 +3,7 @@ title: Swiping to find airstrips in Papua New Guinea publishedDate: 2025-04-03 author: Jana Bauerová, based on Nashath Naufal’s work description: What would you do in the middle of the jungle in Papua New Guinea if you needed medical care? Volunteers have identified airstrips Médecins Sans Frontières can use to get to hard-to-reach communities. -coverImage: /img/blogImages/2025-04-03-cover.jpg +coverImage: "./images/2025-04-03-cover.jpg" featured: true --- @@ -11,7 +11,7 @@ What would you do in the middle of the jungle in Papua New Guinea if you had an Many settlements on the islands of Papua New Guinea are in areas so remote that there is no road and limited to no connections between communities. As a matter of fact, the only access to health may be via aid organizations using rural airstrips. Around eight million Papua New Guineans rely on rural airstrips (2024), according to the Mission Aviation Fellowship (MAF). However, owing to the challenges of economic sustainability in rural regions, many of those airstrips have become defunct. -![An aerial view of a grass airstrip in a forested area.](/img/blogImages/2025-04-03-papua-airstrips-1.jpg) +![An aerial view of a grass airstrip in a forested area.](./images/2025-04-03-papua-airstrips-1.jpg) [Médecins Sans Frontières (MSF)](https://www.msf.org/), a humanitarian medical organization and a founding member of the [Missing Maps ](https://www.missingmaps.org/)project, uses small airstrips to deliver supplies and personnel to remote regions. For MSF, it is thus essential to know whether there are existing airstrips and where they are located. In a MapSwipe project, MSF asked volunteers to identify rural airstrips in the Jiwaka province, where aid can be delivered to and from. @@ -31,7 +31,7 @@ The contributors seemed to be confused with other features, misidentifying fores The study compared the findings from the Mapswipe dataset against four different data sources: OpenStreetMap (OSM), [MAF in Papua New Guinea database, OurAirports](https://data.humdata.org/dataset/papua-new-guinea-airports?), and an internal MSF database. From analysing these datasets, it could be concluded that there are a total of 11 unique airstrips in Jiwaka Province. OSM was the most comprehensive dataset and contained 10 out of 11 airstrips. -![A map showing the locations of airstrips from different databases.](/img/blogImages/2025-04-03-papua-airstrips-2.png) +![A map showing the locations of airstrips from different databases.](./images/2025-04-03-papua-airstrips-2.png) Adapting the validation size to increase the number of contributors seeing each tile to five helped to improve accuracy. In the second project iteration, the contributors managed to detect all 11 airstrips within Jiwaka province, including four airstrips that were missed in the first project. The results of Project 2 are accessible on the MapSwipe website here: [Find - Airstrips in Jiwaka Province (2) | MapSwipe ](https://mapswipe.org/en/projects/-O-k29apT1Q8V8IAMrkS/) @@ -45,17 +45,17 @@ In both projects, MSF faced limitations due to satellite imagery: 1. The satellite imagery available for the projects consisted of a mosaic of different images, each taken on its own date. Retrieving the exact dates of every piece of imagery is not easy, and they do not match like perfect puzzle pieces. 2. Because of the humid climate in Papua New Guinea, a lot of areas are covered by clouds. Some regions, such as Tabibuga, are rarely cloud-free. Hence, cloud-free satellite imagery was not available for use in the MapSwipe projects, which made remote analysis of the airstrips difficult. The ‘latest’ imagery in some regions dated back to more than 12 years ago. -![Three satellite imagery tiles.](/img/blogImages/2025-04-03-papua-airstrips-3.png) +![Three satellite imagery tiles.](./images/2025-04-03-papua-airstrips-3.png) Most Mapswipe projects focus on identifying or validating buildings. This type of map feature was likely new to most MapSwipe contributors. Even though the tutorial for the projects integrated different examples to distinguish various classes of land use from airstrips, the MapSwipe contributors still identified forested land or river features as airstrips. The confusion could have arisen from roads, tracks, or clearings standing out from the forest background. More examples of forested land with roads that are distinctly different colours would possibly help in the tutorial. Even though an example of a river was included in the tutorial, it still was detected by MapSwipe contributors as an airstrip, so a solitary previous example does not necessarily ensure a correct visual interpretation. -![Several rural buildings.](/img/blogImages/2025-04-03-papua-airstrips-4.jpg) +![Several rural buildings.](./images/2025-04-03-papua-airstrips-4.jpg) ## Why mapping matters for health People in the Jiwaka province suffer from many types of violence, including election-related, inter-community, domestic, sexual and gender-based violence, and repercussions due to accusations of sorcery. In Spring 2024, MSF wanted to start a new project in the province to aid survivors of tribal and sexual and gender-based violence. As part of an initial exploration, the field team noted that there were no maps for some of the areas. This led to a GIS Adviser asking for a Missing Maps campaign. Soon, it was launched to help map basic features, enlisting volunteers. For example, in February 2024, volunteers from the Czech and Slovak Missing Maps community mapped shelters at a hybrid mapathon in Prague with support from international validators. -![Six people in a small room are engrossed in their work on laptops. Visible on the screens are the OSM iD editor.](/img/blogImages/2025-04-03-papua-airstrips-5.jpg) +![Six people in a small room are engrossed in their work on laptops. Visible on the screens are the OSM iD editor.](./images/2025-04-03-papua-airstrips-5.jpg) In June 2024, the MSF teams began working on a community-based approach to healthcare by strengthening the capacity of existing medical services, and this Mapswipe analysis was launched. The location of all airstrips is vital to ensuring that survivors of violence in the Jiwaka Province can have access to health. diff --git a/blogs/2026-01-19-mapswipe-2025-reflections.md b/blogs/2026-01-19-mapswipe-2025-reflections.md index 69aa6b05..1863e5d9 100644 --- a/blogs/2026-01-19-mapswipe-2025-reflections.md +++ b/blogs/2026-01-19-mapswipe-2025-reflections.md @@ -3,7 +3,7 @@ title: "Reflecting on MapSwipe in 2025: A Year of Growth and What Comes Next" publishedDate: 2026-01-19 author: Nicole Siggins description: MapSwipe had many successes in 2025 from new project types, to structural revamps. Nicole and Amar, from the MapSwipe Governance Team, get together to celebrate and discuss what's next. -coverImage: /img/blogImages/2026-01-19-mapswipe-2025-reflections-video-thumbnail.png +coverImage: "./images/2026-01-19-mapswipe-2025-reflections-video-thumbnail.png" featured: true --- @@ -37,11 +37,10 @@ Overall, the focus of the backend revamp was to make sure that the project manag ## *“The focus of the backend revamp was to make sure that the project manager experience is enhanced.”*
- An image showing what the MapSwipe project management page looks like in both the old and new backend. -
MapSwipe now has a new and improved backend!
+ +![An image showing what the MapSwipe project management page looks like in both the old and new backend.](./images/2026-01-19-mapswipe-2025-reflections-backend-revamp.png) + +
MapSwipe now has a new and improved backend!
**Nicole** @@ -64,11 +63,10 @@ It's nice that people who are into the technical side of MapSwipe have a place, ## *“We needed a dedicated space where technical contributors could come together and share knowledge.”*
- A group of MapSwipe technical contributors together on a monthly video call. -
MapSwipe technical contributors now have a consistent place to meet to share knowledge and ask questions. + +![A group of MapSwipe technical contributors together on a monthly video call.](./images/2026-01-19-mapswipe-2025-reflections-monthly-tech-call.png) + +
MapSwipe technical contributors now have a consistent place to meet to share knowledge and ask questions.
@@ -87,12 +85,10 @@ Yeah, absolutely. So this was a year for MapSwipe where quite a few different pr This is a project type where volunteers using MapSwipe are able to look at images from street level to then indicate some type of aspect of the features in that image. So for example, volunteers could look at images and indicate something like, is the road paved or not paved? Or is there something visible in this image? So for example, solid waste. Can you see piles of garbage? Things like that. You can also use it to indicate what type of features are in the image. So for example, is this building a hospital or a school? And it can also be used for things like, what is the surface quality of the road? So it's a really interesting project to be able to remotely view places of the world as if you were there on the street.
- A gif of the View Streets project. Zooming around at street level imagery to see if there are piles of trash on the street in the image. -
Validating the world as if you were there. + +![A gif of the View Streets project. Zooming around at street level imagery to see if there are piles of trash on the street in the image.](./images/2026-01-19-mapswipe-2025-reflections-view-streets-project.gif) + +
Validating the world as if you were there.
@@ -113,11 +109,10 @@ Historically with MapSwipe, a lot of the project types have been looking FOR som ## *“It’s very easy to query for something that IS, but finding what isn’t has always been more difficult.”*
- An image of the Completeness MapSwipe project type, showing a blue overlay to indicate where OpenStreetMap data currently exists on top of aerial imagery. -
The Completeness project type helps get OpenStreetMap data gaps filled. + +![An image of the Completeness MapSwipe project type, showing a blue overlay to indicate where OpenStreetMap data currently exists on top of aerial imagery.](./images/2026-01-19-mapswipe-2025-reflections-completeness-project.png) + +
The Completeness project type helps get OpenStreetMap data gaps filled.
@@ -143,11 +138,10 @@ One of the main things is it's important for us to manage the knowledge of the s ## *“A one year maintenance contract was too short. Two years gives us enough time to manage and pass on knowledge.”*
- An image of the MapSwipe 2025 roadmap, in a tool called mural, which uses differently colored virtual post it notes to track brainstorming ideas. -
Progress was made on MapSwipe’s goals by upping the maintenance contract from 1 to 2 years. + +![An image of the MapSwipe 2025 roadmap, in a tool called mural, which uses differently colored virtual post it notes to track brainstorming ideas.](./images/2026-01-19-mapswipe-2025-reflections-roadmap.png) + +
Progress was made on MapSwipe’s goals by upping the maintenance contract from 1 to 2 years.
diff --git a/public/img/blogImages/2016-07-19-MapSwipe-user.jpg b/blogs/images/2016-07-19-MapSwipe-user.jpg similarity index 100% rename from public/img/blogImages/2016-07-19-MapSwipe-user.jpg rename to blogs/images/2016-07-19-MapSwipe-user.jpg diff --git a/public/img/blogImages/2016-07-19-thanks-for-Maiduguri.jpg b/blogs/images/2016-07-19-thanks-for-Maiduguri.jpg similarity index 100% rename from public/img/blogImages/2016-07-19-thanks-for-Maiduguri.jpg rename to blogs/images/2016-07-19-thanks-for-Maiduguri.jpg diff --git a/public/img/blogImages/2022-02-15-Mali-solid-waste.jpg b/blogs/images/2022-02-15-Mali-solid-waste.jpg similarity index 100% rename from public/img/blogImages/2022-02-15-Mali-solid-waste.jpg rename to blogs/images/2022-02-15-Mali-solid-waste.jpg diff --git a/public/img/blogImages/2022-08-15-Madagascar-change-detection.jpg b/blogs/images/2022-08-15-Madagascar-change-detection.jpg similarity index 100% rename from public/img/blogImages/2022-08-15-Madagascar-change-detection.jpg rename to blogs/images/2022-08-15-Madagascar-change-detection.jpg diff --git a/public/img/blogImages/2023-01-31-tallying-community-contributions.jpg b/blogs/images/2023-01-31-tallying-community-contributions.jpg similarity index 100% rename from public/img/blogImages/2023-01-31-tallying-community-contributions.jpg rename to blogs/images/2023-01-31-tallying-community-contributions.jpg diff --git a/public/img/blogImages/2023-08-22-community-page.jpg b/blogs/images/2023-08-22-community-page.jpg similarity index 100% rename from public/img/blogImages/2023-08-22-community-page.jpg rename to blogs/images/2023-08-22-community-page.jpg diff --git a/public/img/blogImages/2023-08-22-cover.jpg b/blogs/images/2023-08-22-cover.jpg similarity index 100% rename from public/img/blogImages/2023-08-22-cover.jpg rename to blogs/images/2023-08-22-cover.jpg diff --git a/public/img/blogImages/2023-08-22-data-page.png b/blogs/images/2023-08-22-data-page.png similarity index 100% rename from public/img/blogImages/2023-08-22-data-page.png rename to blogs/images/2023-08-22-data-page.png diff --git a/public/img/blogImages/2023-08-22-noun-5960267.png b/blogs/images/2023-08-22-noun-5960267.png similarity index 100% rename from public/img/blogImages/2023-08-22-noun-5960267.png rename to blogs/images/2023-08-22-noun-5960267.png diff --git a/public/img/blogImages/2023-08-22-validate.png b/blogs/images/2023-08-22-validate.png similarity index 100% rename from public/img/blogImages/2023-08-22-validate.png rename to blogs/images/2023-08-22-validate.png diff --git a/public/img/blogImages/2023-08-22-website.png b/blogs/images/2023-08-22-website.png similarity index 100% rename from public/img/blogImages/2023-08-22-website.png rename to blogs/images/2023-08-22-website.png diff --git a/public/img/blogImages/2023-10-19-cover.jpg b/blogs/images/2023-10-19-cover.jpg similarity index 100% rename from public/img/blogImages/2023-10-19-cover.jpg rename to blogs/images/2023-10-19-cover.jpg diff --git a/public/img/blogImages/2023-10-19-project-types.jpg b/blogs/images/2023-10-19-project-types.jpg similarity index 100% rename from public/img/blogImages/2023-10-19-project-types.jpg rename to blogs/images/2023-10-19-project-types.jpg diff --git a/public/img/blogImages/2023-10-19-tutorial.jpg b/blogs/images/2023-10-19-tutorial.jpg similarity index 100% rename from public/img/blogImages/2023-10-19-tutorial.jpg rename to blogs/images/2023-10-19-tutorial.jpg diff --git a/public/img/blogImages/2023-10-19-volunteer.jpg b/blogs/images/2023-10-19-volunteer.jpg similarity index 100% rename from public/img/blogImages/2023-10-19-volunteer.jpg rename to blogs/images/2023-10-19-volunteer.jpg diff --git a/public/img/blogImages/2023-11-14-cover.jpeg b/blogs/images/2023-11-14-cover.jpeg similarity index 100% rename from public/img/blogImages/2023-11-14-cover.jpeg rename to blogs/images/2023-11-14-cover.jpeg diff --git a/public/img/blogImages/2024-08-05-color-overlays.jpg b/blogs/images/2024-08-05-color-overlays.jpg similarity index 100% rename from public/img/blogImages/2024-08-05-color-overlays.jpg rename to blogs/images/2024-08-05-color-overlays.jpg diff --git a/public/img/blogImages/2024-08-05-cover.png b/blogs/images/2024-08-05-cover.png similarity index 100% rename from public/img/blogImages/2024-08-05-cover.png rename to blogs/images/2024-08-05-cover.png diff --git a/public/img/blogImages/2024-08-05-with-icons.png b/blogs/images/2024-08-05-with-icons.png similarity index 100% rename from public/img/blogImages/2024-08-05-with-icons.png rename to blogs/images/2024-08-05-with-icons.png diff --git a/public/img/blogImages/2025-01-24-mapswipe-belgium-meetup-1.jpg b/blogs/images/2025-01-24-mapswipe-belgium-meetup-1.jpg similarity index 100% rename from public/img/blogImages/2025-01-24-mapswipe-belgium-meetup-1.jpg rename to blogs/images/2025-01-24-mapswipe-belgium-meetup-1.jpg diff --git a/public/img/blogImages/2025-01-24-mapswipe-belgium-meetup-2.jpg b/blogs/images/2025-01-24-mapswipe-belgium-meetup-2.jpg similarity index 100% rename from public/img/blogImages/2025-01-24-mapswipe-belgium-meetup-2.jpg rename to blogs/images/2025-01-24-mapswipe-belgium-meetup-2.jpg diff --git a/public/img/blogImages/2025-01-24-mapswipe-belgium-meetup-3.jpg b/blogs/images/2025-01-24-mapswipe-belgium-meetup-3.jpg similarity index 100% rename from public/img/blogImages/2025-01-24-mapswipe-belgium-meetup-3.jpg rename to blogs/images/2025-01-24-mapswipe-belgium-meetup-3.jpg diff --git a/public/img/blogImages/2025-01-24-mapswipe-belgium-meetup-4.jpg b/blogs/images/2025-01-24-mapswipe-belgium-meetup-4.jpg similarity index 100% rename from public/img/blogImages/2025-01-24-mapswipe-belgium-meetup-4.jpg rename to blogs/images/2025-01-24-mapswipe-belgium-meetup-4.jpg diff --git a/public/img/blogImages/2025-01-24-mapswipe-belgium-meetup-5.jpg b/blogs/images/2025-01-24-mapswipe-belgium-meetup-5.jpg similarity index 100% rename from public/img/blogImages/2025-01-24-mapswipe-belgium-meetup-5.jpg rename to blogs/images/2025-01-24-mapswipe-belgium-meetup-5.jpg diff --git a/public/img/blogImages/2025-04-03-cover.jpg b/blogs/images/2025-04-03-cover.jpg similarity index 100% rename from public/img/blogImages/2025-04-03-cover.jpg rename to blogs/images/2025-04-03-cover.jpg diff --git a/public/img/blogImages/2025-04-03-papua-airstrips-1.jpg b/blogs/images/2025-04-03-papua-airstrips-1.jpg similarity index 100% rename from public/img/blogImages/2025-04-03-papua-airstrips-1.jpg rename to blogs/images/2025-04-03-papua-airstrips-1.jpg diff --git a/public/img/blogImages/2025-04-03-papua-airstrips-2.png b/blogs/images/2025-04-03-papua-airstrips-2.png similarity index 100% rename from public/img/blogImages/2025-04-03-papua-airstrips-2.png rename to blogs/images/2025-04-03-papua-airstrips-2.png diff --git a/public/img/blogImages/2025-04-03-papua-airstrips-3.png b/blogs/images/2025-04-03-papua-airstrips-3.png similarity index 100% rename from public/img/blogImages/2025-04-03-papua-airstrips-3.png rename to blogs/images/2025-04-03-papua-airstrips-3.png diff --git a/public/img/blogImages/2025-04-03-papua-airstrips-4.jpg b/blogs/images/2025-04-03-papua-airstrips-4.jpg similarity index 100% rename from public/img/blogImages/2025-04-03-papua-airstrips-4.jpg rename to blogs/images/2025-04-03-papua-airstrips-4.jpg diff --git a/public/img/blogImages/2025-04-03-papua-airstrips-5.jpg b/blogs/images/2025-04-03-papua-airstrips-5.jpg similarity index 100% rename from public/img/blogImages/2025-04-03-papua-airstrips-5.jpg rename to blogs/images/2025-04-03-papua-airstrips-5.jpg diff --git a/public/img/blogImages/2026-01-19-mapswipe-2025-reflections-backend-revamp.png b/blogs/images/2026-01-19-mapswipe-2025-reflections-backend-revamp.png similarity index 100% rename from public/img/blogImages/2026-01-19-mapswipe-2025-reflections-backend-revamp.png rename to blogs/images/2026-01-19-mapswipe-2025-reflections-backend-revamp.png diff --git a/public/img/blogImages/2026-01-19-mapswipe-2025-reflections-completeness-project.png b/blogs/images/2026-01-19-mapswipe-2025-reflections-completeness-project.png similarity index 100% rename from public/img/blogImages/2026-01-19-mapswipe-2025-reflections-completeness-project.png rename to blogs/images/2026-01-19-mapswipe-2025-reflections-completeness-project.png diff --git a/public/img/blogImages/2026-01-19-mapswipe-2025-reflections-monthly-tech-call.png b/blogs/images/2026-01-19-mapswipe-2025-reflections-monthly-tech-call.png similarity index 100% rename from public/img/blogImages/2026-01-19-mapswipe-2025-reflections-monthly-tech-call.png rename to blogs/images/2026-01-19-mapswipe-2025-reflections-monthly-tech-call.png diff --git a/public/img/blogImages/2026-01-19-mapswipe-2025-reflections-roadmap.png b/blogs/images/2026-01-19-mapswipe-2025-reflections-roadmap.png similarity index 100% rename from public/img/blogImages/2026-01-19-mapswipe-2025-reflections-roadmap.png rename to blogs/images/2026-01-19-mapswipe-2025-reflections-roadmap.png diff --git a/public/img/blogImages/2026-01-19-mapswipe-2025-reflections-video-thumbnail.png b/blogs/images/2026-01-19-mapswipe-2025-reflections-video-thumbnail.png similarity index 100% rename from public/img/blogImages/2026-01-19-mapswipe-2025-reflections-video-thumbnail.png rename to blogs/images/2026-01-19-mapswipe-2025-reflections-video-thumbnail.png diff --git a/public/img/blogImages/2026-01-19-mapswipe-2025-reflections-view-streets-project.gif b/blogs/images/2026-01-19-mapswipe-2025-reflections-view-streets-project.gif similarity index 100% rename from public/img/blogImages/2026-01-19-mapswipe-2025-reflections-view-streets-project.gif rename to blogs/images/2026-01-19-mapswipe-2025-reflections-view-streets-project.gif diff --git a/codegen.yml b/codegen.yml index 6f6d1695..ab9b4144 100644 --- a/codegen.yml +++ b/codegen.yml @@ -2,14 +2,18 @@ overwrite: true schema: - "./backend/schema.graphql" documents: - - "src/**/*.ts" - - "scripts/**/*.ts" - - "src/**/*.graphql" + - "scripts/graphql/*.graphql" generates: ./generated/types.ts: plugins: - "typescript" - "typescript-operations" - ./generated/schema.json: - plugins: - - "introspection" + config: + enumsAsTypes: true + onlyOperationTypes: true + # JSON-serialized over HTTP: date/time scalars arrive as ISO strings + scalars: + DateTime: string + Date: string + Time: string + GenericScalar: unknown diff --git a/docker-compose.yml b/docker-compose.yml index a8a7a228..d960cac9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,18 +1,10 @@ -version: '3.8' - services: - next: + astro: build: . - command: sh -c 'yarn install && yarn dev' + command: sh -c 'corepack enable && pnpm install && pnpm dev --host 0.0.0.0' ports: - - '3020:3020' - env_file: - .env.local - - checks: - build: . - command: sh -c 'pre-commit run -vvv --all-files' - env_file: - - .env.local - profiles: - - test + - '4321:4321' + volumes: + - .:/code + # keep container installs off the host checkout + - /code/node_modules diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 00000000..b86868c7 --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,47 @@ +import js from '@eslint/js'; +import tseslint from 'typescript-eslint'; +import astro from 'eslint-plugin-astro'; +import reactHooks from 'eslint-plugin-react-hooks'; +import globals from 'globals'; +import stylistic from '@stylistic/eslint-plugin'; + +export default tseslint.config( + { ignores: ['build/', '.astro/', 'node_modules/', 'generated/', 'public/', 'docs/'] }, + js.configs.recommended, + ...tseslint.configs.recommended, + ...astro.configs.recommended, + { + // build scripts and islands run under node and the browser respectively + languageOptions: { globals: { ...globals.node, ...globals.browser } }, + }, + { + files: ['**/*.tsx'], + plugins: { 'react-hooks': reactHooks }, + // just the two established rules; the newer compiler-era checks flag + // deliberate island patterns (SSR mount guard, ref-measured chart) + rules: { + 'react-hooks/rules-of-hooks': 'error', + 'react-hooks/exhaustive-deps': 'warn', + }, + }, + { + plugins: { '@stylistic': stylistic }, + rules: { + // the data layer casts GraphQL JSON at its boundaries; a blanket ban + // would force churn without adding safety there + '@typescript-eslint/no-explicit-any': 'off', + '@stylistic/indent': ['error', 4], + }, + }, + { + // house attribute style: 2+ attributes -> one per line, closing + // bracket tag-aligned; a single attribute stays on the tag line + files: ['**/*.tsx'], + plugins: { '@stylistic': stylistic }, + rules: { + '@stylistic/jsx-max-props-per-line': ['error', { maximum: 1 }], + '@stylistic/jsx-first-prop-new-line': ['error', 'multiline-multiprop'], + '@stylistic/jsx-closing-bracket-location': ['error', 'tag-aligned'], + }, + }, +); diff --git a/export-images.config.js b/export-images.config.js deleted file mode 100644 index 98ac4b50..00000000 --- a/export-images.config.js +++ /dev/null @@ -1,12 +0,0 @@ -/** - * @type {import('next-export-optimize-images').Config} - */ -const config = { - filenameGenerator: (obj) => { - const { path, name, width, quality, extension } = obj; - const newName = name.split('%2F').join('/'); - return `${path}/${newName}.${width}.${quality}.${extension}`; - }, -} - -module.exports = config; diff --git a/i18next-parser.config.js b/i18next-parser.config.js deleted file mode 100644 index a09555c8..00000000 --- a/i18next-parser.config.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - locales: ['en', 'ne', 'hu', 'de', 'cs', 'pt'], -}; diff --git a/knip.json b/knip.json new file mode 100644 index 00000000..1e8f8962 --- /dev/null +++ b/knip.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://unpkg.com/knip@latest/schema.json", + "entry": [ + "src/pages/**/*.astro", + "src/pages/**/*.ts", + "scripts/*.mjs", + "tests/e2e/**/*.spec.ts", + "packages/*/src/index.ts", + "packages/*/src/runner.ts", + "packages/*/src/*.astro" + ], + "project": [ + "src/**", + "scripts/**", + "packages/**" + ], + "ignoreBinaries": [ + "du" + ] +} \ No newline at end of file diff --git a/public/locales/cs/blog.json b/locales/cs/blog.json similarity index 100% rename from public/locales/cs/blog.json rename to locales/cs/blog.json diff --git a/public/locales/cs/blogs.json b/locales/cs/blogs.json similarity index 100% rename from public/locales/cs/blogs.json rename to locales/cs/blogs.json diff --git a/public/locales/cs/common.json b/locales/cs/common.json similarity index 100% rename from public/locales/cs/common.json rename to locales/cs/common.json diff --git a/public/locales/cs/data.json b/locales/cs/data.json similarity index 100% rename from public/locales/cs/data.json rename to locales/cs/data.json diff --git a/public/locales/cs/get-involved.json b/locales/cs/get-involved.json similarity index 100% rename from public/locales/cs/get-involved.json rename to locales/cs/get-involved.json diff --git a/public/locales/cs/home.json b/locales/cs/home.json similarity index 100% rename from public/locales/cs/home.json rename to locales/cs/home.json diff --git a/public/locales/cs/privacy.json b/locales/cs/privacy.json similarity index 100% rename from public/locales/cs/privacy.json rename to locales/cs/privacy.json diff --git a/public/locales/cs/project.json b/locales/cs/project.json similarity index 100% rename from public/locales/cs/project.json rename to locales/cs/project.json diff --git a/public/locales/de/blog.json b/locales/de/blog.json similarity index 100% rename from public/locales/de/blog.json rename to locales/de/blog.json diff --git a/public/locales/de/blogs.json b/locales/de/blogs.json similarity index 100% rename from public/locales/de/blogs.json rename to locales/de/blogs.json diff --git a/public/locales/de/common.json b/locales/de/common.json similarity index 100% rename from public/locales/de/common.json rename to locales/de/common.json diff --git a/public/locales/de/data.json b/locales/de/data.json similarity index 100% rename from public/locales/de/data.json rename to locales/de/data.json diff --git a/public/locales/de/get-involved.json b/locales/de/get-involved.json similarity index 100% rename from public/locales/de/get-involved.json rename to locales/de/get-involved.json diff --git a/public/locales/de/home.json b/locales/de/home.json similarity index 100% rename from public/locales/de/home.json rename to locales/de/home.json diff --git a/public/locales/de/privacy.json b/locales/de/privacy.json similarity index 100% rename from public/locales/de/privacy.json rename to locales/de/privacy.json diff --git a/public/locales/de/project.json b/locales/de/project.json similarity index 100% rename from public/locales/de/project.json rename to locales/de/project.json diff --git a/public/locales/en/blog.json b/locales/en/blog.json similarity index 100% rename from public/locales/en/blog.json rename to locales/en/blog.json diff --git a/public/locales/en/blogs.json b/locales/en/blogs.json similarity index 100% rename from public/locales/en/blogs.json rename to locales/en/blogs.json diff --git a/public/locales/en/common.json b/locales/en/common.json similarity index 100% rename from public/locales/en/common.json rename to locales/en/common.json diff --git a/public/locales/en/data.json b/locales/en/data.json similarity index 100% rename from public/locales/en/data.json rename to locales/en/data.json diff --git a/public/locales/en/get-involved.json b/locales/en/get-involved.json similarity index 100% rename from public/locales/en/get-involved.json rename to locales/en/get-involved.json diff --git a/public/locales/en/home.json b/locales/en/home.json similarity index 100% rename from public/locales/en/home.json rename to locales/en/home.json diff --git a/public/locales/en/privacy.json b/locales/en/privacy.json similarity index 100% rename from public/locales/en/privacy.json rename to locales/en/privacy.json diff --git a/public/locales/en/project.json b/locales/en/project.json similarity index 100% rename from public/locales/en/project.json rename to locales/en/project.json diff --git a/public/locales/es/privacy.json b/locales/es/privacy.json similarity index 100% rename from public/locales/es/privacy.json rename to locales/es/privacy.json diff --git a/public/locales/fr/blogs.json b/locales/fr/blogs.json similarity index 100% rename from public/locales/fr/blogs.json rename to locales/fr/blogs.json diff --git a/public/locales/fr/common.json b/locales/fr/common.json similarity index 100% rename from public/locales/fr/common.json rename to locales/fr/common.json diff --git a/public/locales/fr/privacy.json b/locales/fr/privacy.json similarity index 100% rename from public/locales/fr/privacy.json rename to locales/fr/privacy.json diff --git a/public/locales/hu/blog.json b/locales/hu/blog.json similarity index 100% rename from public/locales/hu/blog.json rename to locales/hu/blog.json diff --git a/public/locales/hu/blogs.json b/locales/hu/blogs.json similarity index 100% rename from public/locales/hu/blogs.json rename to locales/hu/blogs.json diff --git a/public/locales/hu/common.json b/locales/hu/common.json similarity index 100% rename from public/locales/hu/common.json rename to locales/hu/common.json diff --git a/public/locales/hu/data.json b/locales/hu/data.json similarity index 100% rename from public/locales/hu/data.json rename to locales/hu/data.json diff --git a/public/locales/hu/get-involved.json b/locales/hu/get-involved.json similarity index 100% rename from public/locales/hu/get-involved.json rename to locales/hu/get-involved.json diff --git a/public/locales/hu/home.json b/locales/hu/home.json similarity index 100% rename from public/locales/hu/home.json rename to locales/hu/home.json diff --git a/public/locales/hu/privacy.json b/locales/hu/privacy.json similarity index 100% rename from public/locales/hu/privacy.json rename to locales/hu/privacy.json diff --git a/public/locales/hu/project.json b/locales/hu/project.json similarity index 100% rename from public/locales/hu/project.json rename to locales/hu/project.json diff --git a/public/locales/ne/blog.json b/locales/ne/blog.json similarity index 100% rename from public/locales/ne/blog.json rename to locales/ne/blog.json diff --git a/public/locales/ne/blogs.json b/locales/ne/blogs.json similarity index 100% rename from public/locales/ne/blogs.json rename to locales/ne/blogs.json diff --git a/public/locales/ne/common.json b/locales/ne/common.json similarity index 100% rename from public/locales/ne/common.json rename to locales/ne/common.json diff --git a/public/locales/ne/data.json b/locales/ne/data.json similarity index 100% rename from public/locales/ne/data.json rename to locales/ne/data.json diff --git a/public/locales/ne/get-involved.json b/locales/ne/get-involved.json similarity index 100% rename from public/locales/ne/get-involved.json rename to locales/ne/get-involved.json diff --git a/public/locales/ne/home.json b/locales/ne/home.json similarity index 100% rename from public/locales/ne/home.json rename to locales/ne/home.json diff --git a/public/locales/ne/privacy.json b/locales/ne/privacy.json similarity index 100% rename from public/locales/ne/privacy.json rename to locales/ne/privacy.json diff --git a/public/locales/ne/project.json b/locales/ne/project.json similarity index 100% rename from public/locales/ne/project.json rename to locales/ne/project.json diff --git a/public/locales/pt/blog.json b/locales/pt/blog.json similarity index 100% rename from public/locales/pt/blog.json rename to locales/pt/blog.json diff --git a/public/locales/pt/blogs.json b/locales/pt/blogs.json similarity index 100% rename from public/locales/pt/blogs.json rename to locales/pt/blogs.json diff --git a/public/locales/pt/common.json b/locales/pt/common.json similarity index 100% rename from public/locales/pt/common.json rename to locales/pt/common.json diff --git a/public/locales/pt/data.json b/locales/pt/data.json similarity index 100% rename from public/locales/pt/data.json rename to locales/pt/data.json diff --git a/public/locales/pt/get-involved.json b/locales/pt/get-involved.json similarity index 100% rename from public/locales/pt/get-involved.json rename to locales/pt/get-involved.json diff --git a/public/locales/pt/home.json b/locales/pt/home.json similarity index 100% rename from public/locales/pt/home.json rename to locales/pt/home.json diff --git a/public/locales/pt/privacy.json b/locales/pt/privacy.json similarity index 100% rename from public/locales/pt/privacy.json rename to locales/pt/privacy.json diff --git a/public/locales/pt/project.json b/locales/pt/project.json similarity index 100% rename from public/locales/pt/project.json rename to locales/pt/project.json diff --git a/next-env.d.ts b/next-env.d.ts deleted file mode 100644 index 4f11a03d..00000000 --- a/next-env.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -/// -/// - -// NOTE: This file should not be edited -// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/next-i18next.config.js b/next-i18next.config.js deleted file mode 100644 index 4ec5df05..00000000 --- a/next-i18next.config.js +++ /dev/null @@ -1,12 +0,0 @@ -const langConfig = require('./i18next-parser.config.js'); - -/** @type {import('next-i18next').UserConfig} */ -module.exports = { - // debug: process.env.NODE_ENV === 'development', - // reloadOnPrerender: process.env.NODE_ENV === 'development', - i18n: { - defaultLocale: 'en', - locales: langConfig.locales, - }, - returnEmptyString: false, -}; diff --git a/next-sitemap.config.js b/next-sitemap.config.js deleted file mode 100644 index 4bcde3d6..00000000 --- a/next-sitemap.config.js +++ /dev/null @@ -1,19 +0,0 @@ -/** @type {import('next-sitemap').IConfig} */ - -const config = { - siteUrl: process.env.MAPSWIPE_DEPLOYMENT_URL || 'https://mapswipe.org/', - exclude: ['/404'], - generateRobotsTxt: true, - outDir: './out', - robotsTxtOptions: { - policies: [ - { - userAgent: '*', - disallow: ['/404'], - }, - { userAgent: '*', allow: '/' }, - ], - }, -}; - -module.exports = config; diff --git a/next.config.js b/next.config.js deleted file mode 100644 index 6b8262cc..00000000 --- a/next.config.js +++ /dev/null @@ -1,18 +0,0 @@ -const withExportImages = require('next-export-optimize-images'); - -const basePrefix = process.env.NEXT_PUBLIC_BASE_PREFIX; -const isBasePrefixAvailable = basePrefix !== undefined && basePrefix.trim().length > 0; - -const nextConfig = withExportImages({ - output: 'export', - trailingSlash: true, - reactStrictMode: true, - basePath: isBasePrefixAvailable ? `/${basePrefix}` : undefined, - assetPrefix: isBasePrefixAvailable ? `/${basePrefix}/` : undefined, - images: { - domain: ['https://firebasestorage.googleapis.com/v0/b/msf-mapswipe.appspot.com'], - deviceSizes: [640, 960, 1280, 1600, 1920], - }, -}); - -module.exports = nextConfig; diff --git a/package.json b/package.json index 0f7eb300..4efe1f7d 100644 --- a/package.json +++ b/package.json @@ -1,79 +1,77 @@ { "name": "mapswipe-website", - "version": "0.0.1", + "type": "module", "private": true, + "version": "0.0.1", "scripts": { - "predev": "mkdir -p cache", - "prebuild": "mkdir -p cache", - "prestart": "mkdir -p cache", - "dev": "next dev -p 3020", - "build": "next build && next-export-optimize-images && next-sitemap && cp redirects/* out/en/ -r", - "start": "next start", - "generate:i18n": "i18next 'src/**/*.{jsx,tsx}' -o 'public/locales/$LOCALE/$NAMESPACE.json'", - "lint": "next lint", - "fetch-data": "ts-node -P tsconfig.scripts.json scripts/fetchData.ts", - "fetch-data:local": "env-cmd -f .env.local ts-node -P tsconfig.scripts.json scripts/fetchData.ts", + "dev": "astro dev", + "build": "node scripts/prefetch-images.mjs && astro build && node scripts/sweep-dist.mjs", + "build:incremental": "node scripts/incremental-build.mjs", + "prefetch": "node scripts/prefetch-images.mjs", + "preview": "astro preview", + "astro": "astro", + "fetch-data": "node scripts/fetchData.ts", + "lint": "eslint .", "css-lint": "stylelint 'src/**/*.css'", - "typecheck": "tsc", - "check-unused": "unimported", - "generate:type": "graphql-codegen --require dotenv/config --config codegen.yml" + "typecheck": "astro check", + "verify-dist": "node scripts/verify-dist.mjs", + "test:e2e": "playwright test", + "generate:type": "node scripts/generateTypes.mjs", + "fetch-data:local": "node --env-file=.env.local scripts/fetchData.ts", + "check-unused": "knip" }, "dependencies": { - "@togglecorp/fujs": "^2.0.0", + "@astrojs/react": "^4.2.0", + "@astrojs/sitemap": "^3.2.1", + "@fontsource/dm-sans": "^5.2.8", + "@fontsource/lato": "^5.2.7", + "astro": "^5.7.0", "graphql": "^16.6.0", "graphql-request": "^5.1.0", - "gray-matter": "^4.0.3", - "i18next": "^22.5.1", + "i18next": "^24.2.0", "leaflet": "^1.9.4", - "leaflet-gesture-handling": "^1.2.2", - "next": "^13.4.7-canary.0", - "next-i18next": "^13.3.0", - "next-language-detector": "^1.0.2", "papaparse": "^5.4.1", - "posthog-js": "^1.68.4", - "react": "18.2.0", - "react-dom": "18.2.0", - "react-i18next": "^12.3.1", - "react-icons": "^4.9.0", + "react": "^18.3.1", + "react-dom": "^18.3.1", "react-leaflet": "^4.2.1", - "remark": "^14.0.3", - "remark-gfm": "^3.0.1", - "remark-html": "^15.0.2", - "resize-observer-polyfill": "^1.5.1" + "rehype-stringify": "^10.0.1", + "remark-gfm": "^4.0.1", + "remark-parse": "^11.0.0", + "remark-rehype": "^11.1.2", + "sharp": "^0.33.5", + "unified": "^11.0.5" }, "devDependencies": { - "@graphql-codegen/cli": "^2.16.4", - "@graphql-codegen/introspection": "^2.2.3", - "@graphql-codegen/typescript-operations": "^2.5.12", - "@types/leaflet": "^1.9.3", - "@types/node": "^18.11.9", - "@types/papaparse": "^5.3.7", - "@types/react": "^18.0.25", - "@types/react-dom": "^18.2.6", - "@typescript-eslint/eslint-plugin": "^5.48.0", - "@typescript-eslint/parser": "^5.48.0", - "env-cmd": "^10.1.0", - "eslint": "^8.31.0", - "eslint-config-airbnb": "^19.0.4", - "eslint-config-airbnb-typescript": "^17.0.0", - "eslint-config-next": "^13.1.1", - "eslint-plugin-import": "^2.26.0", - "eslint-plugin-jsx-a11y": "^6.6.1", - "eslint-plugin-postcss-modules": "^2.0.0", - "eslint-plugin-react": "^7.31.11", - "eslint-plugin-react-hooks": "^4.6.0", - "i18next-parser": "^8.0.0", - "next-export-optimize-images": "^2.1.0", - "next-sitemap": "^4.1.8", + "@astrojs/check": "^0.9.9", + "@eslint/js": "^10.0.1", + "@graphql-codegen/cli": "^7.2.0", + "@graphql-codegen/typescript": "^6.1.0", + "@graphql-codegen/typescript-operations": "^6.1.0", + "@playwright/test": "^1.61.1", + "@stylistic/eslint-plugin": "^5.10.0", + "@stylistic/stylelint-plugin": "^5.2.1", + "@types/leaflet": "^1.9.12", + "@types/papaparse": "^5.3.15", + "@types/react": "^18.3.12", + "@types/react-dom": "^18.3.1", + "eslint": "^10.7.0", + "eslint-plugin-astro": "^1.7.0", + "eslint-plugin-react-hooks": "^7.1.1", + "globals": "^17.7.0", + "knip": "^6.26.0", + "postcss": "^8.5.19", "postcss-flexbugs-fixes": "^5.0.2", - "postcss-nested": "^6.0.0", - "postcss-normalize": "^10.0.1", - "postcss-preset-env": "^7.8.3", - "stylelint": "^14.16.1", - "stylelint-config-concentric": "^2.0.2", - "stylelint-config-recommended": "^9.0.0", - "ts-node": "^10.9.1", - "typescript": "^5.1.3", - "unimported": "^1.29.1" + "postcss-nested": "^7.0.2", + "postcss-normalize": "^13.0.1", + "postcss-preset-env": "^11.3.2", + "stylelint": "^17.14.0", + "stylelint-config-concentric-order": "^5.5.0", + "stylelint-config-recommended": "^18.0.0", + "typescript": "^5.7.0", + "typescript-eslint": "^8.63.0" + }, + "packageManager": "pnpm@10.34.5", + "engines": { + "node": ">=22.18" } -} +} \ No newline at end of file diff --git a/packages/README.md b/packages/README.md new file mode 100644 index 00000000..89dfaa3a --- /dev/null +++ b/packages/README.md @@ -0,0 +1,23 @@ +# packages/ — deliberately-owned build infrastructure + +Four reusable Astro packages — plain directories imported by relative path +(no workspace linking); each keeps its own `package.json`/README so it can be +published later. +Each exists because Astro measurably cannot do the job via config or an +existing integration — every number cited below was +measured during the migration. + +| Package | One-liner | Configured in this app at | +| --- | --- | --- | +| [`astro-hydration-dedup`](astro-hydration-dedup/) | Externalizes Astro's force-inlined island hydration runtimes (was 57 MB / 12% of dist duplicated across 14.6k pages) into per-script cached files | `astro.config.mjs` (`hydrationDedup({ minOccurrences: 10 })`) | +| [`astro-remote-images`](astro-remote-images/) | Build-time remote-image optimization that cannot kill the build: per-image error policy (error-image/passthrough/fail), bounded concurrent prefetch, negative caching (production data contained 8 build-fatal images) | `src/lib/remoteImages.ts` (instance + policies), `scripts/prefetch-images.mjs` (URL collector) | +| [`astro-incremental-static`](astro-incremental-static/) | Render-skip incremental builds for `output: 'static'` — nothing else does this (verified vs Astro & Next stable/canary); one-project change 6 s vs 33 s full | `scripts/incremental-build.mjs` (runner config); pages import `selectPaths` | +| [`astro-dist-sweep`](astro-dist-sweep/) | Mark-and-sweep for `dist/_astro/`: deletes assets nothing references (astro:assets ships image *originals* that only exist in optimized variants — 35 files / 28.7 MB here; merged incremental trees retain superseded variants) | `scripts/sweep-dist.mjs` (after plain builds); the incremental runner calls it itself | + +Two of the three are stopgaps we *want* to delete: if Astro ever ships the +equivalent capability upstream, delete the corresponding package (see each +README's caveats and the issue links in the source headers). + +Rules for adding a fourth: config → official integration → maintained +community integration → only then hand-roll, with a measured why and an +exit path documented in the package README. diff --git a/packages/astro-dist-sweep/README.md b/packages/astro-dist-sweep/README.md new file mode 100644 index 00000000..c7da0ce9 --- /dev/null +++ b/packages/astro-dist-sweep/README.md @@ -0,0 +1,51 @@ +# astro-dist-sweep + +Mark-and-sweep for Astro static builds: deletes files in the hashed-assets +dir (`dist/_astro/`) that nothing in the final tree references. + +## Why + +Two things leave unreferenced files there: + +- **Always-emitted image originals.** astro:assets emits the *original* of + every processed image whose `src` is touched at build time — markdown-body + images, and any code reading `image().src` (this app's incremental + change-detection signatures do). Pages only reference the derived + variants, so the originals ship as dead weight in every build + (35 files / 28.7 MB measured on this site). +- **Merged incremental trees.** astro-incremental-static overlays the fresh + partial build onto the previous dist; a rebuilt page can reference + newly-hashed variants while the merge keeps the superseded ones. + +## How + +Roots are all text files outside `_astro/` (page HTML — including +astro-island props — JSON endpoints, the sitemap), scanned for absolute +`/_astro/` references. Marked js/css files are then scanned +transitively for vite's relative inter-chunk specifiers (`"./chunk-x.js"`) +and css `url()`s — that transitive step is what keeps lazily imported chunks +alive (e.g. a leaflet island chunk reachable *only* via `import()` from +another chunk, never from HTML). Everything never marked is deleted. + +## Usage + +```js +import { sweepSupersededAssets } from './packages/astro-dist-sweep/src/index.ts'; + +const { removed, bytes } = sweepSupersededAssets('dist'); +``` + +Wired into this app in two places: + +- `scripts/sweep-dist.mjs` — runs after every plain `astro build` + (`pnpm build`), +- `packages/astro-incremental-static` runner — step 7, over the merged tree. + +## Caveats + +- Reference detection is textual. Dynamically *computed* asset URLs + (`import('/_astro/' + name)`) would be missed — vite does not emit such + patterns for its own chunks/assets. +- Only files directly inside `_astro/` are swept; page dirs and other + top-level assets (`_img/`, `img/`) are never touched (`_img/` is managed + by astro-remote-images). diff --git a/packages/astro-dist-sweep/package.json b/packages/astro-dist-sweep/package.json new file mode 100644 index 00000000..294d7552 --- /dev/null +++ b/packages/astro-dist-sweep/package.json @@ -0,0 +1,24 @@ +{ + "name": "astro-dist-sweep", + "version": "0.1.0", + "description": "Mark-and-sweep for Astro static builds: deletes hashed assets in the output dir that nothing in the final tree references (always-emitted image originals, superseded variants in merged incremental trees).", + "type": "module", + "private": true, + "exports": { + ".": "./src/index.ts" + }, + "files": [ + "src", + "README.md" + ], + "keywords": [ + "astro", + "static", + "assets", + "sweep", + "cleanup" + ], + "engines": { + "node": ">=22.18" + } +} diff --git a/packages/astro-dist-sweep/src/index.ts b/packages/astro-dist-sweep/src/index.ts new file mode 100644 index 00000000..6e4fb383 --- /dev/null +++ b/packages/astro-dist-sweep/src/index.ts @@ -0,0 +1,122 @@ +/** + * astro-dist-sweep — delete hashed assets nothing references. + * + * Astro's static output accumulates unreferenced files under the hashed + * assets dir (`_astro/` by default): + * - astro:assets emits the ORIGINAL of every processed image whose `src` + * is touched at build time (e.g. change-detection signatures reading + * `coverImage.src`, or markdown-body images) — pages only reference the + * derived variants, so the originals ship as dead weight in EVERY build; + * - a merged incremental tree (see astro-incremental-static) can retain + * superseded hashed variants: a rebuilt page references newly-hashed + * files while the merge keeps the old ones for the pages it skipped. + * + * Mark-and-sweep over the final tree: roots are all text files outside the + * assets dir (absolute `/_astro/` references — page HTML, astro-island + * props, JSON endpoints, the sitemap); marked js/css files are then scanned + * transitively for the relative specifiers vite emits between chunks + * ("./chunk-x.js") and inside css url()s — this is what keeps lazily + * imported chunks (reachable only from other chunks, never from HTML) alive. + * Anything in the assets dir never marked is deleted. + */ +import { + readFileSync, + readdirSync, + rmSync, + statSync, +} from 'node:fs'; +import { join, sep } from 'node:path'; + +export interface SweepOptions { + /** Hashed-assets dir name inside the dist dir. Default '_astro'. */ + assetsDir?: string; +} + +export interface SweepResult { + removed: number; + bytes: number; +} + +// Files that can carry /_astro/ references (everything else — images, +// fonts, videos — is a leaf and cannot reference further assets). +const TEXT_ROOT = /\.(html|css|js|mjs|json|xml|svg|txt|webmanifest)$/; +const REL_SCAN = /\.(js|mjs|css)$/; +const ABS_REF = /\/_astro\/([\w.@~-]+\.\w+)/g; +const REL_REF = /["'(]\.\/([\w.@~-]+\.\w+)/g; + +function isDir(p: string): boolean { + try { + return statSync(p).isDirectory(); + } catch { + return false; + } +} + +function collectFiles(dir: string, acc: string[]): string[] { + for (const entry of readdirSync(dir, { withFileTypes: true })) { + const full = join(dir, entry.name); + if (entry.isDirectory()) { + collectFiles(full, acc); + } else if (entry.isFile()) { + acc.push(full); + } + } + return acc; +} + +export function sweepSupersededAssets( + distDir: string, + options: SweepOptions = {}, +): SweepResult { + const assetsDirName = options.assetsDir ?? '_astro'; + const assetsDir = join(distDir, assetsDirName); + if (!isDir(assetsDir)) { + return { removed: 0, bytes: 0 }; + } + + const assets = new Set( + readdirSync(assetsDir).filter((name) => !isDir(join(assetsDir, name))), + ); + const referenced = new Set(); + const queue: string[] = []; + const mark = (name: string) => { + if (assets.has(name) && !referenced.has(name)) { + referenced.add(name); + queue.push(name); + } + }; + + const assetsPathMarker = `${sep}${assetsDirName}${sep}`; + for (const file of collectFiles(distDir, [])) { + if (file.includes(assetsPathMarker) || !TEXT_ROOT.test(file)) { + continue; + } + for (const m of readFileSync(file, 'utf8').matchAll(ABS_REF)) { + mark(m[1]); + } + } + while (queue.length > 0) { + const name = queue.pop()!; + if (!REL_SCAN.test(name)) { + continue; + } + const text = readFileSync(join(assetsDir, name), 'utf8'); + for (const m of text.matchAll(ABS_REF)) { + mark(m[1]); + } + for (const m of text.matchAll(REL_REF)) { + mark(m[1]); + } + } + + let removed = 0; + let bytes = 0; + for (const name of assets) { + if (!referenced.has(name)) { + bytes += statSync(join(assetsDir, name)).size; + rmSync(join(assetsDir, name), { force: true }); + removed += 1; + } + } + return { removed, bytes }; +} diff --git a/packages/astro-hydration-dedup/README.md b/packages/astro-hydration-dedup/README.md new file mode 100644 index 00000000..981a8d96 --- /dev/null +++ b/packages/astro-hydration-dedup/README.md @@ -0,0 +1,118 @@ +# astro-hydration-dedup + +Astro integration that deduplicates **byte-identical inline classic +`` blocks and tallies, + per distinct exact byte sequence, how many pages carry it. No hardcoded + paths, no assumptions about which scripts exist. +2. **Emit** — every block that recurs on ≥ `minOccurrences` pages is written + once as `/-.js` (content-hashed: the URL is + stable while the content is, and busts caches across Astro upgrades). +3. **Rewrite in place** — every inline occurrence is replaced *at its own + position* with `` (a classic **blocking** script, + so synchronous execution at the same document position preserves + semantics). Per-script in-place replacement preserves execution position + and order for arbitrary directive combinations across pages. + +Safety properties: + +- **Threshold as guard** — a script must recur verbatim on `minOccurrences` + pages before it is externalized, so page-specific inline scripts are never + touched. +- **Idempotent** — pages already rewritten (e.g. merged from a cached, + previously post-processed dist) carry no inline block anymore and are left + alone; already-externalized pages still count toward the threshold via + their ` blocks and tally, per +// distinct EXACT byte sequence, how many pages carry it. Pages that +// already reference a previously emitted file for the same content (a +// merged, already-rewritten dist) count toward the same tally. +// 2. EMIT one external content-hashed file PER distinct script whose page +// count reaches `minOccurrences` — e.g. `_astro/dedup-.js`. +// 3. REWRITE every occurrence in place: each qualifying inline block becomes +// `` (a classic BLOCKING script) at the exact +// position the inline block occupied. Per-script in-place replacement +// preserves execution position and order for ARBITRARY directive +// combinations across pages — no assumptions about which scripts co-occur. +// Pages carrying none of the qualifying blocks are left untouched. +// +// Idempotency: a page whose inline block was already externalized simply has +// no inline block left to match — re-running the integration over a merged, +// previously rewritten dist is a natural no-op for those pages. +// +// Fail-soft: any anomaly logs a warning and leaves dist as-is; the build never +// fails over an optimization. +// +// Ordering: register LAST in astro.config.mjs so it runs after other +// build:done hooks (e.g. @astrojs/sitemap). +import { createHash } from 'node:crypto'; +import { mkdir, readFile, readdir, writeFile } from 'node:fs/promises'; +import { join, sep } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import type { AstroIntegration } from 'astro'; + +export interface HydrationDedupOptions { + /** + * A distinct inline script is externalized only when it appears verbatim + * on at least this many pages. This is also the safety guard against + * externalizing page-specific scripts: anything rarer stays inline. + * Default 10 — e.g. an island runtime shared by only 6 sibling pages + * stays inline at the default; lower the threshold to catch it. + */ + minOccurrences?: number; + /** Directory under the build output to emit the shared files into. Default `_astro`. */ + outDir?: string; + /** Emitted filename prefix: `-.js`. Default `dedup`. */ + filenamePrefix?: string; + /** + * Page globs (relative to the build output, `/`-separated, `**`/`*`/`?`) + * to consider. Default: every `** /*.html`. + */ + include?: string[]; + /** Page globs to skip. Applied after `include`. Default none. */ + exclude?: string[]; + /** Master switch. Default true. */ + enabled?: boolean; +} + +// Inline classic scripts exactly as Astro emits its directive runtimes: an +// attribute-less `, + 'g', + ); + await asyncPool(POOL_SIZE, pages, async ({ rel }) => { + const html = await readFile(join(dist, rel), 'utf8'); + const found = html.match(INLINE_SCRIPT_RE) ?? []; + if (found.length > 0) { + const distinct = [...new Set(found)]; + pageBlocks.set(rel, distinct); + for (const block of distinct) { + const info = blocks.get(block); + if (info) { + info.pages += 1; + } else { + blocks.set(block, { pages: 1, refPages: 0 }); + } + } + } + for (const m of html.matchAll(REF_RE)) { + refCounts.set(m[1], (refCounts.get(m[1]) ?? 0) + 1); + } + }); + if (blocks.size === 0) { + logger.info(`no inline classic scripts found across ${pages.length} page(s) — nothing to do`); + return; + } + + // -- 2. Select + emit: one file per qualifying block. + interface Emitted { + block: string; + srcTag: string; + fileName: string; + bytes: number; + pages: number; + } + const emitted: Emitted[] = []; + for (const [block, info] of blocks) { + const body = block.replace(/^`, + fileName, + bytes: body.length, + pages: info.pages, + }); + } + if (emitted.length === 0) { + logger.info( + `no inline script recurs on >= ${minOccurrences} page(s) ` + + `(${blocks.size} distinct found) — nothing externalized`, + ); + return; + } + + // -- 3. Rewrite in place, only on pages known to carry a block. + let rewritten = 0; + const carriers = [...pageBlocks.entries()] + .filter(([, list]) => list.some((b) => emitted.some((e) => e.block === b))) + .map(([rel]) => rel); + await asyncPool(POOL_SIZE, carriers, async (rel) => { + const file = join(dist, rel); + const html = await readFile(file, 'utf8'); + let out = html; + for (const e of emitted) { + // Replace EVERY occurrence at its own position — + // preserves execution position/order per page. + out = out.split(e.block).join(e.srcTag); + } + if (out !== html) { + await writeFile(file, out); + rewritten += 1; + } + }); + + const saved = emitted.reduce((acc, e) => acc + e.bytes * Math.max(0, e.pages - 1), 0); + logger.info( + `${emitted.length} shared script(s) externalized, ${rewritten} page(s) rewritten ` + + `(${pages.length} scanned, ${blocks.size} distinct inline blocks, ` + + `~${(saved / 1024 / 1024).toFixed(1)} MB deduplicated): ` + + emitted.map((e) => `${outDir}/${e.fileName} (${e.bytes} B × ${e.pages} pages)`).join(', '), + ); + } catch (err) { + // Fail-soft: never break the build over an optimization. + const reason = err instanceof Error ? err.message : String(err); + logger.warn(`skipped: ${reason}`); + } + }, + }, + }; +} diff --git a/packages/astro-incremental-static/README.md b/packages/astro-incremental-static/README.md new file mode 100644 index 00000000..80174fb9 --- /dev/null +++ b/packages/astro-incremental-static/README.md @@ -0,0 +1,148 @@ +# astro-incremental-static + +Incremental static builds for Astro (`output: 'static'`): a per-page +`propsHash` **render-skip** wired into `getStaticPaths`, plus a build +orchestration that merges the previous `dist`, prunes deleted pages, +regenerates the sitemap over the merged tree, and sweeps `_astro/` assets +nothing references. + +Nothing in the ecosystem does render-skip: Astro re-renders every page every +build (so do Next stable and canary — measured). On a ~14.7k-page site this +package turns a one-record data change into a ~6 s build instead of ~33 s. + +## ⚠️ Correctness contract (read this first) + +1. **`propsData` completeness is YOUR responsibility.** For every candidate + path you pass to `selectPaths`, `propsData` must cover *everything* that + can change the rendered page apart from code (the runner's `codeHash` + covers code/content inputs). Any data you render but leave out of + `propsData` produces **silently stale pages** — the page won't rebuild + when only that data changes. Aggregate pages (lists, stats) must hash the + whole payload they summarize, not just the visible slice. +2. **The stale-page footgun compounds silently.** A missed dependency isn't an + error — the build succeeds and the page is simply old until an unrelated + change happens to rebuild it. Audit every `selectPaths` call site when the + page's data sources change. +3. **Activation is explicit.** Selection only happens when the runner sets + `ASTRO_INCREMENTAL=1` in the spawned build's environment. A plain + `astro build` is ALWAYS a full build, even with a stale state dir on disk + (file presence is stale state, not intent). + +## Install + +Part of this repo as a plain directory (imported by relative path); copy the directory or publish to npm to use elsewhere. +same npm workspace: + +```jsonc +// package.json +{ + "workspaces": ["packages/*"], + "dependencies": { "astro-incremental-static": "*" }, + "scripts": { "build:incremental": "node scripts/incremental-build.mjs" } +} +``` + +Zero dependencies. Requires Node ≥ 22.18 (TypeScript source, loaded via +Node's built-in type stripping; Vite transforms it for page-side imports). + +## Usage + +**1. Page side** — wrap each `getStaticPaths` result in `selectPaths`: + +```ts +import { selectPaths } from 'astro-incremental-static'; + +export const getStaticPaths = (() => { + const candidates = records.flatMap((record) => LOCALES.map((locale) => ({ + key: `${locale}/projects/${record.id}`, // canonical dist out-path, no slashes at ends + params: { locale, id: record.id }, + propsData: record, // SAME reference across the record's locales -> hashed once + }))); + return selectPaths('projects', candidates); +}); +``` + +Outside the runner this returns every candidate and touches no state — the +normal build path is completely unaffected. + +**2. Orchestration** — a small script (`scripts/incremental-build.mjs`): + +```js +import { runIncrementalBuild } from 'astro-incremental-static/runner'; + +runIncrementalBuild({ + codeHash: { + include: ['src/**', 'astro.config.mjs', 'package.json'], + }, + prune: { routes: ['*/projects/*', '*/blogs/*'] }, + sitemap: { + site: 'https://example.org', + locales: ['en', 'de'], + defaultLocale: 'en', + }, + prefetch: { cmd: 'node', args: ['scripts/prefetch-images.mjs'] }, // optional +}); +``` + +Restore `dist/` and the state dir from your CI cache before running; persist +both afterward. + +## API + +```ts +// 'astro-incremental-static' +selectPaths(pageId, candidates, opts?) // the render-skip selector (memoised hashing) +isIncrementalBuild() // ASTRO_INCREMENTAL === '1' +hashProps(propsData) // sha1 of data minus volatile buildDate +readCodeHash(stateDir?) / readManifest(stateDir?) + +// 'astro-incremental-static/runner' +runIncrementalBuild(config) // the whole orchestration (throws on child failure) +``` + +`Candidate`: `{ key, params, propsData, props? }` — unchanged +contract from the original in-app implementation. + +## Config (`runIncrementalBuild`) + +| Key | Type | Default | Description | +| --- | --- | --- | --- | +| `root` | `string` | `process.cwd()` | App root the build runs from. | +| `distDir` | `string` | `'dist'` | Build output dir (root-relative). The merge staging dir is `-cache`. | +| `stateDir` | `string` | `'.astro-incremental'` | Manifest + code hash + shards. Gitignore it; persist in CI. Exported to the build via `ASTRO_INCREMENTAL_STATE_DIR`. | +| `codeHash.include` | `string[]` | — (required) | Root-relative globs (`**` crosses `/`, `../` allowed) of every code/content input that affects rendered output — EXCLUDING the data axis (tracked per page via `propsData`) and build artifacts. | +| `codeHash.exclude` | `string[]` | `[]` | Globs subtracted from the include set. | +| `prune.routes` | `string[]` | off | Dist-relative dir patterns whose children are per-record pages (e.g. `*/projects/*`). Matched dirs not present as manifest keys are removed after the merge. Segments may use `*`/`?`; `**` unsupported here. | +| `sitemap` | object \| `false` | `false` | `{ enabled?, site, locales, defaultLocale }`. Required when using @astrojs/sitemap — regenerates `sitemap-0.xml`/`sitemap-index.xml` over the MERGED tree (structurally identical to @astrojs/sitemap's i18n output: hreflang alternate groups, redirect stubs excluded, root `/` included as the default locale). | +| `buildCommand` | `{cmd, args}` | `astro build` | Resolved via `/node_modules/.bin` when present. Spawned with `ASTRO_INCREMENTAL=1`. | +| `prefetch` | `{cmd, args}` | off | Command run before the build (cache warm etc.). | + +## How a run proceeds + +1. Compute `codeHash` over the configured inputs; write it to the state dir. +2. Move the previous `dist` aside. +3. Run `prefetch` (if any), then `buildCommand` with `ASTRO_INCREMENTAL=1` — + every `selectPaths` returns only changed/new paths and writes its manifest + shard. +4. `codeHash` changed → the fresh (full) dist is authoritative; done. + Otherwise merge: copy the small fresh tree over the cached full tree. +5. Prune deleted record dirs (`prune.routes` minus manifest keys). +6. Regenerate the sitemap over the merged tree. +7. Sweep `_astro/` files nothing in the final tree references (delegated to + the sibling `astro-dist-sweep` package — see its README for what + accumulates and how the mark-and-sweep works). +8. Persist the merged manifest for the next run. + +Routes *without* a `selectPaths`-wired `getStaticPaths` (the 404, static file +endpoints, redirect stubs) are cheaply re-emitted every run and therefore +always fresh. + +## Both historical bugs are fixed inside the package + +- **Env-var activation**: selection is armed ONLY by `ASTRO_INCREMENTAL=1`, never + by state-dir presence — a plain `astro build` after an incremental run is a + full build, not a silent partial one. +- **Merged-tree sitemap regen**: the runner rebuilds the full sitemap from the + merged dist, so an incremental run can't silently shrink the published + sitemap to just the pages it rendered. + diff --git a/packages/astro-incremental-static/package.json b/packages/astro-incremental-static/package.json new file mode 100644 index 00000000..ad289f59 --- /dev/null +++ b/packages/astro-incremental-static/package.json @@ -0,0 +1,25 @@ +{ + "name": "astro-incremental-static", + "version": "0.1.0", + "description": "Incremental static builds for Astro (output: 'static'): per-page propsHash render-skip in getStaticPaths + an orchestration that merges the previous dist, prunes deleted pages, and regenerates the sitemap over the merged tree.", + "type": "module", + "private": true, + "exports": { + ".": "./src/index.ts", + "./runner": "./src/runner.ts" + }, + "files": [ + "src", + "README.md" + ], + "keywords": [ + "astro", + "incremental", + "static", + "build", + "render-skip" + ], + "engines": { + "node": ">=22.18" + } +} diff --git a/packages/astro-incremental-static/src/index.ts b/packages/astro-incremental-static/src/index.ts new file mode 100644 index 00000000..cf37d367 --- /dev/null +++ b/packages/astro-incremental-static/src/index.ts @@ -0,0 +1,206 @@ +/** + * astro-incremental-static — page-side API. + * + * Incremental static-build support for Astro (`output: 'static'`). + * + * A plain `astro build` renders every path returned by every `getStaticPaths` + * on every build. To skip unchanged pages we compute, per candidate path, a + * `propsHash` of the page's *data* and compare it against the manifest + * persisted by the previous build. A page is (re)built only when: + * - the global `codeHash` changed (any code/content input changed), or + * - its `propsHash` changed (its data changed), or + * - it is new (no manifest entry). + * + * Unbuilt pages are supplied by merging the previous build's dist over the + * fresh partial dist — see ./runner.ts (`runIncrementalBuild`). + * + * ACTIVATION: all of this happens ONLY when `ASTRO_INCREMENTAL=1` is set in + * the environment — which only the runner does when it spawns `astro build`. + * A plain `astro build` is ALWAYS a full build, even when a stale state dir + * (code-hash + manifest from an earlier incremental run) is sitting on disk. + * Keying activation on the env var rather than on the presence of the code-hash file is + * deliberate: file presence is stale state, not intent. + * + * CORRECTNESS CONTRACT (yours to uphold): `candidate.propsData` must cover + * EVERYTHING that can change the rendered page apart from code (which the + * runner's codeHash covers). Data you render but leave out of propsData + * silently produces STALE pages — the page won't rebuild when only that data + * changes. + * + * Everything here runs only inside `getStaticPaths` (build time, Node). The + * Node `fs`/`crypto` imports are tree-shaken out of any client/island bundle + * because they are unreachable from a page component. + */ +import { createHash } from 'node:crypto'; +import { + mkdirSync, + readFileSync, + writeFileSync, +} from 'node:fs'; +import { join } from 'node:path'; + +/** + * State dir shared with the runner. The runner exports its configured + * `stateDir` to the spawned build via ASTRO_INCREMENTAL_STATE_DIR; outside + * the runner the default matches the runner's default. + */ +function stateDirPath(override?: string): string { + return override + ?? process.env.ASTRO_INCREMENTAL_STATE_DIR + ?? join(process.cwd(), '.astro-incremental'); +} + +export interface Manifest { + codeHash: string; + paths: Record; +} + +/** + * True ONLY when running under the incremental runner, which sets + * ASTRO_INCREMENTAL=1 in the `astro build` child's environment. `process.env` + * is the real Node env here (getStaticPaths runs server-side at build time), + * not a Vite compile-time replacement. + */ +export function isIncrementalBuild(): boolean { + return process.env.ASTRO_INCREMENTAL === '1'; +} + +/** + * Hash a page's data. Volatile fields that change every build without + * representing a real content change are stripped first (otherwise every page + * would re-invalidate on each run) — currently the conventional `buildDate` + * top-level key. + * + * No key-sorting/canonicalisation: values are expected to come from a data + * snapshot (written once at fetch time) via fixed code paths, so JSON key + * order is already deterministic across builds. Keeping this cheap matters — + * it runs for every candidate path (potentially 10k+). + */ +export function hashProps(propsData: unknown): string { + let payload = propsData; + if (propsData && typeof propsData === 'object' && !Array.isArray(propsData)) { + const rest = { ...(propsData as Record) }; + delete rest.buildDate; + payload = rest; + } + return createHash('sha1').update(JSON.stringify(payload)).digest('hex'); +} + +export function readCodeHash(stateDir?: string): string { + try { + return readFileSync(join(stateDirPath(stateDir), 'code-hash.txt'), 'utf8').trim(); + } catch { + return ''; + } +} + +let cachedManifest: Manifest | undefined; +export function readManifest(stateDir?: string): Manifest { + if (!cachedManifest) { + try { + cachedManifest = JSON.parse( + readFileSync(join(stateDirPath(stateDir), 'manifest.json'), 'utf8'), + ) as Manifest; + } catch { + cachedManifest = { codeHash: '', paths: {} }; + } + } + return cachedManifest; +} + +export interface Candidate { + /** + * Canonical dist out-path key, e.g. `en/projects/` — no leading or + * trailing slash. Used to correlate with the prior manifest and to drive + * pruning of deleted pages in the runner. + */ + key: string; + /** getStaticPaths params for this path. */ + params: Params; + /** + * The data whose change should trigger a rebuild. Pages that share one + * logical record (e.g. a record's per-locale variants) SHOULD pass the + * SAME object reference here so the hash is memoised once across them. + * MUST be complete — see the correctness contract in the module header. + */ + propsData: unknown; + /** Optional getStaticPaths props to forward for selected paths. */ + props?: Props; +} + +export interface SelectPathsOptions { + /** Override the state dir (default: ASTRO_INCREMENTAL_STATE_DIR env or `/.astro-incremental`). */ + stateDir?: string; +} + +function toPath( + candidate: Candidate, +): { params: Params; props?: Props } { + return candidate.props === undefined + ? { params: candidate.params } + : { params: candidate.params, props: candidate.props }; +} + +/** + * Decide which candidate paths to (re)build and persist this page's shard of + * the manifest (the full candidate set with hashes — used by the runner to + * rebuild the merged manifest and to prune deleted pages). + * + * Outside the runner (no ASTRO_INCREMENTAL=1 → a normal full `astro build`) + * EVERY candidate is returned and NO state is read or written — the normal + * build path is completely unaffected, regardless of what a previous + * incremental run may have left in the state dir. + * + * Under the runner, a changed (or unreadable) codeHash also returns every + * candidate (full rebuild), and shards are always written so the runner can + * persist the complete current manifest. + */ +export function selectPaths( + pageId: string, + candidates: Candidate[], + opts: SelectPathsOptions = {}, +): { params: Params; props?: Props }[] { + if (!isIncrementalBuild()) { + return candidates.map(toPath); + } + + const stateDir = stateDirPath(opts.stateDir); + const codeHash = readCodeHash(opts.stateDir); + const prev = readManifest(opts.stateDir); + // Empty/unreadable codeHash (defensive — the runner always writes it + // before spawning the build) or a changed codeHash => full rebuild. + const codeChanged = codeHash === '' || prev.codeHash !== codeHash; + + // propsData is often the SAME object reference for pages sharing data + // (a record's per-locale variants), so memoise the hash by identity to + // avoid re-serialising the same data once per locale. + const hashByRef = new WeakMap(); + const memoHash = (propsData: unknown): string => { + if (propsData && typeof propsData === 'object') { + const cached = hashByRef.get(propsData); + if (cached !== undefined) { + return cached; + } + const computed = hashProps(propsData); + hashByRef.set(propsData, computed); + return computed; + } + return hashProps(propsData); + }; + + const shard: Record = {}; + const toBuild: { params: Params; props?: Props }[] = []; + for (const candidate of candidates) { + const propsHash = memoHash(candidate.propsData); + shard[candidate.key] = propsHash; + if (codeChanged || prev.paths[candidate.key] !== propsHash) { + toBuild.push(toPath(candidate)); + } + } + + const shardDir = join(stateDir, 'shards'); + mkdirSync(shardDir, { recursive: true }); + writeFileSync(join(shardDir, `${pageId}.json`), JSON.stringify(shard)); + + return toBuild; +} diff --git a/packages/astro-incremental-static/src/runner.ts b/packages/astro-incremental-static/src/runner.ts new file mode 100644 index 00000000..8ad3496e --- /dev/null +++ b/packages/astro-incremental-static/src/runner.ts @@ -0,0 +1,504 @@ +/** + * astro-incremental-static — build orchestration (`runIncrementalBuild`). + * + * `astro build` (output: 'static') re-renders every path returned by every + * getStaticPaths on every build. The runner avoids that: + * 1. compute a global `codeHash` over the configured code/content inputs + * (NOT the data axis), + * 2. move the previous build's dist aside (e.g. restored from a CI cache), + * 3. run the optional prefetch command + the build command with + * ASTRO_INCREMENTAL=1 — getStaticPaths (via this package's selectPaths) + * reads the codeHash + previous manifest and returns ONLY the pages + * whose code or data changed, + * 4. merge the previous dist *under* the fresh one (fresh wins), + * 5. prune page dirs (matching the configured prunable route patterns) + * whose record no longer exists, + * 6. regenerate the sitemap over the MERGED tree (the fresh build's sitemap + * only covers the pages rendered this run), + * 7. sweep _astro/ files nothing in the final tree references, + * 8. persist the new manifest for the next run. + * + * codeHash unchanged + data unchanged => ~0 pages rendered (compile only). + * codeHash changed => everything rebuilt (fresh authoritative). + * + * ASTRO_INCREMENTAL=1 is the ONLY thing that activates the per-page + * selection: a plain `astro build` (which never sets it) is always a full + * build, even with a stale state dir on disk. + * + * Always-full pages: routes WITHOUT a selectPaths-wired getStaticPaths are + * (cheaply) re-emitted on every run — including static file endpoints, which + * therefore always stay in sync with whatever data change triggered the run. + */ +import { spawnSync } from 'node:child_process'; +import { createHash } from 'node:crypto'; +import { + closeSync, + cpSync, + existsSync, + mkdirSync, + openSync, + readFileSync, + readSync, + readdirSync, + renameSync, + rmSync, + statSync, + writeFileSync, +} from 'node:fs'; +import { + isAbsolute, + join, + relative, + resolve, + sep, +} from 'node:path'; + +// Relative (not aliased) so this file stays loadable by plain node — the +// runner is executed by scripts/incremental-build.mjs, outside vite. +import { sweepSupersededAssets } from '../../astro-dist-sweep/src/index.ts'; + +export interface CommandSpec { + cmd: string; + args: string[]; +} + +export interface SitemapConfig { + /** Default true when the object is provided. */ + enabled?: boolean; + /** Site origin, e.g. `https://example.org` (no trailing slash). */ + site: string; + /** Locale path prefixes used to group hreflang alternates, e.g. ['en','de']. */ + locales: string[]; + /** Locale attributed to unprefixed paths (e.g. the root `/`). */ + defaultLocale: string; +} + +export interface IncrementalBuildConfig { + /** App root the build runs from. Default `process.cwd()`. */ + root?: string; + /** Build output dir, relative to root. Default `dist`. */ + distDir?: string; + /** State dir (manifest, code hash, shards), relative to root. Gitignore it. Default `.astro-incremental`. */ + stateDir?: string; + /** + * The code/content inputs whose change must trigger a FULL rebuild: + * root-relative globs (`**` crosses `/`; `../` prefixes allowed) plus + * optional excludes. Include everything that affects rendered output + * EXCEPT the data axis (which selectPaths tracks per page via propsHash) + * and build artifacts. + */ + codeHash: { include: string[]; exclude?: string[] }; + /** + * Dist-relative dir patterns whose child dirs are per-record pages that + * may be deleted between builds — e.g. star, "projects", star (every + * locale's per-project dirs; see the README for the literal spelling). + * Segments may contain `*` and `?`; `**` is unsupported here. A matched + * dir whose dist-relative path is not a manifest key is removed after + * the merge. Omit to skip pruning. + */ + prune?: { routes: string[] }; + /** + * Regenerate the sitemap over the merged tree — REQUIRED when the site + * uses @astrojs/sitemap, otherwise the fresh build's sliver of a sitemap + * clobbers the cached full one. `false` to skip. + */ + sitemap?: SitemapConfig | false; + /** Build command. Default `{ cmd: 'astro', args: ['build'] }` (resolved via node_modules/.bin). */ + buildCommand?: CommandSpec; + /** Optional command to run before the build (e.g. an image-prefetch script). */ + prefetch?: CommandSpec; +} + +/** Minimal glob → RegExp: `**` crosses `/`, `*` and `?` don't. */ +function globToRegExp(glob: string): RegExp { + let out = ''; + let i = 0; + while (i < glob.length) { + const c = glob[i]; + if (c === '*') { + if (glob[i + 1] === '*') { + if (glob[i + 2] === '/') { + out += '(?:.*/)?'; + i += 3; + } else { + out += '.*'; + i += 2; + } + } else { + out += '[^/]*'; + i += 1; + } + } else if (c === '?') { + out += '[^/]'; + i += 1; + } else { + out += c.replace(/[.+^${}()|[\]\\]/, '\\$&'); + i += 1; + } + } + return new RegExp(`^${out}$`); +} + +const hasGlobChars = (s: string) => /[*?]/.test(s); + +function toPosix(p: string): string { + return p.split(sep).join('/'); +} + +function collectFiles(dir: string, acc: string[]): string[] { + for (const entry of readdirSync(dir, { withFileTypes: true })) { + const full = join(dir, entry.name); + if (entry.isDirectory()) { + collectFiles(full, acc); + } else if (entry.isFile()) { + acc.push(full); + } + } + return acc; +} + +function isDir(p: string): boolean { + try { + return statSync(p).isDirectory(); + } catch { + return false; + } +} + +export function runIncrementalBuild(config: IncrementalBuildConfig): void { + const ROOT = resolve(config.root ?? process.cwd()); + const DIST = join(ROOT, config.distDir ?? 'dist'); + const DIST_CACHE = `${DIST}-cache`; + const INCR_DIR = isAbsolute(config.stateDir ?? '') + ? (config.stateDir as string) + : join(ROOT, config.stateDir ?? '.astro-incremental'); + const SHARD_DIR = join(INCR_DIR, 'shards'); + const MANIFEST_PATH = join(INCR_DIR, 'manifest.json'); + const CODE_HASH_PATH = join(INCR_DIR, 'code-hash.txt'); + const buildCommand = config.buildCommand ?? { cmd: 'astro', args: ['build'] }; + const sitemapConfig = config.sitemap === false || config.sitemap === undefined + ? undefined + : config.sitemap; + const sitemapEnabled = sitemapConfig !== undefined && (sitemapConfig.enabled ?? true); + + const startTime = process.hrtime.bigint(); + const elapsed = () => `${(Number(process.hrtime.bigint() - startTime) / 1e9).toFixed(1)}s`; + const log = (msg: string) => process.stdout.write(`[incremental-build +${elapsed()}] ${msg}\n`); + + function bin(name: string): string { + const local = join(ROOT, 'node_modules', '.bin', name); + return existsSync(local) ? local : name; + } + + function run(command: CommandSpec, extraEnv: Record = {}): void { + const resolved = command.cmd === 'node' ? 'node' : bin(command.cmd); + log(`$ ${command.cmd} ${command.args.join(' ')}`); + const before = process.hrtime.bigint(); + const result = spawnSync(resolved, command.args, { + cwd: ROOT, + stdio: 'inherit', + env: { ...process.env, ...extraEnv }, + shell: false, + }); + log(` └ ${command.cmd} took ${(Number(process.hrtime.bigint() - before) / 1e9).toFixed(1)}s`); + if (result.status !== 0) { + throw new Error(`${command.cmd} exited with ${result.status ?? result.signal}`); + } + } + + // ----------------------------------------------------------------------- + // codeHash — hash of every configured code/content input. Files are + // labelled with their ROOT-relative posix path so the hash is + // machine-independent (and stable for `../`-reaching inputs). + // ----------------------------------------------------------------------- + function computeCodeHash(): string { + const excludeRes = (config.codeHash.exclude ?? []).map(globToRegExp); + const files = new Set(); + for (const pattern of config.codeHash.include) { + const segments = pattern.split('/'); + const staticSegs: string[] = []; + for (const seg of segments) { + if (hasGlobChars(seg)) { + break; + } + staticSegs.push(seg); + } + if (staticSegs.length === segments.length) { + // No glob chars: a literal file or directory. + const literal = join(ROOT, ...segments); + if (isDir(literal)) { + collectFiles(literal, []).forEach((f) => files.add(f)); + } else if (existsSync(literal)) { + files.add(literal); + } + continue; + } + const base = join(ROOT, ...staticSegs); + if (!isDir(base)) { + continue; + } + const re = globToRegExp(pattern); + for (const file of collectFiles(base, [])) { + if (re.test(toPosix(relative(ROOT, file)))) { + files.add(file); + } + } + } + + const sorted = [...files] + .filter((f) => !excludeRes.some((re) => re.test(toPosix(relative(ROOT, f))))) + .sort(); + const hash = createHash('sha1'); + for (const file of sorted) { + hash.update(toPosix(relative(ROOT, file))); + hash.update('\0'); + hash.update(readFileSync(file)); + hash.update('\0'); + } + return hash.digest('hex'); + } + + // ----------------------------------------------------------------------- + // helpers for merge + prune + // ----------------------------------------------------------------------- + function readShards(): Record { + const merged: Record = {}; + if (!existsSync(SHARD_DIR)) { + return merged; + } + for (const name of readdirSync(SHARD_DIR)) { + if (!name.endsWith('.json')) { + continue; + } + const shard = JSON.parse(readFileSync(join(SHARD_DIR, name), 'utf8')); + Object.assign(merged, shard); + } + return merged; + } + + /** Expand one prunable-route pattern into existing dist dirs (dist-relative keys). */ + function expandRoutePattern(pattern: string): string[] { + const segments = pattern.split('/').filter(Boolean); + let level: string[] = ['']; + for (const seg of segments) { + const re = globToRegExp(seg); + const nextLevel: string[] = []; + for (const relDir of level) { + const abs = join(DIST, relDir); + if (!isDir(abs)) { + continue; + } + for (const entry of readdirSync(abs, { withFileTypes: true })) { + if (entry.isDirectory() && re.test(entry.name)) { + nextLevel.push(relDir === '' ? entry.name : `${relDir}/${entry.name}`); + } + } + } + level = nextLevel; + } + return level; + } + + /** + * Remove per-record page directories (matching the configured prunable + * route patterns) that are no longer in the current path set (deleted + * records). Keys match selectPaths candidate keys, e.g. + * `en/projects/`. Anything outside the patterns is never touched. + */ + function pruneDeletions(validKeys: Set): number { + if (!isDir(DIST) || !config.prune) { + return 0; + } + let removed = 0; + for (const pattern of config.prune.routes) { + for (const key of expandRoutePattern(pattern)) { + if (!validKeys.has(key)) { + rmSync(join(DIST, ...key.split('/')), { recursive: true, force: true }); + removed += 1; + } + } + } + return removed; + } + + // ----------------------------------------------------------------------- + // sitemap regeneration over the MERGED tree + // + // On the merge path the fresh build's sitemap-0.xml covers ONLY the pages + // rendered this run (e.g. 13 URLs after a 1-record change) and, having + // been copied over the cached full sitemap, would silently shrink the + // published one to a sliver. So after merging we regenerate it from the + // merged dist itself, emitting the exact structure @astrojs/sitemap + // produces with its i18n option (verified structurally identical): + // - one per page dir's index.html, + // - pages grouped by path-minus-locale-prefix; every member of a group + // carries the SAME list + // (all members, href-sorted; no x-default), + // - locale-less pages (the root /) count as the default locale — the + // root group then has hreflang "" twice, matching + // @astrojs/sitemap, + // - meta-refresh redirect stubs are EXCLUDED (astro excludes redirect + // routes); the root / — also a meta-refresh page but a real page + // route — is INCLUDED, again matching astro, + // - 404.html is naturally excluded (not an index.html). + // ----------------------------------------------------------------------- + + /** True when the file starts as a meta-refresh redirect page. */ + function isMetaRefreshStub(filePath: string): boolean { + const fd = openSync(filePath, 'r'); + try { + const buf = Buffer.alloc(512); + const n = readSync(fd, buf, 0, 512, 0); + return buf.toString('utf8', 0, n).includes('http-equiv="refresh"'); + } finally { + closeSync(fd); + } + } + + function regenerateSitemap({ site, locales, defaultLocale }: SitemapConfig): number { + // 1. Collect every page URL from the merged dist. + const urlPaths: string[] = []; + for (const entry of readdirSync(DIST, { recursive: true })) { + const rel = String(entry); + if (rel !== 'index.html' && !rel.endsWith(`${sep}index.html`)) { + continue; + } + const dir = rel === 'index.html' ? '' : rel.slice(0, -`${sep}index.html`.length); + const urlPath = dir === '' ? '/' : `/${toPosix(dir)}/`; + // Redirect stubs are excluded from the sitemap; the root '/' + // (also a meta-refresh page, but a page route) is included. + if (urlPath !== '/' && isMetaRefreshStub(join(DIST, rel))) { + continue; + } + urlPaths.push(urlPath); + } + urlPaths.sort(); + + // 2. Group by path-minus-locale-prefix; hreflang from the prefix (or + // the default locale for unprefixed paths like '/'). + const groups = new Map(); + for (const urlPath of urlPaths) { + const [first, ...rest] = urlPath.slice(1).split('/'); + const isLocale = locales.includes(first); + const groupKey = isLocale ? `/${rest.join('/')}` : urlPath; + const hreflang = isLocale ? first : defaultLocale; + if (!groups.has(groupKey)) { + groups.set(groupKey, []); + } + groups.get(groupKey)!.push({ urlPath, hreflang }); + } + + // 3. Emit (single line, same as @astrojs/sitemap). + const loc = (urlPath: string) => `${site}${urlPath}`; + const parts: string[] = []; + for (const urlPath of urlPaths) { + const [first, ...rest] = urlPath.slice(1).split('/'); + const groupKey = locales.includes(first) ? `/${rest.join('/')}` : urlPath; + const group = groups.get(groupKey)!; + const alternates = group.length > 1 + ? group + .map(({ urlPath: p, hreflang }) => ( + `` + )) + .join('') + : ''; + parts.push(`${loc(urlPath)}${alternates}`); + } + const urlset = '' + + '' + + `${parts.join('')}`; + writeFileSync(join(DIST, 'sitemap-0.xml'), urlset); + writeFileSync( + join(DIST, 'sitemap-index.xml'), + '' + + '' + + `${site}/sitemap-0.xml`, + ); + return urlPaths.length; + } + + // ----------------------------------------------------------------------- + // main + // ----------------------------------------------------------------------- + + // 1. codeHash → file (read by getStaticPaths via selectPaths). + mkdirSync(INCR_DIR, { recursive: true }); + rmSync(SHARD_DIR, { recursive: true, force: true }); + const codeHash = computeCodeHash(); + writeFileSync(CODE_HASH_PATH, codeHash); + + let prevManifest: { codeHash: string; paths: Record } = { codeHash: '', paths: {} }; + try { + prevManifest = JSON.parse(readFileSync(MANIFEST_PATH, 'utf8')); + } catch { + // cold build + } + const codeChanged = prevManifest.codeHash !== codeHash; + log(`codeHash ${codeHash}${codeChanged ? ' (changed → full rebuild)' : ' (unchanged → data-only)'}`); + + // 2. Move the restored previous dist aside so the build starts clean. + rmSync(DIST_CACHE, { recursive: true, force: true }); + if (existsSync(DIST)) { + renameSync(DIST, DIST_CACHE); + log(`moved previous ${config.distDir ?? 'dist'}/ → ${config.distDir ?? 'dist'}-cache/`); + } else { + log(`no previous ${config.distDir ?? 'dist'}/ (cold build)`); + } + + // 3. Optional prefetch, then build only the changed/new paths. + // ASTRO_INCREMENTAL=1 is what arms selectPaths — without it (any plain + // `astro build`) every getStaticPaths returns all its candidates. + if (config.prefetch) { + run(config.prefetch); + } + run(buildCommand, { + ASTRO_INCREMENTAL: '1', + ASTRO_INCREMENTAL_STATE_DIR: INCR_DIR, + }); + + // 4. Merge. On a full rebuild (codeChanged) the fresh tree already + // contains every current page — just discard the cache. On a data-only + // build the fresh tree is small while the cache holds ~every page, so + // overlay the SMALL fresh tree onto the LARGE cached one (fresh wins) + // via a copy into the cache, then rename the merged cache back — + // keeping the unchanged files in place. + const manifestPaths = readShards(); + if (codeChanged) { + rmSync(DIST_CACHE, { recursive: true, force: true }); + log('full rebuild — fresh dist is authoritative, skipped merge/prune/sitemap'); + } else if (existsSync(DIST_CACHE)) { + cpSync(DIST, DIST_CACHE, { recursive: true, force: true }); + rmSync(DIST, { recursive: true, force: true }); + renameSync(DIST_CACHE, DIST); + log('merged fresh dist onto cached tree'); + + // 5. Prune deleted records from the merged tree. + const validKeys = new Set(Object.keys(manifestPaths)); + const prunedCount = pruneDeletions(validKeys); + log(`pruned ${prunedCount} deleted page dir(s); ${validKeys.size} tracked pages`); + + // 6. The fresh build's sitemap only lists the pages rendered THIS run + // and just clobbered the cached full one — regenerate it over the + // merged tree. + if (sitemapEnabled && sitemapConfig) { + const urlCount = regenerateSitemap(sitemapConfig); + log(`regenerated sitemap over merged tree (${urlCount} URLs)`); + } + } else { + log('no cached dist — fresh dist kept as-is (cold build)'); + } + + // 7. Sweep _astro files nothing references — on every path: even a fresh + // full build carries the always-emitted image originals. + const swept = sweepSupersededAssets(DIST); + log(`swept ${swept.removed} unreferenced _astro asset(s) (${(swept.bytes / 1e6).toFixed(1)} MB)`); + + // 8. Persist the new manifest for the next run. + writeFileSync(MANIFEST_PATH, JSON.stringify({ codeHash, paths: manifestPaths })); + rmSync(DIST_CACHE, { recursive: true, force: true }); + log('done'); +} diff --git a/packages/astro-remote-images/README.md b/packages/astro-remote-images/README.md new file mode 100644 index 00000000..1b9a4a98 --- /dev/null +++ b/packages/astro-remote-images/README.md @@ -0,0 +1,160 @@ +# astro-remote-images + +Build-time, **per-image, fail-soft** remote image optimizer for Astro static +builds. Owns the fetch + [sharp](https://sharp.pixelplumbing.com/) pipeline so +that every image is individually try/caught: one malformed or unreachable +remote image can never abort your 14k-page build. Comes with an on-disk cache, +a bounded concurrent prefetch pool, a two-tier negative cache, and a +configurable per-image **error policy** (error image / passthrough / fail). + +## Why not `astro:assets` / ``? + +Astro's optimize phase is all-or-nothing for remote images: a single corrupt +image (e.g. a broken SVG mislabeled as raster) makes sharp throw fatally and +aborts the entire build, uncatchable per-image (verified against production +data containing 8 such images). And naive render-time fetching serializes +thousands of downloads (measured: 27 min cold vs ~90 s with this package's +prefetch pool). No upstream issue covers a per-image error policy (closest: +[withastro/roadmap#523](https://github.com/withastro/roadmap/discussions/523), +[withastro/astro#15920](https://github.com/withastro/astro/issues/15920)). +Delete this package when an upstream error policy lands. + +## Install + +Part of this repo as a plain directory (imported by relative path); copy the directory or publish to npm to use elsewhere. +same npm workspace: + +```jsonc +// package.json +{ + "workspaces": ["packages/*"], + "dependencies": { "astro-remote-images": "*" } +} +``` + +For a third-party project, copy the package into your workspace (or publish +it). Only dependency: `sharp`. Requires Node ≥ 22.18 (TypeScript entry point, +loaded via Node's built-in type stripping; Vite-based consumers transform it +natively). + +## Usage + +**1. Configure one instance for your app** (a small module both your pages and +your prefetch script import): + +```ts +// src/lib/remoteImages.ts +import { createRemoteImages } from 'astro-remote-images'; + +export const COVER_WIDTH = 640; + +export const remoteImages = createRemoteImages({ + width: COVER_WIDTH, + onError: 'error-image', // permanent failures (corrupt / 4xx) + onTransientError: 'error-image', // timeouts / 5xx / network +}); +``` + +The **last** `createRemoteImages()` call also becomes the *default instance* +used by the bundled `` component — make sure your config module +is imported before the component renders (importing it from the same file +that imports the component is enough). + +**2. Render images** — either through the bundled component: + +```astro +--- +import '../lib/remoteImages'; // config first +import RemoteImage from 'astro-remote-images/RemoteImage.astro'; +--- + +``` + +or by calling `resolve()` directly (e.g. to build a URL map inside an +endpoint): + +```ts +const { src, optimized } = await remoteImages.resolve(url, { width: 640 }); +``` + +**3. Prefetch before the build** so renders are pure cache hits +(`node scripts/prefetch-images.mjs && astro build`): + +```js +// scripts/prefetch-images.mjs +import { remoteImages } from '../src/lib/remoteImages.ts'; + +// You own the collector: return every remote URL the build will render. +async function collectUrls() { /* read your data source */ } + +remoteImages.prefetch(collectUrls).catch((err) => { + console.warn('[prefetch] non-fatal error, continuing to build:', err); + process.exit(0); // prefetch is a cache warm, never a build blocker +}); +``` + +## API + +```ts +createRemoteImages(options?): RemoteImages +// -> { options, resolve(url, {width?}), prefetch(urls | () => urls, {width?}), cacheSize() } +getDefaultRemoteImages(): RemoteImages // last created (or lazy bare-defaults) +encodeImageBuffer(input, width, format?, quality?) // the raw sharp encode rule +``` + +`resolve()` returns `{ src, optimized, error? }` — `src` is the local +optimized path, the error image, or the original remote URL depending on +outcome and policy. It never throws unless the matching policy is `'fail'`. +`prefetch()` never throws (a `'fail'` policy fires at resolve time, not +during the warm). + +## Options + +| Option | Type | Default | Description | +| --- | --- | --- | --- | +| `width` | `number` | `640` | Default render/prefetch width (px, never upscaled). | +| `widths` | `number[]` | `[width]` | Widths the prefetch pool warms per URL. | +| `quality` | `number` | `80` | Encoder quality. **Not part of the cache key** — wipe `cacheDir` when changing it. | +| `format` | `'webp' \| 'avif' \| 'jpeg' \| 'png'` | `'webp'` | Output format (part of the cache key). | +| `root` | `string` | `process.cwd()` | Anchor for all relative paths (the app dir the build runs from). | +| `cacheDir` | `string` | `'.image-cache'` | Persistent on-disk cache. Persist across CI builds (see below). | +| `distDir` | `string` | `'dist'` | Astro build output dir optimized files are published into. | +| `publicPrefix` | `string` | `'/_img'` | Public URL prefix (and folder under `distDir`). | +| `concurrency` | `number` | `24` | Prefetch pool size. | +| `fetchTimeoutMs` | `number` | `15000` | Per-image fetch timeout. | +| `transientRetryAfterMs` | `number` | 6 h | Retry-after TTL for transient failures (`.slow` markers). | +| `permanentTtlMs` | `number` | `Infinity` | TTL for permanent failures (`.miss` markers). | +| `allowedHosts` | `string[]` | off | Optional URL-host allow-list; other hosts are treated as permanent failures without fetching. | +| `onError` | `'error-image' \| 'passthrough' \| 'fail'` | `'error-image'` | Policy for **permanent** failures (corrupt data, 4xx). | +| `onTransientError` | same | `'passthrough'` | Policy for **transient** failures (timeout / 5xx / network). Default passthrough: the browser may still load a merely-slow origin. Set `'error-image'` for deterministic output — but then a chronically slow origin shows the placeholder even though the image exists. | +| `errorImage` | `string` | bundled SVG | Public URL path of a custom error image you ship yourself (e.g. under `public/`). The bundled default is a neutral, text-free "image unavailable" SVG published once as `/image-error.svg`. | + +Every substitution (error image or passthrough) logs +`[remote-images] (): ` — once per image per process. + +## Caching model + +- **Positive cache**: `/.` — the + encoded output; publish into `dist` is a cheap copy. Persist this directory + in CI (e.g. `actions/cache`) so warm builds skip every fetch+encode. + **CI cache key implication:** the key contains url+width+format, so + changing `width`/`format` (or `quality`, which is keyless) starts a full + re-encode; the old entries are dead weight until the CI cache is rotated. +- **Negative cache**, two tiers of marker next to the would-be output, each + storing the failure reason: + - `.miss` — permanent (corrupt data / 4xx). Skipped for `permanentTtlMs` + (default: forever, until the cache dir is wiped). + - `.slow` — transient (timeout / network / 5xx). Skipped until + `transientRetryAfterMs` elapses, then retried — a chronically-slow origin + stops costing a fetch timeout on every build while a genuine blip still + recovers. +- **In-process negative cache** on top, so one bad URL doesn't re-pay the + timeout once per locale within a single build. + +## Notes + +- Animated GIFs are re-encoded as **animated** webp (every frame resized). +- Build-time only (Node). Never import from client/island code. +- The error-image SVG is inlined in the module (not read from a file) so it + survives Vite bundling into the SSR build. + diff --git a/packages/astro-remote-images/package.json b/packages/astro-remote-images/package.json new file mode 100644 index 00000000..74519379 --- /dev/null +++ b/packages/astro-remote-images/package.json @@ -0,0 +1,29 @@ +{ + "name": "astro-remote-images", + "version": "0.1.0", + "description": "Build-time, per-image, fail-soft remote image optimizer for Astro static builds: on-disk cache, bounded concurrent prefetch, two-tier negative cache, configurable error policy (error image / passthrough / fail).", + "type": "module", + "private": true, + "exports": { + ".": "./src/index.ts", + "./RemoteImage.astro": "./src/RemoteImage.astro" + }, + "files": [ + "src", + "README.md" + ], + "keywords": [ + "astro", + "images", + "remote-images", + "sharp", + "fail-soft", + "static" + ], + "engines": { + "node": ">=22.18" + }, + "dependencies": { + "sharp": "^0.33.5" + } +} diff --git a/packages/astro-remote-images/src/RemoteImage.astro b/packages/astro-remote-images/src/RemoteImage.astro new file mode 100644 index 00000000..9c79ee68 --- /dev/null +++ b/packages/astro-remote-images/src/RemoteImage.astro @@ -0,0 +1,45 @@ +--- +import { getDefaultRemoteImages } from './index.ts'; + +// Remote image, optimized at BUILD time via the fail-soft resolver (see +// ./index.ts). Bypasses Astro's /astro:assets because its optimize +// phase is all-or-nothing: one malformed remote image makes sharp throw +// fatally and aborts the whole build. resolve() wraps each fetch+sharp in +// try/catch and applies the configured error policy (error image / +// passthrough / fail) — so a single bad image can never break the build +// unless you opted into 'fail'. Good images are resized to `width`, encoded, +// cached on disk, and shipped locally. +// +// Uses the DEFAULT instance (the last createRemoteImages() call). Make sure +// the module that configures your instance is imported before this component +// renders — e.g. import it from the same file that imports this component. +interface Props { + src: string; + alt: string; + class?: string; + width?: number; + height?: number; +} +const remoteImages = getDefaultRemoteImages(); +const { + src, + alt, + class: className, + width = remoteImages.options.width, + height = 420, +} = Astro.props; + +// Explicit width (inferSize:false semantics) so there is no render-time +// network fetch just to read dimensions. +const { src: resolvedSrc, optimized } = await remoteImages.resolve(src, { width }); +--- +{alt} diff --git a/packages/astro-remote-images/src/index.ts b/packages/astro-remote-images/src/index.ts new file mode 100644 index 00000000..8bc5e81f --- /dev/null +++ b/packages/astro-remote-images/src/index.ts @@ -0,0 +1,537 @@ +// astro-remote-images — build-time, per-image, fail-soft remote image +// optimizer for Astro static builds, with an on-disk cache, a bounded +// concurrent prefetch pool, a two-tier negative cache, and a configurable +// per-image error policy (error image / passthrough / fail). +// +// UPSTREAM: no existing Astro issue covers a per-image error policy (as of +// 2026-07). Closest existing refs: +// https://github.com/withastro/roadmap/discussions/523 (global error handling) +// https://github.com/withastro/astro/issues/15920 (remote revalidation, adjacent) +// Delete this package when an upstream error policy lands. +// +// Why this exists (and why it bypasses Astro's /astro:assets): +// Astro's optimize phase is all-or-nothing — a single malformed remote image +// (e.g. a broken SVG mislabeled as raster) makes sharp throw fatally and +// aborts the entire build (rc=1), uncatchable per-image. Here we own the +// fetch + sharp pipeline so each image is wrapped in try/catch and fails soft +// per the configured policy while the good ones are optimized + cached. +// +// Runs at build time only (Node). Not for client bundles. +import { createHash } from 'node:crypto'; +import { + access, + copyFile, + mkdir, + readFile, + readdir, + stat, + writeFile, +} from 'node:fs/promises'; +import { existsSync } from 'node:fs'; +import { join } from 'node:path'; +import sharp from 'sharp'; + +export type ErrorPolicy = 'error-image' | 'passthrough' | 'fail'; +export type ImageFormat = 'webp' | 'avif' | 'jpeg' | 'png'; + +export interface RemoteImagesOptions { + /** Default render/prefetch width (px, never upscaled). Default 640. */ + width?: number; + /** Widths the prefetch pool warms per URL. Default `[width]`. */ + widths?: number[]; + /** Encoder quality. Default 80. NOTE: not part of the cache key — changing it requires wiping `cacheDir`. */ + quality?: number; + /** Output format. Default `'webp'`. Part of the cache key. */ + format?: ImageFormat; + /** + * Anchor for all relative paths. Default `process.cwd()` — the app root + * the build runs from (deliberately NOT import.meta.url: after Vite + * bundles this module the emitted file's location is meaningless). + */ + root?: string; + /** Persistent on-disk cache dir (persist across CI builds). Default `.image-cache`. */ + cacheDir?: string; + /** Astro build output dir the optimized files are published into. Default `dist`. */ + distDir?: string; + /** Public URL prefix the browser requests (also the folder under distDir). Default `/_img`. */ + publicPrefix?: string; + /** Prefetch pool size (concurrent in-flight fetches). Default 24. */ + concurrency?: number; + /** Per-image fetch timeout. Prevents one hung origin from stalling the build. Default 15000. */ + fetchTimeoutMs?: number; + /** + * Retry-after TTL for TRANSIENT failures (timeout / network / 5xx), + * recorded as `.slow` markers: the URL is not refetched until the TTL + * elapses. Default 6 h. Without it a chronically-slow origin re-pays the + * full fetch timeout on EVERY build. + */ + transientRetryAfterMs?: number; + /** + * TTL for PERMANENT failures (corrupt data / 4xx), recorded as `.miss` + * markers. Default `Infinity` (never refetched until the cache dir is + * wiped). + */ + permanentTtlMs?: number; + /** Optional allow-list of URL hosts. A URL on another host is treated as a permanent failure (no fetch). */ + allowedHosts?: string[]; + /** Policy for PERMANENT failures (corrupt data, 4xx). Default `'error-image'`. */ + onError?: ErrorPolicy; + /** + * Policy for TRANSIENT failures (timeout / network / 5xx). Default + * `'passthrough'` — the original remote URL is served, so the browser may + * still load it even though the build-time fetch failed. Set + * `'error-image'` for deterministic output at the cost of hiding images + * from merely-slow origins. + */ + onTransientError?: ErrorPolicy; + /** + * Public URL path of a custom error image you ship yourself (e.g. under + * `public/`). Default: a bundled neutral SVG placeholder published once + * into `/image-error.svg`. + */ + errorImage?: string; +} + +export interface ResolveOptions { + width?: number; +} + +export interface ResolvedImage { + /** URL to render in : local optimized path, error image, or original remote URL. */ + src: string; + /** True when a locally optimized file is served. */ + optimized: boolean; + /** Failure reason when the image could not be optimized. */ + error?: string; +} + +export interface PrefetchSummary { + total: number; + optimized: number; + failed: number; + seconds: number; +} + +export interface RemoteImages { + /** Resolved options (defaults applied). */ + options: Required> + & Pick; + /** + * Resolve a remote URL to a build-time optimized local file, applying the + * configured error policy on failure. Never throws unless the matching + * policy is `'fail'`. + */ + resolve(url: string | null | undefined, opts?: ResolveOptions): Promise; + /** + * Warm the on-disk cache for many URLs with a bounded pool, WITHOUT + * publishing into the build output (that happens per-page at render + * time). Never throws — prefetch is a cache warm and must never fail the build; a + * `'fail'` policy only fires at resolve time. + */ + prefetch(urls: string[] | (() => string[] | Promise), opts?: ResolveOptions): Promise; + /** Number of cached optimized images (diagnostic; safe if dir absent). */ + cacheSize(): Promise; +} + +// Bundled default error image: a neutral "image unavailable" placeholder +// (broken-frame iconography, no locale-dependent text). Inlined as a string — +// NOT read from a file next to this module — so it survives Vite bundling +// (import.meta.url-relative paths break once the module is emitted into the +// SSR bundle). +export const ERROR_IMAGE_FILENAME = 'image-error.svg'; +const ERROR_IMAGE_SVG = ` + + + + + + + + +`; + +const MARKER_EXTS = ['miss', 'slow'] as const; +type MarkerExt = (typeof MARKER_EXTS)[number]; + +// True for failures that are the image's fault, not the network's — a corrupt +// or empty buffer, or a 4xx. sharp throws these with recognizable messages. +function isDeterministicFailure(reason: string): boolean { + return ( + reason.startsWith('HTTP 4') + || reason.includes('Input buffer') + || reason.includes('Input Buffer') + || reason.includes('unsupported image format') + || reason.includes('VipsForeignLoad') + || reason.includes('host not allowed') + ); +} + +/** + * Shared sharp encode: resize to `width` (never upscale) + encode. `input` + * may be a Buffer (remote) or a file path. Animated inputs (GIF, multi-page) + * are decoded with { animated: true } and re-encoded animated (webp) so + * animation survives the resize. Returns the encoded buffer. + */ +export async function encodeImageBuffer( + input: Buffer | string, + width: number, + format: ImageFormat = 'webp', + quality = 80, +): Promise { + // Probe for animation. A multi-page (animated) source must be opened with + // { animated: true } or sharp collapses it to the first frame. + let animated = false; + try { + const meta = await sharp(input).metadata(); + animated = (meta.pages ?? 1) > 1 || meta.format === 'gif'; + } catch { + // If probing fails, treat as a normal static image; the encode below + // will surface any real decode error to the caller (fail-soft there). + } + return sharp(input, animated ? { animated: true } : undefined) + .resize({ width, withoutEnlargement: true }) + .toFormat(format, { quality }) + .toBuffer(); +} + +export function createRemoteImages(userOptions: RemoteImagesOptions = {}): RemoteImages { + const width = userOptions.width ?? 640; + const options: RemoteImages['options'] = { + width, + widths: userOptions.widths ?? [width], + quality: userOptions.quality ?? 80, + format: userOptions.format ?? 'webp', + root: userOptions.root ?? process.cwd(), + cacheDir: userOptions.cacheDir ?? '.image-cache', + distDir: userOptions.distDir ?? 'dist', + publicPrefix: userOptions.publicPrefix ?? '/_img', + concurrency: userOptions.concurrency ?? 24, + fetchTimeoutMs: userOptions.fetchTimeoutMs ?? 15000, + transientRetryAfterMs: userOptions.transientRetryAfterMs ?? 6 * 60 * 60 * 1000, + permanentTtlMs: userOptions.permanentTtlMs ?? Infinity, + onError: userOptions.onError ?? 'error-image', + onTransientError: userOptions.onTransientError ?? 'passthrough', + allowedHosts: userOptions.allowedHosts, + errorImage: userOptions.errorImage, + }; + + const CACHE_DIR = join(options.root, options.cacheDir); + // Published directly into distDir (public/ would be copied BEFORE static + // route generation, too early for render-time-produced files). + const OUT_IMG_DIR = join(options.root, options.distDir, ...options.publicPrefix.split('/').filter(Boolean)); + const PUBLIC_URL = options.publicPrefix.replace(/\/$/, ''); + const ext = options.format; + + let dirsReady: Promise | null = null; + function ensureDirs(): Promise { + if (!dirsReady) { + dirsReady = (async () => { + await mkdir(CACHE_DIR, { recursive: true }); + await mkdir(OUT_IMG_DIR, { recursive: true }); + })(); + } + return dirsReady; + } + + // Cache key: sha1 of url|width|format. Quality is deliberately NOT part + // of the key (changing it requires a cache wipe — documented) so the + // layout stays byte-compatible with pre-package caches (url|w=N|webp). + function cacheKey(url: string, w: number): string { + return createHash('sha1').update(`${url}|w=${w}|${options.format}`).digest('hex'); + } + + // Track keys already published into dist/ this process, so concurrent + // page renders don't redundantly copy. + const published = new Set(); + // In-process negative cache: keys that failed THIS process. Within one + // build a URL renders across every locale — without this each locale + // would re-pay the full fetch timeout. Process-scoped, so a transient + // failure is retried on the next build. Maps key -> { reason, permanent }. + const failedThisProcess = new Map(); + // Warn exactly once per key per process (a URL substitutes identically + // across locales — one line is signal, six are noise). + const warned = new Set(); + + // Two-tier persistent negative cache, marker files next to the would-be + // encoded file: + // .miss — PERMANENT (corrupt data / 4xx): skipped for permanentTtlMs + // (default forever, until the cache dir is wiped). + // .slow — TRANSIENT (timeout / network / 5xx): skipped until + // transientRetryAfterMs elapses (marker mtime), then retried. + // Both let render passes short-circuit WITHOUT a refetch. The marker body + // stores the original failure reason for later warnings. + async function writeMarker(key: string, kind: MarkerExt, reason: string): Promise { + await writeFile(join(CACHE_DIR, `${key}.${kind}`), reason); + } + + async function readNegativeCache( + key: string, + ): Promise<{ reason: string; permanent: boolean } | undefined> { + for (const kind of MARKER_EXTS) { + const marker = join(CACHE_DIR, `${key}.${kind}`); + try { + const st = await stat(marker); + const ttl = kind === 'miss' ? options.permanentTtlMs : options.transientRetryAfterMs; + if (Date.now() - st.mtimeMs < ttl) { + let reason = ''; + try { + reason = (await readFile(marker, 'utf8')).trim(); + } catch { + // legacy empty marker + } + return { + reason: reason || `previous ${kind === 'miss' ? 'permanent' : 'transient'} failure (cached)`, + permanent: kind === 'miss', + }; + } + } catch { + // no marker of this kind + } + } + return undefined; + } + + let errorImageReady: Promise | null = null; + // Publish the bundled error image once into the build output and return + // its public URL. A user-supplied errorImage path is returned as-is (the + // user ships that file, e.g. from public/). + function ensureErrorImage(): Promise { + if (options.errorImage) { + return Promise.resolve(options.errorImage); + } + if (!errorImageReady) { + errorImageReady = (async () => { + await ensureDirs(); + const out = join(OUT_IMG_DIR, ERROR_IMAGE_FILENAME); + if (!existsSync(out)) { + await writeFile(out, ERROR_IMAGE_SVG); + } + return `${PUBLIC_URL}/${ERROR_IMAGE_FILENAME}`; + })(); + } + return errorImageReady; + } + + // Apply the configured error policy for a failed image. ALWAYS warns + // (once per key per process) with URL + reason before substituting. + async function applyFailurePolicy( + url: string, + key: string, + reason: string, + permanent: boolean, + ): Promise { + const policy = permanent ? options.onError : options.onTransientError; + if (!warned.has(key)) { + warned.add(key); + + console.warn(`[remote-images] ${policy} (${reason}): ${url}`); + } + if (policy === 'fail') { + throw new Error(`[remote-images] failed to optimize ${url}: ${reason}`); + } + if (policy === 'error-image') { + const src = await ensureErrorImage(); + return { src, optimized: false, error: reason }; + } + return { src: url, optimized: false, error: reason }; + } + + // Cold path shared by the per-page resolver and the prefetch pool: fetch + // the remote image, resize + encode, write into the on-disk cache. NEVER + // throws — on failure it records the negative-cache marker and returns + // the classified failure. Single source of truth for fetch+sharp+cache. + async function encodeToCache( + url: string, + w: number, + key: string, + ): Promise<{ ok: true } | { ok: false; reason: string; permanent: boolean }> { + const cacheFile = join(CACHE_DIR, `${key}.${ext}`); + try { + await ensureDirs(); + if (options.allowedHosts) { + const host = new URL(url).host; + if (!options.allowedHosts.includes(host)) { + throw new Error(`host not allowed: ${host}`); + } + } + // Hard timeout: one slow/hung origin must not stall the build + // (fetch has no default timeout). + const res = await fetch(url, { signal: AbortSignal.timeout(options.fetchTimeoutMs) }); + if (!res.ok) { + throw new Error(`HTTP ${res.status}`); + } + const input = Buffer.from(await res.arrayBuffer()); + // Explicit width (no inferSize): no render-time dimension fetch. + const output = await encodeImageBuffer(input, w, options.format, options.quality); + await writeFile(cacheFile, output); + return { ok: true }; + } catch (err) { + const reason = err instanceof Error ? err.message : String(err); + const permanent = isDeterministicFailure(reason); + // Best-effort marker write — a failed write just means a retry + // next build (still safe). + try { + await writeMarker(key, permanent ? 'miss' : 'slow', reason); + } catch { + // ignore + } + failedThisProcess.set(key, { reason, permanent }); + return { ok: false, reason, permanent }; + } + } + + async function publishFromCache(key: string): Promise { + if (published.has(key)) { + return; + } + const from = join(CACHE_DIR, `${key}.${ext}`); + const to = join(OUT_IMG_DIR, `${key}.${ext}`); + await ensureDirs(); + if (!existsSync(to)) { + await copyFile(from, to); + } + published.add(key); + } + + async function resolve( + url: string | null | undefined, + opts: ResolveOptions = {}, + ): Promise { + if (!url) { + return { src: '', optimized: false }; + } + const w = opts.width ?? options.width; + const key = cacheKey(url, w); + const cacheFile = join(CACHE_DIR, `${key}.${ext}`); + + await ensureDirs(); + + // Warm path: cached file exists (prefetch pool or a prior build/page + // encoded it) -> publish into the build output + return. + try { + await access(cacheFile); + await publishFromCache(key); + return { src: `${PUBLIC_URL}/${key}.${ext}`, optimized: true }; + } catch { + // not cached; fall through + } + + // Negative caches (in-process, then persistent): a prior attempt + // failed — apply the policy WITHOUT refetching. + const processFailure = failedThisProcess.get(key); + if (processFailure) { + return applyFailurePolicy(url, key, processFailure.reason, processFailure.permanent); + } + const diskFailure = await readNegativeCache(key); + if (diskFailure) { + return applyFailurePolicy(url, key, diskFailure.reason, diskFailure.permanent); + } + + // Cold path: same implementation the prefetch pool uses. + const result = await encodeToCache(url, w, key); + if (!result.ok) { + return applyFailurePolicy(url, key, result.reason, result.permanent); + } + await publishFromCache(key); + return { src: `${PUBLIC_URL}/${key}.${ext}`, optimized: true }; + } + + /** Cache warm for one url/width. Cache hit -> no-op. Never throws. */ + async function prefetchOne(url: string, w: number): Promise { + const key = cacheKey(url, w); + try { + await access(join(CACHE_DIR, `${key}.${ext}`)); + return true; // already cached + } catch { + // not cached; consult negative cache before spending a fetch + } + if (failedThisProcess.has(key) || (await readNegativeCache(key)) !== undefined) { + return false; // known-bad / recently-slow + } + const result = await encodeToCache(url, w, key); + if (!result.ok && !warned.has(key)) { + warned.add(key); + + console.warn(`[remote-images] prefetch failed (${result.reason}): ${url}`); + } + return result.ok; + } + + async function prefetch( + urlsOrFn: string[] | (() => string[] | Promise), + opts: ResolveOptions = {}, + ): Promise { + const start = Date.now(); + const raw = typeof urlsOrFn === 'function' ? await urlsOrFn() : urlsOrFn; + const urls = [...new Set(raw.filter(Boolean))]; + const widths = opts.width !== undefined ? [opts.width] : options.widths; + const jobs = urls.flatMap((url) => widths.map((w) => ({ url, w }))); + const total = jobs.length; + + console.log( + `[remote-images] prefetch: ${urls.length} unique URLs × ${widths.length} width(s), ` + + `concurrency=${options.concurrency}`, + ); + + let optimized = 0; + let failed = 0; + let next = 0; + async function worker() { + for (;;) { + const i = next; + next += 1; + if (i >= total) { + return; + } + const ok = await prefetchOne(jobs[i].url, jobs[i].w); + if (ok) { + optimized += 1; + } else { + failed += 1; + } + const done = optimized + failed; + if (done % 250 === 0 || done === total) { + + console.log(`[remote-images] prefetch ${done}/${total} (optimized=${optimized} failed=${failed})`); + } + } + } + await Promise.all(Array.from({ length: Math.min(options.concurrency, total) }, worker)); + + const seconds = (Date.now() - start) / 1000; + + console.log( + `[remote-images] prefetch done: ${optimized} optimized / ${failed} failed / ${total} total ` + + `in ${seconds.toFixed(1)}s`, + ); + return { total, optimized, failed, seconds }; + } + + async function cacheSize(): Promise { + try { + const files = await readdir(CACHE_DIR); + return files.filter((f) => f.endsWith(`.${ext}`)).length; + } catch { + return 0; + } + } + + const instance: RemoteImages = { options, resolve, prefetch, cacheSize }; + defaultInstance = instance; // last created instance is the component default + return instance; +} + +// Default-instance registry for the component: the component +// can't receive the factory instance as a prop ergonomically, so the LAST +// createRemoteImages() call becomes the default. Apps should create their +// configured instance in a module the component's consumers import (import +// order then guarantees configuration happens first). If no instance was ever +// created, a bare-defaults one is created lazily. +let defaultInstance: RemoteImages | undefined; + +export function getDefaultRemoteImages(): RemoteImages { + if (!defaultInstance) { + defaultInstance = createRemoteImages(); + } + return defaultInstance; +} diff --git a/playwright.config.ts b/playwright.config.ts new file mode 100644 index 00000000..19d17957 --- /dev/null +++ b/playwright.config.ts @@ -0,0 +1,15 @@ +import { defineConfig } from '@playwright/test'; + +// Island smoke tests against the built site (`astro preview` over build/out/). +// Run a full build first; CI wires this after verify-dist. +export default defineConfig({ + testDir: 'tests/e2e', + timeout: 30_000, + use: { baseURL: 'http://localhost:43210' }, + webServer: { + command: 'pnpm preview --port 43210', + url: 'http://localhost:43210/en/', + reuseExistingServer: true, + timeout: 30_000, + }, +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 00000000..556bad6d --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,10209 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@astrojs/react': + specifier: ^4.2.0 + version: 4.4.2(@types/node@26.1.1)(@types/react-dom@18.3.7(@types/react@18.3.31))(@types/react@18.3.31)(jiti@2.7.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(yaml@2.9.0) + '@astrojs/sitemap': + specifier: ^3.2.1 + version: 3.7.3 + '@fontsource/dm-sans': + specifier: ^5.2.8 + version: 5.2.8 + '@fontsource/lato': + specifier: ^5.2.7 + version: 5.2.7 + astro: + specifier: ^5.7.0 + version: 5.18.2(@types/node@26.1.1)(jiti@2.7.0)(rollup@4.62.2)(typescript@5.9.3)(yaml@2.9.0) + graphql: + specifier: ^16.6.0 + version: 16.14.2 + graphql-request: + specifier: ^5.1.0 + version: 5.2.0(graphql@16.14.2) + i18next: + specifier: ^24.2.0 + version: 24.2.3(typescript@5.9.3) + leaflet: + specifier: ^1.9.4 + version: 1.9.4 + papaparse: + specifier: ^5.4.1 + version: 5.5.4 + react: + specifier: ^18.3.1 + version: 18.3.1 + react-dom: + specifier: ^18.3.1 + version: 18.3.1(react@18.3.1) + react-leaflet: + specifier: ^4.2.1 + version: 4.2.1(leaflet@1.9.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rehype-stringify: + specifier: ^10.0.1 + version: 10.0.1 + remark-gfm: + specifier: ^4.0.1 + version: 4.0.1 + remark-parse: + specifier: ^11.0.0 + version: 11.0.0 + remark-rehype: + specifier: ^11.1.2 + version: 11.1.2 + sharp: + specifier: ^0.33.5 + version: 0.33.5 + unified: + specifier: ^11.0.5 + version: 11.0.5 + devDependencies: + '@astrojs/check': + specifier: ^0.9.9 + version: 0.9.9(prettier@3.9.5)(typescript@5.9.3) + '@eslint/js': + specifier: ^10.0.1 + version: 10.0.1(eslint@10.7.0(jiti@2.7.0)) + '@graphql-codegen/cli': + specifier: ^7.2.0 + version: 7.2.0(@types/node@26.1.1)(crossws@0.3.5)(graphql@16.14.2)(typescript@5.9.3) + '@graphql-codegen/typescript': + specifier: ^6.1.0 + version: 6.1.0(graphql@16.14.2) + '@graphql-codegen/typescript-operations': + specifier: ^6.1.0 + version: 6.1.0(graphql@16.14.2) + '@playwright/test': + specifier: ^1.61.1 + version: 1.61.1 + '@stylistic/eslint-plugin': + specifier: ^5.10.0 + version: 5.10.0(eslint@10.7.0(jiti@2.7.0)) + '@stylistic/stylelint-plugin': + specifier: ^5.2.1 + version: 5.2.1(stylelint@17.14.0(typescript@5.9.3)) + '@types/leaflet': + specifier: ^1.9.12 + version: 1.9.21 + '@types/papaparse': + specifier: ^5.3.15 + version: 5.5.2 + '@types/react': + specifier: ^18.3.12 + version: 18.3.31 + '@types/react-dom': + specifier: ^18.3.1 + version: 18.3.7(@types/react@18.3.31) + eslint: + specifier: ^10.7.0 + version: 10.7.0(jiti@2.7.0) + eslint-plugin-astro: + specifier: ^1.7.0 + version: 1.7.0(eslint@10.7.0(jiti@2.7.0)) + eslint-plugin-react-hooks: + specifier: ^7.1.1 + version: 7.1.1(eslint@10.7.0(jiti@2.7.0)) + globals: + specifier: ^17.7.0 + version: 17.7.0 + knip: + specifier: ^6.26.0 + version: 6.26.0 + postcss: + specifier: ^8.5.19 + version: 8.5.19 + postcss-flexbugs-fixes: + specifier: ^5.0.2 + version: 5.0.2(postcss@8.5.19) + postcss-nested: + specifier: ^7.0.2 + version: 7.0.2(postcss@8.5.19) + postcss-normalize: + specifier: ^13.0.1 + version: 13.0.1(browserslist@4.28.6)(postcss@8.5.19) + postcss-preset-env: + specifier: ^11.3.2 + version: 11.3.2(postcss@8.5.19) + stylelint: + specifier: ^17.14.0 + version: 17.14.0(typescript@5.9.3) + stylelint-config-concentric-order: + specifier: ^5.5.0 + version: 5.5.0(stylelint@17.14.0(typescript@5.9.3)) + stylelint-config-recommended: + specifier: ^18.0.0 + version: 18.0.0(stylelint@17.14.0(typescript@5.9.3)) + typescript: + specifier: ^5.7.0 + version: 5.9.3 + typescript-eslint: + specifier: ^8.63.0 + version: 8.63.0(eslint@10.7.0(jiti@2.7.0))(typescript@5.9.3) + +packages: + + '@ardatan/relay-compiler@13.0.1': + resolution: {integrity: sha512-afG3YPwuSA0E5foouZusz5GlXKs74dObv4cuWyLyfKsYFj2r7oGRNB28v18HvwuLSQtQFCi+DpIe0TZkgQDYyg==} + peerDependencies: + graphql: '*' + + '@astrojs/check@0.9.9': + resolution: {integrity: sha512-A5UW8uIuErLWEoRQvzgXpO1gTjUFtK8r7nU2Z7GewAMxUb7bPvpk11qaKKgxqXlHJWlAvaaxy+Xg28A6bmQ1Tg==} + hasBin: true + peerDependencies: + typescript: ^5.0.0 || ^6.0.0 + + '@astrojs/compiler@2.13.1': + resolution: {integrity: sha512-f3FN83d2G/v32ipNClRKgYv30onQlMZX1vCeZMjPsMMPl1mDpmbl0+N5BYo4S/ofzqJyS5hvwacEo0CCVDn/Qg==} + + '@astrojs/compiler@3.0.1': + resolution: {integrity: sha512-z97oYbdebO5aoWzuJ/8q5hLK232+17KcLZ7cJ8BCWk6+qNzVxn/gftC0KzMBUTD8WAaBkPpNSQK6PXLnNrZ0CA==} + + '@astrojs/internal-helpers@0.7.6': + resolution: {integrity: sha512-GOle7smBWKfMSP8osUIGOlB5kaHdQLV3foCsf+5Q9Wsuu+C6Fs3Ez/ttXmhjZ1HkSgsogcM1RXSjjOVieHq16Q==} + + '@astrojs/language-server@2.16.11': + resolution: {integrity: sha512-sJ/EfnFp0+gurTrkvONtd9qRqmMZLT9bHelfI1SA35CaQVTrRrA74qteOcNT/al1b9Atg3IiH1Jk/qfckyC+fg==} + hasBin: true + peerDependencies: + prettier: ^3.0.0 + prettier-plugin-astro: '>=0.11.0' + peerDependenciesMeta: + prettier: + optional: true + prettier-plugin-astro: + optional: true + + '@astrojs/markdown-remark@6.3.11': + resolution: {integrity: sha512-hcaxX/5aC6lQgHeGh1i+aauvSwIT6cfyFjKWvExYSxUhZZBBdvCliOtu06gbQyhbe0pGJNoNmqNlQZ5zYUuIyQ==} + + '@astrojs/prism@3.3.0': + resolution: {integrity: sha512-q8VwfU/fDZNoDOf+r7jUnMC2//H2l0TuQ6FkGJL8vD8nw/q5KiL3DS1KKBI3QhI9UQhpJ5dc7AtqfbXWuOgLCQ==} + engines: {node: 18.20.8 || ^20.3.0 || >=22.0.0} + + '@astrojs/react@4.4.2': + resolution: {integrity: sha512-1tl95bpGfuaDMDn8O3x/5Dxii1HPvzjvpL2YTuqOOrQehs60I2DKiDgh1jrKc7G8lv+LQT5H15V6QONQ+9waeQ==} + engines: {node: 18.20.8 || ^20.3.0 || >=22.0.0} + peerDependencies: + '@types/react': ^17.0.50 || ^18.0.21 || ^19.0.0 + '@types/react-dom': ^17.0.17 || ^18.0.6 || ^19.0.0 + react: ^17.0.2 || ^18.0.0 || ^19.0.0 + react-dom: ^17.0.2 || ^18.0.0 || ^19.0.0 + + '@astrojs/sitemap@3.7.3': + resolution: {integrity: sha512-f8euLVsyeAmAkSm/1M2Kb8sL8byQmfgbvBNaHFItCheTj/IpiJYSEWVcqDHZ/yEHxiS7+w87mQkzwZaPHmk5GA==} + + '@astrojs/telemetry@3.3.0': + resolution: {integrity: sha512-UFBgfeldP06qu6khs/yY+q1cDAaArM2/7AEIqQ9Cuvf7B1hNLq0xDrZkct+QoIGyjq56y8IaE2I3CTvG99mlhQ==} + engines: {node: 18.20.8 || ^20.3.0 || >=22.0.0} + + '@astrojs/yaml2ts@0.2.4': + resolution: {integrity: sha512-8oddpOae35pJsXPQXhTkM0ypfKPskVsh2bCxRtbf7e+/Epw2nReakFYpLKjZMEr75CsoF203PMnCocpfz0s69A==} + + '@babel/code-frame@7.29.7': + resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.29.7': + resolution: {integrity: sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.29.7': + resolution: {integrity: sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.29.7': + resolution: {integrity: sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.29.7': + resolution: {integrity: sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==} + engines: {node: '>=6.9.0'} + + '@babel/helper-globals@7.29.7': + resolution: {integrity: sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.29.7': + resolution: {integrity: sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.29.7': + resolution: {integrity: sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-plugin-utils@7.29.7': + resolution: {integrity: sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.29.7': + resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.29.7': + resolution: {integrity: sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.29.7': + resolution: {integrity: sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.29.7': + resolution: {integrity: sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/plugin-syntax-import-assertions@7.29.7': + resolution: {integrity: sha512-/An1OCBN93thpBAGyfsK2pcf0jvju1SAtKkL2Ny++B5Sy6sqgzXDQH1cZxWbF96Wuk+bn41MDA9bLd4VVAw6rw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx-self@7.29.7': + resolution: {integrity: sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx-source@7.29.7': + resolution: {integrity: sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/runtime@7.29.7': + resolution: {integrity: sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==} + engines: {node: '>=6.9.0'} + + '@babel/template@7.29.7': + resolution: {integrity: sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.29.7': + resolution: {integrity: sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.29.7': + resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==} + engines: {node: '>=6.9.0'} + + '@cacheable/memory@2.2.0': + resolution: {integrity: sha512-CTLKqLItRCEixEAewD3/j9DB3/o96gpTPD4eJ1v+DGOlxZRZncRQkGYqqnAGCscYd6RNeXfGeiuCphsPtqyIfQ==} + + '@cacheable/utils@2.5.0': + resolution: {integrity: sha512-buipgOVDkkPXNR5+xBpDw7Zk2n1EvU7qBJCNUcL7rhQ//kfpOXPAvQ511Os0vpLYJ1pZnvudNytkQt2hst3wqA==} + + '@capsizecss/unpack@4.0.1': + resolution: {integrity: sha512-CuNiSqg7+e1cO/GjffyMOm5Tt2jUF9CWHHnvQ/UkqvtkGfHdgwEC0wpmq7fkN3gxwpRnrAN0WzO3vREKmNolMQ==} + engines: {node: '>=18'} + + '@csstools/cascade-layer-name-parser@3.0.0': + resolution: {integrity: sha512-/3iksyevwRfSJx5yH0RkcrcYXwuhMQx3Juqf40t97PeEy2/Mz2TItZ/z/216qpe4GgOyFBP8MKIwVvytzHmfIQ==} + engines: {node: '>=20.19.0'} + peerDependencies: + '@csstools/css-parser-algorithms': ^4.0.0 + '@csstools/css-tokenizer': ^4.0.0 + + '@csstools/color-helpers@6.1.0': + resolution: {integrity: sha512-064IFJdjTfUqnjpCVpMOdbr8FLQBhinbZj6yRv2An2E41O/pLEXqfFRWqGq/SxlE5PEUYTlvWsG2r8MswAVvkg==} + engines: {node: '>=20.19.0'} + + '@csstools/css-calc@3.2.1': + resolution: {integrity: sha512-DtdHlgXh5ZkA43cwBcAm+huzgJiwx3ZTWVjBs94kwz2xKqSimDA3lBgCjphYgwgVUMWatSM0pDd8TILB1yrVVg==} + engines: {node: '>=20.19.0'} + peerDependencies: + '@csstools/css-parser-algorithms': ^4.0.0 + '@csstools/css-tokenizer': ^4.0.0 + + '@csstools/css-color-parser@4.1.9': + resolution: {integrity: sha512-paQcIaOO53Rk5+YrBaBjm/SgrV4INImjo2BT1DtQRYr+XeTRbeAYlS+jxXp9drqvKmtFnWRJKIalDLhZZDu42A==} + engines: {node: '>=20.19.0'} + peerDependencies: + '@csstools/css-parser-algorithms': ^4.0.0 + '@csstools/css-tokenizer': ^4.0.0 + + '@csstools/css-parser-algorithms@4.0.0': + resolution: {integrity: sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==} + engines: {node: '>=20.19.0'} + peerDependencies: + '@csstools/css-tokenizer': ^4.0.0 + + '@csstools/css-syntax-patches-for-csstree@1.1.6': + resolution: {integrity: sha512-TcJCWFbXLPpJYq6z7bfOyjWYJDiDg2/I4gyUC9pqPNqHFRIey0EB0q0L5cSnQDfWJg8Jd6VadakxdIez/3zkqQ==} + peerDependencies: + css-tree: ^3.2.1 + peerDependenciesMeta: + css-tree: + optional: true + + '@csstools/css-tokenizer@4.0.0': + resolution: {integrity: sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==} + engines: {node: '>=20.19.0'} + + '@csstools/media-query-list-parser@5.0.0': + resolution: {integrity: sha512-T9lXmZOfnam3eMERPsszjY5NK0jX8RmThmmm99FZ8b7z8yMaFZWKwLWGZuTwdO3ddRY5fy13GmmEYZXB4I98Eg==} + engines: {node: '>=20.19.0'} + peerDependencies: + '@csstools/css-parser-algorithms': ^4.0.0 + '@csstools/css-tokenizer': ^4.0.0 + + '@csstools/normalize.css@12.1.1': + resolution: {integrity: sha512-YAYeJ+Xqh7fUou1d1j9XHl44BmsuThiTr4iNrgCQ3J27IbhXsxXDGZ1cXv8Qvs99d4rBbLiSKy3+WZiet32PcQ==} + + '@csstools/postcss-alpha-function@2.0.7': + resolution: {integrity: sha512-ueQMpNJfsIUqyOd38Kjc3c7rTaXDLKn0qr9krteAFx3fsSuawXQePAQ02AI5+RbMpdibv+Mfcs4d0AUl/SULsQ==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-cascade-layers@6.0.0': + resolution: {integrity: sha512-WhsECqmrEZQGqaPlBA7JkmF/CJ2/+wetL4fkL9sOPccKd32PQ1qToFM6gqSI5rkpmYqubvbxjEJhyMTHYK0vZQ==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-color-function-display-p3-linear@2.0.6': + resolution: {integrity: sha512-65C1CF/d9/ULr7dwmE5TBrRYZVa5sWLZX7mlR7WcBl1YwSYA8ntOvAhEEZmq8B2rBgosrOjZcDLnvFQuGaoRcA==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-color-function@5.0.6': + resolution: {integrity: sha512-CHWuCfTg7afbSFMgVajmF1WU/bJ1rGBKGCj3L3tolDXZpQG2ZCQRu10ozfk1I10By9aRqQ43FTnAiDwBnbC+Hg==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-color-mix-function@4.0.6': + resolution: {integrity: sha512-gWXS+Qx4GnIq4lRvBcabAcGF5IBkfcbZ7uoUvQw/3+JYDQVkD8SoKIFRoYhwKAOm+zTDJRYLMsjxWieBh9xOZw==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-color-mix-variadic-function-arguments@2.0.6': + resolution: {integrity: sha512-+bL0RlSDLM/02Z2JgxMK2AIas8IIL4fQkFsjETmMIXFSmOH+iRx1YEKyMbc3SogmiR8zJ41Eid7Q8+bxj7+2Dg==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-container-rule-prelude-list@1.0.1': + resolution: {integrity: sha512-c5qlevVGKHU+zDbVoUGSZl1Mw7Vl1gVRKv6cdIYnaoyM+9Ou23Ian0H5Gr2ZF+lsDWovPK03hOSAbkw6HS8aTg==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-content-alt-text@3.0.2': + resolution: {integrity: sha512-LUT1eKBzb71pmky0ke2OHJHdcqHl1vtl++qzJei5FioMVWDzxVApElTZsoFwbqV8Et+UmxzieOuhwomuyRullw==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-contrast-color-function@3.0.6': + resolution: {integrity: sha512-7H+LLv2+A5q/l9TdWLnLB7ZgMT92aOVImzVpYSHg9pCEHSeskbqssn05rKb+ysouRM8rbg649Q2g2kIKFqLYvQ==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-exponential-functions@3.0.3': + resolution: {integrity: sha512-mB/NoeHLBHh0LZiVSrFdRDA/NxSfmg4tSN9117IJH9bdC2BzSTVgc82h3Gu/sdBXay6kDH2sA7fbkTigMiEi2A==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-font-format-keywords@5.0.0': + resolution: {integrity: sha512-M1EjCe/J3u8fFhOZgRci74cQhJ7R0UFBX6T+WqoEvjrr8hVfMiV+HTYrzxLY5OW8YllvXYr5Q5t5OvJbsUSeDg==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-font-width-property@1.0.0': + resolution: {integrity: sha512-AvmySApdijbjYQuXXh95tb7iVnqZBbJrv3oajO927ksE/mDmJBiszm+psW8orL2lRGR8j6ZU5Uv9/ou2Z5KRKA==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-gamut-mapping@3.0.6': + resolution: {integrity: sha512-lopCMFvN6ohy33lLmOY46matUrpL1ymVGOyebflzMb44pXoGy+f27glXCXz0PfQmfv8On3a8eVTIQfkX7nf6gQ==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-gradients-interpolation-method@6.0.6': + resolution: {integrity: sha512-n23eZrwg8/XT6Ml42AAMd1PqRSm1LlrcGL+cbflpjL84xVZcWsXtHeKVzufg2uxICGnB4/t/iGa4XnOfySusEA==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-hwb-function@5.0.6': + resolution: {integrity: sha512-KO1PBdiyzsxVOnripQbdMF8jXlBym3CtwEWHGQWfjRB2Q7BIU6sE9GGv2OrgG3TyqPOz5BgAwackYlYlVeHWvA==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-ic-unit@5.0.2': + resolution: {integrity: sha512-1Wcp0ACoGKImCL9mYm6EIdqtT2JuJoeFp/06Efa6pRT0y3elLgF3jYer+c3+qUoBhV/2uDKtfWrCwAkHJ0CB9w==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-image-function@1.0.1': + resolution: {integrity: sha512-6V7+8npoBxVgO3JbIdKqI6eo1NAIQtO0oNSfLSgH439WYB43/kM+GneG4elkgglRo1ppgVhjXxmNqhI+K4pEzg==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-initial@3.0.0': + resolution: {integrity: sha512-UVUrFmrTQyLomVepnjWlbBg7GoscLmXLwYFyjbcEnmpeGW7wde6lNpx5eM3eVwZI2M+7hCE3ykYnAsEPLcLa+Q==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-is-pseudo-class@6.0.0': + resolution: {integrity: sha512-1Hdy/ykg9RDo8vU8RiM2o+RaXO39WpFPaIkHxlAEJFofle/lc33tdQMKhBk3jR/Fe+uZNLOs3HlowFafyFptVw==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-light-dark-function@3.0.2': + resolution: {integrity: sha512-Fu6si5QhRrT5lP7nmXxfowR8zNkYrqoolsWOZxPZBZpVTdoEKazN6v+K53vPcy1EM4Mlj6tSaEjMu4gAwC8yqw==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-logical-float-and-clear@4.0.0': + resolution: {integrity: sha512-NGzdIRVj/VxOa/TjVdkHeyiJoDihONV0+uB0csUdgWbFFr8xndtfqK8iIGP9IKJzco+w0hvBF2SSk2sDSTAnOQ==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-logical-overflow@3.0.0': + resolution: {integrity: sha512-5cRg93QXVskM0MNepHpPcL0WLSf5Hncky0DrFDQY/4ozbH5lH7SX5ejayVpNTGSX7IpOvu7ykQDLOdMMGYzwpA==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-logical-overscroll-behavior@3.0.0': + resolution: {integrity: sha512-82Jnl/5Wi5jb19nQE1XlBHrZcNL3PzOgcj268cDkfwf+xi10HBqufGo1Unwf5n8bbbEFhEKgyQW+vFsc9iY1jw==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-logical-resize@4.0.0': + resolution: {integrity: sha512-L0T3q0gei/tGetCGZU0c7VN77VTivRpz1YZRNxjXYmW+85PKeI6U9YnSvDqLU2vBT2uN4kLEzfgZ0ThIZpN18A==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-logical-viewport-units@4.0.0': + resolution: {integrity: sha512-TA3AqVN/1IH3dKRC2UUWvprvwyOs2IeD7FDZk5Hz20w4q33yIuSg0i0gjyTUkcn90g8A4n7QpyZ2AgBrnYPnnA==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-media-minmax@3.0.3': + resolution: {integrity: sha512-ch1tNS+1QayiHTGsyc53zv3AzrSd0zigjbkfLxoeuzzJyn32+P3V7em3u5vLVnqLMzBbEZK//GI13EVTIPRdDA==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-media-queries-aspect-ratio-number-values@4.0.0': + resolution: {integrity: sha512-FDdC3lbrj8Vr0SkGIcSLTcRB7ApG6nlJFxOxkEF2C5hIZC1jtgjISFSGn/WjFdVkn8Dqe+Vx9QXI3axS2w1XHw==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-mixins@1.0.0': + resolution: {integrity: sha512-rz6qjT2w9L3k65jGc2dX+3oGiSrYQ70EZPDrINSmSVoVys7lLBFH0tvEa8DW2sr9cbRVD/W+1sy8+7bfu0JUfg==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-nested-calc@5.0.0': + resolution: {integrity: sha512-aPSw8P60e/i9BEfugauhikBqgjiwXcw3I9o4vXs+hktl4NSTgZRI0QHimxk9mst8N01A2TKDBxOln3mssRxiHQ==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-normalize-display-values@5.0.1': + resolution: {integrity: sha512-FcbEmoxDEGYvm2W3rQzVzcuo66+dDJjzzVDs+QwRmZLHYofGmMGwIKPqzF86/YW+euMDa7sh1xjWDvz/fzByZQ==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-oklab-function@5.0.6': + resolution: {integrity: sha512-6yZ+OySSaBybv12OfgKFBgCK1Se2mBbhh9eIDbUdWGHJUXZPQoLbtPj8S6Wg0lqe0nTqKgY/l8Ir9lHrm2TUBQ==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-position-area-property@2.0.0': + resolution: {integrity: sha512-TeEfzsJGB23Syv7yCm8AHCD2XTFujdjr9YYu9ebH64vnfCEvY4BG319jXAYSlNlf3Yc9PNJ6WnkDkUF5XVgSKQ==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-progressive-custom-properties@5.1.1': + resolution: {integrity: sha512-b9+lusgVDTHXRBgYKE9s0RKUyEn6Q/knmMDmYmSrkZjtpb0Nd71pBtgVMf9tSpOIb0K8hyzeyy6JyKFqg5rBJw==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-property-rule-prelude-list@2.0.0': + resolution: {integrity: sha512-qcMAkc9AhpzHgmQCD8hoJgGYifcOAxd1exXjjxilMM6euwRE619xDa4UsKBCv/v4g+sS63sd6c29LPM8s2ylSQ==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-random-function@3.0.3': + resolution: {integrity: sha512-0EScyKxscGonwpi30Hj9DEAr0X8D2eDhOqqayQXE91gIqGli9UT+deLYqoogZLOy5GT+ncqltMqztc/q+0UkhA==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-relative-color-syntax@4.0.6': + resolution: {integrity: sha512-4SUC+qfJE/8sV62oo/fcxNKSBgiSVqE8c7f9TBwvNY3WnD0YHJyEM3eIjXn1CxbMNl7gR6uBVuLuJB3xgr/O1w==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-scope-pseudo-class@5.0.0': + resolution: {integrity: sha512-kBrBFJcAji3MSHS4qQIihPvJfJC5xCabXLbejqDMiQi+86HD4eMBiTayAo46Urg7tlEmZZQFymFiJt+GH6nvXw==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-sign-functions@2.0.3': + resolution: {integrity: sha512-2BCPwlpeQweTC/8S8oQFYhYD5kxYkiroLf3AUJV2kVoKkSZ+4WM4rSwySXlKrqXL8HfCryAwVrJg7B0jr/RnOw==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-stepped-value-functions@5.0.3': + resolution: {integrity: sha512-nXMFQBz5Pi2LLG02iqm2k+scrqwtqJT9ta/gN8S79oBZ23M0E7O3wDJ20//3z5Q6HU5e+K0n+SmmxN6iWtbm6w==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-syntax-descriptor-syntax-production@2.0.0': + resolution: {integrity: sha512-elYcbdiBXAkPqvojB9kIBRuHY6htUhjSITtFQ+XiXnt6SvZCbNGxQmaaw6uZ7SPHu/+i/XVjzIt09/1k3SIerQ==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-system-ui-font-family@2.0.0': + resolution: {integrity: sha512-FyGZCgchFImFyiHS2x3rD5trAqatf/x23veBLTIgbaqyFfna6RNBD+Qf8HRSjt6HGMXOLhAjxJ3OoZg0bbn7Qw==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-text-decoration-shorthand@5.0.4': + resolution: {integrity: sha512-LHQL1a0CW0j3oBEivwgp1Gqv+VUppWpEyedM9GBzE6yIT6tnZqY9v0ADX1jO6IptTZTdrnBwM+dveDIuUP+pTA==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-trigonometric-functions@5.0.3': + resolution: {integrity: sha512-p9LTvLj+DFpl5RHbG/X9QGwg7BoMOBsRBZqsUAKKVvCw7MRCsk1P1llTUR/MW5nyZ4IsjFGDtDwTTj1reJjxvg==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-unset-value@5.0.0': + resolution: {integrity: sha512-EoO54sS2KCIfesvHyFYAW99RtzwHdgaJzhl7cqKZSaMYKZv3fXSOehDjAQx8WZBKn1JrMd7xJJI1T1BxPF7/jA==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + '@csstools/selector-resolve-nested@4.0.0': + resolution: {integrity: sha512-9vAPxmp+Dx3wQBIUwc1v7Mdisw1kbbaGqXUM8QLTgWg7SoPGYtXBsMXvsFs/0Bn5yoFhcktzxNZGNaUt0VjgjA==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss-selector-parser: ^7.1.1 + + '@csstools/selector-specificity@6.0.0': + resolution: {integrity: sha512-4sSgl78OtOXEX/2d++8A83zHNTgwCJMaR24FvsYL7Uf/VS8HZk9PTwR51elTbGqMuwH3szLvvOXEaVnqn0Z3zA==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss-selector-parser: ^7.1.1 + + '@csstools/utilities@3.0.0': + resolution: {integrity: sha512-etDqA/4jYvOGBM6yfKCOsEXfH96BKztZdgGmGqKi2xHnDe0ILIBraRspwgYatJH9JsCZ5HCGoCst8w18EKOAdg==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + '@emmetio/abbreviation@2.3.3': + resolution: {integrity: sha512-mgv58UrU3rh4YgbE/TzgLQwJ3pFsHHhCLqY20aJq+9comytTXUDNGG/SMtSeMJdkpxgXSXunBGLD8Boka3JyVA==} + + '@emmetio/css-abbreviation@2.1.8': + resolution: {integrity: sha512-s9yjhJ6saOO/uk1V74eifykk2CBYi01STTK3WlXWGOepyKa23ymJ053+DNQjpFcy1ingpaO7AxCcwLvHFY9tuw==} + + '@emmetio/css-parser@0.4.1': + resolution: {integrity: sha512-2bC6m0MV/voF4CTZiAbG5MWKbq5EBmDPKu9Sb7s7nVcEzNQlrZP6mFFFlIaISM8X6514H9shWMme1fCm8cWAfQ==} + + '@emmetio/html-matcher@1.3.0': + resolution: {integrity: sha512-NTbsvppE5eVyBMuyGfVu2CRrLvo7J4YHb6t9sBFLyY03WYhXET37qA4zOYUjBWFCRHO7pS1B9khERtY0f5JXPQ==} + + '@emmetio/scanner@1.0.4': + resolution: {integrity: sha512-IqRuJtQff7YHHBk4G8YZ45uB9BaAGcwQeVzgj/zj8/UdOhtQpEIupUhSk8dys6spFIWVZVeK20CzGEnqR5SbqA==} + + '@emmetio/stream-reader-utils@0.1.0': + resolution: {integrity: sha512-ZsZ2I9Vzso3Ho/pjZFsmmZ++FWeEd/txqybHTm4OgaZzdS8V9V/YYWQwg5TC38Z7uLWUV1vavpLLbjJtKubR1A==} + + '@emmetio/stream-reader@2.2.0': + resolution: {integrity: sha512-fXVXEyFA5Yv3M3n8sUGT7+fvecGrZP4k6FnWWMSZVQf69kAq0LLpaBQLGcPR30m3zMmKYhECP4k/ZkzvhEW5kw==} + + '@emnapi/core@1.11.0': + resolution: {integrity: sha512-l9Oo58x0HOP5znGzVhYW9U3e5wVuA4LAZU2AGezTmkhO1CgQRFDhDg4nneHsu/t3WniXg9QrG2nIXL/ZS8ln8Q==} + + '@emnapi/core@1.11.1': + resolution: {integrity: sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==} + + '@emnapi/runtime@1.11.0': + resolution: {integrity: sha512-55coeOFKHv1ywEcUXJtWU5f+Jr/W5tZDvZig8DLKSwUN1JpROQ4rk/SNOQiFWmaR/VKF4zuFyW1B8JduOSv6Pg==} + + '@emnapi/runtime@1.11.1': + resolution: {integrity: sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==} + + '@emnapi/runtime@1.11.2': + resolution: {integrity: sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA==} + + '@emnapi/wasi-threads@1.2.2': + resolution: {integrity: sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==} + + '@envelop/core@5.5.1': + resolution: {integrity: sha512-3DQg8sFskDo386TkL5j12jyRAdip/8yzK3x7YGbZBgobZ4aKXrvDU0GppU0SnmrpQnNaiTUsxBs9LKkwQ/eyvw==} + engines: {node: '>=18.0.0'} + + '@envelop/instrumentation@1.0.0': + resolution: {integrity: sha512-cxgkB66RQB95H3X27jlnxCRNTmPuSTgmBAq6/4n2Dtv4hsk4yz8FadA1ggmd0uZzvKqWD6CR+WFgTjhDqg7eyw==} + engines: {node: '>=18.0.0'} + + '@envelop/types@5.2.1': + resolution: {integrity: sha512-CsFmA3u3c2QoLDTfEpGr4t25fjMU31nyvse7IzWTvb0ZycuPjMjb0fjlheh+PbhBYb9YLugnT2uY6Mwcg1o+Zg==} + engines: {node: '>=18.0.0'} + + '@esbuild/aix-ppc64@0.25.12': + resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/aix-ppc64@0.27.7': + resolution: {integrity: sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.25.12': + resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm64@0.27.7': + resolution: {integrity: sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.25.12': + resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-arm@0.27.7': + resolution: {integrity: sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.25.12': + resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/android-x64@0.27.7': + resolution: {integrity: sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.25.12': + resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-arm64@0.27.7': + resolution: {integrity: sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.25.12': + resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/darwin-x64@0.27.7': + resolution: {integrity: sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.25.12': + resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-arm64@0.27.7': + resolution: {integrity: sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.25.12': + resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.27.7': + resolution: {integrity: sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.25.12': + resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm64@0.27.7': + resolution: {integrity: sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.25.12': + resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-arm@0.27.7': + resolution: {integrity: sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.25.12': + resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-ia32@0.27.7': + resolution: {integrity: sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.25.12': + resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-loong64@0.27.7': + resolution: {integrity: sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.25.12': + resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-mips64el@0.27.7': + resolution: {integrity: sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.25.12': + resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-ppc64@0.27.7': + resolution: {integrity: sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.25.12': + resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-riscv64@0.27.7': + resolution: {integrity: sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.25.12': + resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-s390x@0.27.7': + resolution: {integrity: sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.25.12': + resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/linux-x64@0.27.7': + resolution: {integrity: sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.25.12': + resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-arm64@0.27.7': + resolution: {integrity: sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.12': + resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.27.7': + resolution: {integrity: sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.25.12': + resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-arm64@0.27.7': + resolution: {integrity: sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.25.12': + resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.27.7': + resolution: {integrity: sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.25.12': + resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/openharmony-arm64@0.27.7': + resolution: {integrity: sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.25.12': + resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/sunos-x64@0.27.7': + resolution: {integrity: sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.25.12': + resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-arm64@0.27.7': + resolution: {integrity: sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.25.12': + resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-ia32@0.27.7': + resolution: {integrity: sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.25.12': + resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@esbuild/win32-x64@0.27.7': + resolution: {integrity: sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@eslint-community/eslint-utils@4.9.1': + resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.2': + resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/config-array@0.23.5': + resolution: {integrity: sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + '@eslint/config-helpers@0.6.0': + resolution: {integrity: sha512-ii6Bw9jJ2zi2cWA2Z+9/QZ/+3DX6kwaV5Q986D/CdP3Lap3w/pgQZ373FV7byY/i7L4IRH/G43I5dz1ClsCbpA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + '@eslint/core@1.2.1': + resolution: {integrity: sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + '@eslint/js@10.0.1': + resolution: {integrity: sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + peerDependencies: + eslint: ^10.0.0 + peerDependenciesMeta: + eslint: + optional: true + + '@eslint/object-schema@3.0.5': + resolution: {integrity: sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + '@eslint/plugin-kit@0.7.2': + resolution: {integrity: sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + '@fastify/busboy@3.2.0': + resolution: {integrity: sha512-m9FVDXU3GT2ITSe0UaMA5rU3QkfC/UXtCU8y0gSN/GugTqtVldOBWIB5V6V3sbmenVZUIpU6f+mPEO2+m5iTaA==} + + '@fontsource/dm-sans@5.2.8': + resolution: {integrity: sha512-tlovG42m9ESG28WiHpLq3F5umAlm64rv0RkqTbYowRn70e9OlRr5a3yTJhrhrY+k5lftR/OFJjPzOLQzk8EfCA==} + + '@fontsource/lato@5.2.7': + resolution: {integrity: sha512-k5mum1ADbDW5cTw1Ett1eQVWeoZ6gq0ct6SFBibEhB4LRxhniChJZTBgd6ph5yBxLkN1fcnsnmicBNA4S/3nbw==} + + '@graphql-codegen/add@7.1.0': + resolution: {integrity: sha512-bytJg1kel5zfgK3JSYbGwtpbNe6F9OPZSR6DiMDe9RVxblAgl6w4zEEPd/mM3rhNJ1VmGYLbNnf5e1eUfXQEbg==} + engines: {node: '>=16'} + peerDependencies: + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-codegen/cli@7.2.0': + resolution: {integrity: sha512-JPJw2vquEIpO3b8XJyxFVTrYi6WRn/OKu/SlzQA+IwAVT7GZPeG+AHmfRXAvpVMj31899nTpQYEQGUxx3ZqubQ==} + engines: {node: '>=16'} + hasBin: true + peerDependencies: + '@parcel/watcher': ^2.1.0 + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + peerDependenciesMeta: + '@parcel/watcher': + optional: true + + '@graphql-codegen/client-preset@6.1.0': + resolution: {integrity: sha512-mGmBuwrOU5oRoaWFodx8g9xu1jecYIiydqvk88QsAIsyMcZwuoybs1lyne85TovpBHjH5CC2wnZGsbDQfcgOCQ==} + engines: {node: '>=16'} + peerDependencies: + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + graphql-sock: ^1.0.0 + peerDependenciesMeta: + graphql-sock: + optional: true + + '@graphql-codegen/core@6.2.0': + resolution: {integrity: sha512-RZadhhwYhuy2ZdIGK40vYVBMzXEFGkCC+58MUC/F2af/gKznEYNzHgmNBUBCk/BTklyUsNu0mIXmyGE4tTA0PA==} + engines: {node: '>=16'} + peerDependencies: + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-codegen/gql-tag-operations@6.1.0': + resolution: {integrity: sha512-AmMcZFwonufvWJnQm7I0lBxKpAm+35BcCrOOvUlBoviohiR17aPoTGAOaNAEtpcpI86lnZ9m9AXUdiKMdm8nnQ==} + engines: {node: '>=16'} + peerDependencies: + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-codegen/plugin-helpers@7.1.0': + resolution: {integrity: sha512-ieJH7kZ5oSZKBPJs7CvHMrFY/CLYLklqv74ir93qMwRna6geZsbIMoJzTDBXohxcQTITiProiYSGrEtZjIpYGg==} + engines: {node: '>=16'} + peerDependencies: + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-codegen/schema-ast@6.1.0': + resolution: {integrity: sha512-/xuGkM5gUNFRoaQLumKbENdX7Hc8ha49z9OXsEZY8E+46mMjqzXGF0NtCJ892cmoX7EUgI5c8T+LZqS2upx2Aw==} + engines: {node: '>=16'} + peerDependencies: + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-codegen/typed-document-node@7.1.0': + resolution: {integrity: sha512-V6H+ItyqXtYY+JQb76LAoN627Xfzpn29/ifwCFAv61iEepzNzh86sa+yZclflr0G8LDmhcVY5hpPJd3a1qbOfw==} + engines: {node: '>=16'} + peerDependencies: + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-codegen/typescript-operations@6.1.0': + resolution: {integrity: sha512-zv0ohBJqLP1G/Kgiq1MhLTdNmakChtJivpaMaBmuPz9gasKWJkc9MhxuVytF6xS27PkJOh81TNM9srAkqpdejA==} + engines: {node: '>=16'} + peerDependencies: + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + graphql-sock: ^1.0.0 + peerDependenciesMeta: + graphql-sock: + optional: true + + '@graphql-codegen/typescript@6.1.0': + resolution: {integrity: sha512-2Hu3111O/AwV28Ap7tNsixlmXSAJuQbQArQklx+IC/tNswpckZnCfmlcBtTJrGU1+mJXEneJXGfb2XWvKjbhlQ==} + engines: {node: '>=16'} + peerDependencies: + graphql: ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-codegen/visitor-plugin-common@7.2.0': + resolution: {integrity: sha512-qqtTY8taONuxlR0HvD8z+zgWC3CfJ47M2rATx+geWNIN8v8Itc5TflHYxjkWAtNL6E3q7WZRY5P7se+2S3EjBg==} + engines: {node: '>=16'} + peerDependencies: + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-hive/signal@2.0.0': + resolution: {integrity: sha512-Pz8wB3K0iU6ae9S1fWfsmJX24CcGeTo6hE7T44ucmV/ALKRj+bxClmqrYcDT7v3f0d12Rh4FAXBb6gon+WkDpQ==} + engines: {node: '>=20.0.0'} + + '@graphql-tools/apollo-engine-loader@8.0.32': + resolution: {integrity: sha512-717/RCJQd0RDuv50rqGtIR7KrAR2h/fWYhBeVnSq5zYPyMOVxpSCoB1dHYws3fFENar021mgHyGNcPwJO5Fb3A==} + engines: {node: '>=16.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-tools/batch-execute@10.0.9': + resolution: {integrity: sha512-khIgAPlyaWJ3dVX6SsqOkABZCH1Gii32WHn3xMzavupsxPCfb/9G3zjdswptzTFrOcZ92dWo7MXvwNFkRfNN4w==} + engines: {node: '>=20.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-tools/code-file-loader@8.1.34': + resolution: {integrity: sha512-MOyXRh94EvBVIhlS8kh3aRhQiOdPfiCZvfd7g5/t7cW3VMa+Z0sfuNI5r7E2VxXZkZUsJ6yRj56EwAPY5fR4Jw==} + engines: {node: '>=16.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-tools/delegate@12.0.19': + resolution: {integrity: sha512-TFDKalXQub5dV7FO1y+3kwwJm337jwUFkTmXHlmYAtTZTxDuSVeHc74OfnepFe+6zEDriHqhHKvxSq5TpHCTvA==} + engines: {node: '>=20.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-tools/documents@1.0.1': + resolution: {integrity: sha512-aweoMH15wNJ8g7b2r4C4WRuJxZ0ca8HtNO54rkye/3duxTkW4fGBEutCx03jCIr5+a1l+4vFJNP859QnAVBVCA==} + engines: {node: '>=16.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-tools/executor-common@1.0.6': + resolution: {integrity: sha512-23/K5C+LSlHDI0mj2SwCJ33RcELCcyDUgABm1Z8St7u/4Z5+95i925H/NAjUyggRjiaY8vYtNiMOPE49aPX1sg==} + engines: {node: '>=20.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-tools/executor-graphql-ws@3.1.5': + resolution: {integrity: sha512-WXRsfwu9AkrORD9nShrd61OwwxeQ5+eXYcABRR3XPONFIS8pWQfDJGGqxql9/227o/s0DV5SIfkBURb5Knzv+A==} + engines: {node: '>=20.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-tools/executor-http@3.3.0': + resolution: {integrity: sha512-IkKXIjSg9U8MNsQUBVJAXE4+LSxaQ0cs7p5JTALLGDABY1o17vPDRwWALsX81AXD5dY27ihi/+OhGMueW/Fopg==} + engines: {node: '>=20.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-tools/executor-legacy-ws@1.1.30': + resolution: {integrity: sha512-sCY1Fn5M7/1eMbFuvEwzGqAdjdkKECUWbaTO1lo3RQVyHC8HUYe/DB/tEoQfT7TQarauX9KjXsSf2uqCyJyOOQ==} + engines: {node: '>=16.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-tools/executor@1.5.5': + resolution: {integrity: sha512-JAdn4G+ehthnGdJABS+wO6LxAIcoGPiSRHIz1ECYLtaQGkpFIdqH6BLUbZcToX/W/nmOG0kTFLoepCGkugbsBA==} + engines: {node: '>=16.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-tools/git-loader@8.0.38': + resolution: {integrity: sha512-uY6/Mth0nv35Jg6VSwZA0x19oALxuLnS9wU9r6L1QUDqMQib5agvbSjUGwond8vDXM0U5ZeDDnuyvpGz9yMH5w==} + engines: {node: '>=16.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-tools/github-loader@9.1.4': + resolution: {integrity: sha512-+ul7Y5f7a5r9nc0KWaDt+Rf0NdRzU3L4njvVURcZM2oxn2RcHIVLZQBMjglO/Q8vgC+IUBtQ7C0llgBOBHiQKA==} + engines: {node: '>=20.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-tools/graphql-file-loader@8.1.16': + resolution: {integrity: sha512-1OFMnmadqwAalty2nka8EpejM1T9QCgtNApGMTvIsz141y9lfK3Xj9lNTgsJk+u2kyjsEYyo3F+tc6wEqMdqTA==} + engines: {node: '>=16.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-tools/graphql-tag-pluck@8.3.33': + resolution: {integrity: sha512-12nfDKx1wpe5Zd2VhUCuZKaNzd0/ZQf1X5M8jHkRloGA61k+vr7dTDdlWrWcGDr1EufuoviL+e1UqGxD8D9PWg==} + engines: {node: '>=16.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-tools/import@7.1.16': + resolution: {integrity: sha512-vx2v9qqiQF15RK3O68kaqSXDQ/++xU4qkcNBCME3Y82qLGsKNPNJT8alklWlAJAvFs7LrXrCr+ZqbidO4pYaxw==} + engines: {node: '>=16.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-tools/json-file-loader@8.0.30': + resolution: {integrity: sha512-Ij97Ij+Uz3hvuetRTLX8v/d3ZyT2i17nyZN1maCTHW3tOypUevnuW7TKJcX0rOCsiP0juIcnnG91hcKsBh0Ktw==} + engines: {node: '>=16.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-tools/load@8.1.13': + resolution: {integrity: sha512-XstLIkH2gDVk3xlzbfNyR+MFGTaCop2rJSEyShXTnIy4Sh+DoNM6re8eXzZPIOs+N33kuOWm6e0EGE9tGpHUNQ==} + engines: {node: '>=16.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-tools/merge@9.2.0': + resolution: {integrity: sha512-kChDH/sOxm3TCICup5NgTiz/aJBk+5GAuC0lfJS8FcRfsqZvlCkNwpsYTGAATBCJMrgZ9+csRugCjS97jPcNMw==} + engines: {node: '>=16.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-tools/optimize@2.0.0': + resolution: {integrity: sha512-nhdT+CRGDZ+bk68ic+Jw1OZ99YCDIKYA5AlVAnBHJvMawSx9YQqQAIj4refNc1/LRieGiuWvhbG3jvPVYho0Dg==} + engines: {node: '>=16.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-tools/relay-operation-optimizer@7.1.6': + resolution: {integrity: sha512-uh4ikON4vCb95M8pjGRckucCAm7eq6TB9FXP0zZByaSO6dFkTR1jKrOSWrJ2qx+IR8B6bVaR/Zst9vGs26ZjXw==} + engines: {node: '>=16.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-tools/schema@10.0.36': + resolution: {integrity: sha512-g8S5aLirZInoi3NojzmBxwsZfVawOE0UBlVWYe8kDAR0FxS0riBDiyW7JnlAKayooHMRAMwGaze4RQU3VTTyig==} + engines: {node: '>=16.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-tools/url-loader@9.1.4': + resolution: {integrity: sha512-w65oPIvUxdJ8eSydIfG2jllbBY5+lijG1mCY22kpGcp/gqPUdH+L6TIr81ZeTZIWnV2DioSP7g3rLE4JGW6uyA==} + engines: {node: '>=20.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-tools/utils@11.2.0': + resolution: {integrity: sha512-eu9h1R3j/wWc4rvmYJF5AKtlwniDzstrZ/c6KSz+HdI+n7I7iog9xyKmBfpUwSbG1TqPNZBzWjFMkzdYOKq6Bg==} + engines: {node: '>=16.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-tools/wrap@11.1.18': + resolution: {integrity: sha512-qSstY1hw8rY3bTvy8NlRrJ4QOLtPdo6ykiv4y9EHaeYtNO3TWiIs0nLbUZX2F9wKT/i7T1aMbVysfeQoNmJvdQ==} + engines: {node: '>=20.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@graphql-typed-document-node/core@3.2.0': + resolution: {integrity: sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==} + peerDependencies: + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + '@humanfs/core@0.19.2': + resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.8': + resolution: {integrity: sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==} + engines: {node: '>=18.18.0'} + + '@humanfs/types@0.15.0': + resolution: {integrity: sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==} + engines: {node: '>=18.18.0'} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} + + '@img/colour@1.1.0': + resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==} + engines: {node: '>=18'} + + '@img/sharp-darwin-arm64@0.33.5': + resolution: {integrity: sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [darwin] + + '@img/sharp-darwin-arm64@0.34.5': + resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [darwin] + + '@img/sharp-darwin-x64@0.33.5': + resolution: {integrity: sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [darwin] + + '@img/sharp-darwin-x64@0.34.5': + resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-darwin-arm64@1.0.4': + resolution: {integrity: sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==} + cpu: [arm64] + os: [darwin] + + '@img/sharp-libvips-darwin-arm64@1.2.4': + resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==} + cpu: [arm64] + os: [darwin] + + '@img/sharp-libvips-darwin-x64@1.0.4': + resolution: {integrity: sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-darwin-x64@1.2.4': + resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-linux-arm64@1.0.4': + resolution: {integrity: sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-arm64@1.2.4': + resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-arm@1.0.5': + resolution: {integrity: sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-arm@1.2.4': + resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-ppc64@1.2.4': + resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-riscv64@1.2.4': + resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-s390x@1.0.4': + resolution: {integrity: sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-s390x@1.2.4': + resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-x64@1.0.4': + resolution: {integrity: sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-x64@1.2.4': + resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linuxmusl-arm64@1.0.4': + resolution: {integrity: sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@img/sharp-libvips-linuxmusl-arm64@1.2.4': + resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@img/sharp-libvips-linuxmusl-x64@1.0.4': + resolution: {integrity: sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@img/sharp-libvips-linuxmusl-x64@1.2.4': + resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@img/sharp-linux-arm64@0.33.5': + resolution: {integrity: sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-arm64@0.34.5': + resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-arm@0.33.5': + resolution: {integrity: sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-arm@0.34.5': + resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-ppc64@0.34.5': + resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-riscv64@0.34.5': + resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-s390x@0.33.5': + resolution: {integrity: sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-s390x@0.34.5': + resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-x64@0.33.5': + resolution: {integrity: sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-x64@0.34.5': + resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@img/sharp-linuxmusl-arm64@0.33.5': + resolution: {integrity: sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@img/sharp-linuxmusl-arm64@0.34.5': + resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@img/sharp-linuxmusl-x64@0.33.5': + resolution: {integrity: sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@img/sharp-linuxmusl-x64@0.34.5': + resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@img/sharp-wasm32@0.33.5': + resolution: {integrity: sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [wasm32] + + '@img/sharp-wasm32@0.34.5': + resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [wasm32] + + '@img/sharp-win32-arm64@0.34.5': + resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [win32] + + '@img/sharp-win32-ia32@0.33.5': + resolution: {integrity: sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ia32] + os: [win32] + + '@img/sharp-win32-ia32@0.34.5': + resolution: {integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ia32] + os: [win32] + + '@img/sharp-win32-x64@0.33.5': + resolution: {integrity: sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [win32] + + '@img/sharp-win32-x64@0.34.5': + resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [win32] + + '@inquirer/ansi@2.0.7': + resolution: {integrity: sha512-3eTuUO1vH2cZm2ZKHeQxnOqlTi9EfZDGgIe3BL3I4u+rJHocr9Fz86M4fjYABPvFnQG/gGK551HqDiIcETwU6Q==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + + '@inquirer/checkbox@5.2.1': + resolution: {integrity: sha512-b6xmA/VlTe0ZgDQHDui+Nav470u7u49nRd8/iuhOcQPO9Ch7lGuogydhi2VOmNlZ+zXcM8IcPuNSwQcdJaF/kw==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/confirm@6.1.1': + resolution: {integrity: sha512-eb8DBZcz/2qHWQda4rk2JiQk5h9QV/cVHi1yjt0f69WFZMRFn0sJTye3EAP8icut8UDMjQPsaH5KbcOogefrFQ==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/core@11.2.1': + resolution: {integrity: sha512-Qd6GJT1yVyrZZCfN8W2qKF5ApmqryXRhRKCuip8h01x2w/esJQ2XIYc6f9abMIHgKQdBfFTSOdbHRLAhuM09UA==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/editor@5.2.2': + resolution: {integrity: sha512-ZRVd/oD+sYsUd5zVm0NflqEzlqfYCyHNsqkHl2oWXEUHs12tCbcSFi+wVFEvD8+LGRaMUsVrE7qeo6lSG/S1Vg==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/expand@5.1.1': + resolution: {integrity: sha512-YmQpenjbFSHAK3sOd44puHh3V1KXXr+JiNpUztoSQ4drLh2rTVzTap/YtlAVu/5xavifIlBfNEzJ/neZJ1a/1g==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/external-editor@3.0.3': + resolution: {integrity: sha512-6thf5I8q7lZwzGLAxPaaGEREEkZ3nyePPDQ1oyobblxmEE8mqTLguScP7pDjUTAibiyb4hfXl+qjUEJ+di/aNA==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/figures@2.0.7': + resolution: {integrity: sha512-aJ8TBPOGB6f/2qziPfElISTCEd5XOYTFckA2SGjhNmiKzfK/u4ot3v0DUzGVdUnKjN10EqnnEPck36BkyfLnJw==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + + '@inquirer/input@5.1.2': + resolution: {integrity: sha512-9K/DDBSQpOyZSkt6sOVP9Vo0TR7atX2kuILsUu0x3wVcVbe97lJwIJKMLdMw25tDYuXl/qp6erT0Xs1rfmcfZg==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/number@4.1.1': + resolution: {integrity: sha512-XF4IXAbPnGPgw0wsbC/i2tPcyfdZgDpUlhsqU0SfT4IRIGWha6Xm9VRgN5yYxJq+jnyXlfXI/nQ3ulfk0iEICA==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/password@5.1.1': + resolution: {integrity: sha512-3XBfF7DAsp5qeDsvN5Rd1HmbNokVvEQoUM0QLrRcybC9nX96w3Pbmu7qUsb3IT3J3jBvs2+mTXaKHOUsgHMLzg==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/prompts@8.5.2': + resolution: {integrity: sha512-IYR/3C/paEVVQYQvdDlFZVjRCJVYHHON0XXMH91KO9GSxs0TdKYWlUdvfQl2EfAHDxUaN3IBffkE/BDTh5nJ6g==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/rawlist@5.3.1': + resolution: {integrity: sha512-QqdTqQddL3qPX/PPrjobpsO25NZ4dWXgTLenrR445L2ptLEYE6Z+PD5c5CNDJNx4ugRgELAIpSIJxZaO2jJ2Og==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/search@4.2.1': + resolution: {integrity: sha512-xJj8QWKRSrfKoBIITLZK61dD3zwo0Rz11fgDImku30/Oe81zMdIdGgrLY2h6RkJ+KZ/GhNYIRMKnH/62qBTA5g==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/select@5.2.1': + resolution: {integrity: sha512-FlDndEUww8m7BfukO2nJa25vhD+H5jxxCv4oGioKqzyWz3nPHhhw4LKdYRSlXuAx7DsdWia7iyaBPKKS95Evfw==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/type@4.0.7': + resolution: {integrity: sha512-t28inv14nMQ1PhKpsJPY+kEs/c00qzeCOS2gTNRyTjG5d6qsVA2fItxW4hkvGZ5lvanGLdtCzVIx5dwdRpN1+g==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@keyv/bigmap@1.3.1': + resolution: {integrity: sha512-WbzE9sdmQtKy8vrNPa9BRnwZh5UF4s1KTmSK0KUVLo3eff5BlQNNWDnFOouNpKfPKDnms9xynJjsMYjMaT/aFQ==} + engines: {node: '>= 18'} + peerDependencies: + keyv: ^5.6.0 + + '@keyv/serialize@1.1.1': + resolution: {integrity: sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==} + + '@napi-rs/wasm-runtime@1.1.6': + resolution: {integrity: sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==} + peerDependencies: + '@emnapi/core': ^1.7.1 + '@emnapi/runtime': ^1.7.1 + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@oslojs/encoding@1.1.0': + resolution: {integrity: sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==} + + '@oxc-parser/binding-android-arm-eabi@0.137.0': + resolution: {integrity: sha512-KDs+0VPdEmasOkpuJHW9V5WCF+cvYdMQv2Jd+aJXt+cxIx12NToRQRbXaRwUEDsZw+/jMk81Ve8ZFbjUkJTOwA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] + + '@oxc-parser/binding-android-arm64@0.137.0': + resolution: {integrity: sha512-WhALNzfy3x/RfC6bsqX+csavuUY0yHHE7XfgPE5M542uhoBZUUoGTPG+nkMbGoG4+gcfss5s7urMyn5QBHu0sw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@oxc-parser/binding-darwin-arm64@0.137.0': + resolution: {integrity: sha512-bFPr5hgmNMOMoyPTGtdsK4Ug21RovIPojRMgDDhSp1LtCnc/DkLwGONKjgRjszg677RlGnkYSviQ8hHaUPOVYA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@oxc-parser/binding-darwin-x64@0.137.0': + resolution: {integrity: sha512-CL5dMm1asqXIDZHg14FLxj3Mc36w8PI7xCWh1uA4is6z8g2XrIILoTcQYOxDbwzuk34RDPX5IAGUxZr6LA9KAg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@oxc-parser/binding-freebsd-x64@0.137.0': + resolution: {integrity: sha512-79h8rYGnSlKPGWo7mHr2ixO6ea7aW8B0CT965SZ8SLbNnCOH5aOYBTeVXUY6eMvEaiLyWr8Skuiugr5pDYgLGw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@oxc-parser/binding-linux-arm-gnueabihf@0.137.0': + resolution: {integrity: sha512-ASgmlSimhGyr0lksgVIo6hibz1obnDq4qJbiMX/AzltfgPnanRrzG1Q+23g8ljOHOjv6dsznkUuCYL3gg0sY1Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxc-parser/binding-linux-arm-musleabihf@0.137.0': + resolution: {integrity: sha512-AU2J9aa22Sx32wRGnDjybOU9TQXXQUud5sdUi+ZB0XxwM8aToWLweV+yA0wlQm0yIUVqljquqoHCYEq9II8gJQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxc-parser/binding-linux-arm64-gnu@0.137.0': + resolution: {integrity: sha512-GdEtiG89yMr7XkUGxifgodXEEm2f+xW2f9CpDjlgAnBOwhTmrpQMvhOGobLVKUyzf/qHBXW16smk5zbF3nZU6w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@oxc-parser/binding-linux-arm64-musl@0.137.0': + resolution: {integrity: sha512-EGJ+Bs8iXx8KBH8DQ5BLoEm5lnHaYjlh4/8j8vFhrr/6z4tqONy5BZDzLpKmmNWlN6Hlc5r8YOuBVHqZ9vRFEQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@oxc-parser/binding-linux-ppc64-gnu@0.137.0': + resolution: {integrity: sha512-vzFUQENy/fnbSe5DZWovq6tIBc1uhuMztanSW6rz1e9WdQE4gHwYuD7ZII6JnrJifd1R3RSoqiZbgRFlVL2tYQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@oxc-parser/binding-linux-riscv64-gnu@0.137.0': + resolution: {integrity: sha512-SfVI14HBQs9gtLcUD5hTt5hsNbdrqSUNg9S8muN+LhVQ5nf1WwH3hAoK6B9NKgdYgWAQSXFXGiiBedQ4r/BKuw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@oxc-parser/binding-linux-riscv64-musl@0.137.0': + resolution: {integrity: sha512-e7Ppy4FCIFNQxT/ikSeIWFoQ0l+N9vgtRBtLcyZXeolTzApyVoPqEXsYPrcdM/9i0Bwk8knvYd37vaEMxHyi6g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@oxc-parser/binding-linux-s390x-gnu@0.137.0': + resolution: {integrity: sha512-Bho5qFwdhqsIFR7gipYEUlqvi3SRrY8sugxXig380MIaakBB1PyU9+7dBiBVScfImTNWhijUxdBwqrprGdq5WA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@oxc-parser/binding-linux-x64-gnu@0.137.0': + resolution: {integrity: sha512-36mGWtg7PyFzjJwGDkH6/F4o2nIDEoKXLPr/X/lwqklkomQwJJt1I5GJVmGhovUEmgPK5WAeAZMqlFCehwiy9Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@oxc-parser/binding-linux-x64-musl@0.137.0': + resolution: {integrity: sha512-/Jqx6+N7A44n2BdvUr7pXhVr2vFjs6WGH3unZRczwrfiH0H1zY0QwKQMG/dtRiTlKGDKGukznPT8lx84/oEsZg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@oxc-parser/binding-openharmony-arm64@0.137.0': + resolution: {integrity: sha512-9Uj0qHNNl+OgT1UTGwF7ixIXU6T1u2SbMidmgPy/h1h/fl2gRS6YpAxxY1gwHofcWjoTwkoMFd8xs5Vuj6GOFA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@oxc-parser/binding-wasm32-wasi@0.137.0': + resolution: {integrity: sha512-gW2vfkytNGgMVADiuzdvOfw0mWG9za20F/1fCJsif5aBMAvWJTSbpIXbIe0XkOe0VENk+PadpQ7cZgUy2sUJcA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [wasm32] + + '@oxc-parser/binding-win32-arm64-msvc@0.137.0': + resolution: {integrity: sha512-x+pFANF0yL5uK/6T7lu6SlR5qid6sp//eZXKLq5iNsIE+EQg6EaS8/wsW7E91nXXjpnPhSoMOHXShSVhGRdn8w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@oxc-parser/binding-win32-ia32-msvc@0.137.0': + resolution: {integrity: sha512-sQUqym80PFi6McRsIqfJrSu2JrSClEZIXXD+/FjAFoULEKzOPsldIdFBG96xdX8aVMzCNQ9792FPx3MfkEIrFA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ia32] + os: [win32] + + '@oxc-parser/binding-win32-x64-msvc@0.137.0': + resolution: {integrity: sha512-2AsevxlvNN4WKxpEn3RtqD5zbqMaXF+T7JXblsP4gVuY+vC9dXS4ED/PwfRCliFqoeisYS3Iro4DHzxr0TEvVA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@oxc-project/types@0.137.0': + resolution: {integrity: sha512-WT+Gb24i8hmvo85AIv2oEYouEXkRlKAlT9WaCa3TfLgNCN+GhrJOGZuIlMouAh38Qe4QOx26eUOVsq70qXrywA==} + + '@oxc-resolver/binding-android-arm-eabi@11.21.3': + resolution: {integrity: sha512-eNU11A2WNizh04v3uyaJCootrHIaS0B9aHYXvAvVnPNk4xYSjMUjHnhQ6dewPN2MRYDskV85d1N0Aw0WNWhcyg==} + cpu: [arm] + os: [android] + + '@oxc-resolver/binding-android-arm64@11.21.3': + resolution: {integrity: sha512-8Q+ZjTLvn2dIcWsrmhdrEihm7q+ag/k+mkry7Z+t0QbbHaVxXQfvH9AewyVMh/WrpEKhQ3DDgx9fYbqeCpeOEw==} + cpu: [arm64] + os: [android] + + '@oxc-resolver/binding-darwin-arm64@11.21.3': + resolution: {integrity: sha512-wkh0qKZGHXVUDxFw3oA1TXnU2BDYY/r775oJflGeIr8uDPPoN2pk8gijQIzYRT6hoql/lg3+Tx/SaTn9e2/aGg==} + cpu: [arm64] + os: [darwin] + + '@oxc-resolver/binding-darwin-x64@11.21.3': + resolution: {integrity: sha512-HbNc23FAQYbuyDV2vBWMez4u4mrsm5RAkniGZAWqr6lYZ3N4beeqIb776jzwRl8qL2zRhHVXpUj97X0QgogVzg==} + cpu: [x64] + os: [darwin] + + '@oxc-resolver/binding-freebsd-x64@11.21.3': + resolution: {integrity: sha512-K6xNsTUPEUdfrn0+kbMq5nOUB5w1C5pavPQngt4TM2FpN91lP0PBe2srSpamb4d69O7h86oAi/qWX/kZNRSjkw==} + cpu: [x64] + os: [freebsd] + + '@oxc-resolver/binding-linux-arm-gnueabihf@11.21.3': + resolution: {integrity: sha512-VcFmOpcpWX1zoEy8M58tR2M9YxM+Z9RuQhqAx5q0CTmrruaP7Gveejg75hzd/5sg5nk9G3aLALEa3hE2FsmmTQ==} + cpu: [arm] + os: [linux] + + '@oxc-resolver/binding-linux-arm-musleabihf@11.21.3': + resolution: {integrity: sha512-quVoxFLBy43hWaQbbDtQNRwAX5vX76mv7n64icAtQcJ3eNgVeblqmkupF/hAneNthdqSlnd1sTjb3aQSaDPaCQ==} + cpu: [arm] + os: [linux] + + '@oxc-resolver/binding-linux-arm64-gnu@11.21.3': + resolution: {integrity: sha512-X0AqNZgcD07Q4V3RDK18/vYOj/HQT/FnmEFGYS2jTWqY7JO13ryE3TEs3eAIgUJhBnNkpEaiXqz3VK8M7qQhWQ==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@oxc-resolver/binding-linux-arm64-musl@11.21.3': + resolution: {integrity: sha512-YkaQnaKYdbuaXvRt5Qd0GpbihzVnyfR6z1SpYfIUC6RTu4NF7lDKPjVkYb+jRI2gedVO2rVpN35Y6akG6ud4Lw==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@oxc-resolver/binding-linux-ppc64-gnu@11.21.3': + resolution: {integrity: sha512-gB9HwhrPiFqUzDeEq+y/CgAijz1YdI6BnXz5GaH2Pa9cWdutchlkGFAiAuGb/PjVQpiK6NFKzFuztxrweoit7A==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@oxc-resolver/binding-linux-riscv64-gnu@11.21.3': + resolution: {integrity: sha512-zjDWBlYk8QGv0H8dsPUWqkfjYIIjG2TvspGkzXL0eImbgxtZorA/klKeHyolevoT3Kvbi+1iMr9Lhrh7jf54Og==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@oxc-resolver/binding-linux-riscv64-musl@11.21.3': + resolution: {integrity: sha512-4UfsQvacV388y1zpXL7C1x1FNYaV52JtuNRiuzrfQA2z1z6ElVrsidkGsrvQ5EgeSq1Pj7kaKqrgGkvFuxJ/tw==} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@oxc-resolver/binding-linux-s390x-gnu@11.21.3': + resolution: {integrity: sha512-b5uH+HKH0MP5mNBYaK75SKsJbw52URqrx2LavYdq6wb0l3ExAG5niYRP9DWUNHdKilpaBVM2bXk9HNWrH3ew7Q==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@oxc-resolver/binding-linux-x64-gnu@11.21.3': + resolution: {integrity: sha512-PjYlmilBpNRh2ntXNYAK3Am5w/nPfEpnU/96iNx7CI8EzAn12J4JRiec63wHJTH31nLoCNxBg/829pN+3CfG3Q==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@oxc-resolver/binding-linux-x64-musl@11.21.3': + resolution: {integrity: sha512-QTBAb7JuHlZ7JUEyM8UiQi2f7m/L4swBhP2TNpYIDc9Wp/wRw1G/8sl6i13aIzQAXH7LKIm294LeOHd0lQR8zA==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@oxc-resolver/binding-openharmony-arm64@11.21.3': + resolution: {integrity: sha512-4j1DFwjwv36ec9kds0jU/ucQ5Ha4ERO/H95BxR5JFf0kqUUAJ1kwII7XhTc1vZrkdJkvLGC9Q2MbpObpum8RBg==} + cpu: [arm64] + os: [openharmony] + + '@oxc-resolver/binding-wasm32-wasi@11.21.3': + resolution: {integrity: sha512-i8oluoel5kru/j1WNrjmQSiA3GQ7wvIYVR1IwIoZtKogAhya2iub+ZKIeSIkcJOrnzQ18Tzl/F+kL3fYOxZLvA==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@oxc-resolver/binding-win32-arm64-msvc@11.21.3': + resolution: {integrity: sha512-M/8dw8dD6aOs+NlPJax401CZB9I7Aut84isQLgALGGwke4Afvw+/7yYhZb94yXf6t2sPLhQLmSmtSV+2FhsOWg==} + cpu: [arm64] + os: [win32] + + '@oxc-resolver/binding-win32-x64-msvc@11.21.3': + resolution: {integrity: sha512-H7BCt/VnS9hnmMp42eGhZ99izSCRvlnWwy/N71K1/J8QoExwY4262Z8QiEkMDtduRJrztayDxETTckmUuAVL9Q==} + cpu: [x64] + os: [win32] + + '@pkgr/core@0.3.6': + resolution: {integrity: sha512-SEeaJLb3qBNF/OaXnaR1NmmBbFYk1zC0ZH/52fATcRPLFg/p791YrcyFFy44Bo9sLaGuSuLp5Q6axbb/O+v/RA==} + engines: {node: ^14.18.0 || >=16.0.0} + + '@playwright/test@1.61.1': + resolution: {integrity: sha512-8nKv6+0RJSL9FE4jYOEGXnPeM/Hg12qZpmqzZjRh3qM0Y7c3z1mrOTfFLids72RDQYVh9WpLEfR5WdpNX4fkig==} + engines: {node: '>=18'} + hasBin: true + + '@react-leaflet/core@2.1.0': + resolution: {integrity: sha512-Qk7Pfu8BSarKGqILj4x7bCSZ1pjuAPZ+qmRwH5S7mDS91VSbVVsJSrW4qA+GPrro8t69gFYVMWb1Zc4yFmPiVg==} + peerDependencies: + leaflet: ^1.9.0 + react: ^18.0.0 + react-dom: ^18.0.0 + + '@repeaterjs/repeater@3.1.0': + resolution: {integrity: sha512-TaoVksZRSx2KWYYpyLQtMQXXeS98VsgZImzW65xmiVgbYhXLk+aEsmzPLirqVuE4/XuUapH2iMtxUzaBNDzdSQ==} + + '@rolldown/pluginutils@1.0.0-beta.27': + resolution: {integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==} + + '@rollup/pluginutils@5.4.0': + resolution: {integrity: sha512-MfPp06CjRLfXQ3wY0R8vJDYBy/MvVcc9OulEfR0B8Iv9ko+GCNaRZ+EpJYFl27LhKsZK0o420sYCRHCjfCgeUg==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/rollup-android-arm-eabi@4.62.2': + resolution: {integrity: sha512-6o7ZLZK+BeenkZCFNDXqpbjw9bD6nuWonvS/lwQJp7NoVVxm6p3qE7qQ5jGuBjiFsgvqjD8mZAU5oWxTmbOeOg==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.62.2': + resolution: {integrity: sha512-BaH7BllCACHoH1LguOU56UItGfUWjujlO65kS9LAodViaN4bwIKd7oeW/ZHJ/4ljr/7MIiENnNy3HJ0zXv8Zkw==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.62.2': + resolution: {integrity: sha512-v39RCCvj4He82I9sFmk+M1VZ0PLM9sfsLVikjfx2hYBNALhrrOR2D3JjQA6AhlaSOgcR+RzrKY7e1+bT6SUO/A==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.62.2': + resolution: {integrity: sha512-yl0y2vq3S3lHeuXhEdss6TWfKW8vkujImO12tn4ZkG/4oghr09LvdYm2RElVjokTQiUvDUGXLGsYeLqUMCKpGA==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.62.2': + resolution: {integrity: sha512-tT4pvt4qXD+vEoezupCWi+a1F0vvDiksiHc+PxRlYTOH1I6/X4id9jPxTP+Fg+545euaFT1jJVs4CEdHZAU1vw==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.62.2': + resolution: {integrity: sha512-6nU5F2wCW+qvCBhTn1pdIU3bzsIoF7EUwsCDRxilWGprQR6yd508YnH9+OKFCwpfS8pjZqDUmnCAr7exax0XCg==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.62.2': + resolution: {integrity: sha512-n1GJHPOvpIfhi3TmrCeh6S6URt9BFCt0KQE3qvexyGCTAKpR4Lg+eWvNZEqu7epxwus/8ElT3hacYEucm49SZg==} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm-musleabihf@4.62.2': + resolution: {integrity: sha512-JqgflS8wEB+UXV/vS1RpRbifGBeN4D5lz8D8oOFbFZw4vedvdOgCFAjfBmIMdW3yL10XpQQ0Ambepw6MXrhOnA==} + cpu: [arm] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-arm64-gnu@4.62.2': + resolution: {integrity: sha512-wnFJkogWvN4jm/hQRF2UBaeUmk20j5+DmHvoyWii2b8HJDyvz1MF2OU/6ynXt2KR63rbZLWkFpoytpdc/yBuSA==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm64-musl@4.62.2': + resolution: {integrity: sha512-HVu2bp0zhvJ8xHEV9+UUs7S90VadmBSY3LcIMvozbPo4AuMGDWlz3ymHLHZPX4hR67TKTt8Qp5PJ5RBg/i+RMQ==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-loong64-gnu@4.62.2': + resolution: {integrity: sha512-mQqqAV8QaoSgr9I2fKDLY2BAVvmKjWoGiu/cSYQonsLvtqwEn1E4QYfnCOcp5zoEqNhsDYin1s6jx/VJmrxlZg==} + cpu: [loong64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-loong64-musl@4.62.2': + resolution: {integrity: sha512-IxKLoxCQ2IWi6bT2akyDUBGsOImDKB+sPp4EsTmwFQ/fMwpCKm8uLSSgP/Kx/QYUgKis6SEZ5/Nlhup0DIA0PQ==} + cpu: [loong64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-ppc64-gnu@4.62.2': + resolution: {integrity: sha512-Mk5ha2RQSgyFfmYYLkBpPnUk8D8FriBxesO1u9O75X0mHgXL1UQcH5Itl2lurWL2tj0RxV9b9tJgipac0hRY9A==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-ppc64-musl@4.62.2': + resolution: {integrity: sha512-CjvEnqJL/0/TQ3TXX3OPIJ/kmBellrWd4heXUmHeJlTnmwjKpSJzoehLaL6Xk0ZnMHBu9dZuFADNOrtjF4v+2w==} + cpu: [ppc64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-riscv64-gnu@4.62.2': + resolution: {integrity: sha512-1SiZbzwdkaDURsew/tSOrooKiYy7EQGT6m8ufavAi9NEyQb/6VuIxFXAL1fqa4iZe3g4NbNk4P7J32z2tw5Mgg==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-riscv64-musl@4.62.2': + resolution: {integrity: sha512-nQts12zJ3NQRoE6uYljOH89v7szzLDvG2JD/vsX+vGXU8w/At1GowTZ5/7qeFQ8m7L55rpR8Okugnuo5bgjy2Q==} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-s390x-gnu@4.62.2': + resolution: {integrity: sha512-E9/ll019jhPIJgpzfZoIkBGhcz+kKNgVWYRY0zr9srBdPPFVpvOKW8VaJKUbeK+eZXyQF9ltME+Kk6affeaPgg==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-gnu@4.62.2': + resolution: {integrity: sha512-5BqxR/pshjey51iliyzTD5Xi3EN0aLmQ2lZ3lvefVV9c82BvrLo2/6OT55iifpWBufs6kdwWbuOKS841DrmK9A==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-musl@4.62.2': + resolution: {integrity: sha512-uNN83XxQrRAh/w0/pmAfibcwyb6YWt4gP+dpnQKPVJshAloQ785ii8CT8ZCIxkGg9opVsvAlGhFitSm6D1Jjpg==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rollup/rollup-openbsd-x64@4.62.2': + resolution: {integrity: sha512-srjEIxSH3LRnJN6THczDHWQplqEMFiAJrTab0msUryh9kwNpkICf3Ea6q6MN/2cZwRFUNx5w+h6Hpi4QuHS6Zg==} + cpu: [x64] + os: [openbsd] + + '@rollup/rollup-openharmony-arm64@4.62.2': + resolution: {integrity: sha512-8hOJnxgbyObnCm5AlRA3A931xX19xq80RjVTKgJOvEKWqJruP/Uf12IbAOaDjjEXYRewwHLfmF0YRIdK3OwKWA==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.62.2': + resolution: {integrity: sha512-mmF4AY1i0hG/bLWUctUq59gtmgaSIRa3cu/A3JFRp/sCNEme2bgDEiDS22P9FbnJB8NJNF4jPJiSP5RHQpUTDg==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.62.2': + resolution: {integrity: sha512-DZgkknc6jhHrk46V25vbAM0zZkyP0nSDkJB8/dRkLTxv470dOmWDqGoEJl/9A0dFfS7yE3REOwNDxpHwSLSt0Q==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.62.2': + resolution: {integrity: sha512-T6xr6ucWSFto+VGajA8YH26LdpHRuP4YLHEKAtCWvJDOlnmWcDZVCI2Jmjr+IFHDlt2zRaTAKE4tfjTaWLgJBg==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.62.2': + resolution: {integrity: sha512-BfzEnDJOt9T8M989/lA37EcJgat01wLRnoi5dQf3QzOH7jzpqTAzdDbVfRljVr5r+jzKqpbHeyOfAaXxAd0PAA==} + cpu: [x64] + os: [win32] + + '@shikijs/core@3.23.0': + resolution: {integrity: sha512-NSWQz0riNb67xthdm5br6lAkvpDJRTgB36fxlo37ZzM2yq0PQFFzbd8psqC2XMPgCzo1fW6cVi18+ArJ44wqgA==} + + '@shikijs/engine-javascript@3.23.0': + resolution: {integrity: sha512-aHt9eiGFobmWR5uqJUViySI1bHMqrAgamWE1TYSUoftkAeCCAiGawPMwM+VCadylQtF4V3VNOZ5LmfItH5f3yA==} + + '@shikijs/engine-oniguruma@3.23.0': + resolution: {integrity: sha512-1nWINwKXxKKLqPibT5f4pAFLej9oZzQTsby8942OTlsJzOBZ0MWKiwzMsd+jhzu8YPCHAswGnnN1YtQfirL35g==} + + '@shikijs/langs@3.23.0': + resolution: {integrity: sha512-2Ep4W3Re5aB1/62RSYQInK9mM3HsLeB91cHqznAJMuylqjzNVAVCMnNWRHFtcNHXsoNRayP9z1qj4Sq3nMqYXg==} + + '@shikijs/themes@3.23.0': + resolution: {integrity: sha512-5qySYa1ZgAT18HR/ypENL9cUSGOeI2x+4IvYJu4JgVJdizn6kG4ia5Q1jDEOi7gTbN4RbuYtmHh0W3eccOrjMA==} + + '@shikijs/types@3.23.0': + resolution: {integrity: sha512-3JZ5HXOZfYjsYSk0yPwBrkupyYSLpAE26Qc0HLghhZNGTZg/SKxXIIgoxOpmmeQP0RRSDJTk1/vPfw9tbw+jSQ==} + + '@shikijs/vscode-textmate@10.0.2': + resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} + + '@sindresorhus/merge-streams@4.0.0': + resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==} + engines: {node: '>=18'} + + '@stylistic/eslint-plugin@5.10.0': + resolution: {integrity: sha512-nPK52ZHvot8Ju/0A4ucSX1dcPV2/1clx0kLcH5wDmrE4naKso7TUC/voUyU1O9OTKTrR6MYip6LP0ogEMQ9jPQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^9.0.0 || ^10.0.0 + + '@stylistic/stylelint-plugin@5.2.1': + resolution: {integrity: sha512-BMRWJVtO16uADJmKcGi6mreei/QYdOviABXuaCeHEc3T3pxo4wFYYSvnxIoyiPVfDJqX+DxQdnGkuc6KxsRaSw==} + peerDependencies: + stylelint: ^17.6.0 + + '@tybys/wasm-util@0.10.3': + resolution: {integrity: sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==} + + '@types/babel__core@7.20.5': + resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} + + '@types/babel__generator@7.27.0': + resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==} + + '@types/babel__template@7.4.4': + resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} + + '@types/babel__traverse@7.28.0': + resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} + + '@types/debug@4.1.13': + resolution: {integrity: sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==} + + '@types/esrecurse@4.3.1': + resolution: {integrity: sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==} + + '@types/estree@1.0.9': + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + + '@types/geojson@7946.0.16': + resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==} + + '@types/hast@3.0.5': + resolution: {integrity: sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/leaflet@1.9.21': + resolution: {integrity: sha512-TbAd9DaPGSnzp6QvtYngntMZgcRk+igFELwR2N99XZn7RXUdKgsXMR+28bUO0rPsWp8MIu/f47luLIQuSLYv/w==} + + '@types/mdast@4.0.4': + resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} + + '@types/ms@2.1.0': + resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} + + '@types/nlcst@2.0.3': + resolution: {integrity: sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA==} + + '@types/node@24.13.3': + resolution: {integrity: sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q==} + + '@types/node@26.1.1': + resolution: {integrity: sha512-nxAkRSVkN1Y0JC1W8ky/fTfkGsMmcrRsbx+3XoZE+rMOX71kLYTV7fLXpqud1GpbpP5TuffXFqfX7fH2GgZREw==} + + '@types/papaparse@5.5.2': + resolution: {integrity: sha512-gFnFp/JMzLHCwRf7tQHrNnfhN4eYBVYYI897CGX4MY1tzY9l2aLkVyx2IlKZ/SAqDbB3I1AOZW5gTMGGsqWliA==} + + '@types/prop-types@15.7.15': + resolution: {integrity: sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==} + + '@types/react-dom@18.3.7': + resolution: {integrity: sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==} + peerDependencies: + '@types/react': ^18.0.0 + + '@types/react@18.3.31': + resolution: {integrity: sha512-vfEqpXTvwT91yhmwdfouStN2hSKwTvyRs8qpLfADyrq/kxDw0hZM7Wk9Ug1FELj8hIby+S/+kQCSRFF32nv2Qw==} + + '@types/sax@1.2.7': + resolution: {integrity: sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==} + + '@types/unist@3.0.3': + resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + + '@types/ws@8.18.1': + resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} + + '@typescript-eslint/eslint-plugin@8.63.0': + resolution: {integrity: sha512-rvwSgqT+DHpWdzfSzPatRLm02a0GlESt++9iy3hLCDY4BgkaLcl8LBi9Yh7XGFBpwcBE/K3024QuXWTpbz4FfQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.63.0 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/parser@8.63.0': + resolution: {integrity: sha512-gwh4gvvlaVDKKxyfxMG+Gnu1u9X0OQBwyGLkbwB65dIzBKnxeRiJlNFqlI3zwVhNXJIs6qV7mlFCn/BIajlVig==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/project-service@8.63.0': + resolution: {integrity: sha512-e5dh0/UI0ok53AlZ5wRkXCB32z/f2jUZqPR/ygAw5WYaSw8j9EoJWlS7wQjr/dmOaqWjnPIn2m+HhVPCMWGZVQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/scope-manager@8.63.0': + resolution: {integrity: sha512-uUyfMWCnDSN8bCpcrY8nGP2BLkQ9Xn0GsipcONcpIDWhwhO4ZSyHvyS14U3X75mzxWxL3I2UZIrenTzdzcJO8A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/tsconfig-utils@8.63.0': + resolution: {integrity: sha512-sUAbkulqBAsncKnbRP3+7CtQFRKicexnj7ZwNC6ddCR7EmrXvjvdCYMJbUIqMd6lwoEriZjwLo08aS5tSjVMHg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/type-utils@8.63.0': + resolution: {integrity: sha512-Nzzh/OGxVCOjObjaj1CQF2RUasyYy2Jfuh+zZ3PjLzG2fYRriAiZLib9UKtO+CpQAS3YHiAS+ckZDclwqI1TPA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/types@8.63.0': + resolution: {integrity: sha512-xyLtl9DUBBFrcJS4x2pIqGLH68/tC2uOa4Z7pUteW09D3bXnnXUom4dyPikzWgB7llmIc1zoeI3aoUdC4rPK/Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.63.0': + resolution: {integrity: sha512-ygBkU+B7ex5UI/gKhaqexWev79uISfIv7XQCRNYO/jmD8rGLPyWLAb3KMRT6nd8Gt9bmUBi9+iX6tBdYfOY81Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/utils@8.63.0': + resolution: {integrity: sha512-fUKaeAvrTuQg/Tgt3nliAUSZHJM6DlCcfyEmxCvlX8kieWSStBX+5O5Fnidtc3i2JrH+9c/GL4RY2iasd/GPTA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/visitor-keys@8.63.0': + resolution: {integrity: sha512-UexrHGnGTpbuQHct2ExOc2ZcFbGUS9FOesCxxqdBGcpI1BxYu/LZ6U8Aq6/72XtF/qRBk9nhuGHFJIXXMhPMdw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@ungap/structured-clone@1.3.3': + resolution: {integrity: sha512-60YRaenCQcVjYEKOcG824+DRGGIQ3VKErcBoAEDJZz5bKIs2ZG+X/H9Nk+Q6EVkwJk5QNApxbrc5QtBSwtrXAg==} + + '@vitejs/plugin-react@4.7.0': + resolution: {integrity: sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 + + '@volar/kit@2.4.28': + resolution: {integrity: sha512-cKX4vK9dtZvDRaAzeoUdaAJEew6IdxHNCRrdp5Kvcl6zZOqb6jTOfk3kXkIkG3T7oTFXguEMt5+9ptyqYR84Pg==} + peerDependencies: + typescript: '*' + + '@volar/language-core@2.4.28': + resolution: {integrity: sha512-w4qhIJ8ZSitgLAkVay6AbcnC7gP3glYM3fYwKV3srj8m494E3xtrCv6E+bWviiK/8hs6e6t1ij1s2Endql7vzQ==} + + '@volar/language-server@2.4.28': + resolution: {integrity: sha512-NqcLnE5gERKuS4PUFwlhMxf6vqYo7hXtbMFbViXcbVkbZ905AIVWhnSo0ZNBC2V127H1/2zP7RvVOVnyITFfBw==} + + '@volar/language-service@2.4.28': + resolution: {integrity: sha512-Rh/wYCZJrI5vCwMk9xyw/Z+MsWxlJY1rmMZPsxUoJKfzIRjS/NF1NmnuEcrMbEVGja00aVpCsInJfixQTMdvLw==} + + '@volar/source-map@2.4.28': + resolution: {integrity: sha512-yX2BDBqJkRXfKw8my8VarTyjv48QwxdJtvRgUpNE5erCsgEUdI2DsLbpa+rOQVAJYshY99szEcRDmyHbF10ggQ==} + + '@volar/typescript@2.4.28': + resolution: {integrity: sha512-Ja6yvWrbis2QtN4ClAKreeUZPVYMARDYZl9LMEv1iQ1QdepB6wn0jTRxA9MftYmYa4DQ4k/DaSZpFPUfxl8giw==} + + '@vscode/emmet-helper@2.11.0': + resolution: {integrity: sha512-QLxjQR3imPZPQltfbWRnHU6JecWTF1QSWhx3GAKQpslx7y3Dp6sIIXhKjiUJ/BR9FX8PVthjr9PD6pNwOJfAzw==} + + '@vscode/l10n@0.0.18': + resolution: {integrity: sha512-KYSIHVmslkaCDyw013pphY+d7x1qV8IZupYfeIfzNA+nsaWHbn5uPuQRvdRFsa9zFzGeudPuoGoZ1Op4jrJXIQ==} + + '@whatwg-node/disposablestack@0.0.6': + resolution: {integrity: sha512-LOtTn+JgJvX8WfBVJtF08TGrdjuFzGJc4mkP8EdDI8ADbvO7kiexYep1o8dwnt0okb0jYclCDXF13xU7Ge4zSw==} + engines: {node: '>=18.0.0'} + + '@whatwg-node/fetch@0.10.13': + resolution: {integrity: sha512-b4PhJ+zYj4357zwk4TTuF2nEe0vVtOrwdsrNo5hL+u1ojXNhh1FgJ6pg1jzDlwlT4oBdzfSwaBwMCtFCsIWg8Q==} + engines: {node: '>=18.0.0'} + + '@whatwg-node/node-fetch@0.8.6': + resolution: {integrity: sha512-BDMdYFcerLQkwA2RTldxOqRCs6ZQD1S7UgP3pUdGUkcbgTrP/V5ko77ZkCww9DHmC4lpoYuwigGfQYj285gMvA==} + engines: {node: '>=18.0.0'} + + '@whatwg-node/promise-helpers@1.3.2': + resolution: {integrity: sha512-Nst5JdK47VIl9UcGwtv2Rcgyn5lWtZ0/mhRQ4G8NN2isxpq2TO30iqHzmwoJycjWuyUfg3GFXqP/gFHXeV57IA==} + engines: {node: '>=16.0.0'} + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.17.0: + resolution: {integrity: sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==} + engines: {node: '>=0.4.0'} + hasBin: true + + ajv-draft-04@1.0.0: + resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==} + peerDependencies: + ajv: ^8.5.0 + peerDependenciesMeta: + ajv: + optional: true + + ajv-i18n@4.2.0: + resolution: {integrity: sha512-v/ei2UkCEeuKNXh8RToiFsUclmU+G57LO1Oo22OagNMENIw+Yb8eMwvHu7Vn9fmkjJyv6XclhJ8TbuigSglPkg==} + peerDependencies: + ajv: ^8.0.0-beta.0 + + ajv@6.15.0: + resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==} + + ajv@8.20.0: + resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==} + + ansi-align@3.0.1: + resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==} + + ansi-escapes@7.3.0: + resolution: {integrity: sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==} + engines: {node: '>=18'} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-regex@6.2.2: + resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} + engines: {node: '>=12'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@6.2.3: + resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} + engines: {node: '>=12'} + + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + + arg@5.0.2: + resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + aria-query@5.3.2: + resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} + engines: {node: '>= 0.4'} + + array-iterate@2.0.1: + resolution: {integrity: sha512-I1jXZMjAgCMmxT4qxXfPXa6SthSoE8h6gkSI9BGGNv8mP8G/v0blc+qFnZu6K42vTOiuME596QaLO0TP3Lk0xg==} + + array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + + astral-regex@2.0.0: + resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} + engines: {node: '>=8'} + + astro-eslint-parser@1.4.0: + resolution: {integrity: sha512-+QDcgc7e+au6EZ0YjMmRRjNoQo5bDMlaR45aWDoFsuxQTCM9qmCHRoiKJPELgckJ8Wmr7vcfpa9eCDHBFh6G4w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + astro@5.18.2: + resolution: {integrity: sha512-TnFwLnAXty5MXKPDGuKXqK4AMBXG+FH6RUdK7Oyc3gyfNoFIthT+4eRbzOK43bdRlLaZuxgciDSjgtggZ3OtGQ==} + engines: {node: 18.20.8 || ^20.3.0 || >=22.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0'} + hasBin: true + + astrojs-compiler-sync@1.1.1: + resolution: {integrity: sha512-0mKvB9sDQRIZPsEJadw6OaFbGJ92cJPPR++ICca9XEyiUAZqgVuk25jNmzHPT0KF80rI94trSZrUR5iHFXGGOQ==} + engines: {node: ^18.18.0 || >=20.9.0} + peerDependencies: + '@astrojs/compiler': '>=0.27.0' + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + auto-bind@5.0.1: + resolution: {integrity: sha512-ooviqdwwgfIfNmDwo94wlshcdzfO64XV0Cg6oDsDYBJfITDz1EngD2z7DkbvCWn+XIMsIqW27sEVF6qcpJrRcg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + autoprefixer@10.5.2: + resolution: {integrity: sha512-rD5t5DwOjJdmSORcTq64j8MawTC+tbQ+HHqjR4NDumamy/ambn1UJrlKL+KdwujWxMkFjPM3pPHOEA9tl4767Q==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + + axobject-query@4.1.0: + resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} + engines: {node: '>= 0.4'} + + bail@2.0.2: + resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} + + balanced-match@4.0.4: + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + engines: {node: 18 || 20 || >=22} + + base-64@1.0.0: + resolution: {integrity: sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==} + + baseline-browser-mapping@2.10.43: + resolution: {integrity: sha512-AjYpR78kDWAY3Efj+cDTFH9t9SCoL7OoTp1BOb0mQV7S+6CiLwnWM3FyxhJtdPufDFKzmCSFoUncKjWgJEZTCQ==} + engines: {node: '>=6.0.0'} + hasBin: true + + boolbase@1.0.0: + resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + + boxen@8.0.1: + resolution: {integrity: sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw==} + engines: {node: '>=18'} + + brace-expansion@5.0.7: + resolution: {integrity: sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==} + engines: {node: 18 || 20 || >=22} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + browserslist@4.28.6: + resolution: {integrity: sha512-FQBYNK15VMslhLHpA7+n+n1GOlF1kId2xcCg7/j95f24AOF6VDYMNH4mFxF7KuaTdv627faazpOAjFzMrfJOUw==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + cacheable@2.5.0: + resolution: {integrity: sha512-60cyAOytib/OzBw1JNSoSV/boK1AtHryDIjvVBk7XbN4ugfkM3+Sry7fEjNgPMGgOjuaZPAp8ruZ0Cxafwyq9g==} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + camelcase@8.0.0: + resolution: {integrity: sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==} + engines: {node: '>=16'} + + caniuse-lite@1.0.30001805: + resolution: {integrity: sha512-52noaS3DubycKSXaU30TwPGIp+POyQSUVa5jBEq3vkRkY0kjyb3LQgvhU6WGyCcyXqVLWO0Cw0Q6BSdD0kUfVA==} + + ccount@2.0.1: + resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + + chalk@5.6.2: + resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + + change-case-all@2.1.0: + resolution: {integrity: sha512-v6b0WWWkZUMHVuYk82l+WROgkUm4qEN2w5hKRNWtEOYwWqUGoi8C6xH0l1RLF1EoWqDFK6MFclmN3od6ws3/uw==} + + change-case@5.4.4: + resolution: {integrity: sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==} + + character-entities-html4@2.1.0: + resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} + + character-entities-legacy@3.0.0: + resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} + + character-entities@2.0.2: + resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} + + chardet@2.2.0: + resolution: {integrity: sha512-rddelWYNPRrXq6PtNEN2S3f6t9ILzvqaN5pVgi4kqt9jHQaXIial9PznB5iSPVlQSLNaaH22ItWz3EJtQ10+OA==} + + chokidar@4.0.3: + resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} + engines: {node: '>= 14.16.0'} + + chokidar@5.0.0: + resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==} + engines: {node: '>= 20.19.0'} + + ci-info@4.4.0: + resolution: {integrity: sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==} + engines: {node: '>=8'} + + cli-boxes@3.0.0: + resolution: {integrity: sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==} + engines: {node: '>=10'} + + cli-cursor@5.0.0: + resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} + engines: {node: '>=18'} + + cli-truncate@5.2.0: + resolution: {integrity: sha512-xRwvIOMGrfOAnM1JYtqQImuaNtDEv9v6oIYAs4LIHwTiKee8uwvIi363igssOC0O5U04i4AlENs79LQLu9tEMw==} + engines: {node: '>=20'} + + cli-width@4.1.0: + resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} + engines: {node: '>= 12'} + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + cliui@9.0.1: + resolution: {integrity: sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==} + engines: {node: '>=20'} + + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + color-string@1.9.1: + resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} + + color@4.2.3: + resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==} + engines: {node: '>=12.5.0'} + + colord@2.9.3: + resolution: {integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==} + + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + + comma-separated-tokens@2.0.3: + resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + + commander@11.1.0: + resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} + engines: {node: '>=16'} + + common-ancestor-path@1.0.1: + resolution: {integrity: sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w==} + + common-tags@1.8.2: + resolution: {integrity: sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==} + engines: {node: '>=4.0.0'} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + cookie-es@1.2.3: + resolution: {integrity: sha512-lXVyvUvrNXblMqzIRrxHb57UUVmqsSWlxqt3XIjCkUP0wDAf6uicO6KMbEgYrMNtEvWgWHwe42CKxPu9MYAnWw==} + + cookie@1.1.1: + resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==} + engines: {node: '>=18'} + + cosmiconfig@8.3.6: + resolution: {integrity: sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==} + engines: {node: '>=14'} + peerDependencies: + typescript: '>=4.9.5' + peerDependenciesMeta: + typescript: + optional: true + + cosmiconfig@9.0.2: + resolution: {integrity: sha512-gtTZxTDau1wL7Y7zifc2dd8jHSK/k6BTx/2Xp/BpdlAdnlYWFVt7qhJqgwi7637yRwRQ3qL4ZidbB4I8tA5VOg==} + engines: {node: '>=14'} + peerDependencies: + typescript: '>=4.9.5' + peerDependenciesMeta: + typescript: + optional: true + + cross-fetch@3.2.0: + resolution: {integrity: sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==} + + cross-inspect@1.0.1: + resolution: {integrity: sha512-Pcw1JTvZLSJH83iiGWt6fRcT+BjZlCDRVwYLbUcHzv/CRpB7r0MlSrGbIyQvVSNyGnbt7G4AXuyCiDR3POvZ1A==} + engines: {node: '>=16.0.0'} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + crossws@0.3.5: + resolution: {integrity: sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA==} + + css-blank-pseudo@8.0.1: + resolution: {integrity: sha512-C5B2e5hCM4llrQkUms+KnWEMVW8K1n2XvX9G7ppfMZJQ7KAS/4rNnkP1Cs+HhWriOz1mWWTMFD4j1J7s31Dgug==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + css-functions-list@3.3.3: + resolution: {integrity: sha512-8HFEBPKhOpJPEPu70wJJetjKta86Gw9+CCyCnB3sui2qQfOvRyqBy4IKLKKAwdMpWb2lHXWk9Wb4Z6AmaUT1Pg==} + engines: {node: '>=12'} + + css-has-pseudo@8.0.0: + resolution: {integrity: sha512-Uz/bsHRbOeir/5Oeuz85tq/yLJLxX+3dpoRdjNTshs6jjqwUg8XaEZGDd0ci3fw7l53Srw0EkJ8mYan0eW5uGQ==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + css-prefers-color-scheme@11.0.0: + resolution: {integrity: sha512-fv0mgtwUhh2m9iio3Kxc2CkrogjIaRdMFaaqyzSFdii17JF4cfPyMNX72B15ZW2Nrr/NZUpxI4dec1VMHYJvdw==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + css-select@5.2.2: + resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==} + + css-tree@2.2.1: + resolution: {integrity: sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} + + css-tree@3.2.1: + resolution: {integrity: sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + + css-what@6.2.2: + resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==} + engines: {node: '>= 6'} + + cssdb@8.9.0: + resolution: {integrity: sha512-J8jOU/hLjaXcO1LldOLraJSQpfLXRKof0I7mtbRyOy2AAXgqst0x9rlgi2qXeD6d0ou3ZLqcPAMqYVbpCbrxEw==} + + cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + + csso@5.0.5: + resolution: {integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} + + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + + data-uri-to-buffer@4.0.1: + resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} + engines: {node: '>= 12'} + + dataloader@2.2.3: + resolution: {integrity: sha512-y2krtASINtPFS1rSDjacrFgn1dcUuoREVabwlOGOe4SdxenREqwjwjElAdwvbGM7kgZz9a3KVicWR7vcz8rnzA==} + + debounce@3.0.0: + resolution: {integrity: sha512-64byRbF0/AirwbuHqB3/ZpMG9/nckDa6ZA0yd6UnaQNwbbemCOwvz2sL5sjXLHhZHADyiwLm0M5qMhltUUx+TA==} + engines: {node: '>=20'} + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decode-named-character-reference@1.3.0: + resolution: {integrity: sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==} + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + defu@6.1.7: + resolution: {integrity: sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==} + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + dependency-graph@1.0.0: + resolution: {integrity: sha512-cW3gggJ28HZ/LExwxP2B++aiKxhJXMSIt9K48FOXQkm+vuG5gyatXnLsONRJdzO/7VfjDIiaOOa/bs4l464Lwg==} + engines: {node: '>=4'} + + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + + destr@2.0.5: + resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==} + + detect-indent@7.0.2: + resolution: {integrity: sha512-y+8xyqdGLL+6sh0tVeHcfP/QDd8gUgbasolJJpY7NgeQGSZ739bDtSiaiDgtoicy+mtYB81dKLxO9xRhCyIB3A==} + engines: {node: '>=12.20'} + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + deterministic-object-hash@2.0.2: + resolution: {integrity: sha512-KxektNH63SrbfUyDiwXqRb1rLwKt33AmMv+5Nhsw1kqZ13SJBRTgZHtGbE+hH3a1mVW1cz+4pqSWVPAtLVXTzQ==} + engines: {node: '>=18'} + + devalue@5.8.1: + resolution: {integrity: sha512-4CXDYRBGqN+57wVJkuXBYmpAVUSg3L6JAQa/DFqm238G73E1wuyc/JhGQJzN7vUf/CMphYau2zXbfWzDR5aTEw==} + + devlop@1.1.0: + resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + + diff@8.0.4: + resolution: {integrity: sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==} + engines: {node: '>=0.3.1'} + + dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + + dlv@1.1.3: + resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} + + dom-serializer@2.0.0: + resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} + + domelementtype@2.3.0: + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + + domhandler@5.0.3: + resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} + engines: {node: '>= 4'} + + domutils@3.2.2: + resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} + + dset@3.1.4: + resolution: {integrity: sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==} + engines: {node: '>=4'} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + electron-to-chromium@1.5.389: + resolution: {integrity: sha512-cEto7aeOqBfU1D+c5py5pE+ooscKE75JifxLBdFUZsqAxRS6y7kebtxAZvICszSl05gPjYHDTjY+lXpyGvpJbg==} + + emmet@2.4.11: + resolution: {integrity: sha512-23QPJB3moh/U9sT4rQzGgeyyGIrcM+GH5uVYg2C6wZIxAIJq7Ng3QLT79tl8FUwDXhyq9SusfknOrofAKqvgyQ==} + + emoji-regex@10.6.0: + resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + + entities@6.0.1: + resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} + engines: {node: '>=0.12'} + + entities@7.0.1: + resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} + engines: {node: '>=0.12'} + + env-paths@2.2.1: + resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} + engines: {node: '>=6'} + + environment@1.1.0: + resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} + engines: {node: '>=18'} + + error-ex@1.3.4: + resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-module-lexer@1.7.0: + resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + + es-object-atoms@1.1.2: + resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + esbuild@0.25.12: + resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==} + engines: {node: '>=18'} + hasBin: true + + esbuild@0.27.7: + resolution: {integrity: sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==} + engines: {node: '>=18'} + hasBin: true + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + escape-string-regexp@5.0.0: + resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} + engines: {node: '>=12'} + + eslint-compat-utils@0.6.5: + resolution: {integrity: sha512-vAUHYzue4YAa2hNACjB8HvUQj5yehAZgiClyFVVom9cP8z5NSFq3PwB/TtJslN2zAMgRX6FCFCjYBbQh71g5RQ==} + engines: {node: '>=12'} + peerDependencies: + eslint: '>=6.0.0' + + eslint-plugin-astro@1.7.0: + resolution: {integrity: sha512-89xpAn528UKCdmyysbg0AHHqi6sqcK89wXnJIpu4F0mFBN03zATEBNK7pRtMfl6gwtMOm5ECXs+Wz5qDHhwTFw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: '>=8.57.0' + + eslint-plugin-react-hooks@7.1.1: + resolution: {integrity: sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==} + engines: {node: '>=18'} + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0 + + eslint-scope@8.4.0: + resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-scope@9.1.2: + resolution: {integrity: sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@4.2.1: + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@5.0.1: + resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + eslint@10.7.0: + resolution: {integrity: sha512-GVTD7s1vdIl6UYvAfriOPeY1Df8LIZjfofLvHwde+erDHGGuHyuM6xoxRxmHiebhYuD2p1vN4wWh0XzPARSGDQ==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + espree@10.4.0: + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + espree@11.2.0: + resolution: {integrity: sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + esquery@1.7.0: + resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + eventemitter3@5.0.4: + resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==} + + extend@3.0.2: + resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + + extract-files@9.0.0: + resolution: {integrity: sha512-CvdFfHkC95B4bBBk36hcEmvdR2awOdhhVUYH6S/zrVj3477zven/fJMYg7121h4T1xHZC+tetUpubpAhxwI7hQ==} + engines: {node: ^10.17.0 || ^12.0.0 || >= 13.7.0} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fast-string-truncated-width@3.0.3: + resolution: {integrity: sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==} + + fast-string-width@3.0.2: + resolution: {integrity: sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==} + + fast-uri@3.1.3: + resolution: {integrity: sha512-i70LwGWUduXqzicKXWshooq+sWL1K3WUU5rKZNG/0i3a1OSoX3HqhH5WbWwTmqWfor4urUakGPiRQcleRZTwOg==} + + fast-wrap-ansi@0.2.2: + resolution: {integrity: sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==} + + fastest-levenshtein@1.0.16: + resolution: {integrity: sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==} + engines: {node: '>= 4.9.1'} + + fastq@1.20.1: + resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} + + fd-package-json@2.0.0: + resolution: {integrity: sha512-jKmm9YtsNXN789RS/0mSzOC1NUq9mkVd65vbSSVsKdjGvYXBuE4oWe2QOEoFeRmJg+lPuZxpmrfFclNhoRMneQ==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + fetch-blob@3.2.0: + resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} + engines: {node: ^12.20 || >= 14.13} + + file-entry-cache@11.1.5: + resolution: {integrity: sha512-+PFTHITI08JIGhnNpGNI8T8inUpgZfk3GNEqfT9R2zZV2iFXg3CvqzSl/uEhs7TSGujYRELEANyDvS8Fj7+S7Q==} + + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + + flat-cache@6.1.23: + resolution: {integrity: sha512-f++BY9pTk+983xK1FLzlLpmM0i0z+jHmx3QESGkURMXujQZz1k5wzwX6hjnQ8goaD0B+sYnDK1yZ6MTyZfUaqA==} + + flatted@3.4.2: + resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==} + + flattie@1.1.1: + resolution: {integrity: sha512-9UbaD6XdAL97+k/n+N7JwX46K/M6Zc6KcFYskrYL8wbBV/Uyk0CTAMY0VT+qiK5PM7AIc9aTWYtq65U7T+aCNQ==} + engines: {node: '>=8'} + + fontace@0.4.1: + resolution: {integrity: sha512-lDMvbAzSnHmbYMTEld5qdtvNH2/pWpICOqpean9IgC7vUbUJc3k+k5Dokp85CegamqQpFbXf0rAVkbzpyTA8aw==} + + fontkitten@1.0.3: + resolution: {integrity: sha512-Wp1zXWPVUPBmfoa3Cqc9ctaKuzKAV6uLstRqlR56kSjplf5uAce+qeyYym7F+PHbGTk+tCEdkCW6RD7DX/gBZw==} + engines: {node: '>=20'} + + form-data@3.0.5: + resolution: {integrity: sha512-j23EibVLnp4zNXGW7LjryXYa2X6U/M96yoOX+ybZxwkYajdxRNEqYY3zhh7y0i6kfISKS2jr+EJq1YTUDEv5+w==} + engines: {node: '>= 6'} + + formatly@0.3.0: + resolution: {integrity: sha512-9XNj/o4wrRFyhSMJOvsuyMwy8aUfBaZ1VrqHVfohyXf0Sw0e+yfKG+xZaY3arGCOMdwFsqObtzVOc1gU9KiT9w==} + engines: {node: '>=18.3.0'} + hasBin: true + + formdata-polyfill@4.0.10: + resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} + engines: {node: '>=12.20.0'} + + fraction.js@5.3.4: + resolution: {integrity: sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==} + + fsevents@2.3.2: + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + get-east-asian-width@1.6.0: + resolution: {integrity: sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==} + engines: {node: '>=18'} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + get-tsconfig@4.14.0: + resolution: {integrity: sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==} + + github-slugger@2.0.0: + resolution: {integrity: sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + global-modules@2.0.0: + resolution: {integrity: sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==} + engines: {node: '>=6'} + + global-prefix@3.0.0: + resolution: {integrity: sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==} + engines: {node: '>=6'} + + globals@16.5.0: + resolution: {integrity: sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==} + engines: {node: '>=18'} + + globals@17.7.0: + resolution: {integrity: sha512-Czmyns5dUsq4seFBR/Kdydhmo8y9kC79hiSkPn0YcGtNnYWnrgt0vjrSjx9tspoDGWm2CMarffRuLjM4xUz8xg==} + engines: {node: '>=18'} + + globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + + globby@16.2.1: + resolution: {integrity: sha512-JmsqJalahxxgW8V2ecSQ2G7UjPlI9cpKdrkG9KoNiXhd/YslXOTEB0cViENWUznuovIuNT+FkMbraDGjr4FCUg==} + engines: {node: '>=20'} + + globjoin@0.1.4: + resolution: {integrity: sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + graphql-config@5.1.6: + resolution: {integrity: sha512-fCkYnm4Kdq3un0YIM4BCZHVR5xl0UeLP6syxxO7KAstdY7QVyVvTHP0kRPDYEP1v08uwtJVgis5sj3IOTLOniQ==} + engines: {node: '>= 16.0.0'} + peerDependencies: + cosmiconfig-toml-loader: ^1.0.0 + graphql: ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + peerDependenciesMeta: + cosmiconfig-toml-loader: + optional: true + + graphql-request@5.2.0: + resolution: {integrity: sha512-pLhKIvnMyBERL0dtFI3medKqWOz/RhHdcgbZ+hMMIb32mEPa5MJSzS4AuXxfI4sRAu6JVVk5tvXuGfCWl9JYWQ==} + peerDependencies: + graphql: 14 - 16 + + graphql-tag@2.12.7: + resolution: {integrity: sha512-xnE/NFzy+0eIesvAsREJZ284zTl/wYuBAvpsFSDhRGRdRHdnE90M21Q3xAWyYInb0J756c6x0pIQ62+vtvOs1Q==} + engines: {node: '>=10'} + peerDependencies: + graphql: ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + + graphql-ws@6.1.0: + resolution: {integrity: sha512-7ft6KWkuaLnLABwzEIimjUMeF0iByo2ThD6q0MICgsvp6nDuT5ppubKzEHniu8Kmlp5GNsLgr5dil8JMrIwUEQ==} + engines: {node: '>=20'} + peerDependencies: + '@fastify/websocket': ^10 || ^11 + crossws: ~0.3 + graphql: ^15.10.1 || ^16 || ^17 + ws: ^8 + peerDependenciesMeta: + '@fastify/websocket': + optional: true + crossws: + optional: true + ws: + optional: true + + graphql@16.14.2: + resolution: {integrity: sha512-Chq1s4CY7jmh8gO2qvLIJyfCDIN+EHLFW/9iShnp1z8FjBQMoodWP1kDC36VAMXXIvAjj4ARa7ntfAV2BrjsbA==} + engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} + + h3@1.15.11: + resolution: {integrity: sha512-L3THSe2MPeBwgIZVSH5zLdBBU90TOxarvhK9d04IDY2AmVS8j2Jz2LIWtwsGOU3lu2I5jCN7FNvVfY2+XyF+mg==} + + has-flag@5.0.1: + resolution: {integrity: sha512-CsNUt5x9LUdx6hnk/E2SZLsDyvfqANZSUq4+D3D8RzDJ2M+HDTIkF60ibS1vHaK55vzgiZw1bEPFG9yH7l33wA==} + engines: {node: '>=12'} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hashery@1.5.1: + resolution: {integrity: sha512-iZyKG96/JwPz1N55vj2Ie2vXbhu440zfUfJvSwEqEbeLluk7NnapfGqa7LH0mOsnDxTF85Mx8/dyR6HfqcbmbQ==} + engines: {node: '>=20'} + + hasown@2.0.4: + resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} + engines: {node: '>= 0.4'} + + hast-util-from-html@2.0.3: + resolution: {integrity: sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==} + + hast-util-from-parse5@8.0.3: + resolution: {integrity: sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==} + + hast-util-is-element@3.0.0: + resolution: {integrity: sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==} + + hast-util-parse-selector@4.0.0: + resolution: {integrity: sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==} + + hast-util-raw@9.1.0: + resolution: {integrity: sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==} + + hast-util-to-html@9.0.5: + resolution: {integrity: sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==} + + hast-util-to-parse5@8.0.1: + resolution: {integrity: sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA==} + + hast-util-to-text@4.0.2: + resolution: {integrity: sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A==} + + hast-util-whitespace@3.0.0: + resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} + + hastscript@9.0.1: + resolution: {integrity: sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==} + + hermes-estree@0.25.1: + resolution: {integrity: sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==} + + hermes-parser@0.25.1: + resolution: {integrity: sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==} + + hookified@1.15.1: + resolution: {integrity: sha512-MvG/clsADq1GPM2KGo2nyfaWVyn9naPiXrqIe4jYjXNZQt238kWyOGrsyc/DmRAQ+Re6yeo6yX/yoNCG5KAEVg==} + + hookified@2.2.0: + resolution: {integrity: sha512-p/LgFzRN5FeoD3DLS6bkUapeye6E4SI6yJs6KetENd18S+FBthqYq2amJUWpt5z0EQwwHemidjY5OqJGEKm5uA==} + + html-escaper@3.0.3: + resolution: {integrity: sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==} + + html-tags@5.1.0: + resolution: {integrity: sha512-n6l5uca7/y5joxZ3LUePhzmBFUJ+U2YWzhMa8XUTecSeSlQiZdF5XAd/Q3/WUl0VsXgUwWi8I7CNIwdI5WN1SQ==} + engines: {node: '>=20.10'} + + html-void-elements@3.0.0: + resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} + + http-cache-semantics@4.2.0: + resolution: {integrity: sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==} + + i18next@24.2.3: + resolution: {integrity: sha512-lfbf80OzkocvX7nmZtu7nSTNbrTYR52sLWxPtlXX1zAhVw8WEnFk4puUkCR4B1dNQwbSpEHHHemcZu//7EcB7A==} + peerDependencies: + typescript: ^5 + peerDependenciesMeta: + typescript: + optional: true + + iconv-lite@0.7.3: + resolution: {integrity: sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==} + engines: {node: '>=0.10.0'} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + ignore@7.0.6: + resolution: {integrity: sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==} + engines: {node: '>= 4'} + + immutable@5.1.9: + resolution: {integrity: sha512-m8nVez3rwrgmWxtLMt1ZYXB2Lv7OKYn/disyxAlSDYAlKSlFoPPfIAmAM/M5xqL4m4C/wAPw7S2/CNaUii1Hxg==} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + import-from@4.0.0: + resolution: {integrity: sha512-P9J71vT5nLlDeV8FHs5nNxaLbrpfAV5cF5srvbZfpwpcJoM/xZR3hiv+q+SAnuSmuGbXMWud063iIMx/V/EWZQ==} + engines: {node: '>=12.2'} + + import-meta-resolve@4.2.0: + resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + ini@1.3.8: + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + + invariant@2.2.4: + resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==} + + iron-webcrypto@1.2.1: + resolution: {integrity: sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==} + + is-absolute@1.0.0: + resolution: {integrity: sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==} + engines: {node: '>=0.10.0'} + + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + + is-arrayish@0.3.4: + resolution: {integrity: sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==} + + is-docker@3.0.0: + resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + hasBin: true + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-fullwidth-code-point@5.1.0: + resolution: {integrity: sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==} + engines: {node: '>=18'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-inside-container@1.0.0: + resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} + engines: {node: '>=14.16'} + hasBin: true + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-path-inside@4.0.0: + resolution: {integrity: sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==} + engines: {node: '>=12'} + + is-plain-obj@4.1.0: + resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} + engines: {node: '>=12'} + + is-relative@1.0.0: + resolution: {integrity: sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==} + engines: {node: '>=0.10.0'} + + is-unc-path@1.0.0: + resolution: {integrity: sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==} + engines: {node: '>=0.10.0'} + + is-unicode-supported@2.1.0: + resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==} + engines: {node: '>=18'} + + is-windows@1.0.2: + resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} + engines: {node: '>=0.10.0'} + + is-wsl@3.1.1: + resolution: {integrity: sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==} + engines: {node: '>=16'} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + isomorphic-ws@5.0.0: + resolution: {integrity: sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==} + peerDependencies: + ws: '*' + + isows@1.0.7: + resolution: {integrity: sha512-I1fSfDCZL5P0v33sVqeTDSpcstAg/N+wF5HS033mogOVIp4B+oHC7oOCsA3axAbBSGTJ8QubbNmnIRN/h8U7hg==} + peerDependencies: + ws: '*' + + jiti@2.7.0: + resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} + hasBin: true + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@4.3.0: + resolution: {integrity: sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==} + hasBin: true + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + json-to-pretty-yaml@1.2.2: + resolution: {integrity: sha512-rvm6hunfCcqegwYaG5T4yKJWxc9FXFgBVrcTZ4XfSVRwa5HA/Xs+vB/Eo9treYYHCeNM0nrSUr82V/M31Urc7A==} + engines: {node: '>= 0.2.0'} + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + jsonc-parser@2.3.1: + resolution: {integrity: sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg==} + + jsonc-parser@3.3.1: + resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + keyv@5.6.0: + resolution: {integrity: sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==} + + kind-of@6.0.3: + resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} + engines: {node: '>=0.10.0'} + + kleur@3.0.3: + resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} + engines: {node: '>=6'} + + kleur@4.1.5: + resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} + engines: {node: '>=6'} + + knip@6.26.0: + resolution: {integrity: sha512-e9eELEEpBpGTd4H4HB7818/DYj9dMzMyUqAddfYwUN/EbSkgIjOuWEF96W/xHsmV0SDrsdXjIM+oZ2xpPzPsBA==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + + leaflet@1.9.4: + resolution: {integrity: sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + listr2@10.2.2: + resolution: {integrity: sha512-JtNtbZj8q5BnDMR7trpwvwk3RIrANtIVzEUm8w7amp6xelLgyuq+4WZoTH913XaQAoH/cNdYhaNzBPA2U3xbDw==} + engines: {node: '>=22.13.0'} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lodash.sortby@4.7.0: + resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==} + + lodash.truncate@4.4.2: + resolution: {integrity: sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==} + + log-symbols@7.0.1: + resolution: {integrity: sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg==} + engines: {node: '>=18'} + + log-update@6.1.0: + resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} + engines: {node: '>=18'} + + longest-streak@3.1.0: + resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} + + loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + + lru-cache@11.5.2: + resolution: {integrity: sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==} + engines: {node: 20 || >=22} + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + magicast@0.5.3: + resolution: {integrity: sha512-pVKE4UdSQ7DvHzivsCIFx2BJn1mHG6KsyrFcaxFx6tONdneEuThrDx0Cj3AMg58KyN4pzYT+LHOotxDQDjNvkw==} + + map-cache@0.2.2: + resolution: {integrity: sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==} + engines: {node: '>=0.10.0'} + + markdown-table@3.0.4: + resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + mathml-tag-names@4.0.0: + resolution: {integrity: sha512-aa6AU2Pcx0VP/XWnh8IGL0SYSgQHDT6Ucror2j2mXeFAlN3ahaNs8EZtG1YiticMkSLj3Gt6VPFfZogt7G5iFQ==} + + mdast-util-definitions@6.0.0: + resolution: {integrity: sha512-scTllyX6pnYNZH/AIp/0ePz6s4cZtARxImwoPJ7kS42n+MnVsI4XbnG6d4ibehRIldYMWM2LD7ImQblVhUejVQ==} + + mdast-util-find-and-replace@3.0.2: + resolution: {integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==} + + mdast-util-from-markdown@2.0.3: + resolution: {integrity: sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==} + + mdast-util-gfm-autolink-literal@2.0.1: + resolution: {integrity: sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==} + + mdast-util-gfm-footnote@2.1.0: + resolution: {integrity: sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==} + + mdast-util-gfm-strikethrough@2.0.0: + resolution: {integrity: sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==} + + mdast-util-gfm-table@2.0.0: + resolution: {integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==} + + mdast-util-gfm-task-list-item@2.0.0: + resolution: {integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==} + + mdast-util-gfm@3.1.0: + resolution: {integrity: sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==} + + mdast-util-phrasing@4.1.0: + resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==} + + mdast-util-to-hast@13.2.1: + resolution: {integrity: sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==} + + mdast-util-to-markdown@2.1.2: + resolution: {integrity: sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==} + + mdast-util-to-string@4.0.0: + resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} + + mdn-data@2.0.28: + resolution: {integrity: sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==} + + mdn-data@2.27.1: + resolution: {integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==} + + meow@14.1.0: + resolution: {integrity: sha512-EDYo6VlmtnumlcBCbh1gLJ//9jvM/ndXHfVXIFrZVr6fGcwTUyCTFNTLCKuY3ffbK8L/+3Mzqnd58RojiZqHVw==} + engines: {node: '>=20'} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + meros@1.3.2: + resolution: {integrity: sha512-Q3mobPbvEx7XbwhnC1J1r60+5H6EZyNccdzSz0eGexJRwouUtTZxPVRGdqKtxlpD84ScK4+tIGldkqDtCKdI0A==} + engines: {node: '>=13'} + peerDependencies: + '@types/node': '>=13' + peerDependenciesMeta: + '@types/node': + optional: true + + micromark-core-commonmark@2.0.3: + resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} + + micromark-extension-gfm-autolink-literal@2.1.0: + resolution: {integrity: sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==} + + micromark-extension-gfm-footnote@2.1.0: + resolution: {integrity: sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==} + + micromark-extension-gfm-strikethrough@2.1.0: + resolution: {integrity: sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==} + + micromark-extension-gfm-table@2.1.1: + resolution: {integrity: sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==} + + micromark-extension-gfm-tagfilter@2.0.0: + resolution: {integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==} + + micromark-extension-gfm-task-list-item@2.1.0: + resolution: {integrity: sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==} + + micromark-extension-gfm@3.0.0: + resolution: {integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==} + + micromark-factory-destination@2.0.1: + resolution: {integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==} + + micromark-factory-label@2.0.1: + resolution: {integrity: sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==} + + micromark-factory-space@2.0.1: + resolution: {integrity: sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==} + + micromark-factory-title@2.0.1: + resolution: {integrity: sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==} + + micromark-factory-whitespace@2.0.1: + resolution: {integrity: sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==} + + micromark-util-character@2.1.1: + resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} + + micromark-util-chunked@2.0.1: + resolution: {integrity: sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==} + + micromark-util-classify-character@2.0.1: + resolution: {integrity: sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==} + + micromark-util-combine-extensions@2.0.1: + resolution: {integrity: sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==} + + micromark-util-decode-numeric-character-reference@2.0.2: + resolution: {integrity: sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==} + + micromark-util-decode-string@2.0.1: + resolution: {integrity: sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==} + + micromark-util-encode@2.0.1: + resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} + + micromark-util-html-tag-name@2.0.1: + resolution: {integrity: sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==} + + micromark-util-normalize-identifier@2.0.1: + resolution: {integrity: sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==} + + micromark-util-resolve-all@2.0.1: + resolution: {integrity: sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==} + + micromark-util-sanitize-uri@2.0.1: + resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} + + micromark-util-subtokenize@2.1.0: + resolution: {integrity: sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==} + + micromark-util-symbol@2.0.1: + resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} + + micromark-util-types@2.0.2: + resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==} + + micromark@4.0.2: + resolution: {integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + mimic-function@5.0.1: + resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} + engines: {node: '>=18'} + + minimatch@10.2.5: + resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==} + engines: {node: 18 || 20 || >=22} + + mrmime@2.0.1: + resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} + engines: {node: '>=10'} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + muggle-string@0.4.1: + resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==} + + mute-stream@3.0.0: + resolution: {integrity: sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw==} + engines: {node: ^20.17.0 || >=22.9.0} + + nanoid@3.3.16: + resolution: {integrity: sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + neotraverse@0.6.18: + resolution: {integrity: sha512-Z4SmBUweYa09+o6pG+eASabEpP6QkQ70yHj351pQoEXIs8uHbaU2DWVmzBANKgflPa47A50PtB2+NgRpQvr7vA==} + engines: {node: '>= 10'} + + nlcst-to-string@4.0.0: + resolution: {integrity: sha512-YKLBCcUYKAg0FNlOBT6aI91qFmSiFKiluk655WzPF+DDMA02qIyy8uiRqI8QXtcFpEvll12LpL5MXqEmAZ+dcA==} + + node-domexception@1.0.0: + resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} + engines: {node: '>=10.5.0'} + deprecated: Use your platform's native DOMException instead + + node-fetch-native@1.6.7: + resolution: {integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==} + + node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + + node-fetch@3.3.2: + resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + node-mock-http@1.0.4: + resolution: {integrity: sha512-8DY+kFsDkNXy1sJglUfuODx1/opAGJGyrTuFqEoN90oRc2Vk0ZbD4K2qmKXBBEhZQzdKHIVfEJpDU8Ak2NJEvQ==} + + node-releases@2.0.51: + resolution: {integrity: sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ==} + engines: {node: '>=18'} + + normalize-path@2.1.1: + resolution: {integrity: sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==} + engines: {node: '>=0.10.0'} + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + nth-check@2.1.1: + resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + + ofetch@1.5.1: + resolution: {integrity: sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA==} + + ohash@2.0.11: + resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==} + + onetime@7.0.0: + resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} + engines: {node: '>=18'} + + oniguruma-parser@0.12.2: + resolution: {integrity: sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw==} + + oniguruma-to-es@4.3.6: + resolution: {integrity: sha512-csuQ9x3Yr0cEIs/Zgx/OEt9iBw9vqIunAPQkx19R/fiMq2oGVTgcMqO/V3Ybqefr1TBvosI6jU539ksaBULJyA==} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + oxc-parser@0.137.0: + resolution: {integrity: sha512-yFImD+WLElJpLKy8llG1qe4DCmMsL18peRp8XP1JKfig/gISbJkglnpDtX2aTmAn10kZF7164HbN2H8QPsXxGg==} + engines: {node: ^20.19.0 || >=22.12.0} + + oxc-resolver@11.21.3: + resolution: {integrity: sha512-2Mx3fKQz7+xgrBONjsxOgCGtMHOn38/HxMzW1I5efwXB5a4lRN0Vp40gYUJFBWJslcrvwoofTrqoTnLbwTd3pA==} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-limit@6.2.0: + resolution: {integrity: sha512-kuUqqHNUqoIWp/c467RI4X6mmyuojY5jGutNU0wVTmEOOfcuwLqyMVoAi9MKi2Ak+5i9+nhmrK4ufZE8069kHA==} + engines: {node: '>=18'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + p-queue@8.1.1: + resolution: {integrity: sha512-aNZ+VfjobsWryoiPnEApGGmf5WmNsCo9xu8dfaYamG5qaLP7ClhLN6NgsFe6SwJ2UbLEBK5dv9x8Mn5+RVhMWQ==} + engines: {node: '>=18'} + + p-timeout@6.1.4: + resolution: {integrity: sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg==} + engines: {node: '>=14.16'} + + package-manager-detector@1.7.0: + resolution: {integrity: sha512-xg1eHpwYL/D/HEdWw2goFZP6vV0FH7W+PZ5rFkGjdIDLtxq7EkzBUeT3m+lndYCt8wKbmofUu1MUdMCXkCk9ZQ==} + + papaparse@5.5.4: + resolution: {integrity: sha512-SwzWD9gl/ElwYLCI0nUja1mFJzjq2D8ziShfNBa7zCHzkOozeOGDwHWQ+tvCzEZcewecWZ5U7kUopDnG+DFYEQ==} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + parse-filepath@1.0.2: + resolution: {integrity: sha512-FwdRXKCohSVeXqwtYonZTXtbGJKrn+HNyWDYVcp5yuJlesTwNH4rsmRZ+GrKAPJ5bLpRxESMeS+Rl0VCHRvB2Q==} + engines: {node: '>=0.8'} + + parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + + parse-latin@7.0.0: + resolution: {integrity: sha512-mhHgobPPua5kZ98EF4HWiH167JWBfl4pvAIXXdbaVohtK7a6YBOy56kvhCqduqyo/f3yrHFWmqmiMg/BkBkYYQ==} + + parse5@7.3.0: + resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} + + path-browserify@1.0.1: + resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-root-regex@0.1.2: + resolution: {integrity: sha512-4GlJ6rZDhQZFE0DPVKh0e9jmZ5egZfxTkp7bcRDuPlJXbAwhxcl2dINPUAsjLdejqaLsCeg8axcLjIbvBjN4pQ==} + engines: {node: '>=0.10.0'} + + path-root@0.1.1: + resolution: {integrity: sha512-QLcPegTHF11axjfojBIoDygmS2E3Lf+8+jI6wOVmNVenrKSo3mFdSGiIgdSHenczw3wPtlVMQaFVwGmM7BJdtg==} + engines: {node: '>=0.10.0'} + + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + + piccolore@0.1.3: + resolution: {integrity: sha512-o8bTeDWjE086iwKrROaDf31K0qC/BENdm15/uH9usSC/uZjJOKb2YGiVHfLY4GhwsERiPI1jmwI2XrA7ACOxVw==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.2: + resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} + engines: {node: '>=8.6'} + + picomatch@4.0.5: + resolution: {integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==} + engines: {node: '>=12'} + + playwright-core@1.61.1: + resolution: {integrity: sha512-h7Qlt6m4REp25qvIdvbDtVmD4LqVXfpRxhORv9L0jzETM05p4fuPJ3dKyuSXQxDSbXnmS79HAgi9589lGSpLkg==} + engines: {node: '>=18'} + hasBin: true + + playwright@1.61.1: + resolution: {integrity: sha512-DWnY5o3YbLWK4GovuAVwpqL+1VwGNdUGrRr++8j8PtQQzvAVZUIMjKQ90fY689sEJZJBbZVw1rXaOKSTitkzPQ==} + engines: {node: '>=18'} + hasBin: true + + postcss-attribute-case-insensitive@8.0.0: + resolution: {integrity: sha512-fovIPEV35c2JzVXdmP+sp2xirbBMt54J+upU8u6TSj410kUU5+axgEzvBBSAX8KCybze8CFCelzFAw/FfWg2TA==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + postcss-browser-comments@6.0.2: + resolution: {integrity: sha512-bZFLM8UZupVsuZDR4zFbzrPtKN6Xqpgj+C+vaxlL8r5E0cyhSO4OD3z+MjKstoQsIaKiQS+/Xci5jBUGyo9HlA==} + engines: {node: '>=18'} + peerDependencies: + browserslist: ^4.28.0 + postcss: ^8.4 + + postcss-clamp@4.1.0: + resolution: {integrity: sha512-ry4b1Llo/9zz+PKC+030KUnPITTJAHeOwjfAyyB60eT0AorGLdzp52s31OsPRHRf8NchkgFoG2y6fCfn1IV1Ow==} + engines: {node: '>=7.6.0'} + peerDependencies: + postcss: ^8.4.6 + + postcss-color-functional-notation@8.0.6: + resolution: {integrity: sha512-JwHsQNb/zzjRN/RFdhWWb3bk6WiNs7KW85+vYtLP1YVL6MgPQn/2qNMgFrjD8Ymg+wfP3XlwtSrYUI+K4zPvHA==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + postcss-color-hex-alpha@11.0.0: + resolution: {integrity: sha512-NCGa6vjIyrjosz9GqRxVKbONBklz5TeipYqTJp3IqbnBWlBq5e5EMtG6MaX4vqk9LzocPfMQkuRK9tfk+OQuKg==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + postcss-color-rebeccapurple@11.0.0: + resolution: {integrity: sha512-g9561mx7cbdqx7XeO/L+lJzVlzu7bICyXr72efBVKZGxIhvBBJf9fGXn3Cb6U4Bwh3LbzQO2e9NWBLVYdX5Eag==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + postcss-custom-media@12.0.1: + resolution: {integrity: sha512-66syE14+VeqkUf0rRX0bvbTCbNRJF132jD+ceo8th1dap2YJEAqpdh5uG98CE3IbgHT7m9XM0GIlOazNWqQdeA==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + postcss-custom-properties@15.0.1: + resolution: {integrity: sha512-cuyq8sd8dLY0GLbelz1KB8IMIoDECo6RVXMeHeXY2Uw3Q05k/d1GVITdaKLsheqrHbnxlwxzSRZQQ5u+rNtbMg==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + postcss-custom-selectors@9.0.1: + resolution: {integrity: sha512-2XBELy4DmdVKimChfaZ2id9u9CSGYQhiJ53SvlfBvMTzLMW2VxuMb9rHsMSQw9kRq/zSbhT5x13EaK8JSmK8KQ==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + postcss-dir-pseudo-class@10.0.0: + resolution: {integrity: sha512-DmtIzULpyC8XaH4b5AaUgt4Jic4QmrECqidNCdR7u7naQFdnxX80YI06u238a+ZVRXwURDxVzy0s/UQnWmpVeg==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + postcss-double-position-gradients@7.0.2: + resolution: {integrity: sha512-X62YBNOxoCVvoUpV3uxdg5h86nbzJwF0ogLrgHUL73YsX8FJ5pGeOxRuaUcqhHxQ4wcoKY+O55lJAyCGsxZLJA==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + postcss-flexbugs-fixes@5.0.2: + resolution: {integrity: sha512-18f9voByak7bTktR2QgDveglpn9DTbBWPUzSOe9g0N4WR/2eSt6Vrcbf0hmspvMI6YWGywz6B9f7jzpFNJJgnQ==} + peerDependencies: + postcss: ^8.1.4 + + postcss-focus-visible@11.0.0: + resolution: {integrity: sha512-VG1a9kBKizUBWS66t5xyB4uLONBnvZLCmZXxT40FALu8EF0QgVZBYy5ApC0KhmpHsv+pvHMJHB3agKHwmocWjw==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + postcss-focus-within@10.0.0: + resolution: {integrity: sha512-dvql0fzUTG+gcJYp+KTbag5vAjuo94LDYZHkqDV1rnf5gPGer1v/SrmIZBdvKU8moep3HbcbujqGjzSb3DL53Q==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + postcss-font-variant@5.0.0: + resolution: {integrity: sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==} + peerDependencies: + postcss: ^8.1.0 + + postcss-gap-properties@7.0.0: + resolution: {integrity: sha512-PSDF2QoZMRUbsINvXObQgxx4HExRP85QTT8qS/YN9fBsCPWCqUuwqAD6E6PNp0BqL/jU1eyWUBORaOK/J/9LDA==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + postcss-image-set-function@8.0.0: + resolution: {integrity: sha512-rEGNkOkNusf4+IuMmfEoIdLuVmvbExGbmG+MIsyV6jR5UaWSoyPcAYHV/PxzVDCmudyF+2Nh/o6Ub2saqUdnuA==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + postcss-lab-function@8.0.6: + resolution: {integrity: sha512-zFU65d+uKFTdkvJWPpEMgRwY0gh7RN5FD4aWfLxbeXeAkITSpODG6nH2GKAdbnA4jWjyPac1lw8osw+1ATMcWg==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + postcss-logical@9.0.0: + resolution: {integrity: sha512-A4LNd9dk3q/juEUA9Gd8ALhBO3TeOeYurnyHLlf2aAToD94VHR8c5Uv7KNmf8YVRhTxvWsyug4c5fKtARzyIRQ==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + postcss-nested@7.0.2: + resolution: {integrity: sha512-5osppouFc0VR9/VYzYxO03VaDa3e8F23Kfd6/9qcZTUI8P58GIYlArOET2Wq0ywSl2o2PjELhYOFI4W7l5QHKw==} + engines: {node: '>=18.0'} + peerDependencies: + postcss: ^8.2.14 + + postcss-nesting@14.0.0: + resolution: {integrity: sha512-YGFOfVrjxYfeGTS5XctP1WCI5hu8Lr9SmntjfRC+iX5hCihEO+QZl9Ra+pkjqkgoVdDKvb2JccpElcowhZtzpw==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + postcss-normalize@13.0.1: + resolution: {integrity: sha512-oGfXG7IQ44FUIMFco2N2Uz71UotM+tZ9trEmT1bHIUR5gAplyG3RnHqpMDEcCx1r+1bwBJTrI5uhiQr4YOpqhQ==} + engines: {node: '>= 18'} + peerDependencies: + browserslist: '>= 4' + postcss: '>= 8' + + postcss-opacity-percentage@3.0.0: + resolution: {integrity: sha512-K6HGVzyxUxd/VgZdX04DCtdwWJ4NGLG212US4/LA1TLAbHgmAsTWVR86o+gGIbFtnTkfOpb9sCRBx8K7HO66qQ==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + postcss-overflow-shorthand@7.0.0: + resolution: {integrity: sha512-9SLpjoUdGRoRrzoOdX66HbUs0+uDwfIAiXsRa7piKGOqPd6F4ZlON9oaDSP5r1Qpgmzw5L9Ht0undIK6igJPMA==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + postcss-page-break@3.0.4: + resolution: {integrity: sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==} + peerDependencies: + postcss: ^8 + + postcss-place@11.0.0: + resolution: {integrity: sha512-fAifpyjQ+fuDRp2nmF95WbotqbpjdazebedahXdfBxy5sHembOLpBQ1cHveZD9ZmjK26tYM8tikeNaUlp/KfHA==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + postcss-preset-env@11.3.2: + resolution: {integrity: sha512-O7aA42UBH/89RkGy1YXgJqb+LxZ2+zvR8bHn9qyYcYc+lOo4g9TuIpb75nZT1tN77Bd6pX3DMZ+eYohS4MfKQg==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + postcss-pseudo-class-any-link@11.0.0: + resolution: {integrity: sha512-DNFZ4GMa3C3pU5dM+UCTG1CEeLtS1ZqV5DKSqCTJQMn1G5jnd/30fS8+A7H4o5bSD3MOcnx+VgI+xPE9Z5Wvig==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + postcss-replace-overflow-wrap@4.0.0: + resolution: {integrity: sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==} + peerDependencies: + postcss: ^8.0.3 + + postcss-safe-parser@7.0.1: + resolution: {integrity: sha512-0AioNCJZ2DPYz5ABT6bddIqlhgwhpHZ/l65YAYo0BCIn0xiDpsnTHz0gnoTGk0OXZW0JRs+cDwL8u/teRdz+8A==} + engines: {node: '>=18.0'} + peerDependencies: + postcss: ^8.4.31 + + postcss-selector-not@9.0.0: + resolution: {integrity: sha512-xhAtTdHnVU2M/CrpYOPyRUvg3njhVlKmn2GNYXDaRJV9Ygx4d5OkSkc7NINzjUqnbDFtaKXlISOBeyMXU/zyFQ==} + engines: {node: '>=20.19.0'} + peerDependencies: + postcss: ^8.4 + + postcss-selector-parser@7.1.4: + resolution: {integrity: sha512-HeP7D2wyhkR+XaK6v4W8oRF62Dsz4flyuczALJp61GckGm42u1saSSJ/0auvcBqxs3jMRFEcPK34At/0JBKdOg==} + engines: {node: '>=4'} + + postcss-sorting@9.1.0: + resolution: {integrity: sha512-Mn8KJ45HNNG6JBpBizXcyf6LqY/qyqetGcou/nprDnFwBFBLGj0j/sNKV2lj2KMOVOwdXu14aEzqJv8CIV6e8g==} + peerDependencies: + postcss: ^8.4.20 + + postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + + postcss@8.5.19: + resolution: {integrity: sha512-Mz8SaolMd8nB+G13WkORcxQKHZ/NE4xXevtkJHVuG+guo9/wYKlIMTKAqGdEmYOXR2ijPjTYNHssizdaVSUNdQ==} + engines: {node: ^10 || ^12 || >=14} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + prettier@3.9.5: + resolution: {integrity: sha512-/FVl766LpUfB5vXgCYOYa0MeV/441Ia99AeICQIQFTY/Nw0roZwULcXpku5i1/m5kt/baz+s4Zogspd839HSMg==} + engines: {node: '>=14'} + hasBin: true + + prismjs@1.30.0: + resolution: {integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==} + engines: {node: '>=6'} + + prompts@2.4.2: + resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} + engines: {node: '>= 6'} + + property-information@7.2.0: + resolution: {integrity: sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + qified@0.10.1: + resolution: {integrity: sha512-+Owyggi9IxT1ePKGafcI87ubSmxol6smwJ+RAHDQlx9+9cPwFWDiKFFCPuWhr9ignlGpZ9vDQLw67N4dcTVFEA==} + engines: {node: '>=20'} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + radix3@1.1.2: + resolution: {integrity: sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==} + + react-dom@18.3.1: + resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==} + peerDependencies: + react: ^18.3.1 + + react-leaflet@4.2.1: + resolution: {integrity: sha512-p9chkvhcKrWn/H/1FFeVSqLdReGwn2qmiobOQGO3BifX+/vV/39qhY8dGqbdcPh1e6jxh/QHriLXr7a4eLFK4Q==} + peerDependencies: + leaflet: ^1.9.0 + react: ^18.0.0 + react-dom: ^18.0.0 + + react-refresh@0.17.0: + resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==} + engines: {node: '>=0.10.0'} + + react@18.3.1: + resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} + engines: {node: '>=0.10.0'} + + readdirp@4.1.2: + resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} + engines: {node: '>= 14.18.0'} + + readdirp@5.0.0: + resolution: {integrity: sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==} + engines: {node: '>= 20.19.0'} + + regex-recursion@6.0.2: + resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==} + + regex-utilities@2.3.0: + resolution: {integrity: sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==} + + regex@6.1.0: + resolution: {integrity: sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==} + + rehype-parse@9.0.1: + resolution: {integrity: sha512-ksCzCD0Fgfh7trPDxr2rSylbwq9iYDkSn8TCDmEJ49ljEUBxDVCzCHv7QNzZOfODanX4+bWQ4WZqLCRWYLfhag==} + + rehype-raw@7.0.0: + resolution: {integrity: sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==} + + rehype-stringify@10.0.1: + resolution: {integrity: sha512-k9ecfXHmIPuFVI61B9DeLPN0qFHfawM6RsuX48hoqlaKSF61RskNjSm1lI8PhBEM0MRdLxVVm4WmTqJQccH9mA==} + + rehype@13.0.2: + resolution: {integrity: sha512-j31mdaRFrwFRUIlxGeuPXXKWQxet52RBQRvCmzl5eCefn/KGbomK5GMHNMsOJf55fgo3qw5tST5neDuarDYR2A==} + + remark-gfm@4.0.1: + resolution: {integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==} + + remark-parse@11.0.0: + resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==} + + remark-rehype@11.1.2: + resolution: {integrity: sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==} + + remark-smartypants@3.0.2: + resolution: {integrity: sha512-ILTWeOriIluwEvPjv67v7Blgrcx+LZOkAUVtKI3putuhlZm84FnqDORNXPPm+HY3NdZOMhyDwZ1E+eZB/Df5dA==} + engines: {node: '>=16.0.0'} + + remark-stringify@11.0.0: + resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==} + + remedial@1.0.8: + resolution: {integrity: sha512-/62tYiOe6DzS5BqVsNpH/nkGlX45C/Sp6V+NtiN6JQNS1Viay7cWkazmRkrQrdFj2eshDe96SIQNIoMxqhzBOg==} + + remove-trailing-separator@1.1.0: + resolution: {integrity: sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==} + + remove-trailing-spaces@1.0.9: + resolution: {integrity: sha512-xzG7w5IRijvIkHIjDk65URsJJ7k4J95wmcArY5PRcmjldIOl7oTvG8+X2Ag690R7SfwiOcHrWZKVc1Pp5WIOzA==} + + request-light@0.5.8: + resolution: {integrity: sha512-3Zjgh+8b5fhRJBQZoy+zbVKpAQGLyka0MPgW3zruTF4dFFJ8Fqcfu9YsAvi/rvdcaTeWG3MkbZv4WKxAn/84Lg==} + + request-light@0.7.0: + resolution: {integrity: sha512-lMbBMrDoxgsyO+yB3sDcrDuX85yYt7sS8BfQd11jtbW/z5ZWgLZRcEGLsLoYw7I0WSUGQBs8CC8ScIxkTX1+6Q==} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve-from@5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} + + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + + restore-cursor@5.1.0: + resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} + engines: {node: '>=18'} + + retext-latin@4.0.0: + resolution: {integrity: sha512-hv9woG7Fy0M9IlRQloq/N6atV82NxLGveq+3H2WOi79dtIYWN8OaxogDm77f8YnVXJL2VD3bbqowu5E3EMhBYA==} + + retext-smartypants@6.2.0: + resolution: {integrity: sha512-kk0jOU7+zGv//kfjXEBjdIryL1Acl4i9XNkHxtM7Tm5lFiCog576fjNC9hjoR7LTKQ0DsPWy09JummSsH1uqfQ==} + + retext-stringify@4.0.0: + resolution: {integrity: sha512-rtfN/0o8kL1e+78+uxPTqu1Klt0yPzKuQ2BfWwwfgIUSayyzxpM1PJzkKt4V8803uB9qSy32MvI7Xep9khTpiA==} + + retext@9.0.0: + resolution: {integrity: sha512-sbMDcpHCNjvlheSgMfEcVrZko3cDzdbe1x/e7G66dFp0Ff7Mldvi2uv6JkJQzdRcvLYE8CA8Oe8siQx8ZOgTcA==} + + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + rfdc@1.4.1: + resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} + + rollup@4.62.2: + resolution: {integrity: sha512-RFnrW4lhXA3s3eqHDZvN654g8OTjzRfqpIRJYczCGB6HzphckVAi/Qh4tbPUbRuDi7s1Llv8g/NspLkttY3gTA==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + sanitize.css@13.0.0: + resolution: {integrity: sha512-ZRwKbh/eQ6w9vmTjkuG0Ioi3HBwPFce0O+v//ve+aOq1oeCy7jMV2qzzAlpsNuqpqCBjjriM1lbtZbF/Q8jVyA==} + + sax@1.6.0: + resolution: {integrity: sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==} + engines: {node: '>=11.0.0'} + + scheduler@0.23.2: + resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.8.5: + resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} + engines: {node: '>=10'} + hasBin: true + + sharp@0.33.5: + resolution: {integrity: sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + + sharp@0.34.5: + resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + shell-quote@1.10.0: + resolution: {integrity: sha512-w1aiOKwKuRgtwAReIIj89puqg+I7GvX4IbLrvmhXbzQsj1+Zwi4VO3+fa6ZF91TWSjIxoEkKnMeHcLEODK5ZXA==} + engines: {node: '>= 0.4'} + + shiki@3.23.0: + resolution: {integrity: sha512-55Dj73uq9ZXL5zyeRPzHQsK7Nbyt6Y10k5s7OjuFZGMhpp4r/rsLBH0o/0fstIzX1Lep9VxefWljK/SKCzygIA==} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + simple-swizzle@0.2.4: + resolution: {integrity: sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==} + + sisteransi@1.0.5: + resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + + sitemap@9.0.1: + resolution: {integrity: sha512-S6hzjGJSG3d6if0YoF5kTyeRJvia6FSTBroE5fQ0bu1QNxyJqhhinfUsXi9fH3MgtXODWvwo2BDyQSnhPQ88uQ==} + engines: {node: '>=20.19.5', npm: '>=10.8.2'} + hasBin: true + + slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + + slash@5.1.0: + resolution: {integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==} + engines: {node: '>=14.16'} + + slice-ansi@4.0.0: + resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==} + engines: {node: '>=10'} + + slice-ansi@7.1.2: + resolution: {integrity: sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==} + engines: {node: '>=18'} + + slice-ansi@8.0.0: + resolution: {integrity: sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg==} + engines: {node: '>=20'} + + smol-toml@1.7.0: + resolution: {integrity: sha512-aqVvWoyO21L23mb+drl4RmMXbf6N7FdHjAhTRA9ZBL7apWBgfWC16KjrASI+1p9GAroljyMHj6fK67i0UiTNvQ==} + engines: {node: '>= 18'} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + space-separated-tokens@2.0.2: + resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} + + sponge-case@2.0.3: + resolution: {integrity: sha512-i4h9ZGRfxV6Xw3mpZSFOfbXjf0cQcYmssGWutgNIfFZ2VM+YIWfD71N/kjjwK6X/AAHzBr+rciEcn/L34S8TGw==} + + stream-replace-string@2.0.0: + resolution: {integrity: sha512-TlnjJ1C0QrmxRNrON00JvaFFlNh5TTG00APw23j74ET7gkQpTASi6/L2fuiav8pzK715HXtUeClpBTw2NPSn6w==} + + string-env-interpolation@1.0.1: + resolution: {integrity: sha512-78lwMoCcn0nNu8LszbP1UA7g55OeE4v7rCeWnM5B453rnNr4aq+5it3FEYtZrSEiMvHZOZ9Jlqb0OD0M2VInqg==} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@7.2.0: + resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} + engines: {node: '>=18'} + + string-width@8.2.2: + resolution: {integrity: sha512-GaPUh5gfdrYzqeVNZvUfT23vYYxXzKYidUcnMtJg/3rxRV63EFZy3k6xfKlmfeJD0176lnUV/Usr3XcwSvFzpg==} + engines: {node: '>=20'} + + stringify-entities@4.0.4: + resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.2.0: + resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} + engines: {node: '>=12'} + + strip-json-comments@5.0.3: + resolution: {integrity: sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==} + engines: {node: '>=14.16'} + + style-search@0.1.0: + resolution: {integrity: sha512-Dj1Okke1C3uKKwQcetra4jSuk0DqbzbYtXipzFlFMZtowbF1x7BKJwB9AayVMyFARvU8EDrZdcax4At/452cAg==} + + stylelint-config-concentric-order@5.5.0: + resolution: {integrity: sha512-Vo30Q8ROo4VovI4mDrkX5xmLhu0D+73lJxNmhFt/uqNzPX2rLTqkI+l0ycvP0ffRrlIoJKooLLWx/NVpmPvXhw==} + + stylelint-config-recommended@18.0.0: + resolution: {integrity: sha512-mxgT2XY6YZ3HWWe3Di8umG6aBmWmHTblTgu/f10rqFXnyWxjKWwNdjSWkgkwCtxIKnqjSJzvFmPT5yabVIRxZg==} + engines: {node: '>=20.19.0'} + peerDependencies: + stylelint: ^17.0.0 + + stylelint-order@7.0.1: + resolution: {integrity: sha512-GWPei1zBVDDjxM+/BmcSCiOcHNd8rSqW6FUZtqQGlTRpD0Z5nSzspzWD8rtKif5KPdzUG68DApKEV/y/I9VbTw==} + engines: {node: '>=20.19.0'} + peerDependencies: + stylelint: ^16.18.0 || ^17.0.0 + + stylelint@17.14.0: + resolution: {integrity: sha512-8xkHPpdqYryeIsOgfsYTmr6cIeC4nLYWk5S8BPxpodq8mIuepggkMljsHewWfuAjj/+qpRKou2QerhjMH3iasg==} + engines: {node: '>=20.19.0'} + hasBin: true + + supports-color@10.2.2: + resolution: {integrity: sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==} + engines: {node: '>=18'} + + supports-hyperlinks@4.5.0: + resolution: {integrity: sha512-ZW2OvfeCXrNTbLakPUzjQG922EeGCOteFSVoek5DKStTh898wf7zgtuFlzQN8HfZCxC3Eh02yJVrRW51hADf+w==} + engines: {node: '>=20'} + + svg-tags@1.0.0: + resolution: {integrity: sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==} + + svgo@4.0.2: + resolution: {integrity: sha512-ekx94z1rRc5LDi6oSUaeRnYhd0UOJxdtQCL2rF8xpWxD3TPAsISWOrxezqGovqS38GRZOdpDfvQe3ts6F7nsng==} + engines: {node: '>=16'} + hasBin: true + + swap-case@3.0.3: + resolution: {integrity: sha512-6p4op8wE9CQv7uDFzulI6YXUw4lD9n4oQierdbFThEKVWVQcbQcUjdP27W8XE7V4QnWmnq9jueSHceyyQnqQVA==} + + sync-fetch@0.6.0: + resolution: {integrity: sha512-IELLEvzHuCfc1uTsshPK58ViSdNqXxlml1U+fmwJIKLYKOr/rAtBrorE2RYm5IHaMpDNlmC0fr1LAvdXvyheEQ==} + engines: {node: '>=18'} + + synckit@0.11.13: + resolution: {integrity: sha512-eNRKgb3z66Yp3D2CixVujOUvXLFUTij/zVnV8KRyvFdQwpz7I5DS8UfRkTeLzb64u+dkzDSdelE24izu+zSSUg==} + engines: {node: ^14.18.0 || >=16.0.0} + + table@6.9.0: + resolution: {integrity: sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==} + engines: {node: '>=10.0.0'} + + timeout-signal@2.0.0: + resolution: {integrity: sha512-YBGpG4bWsHoPvofT6y/5iqulfXIiIErl5B0LdtHT1mGXDFTAhhRrbUpTvBgYbovr+3cKblya2WAOcpoy90XguA==} + engines: {node: '>=16'} + + tiny-inflate@1.0.3: + resolution: {integrity: sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==} + + tinyexec@1.2.4: + resolution: {integrity: sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==} + engines: {node: '>=18'} + + tinyglobby@0.2.17: + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} + engines: {node: '>=12.0.0'} + + title-case@3.0.3: + resolution: {integrity: sha512-e1zGYRvbffpcHIrnuqT0Dh+gEJtDaxDSoG4JAIpq4oDFyooziLBIiYQv0GBT4FUAnUop5uZ1hiIAj7oAF6sOCA==} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + + trim-lines@3.0.1: + resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} + + trough@2.2.0: + resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} + + ts-api-utils@2.5.0: + resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + + ts-log@3.0.2: + resolution: {integrity: sha512-esq6hx2lM66sQV1YcFkIYTqrWWabmqBqobKHyn1CswdI5FgfQhkmiKiRWVGBNlIbdjBxEIkNvMIwLKKPgRYZLQ==} + engines: {node: '>=20', npm: '>=10'} + + tsconfck@3.1.6: + resolution: {integrity: sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==} + engines: {node: ^18 || >=20} + deprecated: unmaintained + hasBin: true + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + type-fest@4.41.0: + resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} + engines: {node: '>=16'} + + typesafe-path@0.2.2: + resolution: {integrity: sha512-OJabfkAg1WLZSqJAJ0Z6Sdt3utnbzr/jh+NAHoyWHJe8CMSy79Gm085094M9nvTPy22KzTVn5Zq5mbapCI/hPA==} + + typescript-auto-import-cache@0.3.6: + resolution: {integrity: sha512-RpuHXrknHdVdK7wv/8ug3Fr0WNsNi5l5aB8MYYuXhq2UH5lnEB1htJ1smhtD5VeCsGr2p8mUDtd83LCQDFVgjQ==} + + typescript-eslint@8.63.0: + resolution: {integrity: sha512-xgwXyzG4sK9ALkBxbyGkTMMOS+imnW65iPhxCQMK83KhxyoDNW7l+IDqEf9vMdoUidHpOoS967RCq4eMiTexwQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + ufo@1.6.4: + resolution: {integrity: sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==} + + ultrahtml@1.7.0: + resolution: {integrity: sha512-2xRd0VHoAQE4M+vF/DvFFB7pUV0ZxTW1TLi7lHQWnF/Sb5TPeEUV/l+hxcNnGO00ZXGnR0voCMmYRKQf+rvJ2g==} + + unbash@4.0.2: + resolution: {integrity: sha512-8gwNZ29+0/3zmXw7ToIHZtg6wK37xnniRUdBt7B27xZxaxfgR5tGMaGHT0t0dLtBV9fXE7zurh0s6Z1DHVjfWg==} + engines: {node: '>=14'} + + unc-path-regex@0.1.2: + resolution: {integrity: sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg==} + engines: {node: '>=0.10.0'} + + uncrypto@0.1.3: + resolution: {integrity: sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==} + + undici-types@7.18.2: + resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==} + + undici-types@8.3.0: + resolution: {integrity: sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==} + + unicorn-magic@0.4.0: + resolution: {integrity: sha512-wH590V9VNgYH9g3lH9wWjTrUoKsjLF6sGLjhR4sH1LWpLmCOH0Zf7PukhDA8BiS7KHe4oPNkcTHqYkj7SOGUOw==} + engines: {node: '>=20'} + + unified@11.0.5: + resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} + + unifont@0.7.4: + resolution: {integrity: sha512-oHeis4/xl42HUIeHuNZRGEvxj5AaIKR+bHPNegRq5LV1gdc3jundpONbjglKpihmJf+dswygdMJn3eftGIMemg==} + + unist-util-find-after@5.0.0: + resolution: {integrity: sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==} + + unist-util-is@6.0.1: + resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==} + + unist-util-modify-children@4.0.0: + resolution: {integrity: sha512-+tdN5fGNddvsQdIzUF3Xx82CU9sMM+fA0dLgR9vOmT0oPT2jH+P1nd5lSqfCfXAw+93NhcXNY2qqvTUtE4cQkw==} + + unist-util-position@5.0.0: + resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} + + unist-util-remove-position@5.0.0: + resolution: {integrity: sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==} + + unist-util-stringify-position@4.0.0: + resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} + + unist-util-visit-children@3.0.0: + resolution: {integrity: sha512-RgmdTfSBOg04sdPcpTSD1jzoNBjt9a80/ZCzp5cI9n1qPzLZWF9YdvWGN2zmTumP1HWhXKdUWexjy/Wy/lJ7tA==} + + unist-util-visit-parents@6.0.2: + resolution: {integrity: sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==} + + unist-util-visit@5.1.0: + resolution: {integrity: sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==} + + unixify@1.0.0: + resolution: {integrity: sha512-6bc58dPYhCMHHuwxldQxO3RRNZ4eCogZ/st++0+fcC1nr0jiGUtAdBJ2qzmLQWSxbtz42pWt4QQMiZ9HvZf5cg==} + engines: {node: '>=0.10.0'} + + unstorage@1.17.5: + resolution: {integrity: sha512-0i3iqvRfx29hkNntHyQvJTpf5W9dQ9ZadSoRU8+xVlhVtT7jAX57fazYO9EHvcRCfBCyi5YRya7XCDOsbTgkPg==} + peerDependencies: + '@azure/app-configuration': ^1.8.0 + '@azure/cosmos': ^4.2.0 + '@azure/data-tables': ^13.3.0 + '@azure/identity': ^4.6.0 + '@azure/keyvault-secrets': ^4.9.0 + '@azure/storage-blob': ^12.26.0 + '@capacitor/preferences': ^6 || ^7 || ^8 + '@deno/kv': '>=0.9.0' + '@netlify/blobs': ^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0 + '@planetscale/database': ^1.19.0 + '@upstash/redis': ^1.34.3 + '@vercel/blob': '>=0.27.1' + '@vercel/functions': ^2.2.12 || ^3.0.0 + '@vercel/kv': ^1 || ^2 || ^3 + aws4fetch: ^1.0.20 + db0: '>=0.2.1' + idb-keyval: ^6.2.1 + ioredis: ^5.4.2 + uploadthing: ^7.4.4 + peerDependenciesMeta: + '@azure/app-configuration': + optional: true + '@azure/cosmos': + optional: true + '@azure/data-tables': + optional: true + '@azure/identity': + optional: true + '@azure/keyvault-secrets': + optional: true + '@azure/storage-blob': + optional: true + '@capacitor/preferences': + optional: true + '@deno/kv': + optional: true + '@netlify/blobs': + optional: true + '@planetscale/database': + optional: true + '@upstash/redis': + optional: true + '@vercel/blob': + optional: true + '@vercel/functions': + optional: true + '@vercel/kv': + optional: true + aws4fetch: + optional: true + db0: + optional: true + idb-keyval: + optional: true + ioredis: + optional: true + uploadthing: + optional: true + + update-browserslist-db@1.2.3: + resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + urlpattern-polyfill@10.1.0: + resolution: {integrity: sha512-IGjKp/o0NL3Bso1PymYURCJxMPNAf/ILOpendP9f5B6e1rTJgdgiOvgfoT8VxCAdY+Wisb9uhGaJJf3yZ2V9nw==} + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + vfile-location@5.0.3: + resolution: {integrity: sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==} + + vfile-message@4.0.3: + resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==} + + vfile@6.0.3: + resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} + + vite@6.4.3: + resolution: {integrity: sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + jiti: '>=1.21.0' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vitefu@1.1.3: + resolution: {integrity: sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg==} + peerDependencies: + vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + vite: + optional: true + + volar-service-css@0.0.71: + resolution: {integrity: sha512-wRRFt9BpjMKCazcgOh67MSjUjiWUCAh99DyYSDIOTuxaRjEtDC7PpB0k1Y1wbJIW/pVtMUSVbpPo3UGSm0Byxw==} + peerDependencies: + '@volar/language-service': ~2.4.0 + peerDependenciesMeta: + '@volar/language-service': + optional: true + + volar-service-emmet@0.0.71: + resolution: {integrity: sha512-zqjzt6bN95e3CUstBm0PBFAJnrfz0ZAARka87fart46/gNCLLuP3Vujy8V/J8HEziTFLnfkgIASLFYPUhonJcA==} + peerDependencies: + '@volar/language-service': ~2.4.0 + peerDependenciesMeta: + '@volar/language-service': + optional: true + + volar-service-html@0.0.71: + resolution: {integrity: sha512-e8tHPhgQ7ooLfudAEIku+kgd9pWkq3SSz8RbnQDI1+Eb8wbenkLGHqoirLqz5ORLV6wIMr2Iv08RWBG5eOcgpw==} + peerDependencies: + '@volar/language-service': ~2.4.0 + peerDependenciesMeta: + '@volar/language-service': + optional: true + + volar-service-prettier@0.0.71: + resolution: {integrity: sha512-Rz7JVH3qD108UCdmIEiZvOBNljMt2nLFdbN8AXcDfn7xD9F5I2aCIsDVqBbXw21PsnxG0b7MfwtNF+zPS/NKUg==} + peerDependencies: + '@volar/language-service': ~2.4.0 + prettier: ^2.2 || ^3.0 + peerDependenciesMeta: + '@volar/language-service': + optional: true + prettier: + optional: true + + volar-service-typescript-twoslash-queries@0.0.71: + resolution: {integrity: sha512-9K2k72s4n7rV9s4bX0MyjbX9iBribvKZbBJKuEmTCZfeWJXs6Yh7bGpY4eoc7UufAjvpheBqwyZCOIPBvxCv0A==} + peerDependencies: + '@volar/language-service': ~2.4.0 + peerDependenciesMeta: + '@volar/language-service': + optional: true + + volar-service-typescript@0.0.71: + resolution: {integrity: sha512-yTtM/BVT6hoyEYnDtaCyAtNhdNeS/mhTTABlBOdw3NNiRBUin3IznFJpgfjer4c6RYopiPjjQjc9VFhxVl1mLw==} + peerDependencies: + '@volar/language-service': ~2.4.0 + peerDependenciesMeta: + '@volar/language-service': + optional: true + + volar-service-yaml@0.0.71: + resolution: {integrity: sha512-qYGWGuVpUTnZGu5P/CR4KLK4aIR8RrcVnmfZ2eRcj9q/I8VZCoC5yy9FtEvfNvnDp4MU17yhdJcvpQPIqhJS2Q==} + peerDependencies: + '@volar/language-service': ~2.4.0 + peerDependenciesMeta: + '@volar/language-service': + optional: true + + vscode-css-languageservice@6.3.10: + resolution: {integrity: sha512-eq5N9Er3fC4vA9zd9EFhyBG90wtCCuXgRSpAndaOgXMh1Wgep5lBgRIeDgjZBW9pa+332yC9+49cZMW8jcL3MA==} + + vscode-html-languageservice@5.6.2: + resolution: {integrity: sha512-ulCrSnFnfQ16YzvwnYUgEbUEl/ZG7u2eV27YhvLObSHKkb8fw1Z9cgsnUwjTEeDIdJDoTDTDpxuhQwoenoLNMg==} + + vscode-json-languageservice@4.1.8: + resolution: {integrity: sha512-0vSpg6Xd9hfV+eZAaYN63xVVMOTmJ4GgHxXnkLCh+9RsQBkWKIghzLhW2B9ebfG+LQQg8uLtsQ2aUKjTgE+QOg==} + engines: {npm: '>=7.0.0'} + + vscode-jsonrpc@8.2.0: + resolution: {integrity: sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==} + engines: {node: '>=14.0.0'} + + vscode-jsonrpc@9.0.1: + resolution: {integrity: sha512-rfuA6T75H6m5EkbhtEPzre9pT0HPcDI2MMy4+nPFIBks5J8JBAUHD4tRYSgaBOijIEC7SRkC1kKyXTLqbmh9jw==} + engines: {node: '>=14.0.0'} + + vscode-languageserver-protocol@3.17.5: + resolution: {integrity: sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==} + + vscode-languageserver-protocol@3.18.2: + resolution: {integrity: sha512-XRyDbT0Pp3sSNti3JmxVEUMySWCSi1hhM+/KUlCy1hV1zmrqpM1OwO12EAki8blhmLuIMpaJrYbo0OzGVfK2Qg==} + + vscode-languageserver-textdocument@1.0.12: + resolution: {integrity: sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==} + + vscode-languageserver-types@3.17.5: + resolution: {integrity: sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==} + + vscode-languageserver-types@3.18.0: + resolution: {integrity: sha512-8TsGPNMIMiiBdkORgRSvLjuiEIiAFtO+KssmYWxQ+uSVvlf7RjK8YKCOjPzZ+YA04jXEV7+7LvkSmHkhpNS99g==} + + vscode-languageserver@9.0.1: + resolution: {integrity: sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==} + hasBin: true + + vscode-nls@5.2.0: + resolution: {integrity: sha512-RAaHx7B14ZU04EU31pT+rKz2/zSl7xMsfIZuo8pd+KZO6PXtQmpevpq3vxvWNcrGbdmhM/rr5Uw5Mz+NBfhVng==} + + vscode-uri@3.1.0: + resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==} + + walk-up-path@4.0.0: + resolution: {integrity: sha512-3hu+tD8YzSLGuFYtPRb48vdhKMi0KQV5sn+uWr8+7dMEq/2G/dtLrdDinkLjqq5TIbIBjYJ4Ax/n3YiaW7QM8A==} + engines: {node: 20 || >=22} + + web-namespaces@2.0.1: + resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==} + + web-streams-polyfill@3.3.3: + resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} + engines: {node: '>= 8'} + + webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + + whatwg-mimetype@4.0.0: + resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} + engines: {node: '>=18'} + + whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + + which-pm-runs@1.1.0: + resolution: {integrity: sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA==} + engines: {node: '>=4'} + + which@1.3.1: + resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} + hasBin: true + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + widest-line@5.0.0: + resolution: {integrity: sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA==} + engines: {node: '>=18'} + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + wrap-ansi@10.0.0: + resolution: {integrity: sha512-SGcvg80f0wUy2/fXES19feHMz8E0JoXv2uNgHOu4Dgi2OrCy1lqwFYEJz1BLbDI0exjPMe/ZdzZ/YpGECBG/aQ==} + engines: {node: '>=20'} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrap-ansi@9.0.2: + resolution: {integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==} + engines: {node: '>=18'} + + write-file-atomic@7.0.1: + resolution: {integrity: sha512-OTIk8iR8/aCRWBqvxrzxR0hgxWpnYBblY1S5hDWBQfk/VFmJwzmJgQFN3WsoUKHISv2eAwe+PpbUzyL1CKTLXg==} + engines: {node: ^20.17.0 || >=22.9.0} + + ws@8.21.0: + resolution: {integrity: sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + xxhash-wasm@1.1.0: + resolution: {integrity: sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA==} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yaml-language-server@1.23.0: + resolution: {integrity: sha512-3qVyCOexLCWw06PQa5kRPwvMWMZ/eZeCRWUvgD6a0OkqL/4iCnxy2WumbWifa937Uo5xhyWJ0uxlU39ljhNh7A==} + hasBin: true + + yaml@2.8.3: + resolution: {integrity: sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==} + engines: {node: '>= 14.6'} + hasBin: true + + yaml@2.9.0: + resolution: {integrity: sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==} + engines: {node: '>= 14.6'} + hasBin: true + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs-parser@22.0.0: + resolution: {integrity: sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==} + engines: {node: ^20.19.0 || ^22.12.0 || >=23} + + yargs@17.7.3: + resolution: {integrity: sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==} + engines: {node: '>=12'} + + yargs@18.0.0: + resolution: {integrity: sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==} + engines: {node: ^20.19.0 || ^22.12.0 || >=23} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + yocto-queue@1.2.2: + resolution: {integrity: sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==} + engines: {node: '>=12.20'} + + yocto-spinner@0.2.3: + resolution: {integrity: sha512-sqBChb33loEnkoXte1bLg45bEBsOP9N1kzQh5JZNKj/0rik4zAPTNSAVPj3uQAdc6slYJ0Ksc403G2XgxsJQFQ==} + engines: {node: '>=18.19'} + + yoctocolors@2.1.2: + resolution: {integrity: sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==} + engines: {node: '>=18'} + + zod-to-json-schema@3.25.2: + resolution: {integrity: sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==} + peerDependencies: + zod: ^3.25.28 || ^4 + + zod-to-ts@1.2.0: + resolution: {integrity: sha512-x30XE43V+InwGpvTySRNz9kB7qFU8DlyEy7BsSTCHPH1R0QasMmHWZDCzYm6bVXtj/9NNJAZF3jW8rzFvH5OFA==} + peerDependencies: + typescript: ^4.9.4 || ^5.0.2 + zod: ^3 + + zod-validation-error@4.0.2: + resolution: {integrity: sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==} + engines: {node: '>=18.0.0'} + peerDependencies: + zod: ^3.25.0 || ^4.0.0 + + zod@3.25.76: + resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} + + zod@4.4.3: + resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} + + zwitch@2.0.4: + resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} + +snapshots: + + '@ardatan/relay-compiler@13.0.1(graphql@16.14.2)': + dependencies: + '@babel/runtime': 7.29.7 + graphql: 16.14.2 + immutable: 5.1.9 + invariant: 2.2.4 + + '@astrojs/check@0.9.9(prettier@3.9.5)(typescript@5.9.3)': + dependencies: + '@astrojs/language-server': 2.16.11(prettier@3.9.5)(typescript@5.9.3) + chokidar: 4.0.3 + kleur: 4.1.5 + typescript: 5.9.3 + yargs: 17.7.3 + transitivePeerDependencies: + - prettier + - prettier-plugin-astro + + '@astrojs/compiler@2.13.1': {} + + '@astrojs/compiler@3.0.1': {} + + '@astrojs/internal-helpers@0.7.6': {} + + '@astrojs/language-server@2.16.11(prettier@3.9.5)(typescript@5.9.3)': + dependencies: + '@astrojs/compiler': 2.13.1 + '@astrojs/yaml2ts': 0.2.4 + '@jridgewell/sourcemap-codec': 1.5.5 + '@volar/kit': 2.4.28(typescript@5.9.3) + '@volar/language-core': 2.4.28 + '@volar/language-server': 2.4.28 + '@volar/language-service': 2.4.28 + muggle-string: 0.4.1 + tinyglobby: 0.2.17 + volar-service-css: 0.0.71(@volar/language-service@2.4.28) + volar-service-emmet: 0.0.71(@volar/language-service@2.4.28) + volar-service-html: 0.0.71(@volar/language-service@2.4.28) + volar-service-prettier: 0.0.71(@volar/language-service@2.4.28)(prettier@3.9.5) + volar-service-typescript: 0.0.71(@volar/language-service@2.4.28) + volar-service-typescript-twoslash-queries: 0.0.71(@volar/language-service@2.4.28) + volar-service-yaml: 0.0.71(@volar/language-service@2.4.28) + vscode-html-languageservice: 5.6.2 + vscode-uri: 3.1.0 + optionalDependencies: + prettier: 3.9.5 + transitivePeerDependencies: + - typescript + + '@astrojs/markdown-remark@6.3.11': + dependencies: + '@astrojs/internal-helpers': 0.7.6 + '@astrojs/prism': 3.3.0 + github-slugger: 2.0.0 + hast-util-from-html: 2.0.3 + hast-util-to-text: 4.0.2 + import-meta-resolve: 4.2.0 + js-yaml: 4.3.0 + mdast-util-definitions: 6.0.0 + rehype-raw: 7.0.0 + rehype-stringify: 10.0.1 + remark-gfm: 4.0.1 + remark-parse: 11.0.0 + remark-rehype: 11.1.2 + remark-smartypants: 3.0.2 + shiki: 3.23.0 + smol-toml: 1.7.0 + unified: 11.0.5 + unist-util-remove-position: 5.0.0 + unist-util-visit: 5.1.0 + unist-util-visit-parents: 6.0.2 + vfile: 6.0.3 + transitivePeerDependencies: + - supports-color + + '@astrojs/prism@3.3.0': + dependencies: + prismjs: 1.30.0 + + '@astrojs/react@4.4.2(@types/node@26.1.1)(@types/react-dom@18.3.7(@types/react@18.3.31))(@types/react@18.3.31)(jiti@2.7.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(yaml@2.9.0)': + dependencies: + '@types/react': 18.3.31 + '@types/react-dom': 18.3.7(@types/react@18.3.31) + '@vitejs/plugin-react': 4.7.0(vite@6.4.3(@types/node@26.1.1)(jiti@2.7.0)(yaml@2.9.0)) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + ultrahtml: 1.7.0 + vite: 6.4.3(@types/node@26.1.1)(jiti@2.7.0)(yaml@2.9.0) + transitivePeerDependencies: + - '@types/node' + - jiti + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + + '@astrojs/sitemap@3.7.3': + dependencies: + sitemap: 9.0.1 + stream-replace-string: 2.0.0 + zod: 4.4.3 + + '@astrojs/telemetry@3.3.0': + dependencies: + ci-info: 4.4.0 + debug: 4.4.3 + dlv: 1.1.3 + dset: 3.1.4 + is-docker: 3.0.0 + is-wsl: 3.1.1 + which-pm-runs: 1.1.0 + transitivePeerDependencies: + - supports-color + + '@astrojs/yaml2ts@0.2.4': + dependencies: + yaml: 2.9.0 + + '@babel/code-frame@7.29.7': + dependencies: + '@babel/helper-validator-identifier': 7.29.7 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.29.7': {} + + '@babel/core@7.29.7': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.7 + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) + '@babel/helpers': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/template': 7.29.7 + '@babel/traverse': 7.29.7 + '@babel/types': 7.29.7 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.29.7': + dependencies: + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/helper-compilation-targets@7.29.7': + dependencies: + '@babel/compat-data': 7.29.7 + '@babel/helper-validator-option': 7.29.7 + browserslist: 4.28.6 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-globals@7.29.7': {} + + '@babel/helper-module-imports@7.29.7': + dependencies: + '@babel/traverse': 7.29.7 + '@babel/types': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-module-imports': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + '@babel/traverse': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/helper-plugin-utils@7.29.7': {} + + '@babel/helper-string-parser@7.29.7': {} + + '@babel/helper-validator-identifier@7.29.7': {} + + '@babel/helper-validator-option@7.29.7': {} + + '@babel/helpers@7.29.7': + dependencies: + '@babel/template': 7.29.7 + '@babel/types': 7.29.7 + + '@babel/parser@7.29.7': + dependencies: + '@babel/types': 7.29.7 + + '@babel/plugin-syntax-import-assertions@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-react-jsx-self@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-react-jsx-source@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/runtime@7.29.7': {} + + '@babel/template@7.29.7': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 + + '@babel/traverse@7.29.7': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.7 + '@babel/helper-globals': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/template': 7.29.7 + '@babel/types': 7.29.7 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.29.7': + dependencies: + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + + '@cacheable/memory@2.2.0': + dependencies: + '@cacheable/utils': 2.5.0 + '@keyv/bigmap': 1.3.1(keyv@5.6.0) + hookified: 1.15.1 + keyv: 5.6.0 + + '@cacheable/utils@2.5.0': + dependencies: + hashery: 1.5.1 + keyv: 5.6.0 + + '@capsizecss/unpack@4.0.1': + dependencies: + fontkitten: 1.0.3 + + '@csstools/cascade-layer-name-parser@3.0.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': + dependencies: + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + + '@csstools/color-helpers@6.1.0': {} + + '@csstools/css-calc@3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': + dependencies: + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + + '@csstools/css-color-parser@4.1.9(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': + dependencies: + '@csstools/color-helpers': 6.1.0 + '@csstools/css-calc': 3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + + '@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0)': + dependencies: + '@csstools/css-tokenizer': 4.0.0 + + '@csstools/css-syntax-patches-for-csstree@1.1.6(css-tree@3.2.1)': + optionalDependencies: + css-tree: 3.2.1 + + '@csstools/css-tokenizer@4.0.0': {} + + '@csstools/media-query-list-parser@5.0.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': + dependencies: + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + + '@csstools/normalize.css@12.1.1': {} + + '@csstools/postcss-alpha-function@2.0.7(postcss@8.5.19)': + dependencies: + '@csstools/css-color-parser': 4.1.9(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + '@csstools/postcss-progressive-custom-properties': 5.1.1(postcss@8.5.19) + '@csstools/utilities': 3.0.0(postcss@8.5.19) + postcss: 8.5.19 + + '@csstools/postcss-cascade-layers@6.0.0(postcss@8.5.19)': + dependencies: + '@csstools/selector-specificity': 6.0.0(postcss-selector-parser@7.1.4) + postcss: 8.5.19 + postcss-selector-parser: 7.1.4 + + '@csstools/postcss-color-function-display-p3-linear@2.0.6(postcss@8.5.19)': + dependencies: + '@csstools/css-color-parser': 4.1.9(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + '@csstools/postcss-progressive-custom-properties': 5.1.1(postcss@8.5.19) + '@csstools/utilities': 3.0.0(postcss@8.5.19) + postcss: 8.5.19 + + '@csstools/postcss-color-function@5.0.6(postcss@8.5.19)': + dependencies: + '@csstools/css-color-parser': 4.1.9(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + '@csstools/postcss-progressive-custom-properties': 5.1.1(postcss@8.5.19) + '@csstools/utilities': 3.0.0(postcss@8.5.19) + postcss: 8.5.19 + + '@csstools/postcss-color-mix-function@4.0.6(postcss@8.5.19)': + dependencies: + '@csstools/css-color-parser': 4.1.9(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + '@csstools/postcss-progressive-custom-properties': 5.1.1(postcss@8.5.19) + '@csstools/utilities': 3.0.0(postcss@8.5.19) + postcss: 8.5.19 + + '@csstools/postcss-color-mix-variadic-function-arguments@2.0.6(postcss@8.5.19)': + dependencies: + '@csstools/css-color-parser': 4.1.9(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + '@csstools/postcss-progressive-custom-properties': 5.1.1(postcss@8.5.19) + '@csstools/utilities': 3.0.0(postcss@8.5.19) + postcss: 8.5.19 + + '@csstools/postcss-container-rule-prelude-list@1.0.1(postcss@8.5.19)': + dependencies: + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + postcss: 8.5.19 + + '@csstools/postcss-content-alt-text@3.0.2(postcss@8.5.19)': + dependencies: + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + '@csstools/postcss-progressive-custom-properties': 5.1.1(postcss@8.5.19) + '@csstools/utilities': 3.0.0(postcss@8.5.19) + postcss: 8.5.19 + + '@csstools/postcss-contrast-color-function@3.0.6(postcss@8.5.19)': + dependencies: + '@csstools/css-color-parser': 4.1.9(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + '@csstools/postcss-progressive-custom-properties': 5.1.1(postcss@8.5.19) + '@csstools/utilities': 3.0.0(postcss@8.5.19) + postcss: 8.5.19 + + '@csstools/postcss-exponential-functions@3.0.3(postcss@8.5.19)': + dependencies: + '@csstools/css-calc': 3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + postcss: 8.5.19 + + '@csstools/postcss-font-format-keywords@5.0.0(postcss@8.5.19)': + dependencies: + '@csstools/utilities': 3.0.0(postcss@8.5.19) + postcss: 8.5.19 + postcss-value-parser: 4.2.0 + + '@csstools/postcss-font-width-property@1.0.0(postcss@8.5.19)': + dependencies: + '@csstools/utilities': 3.0.0(postcss@8.5.19) + postcss: 8.5.19 + + '@csstools/postcss-gamut-mapping@3.0.6(postcss@8.5.19)': + dependencies: + '@csstools/css-color-parser': 4.1.9(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + postcss: 8.5.19 + + '@csstools/postcss-gradients-interpolation-method@6.0.6(postcss@8.5.19)': + dependencies: + '@csstools/css-color-parser': 4.1.9(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + '@csstools/postcss-progressive-custom-properties': 5.1.1(postcss@8.5.19) + '@csstools/utilities': 3.0.0(postcss@8.5.19) + postcss: 8.5.19 + + '@csstools/postcss-hwb-function@5.0.6(postcss@8.5.19)': + dependencies: + '@csstools/css-color-parser': 4.1.9(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + '@csstools/postcss-progressive-custom-properties': 5.1.1(postcss@8.5.19) + '@csstools/utilities': 3.0.0(postcss@8.5.19) + postcss: 8.5.19 + + '@csstools/postcss-ic-unit@5.0.2(postcss@8.5.19)': + dependencies: + '@csstools/postcss-progressive-custom-properties': 5.1.1(postcss@8.5.19) + '@csstools/utilities': 3.0.0(postcss@8.5.19) + postcss: 8.5.19 + postcss-value-parser: 4.2.0 + + '@csstools/postcss-image-function@1.0.1(postcss@8.5.19)': + dependencies: + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + '@csstools/postcss-progressive-custom-properties': 5.1.1(postcss@8.5.19) + '@csstools/utilities': 3.0.0(postcss@8.5.19) + postcss: 8.5.19 + + '@csstools/postcss-initial@3.0.0(postcss@8.5.19)': + dependencies: + postcss: 8.5.19 + + '@csstools/postcss-is-pseudo-class@6.0.0(postcss@8.5.19)': + dependencies: + '@csstools/selector-specificity': 6.0.0(postcss-selector-parser@7.1.4) + postcss: 8.5.19 + postcss-selector-parser: 7.1.4 + + '@csstools/postcss-light-dark-function@3.0.2(postcss@8.5.19)': + dependencies: + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + '@csstools/postcss-progressive-custom-properties': 5.1.1(postcss@8.5.19) + '@csstools/utilities': 3.0.0(postcss@8.5.19) + postcss: 8.5.19 + + '@csstools/postcss-logical-float-and-clear@4.0.0(postcss@8.5.19)': + dependencies: + postcss: 8.5.19 + + '@csstools/postcss-logical-overflow@3.0.0(postcss@8.5.19)': + dependencies: + postcss: 8.5.19 + + '@csstools/postcss-logical-overscroll-behavior@3.0.0(postcss@8.5.19)': + dependencies: + postcss: 8.5.19 + + '@csstools/postcss-logical-resize@4.0.0(postcss@8.5.19)': + dependencies: + postcss: 8.5.19 + postcss-value-parser: 4.2.0 + + '@csstools/postcss-logical-viewport-units@4.0.0(postcss@8.5.19)': + dependencies: + '@csstools/css-tokenizer': 4.0.0 + '@csstools/utilities': 3.0.0(postcss@8.5.19) + postcss: 8.5.19 + + '@csstools/postcss-media-minmax@3.0.3(postcss@8.5.19)': + dependencies: + '@csstools/css-calc': 3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + '@csstools/media-query-list-parser': 5.0.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + postcss: 8.5.19 + + '@csstools/postcss-media-queries-aspect-ratio-number-values@4.0.0(postcss@8.5.19)': + dependencies: + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + '@csstools/media-query-list-parser': 5.0.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + postcss: 8.5.19 + + '@csstools/postcss-mixins@1.0.0(postcss@8.5.19)': + dependencies: + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + postcss: 8.5.19 + + '@csstools/postcss-nested-calc@5.0.0(postcss@8.5.19)': + dependencies: + '@csstools/utilities': 3.0.0(postcss@8.5.19) + postcss: 8.5.19 + postcss-value-parser: 4.2.0 + + '@csstools/postcss-normalize-display-values@5.0.1(postcss@8.5.19)': + dependencies: + postcss: 8.5.19 + postcss-value-parser: 4.2.0 + + '@csstools/postcss-oklab-function@5.0.6(postcss@8.5.19)': + dependencies: + '@csstools/css-color-parser': 4.1.9(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + '@csstools/postcss-progressive-custom-properties': 5.1.1(postcss@8.5.19) + '@csstools/utilities': 3.0.0(postcss@8.5.19) + postcss: 8.5.19 + + '@csstools/postcss-position-area-property@2.0.0(postcss@8.5.19)': + dependencies: + postcss: 8.5.19 + + '@csstools/postcss-progressive-custom-properties@5.1.1(postcss@8.5.19)': + dependencies: + postcss: 8.5.19 + postcss-value-parser: 4.2.0 + + '@csstools/postcss-property-rule-prelude-list@2.0.0(postcss@8.5.19)': + dependencies: + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + postcss: 8.5.19 + + '@csstools/postcss-random-function@3.0.3(postcss@8.5.19)': + dependencies: + '@csstools/css-calc': 3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + postcss: 8.5.19 + + '@csstools/postcss-relative-color-syntax@4.0.6(postcss@8.5.19)': + dependencies: + '@csstools/css-color-parser': 4.1.9(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + '@csstools/postcss-progressive-custom-properties': 5.1.1(postcss@8.5.19) + '@csstools/utilities': 3.0.0(postcss@8.5.19) + postcss: 8.5.19 + + '@csstools/postcss-scope-pseudo-class@5.0.0(postcss@8.5.19)': + dependencies: + postcss: 8.5.19 + postcss-selector-parser: 7.1.4 + + '@csstools/postcss-sign-functions@2.0.3(postcss@8.5.19)': + dependencies: + '@csstools/css-calc': 3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + postcss: 8.5.19 + + '@csstools/postcss-stepped-value-functions@5.0.3(postcss@8.5.19)': + dependencies: + '@csstools/css-calc': 3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + postcss: 8.5.19 + + '@csstools/postcss-syntax-descriptor-syntax-production@2.0.0(postcss@8.5.19)': + dependencies: + '@csstools/css-tokenizer': 4.0.0 + postcss: 8.5.19 + + '@csstools/postcss-system-ui-font-family@2.0.0(postcss@8.5.19)': + dependencies: + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + postcss: 8.5.19 + + '@csstools/postcss-text-decoration-shorthand@5.0.4(postcss@8.5.19)': + dependencies: + '@csstools/color-helpers': 6.1.0 + postcss: 8.5.19 + postcss-value-parser: 4.2.0 + + '@csstools/postcss-trigonometric-functions@5.0.3(postcss@8.5.19)': + dependencies: + '@csstools/css-calc': 3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + postcss: 8.5.19 + + '@csstools/postcss-unset-value@5.0.0(postcss@8.5.19)': + dependencies: + postcss: 8.5.19 + + '@csstools/selector-resolve-nested@4.0.0(postcss-selector-parser@7.1.4)': + dependencies: + postcss-selector-parser: 7.1.4 + + '@csstools/selector-specificity@6.0.0(postcss-selector-parser@7.1.4)': + dependencies: + postcss-selector-parser: 7.1.4 + + '@csstools/utilities@3.0.0(postcss@8.5.19)': + dependencies: + postcss: 8.5.19 + + '@emmetio/abbreviation@2.3.3': + dependencies: + '@emmetio/scanner': 1.0.4 + + '@emmetio/css-abbreviation@2.1.8': + dependencies: + '@emmetio/scanner': 1.0.4 + + '@emmetio/css-parser@0.4.1': + dependencies: + '@emmetio/stream-reader': 2.2.0 + '@emmetio/stream-reader-utils': 0.1.0 + + '@emmetio/html-matcher@1.3.0': + dependencies: + '@emmetio/scanner': 1.0.4 + + '@emmetio/scanner@1.0.4': {} + + '@emmetio/stream-reader-utils@0.1.0': {} + + '@emmetio/stream-reader@2.2.0': {} + + '@emnapi/core@1.11.0': + dependencies: + '@emnapi/wasi-threads': 1.2.2 + tslib: 2.8.1 + optional: true + + '@emnapi/core@1.11.1': + dependencies: + '@emnapi/wasi-threads': 1.2.2 + tslib: 2.8.1 + optional: true + + '@emnapi/runtime@1.11.0': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/runtime@1.11.1': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/runtime@1.11.2': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/wasi-threads@1.2.2': + dependencies: + tslib: 2.8.1 + optional: true + + '@envelop/core@5.5.1': + dependencies: + '@envelop/instrumentation': 1.0.0 + '@envelop/types': 5.2.1 + '@whatwg-node/promise-helpers': 1.3.2 + tslib: 2.8.1 + + '@envelop/instrumentation@1.0.0': + dependencies: + '@whatwg-node/promise-helpers': 1.3.2 + tslib: 2.8.1 + + '@envelop/types@5.2.1': + dependencies: + '@whatwg-node/promise-helpers': 1.3.2 + tslib: 2.8.1 + + '@esbuild/aix-ppc64@0.25.12': + optional: true + + '@esbuild/aix-ppc64@0.27.7': + optional: true + + '@esbuild/android-arm64@0.25.12': + optional: true + + '@esbuild/android-arm64@0.27.7': + optional: true + + '@esbuild/android-arm@0.25.12': + optional: true + + '@esbuild/android-arm@0.27.7': + optional: true + + '@esbuild/android-x64@0.25.12': + optional: true + + '@esbuild/android-x64@0.27.7': + optional: true + + '@esbuild/darwin-arm64@0.25.12': + optional: true + + '@esbuild/darwin-arm64@0.27.7': + optional: true + + '@esbuild/darwin-x64@0.25.12': + optional: true + + '@esbuild/darwin-x64@0.27.7': + optional: true + + '@esbuild/freebsd-arm64@0.25.12': + optional: true + + '@esbuild/freebsd-arm64@0.27.7': + optional: true + + '@esbuild/freebsd-x64@0.25.12': + optional: true + + '@esbuild/freebsd-x64@0.27.7': + optional: true + + '@esbuild/linux-arm64@0.25.12': + optional: true + + '@esbuild/linux-arm64@0.27.7': + optional: true + + '@esbuild/linux-arm@0.25.12': + optional: true + + '@esbuild/linux-arm@0.27.7': + optional: true + + '@esbuild/linux-ia32@0.25.12': + optional: true + + '@esbuild/linux-ia32@0.27.7': + optional: true + + '@esbuild/linux-loong64@0.25.12': + optional: true + + '@esbuild/linux-loong64@0.27.7': + optional: true + + '@esbuild/linux-mips64el@0.25.12': + optional: true + + '@esbuild/linux-mips64el@0.27.7': + optional: true + + '@esbuild/linux-ppc64@0.25.12': + optional: true + + '@esbuild/linux-ppc64@0.27.7': + optional: true + + '@esbuild/linux-riscv64@0.25.12': + optional: true + + '@esbuild/linux-riscv64@0.27.7': + optional: true + + '@esbuild/linux-s390x@0.25.12': + optional: true + + '@esbuild/linux-s390x@0.27.7': + optional: true + + '@esbuild/linux-x64@0.25.12': + optional: true + + '@esbuild/linux-x64@0.27.7': + optional: true + + '@esbuild/netbsd-arm64@0.25.12': + optional: true + + '@esbuild/netbsd-arm64@0.27.7': + optional: true + + '@esbuild/netbsd-x64@0.25.12': + optional: true + + '@esbuild/netbsd-x64@0.27.7': + optional: true + + '@esbuild/openbsd-arm64@0.25.12': + optional: true + + '@esbuild/openbsd-arm64@0.27.7': + optional: true + + '@esbuild/openbsd-x64@0.25.12': + optional: true + + '@esbuild/openbsd-x64@0.27.7': + optional: true + + '@esbuild/openharmony-arm64@0.25.12': + optional: true + + '@esbuild/openharmony-arm64@0.27.7': + optional: true + + '@esbuild/sunos-x64@0.25.12': + optional: true + + '@esbuild/sunos-x64@0.27.7': + optional: true + + '@esbuild/win32-arm64@0.25.12': + optional: true + + '@esbuild/win32-arm64@0.27.7': + optional: true + + '@esbuild/win32-ia32@0.25.12': + optional: true + + '@esbuild/win32-ia32@0.27.7': + optional: true + + '@esbuild/win32-x64@0.25.12': + optional: true + + '@esbuild/win32-x64@0.27.7': + optional: true + + '@eslint-community/eslint-utils@4.9.1(eslint@10.7.0(jiti@2.7.0))': + dependencies: + eslint: 10.7.0(jiti@2.7.0) + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.2': {} + + '@eslint/config-array@0.23.5': + dependencies: + '@eslint/object-schema': 3.0.5 + debug: 4.4.3 + minimatch: 10.2.5 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.6.0': + dependencies: + '@eslint/core': 1.2.1 + + '@eslint/core@1.2.1': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/js@10.0.1(eslint@10.7.0(jiti@2.7.0))': + optionalDependencies: + eslint: 10.7.0(jiti@2.7.0) + + '@eslint/object-schema@3.0.5': {} + + '@eslint/plugin-kit@0.7.2': + dependencies: + '@eslint/core': 1.2.1 + levn: 0.4.1 + + '@fastify/busboy@3.2.0': {} + + '@fontsource/dm-sans@5.2.8': {} + + '@fontsource/lato@5.2.7': {} + + '@graphql-codegen/add@7.1.0(graphql@16.14.2)': + dependencies: + '@graphql-codegen/plugin-helpers': 7.1.0(graphql@16.14.2) + graphql: 16.14.2 + tslib: 2.8.1 + + '@graphql-codegen/cli@7.2.0(@types/node@26.1.1)(crossws@0.3.5)(graphql@16.14.2)(typescript@5.9.3)': + dependencies: + '@babel/generator': 7.29.7 + '@babel/template': 7.29.7 + '@babel/types': 7.29.7 + '@graphql-codegen/client-preset': 6.1.0(graphql@16.14.2) + '@graphql-codegen/core': 6.2.0(graphql@16.14.2) + '@graphql-codegen/plugin-helpers': 7.1.0(graphql@16.14.2) + '@graphql-tools/apollo-engine-loader': 8.0.32(graphql@16.14.2) + '@graphql-tools/code-file-loader': 8.1.34(graphql@16.14.2) + '@graphql-tools/git-loader': 8.0.38(graphql@16.14.2) + '@graphql-tools/github-loader': 9.1.4(@types/node@26.1.1)(graphql@16.14.2) + '@graphql-tools/graphql-file-loader': 8.1.16(graphql@16.14.2) + '@graphql-tools/json-file-loader': 8.0.30(graphql@16.14.2) + '@graphql-tools/load': 8.1.13(graphql@16.14.2) + '@graphql-tools/merge': 9.2.0(graphql@16.14.2) + '@graphql-tools/url-loader': 9.1.4(@types/node@26.1.1)(crossws@0.3.5)(graphql@16.14.2) + '@graphql-tools/utils': 11.2.0(graphql@16.14.2) + '@inquirer/prompts': 8.5.2(@types/node@26.1.1) + '@whatwg-node/fetch': 0.10.13 + chalk: 5.6.2 + cosmiconfig: 9.0.2(typescript@5.9.3) + debounce: 3.0.0 + detect-indent: 7.0.2 + graphql: 16.14.2 + graphql-config: 5.1.6(@types/node@26.1.1)(crossws@0.3.5)(graphql@16.14.2)(typescript@5.9.3) + is-glob: 4.0.3 + jiti: 2.7.0 + json-to-pretty-yaml: 1.2.2 + listr2: 10.2.2 + log-symbols: 7.0.1 + micromatch: 4.0.8 + shell-quote: 1.10.0 + string-env-interpolation: 1.0.1 + ts-log: 3.0.2 + tslib: 2.8.1 + yaml: 2.9.0 + yargs: 18.0.0 + transitivePeerDependencies: + - '@fastify/websocket' + - '@types/node' + - bufferutil + - cosmiconfig-toml-loader + - crossws + - graphql-sock + - supports-color + - typescript + - utf-8-validate + + '@graphql-codegen/client-preset@6.1.0(graphql@16.14.2)': + dependencies: + '@babel/helper-plugin-utils': 7.29.7 + '@babel/template': 7.29.7 + '@graphql-codegen/add': 7.1.0(graphql@16.14.2) + '@graphql-codegen/gql-tag-operations': 6.1.0(graphql@16.14.2) + '@graphql-codegen/plugin-helpers': 7.1.0(graphql@16.14.2) + '@graphql-codegen/typed-document-node': 7.1.0(graphql@16.14.2) + '@graphql-codegen/typescript': 6.1.0(graphql@16.14.2) + '@graphql-codegen/typescript-operations': 6.1.0(graphql@16.14.2) + '@graphql-codegen/visitor-plugin-common': 7.2.0(graphql@16.14.2) + '@graphql-tools/documents': 1.0.1(graphql@16.14.2) + '@graphql-tools/utils': 11.2.0(graphql@16.14.2) + '@graphql-typed-document-node/core': 3.2.0(graphql@16.14.2) + graphql: 16.14.2 + tslib: 2.8.1 + + '@graphql-codegen/core@6.2.0(graphql@16.14.2)': + dependencies: + '@graphql-codegen/plugin-helpers': 7.1.0(graphql@16.14.2) + '@graphql-tools/schema': 10.0.36(graphql@16.14.2) + '@graphql-tools/utils': 11.2.0(graphql@16.14.2) + graphql: 16.14.2 + tslib: 2.8.1 + + '@graphql-codegen/gql-tag-operations@6.1.0(graphql@16.14.2)': + dependencies: + '@graphql-codegen/plugin-helpers': 7.1.0(graphql@16.14.2) + '@graphql-codegen/visitor-plugin-common': 7.2.0(graphql@16.14.2) + '@graphql-tools/utils': 11.2.0(graphql@16.14.2) + auto-bind: 5.0.1 + graphql: 16.14.2 + tslib: 2.8.1 + + '@graphql-codegen/plugin-helpers@7.1.0(graphql@16.14.2)': + dependencies: + '@graphql-tools/utils': 11.2.0(graphql@16.14.2) + change-case-all: 2.1.0 + common-tags: 1.8.2 + graphql: 16.14.2 + import-from: 4.0.0 + tslib: 2.8.1 + + '@graphql-codegen/schema-ast@6.1.0(graphql@16.14.2)': + dependencies: + '@graphql-codegen/plugin-helpers': 7.1.0(graphql@16.14.2) + '@graphql-tools/utils': 11.2.0(graphql@16.14.2) + graphql: 16.14.2 + tslib: 2.8.1 + + '@graphql-codegen/typed-document-node@7.1.0(graphql@16.14.2)': + dependencies: + '@graphql-codegen/plugin-helpers': 7.1.0(graphql@16.14.2) + '@graphql-codegen/visitor-plugin-common': 7.2.0(graphql@16.14.2) + auto-bind: 5.0.1 + change-case-all: 2.1.0 + graphql: 16.14.2 + tslib: 2.8.1 + + '@graphql-codegen/typescript-operations@6.1.0(graphql@16.14.2)': + dependencies: + '@graphql-codegen/plugin-helpers': 7.1.0(graphql@16.14.2) + '@graphql-codegen/schema-ast': 6.1.0(graphql@16.14.2) + '@graphql-codegen/visitor-plugin-common': 7.2.0(graphql@16.14.2) + auto-bind: 5.0.1 + graphql: 16.14.2 + tslib: 2.8.1 + + '@graphql-codegen/typescript@6.1.0(graphql@16.14.2)': + dependencies: + '@graphql-codegen/plugin-helpers': 7.1.0(graphql@16.14.2) + '@graphql-codegen/schema-ast': 6.1.0(graphql@16.14.2) + '@graphql-codegen/visitor-plugin-common': 7.2.0(graphql@16.14.2) + auto-bind: 5.0.1 + graphql: 16.14.2 + tslib: 2.8.1 + + '@graphql-codegen/visitor-plugin-common@7.2.0(graphql@16.14.2)': + dependencies: + '@graphql-codegen/plugin-helpers': 7.1.0(graphql@16.14.2) + '@graphql-tools/optimize': 2.0.0(graphql@16.14.2) + '@graphql-tools/relay-operation-optimizer': 7.1.6(graphql@16.14.2) + '@graphql-tools/utils': 11.2.0(graphql@16.14.2) + auto-bind: 5.0.1 + change-case-all: 2.1.0 + dependency-graph: 1.0.0 + graphql: 16.14.2 + graphql-tag: 2.12.7(graphql@16.14.2) + parse-filepath: 1.0.2 + tslib: 2.8.1 + + '@graphql-hive/signal@2.0.0': {} + + '@graphql-tools/apollo-engine-loader@8.0.32(graphql@16.14.2)': + dependencies: + '@graphql-tools/utils': 11.2.0(graphql@16.14.2) + '@whatwg-node/fetch': 0.10.13 + graphql: 16.14.2 + sync-fetch: 0.6.0 + tslib: 2.8.1 + + '@graphql-tools/batch-execute@10.0.9(graphql@16.14.2)': + dependencies: + '@graphql-tools/utils': 11.2.0(graphql@16.14.2) + '@whatwg-node/promise-helpers': 1.3.2 + dataloader: 2.2.3 + graphql: 16.14.2 + tslib: 2.8.1 + + '@graphql-tools/code-file-loader@8.1.34(graphql@16.14.2)': + dependencies: + '@graphql-tools/graphql-tag-pluck': 8.3.33(graphql@16.14.2) + '@graphql-tools/utils': 11.2.0(graphql@16.14.2) + globby: 11.1.0 + graphql: 16.14.2 + tslib: 2.8.1 + unixify: 1.0.0 + transitivePeerDependencies: + - supports-color + + '@graphql-tools/delegate@12.0.19(graphql@16.14.2)': + dependencies: + '@graphql-tools/batch-execute': 10.0.9(graphql@16.14.2) + '@graphql-tools/executor': 1.5.5(graphql@16.14.2) + '@graphql-tools/schema': 10.0.36(graphql@16.14.2) + '@graphql-tools/utils': 11.2.0(graphql@16.14.2) + '@repeaterjs/repeater': 3.1.0 + '@whatwg-node/promise-helpers': 1.3.2 + dataloader: 2.2.3 + graphql: 16.14.2 + tslib: 2.8.1 + + '@graphql-tools/documents@1.0.1(graphql@16.14.2)': + dependencies: + graphql: 16.14.2 + lodash.sortby: 4.7.0 + tslib: 2.8.1 + + '@graphql-tools/executor-common@1.0.6(graphql@16.14.2)': + dependencies: + '@envelop/core': 5.5.1 + '@graphql-tools/utils': 11.2.0(graphql@16.14.2) + graphql: 16.14.2 + + '@graphql-tools/executor-graphql-ws@3.1.5(crossws@0.3.5)(graphql@16.14.2)': + dependencies: + '@graphql-tools/executor-common': 1.0.6(graphql@16.14.2) + '@graphql-tools/utils': 11.2.0(graphql@16.14.2) + '@whatwg-node/disposablestack': 0.0.6 + graphql: 16.14.2 + graphql-ws: 6.1.0(crossws@0.3.5)(graphql@16.14.2)(ws@8.21.0) + isows: 1.0.7(ws@8.21.0) + tslib: 2.8.1 + ws: 8.21.0 + transitivePeerDependencies: + - '@fastify/websocket' + - bufferutil + - crossws + - utf-8-validate + + '@graphql-tools/executor-http@3.3.0(@types/node@26.1.1)(graphql@16.14.2)': + dependencies: + '@graphql-hive/signal': 2.0.0 + '@graphql-tools/executor-common': 1.0.6(graphql@16.14.2) + '@graphql-tools/utils': 11.2.0(graphql@16.14.2) + '@repeaterjs/repeater': 3.1.0 + '@whatwg-node/disposablestack': 0.0.6 + '@whatwg-node/fetch': 0.10.13 + '@whatwg-node/promise-helpers': 1.3.2 + graphql: 16.14.2 + meros: 1.3.2(@types/node@26.1.1) + tslib: 2.8.1 + transitivePeerDependencies: + - '@types/node' + + '@graphql-tools/executor-legacy-ws@1.1.30(graphql@16.14.2)': + dependencies: + '@graphql-tools/utils': 11.2.0(graphql@16.14.2) + '@types/ws': 8.18.1 + graphql: 16.14.2 + isomorphic-ws: 5.0.0(ws@8.21.0) + tslib: 2.8.1 + ws: 8.21.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + '@graphql-tools/executor@1.5.5(graphql@16.14.2)': + dependencies: + '@graphql-tools/utils': 11.2.0(graphql@16.14.2) + '@graphql-typed-document-node/core': 3.2.0(graphql@16.14.2) + '@repeaterjs/repeater': 3.1.0 + '@whatwg-node/disposablestack': 0.0.6 + '@whatwg-node/promise-helpers': 1.3.2 + graphql: 16.14.2 + tslib: 2.8.1 + + '@graphql-tools/git-loader@8.0.38(graphql@16.14.2)': + dependencies: + '@graphql-tools/graphql-tag-pluck': 8.3.33(graphql@16.14.2) + '@graphql-tools/utils': 11.2.0(graphql@16.14.2) + graphql: 16.14.2 + is-glob: 4.0.3 + micromatch: 4.0.8 + tslib: 2.8.1 + unixify: 1.0.0 + transitivePeerDependencies: + - supports-color + + '@graphql-tools/github-loader@9.1.4(@types/node@26.1.1)(graphql@16.14.2)': + dependencies: + '@graphql-tools/executor-http': 3.3.0(@types/node@26.1.1)(graphql@16.14.2) + '@graphql-tools/graphql-tag-pluck': 8.3.33(graphql@16.14.2) + '@graphql-tools/utils': 11.2.0(graphql@16.14.2) + '@whatwg-node/fetch': 0.10.13 + '@whatwg-node/promise-helpers': 1.3.2 + graphql: 16.14.2 + sync-fetch: 0.6.0 + tslib: 2.8.1 + transitivePeerDependencies: + - '@types/node' + - supports-color + + '@graphql-tools/graphql-file-loader@8.1.16(graphql@16.14.2)': + dependencies: + '@graphql-tools/import': 7.1.16(graphql@16.14.2) + '@graphql-tools/utils': 11.2.0(graphql@16.14.2) + globby: 11.1.0 + graphql: 16.14.2 + tslib: 2.8.1 + unixify: 1.0.0 + + '@graphql-tools/graphql-tag-pluck@8.3.33(graphql@16.14.2)': + dependencies: + '@babel/core': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/plugin-syntax-import-assertions': 7.29.7(@babel/core@7.29.7) + '@babel/traverse': 7.29.7 + '@babel/types': 7.29.7 + '@graphql-tools/utils': 11.2.0(graphql@16.14.2) + graphql: 16.14.2 + tslib: 2.8.1 + transitivePeerDependencies: + - supports-color + + '@graphql-tools/import@7.1.16(graphql@16.14.2)': + dependencies: + '@graphql-tools/utils': 11.2.0(graphql@16.14.2) + graphql: 16.14.2 + resolve-from: 5.0.0 + tslib: 2.8.1 + + '@graphql-tools/json-file-loader@8.0.30(graphql@16.14.2)': + dependencies: + '@graphql-tools/utils': 11.2.0(graphql@16.14.2) + globby: 11.1.0 + graphql: 16.14.2 + tslib: 2.8.1 + unixify: 1.0.0 + + '@graphql-tools/load@8.1.13(graphql@16.14.2)': + dependencies: + '@graphql-tools/schema': 10.0.36(graphql@16.14.2) + '@graphql-tools/utils': 11.2.0(graphql@16.14.2) + graphql: 16.14.2 + p-limit: 3.1.0 + tslib: 2.8.1 + + '@graphql-tools/merge@9.2.0(graphql@16.14.2)': + dependencies: + '@graphql-tools/utils': 11.2.0(graphql@16.14.2) + graphql: 16.14.2 + tslib: 2.8.1 + + '@graphql-tools/optimize@2.0.0(graphql@16.14.2)': + dependencies: + graphql: 16.14.2 + tslib: 2.8.1 + + '@graphql-tools/relay-operation-optimizer@7.1.6(graphql@16.14.2)': + dependencies: + '@ardatan/relay-compiler': 13.0.1(graphql@16.14.2) + '@graphql-tools/utils': 11.2.0(graphql@16.14.2) + graphql: 16.14.2 + tslib: 2.8.1 + + '@graphql-tools/schema@10.0.36(graphql@16.14.2)': + dependencies: + '@graphql-tools/merge': 9.2.0(graphql@16.14.2) + '@graphql-tools/utils': 11.2.0(graphql@16.14.2) + graphql: 16.14.2 + tslib: 2.8.1 + + '@graphql-tools/url-loader@9.1.4(@types/node@26.1.1)(crossws@0.3.5)(graphql@16.14.2)': + dependencies: + '@graphql-tools/executor-graphql-ws': 3.1.5(crossws@0.3.5)(graphql@16.14.2) + '@graphql-tools/executor-http': 3.3.0(@types/node@26.1.1)(graphql@16.14.2) + '@graphql-tools/executor-legacy-ws': 1.1.30(graphql@16.14.2) + '@graphql-tools/utils': 11.2.0(graphql@16.14.2) + '@graphql-tools/wrap': 11.1.18(graphql@16.14.2) + '@types/ws': 8.18.1 + '@whatwg-node/fetch': 0.10.13 + '@whatwg-node/promise-helpers': 1.3.2 + graphql: 16.14.2 + isomorphic-ws: 5.0.0(ws@8.21.0) + sync-fetch: 0.6.0 + tslib: 2.8.1 + ws: 8.21.0 + transitivePeerDependencies: + - '@fastify/websocket' + - '@types/node' + - bufferutil + - crossws + - utf-8-validate + + '@graphql-tools/utils@11.2.0(graphql@16.14.2)': + dependencies: + '@graphql-typed-document-node/core': 3.2.0(graphql@16.14.2) + '@whatwg-node/promise-helpers': 1.3.2 + cross-inspect: 1.0.1 + graphql: 16.14.2 + tslib: 2.8.1 + + '@graphql-tools/wrap@11.1.18(graphql@16.14.2)': + dependencies: + '@graphql-tools/delegate': 12.0.19(graphql@16.14.2) + '@graphql-tools/schema': 10.0.36(graphql@16.14.2) + '@graphql-tools/utils': 11.2.0(graphql@16.14.2) + '@whatwg-node/promise-helpers': 1.3.2 + graphql: 16.14.2 + tslib: 2.8.1 + + '@graphql-typed-document-node/core@3.2.0(graphql@16.14.2)': + dependencies: + graphql: 16.14.2 + + '@humanfs/core@0.19.2': + dependencies: + '@humanfs/types': 0.15.0 + + '@humanfs/node@0.16.8': + dependencies: + '@humanfs/core': 0.19.2 + '@humanfs/types': 0.15.0 + '@humanwhocodes/retry': 0.4.3 + + '@humanfs/types@0.15.0': {} + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.4.3': {} + + '@img/colour@1.1.0': + optional: true + + '@img/sharp-darwin-arm64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.0.4 + optional: true + + '@img/sharp-darwin-arm64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.2.4 + optional: true + + '@img/sharp-darwin-x64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.0.4 + optional: true + + '@img/sharp-darwin-x64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.2.4 + optional: true + + '@img/sharp-libvips-darwin-arm64@1.0.4': + optional: true + + '@img/sharp-libvips-darwin-arm64@1.2.4': + optional: true + + '@img/sharp-libvips-darwin-x64@1.0.4': + optional: true + + '@img/sharp-libvips-darwin-x64@1.2.4': + optional: true + + '@img/sharp-libvips-linux-arm64@1.0.4': + optional: true + + '@img/sharp-libvips-linux-arm64@1.2.4': + optional: true + + '@img/sharp-libvips-linux-arm@1.0.5': + optional: true + + '@img/sharp-libvips-linux-arm@1.2.4': + optional: true + + '@img/sharp-libvips-linux-ppc64@1.2.4': + optional: true + + '@img/sharp-libvips-linux-riscv64@1.2.4': + optional: true + + '@img/sharp-libvips-linux-s390x@1.0.4': + optional: true + + '@img/sharp-libvips-linux-s390x@1.2.4': + optional: true + + '@img/sharp-libvips-linux-x64@1.0.4': + optional: true + + '@img/sharp-libvips-linux-x64@1.2.4': + optional: true + + '@img/sharp-libvips-linuxmusl-arm64@1.0.4': + optional: true + + '@img/sharp-libvips-linuxmusl-arm64@1.2.4': + optional: true + + '@img/sharp-libvips-linuxmusl-x64@1.0.4': + optional: true + + '@img/sharp-libvips-linuxmusl-x64@1.2.4': + optional: true + + '@img/sharp-linux-arm64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.0.4 + optional: true + + '@img/sharp-linux-arm64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.2.4 + optional: true + + '@img/sharp-linux-arm@0.33.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.0.5 + optional: true + + '@img/sharp-linux-arm@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.2.4 + optional: true + + '@img/sharp-linux-ppc64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-ppc64': 1.2.4 + optional: true + + '@img/sharp-linux-riscv64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-riscv64': 1.2.4 + optional: true + + '@img/sharp-linux-s390x@0.33.5': + optionalDependencies: + '@img/sharp-libvips-linux-s390x': 1.0.4 + optional: true + + '@img/sharp-linux-s390x@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-s390x': 1.2.4 + optional: true + + '@img/sharp-linux-x64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.0.4 + optional: true + + '@img/sharp-linux-x64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.2.4 + optional: true + + '@img/sharp-linuxmusl-arm64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.0.4 + optional: true + + '@img/sharp-linuxmusl-arm64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 + optional: true + + '@img/sharp-linuxmusl-x64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.0.4 + optional: true + + '@img/sharp-linuxmusl-x64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.2.4 + optional: true + + '@img/sharp-wasm32@0.33.5': + dependencies: + '@emnapi/runtime': 1.11.2 + optional: true + + '@img/sharp-wasm32@0.34.5': + dependencies: + '@emnapi/runtime': 1.11.2 + optional: true + + '@img/sharp-win32-arm64@0.34.5': + optional: true + + '@img/sharp-win32-ia32@0.33.5': + optional: true + + '@img/sharp-win32-ia32@0.34.5': + optional: true + + '@img/sharp-win32-x64@0.33.5': + optional: true + + '@img/sharp-win32-x64@0.34.5': + optional: true + + '@inquirer/ansi@2.0.7': {} + + '@inquirer/checkbox@5.2.1(@types/node@26.1.1)': + dependencies: + '@inquirer/ansi': 2.0.7 + '@inquirer/core': 11.2.1(@types/node@26.1.1) + '@inquirer/figures': 2.0.7 + '@inquirer/type': 4.0.7(@types/node@26.1.1) + optionalDependencies: + '@types/node': 26.1.1 + + '@inquirer/confirm@6.1.1(@types/node@26.1.1)': + dependencies: + '@inquirer/core': 11.2.1(@types/node@26.1.1) + '@inquirer/type': 4.0.7(@types/node@26.1.1) + optionalDependencies: + '@types/node': 26.1.1 + + '@inquirer/core@11.2.1(@types/node@26.1.1)': + dependencies: + '@inquirer/ansi': 2.0.7 + '@inquirer/figures': 2.0.7 + '@inquirer/type': 4.0.7(@types/node@26.1.1) + cli-width: 4.1.0 + fast-wrap-ansi: 0.2.2 + mute-stream: 3.0.0 + signal-exit: 4.1.0 + optionalDependencies: + '@types/node': 26.1.1 + + '@inquirer/editor@5.2.2(@types/node@26.1.1)': + dependencies: + '@inquirer/core': 11.2.1(@types/node@26.1.1) + '@inquirer/external-editor': 3.0.3(@types/node@26.1.1) + '@inquirer/type': 4.0.7(@types/node@26.1.1) + optionalDependencies: + '@types/node': 26.1.1 + + '@inquirer/expand@5.1.1(@types/node@26.1.1)': + dependencies: + '@inquirer/core': 11.2.1(@types/node@26.1.1) + '@inquirer/type': 4.0.7(@types/node@26.1.1) + optionalDependencies: + '@types/node': 26.1.1 + + '@inquirer/external-editor@3.0.3(@types/node@26.1.1)': + dependencies: + chardet: 2.2.0 + iconv-lite: 0.7.3 + optionalDependencies: + '@types/node': 26.1.1 + + '@inquirer/figures@2.0.7': {} + + '@inquirer/input@5.1.2(@types/node@26.1.1)': + dependencies: + '@inquirer/core': 11.2.1(@types/node@26.1.1) + '@inquirer/type': 4.0.7(@types/node@26.1.1) + optionalDependencies: + '@types/node': 26.1.1 + + '@inquirer/number@4.1.1(@types/node@26.1.1)': + dependencies: + '@inquirer/core': 11.2.1(@types/node@26.1.1) + '@inquirer/type': 4.0.7(@types/node@26.1.1) + optionalDependencies: + '@types/node': 26.1.1 + + '@inquirer/password@5.1.1(@types/node@26.1.1)': + dependencies: + '@inquirer/ansi': 2.0.7 + '@inquirer/core': 11.2.1(@types/node@26.1.1) + '@inquirer/type': 4.0.7(@types/node@26.1.1) + optionalDependencies: + '@types/node': 26.1.1 + + '@inquirer/prompts@8.5.2(@types/node@26.1.1)': + dependencies: + '@inquirer/checkbox': 5.2.1(@types/node@26.1.1) + '@inquirer/confirm': 6.1.1(@types/node@26.1.1) + '@inquirer/editor': 5.2.2(@types/node@26.1.1) + '@inquirer/expand': 5.1.1(@types/node@26.1.1) + '@inquirer/input': 5.1.2(@types/node@26.1.1) + '@inquirer/number': 4.1.1(@types/node@26.1.1) + '@inquirer/password': 5.1.1(@types/node@26.1.1) + '@inquirer/rawlist': 5.3.1(@types/node@26.1.1) + '@inquirer/search': 4.2.1(@types/node@26.1.1) + '@inquirer/select': 5.2.1(@types/node@26.1.1) + optionalDependencies: + '@types/node': 26.1.1 + + '@inquirer/rawlist@5.3.1(@types/node@26.1.1)': + dependencies: + '@inquirer/core': 11.2.1(@types/node@26.1.1) + '@inquirer/type': 4.0.7(@types/node@26.1.1) + optionalDependencies: + '@types/node': 26.1.1 + + '@inquirer/search@4.2.1(@types/node@26.1.1)': + dependencies: + '@inquirer/core': 11.2.1(@types/node@26.1.1) + '@inquirer/figures': 2.0.7 + '@inquirer/type': 4.0.7(@types/node@26.1.1) + optionalDependencies: + '@types/node': 26.1.1 + + '@inquirer/select@5.2.1(@types/node@26.1.1)': + dependencies: + '@inquirer/ansi': 2.0.7 + '@inquirer/core': 11.2.1(@types/node@26.1.1) + '@inquirer/figures': 2.0.7 + '@inquirer/type': 4.0.7(@types/node@26.1.1) + optionalDependencies: + '@types/node': 26.1.1 + + '@inquirer/type@4.0.7(@types/node@26.1.1)': + optionalDependencies: + '@types/node': 26.1.1 + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@keyv/bigmap@1.3.1(keyv@5.6.0)': + dependencies: + hashery: 1.5.1 + hookified: 1.15.1 + keyv: 5.6.0 + + '@keyv/serialize@1.1.1': {} + + '@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.11.0)(@emnapi/runtime@1.11.0)': + dependencies: + '@emnapi/core': 1.11.0 + '@emnapi/runtime': 1.11.0 + '@tybys/wasm-util': 0.10.3 + optional: true + + '@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': + dependencies: + '@emnapi/core': 1.11.1 + '@emnapi/runtime': 1.11.1 + '@tybys/wasm-util': 0.10.3 + optional: true + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.20.1 + + '@oslojs/encoding@1.1.0': {} + + '@oxc-parser/binding-android-arm-eabi@0.137.0': + optional: true + + '@oxc-parser/binding-android-arm64@0.137.0': + optional: true + + '@oxc-parser/binding-darwin-arm64@0.137.0': + optional: true + + '@oxc-parser/binding-darwin-x64@0.137.0': + optional: true + + '@oxc-parser/binding-freebsd-x64@0.137.0': + optional: true + + '@oxc-parser/binding-linux-arm-gnueabihf@0.137.0': + optional: true + + '@oxc-parser/binding-linux-arm-musleabihf@0.137.0': + optional: true + + '@oxc-parser/binding-linux-arm64-gnu@0.137.0': + optional: true + + '@oxc-parser/binding-linux-arm64-musl@0.137.0': + optional: true + + '@oxc-parser/binding-linux-ppc64-gnu@0.137.0': + optional: true + + '@oxc-parser/binding-linux-riscv64-gnu@0.137.0': + optional: true + + '@oxc-parser/binding-linux-riscv64-musl@0.137.0': + optional: true + + '@oxc-parser/binding-linux-s390x-gnu@0.137.0': + optional: true + + '@oxc-parser/binding-linux-x64-gnu@0.137.0': + optional: true + + '@oxc-parser/binding-linux-x64-musl@0.137.0': + optional: true + + '@oxc-parser/binding-openharmony-arm64@0.137.0': + optional: true + + '@oxc-parser/binding-wasm32-wasi@0.137.0': + dependencies: + '@emnapi/core': 1.11.1 + '@emnapi/runtime': 1.11.1 + '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) + optional: true + + '@oxc-parser/binding-win32-arm64-msvc@0.137.0': + optional: true + + '@oxc-parser/binding-win32-ia32-msvc@0.137.0': + optional: true + + '@oxc-parser/binding-win32-x64-msvc@0.137.0': + optional: true + + '@oxc-project/types@0.137.0': {} + + '@oxc-resolver/binding-android-arm-eabi@11.21.3': + optional: true + + '@oxc-resolver/binding-android-arm64@11.21.3': + optional: true + + '@oxc-resolver/binding-darwin-arm64@11.21.3': + optional: true + + '@oxc-resolver/binding-darwin-x64@11.21.3': + optional: true + + '@oxc-resolver/binding-freebsd-x64@11.21.3': + optional: true + + '@oxc-resolver/binding-linux-arm-gnueabihf@11.21.3': + optional: true + + '@oxc-resolver/binding-linux-arm-musleabihf@11.21.3': + optional: true + + '@oxc-resolver/binding-linux-arm64-gnu@11.21.3': + optional: true + + '@oxc-resolver/binding-linux-arm64-musl@11.21.3': + optional: true + + '@oxc-resolver/binding-linux-ppc64-gnu@11.21.3': + optional: true + + '@oxc-resolver/binding-linux-riscv64-gnu@11.21.3': + optional: true + + '@oxc-resolver/binding-linux-riscv64-musl@11.21.3': + optional: true + + '@oxc-resolver/binding-linux-s390x-gnu@11.21.3': + optional: true + + '@oxc-resolver/binding-linux-x64-gnu@11.21.3': + optional: true + + '@oxc-resolver/binding-linux-x64-musl@11.21.3': + optional: true + + '@oxc-resolver/binding-openharmony-arm64@11.21.3': + optional: true + + '@oxc-resolver/binding-wasm32-wasi@11.21.3': + dependencies: + '@emnapi/core': 1.11.0 + '@emnapi/runtime': 1.11.0 + '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.11.0)(@emnapi/runtime@1.11.0) + optional: true + + '@oxc-resolver/binding-win32-arm64-msvc@11.21.3': + optional: true + + '@oxc-resolver/binding-win32-x64-msvc@11.21.3': + optional: true + + '@pkgr/core@0.3.6': {} + + '@playwright/test@1.61.1': + dependencies: + playwright: 1.61.1 + + '@react-leaflet/core@2.1.0(leaflet@1.9.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + leaflet: 1.9.4 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + '@repeaterjs/repeater@3.1.0': {} + + '@rolldown/pluginutils@1.0.0-beta.27': {} + + '@rollup/pluginutils@5.4.0(rollup@4.62.2)': + dependencies: + '@types/estree': 1.0.9 + estree-walker: 2.0.2 + picomatch: 4.0.5 + optionalDependencies: + rollup: 4.62.2 + + '@rollup/rollup-android-arm-eabi@4.62.2': + optional: true + + '@rollup/rollup-android-arm64@4.62.2': + optional: true + + '@rollup/rollup-darwin-arm64@4.62.2': + optional: true + + '@rollup/rollup-darwin-x64@4.62.2': + optional: true + + '@rollup/rollup-freebsd-arm64@4.62.2': + optional: true + + '@rollup/rollup-freebsd-x64@4.62.2': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.62.2': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.62.2': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.62.2': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.62.2': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.62.2': + optional: true + + '@rollup/rollup-linux-loong64-musl@4.62.2': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.62.2': + optional: true + + '@rollup/rollup-linux-ppc64-musl@4.62.2': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.62.2': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.62.2': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.62.2': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.62.2': + optional: true + + '@rollup/rollup-linux-x64-musl@4.62.2': + optional: true + + '@rollup/rollup-openbsd-x64@4.62.2': + optional: true + + '@rollup/rollup-openharmony-arm64@4.62.2': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.62.2': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.62.2': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.62.2': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.62.2': + optional: true + + '@shikijs/core@3.23.0': + dependencies: + '@shikijs/types': 3.23.0 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.5 + hast-util-to-html: 9.0.5 + + '@shikijs/engine-javascript@3.23.0': + dependencies: + '@shikijs/types': 3.23.0 + '@shikijs/vscode-textmate': 10.0.2 + oniguruma-to-es: 4.3.6 + + '@shikijs/engine-oniguruma@3.23.0': + dependencies: + '@shikijs/types': 3.23.0 + '@shikijs/vscode-textmate': 10.0.2 + + '@shikijs/langs@3.23.0': + dependencies: + '@shikijs/types': 3.23.0 + + '@shikijs/themes@3.23.0': + dependencies: + '@shikijs/types': 3.23.0 + + '@shikijs/types@3.23.0': + dependencies: + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.5 + + '@shikijs/vscode-textmate@10.0.2': {} + + '@sindresorhus/merge-streams@4.0.0': {} + + '@stylistic/eslint-plugin@5.10.0(eslint@10.7.0(jiti@2.7.0))': + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@10.7.0(jiti@2.7.0)) + '@typescript-eslint/types': 8.63.0 + eslint: 10.7.0(jiti@2.7.0) + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + estraverse: 5.3.0 + picomatch: 4.0.5 + + '@stylistic/stylelint-plugin@5.2.1(stylelint@17.14.0(typescript@5.9.3))': + dependencies: + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + '@csstools/media-query-list-parser': 5.0.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + postcss: 8.5.19 + postcss-selector-parser: 7.1.4 + postcss-value-parser: 4.2.0 + style-search: 0.1.0 + stylelint: 17.14.0(typescript@5.9.3) + + '@tybys/wasm-util@0.10.3': + dependencies: + tslib: 2.8.1 + optional: true + + '@types/babel__core@7.20.5': + dependencies: + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 + '@types/babel__generator': 7.27.0 + '@types/babel__template': 7.4.4 + '@types/babel__traverse': 7.28.0 + + '@types/babel__generator@7.27.0': + dependencies: + '@babel/types': 7.29.7 + + '@types/babel__template@7.4.4': + dependencies: + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 + + '@types/babel__traverse@7.28.0': + dependencies: + '@babel/types': 7.29.7 + + '@types/debug@4.1.13': + dependencies: + '@types/ms': 2.1.0 + + '@types/esrecurse@4.3.1': {} + + '@types/estree@1.0.9': {} + + '@types/geojson@7946.0.16': {} + + '@types/hast@3.0.5': + dependencies: + '@types/unist': 3.0.3 + + '@types/json-schema@7.0.15': {} + + '@types/leaflet@1.9.21': + dependencies: + '@types/geojson': 7946.0.16 + + '@types/mdast@4.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/ms@2.1.0': {} + + '@types/nlcst@2.0.3': + dependencies: + '@types/unist': 3.0.3 + + '@types/node@24.13.3': + dependencies: + undici-types: 7.18.2 + + '@types/node@26.1.1': + dependencies: + undici-types: 8.3.0 + + '@types/papaparse@5.5.2': + dependencies: + '@types/node': 26.1.1 + + '@types/prop-types@15.7.15': {} + + '@types/react-dom@18.3.7(@types/react@18.3.31)': + dependencies: + '@types/react': 18.3.31 + + '@types/react@18.3.31': + dependencies: + '@types/prop-types': 15.7.15 + csstype: 3.2.3 + + '@types/sax@1.2.7': + dependencies: + '@types/node': 24.13.3 + + '@types/unist@3.0.3': {} + + '@types/ws@8.18.1': + dependencies: + '@types/node': 26.1.1 + + '@typescript-eslint/eslint-plugin@8.63.0(@typescript-eslint/parser@8.63.0(eslint@10.7.0(jiti@2.7.0))(typescript@5.9.3))(eslint@10.7.0(jiti@2.7.0))(typescript@5.9.3)': + dependencies: + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 8.63.0(eslint@10.7.0(jiti@2.7.0))(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.63.0 + '@typescript-eslint/type-utils': 8.63.0(eslint@10.7.0(jiti@2.7.0))(typescript@5.9.3) + '@typescript-eslint/utils': 8.63.0(eslint@10.7.0(jiti@2.7.0))(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.63.0 + eslint: 10.7.0(jiti@2.7.0) + ignore: 7.0.6 + natural-compare: 1.4.0 + ts-api-utils: 2.5.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.63.0(eslint@10.7.0(jiti@2.7.0))(typescript@5.9.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.63.0 + '@typescript-eslint/types': 8.63.0 + '@typescript-eslint/typescript-estree': 8.63.0(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.63.0 + debug: 4.4.3 + eslint: 10.7.0(jiti@2.7.0) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/project-service@8.63.0(typescript@5.9.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.63.0(typescript@5.9.3) + '@typescript-eslint/types': 8.63.0 + debug: 4.4.3 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@8.63.0': + dependencies: + '@typescript-eslint/types': 8.63.0 + '@typescript-eslint/visitor-keys': 8.63.0 + + '@typescript-eslint/tsconfig-utils@8.63.0(typescript@5.9.3)': + dependencies: + typescript: 5.9.3 + + '@typescript-eslint/type-utils@8.63.0(eslint@10.7.0(jiti@2.7.0))(typescript@5.9.3)': + dependencies: + '@typescript-eslint/types': 8.63.0 + '@typescript-eslint/typescript-estree': 8.63.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.63.0(eslint@10.7.0(jiti@2.7.0))(typescript@5.9.3) + debug: 4.4.3 + eslint: 10.7.0(jiti@2.7.0) + ts-api-utils: 2.5.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@8.63.0': {} + + '@typescript-eslint/typescript-estree@8.63.0(typescript@5.9.3)': + dependencies: + '@typescript-eslint/project-service': 8.63.0(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.63.0(typescript@5.9.3) + '@typescript-eslint/types': 8.63.0 + '@typescript-eslint/visitor-keys': 8.63.0 + debug: 4.4.3 + minimatch: 10.2.5 + semver: 7.8.5 + tinyglobby: 0.2.17 + ts-api-utils: 2.5.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.63.0(eslint@10.7.0(jiti@2.7.0))(typescript@5.9.3)': + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@10.7.0(jiti@2.7.0)) + '@typescript-eslint/scope-manager': 8.63.0 + '@typescript-eslint/types': 8.63.0 + '@typescript-eslint/typescript-estree': 8.63.0(typescript@5.9.3) + eslint: 10.7.0(jiti@2.7.0) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/visitor-keys@8.63.0': + dependencies: + '@typescript-eslint/types': 8.63.0 + eslint-visitor-keys: 5.0.1 + + '@ungap/structured-clone@1.3.3': {} + + '@vitejs/plugin-react@4.7.0(vite@6.4.3(@types/node@26.1.1)(jiti@2.7.0)(yaml@2.9.0))': + dependencies: + '@babel/core': 7.29.7 + '@babel/plugin-transform-react-jsx-self': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-react-jsx-source': 7.29.7(@babel/core@7.29.7) + '@rolldown/pluginutils': 1.0.0-beta.27 + '@types/babel__core': 7.20.5 + react-refresh: 0.17.0 + vite: 6.4.3(@types/node@26.1.1)(jiti@2.7.0)(yaml@2.9.0) + transitivePeerDependencies: + - supports-color + + '@volar/kit@2.4.28(typescript@5.9.3)': + dependencies: + '@volar/language-service': 2.4.28 + '@volar/typescript': 2.4.28 + typesafe-path: 0.2.2 + typescript: 5.9.3 + vscode-languageserver-textdocument: 1.0.12 + vscode-uri: 3.1.0 + + '@volar/language-core@2.4.28': + dependencies: + '@volar/source-map': 2.4.28 + + '@volar/language-server@2.4.28': + dependencies: + '@volar/language-core': 2.4.28 + '@volar/language-service': 2.4.28 + '@volar/typescript': 2.4.28 + path-browserify: 1.0.1 + request-light: 0.7.0 + vscode-languageserver: 9.0.1 + vscode-languageserver-protocol: 3.18.2 + vscode-languageserver-textdocument: 1.0.12 + vscode-uri: 3.1.0 + + '@volar/language-service@2.4.28': + dependencies: + '@volar/language-core': 2.4.28 + vscode-languageserver-protocol: 3.18.2 + vscode-languageserver-textdocument: 1.0.12 + vscode-uri: 3.1.0 + + '@volar/source-map@2.4.28': {} + + '@volar/typescript@2.4.28': + dependencies: + '@volar/language-core': 2.4.28 + path-browserify: 1.0.1 + vscode-uri: 3.1.0 + + '@vscode/emmet-helper@2.11.0': + dependencies: + emmet: 2.4.11 + jsonc-parser: 2.3.1 + vscode-languageserver-textdocument: 1.0.12 + vscode-languageserver-types: 3.18.0 + vscode-uri: 3.1.0 + + '@vscode/l10n@0.0.18': {} + + '@whatwg-node/disposablestack@0.0.6': + dependencies: + '@whatwg-node/promise-helpers': 1.3.2 + tslib: 2.8.1 + + '@whatwg-node/fetch@0.10.13': + dependencies: + '@whatwg-node/node-fetch': 0.8.6 + urlpattern-polyfill: 10.1.0 + + '@whatwg-node/node-fetch@0.8.6': + dependencies: + '@fastify/busboy': 3.2.0 + '@whatwg-node/disposablestack': 0.0.6 + '@whatwg-node/promise-helpers': 1.3.2 + tslib: 2.8.1 + + '@whatwg-node/promise-helpers@1.3.2': + dependencies: + tslib: 2.8.1 + + acorn-jsx@5.3.2(acorn@8.17.0): + dependencies: + acorn: 8.17.0 + + acorn@8.17.0: {} + + ajv-draft-04@1.0.0(ajv@8.20.0): + optionalDependencies: + ajv: 8.20.0 + + ajv-i18n@4.2.0(ajv@8.20.0): + dependencies: + ajv: 8.20.0 + + ajv@6.15.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ajv@8.20.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.1.3 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + + ansi-align@3.0.1: + dependencies: + string-width: 4.2.3 + + ansi-escapes@7.3.0: + dependencies: + environment: 1.1.0 + + ansi-regex@5.0.1: {} + + ansi-regex@6.2.2: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@6.2.3: {} + + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.2 + + arg@5.0.2: {} + + argparse@2.0.1: {} + + aria-query@5.3.2: {} + + array-iterate@2.0.1: {} + + array-union@2.1.0: {} + + astral-regex@2.0.0: {} + + astro-eslint-parser@1.4.0: + dependencies: + '@astrojs/compiler': 3.0.1 + '@typescript-eslint/scope-manager': 8.63.0 + '@typescript-eslint/types': 8.63.0 + astrojs-compiler-sync: 1.1.1(@astrojs/compiler@3.0.1) + debug: 4.4.3 + entities: 7.0.1 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + fast-glob: 3.3.3 + is-glob: 4.0.3 + semver: 7.8.5 + transitivePeerDependencies: + - supports-color + + astro@5.18.2(@types/node@26.1.1)(jiti@2.7.0)(rollup@4.62.2)(typescript@5.9.3)(yaml@2.9.0): + dependencies: + '@astrojs/compiler': 2.13.1 + '@astrojs/internal-helpers': 0.7.6 + '@astrojs/markdown-remark': 6.3.11 + '@astrojs/telemetry': 3.3.0 + '@capsizecss/unpack': 4.0.1 + '@oslojs/encoding': 1.1.0 + '@rollup/pluginutils': 5.4.0(rollup@4.62.2) + acorn: 8.17.0 + aria-query: 5.3.2 + axobject-query: 4.1.0 + boxen: 8.0.1 + ci-info: 4.4.0 + clsx: 2.1.1 + common-ancestor-path: 1.0.1 + cookie: 1.1.1 + cssesc: 3.0.0 + debug: 4.4.3 + deterministic-object-hash: 2.0.2 + devalue: 5.8.1 + diff: 8.0.4 + dlv: 1.1.3 + dset: 3.1.4 + es-module-lexer: 1.7.0 + esbuild: 0.27.7 + estree-walker: 3.0.3 + flattie: 1.1.1 + fontace: 0.4.1 + github-slugger: 2.0.0 + html-escaper: 3.0.3 + http-cache-semantics: 4.2.0 + import-meta-resolve: 4.2.0 + js-yaml: 4.3.0 + magic-string: 0.30.21 + magicast: 0.5.3 + mrmime: 2.0.1 + neotraverse: 0.6.18 + p-limit: 6.2.0 + p-queue: 8.1.1 + package-manager-detector: 1.7.0 + piccolore: 0.1.3 + picomatch: 4.0.5 + prompts: 2.4.2 + rehype: 13.0.2 + semver: 7.8.5 + shiki: 3.23.0 + smol-toml: 1.7.0 + svgo: 4.0.2 + tinyexec: 1.2.4 + tinyglobby: 0.2.17 + tsconfck: 3.1.6(typescript@5.9.3) + ultrahtml: 1.7.0 + unifont: 0.7.4 + unist-util-visit: 5.1.0 + unstorage: 1.17.5 + vfile: 6.0.3 + vite: 6.4.3(@types/node@26.1.1)(jiti@2.7.0)(yaml@2.9.0) + vitefu: 1.1.3(vite@6.4.3(@types/node@26.1.1)(jiti@2.7.0)(yaml@2.9.0)) + xxhash-wasm: 1.1.0 + yargs-parser: 21.1.1 + yocto-spinner: 0.2.3 + zod: 3.25.76 + zod-to-json-schema: 3.25.2(zod@3.25.76) + zod-to-ts: 1.2.0(typescript@5.9.3)(zod@3.25.76) + optionalDependencies: + sharp: 0.34.5 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@types/node' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - db0 + - idb-keyval + - ioredis + - jiti + - less + - lightningcss + - rollup + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - typescript + - uploadthing + - yaml + + astrojs-compiler-sync@1.1.1(@astrojs/compiler@3.0.1): + dependencies: + '@astrojs/compiler': 3.0.1 + synckit: 0.11.13 + + asynckit@0.4.0: {} + + auto-bind@5.0.1: {} + + autoprefixer@10.5.2(postcss@8.5.19): + dependencies: + browserslist: 4.28.6 + caniuse-lite: 1.0.30001805 + fraction.js: 5.3.4 + picocolors: 1.1.1 + postcss: 8.5.19 + postcss-value-parser: 4.2.0 + + axobject-query@4.1.0: {} + + bail@2.0.2: {} + + balanced-match@4.0.4: {} + + base-64@1.0.0: {} + + baseline-browser-mapping@2.10.43: {} + + boolbase@1.0.0: {} + + boxen@8.0.1: + dependencies: + ansi-align: 3.0.1 + camelcase: 8.0.0 + chalk: 5.6.2 + cli-boxes: 3.0.0 + string-width: 7.2.0 + type-fest: 4.41.0 + widest-line: 5.0.0 + wrap-ansi: 9.0.2 + + brace-expansion@5.0.7: + dependencies: + balanced-match: 4.0.4 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + browserslist@4.28.6: + dependencies: + baseline-browser-mapping: 2.10.43 + caniuse-lite: 1.0.30001805 + electron-to-chromium: 1.5.389 + node-releases: 2.0.51 + update-browserslist-db: 1.2.3(browserslist@4.28.6) + + cacheable@2.5.0: + dependencies: + '@cacheable/memory': 2.2.0 + '@cacheable/utils': 2.5.0 + hookified: 1.15.1 + keyv: 5.6.0 + qified: 0.10.1 + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + callsites@3.1.0: {} + + camelcase@8.0.0: {} + + caniuse-lite@1.0.30001805: {} + + ccount@2.0.1: {} + + chalk@5.6.2: {} + + change-case-all@2.1.0: + dependencies: + change-case: 5.4.4 + sponge-case: 2.0.3 + swap-case: 3.0.3 + title-case: 3.0.3 + + change-case@5.4.4: {} + + character-entities-html4@2.1.0: {} + + character-entities-legacy@3.0.0: {} + + character-entities@2.0.2: {} + + chardet@2.2.0: {} + + chokidar@4.0.3: + dependencies: + readdirp: 4.1.2 + + chokidar@5.0.0: + dependencies: + readdirp: 5.0.0 + + ci-info@4.4.0: {} + + cli-boxes@3.0.0: {} + + cli-cursor@5.0.0: + dependencies: + restore-cursor: 5.1.0 + + cli-truncate@5.2.0: + dependencies: + slice-ansi: 8.0.0 + string-width: 8.2.2 + + cli-width@4.1.0: {} + + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + cliui@9.0.1: + dependencies: + string-width: 7.2.0 + strip-ansi: 7.2.0 + wrap-ansi: 9.0.2 + + clsx@2.1.1: {} + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + color-string@1.9.1: + dependencies: + color-name: 1.1.4 + simple-swizzle: 0.2.4 + + color@4.2.3: + dependencies: + color-convert: 2.0.1 + color-string: 1.9.1 + + colord@2.9.3: {} + + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + + comma-separated-tokens@2.0.3: {} + + commander@11.1.0: {} + + common-ancestor-path@1.0.1: {} + + common-tags@1.8.2: {} + + convert-source-map@2.0.0: {} + + cookie-es@1.2.3: {} + + cookie@1.1.1: {} + + cosmiconfig@8.3.6(typescript@5.9.3): + dependencies: + import-fresh: 3.3.1 + js-yaml: 4.3.0 + parse-json: 5.2.0 + path-type: 4.0.0 + optionalDependencies: + typescript: 5.9.3 + + cosmiconfig@9.0.2(typescript@5.9.3): + dependencies: + env-paths: 2.2.1 + import-fresh: 3.3.1 + js-yaml: 4.3.0 + parse-json: 5.2.0 + optionalDependencies: + typescript: 5.9.3 + + cross-fetch@3.2.0: + dependencies: + node-fetch: 2.7.0 + transitivePeerDependencies: + - encoding + + cross-inspect@1.0.1: + dependencies: + tslib: 2.8.1 + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + crossws@0.3.5: + dependencies: + uncrypto: 0.1.3 + + css-blank-pseudo@8.0.1(postcss@8.5.19): + dependencies: + postcss: 8.5.19 + postcss-selector-parser: 7.1.4 + + css-functions-list@3.3.3: {} + + css-has-pseudo@8.0.0(postcss@8.5.19): + dependencies: + '@csstools/selector-specificity': 6.0.0(postcss-selector-parser@7.1.4) + postcss: 8.5.19 + postcss-selector-parser: 7.1.4 + postcss-value-parser: 4.2.0 + + css-prefers-color-scheme@11.0.0(postcss@8.5.19): + dependencies: + postcss: 8.5.19 + + css-select@5.2.2: + dependencies: + boolbase: 1.0.0 + css-what: 6.2.2 + domhandler: 5.0.3 + domutils: 3.2.2 + nth-check: 2.1.1 + + css-tree@2.2.1: + dependencies: + mdn-data: 2.0.28 + source-map-js: 1.2.1 + + css-tree@3.2.1: + dependencies: + mdn-data: 2.27.1 + source-map-js: 1.2.1 + + css-what@6.2.2: {} + + cssdb@8.9.0: {} + + cssesc@3.0.0: {} + + csso@5.0.5: + dependencies: + css-tree: 2.2.1 + + csstype@3.2.3: {} + + data-uri-to-buffer@4.0.1: {} + + dataloader@2.2.3: {} + + debounce@3.0.0: {} + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + decode-named-character-reference@1.3.0: + dependencies: + character-entities: 2.0.2 + + deep-is@0.1.4: {} + + defu@6.1.7: {} + + delayed-stream@1.0.0: {} + + dependency-graph@1.0.0: {} + + dequal@2.0.3: {} + + destr@2.0.5: {} + + detect-indent@7.0.2: {} + + detect-libc@2.1.2: {} + + deterministic-object-hash@2.0.2: + dependencies: + base-64: 1.0.0 + + devalue@5.8.1: {} + + devlop@1.1.0: + dependencies: + dequal: 2.0.3 + + diff@8.0.4: {} + + dir-glob@3.0.1: + dependencies: + path-type: 4.0.0 + + dlv@1.1.3: {} + + dom-serializer@2.0.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + entities: 4.5.0 + + domelementtype@2.3.0: {} + + domhandler@5.0.3: + dependencies: + domelementtype: 2.3.0 + + domutils@3.2.2: + dependencies: + dom-serializer: 2.0.0 + domelementtype: 2.3.0 + domhandler: 5.0.3 + + dset@3.1.4: {} + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + electron-to-chromium@1.5.389: {} + + emmet@2.4.11: + dependencies: + '@emmetio/abbreviation': 2.3.3 + '@emmetio/css-abbreviation': 2.1.8 + + emoji-regex@10.6.0: {} + + emoji-regex@8.0.0: {} + + entities@4.5.0: {} + + entities@6.0.1: {} + + entities@7.0.1: {} + + env-paths@2.2.1: {} + + environment@1.1.0: {} + + error-ex@1.3.4: + dependencies: + is-arrayish: 0.2.1 + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-module-lexer@1.7.0: {} + + es-object-atoms@1.1.2: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.4 + + esbuild@0.25.12: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.12 + '@esbuild/android-arm': 0.25.12 + '@esbuild/android-arm64': 0.25.12 + '@esbuild/android-x64': 0.25.12 + '@esbuild/darwin-arm64': 0.25.12 + '@esbuild/darwin-x64': 0.25.12 + '@esbuild/freebsd-arm64': 0.25.12 + '@esbuild/freebsd-x64': 0.25.12 + '@esbuild/linux-arm': 0.25.12 + '@esbuild/linux-arm64': 0.25.12 + '@esbuild/linux-ia32': 0.25.12 + '@esbuild/linux-loong64': 0.25.12 + '@esbuild/linux-mips64el': 0.25.12 + '@esbuild/linux-ppc64': 0.25.12 + '@esbuild/linux-riscv64': 0.25.12 + '@esbuild/linux-s390x': 0.25.12 + '@esbuild/linux-x64': 0.25.12 + '@esbuild/netbsd-arm64': 0.25.12 + '@esbuild/netbsd-x64': 0.25.12 + '@esbuild/openbsd-arm64': 0.25.12 + '@esbuild/openbsd-x64': 0.25.12 + '@esbuild/openharmony-arm64': 0.25.12 + '@esbuild/sunos-x64': 0.25.12 + '@esbuild/win32-arm64': 0.25.12 + '@esbuild/win32-ia32': 0.25.12 + '@esbuild/win32-x64': 0.25.12 + + esbuild@0.27.7: + optionalDependencies: + '@esbuild/aix-ppc64': 0.27.7 + '@esbuild/android-arm': 0.27.7 + '@esbuild/android-arm64': 0.27.7 + '@esbuild/android-x64': 0.27.7 + '@esbuild/darwin-arm64': 0.27.7 + '@esbuild/darwin-x64': 0.27.7 + '@esbuild/freebsd-arm64': 0.27.7 + '@esbuild/freebsd-x64': 0.27.7 + '@esbuild/linux-arm': 0.27.7 + '@esbuild/linux-arm64': 0.27.7 + '@esbuild/linux-ia32': 0.27.7 + '@esbuild/linux-loong64': 0.27.7 + '@esbuild/linux-mips64el': 0.27.7 + '@esbuild/linux-ppc64': 0.27.7 + '@esbuild/linux-riscv64': 0.27.7 + '@esbuild/linux-s390x': 0.27.7 + '@esbuild/linux-x64': 0.27.7 + '@esbuild/netbsd-arm64': 0.27.7 + '@esbuild/netbsd-x64': 0.27.7 + '@esbuild/openbsd-arm64': 0.27.7 + '@esbuild/openbsd-x64': 0.27.7 + '@esbuild/openharmony-arm64': 0.27.7 + '@esbuild/sunos-x64': 0.27.7 + '@esbuild/win32-arm64': 0.27.7 + '@esbuild/win32-ia32': 0.27.7 + '@esbuild/win32-x64': 0.27.7 + + escalade@3.2.0: {} + + escape-string-regexp@4.0.0: {} + + escape-string-regexp@5.0.0: {} + + eslint-compat-utils@0.6.5(eslint@10.7.0(jiti@2.7.0)): + dependencies: + eslint: 10.7.0(jiti@2.7.0) + semver: 7.8.5 + + eslint-plugin-astro@1.7.0(eslint@10.7.0(jiti@2.7.0)): + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@10.7.0(jiti@2.7.0)) + '@jridgewell/sourcemap-codec': 1.5.5 + '@typescript-eslint/types': 8.63.0 + astro-eslint-parser: 1.4.0 + eslint: 10.7.0(jiti@2.7.0) + eslint-compat-utils: 0.6.5(eslint@10.7.0(jiti@2.7.0)) + globals: 16.5.0 + postcss: 8.5.19 + postcss-selector-parser: 7.1.4 + transitivePeerDependencies: + - supports-color + + eslint-plugin-react-hooks@7.1.1(eslint@10.7.0(jiti@2.7.0)): + dependencies: + '@babel/core': 7.29.7 + '@babel/parser': 7.29.7 + eslint: 10.7.0(jiti@2.7.0) + hermes-parser: 0.25.1 + zod: 4.4.3 + zod-validation-error: 4.0.2(zod@4.4.3) + transitivePeerDependencies: + - supports-color + + eslint-scope@8.4.0: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-scope@9.1.2: + dependencies: + '@types/esrecurse': 4.3.1 + '@types/estree': 1.0.9 + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@4.2.1: {} + + eslint-visitor-keys@5.0.1: {} + + eslint@10.7.0(jiti@2.7.0): + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@10.7.0(jiti@2.7.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.8 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.9 + ajv: 6.15.0 + cross-spawn: 7.0.6 + debug: 4.4.3 + 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.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + minimatch: 10.2.5 + natural-compare: 1.4.0 + optionator: 0.9.4 + optionalDependencies: + jiti: 2.7.0 + transitivePeerDependencies: + - supports-color + + espree@10.4.0: + dependencies: + acorn: 8.17.0 + acorn-jsx: 5.3.2(acorn@8.17.0) + eslint-visitor-keys: 4.2.1 + + espree@11.2.0: + dependencies: + acorn: 8.17.0 + acorn-jsx: 5.3.2(acorn@8.17.0) + eslint-visitor-keys: 5.0.1 + + esquery@1.7.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + estree-walker@2.0.2: {} + + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.9 + + esutils@2.0.3: {} + + eventemitter3@5.0.4: {} + + extend@3.0.2: {} + + extract-files@9.0.0: {} + + fast-deep-equal@3.1.3: {} + + fast-glob@3.3.3: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fast-string-truncated-width@3.0.3: {} + + fast-string-width@3.0.2: + dependencies: + fast-string-truncated-width: 3.0.3 + + fast-uri@3.1.3: {} + + fast-wrap-ansi@0.2.2: + dependencies: + fast-string-width: 3.0.2 + + fastest-levenshtein@1.0.16: {} + + fastq@1.20.1: + dependencies: + reusify: 1.1.0 + + fd-package-json@2.0.0: + dependencies: + walk-up-path: 4.0.0 + + fdir@6.5.0(picomatch@4.0.5): + optionalDependencies: + picomatch: 4.0.5 + + fetch-blob@3.2.0: + dependencies: + node-domexception: 1.0.0 + web-streams-polyfill: 3.3.3 + + file-entry-cache@11.1.5: + dependencies: + flat-cache: 6.1.23 + + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@4.0.1: + dependencies: + flatted: 3.4.2 + keyv: 4.5.4 + + flat-cache@6.1.23: + dependencies: + cacheable: 2.5.0 + flatted: 3.4.2 + hookified: 1.15.1 + + flatted@3.4.2: {} + + flattie@1.1.1: {} + + fontace@0.4.1: + dependencies: + fontkitten: 1.0.3 + + fontkitten@1.0.3: + dependencies: + tiny-inflate: 1.0.3 + + form-data@3.0.5: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.4 + mime-types: 2.1.35 + + formatly@0.3.0: + dependencies: + fd-package-json: 2.0.0 + + formdata-polyfill@4.0.10: + dependencies: + fetch-blob: 3.2.0 + + fraction.js@5.3.4: {} + + fsevents@2.3.2: + optional: true + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + gensync@1.0.0-beta.2: {} + + get-caller-file@2.0.5: {} + + get-east-asian-width@1.6.0: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.4 + math-intrinsics: 1.1.0 + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.2 + + get-tsconfig@4.14.0: + dependencies: + resolve-pkg-maps: 1.0.0 + + github-slugger@2.0.0: {} + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + global-modules@2.0.0: + dependencies: + global-prefix: 3.0.0 + + global-prefix@3.0.0: + dependencies: + ini: 1.3.8 + kind-of: 6.0.3 + which: 1.3.1 + + globals@16.5.0: {} + + globals@17.7.0: {} + + globby@11.1.0: + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.3 + ignore: 5.3.2 + merge2: 1.4.1 + slash: 3.0.0 + + globby@16.2.1: + dependencies: + '@sindresorhus/merge-streams': 4.0.0 + fast-glob: 3.3.3 + ignore: 7.0.6 + is-path-inside: 4.0.0 + slash: 5.1.0 + unicorn-magic: 0.4.0 + + globjoin@0.1.4: {} + + gopd@1.2.0: {} + + graphql-config@5.1.6(@types/node@26.1.1)(crossws@0.3.5)(graphql@16.14.2)(typescript@5.9.3): + dependencies: + '@graphql-tools/graphql-file-loader': 8.1.16(graphql@16.14.2) + '@graphql-tools/json-file-loader': 8.0.30(graphql@16.14.2) + '@graphql-tools/load': 8.1.13(graphql@16.14.2) + '@graphql-tools/merge': 9.2.0(graphql@16.14.2) + '@graphql-tools/url-loader': 9.1.4(@types/node@26.1.1)(crossws@0.3.5)(graphql@16.14.2) + '@graphql-tools/utils': 11.2.0(graphql@16.14.2) + cosmiconfig: 8.3.6(typescript@5.9.3) + graphql: 16.14.2 + jiti: 2.7.0 + minimatch: 10.2.5 + string-env-interpolation: 1.0.1 + tslib: 2.8.1 + transitivePeerDependencies: + - '@fastify/websocket' + - '@types/node' + - bufferutil + - crossws + - typescript + - utf-8-validate + + graphql-request@5.2.0(graphql@16.14.2): + dependencies: + '@graphql-typed-document-node/core': 3.2.0(graphql@16.14.2) + cross-fetch: 3.2.0 + extract-files: 9.0.0 + form-data: 3.0.5 + graphql: 16.14.2 + transitivePeerDependencies: + - encoding + + graphql-tag@2.12.7(graphql@16.14.2): + dependencies: + graphql: 16.14.2 + tslib: 2.8.1 + + graphql-ws@6.1.0(crossws@0.3.5)(graphql@16.14.2)(ws@8.21.0): + dependencies: + graphql: 16.14.2 + optionalDependencies: + crossws: 0.3.5 + ws: 8.21.0 + + graphql@16.14.2: {} + + h3@1.15.11: + dependencies: + cookie-es: 1.2.3 + crossws: 0.3.5 + defu: 6.1.7 + destr: 2.0.5 + iron-webcrypto: 1.2.1 + node-mock-http: 1.0.4 + radix3: 1.1.2 + ufo: 1.6.4 + uncrypto: 0.1.3 + + has-flag@5.0.1: {} + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hashery@1.5.1: + dependencies: + hookified: 1.15.1 + + hasown@2.0.4: + dependencies: + function-bind: 1.1.2 + + hast-util-from-html@2.0.3: + dependencies: + '@types/hast': 3.0.5 + devlop: 1.1.0 + hast-util-from-parse5: 8.0.3 + parse5: 7.3.0 + vfile: 6.0.3 + vfile-message: 4.0.3 + + hast-util-from-parse5@8.0.3: + dependencies: + '@types/hast': 3.0.5 + '@types/unist': 3.0.3 + devlop: 1.1.0 + hastscript: 9.0.1 + property-information: 7.2.0 + vfile: 6.0.3 + vfile-location: 5.0.3 + web-namespaces: 2.0.1 + + hast-util-is-element@3.0.0: + dependencies: + '@types/hast': 3.0.5 + + hast-util-parse-selector@4.0.0: + dependencies: + '@types/hast': 3.0.5 + + hast-util-raw@9.1.0: + dependencies: + '@types/hast': 3.0.5 + '@types/unist': 3.0.3 + '@ungap/structured-clone': 1.3.3 + hast-util-from-parse5: 8.0.3 + hast-util-to-parse5: 8.0.1 + html-void-elements: 3.0.0 + mdast-util-to-hast: 13.2.1 + parse5: 7.3.0 + unist-util-position: 5.0.0 + unist-util-visit: 5.1.0 + vfile: 6.0.3 + web-namespaces: 2.0.1 + zwitch: 2.0.4 + + hast-util-to-html@9.0.5: + dependencies: + '@types/hast': 3.0.5 + '@types/unist': 3.0.3 + ccount: 2.0.1 + comma-separated-tokens: 2.0.3 + hast-util-whitespace: 3.0.0 + html-void-elements: 3.0.0 + mdast-util-to-hast: 13.2.1 + property-information: 7.2.0 + space-separated-tokens: 2.0.2 + stringify-entities: 4.0.4 + zwitch: 2.0.4 + + hast-util-to-parse5@8.0.1: + dependencies: + '@types/hast': 3.0.5 + comma-separated-tokens: 2.0.3 + devlop: 1.1.0 + property-information: 7.2.0 + space-separated-tokens: 2.0.2 + web-namespaces: 2.0.1 + zwitch: 2.0.4 + + hast-util-to-text@4.0.2: + dependencies: + '@types/hast': 3.0.5 + '@types/unist': 3.0.3 + hast-util-is-element: 3.0.0 + unist-util-find-after: 5.0.0 + + hast-util-whitespace@3.0.0: + dependencies: + '@types/hast': 3.0.5 + + hastscript@9.0.1: + dependencies: + '@types/hast': 3.0.5 + comma-separated-tokens: 2.0.3 + hast-util-parse-selector: 4.0.0 + property-information: 7.2.0 + space-separated-tokens: 2.0.2 + + hermes-estree@0.25.1: {} + + hermes-parser@0.25.1: + dependencies: + hermes-estree: 0.25.1 + + hookified@1.15.1: {} + + hookified@2.2.0: {} + + html-escaper@3.0.3: {} + + html-tags@5.1.0: {} + + html-void-elements@3.0.0: {} + + http-cache-semantics@4.2.0: {} + + i18next@24.2.3(typescript@5.9.3): + dependencies: + '@babel/runtime': 7.29.7 + optionalDependencies: + typescript: 5.9.3 + + iconv-lite@0.7.3: + dependencies: + safer-buffer: 2.1.2 + + ignore@5.3.2: {} + + ignore@7.0.6: {} + + immutable@5.1.9: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + import-from@4.0.0: {} + + import-meta-resolve@4.2.0: {} + + imurmurhash@0.1.4: {} + + ini@1.3.8: {} + + invariant@2.2.4: + dependencies: + loose-envify: 1.4.0 + + iron-webcrypto@1.2.1: {} + + is-absolute@1.0.0: + dependencies: + is-relative: 1.0.0 + is-windows: 1.0.2 + + is-arrayish@0.2.1: {} + + is-arrayish@0.3.4: {} + + is-docker@3.0.0: {} + + is-extglob@2.1.1: {} + + is-fullwidth-code-point@3.0.0: {} + + is-fullwidth-code-point@5.1.0: + dependencies: + get-east-asian-width: 1.6.0 + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-inside-container@1.0.0: + dependencies: + is-docker: 3.0.0 + + is-number@7.0.0: {} + + is-path-inside@4.0.0: {} + + is-plain-obj@4.1.0: {} + + is-relative@1.0.0: + dependencies: + is-unc-path: 1.0.0 + + is-unc-path@1.0.0: + dependencies: + unc-path-regex: 0.1.2 + + is-unicode-supported@2.1.0: {} + + is-windows@1.0.2: {} + + is-wsl@3.1.1: + dependencies: + is-inside-container: 1.0.0 + + isexe@2.0.0: {} + + isomorphic-ws@5.0.0(ws@8.21.0): + dependencies: + ws: 8.21.0 + + isows@1.0.7(ws@8.21.0): + dependencies: + ws: 8.21.0 + + jiti@2.7.0: {} + + js-tokens@4.0.0: {} + + js-yaml@4.3.0: + dependencies: + argparse: 2.0.1 + + jsesc@3.1.0: {} + + json-buffer@3.0.1: {} + + json-parse-even-better-errors@2.3.1: {} + + json-schema-traverse@0.4.1: {} + + json-schema-traverse@1.0.0: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + json-to-pretty-yaml@1.2.2: + dependencies: + remedial: 1.0.8 + remove-trailing-spaces: 1.0.9 + + json5@2.2.3: {} + + jsonc-parser@2.3.1: {} + + jsonc-parser@3.3.1: {} + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + keyv@5.6.0: + dependencies: + '@keyv/serialize': 1.1.1 + + kind-of@6.0.3: {} + + kleur@3.0.3: {} + + kleur@4.1.5: {} + + knip@6.26.0: + dependencies: + fdir: 6.5.0(picomatch@4.0.5) + formatly: 0.3.0 + get-tsconfig: 4.14.0 + jiti: 2.7.0 + oxc-parser: 0.137.0 + oxc-resolver: 11.21.3 + picomatch: 4.0.5 + smol-toml: 1.7.0 + strip-json-comments: 5.0.3 + tinyglobby: 0.2.17 + unbash: 4.0.2 + yaml: 2.9.0 + zod: 4.4.3 + + leaflet@1.9.4: {} + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + lines-and-columns@1.2.4: {} + + listr2@10.2.2: + dependencies: + cli-truncate: 5.2.0 + eventemitter3: 5.0.4 + log-update: 6.1.0 + rfdc: 1.4.1 + wrap-ansi: 10.0.0 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash.sortby@4.7.0: {} + + lodash.truncate@4.4.2: {} + + log-symbols@7.0.1: + dependencies: + is-unicode-supported: 2.1.0 + yoctocolors: 2.1.2 + + log-update@6.1.0: + dependencies: + ansi-escapes: 7.3.0 + cli-cursor: 5.0.0 + slice-ansi: 7.1.2 + strip-ansi: 7.2.0 + wrap-ansi: 9.0.2 + + longest-streak@3.1.0: {} + + loose-envify@1.4.0: + dependencies: + js-tokens: 4.0.0 + + lru-cache@11.5.2: {} + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + magicast@0.5.3: + dependencies: + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 + source-map-js: 1.2.1 + + map-cache@0.2.2: {} + + markdown-table@3.0.4: {} + + math-intrinsics@1.1.0: {} + + mathml-tag-names@4.0.0: {} + + mdast-util-definitions@6.0.0: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + unist-util-visit: 5.1.0 + + mdast-util-find-and-replace@3.0.2: + dependencies: + '@types/mdast': 4.0.4 + escape-string-regexp: 5.0.0 + unist-util-is: 6.0.1 + unist-util-visit-parents: 6.0.2 + + mdast-util-from-markdown@2.0.3: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + decode-named-character-reference: 1.3.0 + devlop: 1.1.0 + mdast-util-to-string: 4.0.0 + micromark: 4.0.2 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-decode-string: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + unist-util-stringify-position: 4.0.0 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-autolink-literal@2.0.1: + dependencies: + '@types/mdast': 4.0.4 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-find-and-replace: 3.0.2 + micromark-util-character: 2.1.1 + + mdast-util-gfm-footnote@2.1.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + micromark-util-normalize-identifier: 2.0.1 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-strikethrough@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-table@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + markdown-table: 3.0.4 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-task-list-item@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm@3.1.0: + dependencies: + mdast-util-from-markdown: 2.0.3 + mdast-util-gfm-autolink-literal: 2.0.1 + mdast-util-gfm-footnote: 2.1.0 + mdast-util-gfm-strikethrough: 2.0.0 + mdast-util-gfm-table: 2.0.0 + mdast-util-gfm-task-list-item: 2.0.0 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-phrasing@4.1.0: + dependencies: + '@types/mdast': 4.0.4 + unist-util-is: 6.0.1 + + mdast-util-to-hast@13.2.1: + dependencies: + '@types/hast': 3.0.5 + '@types/mdast': 4.0.4 + '@ungap/structured-clone': 1.3.3 + devlop: 1.1.0 + micromark-util-sanitize-uri: 2.0.1 + trim-lines: 3.0.1 + unist-util-position: 5.0.0 + unist-util-visit: 5.1.0 + vfile: 6.0.3 + + mdast-util-to-markdown@2.1.2: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + longest-streak: 3.1.0 + mdast-util-phrasing: 4.1.0 + mdast-util-to-string: 4.0.0 + micromark-util-classify-character: 2.0.1 + micromark-util-decode-string: 2.0.1 + unist-util-visit: 5.1.0 + zwitch: 2.0.4 + + mdast-util-to-string@4.0.0: + dependencies: + '@types/mdast': 4.0.4 + + mdn-data@2.0.28: {} + + mdn-data@2.27.1: {} + + meow@14.1.0: {} + + merge2@1.4.1: {} + + meros@1.3.2(@types/node@26.1.1): + optionalDependencies: + '@types/node': 26.1.1 + + micromark-core-commonmark@2.0.3: + dependencies: + decode-named-character-reference: 1.3.0 + devlop: 1.1.0 + micromark-factory-destination: 2.0.1 + micromark-factory-label: 2.0.1 + micromark-factory-space: 2.0.1 + micromark-factory-title: 2.0.1 + micromark-factory-whitespace: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-html-tag-name: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-subtokenize: 2.1.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-autolink-literal@2.1.0: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-footnote@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-strikethrough@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-table@2.1.1: + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-tagfilter@2.0.0: + dependencies: + micromark-util-types: 2.0.2 + + micromark-extension-gfm-task-list-item@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm@3.0.0: + dependencies: + micromark-extension-gfm-autolink-literal: 2.1.0 + micromark-extension-gfm-footnote: 2.1.0 + micromark-extension-gfm-strikethrough: 2.1.0 + micromark-extension-gfm-table: 2.1.1 + micromark-extension-gfm-tagfilter: 2.0.0 + micromark-extension-gfm-task-list-item: 2.1.0 + micromark-util-combine-extensions: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-destination@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-label@2.0.1: + dependencies: + devlop: 1.1.0 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-space@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-types: 2.0.2 + + micromark-factory-title@2.0.1: + dependencies: + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-whitespace@2.0.1: + dependencies: + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-character@2.1.1: + dependencies: + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-chunked@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-classify-character@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-combine-extensions@2.0.1: + dependencies: + micromark-util-chunked: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-decode-numeric-character-reference@2.0.2: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-decode-string@2.0.1: + dependencies: + decode-named-character-reference: 1.3.0 + micromark-util-character: 2.1.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-symbol: 2.0.1 + + micromark-util-encode@2.0.1: {} + + micromark-util-html-tag-name@2.0.1: {} + + micromark-util-normalize-identifier@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-resolve-all@2.0.1: + dependencies: + micromark-util-types: 2.0.2 + + micromark-util-sanitize-uri@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-encode: 2.0.1 + micromark-util-symbol: 2.0.1 + + micromark-util-subtokenize@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-symbol@2.0.1: {} + + micromark-util-types@2.0.2: {} + + micromark@4.0.2: + dependencies: + '@types/debug': 4.1.13 + debug: 4.4.3 + decode-named-character-reference: 1.3.0 + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-combine-extensions: 2.0.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-encode: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-subtokenize: 2.1.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + transitivePeerDependencies: + - supports-color + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.2 + + mime-db@1.52.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + mimic-function@5.0.1: {} + + minimatch@10.2.5: + dependencies: + brace-expansion: 5.0.7 + + mrmime@2.0.1: {} + + ms@2.1.3: {} + + muggle-string@0.4.1: {} + + mute-stream@3.0.0: {} + + nanoid@3.3.16: {} + + natural-compare@1.4.0: {} + + neotraverse@0.6.18: {} + + nlcst-to-string@4.0.0: + dependencies: + '@types/nlcst': 2.0.3 + + node-domexception@1.0.0: {} + + node-fetch-native@1.6.7: {} + + node-fetch@2.7.0: + dependencies: + whatwg-url: 5.0.0 + + node-fetch@3.3.2: + dependencies: + data-uri-to-buffer: 4.0.1 + fetch-blob: 3.2.0 + formdata-polyfill: 4.0.10 + + node-mock-http@1.0.4: {} + + node-releases@2.0.51: {} + + normalize-path@2.1.1: + dependencies: + remove-trailing-separator: 1.1.0 + + normalize-path@3.0.0: {} + + nth-check@2.1.1: + dependencies: + boolbase: 1.0.0 + + ofetch@1.5.1: + dependencies: + destr: 2.0.5 + node-fetch-native: 1.6.7 + ufo: 1.6.4 + + ohash@2.0.11: {} + + onetime@7.0.0: + dependencies: + mimic-function: 5.0.1 + + oniguruma-parser@0.12.2: {} + + oniguruma-to-es@4.3.6: + dependencies: + oniguruma-parser: 0.12.2 + regex: 6.1.0 + regex-recursion: 6.0.2 + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + oxc-parser@0.137.0: + dependencies: + '@oxc-project/types': 0.137.0 + optionalDependencies: + '@oxc-parser/binding-android-arm-eabi': 0.137.0 + '@oxc-parser/binding-android-arm64': 0.137.0 + '@oxc-parser/binding-darwin-arm64': 0.137.0 + '@oxc-parser/binding-darwin-x64': 0.137.0 + '@oxc-parser/binding-freebsd-x64': 0.137.0 + '@oxc-parser/binding-linux-arm-gnueabihf': 0.137.0 + '@oxc-parser/binding-linux-arm-musleabihf': 0.137.0 + '@oxc-parser/binding-linux-arm64-gnu': 0.137.0 + '@oxc-parser/binding-linux-arm64-musl': 0.137.0 + '@oxc-parser/binding-linux-ppc64-gnu': 0.137.0 + '@oxc-parser/binding-linux-riscv64-gnu': 0.137.0 + '@oxc-parser/binding-linux-riscv64-musl': 0.137.0 + '@oxc-parser/binding-linux-s390x-gnu': 0.137.0 + '@oxc-parser/binding-linux-x64-gnu': 0.137.0 + '@oxc-parser/binding-linux-x64-musl': 0.137.0 + '@oxc-parser/binding-openharmony-arm64': 0.137.0 + '@oxc-parser/binding-wasm32-wasi': 0.137.0 + '@oxc-parser/binding-win32-arm64-msvc': 0.137.0 + '@oxc-parser/binding-win32-ia32-msvc': 0.137.0 + '@oxc-parser/binding-win32-x64-msvc': 0.137.0 + + oxc-resolver@11.21.3: + optionalDependencies: + '@oxc-resolver/binding-android-arm-eabi': 11.21.3 + '@oxc-resolver/binding-android-arm64': 11.21.3 + '@oxc-resolver/binding-darwin-arm64': 11.21.3 + '@oxc-resolver/binding-darwin-x64': 11.21.3 + '@oxc-resolver/binding-freebsd-x64': 11.21.3 + '@oxc-resolver/binding-linux-arm-gnueabihf': 11.21.3 + '@oxc-resolver/binding-linux-arm-musleabihf': 11.21.3 + '@oxc-resolver/binding-linux-arm64-gnu': 11.21.3 + '@oxc-resolver/binding-linux-arm64-musl': 11.21.3 + '@oxc-resolver/binding-linux-ppc64-gnu': 11.21.3 + '@oxc-resolver/binding-linux-riscv64-gnu': 11.21.3 + '@oxc-resolver/binding-linux-riscv64-musl': 11.21.3 + '@oxc-resolver/binding-linux-s390x-gnu': 11.21.3 + '@oxc-resolver/binding-linux-x64-gnu': 11.21.3 + '@oxc-resolver/binding-linux-x64-musl': 11.21.3 + '@oxc-resolver/binding-openharmony-arm64': 11.21.3 + '@oxc-resolver/binding-wasm32-wasi': 11.21.3 + '@oxc-resolver/binding-win32-arm64-msvc': 11.21.3 + '@oxc-resolver/binding-win32-x64-msvc': 11.21.3 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-limit@6.2.0: + dependencies: + yocto-queue: 1.2.2 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + p-queue@8.1.1: + dependencies: + eventemitter3: 5.0.4 + p-timeout: 6.1.4 + + p-timeout@6.1.4: {} + + package-manager-detector@1.7.0: {} + + papaparse@5.5.4: {} + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parse-filepath@1.0.2: + dependencies: + is-absolute: 1.0.0 + map-cache: 0.2.2 + path-root: 0.1.1 + + parse-json@5.2.0: + dependencies: + '@babel/code-frame': 7.29.7 + error-ex: 1.3.4 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + + parse-latin@7.0.0: + dependencies: + '@types/nlcst': 2.0.3 + '@types/unist': 3.0.3 + nlcst-to-string: 4.0.0 + unist-util-modify-children: 4.0.0 + unist-util-visit-children: 3.0.0 + vfile: 6.0.3 + + parse5@7.3.0: + dependencies: + entities: 6.0.1 + + path-browserify@1.0.1: {} + + path-exists@4.0.0: {} + + path-key@3.1.1: {} + + path-root-regex@0.1.2: {} + + path-root@0.1.1: + dependencies: + path-root-regex: 0.1.2 + + path-type@4.0.0: {} + + piccolore@0.1.3: {} + + picocolors@1.1.1: {} + + picomatch@2.3.2: {} + + picomatch@4.0.5: {} + + playwright-core@1.61.1: {} + + playwright@1.61.1: + dependencies: + playwright-core: 1.61.1 + optionalDependencies: + fsevents: 2.3.2 + + postcss-attribute-case-insensitive@8.0.0(postcss@8.5.19): + dependencies: + postcss: 8.5.19 + postcss-selector-parser: 7.1.4 + + postcss-browser-comments@6.0.2(browserslist@4.28.6)(postcss@8.5.19): + dependencies: + browserslist: 4.28.6 + postcss: 8.5.19 + + postcss-clamp@4.1.0(postcss@8.5.19): + dependencies: + postcss: 8.5.19 + postcss-value-parser: 4.2.0 + + postcss-color-functional-notation@8.0.6(postcss@8.5.19): + dependencies: + '@csstools/css-color-parser': 4.1.9(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + '@csstools/postcss-progressive-custom-properties': 5.1.1(postcss@8.5.19) + '@csstools/utilities': 3.0.0(postcss@8.5.19) + postcss: 8.5.19 + + postcss-color-hex-alpha@11.0.0(postcss@8.5.19): + dependencies: + '@csstools/utilities': 3.0.0(postcss@8.5.19) + postcss: 8.5.19 + postcss-value-parser: 4.2.0 + + postcss-color-rebeccapurple@11.0.0(postcss@8.5.19): + dependencies: + '@csstools/utilities': 3.0.0(postcss@8.5.19) + postcss: 8.5.19 + postcss-value-parser: 4.2.0 + + postcss-custom-media@12.0.1(postcss@8.5.19): + dependencies: + '@csstools/cascade-layer-name-parser': 3.0.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + '@csstools/media-query-list-parser': 5.0.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + postcss: 8.5.19 + + postcss-custom-properties@15.0.1(postcss@8.5.19): + dependencies: + '@csstools/cascade-layer-name-parser': 3.0.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + '@csstools/utilities': 3.0.0(postcss@8.5.19) + postcss: 8.5.19 + postcss-value-parser: 4.2.0 + + postcss-custom-selectors@9.0.1(postcss@8.5.19): + dependencies: + '@csstools/cascade-layer-name-parser': 3.0.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + postcss: 8.5.19 + postcss-selector-parser: 7.1.4 + + postcss-dir-pseudo-class@10.0.0(postcss@8.5.19): + dependencies: + postcss: 8.5.19 + postcss-selector-parser: 7.1.4 + + postcss-double-position-gradients@7.0.2(postcss@8.5.19): + dependencies: + '@csstools/postcss-progressive-custom-properties': 5.1.1(postcss@8.5.19) + '@csstools/utilities': 3.0.0(postcss@8.5.19) + postcss: 8.5.19 + postcss-value-parser: 4.2.0 + + postcss-flexbugs-fixes@5.0.2(postcss@8.5.19): + dependencies: + postcss: 8.5.19 + + postcss-focus-visible@11.0.0(postcss@8.5.19): + dependencies: + postcss: 8.5.19 + postcss-selector-parser: 7.1.4 + + postcss-focus-within@10.0.0(postcss@8.5.19): + dependencies: + postcss: 8.5.19 + postcss-selector-parser: 7.1.4 + + postcss-font-variant@5.0.0(postcss@8.5.19): + dependencies: + postcss: 8.5.19 + + postcss-gap-properties@7.0.0(postcss@8.5.19): + dependencies: + postcss: 8.5.19 + + postcss-image-set-function@8.0.0(postcss@8.5.19): + dependencies: + '@csstools/utilities': 3.0.0(postcss@8.5.19) + postcss: 8.5.19 + postcss-value-parser: 4.2.0 + + postcss-lab-function@8.0.6(postcss@8.5.19): + dependencies: + '@csstools/css-color-parser': 4.1.9(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + '@csstools/postcss-progressive-custom-properties': 5.1.1(postcss@8.5.19) + '@csstools/utilities': 3.0.0(postcss@8.5.19) + postcss: 8.5.19 + + postcss-logical@9.0.0(postcss@8.5.19): + dependencies: + postcss: 8.5.19 + postcss-value-parser: 4.2.0 + + postcss-nested@7.0.2(postcss@8.5.19): + dependencies: + postcss: 8.5.19 + postcss-selector-parser: 7.1.4 + + postcss-nesting@14.0.0(postcss@8.5.19): + dependencies: + '@csstools/selector-resolve-nested': 4.0.0(postcss-selector-parser@7.1.4) + '@csstools/selector-specificity': 6.0.0(postcss-selector-parser@7.1.4) + postcss: 8.5.19 + postcss-selector-parser: 7.1.4 + + postcss-normalize@13.0.1(browserslist@4.28.6)(postcss@8.5.19): + dependencies: + '@csstools/normalize.css': 12.1.1 + browserslist: 4.28.6 + postcss: 8.5.19 + postcss-browser-comments: 6.0.2(browserslist@4.28.6)(postcss@8.5.19) + sanitize.css: 13.0.0 + + postcss-opacity-percentage@3.0.0(postcss@8.5.19): + dependencies: + postcss: 8.5.19 + + postcss-overflow-shorthand@7.0.0(postcss@8.5.19): + dependencies: + postcss: 8.5.19 + postcss-value-parser: 4.2.0 + + postcss-page-break@3.0.4(postcss@8.5.19): + dependencies: + postcss: 8.5.19 + + postcss-place@11.0.0(postcss@8.5.19): + dependencies: + postcss: 8.5.19 + postcss-value-parser: 4.2.0 + + postcss-preset-env@11.3.2(postcss@8.5.19): + dependencies: + '@csstools/postcss-alpha-function': 2.0.7(postcss@8.5.19) + '@csstools/postcss-cascade-layers': 6.0.0(postcss@8.5.19) + '@csstools/postcss-color-function': 5.0.6(postcss@8.5.19) + '@csstools/postcss-color-function-display-p3-linear': 2.0.6(postcss@8.5.19) + '@csstools/postcss-color-mix-function': 4.0.6(postcss@8.5.19) + '@csstools/postcss-color-mix-variadic-function-arguments': 2.0.6(postcss@8.5.19) + '@csstools/postcss-container-rule-prelude-list': 1.0.1(postcss@8.5.19) + '@csstools/postcss-content-alt-text': 3.0.2(postcss@8.5.19) + '@csstools/postcss-contrast-color-function': 3.0.6(postcss@8.5.19) + '@csstools/postcss-exponential-functions': 3.0.3(postcss@8.5.19) + '@csstools/postcss-font-format-keywords': 5.0.0(postcss@8.5.19) + '@csstools/postcss-font-width-property': 1.0.0(postcss@8.5.19) + '@csstools/postcss-gamut-mapping': 3.0.6(postcss@8.5.19) + '@csstools/postcss-gradients-interpolation-method': 6.0.6(postcss@8.5.19) + '@csstools/postcss-hwb-function': 5.0.6(postcss@8.5.19) + '@csstools/postcss-ic-unit': 5.0.2(postcss@8.5.19) + '@csstools/postcss-image-function': 1.0.1(postcss@8.5.19) + '@csstools/postcss-initial': 3.0.0(postcss@8.5.19) + '@csstools/postcss-is-pseudo-class': 6.0.0(postcss@8.5.19) + '@csstools/postcss-light-dark-function': 3.0.2(postcss@8.5.19) + '@csstools/postcss-logical-float-and-clear': 4.0.0(postcss@8.5.19) + '@csstools/postcss-logical-overflow': 3.0.0(postcss@8.5.19) + '@csstools/postcss-logical-overscroll-behavior': 3.0.0(postcss@8.5.19) + '@csstools/postcss-logical-resize': 4.0.0(postcss@8.5.19) + '@csstools/postcss-logical-viewport-units': 4.0.0(postcss@8.5.19) + '@csstools/postcss-media-minmax': 3.0.3(postcss@8.5.19) + '@csstools/postcss-media-queries-aspect-ratio-number-values': 4.0.0(postcss@8.5.19) + '@csstools/postcss-mixins': 1.0.0(postcss@8.5.19) + '@csstools/postcss-nested-calc': 5.0.0(postcss@8.5.19) + '@csstools/postcss-normalize-display-values': 5.0.1(postcss@8.5.19) + '@csstools/postcss-oklab-function': 5.0.6(postcss@8.5.19) + '@csstools/postcss-position-area-property': 2.0.0(postcss@8.5.19) + '@csstools/postcss-progressive-custom-properties': 5.1.1(postcss@8.5.19) + '@csstools/postcss-property-rule-prelude-list': 2.0.0(postcss@8.5.19) + '@csstools/postcss-random-function': 3.0.3(postcss@8.5.19) + '@csstools/postcss-relative-color-syntax': 4.0.6(postcss@8.5.19) + '@csstools/postcss-scope-pseudo-class': 5.0.0(postcss@8.5.19) + '@csstools/postcss-sign-functions': 2.0.3(postcss@8.5.19) + '@csstools/postcss-stepped-value-functions': 5.0.3(postcss@8.5.19) + '@csstools/postcss-syntax-descriptor-syntax-production': 2.0.0(postcss@8.5.19) + '@csstools/postcss-system-ui-font-family': 2.0.0(postcss@8.5.19) + '@csstools/postcss-text-decoration-shorthand': 5.0.4(postcss@8.5.19) + '@csstools/postcss-trigonometric-functions': 5.0.3(postcss@8.5.19) + '@csstools/postcss-unset-value': 5.0.0(postcss@8.5.19) + autoprefixer: 10.5.2(postcss@8.5.19) + browserslist: 4.28.6 + css-blank-pseudo: 8.0.1(postcss@8.5.19) + css-has-pseudo: 8.0.0(postcss@8.5.19) + css-prefers-color-scheme: 11.0.0(postcss@8.5.19) + cssdb: 8.9.0 + postcss: 8.5.19 + postcss-attribute-case-insensitive: 8.0.0(postcss@8.5.19) + postcss-clamp: 4.1.0(postcss@8.5.19) + postcss-color-functional-notation: 8.0.6(postcss@8.5.19) + postcss-color-hex-alpha: 11.0.0(postcss@8.5.19) + postcss-color-rebeccapurple: 11.0.0(postcss@8.5.19) + postcss-custom-media: 12.0.1(postcss@8.5.19) + postcss-custom-properties: 15.0.1(postcss@8.5.19) + postcss-custom-selectors: 9.0.1(postcss@8.5.19) + postcss-dir-pseudo-class: 10.0.0(postcss@8.5.19) + postcss-double-position-gradients: 7.0.2(postcss@8.5.19) + postcss-focus-visible: 11.0.0(postcss@8.5.19) + postcss-focus-within: 10.0.0(postcss@8.5.19) + postcss-font-variant: 5.0.0(postcss@8.5.19) + postcss-gap-properties: 7.0.0(postcss@8.5.19) + postcss-image-set-function: 8.0.0(postcss@8.5.19) + postcss-lab-function: 8.0.6(postcss@8.5.19) + postcss-logical: 9.0.0(postcss@8.5.19) + postcss-nesting: 14.0.0(postcss@8.5.19) + postcss-opacity-percentage: 3.0.0(postcss@8.5.19) + postcss-overflow-shorthand: 7.0.0(postcss@8.5.19) + postcss-page-break: 3.0.4(postcss@8.5.19) + postcss-place: 11.0.0(postcss@8.5.19) + postcss-pseudo-class-any-link: 11.0.0(postcss@8.5.19) + postcss-replace-overflow-wrap: 4.0.0(postcss@8.5.19) + postcss-selector-not: 9.0.0(postcss@8.5.19) + + postcss-pseudo-class-any-link@11.0.0(postcss@8.5.19): + dependencies: + postcss: 8.5.19 + postcss-selector-parser: 7.1.4 + + postcss-replace-overflow-wrap@4.0.0(postcss@8.5.19): + dependencies: + postcss: 8.5.19 + + postcss-safe-parser@7.0.1(postcss@8.5.19): + dependencies: + postcss: 8.5.19 + + postcss-selector-not@9.0.0(postcss@8.5.19): + dependencies: + postcss: 8.5.19 + postcss-selector-parser: 7.1.4 + + postcss-selector-parser@7.1.4: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss-sorting@9.1.0(postcss@8.5.19): + dependencies: + postcss: 8.5.19 + + postcss-value-parser@4.2.0: {} + + postcss@8.5.19: + dependencies: + nanoid: 3.3.16 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + prelude-ls@1.2.1: {} + + prettier@3.9.5: {} + + prismjs@1.30.0: {} + + prompts@2.4.2: + dependencies: + kleur: 3.0.3 + sisteransi: 1.0.5 + + property-information@7.2.0: {} + + punycode@2.3.1: {} + + qified@0.10.1: + dependencies: + hookified: 2.2.0 + + queue-microtask@1.2.3: {} + + radix3@1.1.2: {} + + react-dom@18.3.1(react@18.3.1): + dependencies: + loose-envify: 1.4.0 + react: 18.3.1 + scheduler: 0.23.2 + + react-leaflet@4.2.1(leaflet@1.9.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@react-leaflet/core': 2.1.0(leaflet@1.9.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + leaflet: 1.9.4 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + react-refresh@0.17.0: {} + + react@18.3.1: + dependencies: + loose-envify: 1.4.0 + + readdirp@4.1.2: {} + + readdirp@5.0.0: {} + + regex-recursion@6.0.2: + dependencies: + regex-utilities: 2.3.0 + + regex-utilities@2.3.0: {} + + regex@6.1.0: + dependencies: + regex-utilities: 2.3.0 + + rehype-parse@9.0.1: + dependencies: + '@types/hast': 3.0.5 + hast-util-from-html: 2.0.3 + unified: 11.0.5 + + rehype-raw@7.0.0: + dependencies: + '@types/hast': 3.0.5 + hast-util-raw: 9.1.0 + vfile: 6.0.3 + + rehype-stringify@10.0.1: + dependencies: + '@types/hast': 3.0.5 + hast-util-to-html: 9.0.5 + unified: 11.0.5 + + rehype@13.0.2: + dependencies: + '@types/hast': 3.0.5 + rehype-parse: 9.0.1 + rehype-stringify: 10.0.1 + unified: 11.0.5 + + remark-gfm@4.0.1: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-gfm: 3.1.0 + micromark-extension-gfm: 3.0.0 + remark-parse: 11.0.0 + remark-stringify: 11.0.0 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-parse@11.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-from-markdown: 2.0.3 + micromark-util-types: 2.0.2 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-rehype@11.1.2: + dependencies: + '@types/hast': 3.0.5 + '@types/mdast': 4.0.4 + mdast-util-to-hast: 13.2.1 + unified: 11.0.5 + vfile: 6.0.3 + + remark-smartypants@3.0.2: + dependencies: + retext: 9.0.0 + retext-smartypants: 6.2.0 + unified: 11.0.5 + unist-util-visit: 5.1.0 + + remark-stringify@11.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-to-markdown: 2.1.2 + unified: 11.0.5 + + remedial@1.0.8: {} + + remove-trailing-separator@1.1.0: {} + + remove-trailing-spaces@1.0.9: {} + + request-light@0.5.8: {} + + request-light@0.7.0: {} + + require-directory@2.1.1: {} + + require-from-string@2.0.2: {} + + resolve-from@4.0.0: {} + + resolve-from@5.0.0: {} + + resolve-pkg-maps@1.0.0: {} + + restore-cursor@5.1.0: + dependencies: + onetime: 7.0.0 + signal-exit: 4.1.0 + + retext-latin@4.0.0: + dependencies: + '@types/nlcst': 2.0.3 + parse-latin: 7.0.0 + unified: 11.0.5 + + retext-smartypants@6.2.0: + dependencies: + '@types/nlcst': 2.0.3 + nlcst-to-string: 4.0.0 + unist-util-visit: 5.1.0 + + retext-stringify@4.0.0: + dependencies: + '@types/nlcst': 2.0.3 + nlcst-to-string: 4.0.0 + unified: 11.0.5 + + retext@9.0.0: + dependencies: + '@types/nlcst': 2.0.3 + retext-latin: 4.0.0 + retext-stringify: 4.0.0 + unified: 11.0.5 + + reusify@1.1.0: {} + + rfdc@1.4.1: {} + + rollup@4.62.2: + dependencies: + '@types/estree': 1.0.9 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.62.2 + '@rollup/rollup-android-arm64': 4.62.2 + '@rollup/rollup-darwin-arm64': 4.62.2 + '@rollup/rollup-darwin-x64': 4.62.2 + '@rollup/rollup-freebsd-arm64': 4.62.2 + '@rollup/rollup-freebsd-x64': 4.62.2 + '@rollup/rollup-linux-arm-gnueabihf': 4.62.2 + '@rollup/rollup-linux-arm-musleabihf': 4.62.2 + '@rollup/rollup-linux-arm64-gnu': 4.62.2 + '@rollup/rollup-linux-arm64-musl': 4.62.2 + '@rollup/rollup-linux-loong64-gnu': 4.62.2 + '@rollup/rollup-linux-loong64-musl': 4.62.2 + '@rollup/rollup-linux-ppc64-gnu': 4.62.2 + '@rollup/rollup-linux-ppc64-musl': 4.62.2 + '@rollup/rollup-linux-riscv64-gnu': 4.62.2 + '@rollup/rollup-linux-riscv64-musl': 4.62.2 + '@rollup/rollup-linux-s390x-gnu': 4.62.2 + '@rollup/rollup-linux-x64-gnu': 4.62.2 + '@rollup/rollup-linux-x64-musl': 4.62.2 + '@rollup/rollup-openbsd-x64': 4.62.2 + '@rollup/rollup-openharmony-arm64': 4.62.2 + '@rollup/rollup-win32-arm64-msvc': 4.62.2 + '@rollup/rollup-win32-ia32-msvc': 4.62.2 + '@rollup/rollup-win32-x64-gnu': 4.62.2 + '@rollup/rollup-win32-x64-msvc': 4.62.2 + fsevents: 2.3.3 + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + safer-buffer@2.1.2: {} + + sanitize.css@13.0.0: {} + + sax@1.6.0: {} + + scheduler@0.23.2: + dependencies: + loose-envify: 1.4.0 + + semver@6.3.1: {} + + semver@7.8.5: {} + + sharp@0.33.5: + dependencies: + color: 4.2.3 + detect-libc: 2.1.2 + semver: 7.8.5 + optionalDependencies: + '@img/sharp-darwin-arm64': 0.33.5 + '@img/sharp-darwin-x64': 0.33.5 + '@img/sharp-libvips-darwin-arm64': 1.0.4 + '@img/sharp-libvips-darwin-x64': 1.0.4 + '@img/sharp-libvips-linux-arm': 1.0.5 + '@img/sharp-libvips-linux-arm64': 1.0.4 + '@img/sharp-libvips-linux-s390x': 1.0.4 + '@img/sharp-libvips-linux-x64': 1.0.4 + '@img/sharp-libvips-linuxmusl-arm64': 1.0.4 + '@img/sharp-libvips-linuxmusl-x64': 1.0.4 + '@img/sharp-linux-arm': 0.33.5 + '@img/sharp-linux-arm64': 0.33.5 + '@img/sharp-linux-s390x': 0.33.5 + '@img/sharp-linux-x64': 0.33.5 + '@img/sharp-linuxmusl-arm64': 0.33.5 + '@img/sharp-linuxmusl-x64': 0.33.5 + '@img/sharp-wasm32': 0.33.5 + '@img/sharp-win32-ia32': 0.33.5 + '@img/sharp-win32-x64': 0.33.5 + + sharp@0.34.5: + dependencies: + '@img/colour': 1.1.0 + detect-libc: 2.1.2 + semver: 7.8.5 + optionalDependencies: + '@img/sharp-darwin-arm64': 0.34.5 + '@img/sharp-darwin-x64': 0.34.5 + '@img/sharp-libvips-darwin-arm64': 1.2.4 + '@img/sharp-libvips-darwin-x64': 1.2.4 + '@img/sharp-libvips-linux-arm': 1.2.4 + '@img/sharp-libvips-linux-arm64': 1.2.4 + '@img/sharp-libvips-linux-ppc64': 1.2.4 + '@img/sharp-libvips-linux-riscv64': 1.2.4 + '@img/sharp-libvips-linux-s390x': 1.2.4 + '@img/sharp-libvips-linux-x64': 1.2.4 + '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 + '@img/sharp-libvips-linuxmusl-x64': 1.2.4 + '@img/sharp-linux-arm': 0.34.5 + '@img/sharp-linux-arm64': 0.34.5 + '@img/sharp-linux-ppc64': 0.34.5 + '@img/sharp-linux-riscv64': 0.34.5 + '@img/sharp-linux-s390x': 0.34.5 + '@img/sharp-linux-x64': 0.34.5 + '@img/sharp-linuxmusl-arm64': 0.34.5 + '@img/sharp-linuxmusl-x64': 0.34.5 + '@img/sharp-wasm32': 0.34.5 + '@img/sharp-win32-arm64': 0.34.5 + '@img/sharp-win32-ia32': 0.34.5 + '@img/sharp-win32-x64': 0.34.5 + optional: true + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + shell-quote@1.10.0: {} + + shiki@3.23.0: + dependencies: + '@shikijs/core': 3.23.0 + '@shikijs/engine-javascript': 3.23.0 + '@shikijs/engine-oniguruma': 3.23.0 + '@shikijs/langs': 3.23.0 + '@shikijs/themes': 3.23.0 + '@shikijs/types': 3.23.0 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.5 + + signal-exit@4.1.0: {} + + simple-swizzle@0.2.4: + dependencies: + is-arrayish: 0.3.4 + + sisteransi@1.0.5: {} + + sitemap@9.0.1: + dependencies: + '@types/node': 24.13.3 + '@types/sax': 1.2.7 + arg: 5.0.2 + sax: 1.6.0 + + slash@3.0.0: {} + + slash@5.1.0: {} + + slice-ansi@4.0.0: + dependencies: + ansi-styles: 4.3.0 + astral-regex: 2.0.0 + is-fullwidth-code-point: 3.0.0 + + slice-ansi@7.1.2: + dependencies: + ansi-styles: 6.2.3 + is-fullwidth-code-point: 5.1.0 + + slice-ansi@8.0.0: + dependencies: + ansi-styles: 6.2.3 + is-fullwidth-code-point: 5.1.0 + + smol-toml@1.7.0: {} + + source-map-js@1.2.1: {} + + space-separated-tokens@2.0.2: {} + + sponge-case@2.0.3: {} + + stream-replace-string@2.0.0: {} + + string-env-interpolation@1.0.1: {} + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string-width@7.2.0: + dependencies: + emoji-regex: 10.6.0 + get-east-asian-width: 1.6.0 + strip-ansi: 7.2.0 + + string-width@8.2.2: + dependencies: + get-east-asian-width: 1.6.0 + strip-ansi: 7.2.0 + + stringify-entities@4.0.4: + dependencies: + character-entities-html4: 2.1.0 + character-entities-legacy: 3.0.0 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-ansi@7.2.0: + dependencies: + ansi-regex: 6.2.2 + + strip-json-comments@5.0.3: {} + + style-search@0.1.0: {} + + stylelint-config-concentric-order@5.5.0(stylelint@17.14.0(typescript@5.9.3)): + dependencies: + stylelint-order: 7.0.1(stylelint@17.14.0(typescript@5.9.3)) + transitivePeerDependencies: + - stylelint + + stylelint-config-recommended@18.0.0(stylelint@17.14.0(typescript@5.9.3)): + dependencies: + stylelint: 17.14.0(typescript@5.9.3) + + stylelint-order@7.0.1(stylelint@17.14.0(typescript@5.9.3)): + dependencies: + postcss: 8.5.19 + postcss-sorting: 9.1.0(postcss@8.5.19) + stylelint: 17.14.0(typescript@5.9.3) + + stylelint@17.14.0(typescript@5.9.3): + dependencies: + '@csstools/css-calc': 3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-syntax-patches-for-csstree': 1.1.6(css-tree@3.2.1) + '@csstools/css-tokenizer': 4.0.0 + '@csstools/media-query-list-parser': 5.0.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/selector-resolve-nested': 4.0.0(postcss-selector-parser@7.1.4) + '@csstools/selector-specificity': 6.0.0(postcss-selector-parser@7.1.4) + colord: 2.9.3 + cosmiconfig: 9.0.2(typescript@5.9.3) + css-functions-list: 3.3.3 + css-tree: 3.2.1 + debug: 4.4.3 + fast-glob: 3.3.3 + fastest-levenshtein: 1.0.16 + file-entry-cache: 11.1.5 + global-modules: 2.0.0 + globby: 16.2.1 + globjoin: 0.1.4 + html-tags: 5.1.0 + ignore: 7.0.6 + import-meta-resolve: 4.2.0 + mathml-tag-names: 4.0.0 + meow: 14.1.0 + micromatch: 4.0.8 + normalize-path: 3.0.0 + picocolors: 1.1.1 + postcss: 8.5.19 + postcss-safe-parser: 7.0.1(postcss@8.5.19) + postcss-selector-parser: 7.1.4 + postcss-value-parser: 4.2.0 + string-width: 8.2.2 + supports-hyperlinks: 4.5.0 + svg-tags: 1.0.0 + table: 6.9.0 + write-file-atomic: 7.0.1 + transitivePeerDependencies: + - supports-color + - typescript + + supports-color@10.2.2: {} + + supports-hyperlinks@4.5.0: + dependencies: + has-flag: 5.0.1 + supports-color: 10.2.2 + + svg-tags@1.0.0: {} + + svgo@4.0.2: + dependencies: + commander: 11.1.0 + css-select: 5.2.2 + css-tree: 3.2.1 + css-what: 6.2.2 + csso: 5.0.5 + picocolors: 1.1.1 + sax: 1.6.0 + + swap-case@3.0.3: {} + + sync-fetch@0.6.0: + dependencies: + node-fetch: 3.3.2 + timeout-signal: 2.0.0 + whatwg-mimetype: 4.0.0 + + synckit@0.11.13: + dependencies: + '@pkgr/core': 0.3.6 + + table@6.9.0: + dependencies: + ajv: 8.20.0 + lodash.truncate: 4.4.2 + slice-ansi: 4.0.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + timeout-signal@2.0.0: {} + + tiny-inflate@1.0.3: {} + + tinyexec@1.2.4: {} + + tinyglobby@0.2.17: + dependencies: + fdir: 6.5.0(picomatch@4.0.5) + picomatch: 4.0.5 + + title-case@3.0.3: + dependencies: + tslib: 2.8.1 + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + tr46@0.0.3: {} + + trim-lines@3.0.1: {} + + trough@2.2.0: {} + + ts-api-utils@2.5.0(typescript@5.9.3): + dependencies: + typescript: 5.9.3 + + ts-log@3.0.2: {} + + tsconfck@3.1.6(typescript@5.9.3): + optionalDependencies: + typescript: 5.9.3 + + tslib@2.8.1: {} + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + type-fest@4.41.0: {} + + typesafe-path@0.2.2: {} + + typescript-auto-import-cache@0.3.6: + dependencies: + semver: 7.8.5 + + typescript-eslint@8.63.0(eslint@10.7.0(jiti@2.7.0))(typescript@5.9.3): + dependencies: + '@typescript-eslint/eslint-plugin': 8.63.0(@typescript-eslint/parser@8.63.0(eslint@10.7.0(jiti@2.7.0))(typescript@5.9.3))(eslint@10.7.0(jiti@2.7.0))(typescript@5.9.3) + '@typescript-eslint/parser': 8.63.0(eslint@10.7.0(jiti@2.7.0))(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.63.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.63.0(eslint@10.7.0(jiti@2.7.0))(typescript@5.9.3) + eslint: 10.7.0(jiti@2.7.0) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + typescript@5.9.3: {} + + ufo@1.6.4: {} + + ultrahtml@1.7.0: {} + + unbash@4.0.2: {} + + unc-path-regex@0.1.2: {} + + uncrypto@0.1.3: {} + + undici-types@7.18.2: {} + + undici-types@8.3.0: {} + + unicorn-magic@0.4.0: {} + + unified@11.0.5: + dependencies: + '@types/unist': 3.0.3 + bail: 2.0.2 + devlop: 1.1.0 + extend: 3.0.2 + is-plain-obj: 4.1.0 + trough: 2.2.0 + vfile: 6.0.3 + + unifont@0.7.4: + dependencies: + css-tree: 3.2.1 + ofetch: 1.5.1 + ohash: 2.0.11 + + unist-util-find-after@5.0.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + + unist-util-is@6.0.1: + dependencies: + '@types/unist': 3.0.3 + + unist-util-modify-children@4.0.0: + dependencies: + '@types/unist': 3.0.3 + array-iterate: 2.0.1 + + unist-util-position@5.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-remove-position@5.0.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-visit: 5.1.0 + + unist-util-stringify-position@4.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-visit-children@3.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-visit-parents@6.0.2: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + + unist-util-visit@5.1.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + unist-util-visit-parents: 6.0.2 + + unixify@1.0.0: + dependencies: + normalize-path: 2.1.1 + + unstorage@1.17.5: + dependencies: + anymatch: 3.1.3 + chokidar: 5.0.0 + destr: 2.0.5 + h3: 1.15.11 + lru-cache: 11.5.2 + node-fetch-native: 1.6.7 + ofetch: 1.5.1 + ufo: 1.6.4 + + update-browserslist-db@1.2.3(browserslist@4.28.6): + dependencies: + browserslist: 4.28.6 + escalade: 3.2.0 + picocolors: 1.1.1 + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + urlpattern-polyfill@10.1.0: {} + + util-deprecate@1.0.2: {} + + vfile-location@5.0.3: + dependencies: + '@types/unist': 3.0.3 + vfile: 6.0.3 + + vfile-message@4.0.3: + dependencies: + '@types/unist': 3.0.3 + unist-util-stringify-position: 4.0.0 + + vfile@6.0.3: + dependencies: + '@types/unist': 3.0.3 + vfile-message: 4.0.3 + + vite@6.4.3(@types/node@26.1.1)(jiti@2.7.0)(yaml@2.9.0): + dependencies: + esbuild: 0.25.12 + fdir: 6.5.0(picomatch@4.0.5) + picomatch: 4.0.5 + postcss: 8.5.19 + rollup: 4.62.2 + tinyglobby: 0.2.17 + optionalDependencies: + '@types/node': 26.1.1 + fsevents: 2.3.3 + jiti: 2.7.0 + yaml: 2.9.0 + + vitefu@1.1.3(vite@6.4.3(@types/node@26.1.1)(jiti@2.7.0)(yaml@2.9.0)): + optionalDependencies: + vite: 6.4.3(@types/node@26.1.1)(jiti@2.7.0)(yaml@2.9.0) + + volar-service-css@0.0.71(@volar/language-service@2.4.28): + dependencies: + vscode-css-languageservice: 6.3.10 + vscode-languageserver-textdocument: 1.0.12 + vscode-uri: 3.1.0 + optionalDependencies: + '@volar/language-service': 2.4.28 + + volar-service-emmet@0.0.71(@volar/language-service@2.4.28): + dependencies: + '@emmetio/css-parser': 0.4.1 + '@emmetio/html-matcher': 1.3.0 + '@vscode/emmet-helper': 2.11.0 + vscode-uri: 3.1.0 + optionalDependencies: + '@volar/language-service': 2.4.28 + + volar-service-html@0.0.71(@volar/language-service@2.4.28): + dependencies: + vscode-html-languageservice: 5.6.2 + vscode-languageserver-textdocument: 1.0.12 + vscode-uri: 3.1.0 + optionalDependencies: + '@volar/language-service': 2.4.28 + + volar-service-prettier@0.0.71(@volar/language-service@2.4.28)(prettier@3.9.5): + dependencies: + vscode-uri: 3.1.0 + optionalDependencies: + '@volar/language-service': 2.4.28 + prettier: 3.9.5 + + volar-service-typescript-twoslash-queries@0.0.71(@volar/language-service@2.4.28): + dependencies: + vscode-uri: 3.1.0 + optionalDependencies: + '@volar/language-service': 2.4.28 + + volar-service-typescript@0.0.71(@volar/language-service@2.4.28): + dependencies: + path-browserify: 1.0.1 + semver: 7.8.5 + typescript-auto-import-cache: 0.3.6 + vscode-languageserver-textdocument: 1.0.12 + vscode-nls: 5.2.0 + vscode-uri: 3.1.0 + optionalDependencies: + '@volar/language-service': 2.4.28 + + volar-service-yaml@0.0.71(@volar/language-service@2.4.28): + dependencies: + vscode-uri: 3.1.0 + yaml-language-server: 1.23.0 + optionalDependencies: + '@volar/language-service': 2.4.28 + + vscode-css-languageservice@6.3.10: + dependencies: + '@vscode/l10n': 0.0.18 + vscode-languageserver-textdocument: 1.0.12 + vscode-languageserver-types: 3.17.5 + vscode-uri: 3.1.0 + + vscode-html-languageservice@5.6.2: + dependencies: + '@vscode/l10n': 0.0.18 + vscode-languageserver-textdocument: 1.0.12 + vscode-languageserver-types: 3.18.0 + vscode-uri: 3.1.0 + + vscode-json-languageservice@4.1.8: + dependencies: + jsonc-parser: 3.3.1 + vscode-languageserver-textdocument: 1.0.12 + vscode-languageserver-types: 3.18.0 + vscode-nls: 5.2.0 + vscode-uri: 3.1.0 + + vscode-jsonrpc@8.2.0: {} + + vscode-jsonrpc@9.0.1: {} + + vscode-languageserver-protocol@3.17.5: + dependencies: + vscode-jsonrpc: 8.2.0 + vscode-languageserver-types: 3.17.5 + + vscode-languageserver-protocol@3.18.2: + dependencies: + vscode-jsonrpc: 9.0.1 + vscode-languageserver-types: 3.18.0 + + vscode-languageserver-textdocument@1.0.12: {} + + vscode-languageserver-types@3.17.5: {} + + vscode-languageserver-types@3.18.0: {} + + vscode-languageserver@9.0.1: + dependencies: + vscode-languageserver-protocol: 3.17.5 + + vscode-nls@5.2.0: {} + + vscode-uri@3.1.0: {} + + walk-up-path@4.0.0: {} + + web-namespaces@2.0.1: {} + + web-streams-polyfill@3.3.3: {} + + webidl-conversions@3.0.1: {} + + whatwg-mimetype@4.0.0: {} + + whatwg-url@5.0.0: + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + + which-pm-runs@1.1.0: {} + + which@1.3.1: + dependencies: + isexe: 2.0.0 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + widest-line@5.0.0: + dependencies: + string-width: 7.2.0 + + word-wrap@1.2.5: {} + + wrap-ansi@10.0.0: + dependencies: + ansi-styles: 6.2.3 + string-width: 8.2.2 + strip-ansi: 7.2.0 + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@9.0.2: + dependencies: + ansi-styles: 6.2.3 + string-width: 7.2.0 + strip-ansi: 7.2.0 + + write-file-atomic@7.0.1: + dependencies: + signal-exit: 4.1.0 + + ws@8.21.0: {} + + xxhash-wasm@1.1.0: {} + + y18n@5.0.8: {} + + yallist@3.1.1: {} + + yaml-language-server@1.23.0: + dependencies: + '@vscode/l10n': 0.0.18 + ajv: 8.20.0 + ajv-draft-04: 1.0.0(ajv@8.20.0) + ajv-i18n: 4.2.0(ajv@8.20.0) + prettier: 3.9.5 + request-light: 0.5.8 + vscode-json-languageservice: 4.1.8 + vscode-languageserver: 9.0.1 + vscode-languageserver-textdocument: 1.0.12 + vscode-languageserver-types: 3.18.0 + vscode-uri: 3.1.0 + yaml: 2.8.3 + + yaml@2.8.3: {} + + yaml@2.9.0: {} + + yargs-parser@21.1.1: {} + + yargs-parser@22.0.0: {} + + yargs@17.7.3: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + + yargs@18.0.0: + dependencies: + cliui: 9.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + string-width: 7.2.0 + y18n: 5.0.8 + yargs-parser: 22.0.0 + + yocto-queue@0.1.0: {} + + yocto-queue@1.2.2: {} + + yocto-spinner@0.2.3: + dependencies: + yoctocolors: 2.1.2 + + yoctocolors@2.1.2: {} + + zod-to-json-schema@3.25.2(zod@3.25.76): + dependencies: + zod: 3.25.76 + + zod-to-ts@1.2.0(typescript@5.9.3)(zod@3.25.76): + dependencies: + typescript: 5.9.3 + zod: 3.25.76 + + zod-validation-error@4.0.2(zod@4.4.3): + dependencies: + zod: 4.4.3 + + zod@3.25.76: {} + + zod@4.4.3: {} + + zwitch@2.0.4: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 00000000..804c7810 --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,4 @@ +# Settings only — no `packages:` key, so this does NOT define a workspace +# (pnpm 10 reads config from this file; packages/ are imported by path). +onlyBuiltDependencies: + - sharp diff --git a/postcss.config.cjs b/postcss.config.cjs new file mode 100644 index 00000000..04f362ac --- /dev/null +++ b/postcss.config.cjs @@ -0,0 +1,24 @@ +// Base config copied from https://nextjs.org/docs/advanced-features/customizing-postcss-config#customizing-plugins +// NOTE: restored from the Next app's postcss.config.js. Two mechanical +// adaptations for the Astro/Vite toolchain, semantics unchanged: +// - .cjs extension (package.json is "type": "module"; module.exports needs CJS) +// - plugins as an object map instead of Next's string-array format: Vite's +// bundled postcss-load-config only resolves plugin *names* in the object +// form (array entries must already be plugin functions). Order and options +// are identical to the original. +module.exports = { + plugins: { + 'postcss-flexbugs-fixes': {}, + 'postcss-nested': {}, + 'postcss-normalize': {}, + 'postcss-preset-env': { + autoprefixer: { + flexbox: 'no-2009', + }, + stage: 3, + features: { + 'custom-properties': false, + }, + }, + }, +} diff --git a/postcss.config.js b/postcss.config.js deleted file mode 100644 index 32956a95..00000000 --- a/postcss.config.js +++ /dev/null @@ -1,20 +0,0 @@ -// Base config copied from https://nextjs.org/docs/advanced-features/customizing-postcss-config#customizing-plugins -module.exports = { - plugins: [ - 'postcss-flexbugs-fixes', - 'postcss-nested', - 'postcss-normalize', - [ - 'postcss-preset-env', - { - autoprefixer: { - flexbox: 'no-2009', - }, - stage: 3, - features: { - 'custom-properties': false, - }, - }, - ], - ], -} diff --git a/redirects/about.html b/public/en/about.html similarity index 100% rename from redirects/about.html rename to public/en/about.html diff --git a/redirects/data.html b/public/en/data.html similarity index 100% rename from redirects/data.html rename to public/en/data.html diff --git a/redirects/get-involved.html b/public/en/get-involved.html similarity index 100% rename from redirects/get-involved.html rename to public/en/get-involved.html diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 00000000..1eaa47f4 Binary files /dev/null and b/public/favicon.ico differ diff --git a/public/icons.svg b/public/icons.svg new file mode 100644 index 00000000..5c3cbc3a --- /dev/null +++ b/public/icons.svg @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/locales/cs/common_old.json b/public/locales/cs/common_old.json deleted file mode 100644 index 0967ef42..00000000 --- a/public/locales/cs/common_old.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/public/locales/cs/data_old.json b/public/locales/cs/data_old.json deleted file mode 100644 index cfca417d..00000000 --- a/public/locales/cs/data_old.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "active-projects": "Aktivní", - "finished-projects": "Dokončeno", - "type-street-title": "", - "type-check-completeness-title": "", - "build-area": "Najít", - "footprint": "Ověření", - "change-detection": "Porovnání", - "completeness": "", - "validate-image": "", - "street": "", - "published": "", - "withdrawn": "", - "download-projects-with-geometry-heading": "Projekty s geometrií", - "type-locate-view-title": "" -} diff --git a/public/locales/cs/project_old.json b/public/locales/cs/project_old.json deleted file mode 100644 index 7bcfb2fc..00000000 --- a/public/locales/cs/project_old.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "users-title": "Uživatelé", - "users-description": "Tento datový soubor obsahuje informace o jednotlivých příspěvcích jednotlivých uživatelů. Dozvíte se tak například o nejaktivnějších uživatelích tohoto projektu. (Všimněte si, že před použitím tohoto souboru .gz jej musíte rozbalit.)", - "date": "{{date, datetime}}", - "active-projects": "Aktivní", - "finished-projects": "Dokončeno", - "build-area": "Hledat", - "footprint": "Validovat", - "change-detection": "Porovnat", - "completeness": "", - "validate-image": "", - "street": "", - "published": "", - "withdrawn": "" -} diff --git a/public/locales/de/common_old.json b/public/locales/de/common_old.json deleted file mode 100644 index 0967ef42..00000000 --- a/public/locales/de/common_old.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/public/locales/de/data_old.json b/public/locales/de/data_old.json deleted file mode 100644 index 6f095d22..00000000 --- a/public/locales/de/data_old.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "active-projects": "Aktiv", - "finished-projects": "Erledigt", - "type-street-title": "", - "type-check-completeness-title": "", - "build-area": "Finden", - "footprint": "Bestätigen", - "change-detection": "Vergleichen", - "completeness": "", - "validate-image": "", - "street": "", - "published": "", - "withdrawn": "", - "download-projects-with-geometry-heading": "Projekte mit Geometrie", - "type-locate-view-title": "" -} diff --git a/public/locales/de/project_old.json b/public/locales/de/project_old.json deleted file mode 100644 index 0dcf6189..00000000 --- a/public/locales/de/project_old.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "users-title": "Benutzer", - "users-description": "Dieser Datensatz enthält Informationen zu den einzelnen Beiträgen pro Benutzer. Dadurch erfahren Sie beispielsweise die aktivsten Benutzer dieses Projekts. (Beachte das du die .gz Dateien erst entpacken musst bevor du sie verwenden kannst.)", - "date": "{{date, datetime}}", - "active-projects": "Aktiv", - "finished-projects": "Beendet", - "build-area": "Finden", - "footprint": "Bestätigen", - "change-detection": "Vergleichen", - "completeness": "", - "validate-image": "", - "street": "", - "published": "", - "withdrawn": "" -} diff --git a/public/locales/en/common_old.json b/public/locales/en/common_old.json deleted file mode 100644 index 0967ef42..00000000 --- a/public/locales/en/common_old.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/public/locales/en/data_old.json b/public/locales/en/data_old.json deleted file mode 100644 index 27115f14..00000000 --- a/public/locales/en/data_old.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "ready-to-process": "Ready to Process", - "draft": "Draft", - "discarded": "Discarded", - "processing-failed": "Proccessing Failed", - "processed": "Processed", - "ready-to-published": "Ready to Publish", - "publishing-failed": "Publishing Failed", - "paused": "Paused", - "download-projects-with-geometry-heading": "Projects with Geometry", - "type-locate-view-title": "" -} diff --git a/public/locales/en/home_old.json b/public/locales/en/home_old.json deleted file mode 100644 index e69de29b..00000000 diff --git a/public/locales/en/project_old.json b/public/locales/en/project_old.json deleted file mode 100644 index 0985b55d..00000000 --- a/public/locales/en/project_old.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "users-title": "Users", - "users-description": "This dataset contains information on the individual contributions per user. This tells you for instance the most active users of this project. (Note that you need to unzip this .gz file before you can use it.)", - "date": "{{date, datetime}}", - "active-projects": "Active", - "finished-projects": "Finished", - "build-area": "Find Features", - "footprint": "Validate Footprints", - "change-detection": "Compare Dates", - "completeness": "Check Completeness", - "validate-image": "Access Images", - "street": "View Streets", - "published": "Published", - "withdrawn": "Withdrawn" -} diff --git a/public/locales/hu/common_old.json b/public/locales/hu/common_old.json deleted file mode 100644 index cec013f7..00000000 --- a/public/locales/hu/common_old.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "twitter": "" -} diff --git a/public/locales/hu/data_old.json b/public/locales/hu/data_old.json deleted file mode 100644 index 49343905..00000000 --- a/public/locales/hu/data_old.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "active-projects": "Aktív", - "finished-projects": "Befejezett", - "type-street-title": "", - "type-check-completeness-title": "", - "build-area": "Keresés", - "footprint": "Érvényesítés", - "change-detection": "Összehasonlítás", - "completeness": "", - "validate-image": "", - "street": "", - "published": "", - "withdrawn": "", - "download-projects-with-geometry-heading": "Projektek alakzatfájllal", - "type-locate-view-title": "" -} diff --git a/public/locales/hu/project_old.json b/public/locales/hu/project_old.json deleted file mode 100644 index 25915ef7..00000000 --- a/public/locales/hu/project_old.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "users-title": "Felhasználók", - "users-description": "Ez az adatkészlet az egyes felhasználók egyedi közreműködéseit tartalmazza. Ebből lehet felismerni például a projekt legaktívabb felhasználóit. (Ne feledje, hogy használat előtt ki kell tömörítenie a .gz fájlt.)", - "date": "{{date, datetime}}", - "active-projects": "Aktív", - "finished-projects": "Befejezett", - "build-area": "Keresés", - "footprint": "Érvényesítés", - "change-detection": "Összehasonlítás", - "completeness": "", - "validate-image": "", - "street": "", - "published": "", - "withdrawn": "" -} diff --git a/public/locales/ne/common_old.json b/public/locales/ne/common_old.json deleted file mode 100644 index 3fcb18d5..00000000 --- a/public/locales/ne/common_old.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "twitter": "Twitter" -} diff --git a/public/locales/ne/data_old.json b/public/locales/ne/data_old.json deleted file mode 100644 index ece19d5f..00000000 --- a/public/locales/ne/data_old.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "active-projects": "सक्रिय", - "finished-projects": "समाप्त", - "type-street-title": "", - "type-check-completeness-title": "", - "build-area": "खोजी", - "footprint": "प्रमाणीकरण", - "change-detection": "तुलना", - "completeness": "", - "validate-image": "", - "street": "", - "published": "", - "withdrawn": "", - "download-projects-with-geometry-heading": "ज्यामिति का साथ परियोजनाहरू", - "type-locate-view-title": "" -} diff --git a/public/locales/ne/project_old.json b/public/locales/ne/project_old.json deleted file mode 100644 index 54372e94..00000000 --- a/public/locales/ne/project_old.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "users-title": "प्रयोगकर्ताहरू", - "users-description": "यस डेटासेटमा प्रति प्रयोगकर्ताको व्यक्तिगत योगदानहरूको जानकारी समावेश गरिएको छ। उदाहरणका लागि यसले तपाईंलाई यस परियोजनाको सबैभन्दा सक्रिय प्रयोगकर्ताहरूको बारेमा बताउँछ। (ध्यान दिनुहोस् कि तपाईंले यो .gz फाइललाई प्रयोग गर्न अघि अनजिप गर्न आवश्यक छ।)", - "date": "{{date, datetime}}", - "active-projects": "सक्रिय", - "finished-projects": "समाप्त", - "build-area": "खोजी ", - "footprint": "प्रमाणीकरण", - "change-detection": "तुलना", - "completeness": "", - "validate-image": "", - "street": "", - "published": "", - "withdrawn": "" -} diff --git a/public/locales/pt/data_old.json b/public/locales/pt/data_old.json deleted file mode 100644 index f0de5e62..00000000 --- a/public/locales/pt/data_old.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "active-projects": "Ativo", - "finished-projects": "Terminado", - "type-street-title": "", - "type-check-completeness-title": "", - "build-area": "Encontrar", - "footprint": "Validar", - "change-detection": "Comparar", - "completeness": "", - "validate-image": "", - "street": "", - "published": "", - "withdrawn": "", - "download-projects-with-geometry-heading": "Projetos com geometria", - "type-locate-view-title": "" -} diff --git a/public/locales/pt/project_old.json b/public/locales/pt/project_old.json deleted file mode 100644 index 922c5b54..00000000 --- a/public/locales/pt/project_old.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "users-title": "Utilizadores", - "users-description": "Este conjunto de dados contém informações sobre as contribuições individuais por utilizador. Isto indica-lhe, por exemplo, os utilizadores mais ativos deste projeto. (Note que é necessário descompactar este ficheiro .gz antes de o poder utilizar).", - "date": "{{date, datetime}}", - "active-projects": "Ativo", - "finished-projects": "Terminado", - "build-area": "Encontrar", - "footprint": "Validar", - "change-detection": "Comparar", - "completeness": "", - "validate-image": "", - "street": "", - "published": "", - "withdrawn": "" -} diff --git a/public/screenshot.png b/public/screenshot.png deleted file mode 100644 index bbc71c19..00000000 Binary files a/public/screenshot.png and /dev/null differ diff --git a/scripts/fetchData.ts b/scripts/fetchData.ts index c9eb0f1b..c4fe4d7f 100644 --- a/scripts/fetchData.ts +++ b/scripts/fetchData.ts @@ -1,258 +1,222 @@ -import { GraphQLClient, gql } from 'graphql-request'; +// Run with plain `node scripts/fetchData.ts` (Node >= 22 type stripping — the +// type-only import below is erased, so no ts-node/tsc is needed at runtime). +// The GraphQL documents live in scripts/graphql/*.graphql so graphql-codegen +// can parse them (interpolated template literals break its tag plucker); the +// same files are read here at runtime. +import { GraphQLClient } from 'graphql-request'; import fs from 'fs'; import path from 'path'; -import { AllDataQuery } from '../generated/types'; - -const datadir = path.join(__dirname, '../fullData'); +import type { + ProjectScanQuery, + ProjectsByIdsQuery, + PublicProjectsQuery, + RestDataQuery, +} from '../generated/types'; + +const datadir = path.join(import.meta.dirname, '../build/full-data'); +const outputPath = path.join(datadir, 'staticData.json'); const baseUrl = process.env.MAPSWIPE_API_ENDPOINT || 'http://localhost:8000/'; const GRAPHQL_ENDPOINT = `${baseUrl}graphql/`; // TODO: Validate the app environment env to be 'DEV', 'ALPHA-X' or 'PROD' const COOKIE_NAME = `MAPSWIPE-${process.env.APP_ENVIRONMENT}-CSRFTOKEN`; const pipelineType = process.env.PIPELINE_TYPE; +// Full project fetch is paged at 100; the cheap id+modifiedAt scan is paged +// large (tiny rows), so it costs 2-3 requests instead of ~25. +const PROJECTS_PAGE_SIZE = 100; +const SCAN_PAGE_SIZE = 1000; +// Force a full re-fetch (ignore the incremental cache). Use periodically as a +// safety net for changes that don't bump `project.modifiedAt`. +const forceFull = process.env.FORCE_FULL_FETCH === '1' || process.env.FORCE_FULL_FETCH === 'true'; + const graphQLClient = new GraphQLClient(GRAPHQL_ENDPOINT); -const dummyData: AllDataQuery = { - publicProjects: { - results: [], - totalCount: 0, - }, +const gqlDir = path.join(import.meta.dirname, 'graphql'); +const readDoc = (name: string) => fs.readFileSync(path.join(gqlDir, name), 'utf8'); +const projectFieldsFragment = readDoc('projectFields.graphql'); +const scanQuery = readDoc('projectScan.graphql'); +const projectsQuery = `${readDoc('publicProjects.graphql')}\n${projectFieldsFragment}`; +const projectsByIdsQuery = `${readDoc('projectsByIds.graphql')}\n${projectFieldsFragment}`; +const restQuery = readDoc('restData.graphql'); + +// staticData.json's shape: the singular rest-data plus the project list. +type StaticData = RestDataQuery & Pick; +type PublicProjects = NonNullable; +type ProjectResult = NonNullable[number]; + +const dummyData: StaticData = { + publicProjects: { results: [], totalCount: 0 }, communityStats: { - id: '0', - totalContributors: 1, - totalUserGroups: 1, - totalSwipes: 1, - }, - publicOrganizations: { - results: [], + id: '0', totalContributors: 1, totalSwipes: 1, }, + publicOrganizations: { results: [] }, globalExportAssets: [], }; -const query = gql` - query AllData { - publicProjects( - filters: { - status: { - inList: [PUBLISHED, FINISHED], - }, - }, - pagination: { limit: 9999 }, - ) { - results { - id - exportAggregatedResultsWithGeometry { - id - fileSize - file { - name - url - } - mimetype - } - exportAggregatedResults { - id - fileSize - file { - name - url - } - mimetype - } - exportAreaOfInterest { - id - fileSize - file { - name - url - } - mimetype - } - exportGroups { - id - fileSize - file { - name - url - } - mimetype - } - exportHistory { - id - fileSize - file { - name - url - } - mimetype - modifiedAt - } - exportResults { - id - fileSize - file { - name - url - } - mimetype - } - exportTasks { - id - fileSize - file { - name - url - } - mimetype - } - exportUsers { - id - file { - url - name - } - mimetype - fileSize - } - exportHotTaskingManagerGeometries { - id - file { - url - name - } - mimetype - fileSize - } - exportModerateToHighAgreementYesMaybeGeometries { - id - file { - url - name - } - mimetype - fileSize - } - name - firebaseId - image { - id - file { - name - url - } - createdAt - } - description - requestingOrganization { - id - name - modifiedAt - } - progress - status - projectType - createdAt - modifiedAt - region - requestingOrganizationId - numberOfContributorUsers - totalArea - aoiGeometry { - centroid - id - totalArea - bbox - } - aoiGeometryInputAsset { - id - fileSize - file { - name - url - } - mimetype - } - } - totalCount - } - communityStats { - id - totalContributors - totalUserGroups - totalSwipes +async function fetchAllProjects(): Promise { + const results: ProjectResult[] = []; + let offset = 0; + let totalCount = 0; + for (;;) { + const page = (await graphQLClient.request(projectsQuery, { + limit: PROJECTS_PAGE_SIZE, offset, + })) as PublicProjectsQuery; + const pageResults = page.publicProjects?.results ?? []; + totalCount = page.publicProjects?.totalCount ?? totalCount; + results.push(...pageResults); + console.log(` full-fetch projects ${results.length}/${totalCount}`); + offset += PROJECTS_PAGE_SIZE; + if (pageResults.length < PROJECTS_PAGE_SIZE || results.length >= totalCount) { + break; } - publicOrganizations(pagination: { limit: 9999 }) { - results { - id - name - } + } + return { results, totalCount }; +} + +// A project is considered changed when either timestamp differs: `modifiedAt` +// covers project edits; `lastContributionDate` covers new swipes that update +// progress without bumping `modifiedAt`. +type ChangeKeyFields = { modifiedAt?: string | null; lastContributionDate?: string | null }; +function changeSignature(p: ChangeKeyFields): string { + return `${p.modifiedAt ?? ''}|${p.lastContributionDate ?? ''}`; +} + +// Cheap scan → Map of the current published/finished set. +async function scanProjects(): Promise> { + const map = new Map(); + let offset = 0; + let totalCount = 0; + for (;;) { + const page = (await graphQLClient.request(scanQuery, { + limit: SCAN_PAGE_SIZE, offset, + })) as ProjectScanQuery; + const pageResults = page.publicProjects?.results ?? []; + totalCount = page.publicProjects?.totalCount ?? totalCount; + pageResults.forEach((p) => map.set(p.id, changeSignature(p))); + offset += SCAN_PAGE_SIZE; + if (pageResults.length < SCAN_PAGE_SIZE || map.size >= totalCount) { + break; } - globalExportAssets { - type - lastUpdatedAt - fileSize - file { - url - name - } + } + return map; +} + +// Full fetch for a specific set of ids, batched by inList. +async function fetchProjectsByIds(ids: string[]): Promise { + const out: ProjectResult[] = []; + for (let i = 0; i < ids.length; i += PROJECTS_PAGE_SIZE) { + const batch = ids.slice(i, i + PROJECTS_PAGE_SIZE); + const page = (await graphQLClient.request(projectsByIdsQuery, { + ids: batch, limit: PROJECTS_PAGE_SIZE, offset: 0, + })) as ProjectsByIdsQuery; + out.push(...(page.publicProjects?.results ?? [])); + } + return out; +} + +// Load the previous run's output as the incremental cache (id + modifiedAt + +// full data). In CI this file is restored from actions/cache before the run. +function loadPreviousProjects(): Map | null { + try { + const prev = JSON.parse(fs.readFileSync(outputPath, 'utf8')) as StaticData; + const results = prev.publicProjects?.results ?? []; + if (results.length === 0) { + return null; } + return new Map(results.map((p) => [p.id, p])); + } catch { + return null; + } +} + +async function fetchProjectsIncrementally(): Promise { + const prevById = forceFull ? null : loadPreviousProjects(); + if (!prevById) { + console.log(forceFull ? 'FORCE_FULL_FETCH set — full fetch' : 'no usable cache — full fetch'); + return fetchAllProjects(); } -`; + + const scan = await scanProjects(); + const changedIds: string[] = []; + scan.forEach((sig, id) => { + const prev = prevById.get(id); + if (!prev || changeSignature(prev) !== sig) { + changedIds.push(id); + } + }); + + const fresh = changedIds.length > 0 ? await fetchProjectsByIds(changedIds) : []; + const freshById = new Map(fresh.map((p) => [p.id, p])); + + // Assemble in the current scan order; deleted projects (in cache, not in + // scan) are naturally excluded. Missing new ids (edge race) are dropped. + const results: ProjectResult[] = []; + scan.forEach((_sig, id) => { + const project = freshById.get(id) ?? prevById.get(id); + if (project) { + results.push(project); + } + }); + + const reused = scan.size - changedIds.length; + let deleted = 0; + prevById.forEach((_project, id) => { + if (!scan.has(id)) { + deleted += 1; + } + }); + console.log(`incremental: ${changedIds.length} changed/new, ${reused} reused, ${deleted} deleted (of ${scan.size} current)`); + + return { results, totalCount: scan.size }; +} async function getCsrfTokenValue() { const healthcheckUrl = `${baseUrl}health-check/?format=json`; try { const healthcheckData = await fetch(healthcheckUrl, { credentials: 'include' }); - const cookiesToSet = ( healthcheckData.headers as (Headers & { getSetCookie: () => string[] }) ).getSetCookie(); - const parsedCookiesToSet = cookiesToSet .flatMap((item: string) => item.split('; ')) .map((item: string) => { const [key, value] = item.split('='); return { key, value } as { key: string, value: string }; }); - const csrfToken = parsedCookiesToSet.find((item) => item.key === COOKIE_NAME); return csrfToken?.value; } catch (err) { - // eslint-disable-next-line no-console - console.error('failed to do the healthcheck', healthcheckUrl); + console.error('failed to do the healthcheck', healthcheckUrl, err); } - return undefined; } async function fetchAndWriteData() { - let data = {} as AllDataQuery; + let data: StaticData; if (pipelineType === 'ci') { data = dummyData; } else { console.log('Fetching data from GraphQL endpoint from ', GRAPHQL_ENDPOINT); - const csrfTokenValue = await getCsrfTokenValue(); if (!csrfTokenValue) { console.error('Could not fetch crsf token'); return; } const referer = process.env.MAPSWIPE_REFERER_ENDPOINT ?? baseUrl; - console.log('CSRF Token exists:', !!csrfTokenValue); console.log('Referer exists:', !!referer); - graphQLClient.setHeader('X-CSRFToken', csrfTokenValue); graphQLClient.setHeader('Cookie', `${COOKIE_NAME}=${csrfTokenValue}`); graphQLClient.setHeader('Referer', referer); - data = (await graphQLClient.request(query)) as AllDataQuery; + + const publicProjects = await fetchProjectsIncrementally(); + const rest = (await graphQLClient.request(restQuery)) as RestDataQuery; + data = { ...rest, publicProjects }; } - // ensure the `data` directory exists if (!fs.existsSync(datadir)) { fs.mkdirSync(datadir, { recursive: true }); } - const outputPath = path.join(__dirname, '../fullData/staticData.json'); fs.writeFileSync(outputPath, JSON.stringify(data, null, 2)); console.log(`Data written to ${outputPath}`); console.log(`Top-level keys: ${Object.keys(data ?? {}).join(', ')}`); @@ -260,9 +224,7 @@ async function fetchAndWriteData() { const lastModifiedEpoch = Date.now(); if (process.env.GITHUB_ENV) { - console.log( - 'Setting MAPSWIPE_API_LAST_MODIFIED_EPOCH in GitHub Actions environment', - ); + console.log('Setting MAPSWIPE_API_LAST_MODIFIED_EPOCH in GitHub Actions environment'); fs.appendFileSync( process.env.GITHUB_ENV, `MAPSWIPE_API_LAST_MODIFIED_EPOCH=${lastModifiedEpoch}\n`, diff --git a/scripts/generateTypes.mjs b/scripts/generateTypes.mjs new file mode 100644 index 00000000..507b7be9 --- /dev/null +++ b/scripts/generateTypes.mjs @@ -0,0 +1,29 @@ +// graphql-codegen wrapper: the typescript + typescript-operations plugin pair +// emits the operation-referenced enums twice (identical blocks), which is a +// TS "duplicate identifier" error. Run codegen, then drop exact-duplicate +// top-level `export type X = ...` blocks. +import { execSync } from 'node:child_process'; +import { readFileSync, writeFileSync } from 'node:fs'; + +execSync('pnpm exec graphql-codegen --config codegen.yml', { stdio: 'inherit' }); + +const path = 'generated/types.ts'; +const src = readFileSync(path, 'utf8'); +const blocks = src.split(/\n\n(?=export |\/\*\*)/); +const seenName = new Set(); +const seenBody = new Set(); +const out = blocks.filter((block) => { + const m = block.match(/^export type (\w+)/); + if (!m) return true; + const name = m[1]; + const body = block.trim(); + if (seenName.has(name)) { + if (seenBody.has(body)) return false; // identical duplicate — drop + throw new Error(`conflicting duplicate type: ${name}`); + } + seenName.add(name); + seenBody.add(body); + return true; +}); +writeFileSync(path, out.join('\n\n')); +console.log(`generated/types.ts: ${blocks.length - out.length} duplicate block(s) removed`); diff --git a/scripts/graphql/projectFields.graphql b/scripts/graphql/projectFields.graphql new file mode 100644 index 00000000..ee4431ac --- /dev/null +++ b/scripts/graphql/projectFields.graphql @@ -0,0 +1,31 @@ +# Only fields the pages render (see the page slim functions). Shared by the +# full and by-id queries so the selection can't drift. +fragment ProjectFields on ProjectType { + id + exportAggregatedResultsWithGeometry { id fileSize file { url } mimetype } + exportAggregatedResults { id fileSize file { url } mimetype } + exportAreaOfInterest { id fileSize file { url } mimetype } + exportGroups { id fileSize file { url } mimetype } + exportHistory { id fileSize file { url } mimetype modifiedAt } + exportResults { id fileSize file { url } mimetype } + exportTasks { id fileSize file { url } mimetype } + exportUsers { id fileSize file { url } mimetype } + exportHotTaskingManagerGeometries { id fileSize file { url } mimetype } + exportModerateToHighAgreementYesMaybeGeometries { id fileSize file { url } mimetype } + name + firebaseId + image { file { url } } + description + requestingOrganization { id name } + progress + status + projectType + createdAt + modifiedAt + lastContributionDate + region + requestingOrganizationId + numberOfContributorUsers + aoiGeometry { centroid id totalArea bbox } + aoiGeometryInputAsset { id fileSize file { url } mimetype } +} diff --git a/scripts/graphql/projectScan.graphql b/scripts/graphql/projectScan.graphql new file mode 100644 index 00000000..8808148b --- /dev/null +++ b/scripts/graphql/projectScan.graphql @@ -0,0 +1,10 @@ +# Cheap change-detection scan: id + the two timestamps that signal a change. +query ProjectScan($limit: Int!, $offset: Int!) { + publicProjects( + filters: { status: { inList: [PUBLISHED, FINISHED] } }, + pagination: { limit: $limit, offset: $offset }, + ) { + results { id modifiedAt lastContributionDate } + totalCount + } +} diff --git a/scripts/graphql/projectsByIds.graphql b/scripts/graphql/projectsByIds.graphql new file mode 100644 index 00000000..bbacd083 --- /dev/null +++ b/scripts/graphql/projectsByIds.graphql @@ -0,0 +1,9 @@ +query ProjectsByIds($ids: [ID!], $limit: Int!, $offset: Int!) { + publicProjects( + filters: { status: { inList: [PUBLISHED, FINISHED] }, id: { inList: $ids } }, + pagination: { limit: $limit, offset: $offset }, + ) { + results { ...ProjectFields } + totalCount + } +} diff --git a/scripts/graphql/publicProjects.graphql b/scripts/graphql/publicProjects.graphql new file mode 100644 index 00000000..5b34f4aa --- /dev/null +++ b/scripts/graphql/publicProjects.graphql @@ -0,0 +1,9 @@ +query PublicProjects($limit: Int!, $offset: Int!) { + publicProjects( + filters: { status: { inList: [PUBLISHED, FINISHED] } }, + pagination: { limit: $limit, offset: $offset }, + ) { + results { ...ProjectFields } + totalCount + } +} diff --git a/scripts/graphql/restData.graphql b/scripts/graphql/restData.graphql new file mode 100644 index 00000000..e8ea9a22 --- /dev/null +++ b/scripts/graphql/restData.graphql @@ -0,0 +1,6 @@ +# The non-project data — small, singular, fetched every run. +query RestData { + communityStats { id totalContributors totalSwipes } + publicOrganizations(pagination: { limit: 9999 }) { results { id name } } + globalExportAssets { type lastUpdatedAt fileSize file { url name } } +} diff --git a/scripts/incremental-build.mjs b/scripts/incremental-build.mjs new file mode 100644 index 00000000..0e4723bd --- /dev/null +++ b/scripts/incremental-build.mjs @@ -0,0 +1,36 @@ +#!/usr/bin/env node +// Incremental static-build orchestration — thin wrapper over +// astro-incremental-static (see packages/astro-incremental-static/README.md, +// incl. the propsData correctness contract). This file only holds THIS app's +// config; all mechanics (codeHash, dist merge, prune, merged-tree sitemap +// regen, manifest persistence) live in the package. +// +// Keep sitemap site/locales in sync with astro.config.mjs (not imported from +// there: the config pulls in integrations plain Node shouldn't load here). +import { runIncrementalBuild } from '../packages/astro-incremental-static/src/runner.ts'; + +runIncrementalBuild({ + distDir: 'build/out', + stateDir: 'build/incremental', + codeHash: { + // Everything that affects rendered output EXCEPT the data axis + // (build/full-data/ + blogs/ are tracked per page via propsHash) and build + // artifacts. Repo-root locales/ is read by src/i18n's glob. + include: [ + 'src/**', + 'locales/**', + 'astro.config.mjs', + 'package.json', + ], + }, + // Per-record page dirs that may disappear between builds (deleted + // projects/blogs). Keys match the selectPaths candidates. + prune: { routes: ['*/projects/*', '*/blogs/*'] }, + sitemap: { + site: 'https://mapswipe.org', + locales: ['en', 'ne', 'hu', 'de', 'cs', 'pt'], + defaultLocale: 'en', + }, + prefetch: { cmd: 'node', args: ['scripts/prefetch-images.mjs'] }, + buildCommand: { cmd: 'astro', args: ['build'] }, +}); diff --git a/scripts/prefetch-images.mjs b/scripts/prefetch-images.mjs new file mode 100644 index 00000000..a637522c --- /dev/null +++ b/scripts/prefetch-images.mjs @@ -0,0 +1,43 @@ +// Concurrent image-prefetch step — thin wrapper over the app's configured +// astro-remote-images instance. Runs BEFORE `astro build` (see the npm +// `build` script) to warm the on-disk build/image-cache/ with a bounded pool, so +// the per-page render pass hits pure cache and does NOT serialize thousands +// of remote fetches. +// +// The app owns only the URL COLLECTOR below; the fetch+sharp+cache pipeline, +// pool, and negative caches live in the package (same instance = same cache +// keys the render pass looks up, warmed at COVER_WIDTH). +import { createRequire } from 'node:module'; +import { join } from 'node:path'; +import { remoteImages, COVER_WIDTH } from '../src/lib/remoteImages.ts'; + +const require = createRequire(import.meta.url); + +// Collect the unique cover URLs the build will actually render. Mirrors +// src/lib/data.ts (same staticData path + same BAD_IMAGE_TEST / SLICE_LIMIT +// knobs) so the warmed set matches the render set exactly. +function collectCoverUrls() { + const data = require(process.env.MAPSWIPE_DATA_FILE ?? join(process.cwd(), 'build', 'full-data', 'staticData.json')); + let projects = (data?.publicProjects?.results ?? []); + + const limit = Number(process.env.SLICE_LIMIT ?? 0); + if (limit > 0) projects = projects.slice(0, limit); + + // Match data.ts's BAD_IMAGE_TEST knob: inject the bad URL into the first + // image-bearing project so the prefetch exercises the same (bad) cover + // the render will, proving fail-soft end to end. + if (process.env.BAD_IMAGE_TEST) { + const badUrl = process.env.BAD_IMAGE_TEST; + const target = projects.find((p) => p?.image?.file?.url); + if (target?.image?.file) target.image.file.url = badUrl; + } + + return projects.map((p) => p?.image?.file?.url).filter(Boolean); +} + +// Fail-soft at the top level too: prefetch is a cache warm, never a build +// blocker (the package's prefetch never throws; this guards the collector). +remoteImages.prefetch(collectCoverUrls, { width: COVER_WIDTH }).catch((err) => { + console.warn('[prefetch] non-fatal error, continuing to build:', err); + process.exit(0); +}); diff --git a/scripts/sweep-dist.mjs b/scripts/sweep-dist.mjs new file mode 100644 index 00000000..a80abc7e --- /dev/null +++ b/scripts/sweep-dist.mjs @@ -0,0 +1,10 @@ +#!/usr/bin/env node +// Sweep build/out/_astro assets nothing references, after a plain `astro build` — +// astro:assets emits image originals that only exist in optimized variants +// (see packages/astro-dist-sweep/README.md). The incremental runner performs +// the same sweep itself over the merged tree. +import { resolve } from 'node:path'; +import { sweepSupersededAssets } from '../packages/astro-dist-sweep/src/index.ts'; + +const { removed, bytes } = sweepSupersededAssets(resolve('build/out')); +console.log(`[dist-sweep] removed ${removed} unreferenced _astro asset(s) (${(bytes / 1e6).toFixed(1)} MB)`); diff --git a/scripts/verify-dist.mjs b/scripts/verify-dist.mjs new file mode 100644 index 00000000..370529d7 --- /dev/null +++ b/scripts/verify-dist.mjs @@ -0,0 +1,61 @@ +// Post-build verification: every invariant we shipped a bug against, as one +// script. Run after a FULL build (CI fails the deploy on any regression). +import { readFileSync, existsSync } from 'node:fs'; +import { execSync } from 'node:child_process'; +import { join } from 'node:path'; + +const DIST = 'build/out'; +const failures = []; +const check = (name, ok, detail = '') => { + console.log(`${ok ? ' ok ' : 'FAIL '} ${name}${detail ? ` — ${detail}` : ''}`); + if (!ok) failures.push(name); +}; + +const data = JSON.parse(readFileSync('build/full-data/staticData.json', 'utf8')); +const projects = data.publicProjects.results.length; +const locales = ['en', 'ne', 'hu', 'de', 'cs', 'pt']; + +const htmlCount = Number(execSync(`find ${DIST} -name index.html -o -name '*.html' | wc -l`).toString()); +// per locale: projects + home + data + get-involved + privacy + blogs list +// + blog posts; plus 404 + redirect stubs. Lower bound guards mass page loss +// without needing to model every stub exactly. +const expectedMin = locales.length * (projects + 5); +check('page count', htmlCount >= expectedMin, `${htmlCount} html (≥ ${expectedMin} expected for ${projects} projects)`); + +const sitemap = readFileSync(join(DIST, 'sitemap-0.xml'), 'utf8'); +const locs = (sitemap.match(//g) ?? []).length; +check('sitemap present + full', locs >= expectedMin, `${locs} entries`); +check('sitemap not partial', locs > 1000, `${locs} (a partial-merge sitemap would be tiny)`); + +const dataJson = JSON.parse(readFileSync(join(DIST, 'data-explorer.json'), 'utf8')); +check('data-explorer.json matches data', dataJson.miniProjects.length === projects, + `${dataJson.miniProjects.length} vs ${projects}`); + +const sampleProject = execSync( + `find ${DIST}/en/projects -mindepth 2 -name index.html | head -1`).toString().trim(); +const page = readFileSync(sampleProject, 'utf8'); +const hydrationSrc = page.match(/src="(\/_astro\/dedup-[^"]+\.js)"/); +check('hydration script referenced', !!hydrationSrc); +check('hydration script exists', !!hydrationSrc && existsSync(join(DIST, hydrationSrc[1]))); + +check('no unoptimized blog-image refs', + execSync(`grep -rl "/img/blogImages" ${DIST} --include='*.html' | wc -l`).toString().trim() === '0'); +check('error-image asset shipped', existsSync(join(DIST, '_img/image-error.svg'))); + +const animated = execSync( + `for f in ${DIST}/_astro/*.webp; do grep -lq ANMF "$f" 2>/dev/null && echo "$f"; done | wc -l`, + { shell: '/bin/bash' }).toString().trim(); +check('animated webp survives (GIF regression guard)', Number(animated) >= 1, `${animated} animated`); + +check('German content renders', readFileSync(join(DIST, 'de/index.html'), 'utf8').includes('Projekt')); +check('Nepali content renders', /[ऀ-ॿ]/.test(readFileSync(join(DIST, 'ne/index.html'), 'utf8'))); +check('dist/locales not shipped', !existsSync(join(DIST, 'locales'))); + +const distMB = Number(execSync(`du -sm ${DIST}`).toString().split('\t')[0]); +check('dist under 900 MB (1 GB Pages limit early warning)', distMB < 900, `${distMB} MB`); + +if (failures.length) { + console.error(`\n${failures.length} check(s) failed: ${failures.join(', ')}`); + process.exit(1); +} +console.log(`\nall checks passed (dist ${distMB} MB, ${htmlCount} pages)`); diff --git a/src/components/Button/index.astro b/src/components/Button/index.astro new file mode 100644 index 00000000..fd460cdf --- /dev/null +++ b/src/components/Button/index.astro @@ -0,0 +1,33 @@ +--- +// Extra attributes (aria-*, id, …) pass straight through to the element. +import type { HTMLAttributes } from 'astro/types'; +import styles from 'components/Button/styles.module.css'; + +type Variant = 'transparent' | 'primary' | 'border' | 'icon'; + +interface Props extends Omit, 'type'> { + variant?: Variant; + class?: string; +} + +const { + variant = 'primary', + class: className, + ...rest +} = Astro.props; +// `slot` on a component is placement metadata for the parent, but Astro also +// leaves it in props — it must not be spread onto the element. +delete rest.slot; + +const variantMap: Record = { + transparent: undefined, + primary: styles.primary, + border: styles.border, + icon: styles.icon, +}; +--- + diff --git a/src/components/Button/index.tsx b/src/components/Button/index.tsx index 01763569..44a88a0a 100644 --- a/src/components/Button/index.tsx +++ b/src/components/Button/index.tsx @@ -1,45 +1,35 @@ import React from 'react'; -import { _cs } from '@togglecorp/fujs'; - +import cs from 'lib/cs'; import styles from './styles.module.css'; +// React twin of Button.astro, for the islands (.astro components cannot +// render inside React trees; islands also need onClick). Keep the two in sync. + type Variant = 'transparent' | 'primary' | 'border' | 'icon'; -const variantToStyleMap: { - [key in Variant]: string | undefined; -} = { + +const variantMap: Record = { transparent: undefined, primary: styles.primary, border: styles.border, icon: styles.icon, }; -export interface Props extends React.HTMLProps { - className?: string; + +interface Props extends Omit, 'type'> { variant?: Variant; - elementRef?: React.Ref; } -// NOTE: this does not support relative buttons - function Button(props: Props) { const { - children, variant = 'primary', className, - elementRef, + children, ...rest } = props; - return ( diff --git a/src/components/Button/styles.module.css b/src/components/Button/styles.module.css index 08d86c80..70891122 100644 --- a/src/components/Button/styles.module.css +++ b/src/components/Button/styles.module.css @@ -2,9 +2,9 @@ outline: none; border: 0; background-color: transparent; - cursor: pointer; font-family: inherit; font-size: inherit; + cursor: pointer; &.primary { transition: 0.2s all ease-in-out; @@ -39,7 +39,7 @@ align-items: center; transition: 0.2s all ease-in-out; border-radius: var(--border-radius-medium); - background-color: rgba(0, 0, 0, 0.2); + background-color: rgb(0 0 0 / 20%); padding: var(--spacing-super-small) var(--spacing-extra-small); line-height: 1; color: var(--color-text-on-dark); diff --git a/src/components/Card/index.astro b/src/components/Card/index.astro new file mode 100644 index 00000000..24924bb6 --- /dev/null +++ b/src/components/Card/index.astro @@ -0,0 +1,65 @@ +--- +// The cover has two forms: `coverImageUrl` for the plain-image case, the +// `cover` slot for custom media (astro:assets , the before/after +// pair) — a slot can't take the plain case because astro:assets components +// must be instantiated by the caller. `as` picks the root element (article +// in the semantic card lists, div elsewhere). +import styles from 'components/Card/styles.module.css'; +import Heading from 'components/Heading/index.astro'; +import ImageWrapper from 'components/ImageWrapper/index.astro'; + +interface Props { + as?: 'div' | 'article'; + heading?: string; + // string or a class:list-style array (falsy entries dropped) + class?: string | (string | false | null | undefined)[]; + coverImageUrl?: string; + coverWrapperClass?: string; + childrenContainerClass?: string; + borderless?: boolean; + coverImageOnSide?: boolean; +} + +const { + as: Element = 'div', + heading, + class: className, + coverImageUrl, + coverWrapperClass, + childrenContainerClass, + borderless = false, + coverImageOnSide = false, +} = Astro.props; + +const icons = Astro.slots.has('icons') ? await Astro.slots.render('icons') : ''; +const actions = Astro.slots.has('actions') ? await Astro.slots.render('actions') : ''; +const cover = Astro.slots.has('cover') ? await Astro.slots.render('cover') : ''; +--- + + {cover.trim() && } + {coverImageUrl && ( + + )} +
+
+
+ {icons.trim() &&
} + {heading && ( + {heading} + )} +
+ {actions.trim() &&
} +
+
+
+ diff --git a/src/components/Card/index.tsx b/src/components/Card/index.tsx index 5232da5e..158bca54 100644 --- a/src/components/Card/index.tsx +++ b/src/components/Card/index.tsx @@ -1,135 +1,90 @@ import React from 'react'; -import { _cs } from '@togglecorp/fujs'; - -import Heading from 'components/Heading'; -import ImageWrapper from 'components/ImageWrapper'; - +import cs from 'lib/cs'; +import Heading from '../Heading'; +import ImageWrapper from '../ImageWrapper'; import styles from './styles.module.css'; +// React twin of Card.astro, for the islands (.astro components cannot render +// inside React trees). Keep the two in sync. Extras over the .astro twin, +// used only by the islands: headingFont, actionsClassName, coverAlt, footer. + interface Props { + as?: 'div' | 'article'; className?: string; heading?: React.ReactNode; - description?: React.ReactNode; - headingFont?: 'normal' | 'heading'; - children?: React.ReactNode; - childrenContainerClassName?: string; + headingFont?: 'heading' | 'normal'; icons?: React.ReactNode; actions?: React.ReactNode; - footerContent?: React.ReactNode; - footerIcons?: React.ReactNode; - footerActions?: React.ReactNode; - imageClassName?: string; - cardContentClassName?: string; + actionsClassName?: string; + cover?: React.ReactNode; coverImageUrl?: string; - coverImageContent?: React.ReactNode; - coverImageOnSide?: boolean; + coverAlt?: string; + coverWrapperClassName?: string; + childrenContainerClassName?: string; + footer?: React.ReactNode; borderless?: boolean; - nonOptimizedImage?: boolean; + coverImageOnSide?: boolean; + children?: React.ReactNode; } function Card(props: Props) { const { + as: Element = 'div', className, heading, headingFont, - description, - imageClassName, - childrenContainerClassName, icons, actions, - footerContent, - cardContentClassName, - footerActions, - footerIcons, - children, + actionsClassName, + cover, coverImageUrl, - coverImageOnSide, - coverImageContent, - borderless, - nonOptimizedImage, + coverAlt, + coverWrapperClassName, + childrenContainerClassName, + footer, + borderless = false, + coverImageOnSide = false, + children, } = props; - - const showHeader = icons || heading || actions; - const showFooter = footerIcons || footerContent || footerActions; - return ( -
- {coverImageUrl && !coverImageContent && ( + + {cover} + {coverImageUrl && ( )} - {coverImageContent && ( -
- {coverImageContent} -
- )} -
- {(showHeader || !!description) && ( -
- {showHeader && ( -
- {icons && ( -
- {icons} -
- )} - - {heading} - - {actions && ( -
- {actions} -
- )} -
- )} - {description && ( -
- {description} -
+
+
+
+ {icons &&
{icons}
} + {heading && ( + + {heading} + )}
- )} -
+ {actions &&
{actions}
} +
+
{children}
- {showFooter && ( + {footer && (
- {footerIcons && ( -
- {footerIcons} -
- )} -
- {footerContent} -
- {footerActions && ( -
- {footerActions} -
- )} +
{footer}
)}
-
+ ); } diff --git a/src/components/Card/styles.module.css b/src/components/Card/styles.module.css index 5f1988f7..1a8abe7f 100644 --- a/src/components/Card/styles.module.css +++ b/src/components/Card/styles.module.css @@ -18,29 +18,12 @@ } } - @media screen and (min-width: 720px) { - &.coverImageOnSide { - flex-direction: row; - - .coverImageWrapper { - width: 14rem; - height: 16rem; - - .image { - border-top-left-radius: 0rem; - border-top-right-radius: 0rem; - object-fit: cover; - } - } - } - } - .cardContent { display: flex; flex-direction: column; flex-grow: 1; - padding: var(--spacing-extra-large); gap: var(--spacing-small); + padding: var(--spacing-extra-large); .headerWrapper { display: flex; @@ -49,9 +32,9 @@ .header { display: flex; - align-items: center; flex-shrink: 0; gap: var(--spacing-extra-small); + align-items: center; .heading { display: -webkit-box; @@ -79,8 +62,8 @@ } &.borderless { - border: 0; gap: var(--spacing-medium); + border: 0; .coverImageWrapper { .image { @@ -92,4 +75,21 @@ padding: 0; } } + + @media screen and (width >= 720px) { + &.coverImageOnSide { + flex-direction: row; + + .coverImageWrapper { + width: 14rem; + height: 16rem; + + .image { + border-top-left-radius: 0; + border-top-right-radius: 0; + object-fit: cover; + } + } + } + } } diff --git a/src/components/CenteredCard/index.astro b/src/components/CenteredCard/index.astro new file mode 100644 index 00000000..ff3ed8ff --- /dev/null +++ b/src/components/CenteredCard/index.astro @@ -0,0 +1,15 @@ +--- +import styles from 'components/CenteredCard/styles.module.css'; + +interface Props { + heading?: string; + class?: string; +} + +const { heading, class: className } = Astro.props; +--- +
+
{heading}
+
+
+
diff --git a/src/components/CenteredCard/index.tsx b/src/components/CenteredCard/index.tsx deleted file mode 100644 index 318a613f..00000000 --- a/src/components/CenteredCard/index.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import React from 'react'; -import { _cs } from '@togglecorp/fujs'; - -import styles from './styles.module.css'; - -interface Props { - className?: string; - actionsClassName?: string; - title?: React.ReactNode; - description?: React.ReactNode; - actions?: React.ReactNode; -} - -function CenteredCard(props: Props) { - const { - className, - actionsClassName, - title, - description, - actions, - } = props; - - return ( -
- {title && ( -
- {title} -
- )} - {description && ( -
- {description} -
- )} - {actions && ( -
- {actions} -
- )} -
- ); -} - -export default CenteredCard; diff --git a/src/components/CenteredCard/styles.module.css b/src/components/CenteredCard/styles.module.css index d9c18bbd..fe1c1c1a 100644 --- a/src/components/CenteredCard/styles.module.css +++ b/src/components/CenteredCard/styles.module.css @@ -1,13 +1,13 @@ .card { display: flex; - align-items: center; flex-direction: column; + gap: var(--spacing-medium); + align-items: center; justify-content: center; border: var(--width-separator-thin) solid var(--color-separator); border-radius: var(--border-radius-medium); padding: var(--spacing-extra-large) var(--spacing-large); text-align: center; - gap: var(--spacing-medium); .heading { max-width: 32rem; diff --git a/src/components/DataExplorer.tsx b/src/components/DataExplorer.tsx new file mode 100644 index 00000000..ac3b8df0 --- /dev/null +++ b/src/components/DataExplorer.tsx @@ -0,0 +1,704 @@ +import React, { + useState, + useCallback, + useMemo, + useEffect, + Suspense, + lazy, +} from 'react'; + +import ProjectTypeIcon from './ProjectTypeIcon'; +import Section from './Section'; +import Card from './Card'; +import Tag from './Tag'; +import Button from './Button'; +import Link from './Link'; +import cs from 'lib/cs'; +import type { MiniProject, Organization, ExportAsset } from 'lib/dataExplorer'; + +// CSS Modules recovered from the Next app. The island shares the data page's +// module (same hashed classes as the .astro page that mounts it) plus the +// form-control modules whose markup stays island-local (MultiSelectInput, +// RadioInput, RawInput, SelectInput); the chrome comes from the React twins +// of the shared components (Section, Card, Tag, Button, Link). +import styles from 'pages/[locale]/data/styles.module.css'; +import multiSelectStyles from './MultiSelectInput/styles.module.css'; +import radioStyles from './RadioInput/styles.module.css'; +import rawInputStyles from './RawInput/styles.module.css'; +import selectInputStyles from './SelectInput/styles.module.css'; + +// Leaflet touches window at import time and cannot SSR. We lazy-load the map +// module via a dynamic import() so its (leaflet) top-level code only runs in the +// browser — the parent defers rendering behind a post-mount flag. This mirrors +// the Next page's dynamic(() => import('ProjectsMap'), { ssr: false }). +const ProjectsMapIsland = lazy(() => import('./ProjectsMapIsland')); + +// --------------------------------------------------------------------------- +// The ENTIRE interactive region of the data-explorer page as ONE React island. +// Filters + text search + sort (implicit: pre-sorted desc by createdAt) + +// pagination + the card list + the leaflet map all share this component's +// state, so the filters drive BOTH the map and the list (the non-negotiable +// shared-state requirement). +// +// SSR: mounted with client:load, so filters + the initial card list are in the +// server-rendered HTML (SEO, no blank page). Leaflet cannot SSR, so ONLY the +// map is guarded behind a `mounted` flag set in useEffect — it renders on the +// client after hydration without breaking SSR. This mirrors the Next page's +// dynamic(ssr:false) map inside an otherwise-SSR'd page. +// +// DATA: only the first PAGE_SIZE projects (the SSR'd cards) arrive as props; +// the full ~2.4k-project dataset + organizations + cover-image map is a +// SHARED, locale-independent /data-explorer.json fetched on mount (see +// src/pages/data-explorer.json.ts — inlining it into every locale's HTML would +// cost ~3 MB apiece). While loading, the initial cards render and the filter +// inputs are disabled (loading flag); filters/search/map operate on the full +// dataset once it arrives. Fail-soft: if the fetch fails we keep the initial +// slice and warn. +// --------------------------------------------------------------------------- + +export const PAGE_SIZE = 9; + +// --- Inlined @togglecorp/fujs helpers (fujs isn't a dep of the astro app; we +// avoid adding it just for these tiny functions) -------------------------- +function isDefined(v: T | null | undefined): v is T { + return v !== null && v !== undefined; +} +function bound(value: number, min: number, max: number): number { + return Math.max(min, Math.min(max, value)); +} +function sum(list: number[]): number { + return list.reduce((acc, v) => acc + v, 0); +} +function compareDate(a: string | null | undefined, b: string | null | undefined): number { + if (!a && !b) return 0; + if (!a) return -1; + if (!b) return 1; + const ta = new Date(a).getTime(); + const tb = new Date(b).getTime(); + return ta < tb ? -1 : ta > tb ? 1 : 0; +} +function caseInsensitiveSubmatch(text: string | undefined, search: string): boolean { + return (text ?? '').toLowerCase().includes(search.toLowerCase()); +} +function compareStringSearch(a: string, b: string, search: string): number { + const la = a.toLowerCase(); + const lb = b.toLowerCase(); + const s = search.toLowerCase(); + return (la.indexOf(s)) - (lb.indexOf(s)); +} +// Faithful port of utils/common#rankedSearchOnList. +function rankedSearchOnList( + list: T[], + searchString: string | undefined, + labelSelector: (item: T) => string, +): T[] { + if (!searchString || searchString.trim() === '') return list; + return list + .filter((option) => caseInsensitiveSubmatch(labelSelector(option), searchString)) + .sort((a, b) => compareStringSearch(labelSelector(a), labelSelector(b), searchString)); +} +function getFileSizeProperties(fileSize: number) { + const mb = 1024 * 1024; + if (fileSize > mb / 10) return { size: fileSize / mb, unit: 'megabyte' }; + return { size: fileSize / 1024, unit: 'kilobyte' }; +} + +// --- Tiny i18n interpolation. The .astro frontmatter hands us the raw template +// strings for the `data` namespace; here we resolve `{{name}}`, `{{name, +// number}}` and `{{name, datetime}}` placeholders using Intl (matching the Next +// app's i18next number/datetime formatting) ------------------------------- +type Dict = Record; +function makeT(dict: Dict) { + return function t(key: string, params?: Record): string { + let str = dict[key] ?? key; + if (params) { + str = str.replace(/\{\{\s*([^},]+?)\s*(?:,\s*(number|datetime))?\s*\}\}/g, (_m, name, fmt) => { + const raw = params[name as string]; + if (raw === undefined || raw === null) return ''; + if (fmt === 'number' && typeof raw === 'number') { + return new Intl.NumberFormat().format(raw); + } + if (fmt === 'datetime') { + const d = raw instanceof Date ? raw : new Date(String(raw)); + if (!Number.isNaN(d.getTime())) return d.toLocaleDateString(undefined, { dateStyle: 'medium' }); + return String(raw); + } + return String(raw); + }); + } + return str; + }; +} + +// --- Debounce hook (parity with hooks/useDebouncedValue) ------------------ +function useDebouncedValue(value: T, delay = 200): T { + const [debounced, setDebounced] = useState(value); + useEffect(() => { + const id = setTimeout(() => setDebounced(value), delay); + return () => clearTimeout(id); + }, [value, delay]); + return debounced; +} + +const PROJECT_TYPES = ['FIND', 'VALIDATE', 'COMPARE', 'COMPLETENESS', 'VALIDATE_IMAGE', 'STREET', 'LOCATE'] as const; + +// Shape of the shared /data-explorer.json emitted at build time by +// src/pages/data-explorer.json.ts. +interface DataExplorerDataset { + miniProjects: MiniProject[]; + organizations: Organization[]; + imageMap: Record; +} + +interface Props { + dict: Dict; + // Locale of the page rendering this island — project links carry it + // (parity with Next's /[locale]/projects/ links). + locale: string; + // {originalCoverUrl -> optimized (or passthrough) src} for the INITIAL slice + // only, resolved at build time by the fail-soft optimizer; the full map + // arrives with the fetched dataset. + imageMap: Record; + // First PAGE_SIZE projects, SSR'd for SEO / no blank page. + initialProjects: MiniProject[]; + // Total project count (the fetched dataset's length) — lets the summary line + // show the real count while the dataset is still loading. + totalCount: number; + // URL of the shared locale-independent dataset JSON. + dataUrl: string; + globalExportAssets: ExportAsset[]; + minArea: number; + maxArea: number; + minContributors: number; + maxContributors: number; + buildDate: string | null; +} + +export default function DataExplorer(props: Props) { + const { + dict, + locale, + imageMap: initialImageMap, + initialProjects, + totalCount, + dataUrl, + globalExportAssets, + minArea, + maxArea, + minContributors, + maxContributors, + buildDate, + } = props; + + const t = useMemo(() => makeT(dict), [dict]); + + // client-guard for the (non-SSR-able) leaflet map only. + const [mounted, setMounted] = useState(false); + useEffect(() => setMounted(true), []); + + // Full dataset, fetched on mount from the shared locale-independent JSON. + // Initial state mirrors the SSR'd props so hydration matches the static + // HTML. `loading` starts true (also in the SSR'd HTML — filter inputs render + // disabled until the data they filter has arrived). + const [projects, setProjects] = useState(initialProjects); + const [organizations, setOrganizations] = useState([]); + const [imageMap, setImageMap] = useState>(initialImageMap); + const [loading, setLoading] = useState(true); + + useEffect(() => { + let cancelled = false; + fetch(dataUrl) + .then((res) => { + if (!res.ok) throw new Error(`HTTP ${res.status}`); + return res.json() as Promise; + }) + .then((dataset) => { + if (cancelled) return; + setProjects(dataset.miniProjects ?? []); + setOrganizations(dataset.organizations ?? []); + // Merge over the initial map so the already-rendered cards never lose + // their resolved src even if the fetched map were somehow partial. + setImageMap((prev) => ({ ...prev, ...(dataset.imageMap ?? {}) })); + setLoading(false); + }) + .catch((err: unknown) => { + if (cancelled) return; + // Fail-soft: keep showing the SSR'd initial slice. + + console.warn(`data-explorer: failed to load ${dataUrl}; showing the initial ${initialProjects.length} projects only`, err); + setLoading(false); + }); + return () => { cancelled = true; }; + // dataUrl is a build-time constant; initialProjects only feeds the warning. + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [dataUrl]); + + const [items, setItems] = useState(PAGE_SIZE); + const [searchText, setSearchText] = useState(''); + const [locationSearchText, setLocationSearchText] = useState(''); + const [dateFrom, setDateFrom] = useState(''); + const [dateTo, setDateTo] = useState(''); + const [projectTypes, setProjectTypes] = useState([]); + const [projectStatuses, setProjectStatuses] = useState([]); + const [bubble, setBubble] = useState(''); + const [organization, setOrganization] = useState(''); + + const debouncedSearchText = useDebouncedValue(searchText); + const debouncedLocationSearchText = useDebouncedValue(locationSearchText); + + const typeLabels = useMemo>(() => ({ + FIND: t('type-find-title'), + VALIDATE: t('type-validate-title'), + COMPARE: t('type-compare-title'), + COMPLETENESS: t('type-completeness-title'), + VALIDATE_IMAGE: t('type-validate-image-title'), + STREET: t('type-streets-view-title'), + LOCATE: t('type-locate-title'), + }), [t]); + const statusLabels = useMemo>(() => ({ + PUBLISHED: t('active'), + FINISHED: t('finished'), + }), [t]); + const statusClass: Record = { + PUBLISHED: styles.active, + FINISHED: styles.finished, + }; + + const toggleInList = useCallback((list: string[], value: string): string[] => + list.includes(value) ? list.filter((v) => v !== value) : [...list, value], []); + + const visibleProjects = useMemo(() => { + let filtered = projects; + if (projectStatuses.length > 0) { + filtered = filtered.filter((p) => p.status !== null && projectStatuses.includes(p.status)); + } + if (projectTypes.length > 0) { + filtered = filtered.filter((p) => projectTypes.includes(String(p.projectType))); + } + if (dateFrom) { + filtered = filtered.filter((p) => compareDate(p.createdAt, dateFrom) >= 0); + } + if (dateTo) { + filtered = filtered.filter((p) => compareDate(dateTo, p.createdAt) >= 0); + } + if (organization) { + filtered = filtered.filter((p) => p.requestingOrganizationId === organization); + } + if (debouncedLocationSearchText) { + filtered = rankedSearchOnList(filtered, debouncedLocationSearchText, (p) => p.region ?? ''); + } + if (debouncedSearchText) { + filtered = rankedSearchOnList(filtered, debouncedSearchText, (p) => p.name); + } + return filtered; + }, [ + projects, projectStatuses, projectTypes, dateFrom, dateTo, organization, + debouncedLocationSearchText, debouncedSearchText, + ]); + + const handleSeeMore = useCallback(() => { + setItems((n) => bound(n + PAGE_SIZE, 0, projects.length)); + }, [projects.length]); + + const totalAreaSum = sum(visibleProjects.map((p) => p.aoiGeometry?.totalArea ?? 0).filter(isDefined)); + const roundedTotalArea = Math.round(totalAreaSum / 1000) * 1000; + const tableProjects = visibleProjects.slice(0, items); + + const radiusSelector = useCallback((project: MiniProject) => { + if (bubble === 'area') { + return 4 + 16 * (((project.aoiGeometry?.totalArea ?? 0) - minArea) / (maxArea - minArea || 1)); + } + if (bubble === 'contributors') { + return 4 + 16 * (((project.numberOfContributorUsers ?? 0) - minContributors) / (maxContributors - minContributors || 1)); + } + return 4; + }, [bubble, maxArea, minArea, maxContributors, minContributors]); + + const handleClearFilters = useCallback(() => { + setProjectStatuses([]); + setOrganization(''); + setSearchText(''); + setDateFrom(''); + setDateTo(''); + setProjectTypes([]); + setLocationSearchText(''); + }, []); + + const filtersApplied = !!( + searchText || dateFrom || locationSearchText || dateTo || + organization || projectTypes.length > 0 || projectStatuses.length > 0 + ); + + const assetConfig: Record = { + PROJECTS_CSV: { + heading: t('download-projects-overview-heading'), + description: t('download-projects-overview-description'), + fileLabel: 'CSV', + }, + PROJECT_STATS_BY_TYPES: { + heading: t('download-projects-csv-heading'), + description: t('download-projects-csv-description'), + fileLabel: 'CSV', + }, + PROJECTS_CENTROID_GEOJSON: { + heading: t('download-projects-with-centroid-heading'), + description: t('download-projects-with-centroid-description'), + fileLabel: 'GEOJSON', + }, + PROJECTS_GEOM_GEOJSON: { + heading: t('download-projects-with-geometry-headingdownload-projects-geometry-heading'), + description: t('download-projects-with-geometry-description'), + fileLabel: 'GEOJSON', + }, + }; + + // Shared pre-hydration/pre-data stand-in: same element for the Suspense + // fallback and the not-yet-mounted branch, so layout never shifts. + const mapPlaceholder = ( +