From a2f723ca2d8a37b2b23e14f177d6f539d9e998b8 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 11 Oct 2026 18:40:55 +0000 Subject: [PATCH 01/16] Start the rebuild: note schema, vault kit and data migration Phase 1 of the functional design plan. - Move the old site into legacy/, unchanged, for reference. It still builds from there until cutover. - Add packages/schema: the one definition of all 20 note types, with field titles and descriptions, note and vault checks, and JSON Schema export so later editing tools and the MCP server share it. - Add vault-kit: Obsidian templates generated from the schema, and the Site guide for 24.01_PersonalWebsite. - Add tools/migrate: turns the legacy JSON, collections, store data and page text into 545 Site folder notes and 84 images, and checks the result with the schema. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_012aSbo2D1qP8cMFQCykf7kx --- .gitignore | 21 +- README.md | 171 +- docs/content-model.md | 48 + .dockerignore => legacy/.dockerignore | 0 .env.example => legacy/.env.example | 0 legacy/.gitignore | 30 + ...ss_cfbaea37-9611-497f-804c-b438e5e659b3.md | 0 Dockerfile => legacy/Dockerfile | 0 legacy/LEGACY.md | 7 + .../Profile_Website_Spec.md | 0 legacy/README.md | 156 + astro.config.mjs => legacy/astro.config.mjs | 0 .../docker-compose.yml | 0 {docs => legacy/docs}/CONTENT.md | 0 {docs => legacy/docs}/DEPLOYMENT.md | 0 {docs => legacy/docs}/PHOTOS.md | 0 {docs => legacy/docs}/STORE.md | 0 {docs => legacy/docs}/THEMING.md | 0 legacy/package-lock.json | 8470 +++++++++++++++++ legacy/package.json | 39 + .../public}/.well-known/security.txt | 0 .../public}/apple-touch-icon.png | Bin .../public}/brand/patiun-seal.svg | 0 {public => legacy/public}/favicon-16.png | Bin {public => legacy/public}/favicon-32.png | Bin {public => legacy/public}/favicon-48.png | Bin {public => legacy/public}/favicon.ico | Bin {public => legacy/public}/favicon.svg | 0 {public => legacy/public}/icon-192.png | Bin {public => legacy/public}/icon-512.png | Bin .../public}/icon-maskable-192.png | Bin .../public}/icon-maskable-512.png | Bin {public => legacy/public}/icon_2.png | Bin .../public}/images/OLDgaming-setup.jpg | Bin .../public}/images/OLDhero-portrait.jpg | Bin .../public}/images/OLDpro-workspace.jpg | Bin .../public}/images/about-portrait.jpg | Bin .../public}/images/anime-hero.jpg | Bin .../public}/images/anime/attack-on-titan.jpg | Bin .../public}/images/anime/bleach.jpg | Bin .../public}/images/anime/code-geass.jpg | Bin .../public}/images/anime/demon-slayer.jpg | Bin .../anime/frieren-beyond-journeys-end.jpg | Bin .../images/anime/gundam-all-series.jpg | Bin .../public}/images/anime/jujutsu-kaisen.jpg | Bin .../public}/images/anime/kimetsu-no-yaiba.jpg | Bin .../anime/magi-the-labyrinth-of-magic.jpg | Bin .../public}/images/anime/my-hero-academia.jpg | Bin .../public}/images/anime/one-piece.jpg | Bin ...hat-time-i-got-reincarnated-as-a-slime.jpg | Bin .../public}/images/anime/vinland-saga.jpg | Bin .../public}/images/boardgames/.gitkeep | 0 .../public}/images/boardgames/wyrmspan.jpg | Bin {public => legacy/public}/images/cards.jpg | Bin .../public}/images/deckboxes-example-1.svg | 0 .../public}/images/deckboxes-example-2.svg | 0 .../public}/images/deckboxes.jpg | Bin .../public}/images/dice_galaxy.jpg | Bin .../public}/images/dice_golden_moss.jpg | Bin .../public}/images/dice_scarlet.jpg | Bin .../public}/images/dice_seastone.jpg | Bin .../public}/images/fantasy-books.jpg | Bin {public => legacy/public}/images/fav.png | Bin .../public}/images/games/.gitkeep | 0 .../public}/images/games/age-of-empires.jpg | Bin .../images/games/ark-survival-evolved.jpg | Bin .../public}/images/games/dave-the-diver.jpg | Bin .../public}/images/games/elden-ring.jpg | Bin .../public}/images/games/granblue-relink.jpg | Bin .../public}/images/games/minecraft.jpg | Bin .../images/games/pok-mon-crystal-version.jpg | Bin .../public}/images/games/satisfactory.jpg | Bin .../games/sly-3-honor-among-thieves.jpg | Bin .../sly-cooper-and-the-thievius-raccoonus.jpg | Bin .../star-wars-knights-of-the-old-republic.jpg | Bin .../public}/images/games/stardew-valley.jpg | Bin .../public}/images/gaming-setup.jpg | Bin .../public}/images/hero-portrait.jpg | Bin .../public}/images/kashi_at_work.jpg | Bin .../public}/images/making-print - Copy.jpg | Bin .../public}/images/making-print.jpg | Bin .../public}/images/making-resin.jpg | Bin {public => legacy/public}/images/minis.jpg | Bin .../public}/images/movies-hero.jpg | Bin .../images/movies/avengers-endgame.jpg | Bin .../images/movies/avengers-infinity-war.jpg | Bin .../public}/images/movies/contact.jpg | Bin .../images/movies/howls-moving-castle.jpg | Bin .../public}/images/movies/jurassic-park.jpg | Bin .../public}/images/movies/oceans-eleven.jpg | Bin ...wars-episode-v-the-empire-strikes-back.jpg | Bin .../public}/images/movies/the-matrix.jpg | Bin .../images/movies/the-princess-bride.jpg | Bin .../public}/images/movies/transformers.jpg | Bin .../public}/images/pokemon-cards.jpg | Bin {public => legacy/public}/images/pro-team.jpg | Bin .../public}/images/pro-workspace.jpg | Bin .../public}/images/proxies-example-1.svg | 0 .../public}/images/proxies-example-2.svg | 0 {public => legacy/public}/images/proxies.jpg | Bin {public => legacy/public}/images/proxies2.jpg | Bin .../public}/images/reading-nook.jpg | Bin .../reading/brandon-sanderson-cosmere.jpg | Bin .../public}/images/reading/desolate-era.jpg | Bin ...uglas-preston-lincoln-child-pendergast.jpg | Bin .../reading/emily-rodda-deltora-quest.jpg | Bin .../images/reading/frank-herbert-dune.jpg | Bin .../j-r-r-tolkien-the-lord-of-the-rings.jpg | Bin .../reading/omniscient-reader-s-viewpoint.jpg | Bin .../public}/images/reading/overgeared.jpg | Bin .../public}/images/reading/solo-leveling.jpg | Bin .../images/reading/the-king-s-avatar.jpg | Bin .../public}/images/resin_rings.jpg | Bin .../store/cards/PXL_20260806_204112875.jpg | Bin .../store/cards/PXL_20260806_204116184.jpg | Bin .../store/cards/PXL_20260806_204128368.jpg | Bin .../store/cards/PXL_20260806_204129979.jpg | Bin .../public}/images/store/cards/disclosure.jpg | Bin .../store/cards/foil_cloud_measured.jpg | Bin .../public}/images/store/cards/ghalta_duo.jpg | Bin .../public}/images/store/cards/proxies.jpg | Bin .../public}/images/store/cards/proxies2.jpg | Bin .../images/store/cards/ravenous_duo.jpg | Bin .../public}/images/store/cards/sleeved.jpg | Bin .../images/store/deckboxes/black_front.jpg | Bin .../images/store/deckboxes/black_side.jpg | Bin .../images/store/deckboxes/ff_back.jpg | Bin .../images/store/deckboxes/ff_front.jpg | Bin .../images/store/deckboxes/ff_open.jpg | Bin .../images/store/deckboxes/ff_side.jpg | Bin .../public}/images/tea-gaiwan.jpg | Bin .../public}/images/ttrpg-boardgame-shelf.jpg | Bin .../public}/images/ttrpg-dice.jpg | Bin .../public}/images/ttrpg-mini.jpg | Bin .../public}/images/ttrpg-table - Copy.jpg | Bin .../public}/images/ttrpg-table.jpg | Bin {public => legacy/public}/images/tv-hero.jpg | Bin .../public}/images/tv/30-rock.jpg | Bin .../public}/images/tv/arcane.jpg | Bin .../public}/images/tv/breaking-bad.jpg | Bin .../public}/images/tv/brooklyn-nine-nine.jpg | Bin .../public}/images/tv/castlevania.jpg | Bin .../public}/images/tv/cobra-kai.jpg | Bin .../public}/images/tv/daredevil.jpg | Bin .../public}/images/tv/game-of-thrones.jpg | Bin {public => legacy/public}/images/tv/house.jpg | Bin .../public}/images/tv/invincible.jpg | Bin {public => legacy/public}/images/tv/ozark.jpg | Bin .../images/tv/parks-and-recreation.jpg | Bin .../public}/images/tv/peaky-blinders.jpg | Bin .../public}/images/tv/succession.jpg | Bin {public => legacy/public}/images/tv/suits.jpg | Bin .../public}/images/tv/the-bear.jpg | Bin .../public}/images/tv/the-crown.jpg | Bin .../tv/the-great-british-baking-show.jpg | Bin .../public}/images/tv/the-mandalorian.jpg | Bin .../public}/images/tv/the-queens-gambit.jpg | Bin .../public}/images/tv/the-sopranos.jpg | Bin .../public}/images/tv/the-wire.jpg | Bin .../public}/images/tv/wandavision.jpg | Bin .../public}/images/xmasgifts.jpg | Bin .../public}/motifs/astrolabe.svg | 0 .../public}/motifs/craft-seal-print-light.svg | 0 .../public}/motifs/kepler-nest-seal-light.svg | 0 .../public}/motifs/layered-seal.svg | 0 .../motifs/squared-circle-seal-light.svg | 0 .../motifs/transmutation-seal-light.svg | 0 .../motifs/tria-prima-array-seal-light.svg | 0 .../public}/motifs/zodiac-seal-light.svg | 0 {public => legacy/public}/robots.txt | 0 {public => legacy/public}/site.webmanifest | 0 .../scripts}/fetch-anime-art.mjs | 0 .../scripts}/fetch-bgg-covers.mjs | 0 .../scripts}/fetch-book-covers.mjs | 0 {scripts => legacy/scripts}/fetch-box-art.mjs | 0 .../scripts}/fetch-tmdb-art.mjs | 0 {src => legacy/src}/components/Icon.astro | 0 .../src}/components/ObfuscatedEmail.astro | 0 .../src}/components/brand/BrandIcon.tsx | 0 .../src}/components/brand/Motif.astro | 0 .../src}/components/brand/PageSeal.astro | 0 .../src}/components/cards/DeckCard.astro | 0 .../src}/components/cards/GameCard.astro | 0 .../src}/components/cards/InterestCard.astro | 0 .../src}/components/cards/MediaCard.astro | 0 .../components/hero/AnimatedBackground.tsx | 0 .../src}/components/layout/Footer.astro | 0 .../src}/components/layout/InterestHero.astro | 0 .../src}/components/layout/Placeholder.astro | 0 .../components/layout/RelatedInterests.astro | 0 .../components/layout/SectionHeader.astro | 0 .../components/layout/SplitTransition.astro | 0 .../src}/components/media/CategoryList.astro | 0 .../src}/components/motion/Reveal.astro | 0 .../components/mystic/AlchemicalDivider.astro | 0 .../components/mystic/CelestialFrame.astro | 0 .../components/mystic/CelestialImage.astro | 0 .../src}/components/mystic/CircuitField.tsx | 0 .../components/mystic/ConstellationField.tsx | 0 .../src}/components/mystic/StarBurst.astro | 0 .../src}/components/mystic/StarField.astro | 0 .../src}/components/nav/MobileMenu.tsx | 0 .../src}/components/nav/NavBar.astro | 0 .../src}/components/nav/ThemeToggle.tsx | 0 .../professional/Certifications.astro | 0 .../components/professional/ContactForm.tsx | 0 .../professional/EducationHistory.astro | 0 .../components/professional/ProjectCard.astro | 0 .../components/professional/SectionNav.astro | 0 .../components/professional/SkillGrid.astro | 0 .../components/professional/Timeline.astro | 0 .../src}/components/store/Carousel.tsx | 0 .../src}/components/store/ColorGrid.astro | 0 .../src}/components/store/OrderForm.tsx | 0 .../src}/components/store/PricingTable.astro | 0 .../src}/components/store/ProductCard.astro | 0 .../src}/components/store/ProductTile.astro | 0 .../components/store/RequirementList.astro | 0 .../src}/components/store/SlotMeter.astro | 0 .../src}/components/store/StoreNav.astro | 0 .../src}/components/ttrpgs/BoardGameShelf.tsx | 0 {src => legacy/src}/content.config.ts | 0 .../src}/content/experience/comcast_1.md | 0 .../src}/content/experience/drexel.md | 0 .../src}/content/experience/implico.md | 0 .../games/hall-of-fame/age-of-empires.md | 0 .../hall-of-fame/ark-survival-evolved.md | 0 .../games/hall-of-fame/dave-the-diver.md | 0 .../content/games/hall-of-fame/elden-ring.md | 0 .../games/hall-of-fame/granblue-relink.md | 0 .../src}/content/games/hall-of-fame/kotor.md | 0 .../content/games/hall-of-fame/minecraft.md | 0 .../games/hall-of-fame/pokemon-crystal.md | 0 .../games/hall-of-fame/satisfactory.md | 0 .../content/games/hall-of-fame/sly-cooper.md | 0 .../games/hall-of-fame/stardew-valley.md | 0 .../src}/content/games/now-playing.yaml | 0 .../src}/content/projects/carrier-connect.md | 0 .../src}/content/projects/cosmos-homelab.md | 0 .../src}/content/projects/driver-app.md | 0 .../content/projects/placeholder-project.md | 0 {src => legacy/src}/data/boardgames.json | 0 {src => legacy/src}/data/media/anime.json | 0 {src => legacy/src}/data/media/movies.json | 0 {src => legacy/src}/data/media/tv.json | 0 {src => legacy/src}/data/reading.json | 0 .../src}/data/store-availability.json | 0 .../src}/data/store-availability.ts | 0 {src => legacy/src}/data/store-products.ts | 0 {src => legacy/src}/env.d.ts | 0 {src => legacy/src}/layouts/BaseLayout.astro | 0 {src => legacy/src}/layouts/StoreLayout.astro | 0 {src => legacy/src}/lib/archidekt.ts | 0 {src => legacy/src}/lib/brand-icons.ts | 0 {src => legacy/src}/lib/env.ts | 0 {src => legacy/src}/lib/media.ts | 0 {src => legacy/src}/lib/motion.ts | 0 {src => legacy/src}/lib/platforms.ts | 0 {src => legacy/src}/lib/site.ts | 0 {src => legacy/src}/middleware.ts | 0 {src => legacy/src}/pages/about.astro | 0 {src => legacy/src}/pages/api/contact.ts | 0 {src => legacy/src}/pages/api/order.ts | 0 {src => legacy/src}/pages/contact.astro | 0 {src => legacy/src}/pages/index.astro | 0 .../src}/pages/interests/anime.astro | 0 .../src}/pages/interests/gaming.astro | 0 .../src}/pages/interests/index.astro | 0 .../src}/pages/interests/making.astro | 0 .../src}/pages/interests/movies.astro | 0 .../src}/pages/interests/pokemon.astro | 0 .../src}/pages/interests/reading.astro | 0 .../src}/pages/interests/tcgs.astro | 0 .../src}/pages/interests/ttrpgs.astro | 0 {src => legacy/src}/pages/interests/tv.astro | 0 .../src}/pages/professional/index.astro | 0 {src => legacy/src}/pages/store/cards.astro | 0 .../src}/pages/store/deck-boxes.astro | 0 {src => legacy/src}/pages/store/index.astro | 0 {src => legacy/src}/pages/store/order.astro | 0 {src => legacy/src}/scripts/reveal-init.ts | 0 {src => legacy/src}/scripts/theme-init.ts | 0 {src => legacy/src}/styles/animations.css | 0 {src => legacy/src}/styles/global.css | 0 {src => legacy/src}/styles/themes.css | 0 tsconfig.json => legacy/tsconfig.json | 0 package-lock.json | 7967 ++-------------- package.json | 56 +- packages/schema/package.json | 18 + packages/schema/scripts/emit-json-schema.ts | 24 + packages/schema/scripts/emit-templates.ts | 9 + packages/schema/src/fields.ts | 97 + packages/schema/src/index.ts | 6 + packages/schema/src/names.ts | 64 + packages/schema/src/notes.ts | 112 + packages/schema/src/templates.ts | 60 + packages/schema/src/types.ts | 508 + packages/schema/src/vault.ts | 75 + packages/schema/test/schema.test.ts | 89 + packages/schema/tsconfig.json | 5 + tools/migrate/package.json | 15 + tools/migrate/src/build.ts | 402 + tools/migrate/src/cli.ts | 73 + tools/migrate/src/seed.ts | 278 + tools/migrate/test/migrate.test.ts | 62 + tools/migrate/tsconfig.json | 4 + tsconfig.base.json | 15 + vault-kit/24.01_PersonalWebsite/Site guide.md | 19 + .../Site/_templates/Board game.md | 14 + .../Site/_templates/Campaign.md | 13 + .../Site/_templates/Card game.md | 9 + .../Site/_templates/Craft.md | 13 + .../Site/_templates/Deck.md | 11 + .../Site/_templates/Experience.md | 16 + .../Site/_templates/Library item.md | 18 + .../Site/_templates/Make.md | 12 + .../Site/_templates/Page text.md | 10 + .../Site/_templates/Post.md | 17 + .../Site/_templates/Product line.md | 18 + .../Site/_templates/Station.md | 9 + .../Site/_templates/Status.md | 11 + .../Site/_templates/Tabletop system.md | 9 + .../Site/_templates/Tool or material.md | 9 + vitest.config.ts | 5 + 324 files changed, 11538 insertions(+), 7556 deletions(-) create mode 100644 docs/content-model.md rename .dockerignore => legacy/.dockerignore (100%) rename .env.example => legacy/.env.example (100%) create mode 100644 legacy/.gitignore rename {.zcode => legacy/.zcode}/plans/plan-sess_cfbaea37-9611-497f-804c-b438e5e659b3.md (100%) rename Dockerfile => legacy/Dockerfile (100%) create mode 100644 legacy/LEGACY.md rename Profile_Website_Spec.md => legacy/Profile_Website_Spec.md (100%) create mode 100644 legacy/README.md rename astro.config.mjs => legacy/astro.config.mjs (100%) rename docker-compose.yml => legacy/docker-compose.yml (100%) rename {docs => legacy/docs}/CONTENT.md (100%) rename {docs => legacy/docs}/DEPLOYMENT.md (100%) rename {docs => legacy/docs}/PHOTOS.md (100%) rename {docs => legacy/docs}/STORE.md (100%) rename {docs => legacy/docs}/THEMING.md (100%) create mode 100644 legacy/package-lock.json create mode 100644 legacy/package.json rename {public => legacy/public}/.well-known/security.txt (100%) rename {public => legacy/public}/apple-touch-icon.png (100%) rename {public => legacy/public}/brand/patiun-seal.svg (100%) rename {public => legacy/public}/favicon-16.png (100%) rename {public => legacy/public}/favicon-32.png (100%) rename {public => legacy/public}/favicon-48.png (100%) rename {public => legacy/public}/favicon.ico (100%) rename {public => legacy/public}/favicon.svg (100%) rename {public => legacy/public}/icon-192.png (100%) rename {public => legacy/public}/icon-512.png (100%) rename {public => legacy/public}/icon-maskable-192.png (100%) rename {public => legacy/public}/icon-maskable-512.png (100%) rename {public => legacy/public}/icon_2.png (100%) rename {public => legacy/public}/images/OLDgaming-setup.jpg (100%) rename {public => legacy/public}/images/OLDhero-portrait.jpg (100%) rename {public => legacy/public}/images/OLDpro-workspace.jpg (100%) rename {public => legacy/public}/images/about-portrait.jpg (100%) rename {public => legacy/public}/images/anime-hero.jpg (100%) rename {public => legacy/public}/images/anime/attack-on-titan.jpg (100%) rename {public => legacy/public}/images/anime/bleach.jpg (100%) rename {public => legacy/public}/images/anime/code-geass.jpg (100%) rename {public => legacy/public}/images/anime/demon-slayer.jpg (100%) rename {public => legacy/public}/images/anime/frieren-beyond-journeys-end.jpg (100%) rename {public => legacy/public}/images/anime/gundam-all-series.jpg (100%) rename {public => legacy/public}/images/anime/jujutsu-kaisen.jpg (100%) rename {public => legacy/public}/images/anime/kimetsu-no-yaiba.jpg (100%) rename {public => legacy/public}/images/anime/magi-the-labyrinth-of-magic.jpg (100%) rename {public => legacy/public}/images/anime/my-hero-academia.jpg (100%) rename {public => legacy/public}/images/anime/one-piece.jpg (100%) rename {public => legacy/public}/images/anime/that-time-i-got-reincarnated-as-a-slime.jpg (100%) rename {public => legacy/public}/images/anime/vinland-saga.jpg (100%) rename {public => legacy/public}/images/boardgames/.gitkeep (100%) rename {public => legacy/public}/images/boardgames/wyrmspan.jpg (100%) rename {public => legacy/public}/images/cards.jpg (100%) rename {public => legacy/public}/images/deckboxes-example-1.svg (100%) rename {public => legacy/public}/images/deckboxes-example-2.svg (100%) rename {public => legacy/public}/images/deckboxes.jpg (100%) rename {public => legacy/public}/images/dice_galaxy.jpg (100%) rename {public => legacy/public}/images/dice_golden_moss.jpg (100%) rename {public => legacy/public}/images/dice_scarlet.jpg (100%) rename {public => legacy/public}/images/dice_seastone.jpg (100%) rename {public => legacy/public}/images/fantasy-books.jpg (100%) rename {public => legacy/public}/images/fav.png (100%) rename {public => legacy/public}/images/games/.gitkeep (100%) rename {public => legacy/public}/images/games/age-of-empires.jpg (100%) rename {public => legacy/public}/images/games/ark-survival-evolved.jpg (100%) rename {public => legacy/public}/images/games/dave-the-diver.jpg (100%) rename {public => legacy/public}/images/games/elden-ring.jpg (100%) rename {public => legacy/public}/images/games/granblue-relink.jpg (100%) rename {public => legacy/public}/images/games/minecraft.jpg (100%) rename {public => legacy/public}/images/games/pok-mon-crystal-version.jpg (100%) rename {public => legacy/public}/images/games/satisfactory.jpg (100%) rename {public => legacy/public}/images/games/sly-3-honor-among-thieves.jpg (100%) rename {public => legacy/public}/images/games/sly-cooper-and-the-thievius-raccoonus.jpg (100%) rename {public => legacy/public}/images/games/star-wars-knights-of-the-old-republic.jpg (100%) rename {public => legacy/public}/images/games/stardew-valley.jpg (100%) rename {public => legacy/public}/images/gaming-setup.jpg (100%) rename {public => legacy/public}/images/hero-portrait.jpg (100%) rename {public => legacy/public}/images/kashi_at_work.jpg (100%) rename {public => legacy/public}/images/making-print - Copy.jpg (100%) rename {public => legacy/public}/images/making-print.jpg (100%) rename {public => legacy/public}/images/making-resin.jpg (100%) rename {public => legacy/public}/images/minis.jpg (100%) rename {public => legacy/public}/images/movies-hero.jpg (100%) rename {public => legacy/public}/images/movies/avengers-endgame.jpg (100%) rename {public => legacy/public}/images/movies/avengers-infinity-war.jpg (100%) rename {public => legacy/public}/images/movies/contact.jpg (100%) rename {public => legacy/public}/images/movies/howls-moving-castle.jpg (100%) rename {public => legacy/public}/images/movies/jurassic-park.jpg (100%) rename {public => legacy/public}/images/movies/oceans-eleven.jpg (100%) rename {public => legacy/public}/images/movies/star-wars-episode-v-the-empire-strikes-back.jpg (100%) rename {public => legacy/public}/images/movies/the-matrix.jpg (100%) rename {public => legacy/public}/images/movies/the-princess-bride.jpg (100%) rename {public => legacy/public}/images/movies/transformers.jpg (100%) rename {public => legacy/public}/images/pokemon-cards.jpg (100%) rename {public => legacy/public}/images/pro-team.jpg (100%) rename {public => legacy/public}/images/pro-workspace.jpg (100%) rename {public => legacy/public}/images/proxies-example-1.svg (100%) rename {public => legacy/public}/images/proxies-example-2.svg (100%) rename {public => legacy/public}/images/proxies.jpg (100%) rename {public => legacy/public}/images/proxies2.jpg (100%) rename {public => legacy/public}/images/reading-nook.jpg (100%) rename {public => legacy/public}/images/reading/brandon-sanderson-cosmere.jpg (100%) rename {public => legacy/public}/images/reading/desolate-era.jpg (100%) rename {public => legacy/public}/images/reading/douglas-preston-lincoln-child-pendergast.jpg (100%) rename {public => legacy/public}/images/reading/emily-rodda-deltora-quest.jpg (100%) rename {public => legacy/public}/images/reading/frank-herbert-dune.jpg (100%) rename {public => legacy/public}/images/reading/j-r-r-tolkien-the-lord-of-the-rings.jpg (100%) rename {public => legacy/public}/images/reading/omniscient-reader-s-viewpoint.jpg (100%) rename {public => legacy/public}/images/reading/overgeared.jpg (100%) rename {public => legacy/public}/images/reading/solo-leveling.jpg (100%) rename {public => legacy/public}/images/reading/the-king-s-avatar.jpg (100%) rename {public => legacy/public}/images/resin_rings.jpg (100%) rename {public => legacy/public}/images/store/cards/PXL_20260806_204112875.jpg (100%) rename {public => legacy/public}/images/store/cards/PXL_20260806_204116184.jpg (100%) rename {public => legacy/public}/images/store/cards/PXL_20260806_204128368.jpg (100%) rename {public => legacy/public}/images/store/cards/PXL_20260806_204129979.jpg (100%) rename {public => legacy/public}/images/store/cards/disclosure.jpg (100%) rename {public => legacy/public}/images/store/cards/foil_cloud_measured.jpg (100%) rename {public => legacy/public}/images/store/cards/ghalta_duo.jpg (100%) rename {public => legacy/public}/images/store/cards/proxies.jpg (100%) rename {public => legacy/public}/images/store/cards/proxies2.jpg (100%) rename {public => legacy/public}/images/store/cards/ravenous_duo.jpg (100%) rename {public => legacy/public}/images/store/cards/sleeved.jpg (100%) rename {public => legacy/public}/images/store/deckboxes/black_front.jpg (100%) rename {public => legacy/public}/images/store/deckboxes/black_side.jpg (100%) rename {public => legacy/public}/images/store/deckboxes/ff_back.jpg (100%) rename {public => legacy/public}/images/store/deckboxes/ff_front.jpg (100%) rename {public => legacy/public}/images/store/deckboxes/ff_open.jpg (100%) rename {public => legacy/public}/images/store/deckboxes/ff_side.jpg (100%) rename {public => legacy/public}/images/tea-gaiwan.jpg (100%) rename {public => legacy/public}/images/ttrpg-boardgame-shelf.jpg (100%) rename {public => legacy/public}/images/ttrpg-dice.jpg (100%) rename {public => legacy/public}/images/ttrpg-mini.jpg (100%) rename {public => legacy/public}/images/ttrpg-table - Copy.jpg (100%) rename {public => legacy/public}/images/ttrpg-table.jpg (100%) rename {public => legacy/public}/images/tv-hero.jpg (100%) rename {public => legacy/public}/images/tv/30-rock.jpg (100%) rename {public => legacy/public}/images/tv/arcane.jpg (100%) rename {public => legacy/public}/images/tv/breaking-bad.jpg (100%) rename {public => legacy/public}/images/tv/brooklyn-nine-nine.jpg (100%) rename {public => legacy/public}/images/tv/castlevania.jpg (100%) rename {public => legacy/public}/images/tv/cobra-kai.jpg (100%) rename {public => legacy/public}/images/tv/daredevil.jpg (100%) rename {public => legacy/public}/images/tv/game-of-thrones.jpg (100%) rename {public => legacy/public}/images/tv/house.jpg (100%) rename {public => legacy/public}/images/tv/invincible.jpg (100%) rename {public => legacy/public}/images/tv/ozark.jpg (100%) rename {public => legacy/public}/images/tv/parks-and-recreation.jpg (100%) rename {public => legacy/public}/images/tv/peaky-blinders.jpg (100%) rename {public => legacy/public}/images/tv/succession.jpg (100%) rename {public => legacy/public}/images/tv/suits.jpg (100%) rename {public => legacy/public}/images/tv/the-bear.jpg (100%) rename {public => legacy/public}/images/tv/the-crown.jpg (100%) rename {public => legacy/public}/images/tv/the-great-british-baking-show.jpg (100%) rename {public => legacy/public}/images/tv/the-mandalorian.jpg (100%) rename {public => legacy/public}/images/tv/the-queens-gambit.jpg (100%) rename {public => legacy/public}/images/tv/the-sopranos.jpg (100%) rename {public => legacy/public}/images/tv/the-wire.jpg (100%) rename {public => legacy/public}/images/tv/wandavision.jpg (100%) rename {public => legacy/public}/images/xmasgifts.jpg (100%) rename {public => legacy/public}/motifs/astrolabe.svg (100%) rename {public => legacy/public}/motifs/craft-seal-print-light.svg (100%) rename {public => legacy/public}/motifs/kepler-nest-seal-light.svg (100%) rename {public => legacy/public}/motifs/layered-seal.svg (100%) rename {public => legacy/public}/motifs/squared-circle-seal-light.svg (100%) rename {public => legacy/public}/motifs/transmutation-seal-light.svg (100%) rename {public => legacy/public}/motifs/tria-prima-array-seal-light.svg (100%) rename {public => legacy/public}/motifs/zodiac-seal-light.svg (100%) rename {public => legacy/public}/robots.txt (100%) rename {public => legacy/public}/site.webmanifest (100%) rename {scripts => legacy/scripts}/fetch-anime-art.mjs (100%) rename {scripts => legacy/scripts}/fetch-bgg-covers.mjs (100%) rename {scripts => legacy/scripts}/fetch-book-covers.mjs (100%) rename {scripts => legacy/scripts}/fetch-box-art.mjs (100%) rename {scripts => legacy/scripts}/fetch-tmdb-art.mjs (100%) rename {src => legacy/src}/components/Icon.astro (100%) rename {src => legacy/src}/components/ObfuscatedEmail.astro (100%) rename {src => legacy/src}/components/brand/BrandIcon.tsx (100%) rename {src => legacy/src}/components/brand/Motif.astro (100%) rename {src => legacy/src}/components/brand/PageSeal.astro (100%) rename {src => legacy/src}/components/cards/DeckCard.astro (100%) rename {src => legacy/src}/components/cards/GameCard.astro (100%) rename {src => legacy/src}/components/cards/InterestCard.astro (100%) rename {src => legacy/src}/components/cards/MediaCard.astro (100%) rename {src => legacy/src}/components/hero/AnimatedBackground.tsx (100%) rename {src => legacy/src}/components/layout/Footer.astro (100%) rename {src => legacy/src}/components/layout/InterestHero.astro (100%) rename {src => legacy/src}/components/layout/Placeholder.astro (100%) rename {src => legacy/src}/components/layout/RelatedInterests.astro (100%) rename {src => legacy/src}/components/layout/SectionHeader.astro (100%) rename {src => legacy/src}/components/layout/SplitTransition.astro (100%) rename {src => legacy/src}/components/media/CategoryList.astro (100%) rename {src => legacy/src}/components/motion/Reveal.astro (100%) rename {src => legacy/src}/components/mystic/AlchemicalDivider.astro (100%) rename {src => legacy/src}/components/mystic/CelestialFrame.astro (100%) rename {src => legacy/src}/components/mystic/CelestialImage.astro (100%) rename {src => legacy/src}/components/mystic/CircuitField.tsx (100%) rename {src => legacy/src}/components/mystic/ConstellationField.tsx (100%) rename {src => legacy/src}/components/mystic/StarBurst.astro (100%) rename {src => legacy/src}/components/mystic/StarField.astro (100%) rename {src => legacy/src}/components/nav/MobileMenu.tsx (100%) rename {src => legacy/src}/components/nav/NavBar.astro (100%) rename {src => legacy/src}/components/nav/ThemeToggle.tsx (100%) rename {src => legacy/src}/components/professional/Certifications.astro (100%) rename {src => legacy/src}/components/professional/ContactForm.tsx (100%) rename {src => legacy/src}/components/professional/EducationHistory.astro (100%) rename {src => legacy/src}/components/professional/ProjectCard.astro (100%) rename {src => legacy/src}/components/professional/SectionNav.astro (100%) rename {src => legacy/src}/components/professional/SkillGrid.astro (100%) rename {src => legacy/src}/components/professional/Timeline.astro (100%) rename {src => legacy/src}/components/store/Carousel.tsx (100%) rename {src => legacy/src}/components/store/ColorGrid.astro (100%) rename {src => legacy/src}/components/store/OrderForm.tsx (100%) rename {src => legacy/src}/components/store/PricingTable.astro (100%) rename {src => legacy/src}/components/store/ProductCard.astro (100%) rename {src => legacy/src}/components/store/ProductTile.astro (100%) rename {src => legacy/src}/components/store/RequirementList.astro (100%) rename {src => legacy/src}/components/store/SlotMeter.astro (100%) rename {src => legacy/src}/components/store/StoreNav.astro (100%) rename {src => legacy/src}/components/ttrpgs/BoardGameShelf.tsx (100%) rename {src => legacy/src}/content.config.ts (100%) rename {src => legacy/src}/content/experience/comcast_1.md (100%) rename {src => legacy/src}/content/experience/drexel.md (100%) rename {src => legacy/src}/content/experience/implico.md (100%) rename {src => legacy/src}/content/games/hall-of-fame/age-of-empires.md (100%) rename {src => legacy/src}/content/games/hall-of-fame/ark-survival-evolved.md (100%) rename {src => legacy/src}/content/games/hall-of-fame/dave-the-diver.md (100%) rename {src => legacy/src}/content/games/hall-of-fame/elden-ring.md (100%) rename {src => legacy/src}/content/games/hall-of-fame/granblue-relink.md (100%) rename {src => legacy/src}/content/games/hall-of-fame/kotor.md (100%) rename {src => legacy/src}/content/games/hall-of-fame/minecraft.md (100%) rename {src => legacy/src}/content/games/hall-of-fame/pokemon-crystal.md (100%) rename {src => legacy/src}/content/games/hall-of-fame/satisfactory.md (100%) rename {src => legacy/src}/content/games/hall-of-fame/sly-cooper.md (100%) rename {src => legacy/src}/content/games/hall-of-fame/stardew-valley.md (100%) rename {src => legacy/src}/content/games/now-playing.yaml (100%) rename {src => legacy/src}/content/projects/carrier-connect.md (100%) rename {src => legacy/src}/content/projects/cosmos-homelab.md (100%) rename {src => legacy/src}/content/projects/driver-app.md (100%) rename {src => legacy/src}/content/projects/placeholder-project.md (100%) rename {src => legacy/src}/data/boardgames.json (100%) rename {src => legacy/src}/data/media/anime.json (100%) rename {src => legacy/src}/data/media/movies.json (100%) rename {src => legacy/src}/data/media/tv.json (100%) rename {src => legacy/src}/data/reading.json (100%) rename {src => legacy/src}/data/store-availability.json (100%) rename {src => legacy/src}/data/store-availability.ts (100%) rename {src => legacy/src}/data/store-products.ts (100%) rename {src => legacy/src}/env.d.ts (100%) rename {src => legacy/src}/layouts/BaseLayout.astro (100%) rename {src => legacy/src}/layouts/StoreLayout.astro (100%) rename {src => legacy/src}/lib/archidekt.ts (100%) rename {src => legacy/src}/lib/brand-icons.ts (100%) rename {src => legacy/src}/lib/env.ts (100%) rename {src => legacy/src}/lib/media.ts (100%) rename {src => legacy/src}/lib/motion.ts (100%) rename {src => legacy/src}/lib/platforms.ts (100%) rename {src => legacy/src}/lib/site.ts (100%) rename {src => legacy/src}/middleware.ts (100%) rename {src => legacy/src}/pages/about.astro (100%) rename {src => legacy/src}/pages/api/contact.ts (100%) rename {src => legacy/src}/pages/api/order.ts (100%) rename {src => legacy/src}/pages/contact.astro (100%) rename {src => legacy/src}/pages/index.astro (100%) rename {src => legacy/src}/pages/interests/anime.astro (100%) rename {src => legacy/src}/pages/interests/gaming.astro (100%) rename {src => legacy/src}/pages/interests/index.astro (100%) rename {src => legacy/src}/pages/interests/making.astro (100%) rename {src => legacy/src}/pages/interests/movies.astro (100%) rename {src => legacy/src}/pages/interests/pokemon.astro (100%) rename {src => legacy/src}/pages/interests/reading.astro (100%) rename {src => legacy/src}/pages/interests/tcgs.astro (100%) rename {src => legacy/src}/pages/interests/ttrpgs.astro (100%) rename {src => legacy/src}/pages/interests/tv.astro (100%) rename {src => legacy/src}/pages/professional/index.astro (100%) rename {src => legacy/src}/pages/store/cards.astro (100%) rename {src => legacy/src}/pages/store/deck-boxes.astro (100%) rename {src => legacy/src}/pages/store/index.astro (100%) rename {src => legacy/src}/pages/store/order.astro (100%) rename {src => legacy/src}/scripts/reveal-init.ts (100%) rename {src => legacy/src}/scripts/theme-init.ts (100%) rename {src => legacy/src}/styles/animations.css (100%) rename {src => legacy/src}/styles/global.css (100%) rename {src => legacy/src}/styles/themes.css (100%) rename tsconfig.json => legacy/tsconfig.json (100%) create mode 100644 packages/schema/package.json create mode 100644 packages/schema/scripts/emit-json-schema.ts create mode 100644 packages/schema/scripts/emit-templates.ts create mode 100644 packages/schema/src/fields.ts create mode 100644 packages/schema/src/index.ts create mode 100644 packages/schema/src/names.ts create mode 100644 packages/schema/src/notes.ts create mode 100644 packages/schema/src/templates.ts create mode 100644 packages/schema/src/types.ts create mode 100644 packages/schema/src/vault.ts create mode 100644 packages/schema/test/schema.test.ts create mode 100644 packages/schema/tsconfig.json create mode 100644 tools/migrate/package.json create mode 100644 tools/migrate/src/build.ts create mode 100644 tools/migrate/src/cli.ts create mode 100644 tools/migrate/src/seed.ts create mode 100644 tools/migrate/test/migrate.test.ts create mode 100644 tools/migrate/tsconfig.json create mode 100644 tsconfig.base.json create mode 100644 vault-kit/24.01_PersonalWebsite/Site guide.md create mode 100644 vault-kit/24.01_PersonalWebsite/Site/_templates/Board game.md create mode 100644 vault-kit/24.01_PersonalWebsite/Site/_templates/Campaign.md create mode 100644 vault-kit/24.01_PersonalWebsite/Site/_templates/Card game.md create mode 100644 vault-kit/24.01_PersonalWebsite/Site/_templates/Craft.md create mode 100644 vault-kit/24.01_PersonalWebsite/Site/_templates/Deck.md create mode 100644 vault-kit/24.01_PersonalWebsite/Site/_templates/Experience.md create mode 100644 vault-kit/24.01_PersonalWebsite/Site/_templates/Library item.md create mode 100644 vault-kit/24.01_PersonalWebsite/Site/_templates/Make.md create mode 100644 vault-kit/24.01_PersonalWebsite/Site/_templates/Page text.md create mode 100644 vault-kit/24.01_PersonalWebsite/Site/_templates/Post.md create mode 100644 vault-kit/24.01_PersonalWebsite/Site/_templates/Product line.md create mode 100644 vault-kit/24.01_PersonalWebsite/Site/_templates/Station.md create mode 100644 vault-kit/24.01_PersonalWebsite/Site/_templates/Status.md create mode 100644 vault-kit/24.01_PersonalWebsite/Site/_templates/Tabletop system.md create mode 100644 vault-kit/24.01_PersonalWebsite/Site/_templates/Tool or material.md create mode 100644 vitest.config.ts diff --git a/.gitignore b/.gitignore index 5c155e1..226df75 100644 --- a/.gitignore +++ b/.gitignore @@ -1,30 +1,21 @@ -# build output -dist/ -.output/ - # dependencies node_modules/ -# generated types -.astro/ +# migration output (it goes into the vault, not the repo) +out/ + +# generated from the schema +packages/schema/json/ # environment .env .env.* !.env.example -# logs +# logs and editor files npm-debug.log* -yarn-debug.log* -yarn-error.log* -pnpm-debug.log* - -# macOS / editor .DS_Store .vscode/* !.vscode/extensions.json !.vscode/settings.json .idea/ - -# misc -*.local diff --git a/README.md b/README.md index 3cdaf10..bccf14d 100644 --- a/README.md +++ b/README.md @@ -1,156 +1,41 @@ -# Greg Kilmer — Profile & Portfolio Site +# GregKilmer.com -A responsive, content-driven personal site for Greg Kilmer, built as **two wings -of the same house**: a polished professional resume/portfolio on one side, and a -warm, personality-driven interests hub on the other. +The site, rebuilt around Greg's Obsidian vault. Notes in the vault's Site +folder are the content; a content service checks every change and keeps the +last good version; the site serves pages from that service. The full plan, +with the decisions behind it, is the +[Functional Design Plan](https://claude.ai/code/artifact/2a4e8339-7887-4919-a51d-2c11547b80b3). -Built with **Astro 7 + React Islands + TypeScript**, served by a **Node server** -with API routes, ready to deploy as a container on a homelab Cosmos system. +## What is here ---- +| Path | What it is | +| --- | --- | +| `packages/schema` | The one definition of every note type: fields, folders, file names, and the checks for one note and for the whole folder. The migration, the content service, the MCP server, the site and any later editing tool all use it. | +| `vault-kit` | What goes into the vault: the Obsidian templates (generated from the schema) and the Site guide. | +| `tools/migrate` | The one-time migration from the old site's data into Site folder notes. | +| `docs` | How the pieces fit. Start with [docs/content-model.md](docs/content-model.md). | +| `legacy` | The old site, kept for reference only. Nothing outside it imports from it except the migration. | -## Highlights +Coming in later phases: `services/content` (the content service and its MCP +server), the admin page, and the new site. -- **Astro 7** static-first core with React Islands for interactivity (mobile - menu, theme toggle, animated hero background, contact form). -- **Node backend** (`@astrojs/node`, standalone) with a typed `/api/contact` - endpoint (SMTP via nodemailer + webhook fallback, Zod validation, rate limit). -- **Section-aware theming**: the Professional wing reads dark/polished, the - Interests wing reads warm/inviting, plus a global light/dark toggle. -- **Rich motion**: animated mesh-gradient hero with an optional canvas particle - field, IntersectionObserver scroll reveals, View Transitions page swaps, - scroll-triggered nav blur. All `prefers-reduced-motion` aware. -- **Content Collections** (typed): add projects and experience by dropping - markdown/MDX files. -- **mdi icons everywhere** (no emojis, per the spec), inlined at build time from - `@iconify-json/mdi` for zero runtime cost. -- **Self-hosted variable fonts** (Inter, Plus Jakarta Sans, JetBrains Mono). -- **Accessible**: semantic landmarks, skip link, keyboard-operable menu, - focus-visible rings, ARIA on interactive bits, WCAG-AA contrast targets. -- **Deploy artifacts**: multi-stage `Dockerfile` + `docker-compose.yml`. +## Commands ---- +Node 22 or newer. -## Quickstart - -```bash -# 1. Install dependencies (Node 20+) +```sh npm install - -# 2. Start the dev server with hot reload -npm run dev -# -> http://localhost:4321 - -# 3. Production build (runs astro check + build) -npm run build - -# 4. Run the built Node server -npm run serve -# -> http://localhost:4321 +npm run check # typecheck and tests +npm run migrate # writes out/24.01_PersonalWebsite and out/MIGRATION-REPORT.md +npm run templates # regenerates vault-kit templates after a schema change +npm run schema:json # writes a JSON Schema per note type to packages/schema/json/ ``` -Environment is optional for local dev. To enable contact-form delivery, copy -`.env.example` to `.env` and fill in SMTP or webhook vars (see -[docs/DEPLOYMENT.md](docs/DEPLOYMENT.md)). +## The old site ---- - -## Project structure +The live site still runs from the old code until cutover. It builds the same +way as before, from inside `legacy/`: +```sh +cd legacy && docker compose up -d --build ``` -src/ -├── components/ -│ ├── hero/ AnimatedBackground.tsx (gradient + canvas particles) -│ ├── motion/ Reveal.astro (IntersectionObserver scroll reveals) -│ ├── nav/ NavBar.astro, MobileMenu.tsx, ThemeToggle.tsx -│ ├── cards/ InterestCard.astro -│ ├── professional/ Timeline, SkillGrid, ProjectCard, SectionNav, ContactForm -│ ├── layout/ Footer, SectionHeader, SplitTransition, InterestHero, -│ │ RelatedInterests, Placeholder -│ └── Icon.astro Inline mdi icon renderer (build-time) -├── content/ -│ ├── projects/ Project collection entries (markdown/MDX) -│ └── experience/ Experience collection entries -├── layouts/ -│ └── BaseLayout.astro shell, head, fonts, theme init, nav, footer -├── pages/ -│ ├── index.astro Home (hero + snapshot grid) -│ ├── about.astro About + split transition fork -│ ├── professional/index.astro Single long page: Summary, Experience, -│ │ Skills, Projects, Education, Contact -│ ├── interests/ Hub + 5 sub-pages -│ │ ├── index.astro -│ │ ├── ttrpgs / gaming / reading / making / pokemon .astro -│ ├── contact.astro Two-column connect page + contact form -│ └── api/contact.ts POST endpoint -> SMTP / webhook -├── content.config.ts Collection schemas (projects, experience) -├── lib/ site.ts (nav/socials), motion.ts, content helpers -├── styles/ global.css, themes.css, animations.css -└── scripts/theme-init.ts No-FOUC theme boot (inlined in ) -``` - ---- - -## The two wings (theming model) - -Theming has two independent axes (see [docs/THEMING.md](docs/THEMING.md)): - -1. **Global light/dark** on ``, set before paint by an inline - script, persisted to `localStorage`, toggleable via the nav button. -2. **Section tone** via `data-surface` on ``: - - `neutral` (Home/About/Contact) — balanced middle ground - - `professional` — dark slate, polished - - `interest` — warm cream/amber, inviting - -Components only reference semantic CSS variables (`--surface`, `--text`, -`--accent`...), so changing either axis restyles everything automatically. - ---- - -## Adding content - -- **Projects / Experience:** drop a markdown or MDX file in - `src/content/projects/` or `src/content/experience/`. Schemas and field - reference are in [docs/CONTENT.md](docs/CONTENT.md). -- **Interest pages:** hand-built `.astro` files under `src/pages/interests/`, - each following the spec's template (Header, Overview, Highlights, Currently, - Related). Edit copy in place. -- **Placeholders:** every `[Greg to fill]` gap from the spec is a styled - `Placeholder` slot with a TODO badge so you can find and replace them. - ---- - -## Deployment (Cosmos homelab) - -The site runs as a Node server inside a container. Full walkthrough in -[docs/DEPLOYMENT.md](docs/DEPLOYMENT.md): - -```bash -docker compose up -d --build -# Then point your reverse proxy at port 4321 with TLS for gregkilmer.com. -``` - ---- - -## Scripts - -| Script | Description | -| ----------------- | ----------------------------------------------------- | -| `npm run dev` | Dev server with hot reload | -| `npm run build` | Type-check (`astro check`) then build the Node server | -| `npm run serve` | Run the built server (`dist/server/entry.mjs`) | -| `npm run preview` | Preview the build locally | -| `npm run check` | Type-check only | - ---- - -## Tech stack - -Astro 7, React 19, TypeScript 6, Tailwind CSS v4, Framer Motion, nodemailer, -Zod, `@iconify-json/mdi`, Fontsource variable fonts, `@astrojs/node` standalone. - ---- - -## License - -Personal site for Greg Kilmer. All rights reserved. diff --git a/docs/content-model.md b/docs/content-model.md new file mode 100644 index 0000000..63edde7 --- /dev/null +++ b/docs/content-model.md @@ -0,0 +1,48 @@ +# Content model + +Everything the site shows is a Markdown note with YAML frontmatter in the +vault at `20-29_Makerspace/24_Homelab_Infrastructure/24.01_PersonalWebsite/Site/`. +`24.02_PatiunWebsite` is reserved for the Patiun site later. + +## Rules every note follows + +- A note's `type` field says what it is. The types, their fields and their + folders are defined once, in `packages/schema/src/types.ts`. +- A note is live only when `publish: true`. Missing means false. +- Links between notes are Obsidian wikilinks, `[[Note name]]`, in frontmatter + or in the body. A link resolves by file name, so file names are unique across + the whole Site folder. +- Images live in `Site/_attachments/` and are linked the same way, + `[[galaxy-dice.jpg]]`, or are full https URLs. +- Folders starting with `_` hold templates and attachments, never notes. + Files starting with `_` are single settings notes: `_site.md`, + `Now/_kinds.md`, `Store/_settings.md`. +- Slugs come from the title unless a note sets `slug`. They are unique within + a type. + +## Note types + +| Type | Folder | Notes | +| --- | --- | --- | +| `post` | `Posts/` | Blog posts. `postType: case study` also appears on the Work page. | +| `library-item` | `Library//` | Books, light novels, webnovels, manga, manhwa, video games, anime, TV, movies. | +| `board-game` | `Shelf/` | The Shelf. Players and play time sync from BoardGameGeek by `bggId`. | +| `craft`, `station`, `tool`, `make` | `Workshop/…` | The Workshop. | +| `tabletop-system`, `campaign`, `card-game`, `deck` | `Tabletop/…` | Tabletop. Magic decks sync from Archidekt. | +| `experience`, `skills`, `education` | `Resume/…` | The resume page and its PDF. | +| `status`, `status-kinds` | `Now/` | The status log behind Now, the Landing strip and Now chips. | +| `product-line`, `store-settings` | `Store/…` | The Store. | +| `site-settings` | `_site.md` | Name, tagline, location and social links. | +| `page` | `Pages/` | The written introduction for a page. | + +Each field's title and description live in the schema too, so the +JSON Schema export (`npm run schema:json`) can drive a form or explain a field +to an agent. + +## Designed for editing tools + +The content service will be the only writer apart from Obsidian. It will +expose one write API, built on the schema's `checkNote`, `writeNote` and +`checkVault`. The MCP server for agents is one client of that API; the admin +page is another; any editing tool built later is a third. None of them define +fields or rules of their own. diff --git a/.dockerignore b/legacy/.dockerignore similarity index 100% rename from .dockerignore rename to legacy/.dockerignore diff --git a/.env.example b/legacy/.env.example similarity index 100% rename from .env.example rename to legacy/.env.example diff --git a/legacy/.gitignore b/legacy/.gitignore new file mode 100644 index 0000000..5c155e1 --- /dev/null +++ b/legacy/.gitignore @@ -0,0 +1,30 @@ +# build output +dist/ +.output/ + +# dependencies +node_modules/ + +# generated types +.astro/ + +# environment +.env +.env.* +!.env.example + +# logs +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* + +# macOS / editor +.DS_Store +.vscode/* +!.vscode/extensions.json +!.vscode/settings.json +.idea/ + +# misc +*.local diff --git a/.zcode/plans/plan-sess_cfbaea37-9611-497f-804c-b438e5e659b3.md b/legacy/.zcode/plans/plan-sess_cfbaea37-9611-497f-804c-b438e5e659b3.md similarity index 100% rename from .zcode/plans/plan-sess_cfbaea37-9611-497f-804c-b438e5e659b3.md rename to legacy/.zcode/plans/plan-sess_cfbaea37-9611-497f-804c-b438e5e659b3.md diff --git a/Dockerfile b/legacy/Dockerfile similarity index 100% rename from Dockerfile rename to legacy/Dockerfile diff --git a/legacy/LEGACY.md b/legacy/LEGACY.md new file mode 100644 index 0000000..a002c22 --- /dev/null +++ b/legacy/LEGACY.md @@ -0,0 +1,7 @@ +# Legacy site + +The GregKilmer.com site as it was before the 2026 rebuild, kept for reference. +It still runs the live site until cutover, built from this folder. + +Do not add features here. Its data was migrated into vault notes by +`tools/migrate`; new content goes into the vault. diff --git a/Profile_Website_Spec.md b/legacy/Profile_Website_Spec.md similarity index 100% rename from Profile_Website_Spec.md rename to legacy/Profile_Website_Spec.md diff --git a/legacy/README.md b/legacy/README.md new file mode 100644 index 0000000..3cdaf10 --- /dev/null +++ b/legacy/README.md @@ -0,0 +1,156 @@ +# Greg Kilmer — Profile & Portfolio Site + +A responsive, content-driven personal site for Greg Kilmer, built as **two wings +of the same house**: a polished professional resume/portfolio on one side, and a +warm, personality-driven interests hub on the other. + +Built with **Astro 7 + React Islands + TypeScript**, served by a **Node server** +with API routes, ready to deploy as a container on a homelab Cosmos system. + +--- + +## Highlights + +- **Astro 7** static-first core with React Islands for interactivity (mobile + menu, theme toggle, animated hero background, contact form). +- **Node backend** (`@astrojs/node`, standalone) with a typed `/api/contact` + endpoint (SMTP via nodemailer + webhook fallback, Zod validation, rate limit). +- **Section-aware theming**: the Professional wing reads dark/polished, the + Interests wing reads warm/inviting, plus a global light/dark toggle. +- **Rich motion**: animated mesh-gradient hero with an optional canvas particle + field, IntersectionObserver scroll reveals, View Transitions page swaps, + scroll-triggered nav blur. All `prefers-reduced-motion` aware. +- **Content Collections** (typed): add projects and experience by dropping + markdown/MDX files. +- **mdi icons everywhere** (no emojis, per the spec), inlined at build time from + `@iconify-json/mdi` for zero runtime cost. +- **Self-hosted variable fonts** (Inter, Plus Jakarta Sans, JetBrains Mono). +- **Accessible**: semantic landmarks, skip link, keyboard-operable menu, + focus-visible rings, ARIA on interactive bits, WCAG-AA contrast targets. +- **Deploy artifacts**: multi-stage `Dockerfile` + `docker-compose.yml`. + +--- + +## Quickstart + +```bash +# 1. Install dependencies (Node 20+) +npm install + +# 2. Start the dev server with hot reload +npm run dev +# -> http://localhost:4321 + +# 3. Production build (runs astro check + build) +npm run build + +# 4. Run the built Node server +npm run serve +# -> http://localhost:4321 +``` + +Environment is optional for local dev. To enable contact-form delivery, copy +`.env.example` to `.env` and fill in SMTP or webhook vars (see +[docs/DEPLOYMENT.md](docs/DEPLOYMENT.md)). + +--- + +## Project structure + +``` +src/ +├── components/ +│ ├── hero/ AnimatedBackground.tsx (gradient + canvas particles) +│ ├── motion/ Reveal.astro (IntersectionObserver scroll reveals) +│ ├── nav/ NavBar.astro, MobileMenu.tsx, ThemeToggle.tsx +│ ├── cards/ InterestCard.astro +│ ├── professional/ Timeline, SkillGrid, ProjectCard, SectionNav, ContactForm +│ ├── layout/ Footer, SectionHeader, SplitTransition, InterestHero, +│ │ RelatedInterests, Placeholder +│ └── Icon.astro Inline mdi icon renderer (build-time) +├── content/ +│ ├── projects/ Project collection entries (markdown/MDX) +│ └── experience/ Experience collection entries +├── layouts/ +│ └── BaseLayout.astro shell, head, fonts, theme init, nav, footer +├── pages/ +│ ├── index.astro Home (hero + snapshot grid) +│ ├── about.astro About + split transition fork +│ ├── professional/index.astro Single long page: Summary, Experience, +│ │ Skills, Projects, Education, Contact +│ ├── interests/ Hub + 5 sub-pages +│ │ ├── index.astro +│ │ ├── ttrpgs / gaming / reading / making / pokemon .astro +│ ├── contact.astro Two-column connect page + contact form +│ └── api/contact.ts POST endpoint -> SMTP / webhook +├── content.config.ts Collection schemas (projects, experience) +├── lib/ site.ts (nav/socials), motion.ts, content helpers +├── styles/ global.css, themes.css, animations.css +└── scripts/theme-init.ts No-FOUC theme boot (inlined in ) +``` + +--- + +## The two wings (theming model) + +Theming has two independent axes (see [docs/THEMING.md](docs/THEMING.md)): + +1. **Global light/dark** on ``, set before paint by an inline + script, persisted to `localStorage`, toggleable via the nav button. +2. **Section tone** via `data-surface` on ``: + - `neutral` (Home/About/Contact) — balanced middle ground + - `professional` — dark slate, polished + - `interest` — warm cream/amber, inviting + +Components only reference semantic CSS variables (`--surface`, `--text`, +`--accent`...), so changing either axis restyles everything automatically. + +--- + +## Adding content + +- **Projects / Experience:** drop a markdown or MDX file in + `src/content/projects/` or `src/content/experience/`. Schemas and field + reference are in [docs/CONTENT.md](docs/CONTENT.md). +- **Interest pages:** hand-built `.astro` files under `src/pages/interests/`, + each following the spec's template (Header, Overview, Highlights, Currently, + Related). Edit copy in place. +- **Placeholders:** every `[Greg to fill]` gap from the spec is a styled + `Placeholder` slot with a TODO badge so you can find and replace them. + +--- + +## Deployment (Cosmos homelab) + +The site runs as a Node server inside a container. Full walkthrough in +[docs/DEPLOYMENT.md](docs/DEPLOYMENT.md): + +```bash +docker compose up -d --build +# Then point your reverse proxy at port 4321 with TLS for gregkilmer.com. +``` + +--- + +## Scripts + +| Script | Description | +| ----------------- | ----------------------------------------------------- | +| `npm run dev` | Dev server with hot reload | +| `npm run build` | Type-check (`astro check`) then build the Node server | +| `npm run serve` | Run the built server (`dist/server/entry.mjs`) | +| `npm run preview` | Preview the build locally | +| `npm run check` | Type-check only | + +--- + +## Tech stack + +Astro 7, React 19, TypeScript 6, Tailwind CSS v4, Framer Motion, nodemailer, +Zod, `@iconify-json/mdi`, Fontsource variable fonts, `@astrojs/node` standalone. + +--- + +## License + +Personal site for Greg Kilmer. All rights reserved. diff --git a/astro.config.mjs b/legacy/astro.config.mjs similarity index 100% rename from astro.config.mjs rename to legacy/astro.config.mjs diff --git a/docker-compose.yml b/legacy/docker-compose.yml similarity index 100% rename from docker-compose.yml rename to legacy/docker-compose.yml diff --git a/docs/CONTENT.md b/legacy/docs/CONTENT.md similarity index 100% rename from docs/CONTENT.md rename to legacy/docs/CONTENT.md diff --git a/docs/DEPLOYMENT.md b/legacy/docs/DEPLOYMENT.md similarity index 100% rename from docs/DEPLOYMENT.md rename to legacy/docs/DEPLOYMENT.md diff --git a/docs/PHOTOS.md b/legacy/docs/PHOTOS.md similarity index 100% rename from docs/PHOTOS.md rename to legacy/docs/PHOTOS.md diff --git a/docs/STORE.md b/legacy/docs/STORE.md similarity index 100% rename from docs/STORE.md rename to legacy/docs/STORE.md diff --git a/docs/THEMING.md b/legacy/docs/THEMING.md similarity index 100% rename from docs/THEMING.md rename to legacy/docs/THEMING.md diff --git a/legacy/package-lock.json b/legacy/package-lock.json new file mode 100644 index 0000000..11d5bf2 --- /dev/null +++ b/legacy/package-lock.json @@ -0,0 +1,8470 @@ +{ + "name": "greg-kilmer-profile", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "greg-kilmer-profile", + "version": "1.0.0", + "dependencies": { + "@astrojs/check": "^0.9.9", + "@astrojs/mdx": "^7.0.2", + "@astrojs/node": "^11.0.2", + "@astrojs/react": "^6.0.2", + "@fontsource-variable/cinzel": "^5.3.0", + "@fontsource-variable/jetbrains-mono": "^5.2.8", + "@fontsource-variable/montserrat": "^5.3.0", + "@iconify-json/mdi": "^1.2.3", + "@tailwindcss/vite": "^4.3.2", + "@types/nodemailer": "^8.0.1", + "@types/react": "^19.2.17", + "@types/react-dom": "^19.2.3", + "astro": "^7.2.0", + "framer-motion": "^12.42.2", + "nodemailer": "^9.0.3", + "react": "^19.2.7", + "react-dom": "^19.2.7", + "tailwindcss": "^4.3.2", + "typescript": "^6.0.3", + "zod": "^4.4.3" + } + }, + "node_modules/@astrojs/check": { + "version": "0.9.10", + "resolved": "https://registry.npmjs.org/@astrojs/check/-/check-0.9.10.tgz", + "integrity": "sha512-zgx/UQMozdjOa3bOxjgeCFdtpE3c9rRX6xHwa+2QXvy8z8Akifu2AtubHyv/zzC2znO8dl8fFWL4K+Ba9kS8HQ==", + "license": "MIT", + "dependencies": { + "@astrojs/language-server": "^2.16.7", + "chokidar": "^4.0.3", + "kleur": "^4.1.5", + "yargs": "^18.0.0" + }, + "bin": { + "astro-check": "bin/astro-check.js" + }, + "peerDependencies": { + "typescript": "^5.0.0 || ^6.0.0" + } + }, + "node_modules/@astrojs/compiler": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler/-/compiler-2.13.1.tgz", + "integrity": "sha512-f3FN83d2G/v32ipNClRKgYv30onQlMZX1vCeZMjPsMMPl1mDpmbl0+N5BYo4S/ofzqJyS5hvwacEo0CCVDn/Qg==", + "license": "MIT" + }, + "node_modules/@astrojs/compiler-binding": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding/-/compiler-binding-0.3.2.tgz", + "integrity": "sha512-8w/9CWmYrAJJ8N0SY3O43ws2BgxoW6u3QsD8u2mE140lMYAlwh+tlNoUeSBq22wVheFuiBbR212l6ixZ2IIgCQ==", + "license": "MIT", + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@astrojs/compiler-binding-darwin-arm64": "0.3.2", + "@astrojs/compiler-binding-darwin-x64": "0.3.2", + "@astrojs/compiler-binding-linux-arm64-gnu": "0.3.2", + "@astrojs/compiler-binding-linux-arm64-musl": "0.3.2", + "@astrojs/compiler-binding-linux-x64-gnu": "0.3.2", + "@astrojs/compiler-binding-linux-x64-musl": "0.3.2", + "@astrojs/compiler-binding-wasm32-wasi": "0.3.2", + "@astrojs/compiler-binding-win32-arm64-msvc": "0.3.2", + "@astrojs/compiler-binding-win32-x64-msvc": "0.3.2" + } + }, + "node_modules/@astrojs/compiler-binding-darwin-arm64": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-darwin-arm64/-/compiler-binding-darwin-arm64-0.3.2.tgz", + "integrity": "sha512-MM8tn8CSimcfytaOla4b6acN8mKWiL/rlAA1fpT3/Wl7dNGSE4y8FjTN/zJVNnb63CsLWG5zZwCt01TXtDKh9g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-darwin-x64": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-darwin-x64/-/compiler-binding-darwin-x64-0.3.2.tgz", + "integrity": "sha512-2lXOlzf8xb7jLomRsf/aswh61/NnGusynB2OwFkK6k4pmOtpfXMYnG0PLfXrEvxXYj69NdCnmUYXtHDd+JOOag==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-linux-arm64-gnu": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-arm64-gnu/-/compiler-binding-linux-arm64-gnu-0.3.2.tgz", + "integrity": "sha512-BmU3kWj7qnLrd4vzm49zFEPJ5oFnn1tCT4Vt9hZbqdU5Cmb8GZl7fn6VFsnNfe7B18a2gIFtVzbLINtYl5kBjQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-linux-arm64-musl": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-arm64-musl/-/compiler-binding-linux-arm64-musl-0.3.2.tgz", + "integrity": "sha512-f0heT9ZZEseSu5bHCeb80eL2DH07ArE6U9xi1WT/PEusNjzPmEr3GJsjG1tRLo5VYUUYX7h3ScaqGmGrMOVGmw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-linux-x64-gnu": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-x64-gnu/-/compiler-binding-linux-x64-gnu-0.3.2.tgz", + "integrity": "sha512-M8fOUt0itRpqiGyoEA/ij184s8O+hqbCz3+YozRusOOM3osgGljpDThhbKAJjqh82wOo6FioQ4w8PBvU1XMD5Q==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-linux-x64-musl": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-x64-musl/-/compiler-binding-linux-x64-musl-0.3.2.tgz", + "integrity": "sha512-/Kebk8sO6HnLeSd691JkaAPfN7CqR9/KEXmWvyNPkaKNGmj8rTZ/lf2uXtnPu92Lan84UrKdIKVPy1fSo2encQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-wasm32-wasi": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-wasm32-wasi/-/compiler-binding-wasm32-wasi-0.3.2.tgz", + "integrity": "sha512-pUA6xbcOSB7DhfzIArB8BCAkFfAIqriiR7zl5zOStd6oU2G0kIKj+GUdGnyXyhfiv881Hffyk5tC0mR18sDjDw==", + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^1.2.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@astrojs/compiler-binding-win32-arm64-msvc": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-win32-arm64-msvc/-/compiler-binding-win32-arm64-msvc-0.3.2.tgz", + "integrity": "sha512-ESruf+6Qkl1trHUFxI6GSf6t52j8yN2kCNSzMWdzt7V/T09tFHrYzrVaJQohb2C9bJUH76pNvX6Zb51+xCQc9Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-win32-x64-msvc": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-win32-x64-msvc/-/compiler-binding-win32-x64-msvc-0.3.2.tgz", + "integrity": "sha512-wzzVrEbOwbsLWOdEbocskjMRx2aZPxJ7ZbmL+jnpBamFwmigm+2M/wzuM6JWncocgYwLic1csSpalBh96kQKXA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-rs": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-rs/-/compiler-rs-0.3.2.tgz", + "integrity": "sha512-xlx/T7JovIKduu4ucbTQUxQ5+Q8wxkHxhLjnZk3VlJbhbQ9RLvvuDk1p2YYFYFQ5y14dVm3FGGO4isQXa4F+Tg==", + "license": "MIT", + "dependencies": { + "@astrojs/compiler-binding": "0.3.2" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@astrojs/internal-helpers": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@astrojs/internal-helpers/-/internal-helpers-0.10.2.tgz", + "integrity": "sha512-yt7fMgPYqSM4Tmr+taTW6Per+hjJ8Pk6lA1PAcDyqzOt8HzJ6Kje5WzCxA2Sd+9wsUW7uhkLeoTMK0cXPwH9rQ==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.4", + "@types/mdast": "^4.0.4", + "js-yaml": "^4.3.0", + "picomatch": "^4.0.4", + "retext-smartypants": "^6.2.0", + "shiki": "^4.0.2", + "smol-toml": "^1.6.0", + "unified": "^11.0.5" + } + }, + "node_modules/@astrojs/language-server": { + "version": "2.16.13", + "resolved": "https://registry.npmjs.org/@astrojs/language-server/-/language-server-2.16.13.tgz", + "integrity": "sha512-ekOa+CYprEq5n4EJC1qTIAhLk49HZIUQuFwrEuF+3JK/pdMaYnWoREFUI2A0KEPOJiFA2kamBzKzbYljDvUxLg==", + "license": "MIT", + "dependencies": { + "@astrojs/compiler": "^2.13.1", + "@astrojs/yaml2ts": "^0.2.4", + "@jridgewell/sourcemap-codec": "^1.5.5", + "@volar/kit": "~2.4.28", + "@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.16", + "volar-service-css": "0.0.71", + "volar-service-emmet": "0.0.71", + "volar-service-html": "0.0.71", + "volar-service-prettier": "0.0.71", + "volar-service-typescript": "0.0.71", + "volar-service-typescript-twoslash-queries": "0.0.71", + "volar-service-yaml": "0.0.71", + "vscode-html-languageservice": "^5.6.2", + "vscode-uri": "^3.1.0" + }, + "bin": { + "astro-ls": "bin/nodeServer.js" + }, + "peerDependencies": { + "prettier": "^3.0.0", + "prettier-plugin-astro": ">=0.11.0" + }, + "peerDependenciesMeta": { + "prettier": { + "optional": true + }, + "prettier-plugin-astro": { + "optional": true + } + } + }, + "node_modules/@astrojs/markdown-remark": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@astrojs/markdown-remark/-/markdown-remark-7.2.2.tgz", + "integrity": "sha512-FGfmK84zSNcrsBd0dl1gXE9JvZYElp8EXQa2jpHVAxG4deGKAp43wspxFupjADJX7MSsMRHwYCnfT6EyVmgeFQ==", + "license": "MIT", + "dependencies": { + "@astrojs/internal-helpers": "0.10.2", + "@astrojs/prism": "4.0.2", + "github-slugger": "^2.0.0", + "hast-util-from-html": "^2.0.3", + "hast-util-to-text": "^4.0.2", + "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", + "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" + } + }, + "node_modules/@astrojs/markdown-satteri": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@astrojs/markdown-satteri/-/markdown-satteri-0.3.5.tgz", + "integrity": "sha512-CvWVEFAbay7YO+i9SaqDJubipA5ckiVB89QWoMJ5XC0m5CtFg8JwZ7Kau6X9sYY7FZURH0w2l03ISH2jOS/RDQ==", + "license": "MIT", + "dependencies": { + "@astrojs/internal-helpers": "0.10.2", + "@astrojs/prism": "4.0.2", + "github-slugger": "^2.0.0", + "hast-util-from-html": "^2.0.3", + "satteri": "^0.9.1" + } + }, + "node_modules/@astrojs/mdx": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/@astrojs/mdx/-/mdx-7.0.5.tgz", + "integrity": "sha512-wEM/HH1RiEntyPVagdiF+yArzfcYLKBB0C1RZspVidKZ97rRMbaqP1Nbl/GR0sJs8zwaceqxRymw8aOKKJRdYw==", + "license": "MIT", + "dependencies": { + "@astrojs/internal-helpers": "0.10.2", + "@astrojs/markdown-remark": "7.2.2", + "@mdx-js/mdx": "^3.1.1", + "acorn": "^8.16.0", + "es-module-lexer": "^2.0.0", + "estree-util-visit": "^2.0.0", + "hast-util-to-html": "^9.0.5", + "piccolore": "^0.1.3", + "rehype-raw": "^7.0.0", + "remark-gfm": "^4.0.1", + "remark-smartypants": "^3.0.2", + "source-map": "^0.7.6", + "unist-util-visit": "^5.1.0", + "vfile": "^6.0.3" + }, + "engines": { + "node": ">=22.12.0" + }, + "peerDependencies": { + "@astrojs/markdown-satteri": "^0.3.1", + "astro": "^7.0.0" + }, + "peerDependenciesMeta": { + "@astrojs/markdown-satteri": { + "optional": true + } + } + }, + "node_modules/@astrojs/node": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/@astrojs/node/-/node-11.1.0.tgz", + "integrity": "sha512-utAqBE1eIcAhyIkBUnNIWrzj4vyTiOlbbqcjUSE2Yz5XrMu+9sQdBwp/UmZXfJ9wjaEIHf26HInNLUUmTOFeEw==", + "license": "MIT", + "dependencies": { + "@astrojs/internal-helpers": "0.10.2", + "send": "^1.2.1", + "server-destroy": "^1.0.1" + }, + "peerDependencies": { + "astro": "^7.0.0" + } + }, + "node_modules/@astrojs/prism": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@astrojs/prism/-/prism-4.0.2.tgz", + "integrity": "sha512-KTivpmnz6lDsC6o9H4+DNm2SrE/GHzw8cNAvEJwAvUT+eoaEnn/4NtbDNfRRaxaJHdp15gf+tfHAWiXR4wB3BA==", + "license": "MIT", + "dependencies": { + "prismjs": "^1.30.0" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@astrojs/react": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@astrojs/react/-/react-6.0.2.tgz", + "integrity": "sha512-Fvv2UqS7ajFL2Yjd5s4s5PcCBCSSfks4r04rbCHZO5z4pUPwCygV2ketI+iC+1P/sdHANLV3tgDL1wjzty4cJg==", + "license": "MIT", + "dependencies": { + "@astrojs/internal-helpers": "0.10.2", + "@vitejs/plugin-react": "^5.2.0", + "devalue": "^5.8.1", + "ultrahtml": "^1.6.0", + "vite": "^8.0.13" + }, + "engines": { + "node": ">=22.12.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" + } + }, + "node_modules/@astrojs/telemetry": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/@astrojs/telemetry/-/telemetry-3.3.3.tgz", + "integrity": "sha512-C1TLn5sPJr0x4vk56piHWKbnqlEB8BKyte5Y45V02U+D7BGO5eMqZDH5aPjnkXQWJggvmsTXxH03QMZ9NgWLzQ==", + "license": "MIT", + "dependencies": { + "ci-info": "^4.4.0", + "dset": "^3.1.4", + "is-docker": "^4.0.0", + "package-manager-detector": "^1.6.0" + }, + "engines": { + "node": "18.20.8 || ^20.3.0 || >=22.0.0" + } + }, + "node_modules/@astrojs/yaml2ts": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@astrojs/yaml2ts/-/yaml2ts-0.2.4.tgz", + "integrity": "sha512-8oddpOae35pJsXPQXhTkM0ypfKPskVsh2bCxRtbf7e+/Epw2nReakFYpLKjZMEr75CsoF203PMnCocpfz0s69A==", + "license": "MIT", + "dependencies": { + "yaml": "^2.8.3" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", + "license": "MIT", + "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/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.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.8.tgz", + "integrity": "sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.8", + "@babel/types": "^7.29.8", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", + "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", + "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", + "license": "MIT", + "dependencies": { + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.8.tgz", + "integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.8" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.29.7.tgz", + "integrity": "sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.29.7.tgz", + "integrity": "sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.8.tgz", + "integrity": "sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.8", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.8", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.8", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", + "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bruits/satteri-darwin-arm64": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-darwin-arm64/-/satteri-darwin-arm64-0.9.5.tgz", + "integrity": "sha512-iw4nZgx9v30lWo/MTngQqi1pI78KI0DnkSm+lVJGYdmPLgAyDNJigVhpG42/Iq55A6c1Ll8q66ljyyRiQUxwow==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@bruits/satteri-darwin-x64": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-darwin-x64/-/satteri-darwin-x64-0.9.5.tgz", + "integrity": "sha512-6T26Z5Kf3cFW2PSlk9p7zT7yVxvuBSiJvYyz9u8KjYwMTqZyIDOj2wDyNpxKV4+6yUVG7rddq2QwvG/8LJA2+Q==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@bruits/satteri-linux-arm64-gnu": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-linux-arm64-gnu/-/satteri-linux-arm64-gnu-0.9.5.tgz", + "integrity": "sha512-u51id17uJwNEMK9nBlICsq6U31c+XVqQueVBkwRIzZG+gMpS8TOJctt5h5Wz33Z8xnMdTd+adtACVz0yHgGuOA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@bruits/satteri-linux-arm64-musl": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-linux-arm64-musl/-/satteri-linux-arm64-musl-0.9.5.tgz", + "integrity": "sha512-v39HxiwGC5Rqm01HksP6+5Y+xKLPlsuVFgIgpEAo+SiQ22c+mJVhS3u7Z6ePAKdhL5NJoK1xq70kLz3L13AhpQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@bruits/satteri-linux-x64-gnu": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-linux-x64-gnu/-/satteri-linux-x64-gnu-0.9.5.tgz", + "integrity": "sha512-F3uO8uFp3pAP5ZGXttwvh57GS7s0lL953tnNdyI2gRyP4kOOkp6pyGojNJzCjkDvWI2Cvb9iNrKok3aqQPauAw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@bruits/satteri-linux-x64-musl": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-linux-x64-musl/-/satteri-linux-x64-musl-0.9.5.tgz", + "integrity": "sha512-bicEqglLlz++mWyADaZoP0JY20s4vDfLjaPYgQqC+NI4zZLTOOg1T4GB8aqtc822Pqji8SQBmSrTb7CrP8i08Q==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@bruits/satteri-wasm32-wasi": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-wasm32-wasi/-/satteri-wasm32-wasi-0.9.5.tgz", + "integrity": "sha512-zauAuMwfPnKPUkd4AFixRFpXdgKwP2mKgxrIIo2gJzW0/ZneF9dbHnLkojSpaBnCCp7VUL1hIi5WWZvB1CqmAQ==", + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.11.1", + "@emnapi/runtime": "1.11.1", + "@napi-rs/wasm-runtime": "^1.1.6" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@bruits/satteri-wasm32-wasi/node_modules/@emnapi/core": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", + "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.2", + "tslib": "^2.4.0" + } + }, + "node_modules/@bruits/satteri-wasm32-wasi/node_modules/@emnapi/runtime": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", + "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@bruits/satteri-wasm32-wasi/node_modules/@emnapi/wasi-threads": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", + "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@bruits/satteri-win32-arm64-msvc": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-win32-arm64-msvc/-/satteri-win32-arm64-msvc-0.9.5.tgz", + "integrity": "sha512-SrfE7NEsgZjBvU3c+RR6oQRu0ToXY5uVJEbieXEF0YTctIV2zAVlbaMjWLts074QCgh3a+XHWkR/lWh2VH2LUg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@bruits/satteri-win32-x64-msvc": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-win32-x64-msvc/-/satteri-win32-x64-msvc-0.9.5.tgz", + "integrity": "sha512-5Kw9ZAtTGS8WHizyn+CJhjjfIQrw+7jcZodpmpXJjefnO15M8UexIi6JR2E5thyvsmHyhL6ZDDMUNR4bKJPd4g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@capsizecss/unpack": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@capsizecss/unpack/-/unpack-4.0.1.tgz", + "integrity": "sha512-CuNiSqg7+e1cO/GjffyMOm5Tt2jUF9CWHHnvQ/UkqvtkGfHdgwEC0wpmq7fkN3gxwpRnrAN0WzO3vREKmNolMQ==", + "license": "MIT", + "dependencies": { + "fontkitten": "^1.0.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@clack/core": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/@clack/core/-/core-1.4.3.tgz", + "integrity": "sha512-/kr3UWNtdJfxZtPgDqUOmG2pvwlmcLGheex5yiZKdwbzZJxhV+HMNR9QNmyY5cGwTNV6LrR7Jtp+KjhUAP1qBQ==", + "license": "MIT", + "dependencies": { + "fast-wrap-ansi": "^0.2.0", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 20.12.0" + } + }, + "node_modules/@clack/prompts": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@clack/prompts/-/prompts-1.7.0.tgz", + "integrity": "sha512-y7/yvZ2TPAnR9+jnc00klvNNLkJiXFFrQA/hlLCcxA9a2A4zQIOimyFQ9XfwYKiGD1fb5GY8vbKIIgO8d5Tb2A==", + "license": "MIT", + "dependencies": { + "@clack/core": "1.4.3", + "fast-string-width": "^3.0.2", + "fast-wrap-ansi": "^0.2.0", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 20.12.0" + } + }, + "node_modules/@emmetio/abbreviation": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/@emmetio/abbreviation/-/abbreviation-2.3.3.tgz", + "integrity": "sha512-mgv58UrU3rh4YgbE/TzgLQwJ3pFsHHhCLqY20aJq+9comytTXUDNGG/SMtSeMJdkpxgXSXunBGLD8Boka3JyVA==", + "license": "MIT", + "dependencies": { + "@emmetio/scanner": "^1.0.4" + } + }, + "node_modules/@emmetio/css-abbreviation": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@emmetio/css-abbreviation/-/css-abbreviation-2.1.8.tgz", + "integrity": "sha512-s9yjhJ6saOO/uk1V74eifykk2CBYi01STTK3WlXWGOepyKa23ymJ053+DNQjpFcy1ingpaO7AxCcwLvHFY9tuw==", + "license": "MIT", + "dependencies": { + "@emmetio/scanner": "^1.0.4" + } + }, + "node_modules/@emmetio/css-parser": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@emmetio/css-parser/-/css-parser-0.4.1.tgz", + "integrity": "sha512-2bC6m0MV/voF4CTZiAbG5MWKbq5EBmDPKu9Sb7s7nVcEzNQlrZP6mFFFlIaISM8X6514H9shWMme1fCm8cWAfQ==", + "license": "MIT", + "dependencies": { + "@emmetio/stream-reader": "^2.2.0", + "@emmetio/stream-reader-utils": "^0.1.0" + } + }, + "node_modules/@emmetio/html-matcher": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@emmetio/html-matcher/-/html-matcher-1.3.0.tgz", + "integrity": "sha512-NTbsvppE5eVyBMuyGfVu2CRrLvo7J4YHb6t9sBFLyY03WYhXET37qA4zOYUjBWFCRHO7pS1B9khERtY0f5JXPQ==", + "license": "ISC", + "dependencies": { + "@emmetio/scanner": "^1.0.0" + } + }, + "node_modules/@emmetio/scanner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@emmetio/scanner/-/scanner-1.0.4.tgz", + "integrity": "sha512-IqRuJtQff7YHHBk4G8YZ45uB9BaAGcwQeVzgj/zj8/UdOhtQpEIupUhSk8dys6spFIWVZVeK20CzGEnqR5SbqA==", + "license": "MIT" + }, + "node_modules/@emmetio/stream-reader": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@emmetio/stream-reader/-/stream-reader-2.2.0.tgz", + "integrity": "sha512-fXVXEyFA5Yv3M3n8sUGT7+fvecGrZP4k6FnWWMSZVQf69kAq0LLpaBQLGcPR30m3zMmKYhECP4k/ZkzvhEW5kw==", + "license": "MIT" + }, + "node_modules/@emmetio/stream-reader-utils": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@emmetio/stream-reader-utils/-/stream-reader-utils-0.1.0.tgz", + "integrity": "sha512-ZsZ2I9Vzso3Ho/pjZFsmmZ++FWeEd/txqybHTm4OgaZzdS8V9V/YYWQwg5TC38Z7uLWUV1vavpLLbjJtKubR1A==", + "license": "MIT" + }, + "node_modules/@emnapi/core": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.3.tgz", + "integrity": "sha512-zLpS5asjEb7lq8jYLq37N6XKaE41DIexlY1rF/z4/tIl3wo13Sqm28fRyfIsKZD+NZ8mM5RoKkpW/rBcuoSZSg==", + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.3", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.3.tgz", + "integrity": "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.3.tgz", + "integrity": "sha512-ELEBe8PsLvvJ6QMr0zLt8ffvOHW/dc1m3CEzNMg7aJUv3bMaoDtw2TXyDAwkYBuroxxuHEwhRTLJSe5sya547g==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz", + "integrity": "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.1.tgz", + "integrity": "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.1.tgz", + "integrity": "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.1.tgz", + "integrity": "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.1.tgz", + "integrity": "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.1.tgz", + "integrity": "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.1.tgz", + "integrity": "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.1.tgz", + "integrity": "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.1.tgz", + "integrity": "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.1.tgz", + "integrity": "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.1.tgz", + "integrity": "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.1.tgz", + "integrity": "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.1.tgz", + "integrity": "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.1.tgz", + "integrity": "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.1.tgz", + "integrity": "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.1.tgz", + "integrity": "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.1.tgz", + "integrity": "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.1.tgz", + "integrity": "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.1.tgz", + "integrity": "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.1.tgz", + "integrity": "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.1.tgz", + "integrity": "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.1.tgz", + "integrity": "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.1.tgz", + "integrity": "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.1.tgz", + "integrity": "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.1.tgz", + "integrity": "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.1.tgz", + "integrity": "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@fontsource-variable/cinzel": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@fontsource-variable/cinzel/-/cinzel-5.3.0.tgz", + "integrity": "sha512-eFcU0TwaAP5QESF2pV8VoOPJJXJ6sSGm/6mXMwqTdMBPAJMaLhfa6oISHuOtFeddsoHP9RYm4Io0j1j3mNEz2Q==", + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, + "node_modules/@fontsource-variable/jetbrains-mono": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@fontsource-variable/jetbrains-mono/-/jetbrains-mono-5.3.0.tgz", + "integrity": "sha512-F32xpS2NsGYoQi2ADSkKTgpJj7ozajsGgDJ8woTnqjmIB+dxDIqImjl4pXZVEExu8UFZ2ndhmX18EBS/hdz3Lw==", + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, + "node_modules/@fontsource-variable/montserrat": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@fontsource-variable/montserrat/-/montserrat-5.3.0.tgz", + "integrity": "sha512-7PaZoxaxrWLAyrhO46v65An9LhUhfkTExWLhfbywYZCnZEgg/W1rEHnlNmZKjNZ3nJTVYyicqxlJt10z/26yTA==", + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, + "node_modules/@iconify-json/mdi": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@iconify-json/mdi/-/mdi-1.2.3.tgz", + "integrity": "sha512-O3cLwbDOK7NNDf2ihaQOH5F9JglnulNDFV7WprU2dSoZu3h3cWH//h74uQAB87brHmvFVxIOkuBX2sZSzYhScg==", + "license": "Apache-2.0", + "dependencies": { + "@iconify/types": "*" + } + }, + "node_modules/@iconify/types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz", + "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==", + "license": "MIT" + }, + "node_modules/@img/colour": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz", + "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.35.3.tgz", + "integrity": "sha512-RMnFX7YQsMoh7lWfcM4NEHHymBX/rLuKNPVM84XE9ONPcaSCDgE7CHIHpSgPcO2xcRthgBy1HfNO319mwhIAkg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.3.2" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.35.3.tgz", + "integrity": "sha512-Xo+5uFBtLN0BKqieTxiFzFPQAUlBbbH5iBKyRX/z1JrbnYsHTfKJnUfL8+p2TPXr1pXqao4eeL4Rl144uDpK9w==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.3.2" + } + }, + "node_modules/@img/sharp-freebsd-wasm32": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-freebsd-wasm32/-/sharp-freebsd-wasm32-0.35.3.tgz", + "integrity": "sha512-lUxcqWIj2wMQ9BrwNjngcr1gWUr5xgaGThBRqPPalIC2n67Cqj1uPh8NnA/ZhAg8hUbKl+kVHKwgUIwe6ZYPrg==", + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "dependencies": { + "@img/sharp-wasm32": "0.35.3" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.3.2.tgz", + "integrity": "sha512-9J6ypZFpQBj4YnePGoq/S38w6nz+vqg5WZLrLGY4YuSemdMq47GMLBPO42MzwdGwpg/agZ7xzZcFHa48xlywfg==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.3.2.tgz", + "integrity": "sha512-m2pW1n6cns9VaubNwsZ+c3CRYjxNQWgJ5gPlnL1nbBcpkBvFm6SCFN5o0psFHI8w9n11NKhFkeEDns98tiqbEw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.3.2.tgz", + "integrity": "sha512-1eMLzy92I4J6rmi4mAT8yC3HxOtniyGELlzGbNMLLeqe052ahFQ0h6LFq+lh5DsDIdYViIDst08abvSbcEdLXQ==", + "cpu": [ + "arm" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.3.2.tgz", + "integrity": "sha512-dqVSFynCox4C/J8kT16V7SIFAns0IjgLwkvYT7p8LQVmJ5OS5b6tI9IGflxTeuBS//zXeFIUbwt5dwxyZ17cnA==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.3.2.tgz", + "integrity": "sha512-3z0NHDxD6n5I9gc05U1eW1AyRm+Gznzq3naMrthPNqE6oYykcogW0l/jfpJdjYnuNl8R7yI9pNbE1XiUeyq0Aw==", + "cpu": [ + "ppc64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.3.2.tgz", + "integrity": "sha512-bsb4rI+NldGOsXuej2r8OdSS8+zXDVaCWxyWrcv6kneTOlgAHtZABRzBBCwdsPiD90J4myNJuHpg6kA20ImW/w==", + "cpu": [ + "riscv64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.3.2.tgz", + "integrity": "sha512-/ABshyj8gCpyIrNXnHn4LorDJ0HHm1VhXPBlxZ8zAtfVPAaSafXPGn+sUSIRiwaSBy0mmFjSjiXI5mkcwdChKQ==", + "cpu": [ + "s390x" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.3.2.tgz", + "integrity": "sha512-ITPEtgffGJ0S6G9dRyw/366tJQqFRcHWPHhC+Stpg3Z8AEMrDrTr2lhdz4f/Y/HMbRh//7Z5mBzEpVdi62Oc3w==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.3.2.tgz", + "integrity": "sha512-zE9EdiUzUmg5mDT5a1rk5fYJ6GWPloTwWBYDS14naqHsL+EaMpDj1AWnpLgh3u0YCORv2Tt50wrcrpYqkP97Kw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.3.2.tgz", + "integrity": "sha512-m0lrLiUt+lBYnCFr8qV/65yMR4E/c7/wf78I5eKTdkEakFAlZ9QlzEM3QIhhAwVeUhLAHLcCq7a7Vszq/oFNZQ==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.35.3.tgz", + "integrity": "sha512-affVWCTLooy8TSxbDx2qkzuDeaWLNVBA+P//FNBirHsXpP2fuBhk5AuboYUnrDnzoXes8GFjpTx0SBFOCRg+FA==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.3.2" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.35.3.tgz", + "integrity": "sha512-QgKDspHPnrU+GQ55XPhGwyhC8acLVOOSyAvo1oVfFmrIXLkDNmGWzAfDZ4xK8oSA1qBQrALcHX0G5UZni/SuFQ==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.3.2" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.35.3.tgz", + "integrity": "sha512-sMd8rDxmpLOwv/7N44klFjOD5DUO7FLdjiXDI0hoxYaf7Ar262dQIEkosE98bps+5HPLtp/EvNqeqQtOycP/IA==", + "cpu": [ + "ppc64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.3.2" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.35.3.tgz", + "integrity": "sha512-0Eob78yjlYPfL5vMNWAW55l3R9Y6BQS/gOfe0ZcP9mEz9ohhKSt4im1hayiknXgf8AWrFqMvJcKIdmLmEe7yeQ==", + "cpu": [ + "riscv64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.3.2" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.35.3.tgz", + "integrity": "sha512-KgAxQ0DxpNOq1rG2t5cgTgShJFGSuU7XO45cqC+1NVOuZnP6tlgZRuSYOfNupGkHID0o3cJOsw4DVeJpMovcGw==", + "cpu": [ + "s390x" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.3.2" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.35.3.tgz", + "integrity": "sha512-8pqvxubL2PGdhlPy6GLqzDYMUjyRmKAwKHYKixpdJYBUK7PJ0C029XdsnpFIdgRZG68fZiGdHVWcKPvtiPB4cA==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.3.2" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.35.3.tgz", + "integrity": "sha512-Vz0iQjzzcSX3HCbfwFfCSG/9SCIqyO0mH2sXyiHaAYfBk0cRsCWXRyQYX0ovCK/PAQBbTzQ0dsPQHh5MAFL59w==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.3.2" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.35.3.tgz", + "integrity": "sha512-6O1NPKcDVj9QEdg7Hx549EX8U0rp6yXQERqru6yRN7fGBn32UvIRJUlWnk+8xDCiG76hXVBbX82NZ/ZKr0euIg==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.3.2" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.35.3.tgz", + "integrity": "sha512-cZ0XkcYGpHZkqW6iCkqTcmUC0CD9DhD5d/qeZlZkfRBn6GnHniZXLUo5+9xw8Iv76YE6LQFN9YNBlKREcCG76w==", + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.11.1" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-webcontainers-wasm32": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-webcontainers-wasm32/-/sharp-webcontainers-wasm32-0.35.3.tgz", + "integrity": "sha512-2rnq7bX3NzeR2T4YWgz8qiG4h3TSdMe+vN1iQXpJleSJ3SM5zQ8Fy2SyyXAWlbxpEZ2Y+Z4u1BePgJEYbSy80Q==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@img/sharp-wasm32": "0.35.3" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.35.3.tgz", + "integrity": "sha512-4bPwFdMbeC4JQ8L8LOyWp6nsHcboP5fxkp6iPOXz2Vg49R42TuMs2whkJ5OAP4/Ul035qOzy0AecOF9VOscn4w==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.35.3.tgz", + "integrity": "sha512-r53mXsBN6lFUDiST764SvgwUdHAqM4rPAiDzAmf4fLoB6X/rkfyTrLCg6+g17wJJiCmB3JYgHuUldCWUIRFSXw==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.35.3.tgz", + "integrity": "sha512-D4y1vNeZrIIJCN+uHaWVtH86B+aCrdMYYjicy9pXHvbGZeGYLLSd3wdVuC37FxVXlU1ARsk84eKWfWMXGYEqvA==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@mdx-js/mdx": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-3.1.1.tgz", + "integrity": "sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdx": "^2.0.0", + "acorn": "^8.0.0", + "collapse-white-space": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "estree-util-scope": "^1.0.0", + "estree-walker": "^3.0.0", + "hast-util-to-jsx-runtime": "^2.0.0", + "markdown-extensions": "^2.0.0", + "recma-build-jsx": "^1.0.0", + "recma-jsx": "^1.0.0", + "recma-stringify": "^1.0.0", + "rehype-recma": "^1.0.0", + "remark-mdx": "^3.0.0", + "remark-parse": "^11.0.0", + "remark-rehype": "^11.0.0", + "source-map": "^0.7.0", + "unified": "^11.0.0", + "unist-util-position-from-estree": "^2.0.0", + "unist-util-stringify-position": "^4.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.2.2.tgz", + "integrity": "sha512-JfB4kuJQjaoHuCTseIINHtHWeJnvgEcxjwA5t/Y00ZgaOO1Crz3fjT/p8kT28zA/Caz7oiUMn3d6H2yOVCVwuw==", + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.3" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=23.5.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1 || ^2.0.0-alpha.3", + "@emnapi/runtime": "^1.7.1 || ^2.0.0-alpha.3" + } + }, + "node_modules/@oslojs/encoding": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@oslojs/encoding/-/encoding-1.1.0.tgz", + "integrity": "sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==", + "license": "MIT" + }, + "node_modules/@oxc-project/types": { + "version": "0.143.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.143.0.tgz", + "integrity": "sha512-u6JZdLBTLotrNC9Vd6vPssINdzcCzleKAH6EJKImQb7GtYvX5keN2dxkoK44stCc4tffE6QQRtZTXVSzsLUlWA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.3.tgz", + "integrity": "sha512-zrJtHDcaZJ1Fp7xf4hNl+7seH9Cn/N5TwLYkhgXREtBwAd/jaqW3uqeHxpDugJLVICWg4eW44kOQEGJ1r6jCGw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.3.tgz", + "integrity": "sha512-ieIiibVCp0tX7TLu2cafoNPv8wJyYi01ekXpbf8q2j7F4rGAhhXb/eQh7ge9DRBY78GwmRQtvjZDux7EDbA8kA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.3.tgz", + "integrity": "sha512-Zh9tCon19eDXJoihx0rqKhMUlMYqzwj3aPsSuHmI4RWZh62dWUL+DJN4C5YQya5TcQBJU/Fe8+rY0jhXTQITqA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.3.tgz", + "integrity": "sha512-nGbJWewA1wrXXZiQhjAT5rhibGfns5ZNkDVqxsO6zJ3f3YvpoDNNmGMSbbhLuXKjNScaBJVOAboztAWVespQMg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.3.tgz", + "integrity": "sha512-QNniJr5Kml0kDEB98jiDOJjXNroxIIi0IXIbdYzY26Xt1pVbeP62+KnoIZLwirOymX/0jDk/2gI/bNUv7A7OIw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.3.tgz", + "integrity": "sha512-TkqEAcmmvH3I/q4114NB4RVt6241Dao48pF45uLcFGrwAaIn0iITgTAKP/dLjbN0R4buJjGb91+UHSoFmpgIWw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.3.tgz", + "integrity": "sha512-NHqjnxpsndf4MPymxteFAWHHfkTL8HjWh1KB7z23ofZ6QO2euONuxDXjat69dKZRALnGypg8k8SsK8vZJoXv1Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.3.tgz", + "integrity": "sha512-6tbrbwfz5GB9DQ4Jwo6hy9v+vR31xZlvzZ6n5Xut6Hhx5PvrA9q/HsK8KMaYQp063iqZGXwNvZtYNLD7EM/x0w==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.3.tgz", + "integrity": "sha512-oyuXxXmoZHjXC917IAPFAAv4wWAa0cM9afk8nx1+9/jNNOX1uPf8yDA6p7G0RypOfw/X0PQt5IfoquY1um+zSg==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.3.tgz", + "integrity": "sha512-TytMwF2KVGqP2tgd0I1OY0PAv78dZRAYcF5ssDzjM34SUXCED3uXvSd5+lHoC0bTD6eEdFz7LdQNCO1y0oVk9w==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.3.tgz", + "integrity": "sha512-/E9m3qstrJFVPoULV25mVQblSNExY2+kBsYe4sy0Tn0yOOgJ8wZbZt3KnRbF/XeU2Gl1STKUQnDNTqhIE5MD4A==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.3.tgz", + "integrity": "sha512-Kr0OcsoQI816i6HOl3vFHpd1K0eZyh76zgfj4c1nTyaTsd5r2Mj1lwM4R90y/qaCfmTn9eHy0SKwi98eitRxug==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.3.tgz", + "integrity": "sha512-hOtMwTqnME+/gJcH/PCZ0wn0zPUjiWOgkHpxbSJpfGKMezHltx1S7/k1SitzVa7Ww2cqrDDaFbZEhcJZO8o+Jw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.3.tgz", + "integrity": "sha512-ekcqMMkI2PlhYnfzQnB/cEdYUVVJViWvoUyLrbzgDoi3Snfc1mVBwdnc306ufA5ejy8JSPjT2RlW1nQSjW7efg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.3", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.3.tgz", + "integrity": "sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q==", + "license": "MIT" + }, + "node_modules/@shikijs/core": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-4.4.2.tgz", + "integrity": "sha512-StyzbAyxg2/tBGf78gwbBkGyeQ73lf8UiJArFaQhTQIDqQOCKPCQFanvrs4/Yv3Yfyc+ONInJM6K+FMIf+P+kA==", + "license": "MIT", + "dependencies": { + "@shikijs/primitive": "4.4.2", + "@shikijs/types": "4.4.2", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.5", + "hast-util-to-html": "^9.0.5" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/engine-javascript": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-4.4.2.tgz", + "integrity": "sha512-MnIkeqWdVPUWsxlx8gKLVCJFTsqrQJgpTPBPpQwaFeJ56lOnJxj5aN2LUFnfxUEcvOQuNocmbaMnVrCEln6rkw==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.4.2", + "@shikijs/vscode-textmate": "^10.0.2", + "oniguruma-to-es": "^4.3.6" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/engine-oniguruma": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-4.4.2.tgz", + "integrity": "sha512-GLhowz1+jixjz+wiZ3wMnOn1jTxiFCGl2PkXufivbnwPHKuyw1AYqu5/hbWhZZ2oAb0NP05WUJhYeigY14drnw==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.4.2", + "@shikijs/vscode-textmate": "^10.0.2" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/langs": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-4.4.2.tgz", + "integrity": "sha512-8DfeusD+Zdv/eYIDdXyJTUnSMHt+aAWjAOCXV20HNGAHRlInXpG8wh421v6B91WOm9TFwRLN+b/LG5F2NAIojg==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.4.2" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/primitive": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@shikijs/primitive/-/primitive-4.4.2.tgz", + "integrity": "sha512-l6fQQKsOMlz72n38fztmSgZ76MO6KSWuw8o+GJ+FhmqrpC9pIOJNQNXGgbb5yX2AwpzlEHwsaLPnk/8o4Fm+rA==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.4.2", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.5" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/themes": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-4.4.2.tgz", + "integrity": "sha512-H0CFoL07ddDC2Dd6EdrPYNkRhUR6YCkJlnuYFceYYUJJA5TIm2b5B33qqiDYryBExgbKMndFJPb2u1gTuqO37g==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.4.2" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/types": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-4.4.2.tgz", + "integrity": "sha512-PFYitV4vpDr/iPCIhnHp+Q4ftic5N5VeNJ3KQ1O8gn3h2ar8qgwMAXF7tq4m1CWaMS60fV4VqF6vfnWH4F7vqQ==", + "license": "MIT", + "dependencies": { + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.5" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/vscode-textmate": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz", + "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==", + "license": "MIT" + }, + "node_modules/@tailwindcss/node": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.3.tgz", + "integrity": "sha512-/T8IKEsf9VTU6tLjgC7+sv2mOPtQxzE2jMw7u4Tt40Tx+QSZxpzh95/H6cMKoja9XuW7iMdLJYBB0o9G1CaAgg==", + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "enhanced-resolve": "^5.24.1", + "jiti": "^2.7.0", + "lightningcss": "1.32.0", + "magic-string": "^0.30.21", + "source-map-js": "^1.2.1", + "tailwindcss": "4.3.3" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.3.3.tgz", + "integrity": "sha512-krXjAikiaFSPaK/FkAQT5UTx3VormQaiZ5hBFlJZ9UFQGB/rwg1MZIhHAG9smMQRTdyJxP6Qt5MwMtdyU5FWrA==", + "license": "MIT", + "engines": { + "node": ">= 20" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.3.3", + "@tailwindcss/oxide-darwin-arm64": "4.3.3", + "@tailwindcss/oxide-darwin-x64": "4.3.3", + "@tailwindcss/oxide-freebsd-x64": "4.3.3", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.3", + "@tailwindcss/oxide-linux-arm64-gnu": "4.3.3", + "@tailwindcss/oxide-linux-arm64-musl": "4.3.3", + "@tailwindcss/oxide-linux-x64-gnu": "4.3.3", + "@tailwindcss/oxide-linux-x64-musl": "4.3.3", + "@tailwindcss/oxide-wasm32-wasi": "4.3.3", + "@tailwindcss/oxide-win32-arm64-msvc": "4.3.3", + "@tailwindcss/oxide-win32-x64-msvc": "4.3.3" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.3.tgz", + "integrity": "sha512-Y85A2gmPSkl5Ve5qR86GL4HT509cFqQh1aes9p3sSkyTPwt0Pppf3GkwGe4JPACcRYjgJIEhQgM6dBClnr0NYw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.3.tgz", + "integrity": "sha512-BiaWatpBcERQFDlOjRDpIVXuFK5PJez5SA4JMg6VYZdBYU+qKfV/vqjcIs+IYmtitf1xYQZTwXvU/8y4lfZUGw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.3.tgz", + "integrity": "sha512-fAeUqfV5ndhxRwai8cXGzdLvul9utWOmeTkv69unv4ZXixjn61Z+p9lCWdwOwA3TYboG3BwdVuN/RDjhBRl0mw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.3.tgz", + "integrity": "sha512-iyf5bV6+wnAlflVeEy7R25dupxTNECZN5QMI0qNT6eT+EgaGdZcKhGkr5SdoaWiLJ3spLqIY9VCeSGrwmtg4kw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.3.tgz", + "integrity": "sha512-aAYUprJAJQWWbRrPvtjdroZ56Md+JM8pMiopS6xGEwDfLhqj+2ver2p4nU4Mb3CRqcMmNBjo8KkUgcxhkzVQGQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.3.tgz", + "integrity": "sha512-nDxldcEENOxZRzC2uu9jrutZdAAQtb+8WWDCSnWL1zvBk1+FN+x6MtDViPB5AJMfttVCUhehGWus3XBPgatM/w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.3.tgz", + "integrity": "sha512-Md44bD6veX/PC5iyF8cDVnw4HBIANZepRZZ7a8DQOvkfo5WUBwcp6iAuCUz23u+4SUkhJlD3eL7hNdW8ezd/kA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.3.tgz", + "integrity": "sha512-tx7us1muwOKAKWao2v/GaafFeQboE6aj88vC6ziN2NCGcRm8gWUhwjzg+YdVB1e4boAtdtma4L43onunI6NS4w==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.3.tgz", + "integrity": "sha512-SJxX60smvHgasZoBy11dX6YRjXJFovwWBoedhbQPOBzgFWBHGB+TVPWB9BxzR7TTxU8FQZAI2AyiNCMzFm8Img==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.3.tgz", + "integrity": "sha512-jx1+rPhY/5Ympkktd656HBWEBLxP7dH06losBLjjf5vgCODXvi9KhtftWcMIwTFIDqBr7cRnQkdLnAG+IOlGvQ==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.11.1", + "@emnapi/runtime": "^1.11.1", + "@emnapi/wasi-threads": "^1.2.2", + "@napi-rs/wasm-runtime": "^1.1.4", + "@tybys/wasm-util": "^0.10.2", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.3.tgz", + "integrity": "sha512-3rc292Ca2ceK6Ulcc/bAVnTs/3nDtoPhyEKlgPv+yQJQi/JS/AMJlqzxvlDacL1nekbrcf6bTqp/jV4qgnPxNQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.3.tgz", + "integrity": "sha512-yJ0pwIVc/nYeGoV02WtsN8KYyLQv7kyI2wDnkezyJlGGjkd4QLwDGAwl47YpPJeuI0M0ObaXGSPjvWDPeTPggw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/vite": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.3.3.tgz", + "integrity": "sha512-yYU8cogLeSh/ms2jh8Fj7jaba/EWa7Ja6GoUqYZaraEuCI5YS6ms6ObZgjjedm+jm6XZjdNRWBpPP6Z86oOxcw==", + "license": "MIT", + "dependencies": { + "@tailwindcss/node": "4.3.3", + "@tailwindcss/oxide": "4.3.3", + "tailwindcss": "4.3.3" + }, + "peerDependencies": { + "vite": "^5.2.0 || ^6 || ^7 || ^8" + } + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", + "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/debug": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz", + "integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==", + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "license": "MIT" + }, + "node_modules/@types/estree-jsx": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", + "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/@types/hast": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.5.tgz", + "integrity": "sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/mdx": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.14.tgz", + "integrity": "sha512-T48PeuJtvLosNTPVhfnIp3i/n3a4g4Bad7YCq5k64D4u7NwDrAotikQ+5+sjtUvBmxCMlbo3dVL+C2dP0rWHzg==", + "license": "MIT" + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "license": "MIT" + }, + "node_modules/@types/nlcst": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/nlcst/-/nlcst-2.0.3.tgz", + "integrity": "sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/node": { + "version": "26.1.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-26.1.2.tgz", + "integrity": "sha512-Vu4a5UFA9rIIFJ7rB/Vaafh9lrCQszopTCx6KjFboXTGQbPNasehVR5TEiithSDGyd1DEiUByggTZsg8jukeIg==", + "license": "MIT", + "dependencies": { + "undici-types": "~8.3.0" + } + }, + "node_modules/@types/nodemailer": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-8.0.1.tgz", + "integrity": "sha512-PxpaInm8V1JQDd4j0ds5HfvWQk8JupS1C0Picb96QJsrrRDjBH+DlK7L4ZdNSqNULhiZRQHc40nLVShaGxXAMw==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/react": { + "version": "19.2.18", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.18.tgz", + "integrity": "sha512-AnzbBERsrLKtk2XSfTbYRLjQPdy116Sty4q+T+Bp3IC4l6jNBvreVPAHmpq9qhXQM7CXZPjLVmGMw9sy+hxQ3w==", + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.4.tgz", + "integrity": "sha512-Bsc+QHgp+P/F02XDzNCY9jnZNCUuLki36KT7VKrTXXLdHf+vHMNZnW1rVu5DNW/rCK+fya3DATySbLM4yhtKUw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.3.tgz", + "integrity": "sha512-60YRaenCQcVjYEKOcG824+DRGGIQ3VKErcBoAEDJZz5bKIs2ZG+X/H9Nk+Q6EVkwJk5QNApxbrc5QtBSwtrXAg==", + "license": "ISC" + }, + "node_modules/@vitejs/plugin-react": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.2.0.tgz", + "integrity": "sha512-YmKkfhOAi3wsB1PhJq5Scj3GXMn3WvtQ/JC0xoopuHoXSdmtdStOpFrYaT1kie2YgFBcIe64ROzMYRjCrYOdYw==", + "license": "MIT", + "dependencies": { + "@babel/core": "^7.29.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-rc.3", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.18.0" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@volar/kit": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/kit/-/kit-2.4.28.tgz", + "integrity": "sha512-cKX4vK9dtZvDRaAzeoUdaAJEew6IdxHNCRrdp5Kvcl6zZOqb6jTOfk3kXkIkG3T7oTFXguEMt5+9ptyqYR84Pg==", + "license": "MIT", + "dependencies": { + "@volar/language-service": "2.4.28", + "@volar/typescript": "2.4.28", + "typesafe-path": "^0.2.2", + "vscode-languageserver-textdocument": "^1.0.11", + "vscode-uri": "^3.0.8" + }, + "peerDependencies": { + "typescript": "*" + } + }, + "node_modules/@volar/language-core": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.28.tgz", + "integrity": "sha512-w4qhIJ8ZSitgLAkVay6AbcnC7gP3glYM3fYwKV3srj8m494E3xtrCv6E+bWviiK/8hs6e6t1ij1s2Endql7vzQ==", + "license": "MIT", + "dependencies": { + "@volar/source-map": "2.4.28" + } + }, + "node_modules/@volar/language-server": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/language-server/-/language-server-2.4.28.tgz", + "integrity": "sha512-NqcLnE5gERKuS4PUFwlhMxf6vqYo7hXtbMFbViXcbVkbZ905AIVWhnSo0ZNBC2V127H1/2zP7RvVOVnyITFfBw==", + "license": "MIT", + "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.17.5", + "vscode-languageserver-textdocument": "^1.0.11", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/@volar/language-service": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/language-service/-/language-service-2.4.28.tgz", + "integrity": "sha512-Rh/wYCZJrI5vCwMk9xyw/Z+MsWxlJY1rmMZPsxUoJKfzIRjS/NF1NmnuEcrMbEVGja00aVpCsInJfixQTMdvLw==", + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.28", + "vscode-languageserver-protocol": "^3.17.5", + "vscode-languageserver-textdocument": "^1.0.11", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/@volar/source-map": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.28.tgz", + "integrity": "sha512-yX2BDBqJkRXfKw8my8VarTyjv48QwxdJtvRgUpNE5erCsgEUdI2DsLbpa+rOQVAJYshY99szEcRDmyHbF10ggQ==", + "license": "MIT" + }, + "node_modules/@volar/typescript": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.28.tgz", + "integrity": "sha512-Ja6yvWrbis2QtN4ClAKreeUZPVYMARDYZl9LMEv1iQ1QdepB6wn0jTRxA9MftYmYa4DQ4k/DaSZpFPUfxl8giw==", + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.28", + "path-browserify": "^1.0.1", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/@vscode/emmet-helper": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@vscode/emmet-helper/-/emmet-helper-2.11.0.tgz", + "integrity": "sha512-QLxjQR3imPZPQltfbWRnHU6JecWTF1QSWhx3GAKQpslx7y3Dp6sIIXhKjiUJ/BR9FX8PVthjr9PD6pNwOJfAzw==", + "license": "MIT", + "dependencies": { + "emmet": "^2.4.3", + "jsonc-parser": "^2.3.0", + "vscode-languageserver-textdocument": "^1.0.1", + "vscode-languageserver-types": "^3.15.1", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/@vscode/emmet-helper/node_modules/jsonc-parser": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-2.3.1.tgz", + "integrity": "sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg==", + "license": "MIT" + }, + "node_modules/@vscode/l10n": { + "version": "0.0.18", + "resolved": "https://registry.npmjs.org/@vscode/l10n/-/l10n-0.0.18.tgz", + "integrity": "sha512-KYSIHVmslkaCDyw013pphY+d7x1qV8IZupYfeIfzNA+nsaWHbn5uPuQRvdRFsa9zFzGeudPuoGoZ1Op4jrJXIQ==", + "license": "MIT" + }, + "node_modules/acorn": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz", + "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-draft-04": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz", + "integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==", + "license": "MIT", + "peerDependencies": { + "ajv": "^8.5.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-i18n": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/ajv-i18n/-/ajv-i18n-4.2.0.tgz", + "integrity": "sha512-v/ei2UkCEeuKNXh8RToiFsUclmU+G57LO1Oo22OagNMENIw+Yb8eMwvHu7Vn9fmkjJyv6XclhJ8TbuigSglPkg==", + "license": "MIT", + "peerDependencies": { + "ajv": "^8.0.0-beta.0" + } + }, + "node_modules/am-i-vibing": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/am-i-vibing/-/am-i-vibing-0.4.0.tgz", + "integrity": "sha512-MxT4XZL7pzLHpuvhDKdMaQHMGGkJDLluKBLsbstn+8wv9sWcFT6h+0ve9qkml95amVTZtZV83gQe2hY+ojgHLg==", + "license": "MIT", + "dependencies": { + "process-ancestry": "^0.1.0" + }, + "bin": { + "am-i-vibing": "dist/cli.mjs" + } + }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, + "node_modules/aria-query": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/array-iterate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/array-iterate/-/array-iterate-2.0.1.tgz", + "integrity": "sha512-I1jXZMjAgCMmxT4qxXfPXa6SthSoE8h6gkSI9BGGNv8mP8G/v0blc+qFnZu6K42vTOiuME596QaLO0TP3Lk0xg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/astring": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/astring/-/astring-1.9.0.tgz", + "integrity": "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==", + "license": "MIT", + "bin": { + "astring": "bin/astring" + } + }, + "node_modules/astro": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/astro/-/astro-7.2.0.tgz", + "integrity": "sha512-lLTYzx3fOvCmtwD3JVBLQcbORbIOW1/j0R+3IvJx/XKwMGrk7mFnF0BYSOeRiNw1qHUR5mdA6+hRnyvyDfqrWQ==", + "license": "MIT", + "dependencies": { + "@astrojs/compiler-rs": "^0.3.2", + "@astrojs/internal-helpers": "0.10.2", + "@astrojs/markdown-satteri": "0.3.5", + "@astrojs/telemetry": "3.3.3", + "@capsizecss/unpack": "^4.0.0", + "@clack/prompts": "^1.1.0", + "@oslojs/encoding": "^1.1.0", + "am-i-vibing": "^0.4.0", + "aria-query": "^5.3.2", + "axobject-query": "^4.1.0", + "ci-info": "^4.4.0", + "clsx": "^2.1.1", + "common-ancestor-path": "^2.0.0", + "cookie": "^2.0.1", + "devalue": "^5.8.1", + "diff": "^8.0.3", + "dset": "^3.1.4", + "es-module-lexer": "^2.0.0", + "esbuild": "^0.28.0", + "flattie": "^1.1.1", + "fontace": "~0.4.1", + "get-tsconfig": "5.0.0-beta.4", + "github-slugger": "^2.0.0", + "html-escaper": "3.0.3", + "http-cache-semantics": "^4.2.0", + "js-yaml": "^4.3.0", + "jsonc-parser": "^3.3.1", + "magic-string": "^1.0.0", + "magicast": "^0.5.2", + "mrmime": "^2.0.1", + "neotraverse": "^1.0.1", + "obug": "^2.1.1", + "p-limit": "^7.3.0", + "p-queue": "^9.1.0", + "package-manager-detector": "^1.6.0", + "piccolore": "^0.1.3", + "picomatch": "^4.0.4", + "semver": "^7.7.4", + "shiki": "^4.0.2", + "smol-toml": "^1.6.0", + "svgo": "^4.0.1", + "tinyclip": "^0.1.12", + "tinyexec": "^1.0.4", + "tinyglobby": "^0.2.15", + "ultrahtml": "^1.6.0", + "unifont": "~0.7.4", + "unstorage": "^1.17.5", + "vite": "^8.0.13", + "vitefu": "^1.1.2", + "xxhash-wasm": "^1.1.0", + "yargs-parser": "^22.0.0", + "zod": "^4.3.6" + }, + "bin": { + "astro": "bin/astro.mjs" + }, + "engines": { + "node": ">=22.12.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/astrodotbuild" + }, + "optionalDependencies": { + "sharp": "^0.34.0 || ^0.35.0" + }, + "peerDependencies": { + "@astrojs/markdown-remark": "7.2.2" + }, + "peerDependenciesMeta": { + "@astrojs/markdown-remark": { + "optional": true + } + } + }, + "node_modules/astro/node_modules/magic-string": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-1.1.0.tgz", + "integrity": "sha512-kS3VHe0nEPST2saQV4Rbkchcd3UBRkVTQHo1D3h/ZTwFDhai/mfKkmtPAtD129EOI7K3HlHIsFOt0WrI2/oU9g==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/astro/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/bail": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.11.12", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.12.tgz", + "integrity": "sha512-r7WnVImvVCeFpf2DOXfy41aPWzeNg3H/A2X4dKmy1QL0MSyyk/e7z8ihJ3N6Nn2PsdhkVlqnEfnUE4a05P2aTA==", + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "license": "ISC" + }, + "node_modules/browserslist": { + "version": "4.28.7", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.7.tgz", + "integrity": "sha512-JxV13hNrFxqjOc8alRbq9dK1MM79NEXYpma2B2J4wAtpWS5zIEIKqWPGCl7N4o7Uc7B7itylh7SuDujATRyyTw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.10.44", + "caniuse-lite": "^1.0.30001806", + "electron-to-chromium": "^1.5.393", + "node-releases": "^2.0.51", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001807", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001807.tgz", + "integrity": "sha512-daRXJ9EB/rdRgu7kV+TTl1YUKtlsMWblPl2sLnpg9DZae16QCegol6A1SmCE31Lm9mXC1sRWGt/krouH+/dl7Q==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-reference-invalid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", + "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/ci-info": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", + "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz", + "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==", + "license": "ISC", + "dependencies": { + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/cliui/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/collapse-white-space": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-2.1.0.tgz", + "integrity": "sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/commander": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", + "license": "MIT", + "engines": { + "node": ">=16" + } + }, + "node_modules/common-ancestor-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/common-ancestor-path/-/common-ancestor-path-2.0.0.tgz", + "integrity": "sha512-dnN3ibLeoRf2HNC+OlCiNc5d2zxbLJXOtiZUudNFSXZrNSydxcCsSpRzXwfu7BBWCIfHPw+xTayeBvJCP/D8Ng==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">= 18" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "license": "MIT" + }, + "node_modules/cookie": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-2.0.1.tgz", + "integrity": "sha512-yuToqVvRrj6pfDXREyQAAv8SkAEk/8GS3jQRTiUMm66TVtBYmqQeoEjL2Lmq8Rpo6271vH76InTChTitEAm65w==", + "license": "MIT", + "engines": { + "node": ">=22" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/cookie-es": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-1.2.3.tgz", + "integrity": "sha512-lXVyvUvrNXblMqzIRrxHb57UUVmqsSWlxqt3XIjCkUP0wDAf6uicO6KMbEgYrMNtEvWgWHwe42CKxPu9MYAnWw==", + "license": "MIT" + }, + "node_modules/crossws": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/crossws/-/crossws-0.3.5.tgz", + "integrity": "sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA==", + "license": "MIT", + "dependencies": { + "uncrypto": "^0.1.3" + } + }, + "node_modules/css-select": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", + "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-tree": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz", + "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==", + "license": "MIT", + "dependencies": { + "mdn-data": "2.27.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/css-what": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", + "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/csso": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", + "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", + "license": "MIT", + "dependencies": { + "css-tree": "~2.2.0" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/csso/node_modules/css-tree": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", + "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", + "license": "MIT", + "dependencies": { + "mdn-data": "2.0.28", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/csso/node_modules/mdn-data": { + "version": "2.0.28", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", + "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==", + "license": "CC0-1.0" + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decode-named-character-reference": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.3.0.tgz", + "integrity": "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==", + "license": "MIT", + "dependencies": { + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/defu": { + "version": "6.1.7", + "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.7.tgz", + "integrity": "sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==", + "license": "MIT" + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/destr": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz", + "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==", + "license": "MIT" + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/devalue": { + "version": "5.9.0", + "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.9.0.tgz", + "integrity": "sha512-RWrqdArjvPbsATEhOPUo6Wndc/iWnkWKlhIrdlF3zMMYo/c3CVtoaVAyLtWxz5h8nSlkHzxnzV2uLydPXmtF+A==", + "license": "MIT" + }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/diff": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.4.tgz", + "integrity": "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/dom-serializer/node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dset": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/dset/-/dset-3.1.4.tgz", + "integrity": "sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.402", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.402.tgz", + "integrity": "sha512-/oOpMaPT6Yg+6/1XQhyIPlzgj7Ye9zf+nNM2Uh6OcE2G2oNptWazFa+qB2Pdqqbsc9KnIDzgAntoYN0dbwOXwA==", + "license": "ISC" + }, + "node_modules/emmet": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/emmet/-/emmet-2.4.11.tgz", + "integrity": "sha512-23QPJB3moh/U9sT4rQzGgeyyGIrcM+GH5uVYg2C6wZIxAIJq7Ng3QLT79tl8FUwDXhyq9SusfknOrofAKqvgyQ==", + "license": "MIT", + "workspaces": [ + "./packages/scanner", + "./packages/abbreviation", + "./packages/css-abbreviation", + "./" + ], + "dependencies": { + "@emmetio/abbreviation": "^2.3.3", + "@emmetio/css-abbreviation": "^2.1.8" + } + }, + "node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.24.5", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.24.5.tgz", + "integrity": "sha512-L1l8TNvomm6UVW5B253AGxQagSQr+vGwhMlrrfRS2qmhx46AMpMVJKQYLvWYbysTMY8VoicOvzHzoHMbyzB+4A==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/es-module-lexer": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.1.tgz", + "integrity": "sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA==", + "license": "MIT" + }, + "node_modules/esast-util-from-estree": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/esast-util-from-estree/-/esast-util-from-estree-2.0.0.tgz", + "integrity": "sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "devlop": "^1.0.0", + "estree-util-visit": "^2.0.0", + "unist-util-position-from-estree": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/esast-util-from-js": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/esast-util-from-js/-/esast-util-from-js-2.0.1.tgz", + "integrity": "sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "acorn": "^8.0.0", + "esast-util-from-estree": "^2.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/esbuild": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz", + "integrity": "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.1", + "@esbuild/android-arm": "0.28.1", + "@esbuild/android-arm64": "0.28.1", + "@esbuild/android-x64": "0.28.1", + "@esbuild/darwin-arm64": "0.28.1", + "@esbuild/darwin-x64": "0.28.1", + "@esbuild/freebsd-arm64": "0.28.1", + "@esbuild/freebsd-x64": "0.28.1", + "@esbuild/linux-arm": "0.28.1", + "@esbuild/linux-arm64": "0.28.1", + "@esbuild/linux-ia32": "0.28.1", + "@esbuild/linux-loong64": "0.28.1", + "@esbuild/linux-mips64el": "0.28.1", + "@esbuild/linux-ppc64": "0.28.1", + "@esbuild/linux-riscv64": "0.28.1", + "@esbuild/linux-s390x": "0.28.1", + "@esbuild/linux-x64": "0.28.1", + "@esbuild/netbsd-arm64": "0.28.1", + "@esbuild/netbsd-x64": "0.28.1", + "@esbuild/openbsd-arm64": "0.28.1", + "@esbuild/openbsd-x64": "0.28.1", + "@esbuild/openharmony-arm64": "0.28.1", + "@esbuild/sunos-x64": "0.28.1", + "@esbuild/win32-arm64": "0.28.1", + "@esbuild/win32-ia32": "0.28.1", + "@esbuild/win32-x64": "0.28.1" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/estree-util-attach-comments": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/estree-util-attach-comments/-/estree-util-attach-comments-3.0.0.tgz", + "integrity": "sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-build-jsx": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/estree-util-build-jsx/-/estree-util-build-jsx-3.0.1.tgz", + "integrity": "sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "estree-walker": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-is-identifier-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", + "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-scope": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/estree-util-scope/-/estree-util-scope-1.0.0.tgz", + "integrity": "sha512-2CAASclonf+JFWBNJPndcOpA8EMJwa0Q8LUFJEKqXLW6+qBvbFZuF5gItbQOs/umBUkjviCSDCbBwU2cXbmrhQ==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-to-js": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/estree-util-to-js/-/estree-util-to-js-2.0.0.tgz", + "integrity": "sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "astring": "^1.8.0", + "source-map": "^0.7.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-visit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/estree-util-visit/-/estree-util-visit-2.0.0.tgz", + "integrity": "sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eventemitter3": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", + "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", + "license": "MIT" + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-string-truncated-width": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fast-string-truncated-width/-/fast-string-truncated-width-3.0.3.tgz", + "integrity": "sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==", + "license": "MIT" + }, + "node_modules/fast-string-width": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/fast-string-width/-/fast-string-width-3.0.2.tgz", + "integrity": "sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==", + "license": "MIT", + "dependencies": { + "fast-string-truncated-width": "^3.0.2" + } + }, + "node_modules/fast-uri": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.5.tgz", + "integrity": "sha512-gHwA1O9LDIcKunMKhObS/HimwtehO1nPUECKAu5TpKgaO19fcWEl4bliWe1jWxVFvIXztJjjQ4L8XQ1EU9f7Jw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fast-wrap-ansi": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/fast-wrap-ansi/-/fast-wrap-ansi-0.2.2.tgz", + "integrity": "sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==", + "license": "MIT", + "dependencies": { + "fast-string-width": "^3.0.2" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/flattie": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/flattie/-/flattie-1.1.1.tgz", + "integrity": "sha512-9UbaD6XdAL97+k/n+N7JwX46K/M6Zc6KcFYskrYL8wbBV/Uyk0CTAMY0VT+qiK5PM7AIc9aTWYtq65U7T+aCNQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/fontace": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/fontace/-/fontace-0.4.1.tgz", + "integrity": "sha512-lDMvbAzSnHmbYMTEld5qdtvNH2/pWpICOqpean9IgC7vUbUJc3k+k5Dokp85CegamqQpFbXf0rAVkbzpyTA8aw==", + "license": "MIT", + "dependencies": { + "fontkitten": "^1.0.2" + } + }, + "node_modules/fontkitten": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/fontkitten/-/fontkitten-1.0.3.tgz", + "integrity": "sha512-Wp1zXWPVUPBmfoa3Cqc9ctaKuzKAV6uLstRqlR56kSjplf5uAce+qeyYym7F+PHbGTk+tCEdkCW6RD7DX/gBZw==", + "license": "MIT", + "dependencies": { + "tiny-inflate": "^1.0.3" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/framer-motion": { + "version": "12.43.0", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.43.0.tgz", + "integrity": "sha512-1eaL3RvR/kAlbG7UYcpMptEyzPoENO0c6w7ZnB3/hh2vSAz/6uGAFn6fdoqTBguNstf3MsFhJHsD/0DHiclG+g==", + "license": "MIT", + "dependencies": { + "motion-dom": "^12.43.0", + "motion-utils": "^12.39.0", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz", + "integrity": "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-tsconfig": { + "version": "5.0.0-beta.4", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-5.0.0-beta.4.tgz", + "integrity": "sha512-7nF7C9fIPFEMHgEMEfgIlO9wDdZ8CyHw27rWciFZfHvHDReIiPhsYuzPRXsfvBCqFy1l8RRyyWV7QLM+ZhUJsQ==", + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "engines": { + "node": ">=20.20.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/github-slugger": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-2.0.0.tgz", + "integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==", + "license": "ISC" + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/h3": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/h3/-/h3-1.15.11.tgz", + "integrity": "sha512-L3THSe2MPeBwgIZVSH5zLdBBU90TOxarvhK9d04IDY2AmVS8j2Jz2LIWtwsGOU3lu2I5jCN7FNvVfY2+XyF+mg==", + "license": "MIT", + "dependencies": { + "cookie-es": "^1.2.3", + "crossws": "^0.3.5", + "defu": "^6.1.6", + "destr": "^2.0.5", + "iron-webcrypto": "^1.2.1", + "node-mock-http": "^1.0.4", + "radix3": "^1.1.2", + "ufo": "^1.6.3", + "uncrypto": "^0.1.3" + } + }, + "node_modules/hast-util-from-html": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hast-util-from-html/-/hast-util-from-html-2.0.3.tgz", + "integrity": "sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "devlop": "^1.1.0", + "hast-util-from-parse5": "^8.0.0", + "parse5": "^7.0.0", + "vfile": "^6.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-parse5": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-8.0.3.tgz", + "integrity": "sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "devlop": "^1.0.0", + "hastscript": "^9.0.0", + "property-information": "^7.0.0", + "vfile": "^6.0.0", + "vfile-location": "^5.0.0", + "web-namespaces": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-is-element": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-3.0.0.tgz", + "integrity": "sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-parse-selector": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz", + "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-raw": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-9.1.0.tgz", + "integrity": "sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "@ungap/structured-clone": "^1.0.0", + "hast-util-from-parse5": "^8.0.0", + "hast-util-to-parse5": "^8.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "parse5": "^7.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0", + "web-namespaces": "^2.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-estree": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/hast-util-to-estree/-/hast-util-to-estree-3.1.3.tgz", + "integrity": "sha512-48+B/rJWAp0jamNbAAf9M7Uf//UVqAoMmgXhBdxTDJLGKY+LRnZ99qcG+Qjl5HfMpYNzS5v4EAwVEF34LeAj7w==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-attach-comments": "^3.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-js": "^1.0.0", + "unist-util-position": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-html": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz", + "integrity": "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-whitespace": "^3.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "stringify-entities": "^4.0.0", + "zwitch": "^2.0.4" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-jsx-runtime": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz", + "integrity": "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-js": "^1.0.0", + "unist-util-position": "^5.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-parse5": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-8.0.1.tgz", + "integrity": "sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "web-namespaces": "^2.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-text": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/hast-util-to-text/-/hast-util-to-text-4.0.2.tgz", + "integrity": "sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "hast-util-is-element": "^3.0.0", + "unist-util-find-after": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hastscript": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-9.0.1.tgz", + "integrity": "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-parse-selector": "^4.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/html-escaper": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-3.0.3.tgz", + "integrity": "sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==", + "license": "MIT" + }, + "node_modules/html-void-elements": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", + "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", + "license": "BSD-2-Clause" + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/inline-style-parser": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.7.tgz", + "integrity": "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==", + "license": "MIT" + }, + "node_modules/iron-webcrypto": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/iron-webcrypto/-/iron-webcrypto-1.2.1.tgz", + "integrity": "sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/brc-dd" + } + }, + "node_modules/is-alphabetical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", + "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-alphanumerical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", + "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", + "license": "MIT", + "dependencies": { + "is-alphabetical": "^2.0.0", + "is-decimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-decimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", + "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-docker": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-4.0.0.tgz", + "integrity": "sha512-LHE+wROyG/Y/0ZnbktRCoTix2c1RhgWaZraMZ8o1Q7zCh0VSrICJQO5oqIIISrcSBtrXv0o233w1IYwsWCjTzA==", + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-hexadecimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", + "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jiti": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", + "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.1.tgz", + "integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "license": "MIT" + }, + "node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/longest-streak": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", + "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/magicast": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.4.tgz", + "integrity": "sha512-llBEhWm1SacoRwgHUoQJYtwp4PBLF4faQi5TCpIGyGs9n4y5+juI0tDgyKIfpqxckRHaHzouUEph3THklWh03w==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7", + "source-map-js": "^1.2.1" + } + }, + "node_modules/markdown-extensions": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/markdown-extensions/-/markdown-extensions-2.0.0.tgz", + "integrity": "sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==", + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/markdown-table": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", + "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/mdast-util-definitions": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-6.0.0.tgz", + "integrity": "sha512-scTllyX6pnYNZH/AIp/0ePz6s4cZtARxImwoPJ7kS42n+MnVsI4XbnG6d4ibehRIldYMWM2LD7ImQblVhUejVQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-find-and-replace": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz", + "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "escape-string-regexp": "^5.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-from-markdown": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.3.tgz", + "integrity": "sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz", + "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==", + "license": "MIT", + "dependencies": { + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-gfm-autolink-literal": "^2.0.0", + "mdast-util-gfm-footnote": "^2.0.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.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-autolink-literal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz", + "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "ccount": "^2.0.0", + "devlop": "^1.0.0", + "mdast-util-find-and-replace": "^3.0.0", + "micromark-util-character": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-strikethrough": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", + "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-table": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", + "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "markdown-table": "^3.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-task-list-item": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", + "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-mdx/-/mdast-util-mdx-3.0.0.tgz", + "integrity": "sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w==", + "license": "MIT", + "dependencies": { + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-expression": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz", + "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-jsx": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz", + "integrity": "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "parse-entities": "^4.0.0", + "stringify-entities": "^4.0.0", + "unist-util-stringify-position": "^4.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdxjs-esm": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", + "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-phrasing": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", + "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast": { + "version": "13.2.1", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", + "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz", + "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "longest-streak": "^3.0.0", + "mdast-util-phrasing": "^4.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "unist-util-visit": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdn-data": { + "version": "2.27.1", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", + "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==", + "license": "CC0-1.0" + }, + "node_modules/micromark": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", + "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", + "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", + "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==", + "license": "MIT", + "dependencies": { + "micromark-extension-gfm-autolink-literal": "^2.0.0", + "micromark-extension-gfm-footnote": "^2.0.0", + "micromark-extension-gfm-strikethrough": "^2.0.0", + "micromark-extension-gfm-table": "^2.0.0", + "micromark-extension-gfm-tagfilter": "^2.0.0", + "micromark-extension-gfm-task-list-item": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-autolink-literal": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", + "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-strikethrough": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz", + "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-table": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz", + "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-tagfilter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", + "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==", + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-task-list-item": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz", + "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdx-expression": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/micromark-extension-mdx-expression/-/micromark-extension-mdx-expression-3.0.1.tgz", + "integrity": "sha512-dD/ADLJ1AeMvSAKBwO22zG22N4ybhe7kFIZ3LsDI0GlsNr2A3KYxb0LdC1u5rj4Nw+CHKY0RVdnHX8vj8ejm4Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-mdx-expression": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-events-to-acorn": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-mdx-jsx": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/micromark-extension-mdx-jsx/-/micromark-extension-mdx-jsx-3.0.2.tgz", + "integrity": "sha512-e5+q1DjMh62LZAJOnDraSSbDMvGJ8x3cbjygy2qFEi7HCeUT4BDKCvMozPozcD6WmOt6sVvYDNBKhFSz3kjOVQ==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "micromark-factory-mdx-expression": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-events-to-acorn": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdx-md": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-mdx-md/-/micromark-extension-mdx-md-2.0.0.tgz", + "integrity": "sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ==", + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdxjs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs/-/micromark-extension-mdxjs-3.0.0.tgz", + "integrity": "sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ==", + "license": "MIT", + "dependencies": { + "acorn": "^8.0.0", + "acorn-jsx": "^5.0.0", + "micromark-extension-mdx-expression": "^3.0.0", + "micromark-extension-mdx-jsx": "^3.0.0", + "micromark-extension-mdx-md": "^2.0.0", + "micromark-extension-mdxjs-esm": "^3.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdxjs-esm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs-esm/-/micromark-extension-mdxjs-esm-3.0.0.tgz", + "integrity": "sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-events-to-acorn": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-position-from-estree": "^2.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-factory-destination": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-label": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-mdx-expression": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-factory-mdx-expression/-/micromark-factory-mdx-expression-2.0.3.tgz", + "integrity": "sha512-kQnEtA3vzucU2BkrIa8/VaSAsP+EJ3CKOvhMuJgOEGg9KDC6OAY6nSnNDVRiVNRqj7Y4SlSzcStaH/5jge8JdQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-events-to-acorn": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-position-from-estree": "^2.0.0", + "vfile-message": "^4.0.0" + } + }, + "node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-title": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-combine-extensions": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-string": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", + "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-events-to-acorn": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-util-events-to-acorn/-/micromark-util-events-to-acorn-2.0.3.tgz", + "integrity": "sha512-jmsiEIiZ1n7X1Rr5k8wVExBQCg5jy4UXVADItHmNk1zkwEVhBuIUKRu3fqv+hs4nxLISi2DQGlqIOGiFxgbfHg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/unist": "^3.0.0", + "devlop": "^1.0.0", + "estree-util-visit": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "vfile-message": "^4.0.0" + } + }, + "node_modules/micromark-util-html-tag-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-resolve-all": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-subtokenize": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", + "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/motion-dom": { + "version": "12.43.0", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.43.0.tgz", + "integrity": "sha512-azKON4d9S65PEoFUiQTMTgPheEmzf2QngdRc50AKfJp9Q9mmcBVw22c8eMq9k8kxOFHdL7+WZY7N/5F/lwiDag==", + "license": "MIT", + "dependencies": { + "motion-utils": "^12.39.0" + } + }, + "node_modules/motion-utils": { + "version": "12.39.0", + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.39.0.tgz", + "integrity": "sha512-8nadJAJjTtqRkmRF36FoJTrywK9nnFmnPwnSMyxaOCU7GDjN9RTMJIxx9De8ErM+vpPhMccr/6fo5WciyQLnMQ==", + "license": "MIT" + }, + "node_modules/mrmime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", + "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/muggle-string": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz", + "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==", + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.17", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.17.tgz", + "integrity": "sha512-xQLf0A3HOMlgHq0n247/LRuAOYmB7dXJ/DvAxGvsSBij45XtBSmQycu+F8ODbHwns/XyFZagyL1+J0Offw1E0g==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/neotraverse": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/neotraverse/-/neotraverse-1.0.1.tgz", + "integrity": "sha512-WmmLty1YWwJl9yZi77v2dVIV6X2kuYV8YYBI/G3LWGKdGHmHUvL1z7FW0iDvEvGAwNEoc5x1tOOOyDnf5jJw/w==", + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/nlcst-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/nlcst-to-string/-/nlcst-to-string-4.0.0.tgz", + "integrity": "sha512-YKLBCcUYKAg0FNlOBT6aI91qFmSiFKiluk655WzPF+DDMA02qIyy8uiRqI8QXtcFpEvll12LpL5MXqEmAZ+dcA==", + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/node-fetch-native": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz", + "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==", + "license": "MIT" + }, + "node_modules/node-mock-http": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/node-mock-http/-/node-mock-http-1.0.5.tgz", + "integrity": "sha512-KQyt/wLjG3TAc7DOUhpqWzgd4ERxR80JOlTK5VE5R1S12IaPVN5qkj4klBce9HPG1Njuup4Sb5bljaT34lIyjw==", + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.53", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.53.tgz", + "integrity": "sha512-D9UOmYG3UH1V+ENW56t5QXBwJw1YEY18ruVeus89Rw+SyIgjPkCO84bRzO3uNIYosJbNwiabWVn48o3uJLjxFQ==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/nodemailer": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-9.0.4.tgz", + "integrity": "sha512-LmJNRVRtfSCULxcZpy0Cpg4WWenlUZ9+zbmTO+S7v9wD6XreYLjXRFtDjtV/4F0HT5p1GyZfA0Ux/myxHb18CQ==", + "license": "MIT-0", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/obug": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.4.tgz", + "integrity": "sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==", + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/ofetch": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/ofetch/-/ofetch-1.5.1.tgz", + "integrity": "sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA==", + "license": "MIT", + "dependencies": { + "destr": "^2.0.5", + "node-fetch-native": "^1.6.7", + "ufo": "^1.6.1" + } + }, + "node_modules/ohash": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz", + "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==", + "license": "MIT" + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/oniguruma-parser": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/oniguruma-parser/-/oniguruma-parser-0.12.2.tgz", + "integrity": "sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw==", + "license": "MIT" + }, + "node_modules/oniguruma-to-es": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-4.3.6.tgz", + "integrity": "sha512-csuQ9x3Yr0cEIs/Zgx/OEt9iBw9vqIunAPQkx19R/fiMq2oGVTgcMqO/V3Ybqefr1TBvosI6jU539ksaBULJyA==", + "license": "MIT", + "dependencies": { + "oniguruma-parser": "^0.12.2", + "regex": "^6.1.0", + "regex-recursion": "^6.0.2" + } + }, + "node_modules/p-limit": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-7.3.1.tgz", + "integrity": "sha512-0trZaiG7Y7kN/Egy9a8j47t9osC0Tch4PaIWd9yGF6bvmlk7muExRvGNYb8sXBwEKMoNKsbNN9P8EefuQekE4Q==", + "license": "MIT", + "dependencies": { + "yocto-queue": "^1.2.1" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-queue": { + "version": "9.3.3", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-9.3.3.tgz", + "integrity": "sha512-NXAOdnEe5FsZJfT4oK84lE1Y5cFFdWlRuOo5tww8DyNMxyRXwn39fIkUtNLKppcPC+UYU/bXujNCUGDv01y7CA==", + "license": "MIT", + "dependencies": { + "eventemitter3": "^5.0.4", + "p-timeout": "^7.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-timeout": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-7.0.1.tgz", + "integrity": "sha512-AxTM2wDGORHGEkPCt8yqxOTMgpfbEHqF51f/5fJCmwFC3C/zNcGT63SymH2ttOAaiIws2zVg4+izQCjrakcwHg==", + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-manager-detector": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.8.0.tgz", + "integrity": "sha512-yQA4H19AmPEoMUeavPMDIe1higySl/gH/yaQrkT/s07Qp+7pp2hYz30N3z2l5BkjVkF9Ow6o0wjJamm2y7Sn0A==", + "license": "MIT" + }, + "node_modules/parse-entities": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", + "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "character-entities-legacy": "^3.0.0", + "character-reference-invalid": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "is-alphanumerical": "^2.0.0", + "is-decimal": "^2.0.0", + "is-hexadecimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/parse-entities/node_modules/@types/unist": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", + "license": "MIT" + }, + "node_modules/parse-latin": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/parse-latin/-/parse-latin-7.0.0.tgz", + "integrity": "sha512-mhHgobPPua5kZ98EF4HWiH167JWBfl4pvAIXXdbaVohtK7a6YBOy56kvhCqduqyo/f3yrHFWmqmiMg/BkBkYYQ==", + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0", + "@types/unist": "^3.0.0", + "nlcst-to-string": "^4.0.0", + "unist-util-modify-children": "^4.0.0", + "unist-util-visit-children": "^3.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "license": "MIT" + }, + "node_modules/piccolore": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/piccolore/-/piccolore-0.1.3.tgz", + "integrity": "sha512-o8bTeDWjE086iwKrROaDf31K0qC/BENdm15/uH9usSC/uZjJOKb2YGiVHfLY4GhwsERiPI1jmwI2XrA7ACOxVw==", + "license": "ISC" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.26", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.26.tgz", + "integrity": "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.17", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prettier": { + "version": "3.9.6", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.6.tgz", + "integrity": "sha512-OpN0zzVdiaiAhxpuuj5efpIS4sY9j7bY6uR5mnj5yPzGkdkjNKSJeUThPb60Jw29QuAZgA4o+/iB49kFiaBX6g==", + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prismjs": { + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", + "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/process-ancestry": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/process-ancestry/-/process-ancestry-0.1.0.tgz", + "integrity": "sha512-tGqJW/UnclpYASFcM6Xh8D8l/BMtaQ9+CSG0vlJSJTcdMM4lDRv4c6H0Pdcsfted+bVczdYSfk2fdukg2gQkZg==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/property-information": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.2.0.tgz", + "integrity": "sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/radix3": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/radix3/-/radix3-1.1.2.tgz", + "integrity": "sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==", + "license": "MIT" + }, + "node_modules/range-parser": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.3.0.tgz", + "integrity": "sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/react": { + "version": "19.2.8", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.8.tgz", + "integrity": "sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.8", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.8.tgz", + "integrity": "sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.8" + } + }, + "node_modules/react-refresh": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz", + "integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/recma-build-jsx": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/recma-build-jsx/-/recma-build-jsx-1.0.0.tgz", + "integrity": "sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-util-build-jsx": "^3.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/recma-jsx": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/recma-jsx/-/recma-jsx-1.0.1.tgz", + "integrity": "sha512-huSIy7VU2Z5OLv6oFLosQGGDqPqdO1iq6bWNAdhzMxSJP7RAso4fCZ1cKu8j9YHCZf3TPrq4dw3okhrylgcd7w==", + "license": "MIT", + "dependencies": { + "acorn-jsx": "^5.0.0", + "estree-util-to-js": "^2.0.0", + "recma-parse": "^1.0.0", + "recma-stringify": "^1.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/recma-parse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/recma-parse/-/recma-parse-1.0.0.tgz", + "integrity": "sha512-OYLsIGBB5Y5wjnSnQW6t3Xg7q3fQ7FWbw/vcXtORTnyaSFscOtABg+7Pnz6YZ6c27fG1/aN8CjfwoUEUIdwqWQ==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "esast-util-from-js": "^2.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/recma-stringify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/recma-stringify/-/recma-stringify-1.0.0.tgz", + "integrity": "sha512-cjwII1MdIIVloKvC9ErQ+OgAtwHBmcZ0Bg4ciz78FtbT8In39aAYbaA7zvxQ61xVMSPE8WxhLwLbhif4Js2C+g==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-util-to-js": "^2.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/regex/-/regex-6.1.0.tgz", + "integrity": "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==", + "license": "MIT", + "dependencies": { + "regex-utilities": "^2.3.0" + } + }, + "node_modules/regex-recursion": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/regex-recursion/-/regex-recursion-6.0.2.tgz", + "integrity": "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==", + "license": "MIT", + "dependencies": { + "regex-utilities": "^2.3.0" + } + }, + "node_modules/regex-utilities": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/regex-utilities/-/regex-utilities-2.3.0.tgz", + "integrity": "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==", + "license": "MIT" + }, + "node_modules/rehype-raw": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/rehype-raw/-/rehype-raw-7.0.0.tgz", + "integrity": "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-raw": "^9.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-recma": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/rehype-recma/-/rehype-recma-1.0.0.tgz", + "integrity": "sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/hast": "^3.0.0", + "hast-util-to-estree": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-stringify": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/rehype-stringify/-/rehype-stringify-10.0.1.tgz", + "integrity": "sha512-k9ecfXHmIPuFVI61B9DeLPN0qFHfawM6RsuX48hoqlaKSF61RskNjSm1lI8PhBEM0MRdLxVVm4WmTqJQccH9mA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-to-html": "^9.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-gfm": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz", + "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-gfm": "^3.0.0", + "micromark-extension-gfm": "^3.0.0", + "remark-parse": "^11.0.0", + "remark-stringify": "^11.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-mdx": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/remark-mdx/-/remark-mdx-3.1.1.tgz", + "integrity": "sha512-Pjj2IYlUY3+D8x00UJsIOg5BEvfMyeI+2uLPn9VO9Wg4MEtN/VTIq2NEJQfde9PnX15KgtHyl9S0BcTnWrIuWg==", + "license": "MIT", + "dependencies": { + "mdast-util-mdx": "^3.0.0", + "micromark-extension-mdxjs": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-parse": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", + "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-rehype": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.2.tgz", + "integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "mdast-util-to-hast": "^13.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-smartypants": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/remark-smartypants/-/remark-smartypants-3.0.3.tgz", + "integrity": "sha512-gCaK+ndZ0hYezlqFegHFCVh2CQemsi0Npdh1qVM9bxlUFknjkbP6VmojWhddOCrbK0PbbacmYLWfTULRiT1eWA==", + "license": "MIT", + "dependencies": { + "retext": "^9.0.0", + "retext-smartypants": "^6.0.0", + "unified": "^11.0.4", + "unist-util-visit": "^5.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/remark-stringify": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz", + "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-to-markdown": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/request-light": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/request-light/-/request-light-0.7.0.tgz", + "integrity": "sha512-lMbBMrDoxgsyO+yB3sDcrDuX85yYt7sS8BfQd11jtbW/z5ZWgLZRcEGLsLoYw7I0WSUGQBs8CC8ScIxkTX1+6Q==", + "license": "MIT" + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/retext": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/retext/-/retext-9.0.0.tgz", + "integrity": "sha512-sbMDcpHCNjvlheSgMfEcVrZko3cDzdbe1x/e7G66dFp0Ff7Mldvi2uv6JkJQzdRcvLYE8CA8Oe8siQx8ZOgTcA==", + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0", + "retext-latin": "^4.0.0", + "retext-stringify": "^4.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/retext-latin": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/retext-latin/-/retext-latin-4.0.0.tgz", + "integrity": "sha512-hv9woG7Fy0M9IlRQloq/N6atV82NxLGveq+3H2WOi79dtIYWN8OaxogDm77f8YnVXJL2VD3bbqowu5E3EMhBYA==", + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0", + "parse-latin": "^7.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/retext-smartypants": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/retext-smartypants/-/retext-smartypants-6.2.0.tgz", + "integrity": "sha512-kk0jOU7+zGv//kfjXEBjdIryL1Acl4i9XNkHxtM7Tm5lFiCog576fjNC9hjoR7LTKQ0DsPWy09JummSsH1uqfQ==", + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0", + "nlcst-to-string": "^4.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/retext-stringify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/retext-stringify/-/retext-stringify-4.0.0.tgz", + "integrity": "sha512-rtfN/0o8kL1e+78+uxPTqu1Klt0yPzKuQ2BfWwwfgIUSayyzxpM1PJzkKt4V8803uB9qSy32MvI7Xep9khTpiA==", + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0", + "nlcst-to-string": "^4.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rolldown": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.3.tgz", + "integrity": "sha512-rn9wpmxplLf7NLNyCk9FyWh3FM43DbY8jOzCdEPzH7uflhTftRbCEpqi6Ly2osgoU8OwObtmavMbWLaWy4LX7A==", + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.143.0", + "@rolldown/pluginutils": "^1.0.0" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.2.3", + "@rolldown/binding-darwin-arm64": "1.2.3", + "@rolldown/binding-darwin-x64": "1.2.3", + "@rolldown/binding-freebsd-x64": "1.2.3", + "@rolldown/binding-linux-arm-gnueabihf": "1.2.3", + "@rolldown/binding-linux-arm64-gnu": "1.2.3", + "@rolldown/binding-linux-arm64-musl": "1.2.3", + "@rolldown/binding-linux-ppc64-gnu": "1.2.3", + "@rolldown/binding-linux-s390x-gnu": "1.2.3", + "@rolldown/binding-linux-x64-gnu": "1.2.3", + "@rolldown/binding-linux-x64-musl": "1.2.3", + "@rolldown/binding-openharmony-arm64": "1.2.3", + "@rolldown/binding-win32-arm64-msvc": "1.2.3", + "@rolldown/binding-win32-x64-msvc": "1.2.3" + } + }, + "node_modules/rolldown/node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "license": "MIT" + }, + "node_modules/satteri": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/satteri/-/satteri-0.9.5.tgz", + "integrity": "sha512-ZuWVl+vnM64y+/TtX8Kosv2c00W+hLQiiwnEL6H0UKVVrxFqMw4D2CJHHQaouVd89OAhtBBfjWLqhKi3TVUV4w==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.5", + "@types/hast": "^3.0.4", + "@types/mdast": "^4.0.4", + "@types/unist": "^3.0.3" + }, + "optionalDependencies": { + "@bruits/satteri-darwin-arm64": "0.9.5", + "@bruits/satteri-darwin-x64": "0.9.5", + "@bruits/satteri-linux-arm64-gnu": "0.9.5", + "@bruits/satteri-linux-arm64-musl": "0.9.5", + "@bruits/satteri-linux-x64-gnu": "0.9.5", + "@bruits/satteri-linux-x64-musl": "0.9.5", + "@bruits/satteri-wasm32-wasi": "0.9.5", + "@bruits/satteri-win32-arm64-msvc": "0.9.5", + "@bruits/satteri-win32-x64-msvc": "0.9.5" + } + }, + "node_modules/sax": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.1.tgz", + "integrity": "sha512-42tBVwLWnaQvW5zc4HbZrTuWccECCZfBi92FDuwtqxasH+JbPB3/FOKb1m222K42R4WxuxzzMsTswfzgtSu64Q==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=11.0.0" + } + }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/server-destroy": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/server-destroy/-/server-destroy-1.0.1.tgz", + "integrity": "sha512-rb+9B5YBIEzYcD6x2VKidaa+cqYBJQKnU4oe4E3ANwRRN56yk/ua1YCJT1n21NTS8w6CcOclAKNP3PhdCXKYtQ==", + "license": "ISC" + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/sharp": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.35.3.tgz", + "integrity": "sha512-ej0zVHuZGHCiABXcNxeYhpRnPNPAcvbG8RMdBAhDAxLKkCRVSpK3Iyu7qbqw3JMzoj0REeM6f3tJLtVwl0023Q==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@img/colour": "^1.1.0", + "detect-libc": "^2.1.2", + "semver": "^7.8.5" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.35.3", + "@img/sharp-darwin-x64": "0.35.3", + "@img/sharp-freebsd-wasm32": "0.35.3", + "@img/sharp-libvips-darwin-arm64": "1.3.2", + "@img/sharp-libvips-darwin-x64": "1.3.2", + "@img/sharp-libvips-linux-arm": "1.3.2", + "@img/sharp-libvips-linux-arm64": "1.3.2", + "@img/sharp-libvips-linux-ppc64": "1.3.2", + "@img/sharp-libvips-linux-riscv64": "1.3.2", + "@img/sharp-libvips-linux-s390x": "1.3.2", + "@img/sharp-libvips-linux-x64": "1.3.2", + "@img/sharp-libvips-linuxmusl-arm64": "1.3.2", + "@img/sharp-libvips-linuxmusl-x64": "1.3.2", + "@img/sharp-linux-arm": "0.35.3", + "@img/sharp-linux-arm64": "0.35.3", + "@img/sharp-linux-ppc64": "0.35.3", + "@img/sharp-linux-riscv64": "0.35.3", + "@img/sharp-linux-s390x": "0.35.3", + "@img/sharp-linux-x64": "0.35.3", + "@img/sharp-linuxmusl-arm64": "0.35.3", + "@img/sharp-linuxmusl-x64": "0.35.3", + "@img/sharp-webcontainers-wasm32": "0.35.3", + "@img/sharp-win32-arm64": "0.35.3", + "@img/sharp-win32-ia32": "0.35.3", + "@img/sharp-win32-x64": "0.35.3" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/sharp/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "license": "ISC", + "optional": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shiki": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-4.4.2.tgz", + "integrity": "sha512-P8F/dFhRevaw2uSdeIYlq/5SXZNY85DPtmXQ947gD1Zj2JqO5AkNvVVBar0Me9JkFx3uzVud/qOtP5ek9NEQGA==", + "license": "MIT", + "dependencies": { + "@shikijs/core": "4.4.2", + "@shikijs/engine-javascript": "4.4.2", + "@shikijs/engine-oniguruma": "4.4.2", + "@shikijs/langs": "4.4.2", + "@shikijs/themes": "4.4.2", + "@shikijs/types": "4.4.2", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.5" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "license": "MIT" + }, + "node_modules/smol-toml": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.7.1.tgz", + "integrity": "sha512-PPlsspAZ4jbMBu5DMFhfUGDQLu/vrL4SyBROVS37x8ynnVmFIs1VPBz1Co8Xks3TvpIaZXmU85y4DrQ+UyVFoQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">= 18" + }, + "funding": { + "url": "https://github.com/sponsors/cyyynthia" + } + }, + "node_modules/source-map": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">= 12" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/string-width": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.2.tgz", + "integrity": "sha512-GaPUh5gfdrYzqeVNZvUfT23vYYxXzKYidUcnMtJg/3rxRV63EFZy3k6xfKlmfeJD0176lnUV/Usr3XcwSvFzpg==", + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.5.0", + "strip-ansi": "^7.1.2" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/stringify-entities": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", + "license": "MIT", + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/style-to-js": { + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.21.tgz", + "integrity": "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==", + "license": "MIT", + "dependencies": { + "style-to-object": "1.0.14" + } + }, + "node_modules/style-to-object": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.14.tgz", + "integrity": "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==", + "license": "MIT", + "dependencies": { + "inline-style-parser": "0.2.7" + } + }, + "node_modules/svgo": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-4.0.2.tgz", + "integrity": "sha512-ekx94z1rRc5LDi6oSUaeRnYhd0UOJxdtQCL2rF8xpWxD3TPAsISWOrxezqGovqS38GRZOdpDfvQe3ts6F7nsng==", + "license": "MIT", + "dependencies": { + "commander": "^11.1.0", + "css-select": "^5.1.0", + "css-tree": "^3.0.1", + "css-what": "^6.1.0", + "csso": "^5.0.5", + "picocolors": "^1.1.1", + "sax": "^1.5.0" + }, + "bin": { + "svgo": "bin/svgo.js" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/svgo" + } + }, + "node_modules/tailwindcss": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.3.tgz", + "integrity": "sha512-gOhV3P7ufE62QDGg1zVaTgCR+EtPv92k2nIhVcVKcLmxT1sUBsQGhnZj175j+MqRt4zLF7ic+sCYjfhxMxj7YQ==", + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", + "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/tiny-inflate": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", + "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==", + "license": "MIT" + }, + "node_modules/tinyclip": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/tinyclip/-/tinyclip-0.1.15.tgz", + "integrity": "sha512-uo33abH+Ays0xYaDysoBt494Hb3hsEczMpcC0MwFl773pazORx4fmvKhclhR1wonUbB6vvpRsvVMwnhfqeMc+A==", + "license": "MIT", + "engines": { + "node": "^16.14.0 || >= 17.3.0" + } + }, + "node_modules/tinyexec": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.0.tgz", + "integrity": "sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/trough": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", + "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/typesafe-path": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/typesafe-path/-/typesafe-path-0.2.2.tgz", + "integrity": "sha512-OJabfkAg1WLZSqJAJ0Z6Sdt3utnbzr/jh+NAHoyWHJe8CMSy79Gm085094M9nvTPy22KzTVn5Zq5mbapCI/hPA==", + "license": "MIT" + }, + "node_modules/typescript": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-auto-import-cache": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/typescript-auto-import-cache/-/typescript-auto-import-cache-0.3.6.tgz", + "integrity": "sha512-RpuHXrknHdVdK7wv/8ug3Fr0WNsNi5l5aB8MYYuXhq2UH5lnEB1htJ1smhtD5VeCsGr2p8mUDtd83LCQDFVgjQ==", + "license": "MIT", + "dependencies": { + "semver": "^7.3.8" + } + }, + "node_modules/typescript-auto-import-cache/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ufo": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.4.tgz", + "integrity": "sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==", + "license": "MIT" + }, + "node_modules/ultrahtml": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ultrahtml/-/ultrahtml-1.7.0.tgz", + "integrity": "sha512-2xRd0VHoAQE4M+vF/DvFFB7pUV0ZxTW1TLi7lHQWnF/Sb5TPeEUV/l+hxcNnGO00ZXGnR0voCMmYRKQf+rvJ2g==", + "license": "MIT" + }, + "node_modules/uncrypto": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/uncrypto/-/uncrypto-0.1.3.tgz", + "integrity": "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==", + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.3.0.tgz", + "integrity": "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==", + "license": "MIT" + }, + "node_modules/unified": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", + "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "bail": "^2.0.0", + "devlop": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unifont": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/unifont/-/unifont-0.7.4.tgz", + "integrity": "sha512-oHeis4/xl42HUIeHuNZRGEvxj5AaIKR+bHPNegRq5LV1gdc3jundpONbjglKpihmJf+dswygdMJn3eftGIMemg==", + "license": "MIT", + "dependencies": { + "css-tree": "^3.1.0", + "ofetch": "^1.5.1", + "ohash": "^2.0.11" + } + }, + "node_modules/unist-util-find-after": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-find-after/-/unist-util-find-after-5.0.0.tgz", + "integrity": "sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-is": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", + "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-modify-children": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-modify-children/-/unist-util-modify-children-4.0.0.tgz", + "integrity": "sha512-+tdN5fGNddvsQdIzUF3Xx82CU9sMM+fA0dLgR9vOmT0oPT2jH+P1nd5lSqfCfXAw+93NhcXNY2qqvTUtE4cQkw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "array-iterate": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position-from-estree": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position-from-estree/-/unist-util-position-from-estree-2.0.0.tgz", + "integrity": "sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-remove-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-5.0.0.tgz", + "integrity": "sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.1.0.tgz", + "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-children": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unist-util-visit-children/-/unist-util-visit-children-3.0.0.tgz", + "integrity": "sha512-RgmdTfSBOg04sdPcpTSD1jzoNBjt9a80/ZCzp5cI9n1qPzLZWF9YdvWGN2zmTumP1HWhXKdUWexjy/Wy/lJ7tA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", + "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unstorage": { + "version": "1.17.5", + "resolved": "https://registry.npmjs.org/unstorage/-/unstorage-1.17.5.tgz", + "integrity": "sha512-0i3iqvRfx29hkNntHyQvJTpf5W9dQ9ZadSoRU8+xVlhVtT7jAX57fazYO9EHvcRCfBCyi5YRya7XCDOsbTgkPg==", + "license": "MIT", + "dependencies": { + "anymatch": "^3.1.3", + "chokidar": "^5.0.0", + "destr": "^2.0.5", + "h3": "^1.15.10", + "lru-cache": "^11.2.7", + "node-fetch-native": "^1.6.7", + "ofetch": "^1.5.1", + "ufo": "^1.6.3" + }, + "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 + } + } + }, + "node_modules/unstorage/node_modules/chokidar": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", + "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", + "license": "MIT", + "dependencies": { + "readdirp": "^5.0.0" + }, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/unstorage/node_modules/lru-cache": { + "version": "11.5.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz", + "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==", + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/unstorage/node_modules/readdirp": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.1.1.tgz", + "integrity": "sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA==", + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-location": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-5.0.3.tgz", + "integrity": "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", + "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vite": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.2.1.tgz", + "integrity": "sha512-EU/eS7BH3XROHh2YnBefjM6DBKA6ZeMZEYQbj7NLWg5wHYlhB8B/Mayd5XsgWq+NFYccDOTemRpdETWR6Ka/lw==", + "license": "MIT", + "dependencies": { + "lightningcss": "^1.33.0", + "picomatch": "^4.0.5", + "postcss": "^8.5.25", + "rolldown": "~1.2.1", + "tinyglobby": "^0.2.17" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.4.0", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/lightningcss": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", + "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.33.0", + "lightningcss-darwin-arm64": "1.33.0", + "lightningcss-darwin-x64": "1.33.0", + "lightningcss-freebsd-x64": "1.33.0", + "lightningcss-linux-arm-gnueabihf": "1.33.0", + "lightningcss-linux-arm64-gnu": "1.33.0", + "lightningcss-linux-arm64-musl": "1.33.0", + "lightningcss-linux-x64-gnu": "1.33.0", + "lightningcss-linux-x64-musl": "1.33.0", + "lightningcss-win32-arm64-msvc": "1.33.0", + "lightningcss-win32-x64-msvc": "1.33.0" + } + }, + "node_modules/vite/node_modules/lightningcss-android-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz", + "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-darwin-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz", + "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-darwin-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz", + "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-freebsd-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz", + "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz", + "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", + "cpu": [ + "arm" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz", + "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-linux-arm64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz", + "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-linux-x64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz", + "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-linux-x64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz", + "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz", + "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-win32-x64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz", + "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vitefu": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.1.3.tgz", + "integrity": "sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg==", + "license": "MIT", + "workspaces": [ + "tests/deps/*", + "tests/projects/*", + "tests/projects/workspace/packages/*" + ], + "peerDependencies": { + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } + } + }, + "node_modules/volar-service-css": { + "version": "0.0.71", + "resolved": "https://registry.npmjs.org/volar-service-css/-/volar-service-css-0.0.71.tgz", + "integrity": "sha512-wRRFt9BpjMKCazcgOh67MSjUjiWUCAh99DyYSDIOTuxaRjEtDC7PpB0k1Y1wbJIW/pVtMUSVbpPo3UGSm0Byxw==", + "license": "MIT", + "dependencies": { + "vscode-css-languageservice": "^6.3.0", + "vscode-languageserver-textdocument": "^1.0.11", + "vscode-uri": "^3.0.8" + }, + "peerDependencies": { + "@volar/language-service": "~2.4.0" + }, + "peerDependenciesMeta": { + "@volar/language-service": { + "optional": true + } + } + }, + "node_modules/volar-service-emmet": { + "version": "0.0.71", + "resolved": "https://registry.npmjs.org/volar-service-emmet/-/volar-service-emmet-0.0.71.tgz", + "integrity": "sha512-zqjzt6bN95e3CUstBm0PBFAJnrfz0ZAARka87fart46/gNCLLuP3Vujy8V/J8HEziTFLnfkgIASLFYPUhonJcA==", + "license": "MIT", + "dependencies": { + "@emmetio/css-parser": "^0.4.1", + "@emmetio/html-matcher": "^1.3.0", + "@vscode/emmet-helper": "^2.9.3", + "vscode-uri": "^3.0.8" + }, + "peerDependencies": { + "@volar/language-service": "~2.4.0" + }, + "peerDependenciesMeta": { + "@volar/language-service": { + "optional": true + } + } + }, + "node_modules/volar-service-html": { + "version": "0.0.71", + "resolved": "https://registry.npmjs.org/volar-service-html/-/volar-service-html-0.0.71.tgz", + "integrity": "sha512-e8tHPhgQ7ooLfudAEIku+kgd9pWkq3SSz8RbnQDI1+Eb8wbenkLGHqoirLqz5ORLV6wIMr2Iv08RWBG5eOcgpw==", + "license": "MIT", + "dependencies": { + "vscode-html-languageservice": "^5.3.0", + "vscode-languageserver-textdocument": "^1.0.11", + "vscode-uri": "^3.0.8" + }, + "peerDependencies": { + "@volar/language-service": "~2.4.0" + }, + "peerDependenciesMeta": { + "@volar/language-service": { + "optional": true + } + } + }, + "node_modules/volar-service-prettier": { + "version": "0.0.71", + "resolved": "https://registry.npmjs.org/volar-service-prettier/-/volar-service-prettier-0.0.71.tgz", + "integrity": "sha512-Rz7JVH3qD108UCdmIEiZvOBNljMt2nLFdbN8AXcDfn7xD9F5I2aCIsDVqBbXw21PsnxG0b7MfwtNF+zPS/NKUg==", + "license": "MIT", + "dependencies": { + "vscode-uri": "^3.0.8" + }, + "peerDependencies": { + "@volar/language-service": "~2.4.0", + "prettier": "^2.2 || ^3.0" + }, + "peerDependenciesMeta": { + "@volar/language-service": { + "optional": true + }, + "prettier": { + "optional": true + } + } + }, + "node_modules/volar-service-typescript": { + "version": "0.0.71", + "resolved": "https://registry.npmjs.org/volar-service-typescript/-/volar-service-typescript-0.0.71.tgz", + "integrity": "sha512-yTtM/BVT6hoyEYnDtaCyAtNhdNeS/mhTTABlBOdw3NNiRBUin3IznFJpgfjer4c6RYopiPjjQjc9VFhxVl1mLw==", + "license": "MIT", + "dependencies": { + "path-browserify": "^1.0.1", + "semver": "^7.6.2", + "typescript-auto-import-cache": "^0.3.5", + "vscode-languageserver-textdocument": "^1.0.11", + "vscode-nls": "^5.2.0", + "vscode-uri": "^3.0.8" + }, + "peerDependencies": { + "@volar/language-service": "~2.4.0" + }, + "peerDependenciesMeta": { + "@volar/language-service": { + "optional": true + } + } + }, + "node_modules/volar-service-typescript-twoslash-queries": { + "version": "0.0.71", + "resolved": "https://registry.npmjs.org/volar-service-typescript-twoslash-queries/-/volar-service-typescript-twoslash-queries-0.0.71.tgz", + "integrity": "sha512-9K2k72s4n7rV9s4bX0MyjbX9iBribvKZbBJKuEmTCZfeWJXs6Yh7bGpY4eoc7UufAjvpheBqwyZCOIPBvxCv0A==", + "license": "MIT", + "dependencies": { + "vscode-uri": "^3.0.8" + }, + "peerDependencies": { + "@volar/language-service": "~2.4.0" + }, + "peerDependenciesMeta": { + "@volar/language-service": { + "optional": true + } + } + }, + "node_modules/volar-service-typescript/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/volar-service-yaml": { + "version": "0.0.71", + "resolved": "https://registry.npmjs.org/volar-service-yaml/-/volar-service-yaml-0.0.71.tgz", + "integrity": "sha512-qYGWGuVpUTnZGu5P/CR4KLK4aIR8RrcVnmfZ2eRcj9q/I8VZCoC5yy9FtEvfNvnDp4MU17yhdJcvpQPIqhJS2Q==", + "license": "MIT", + "dependencies": { + "vscode-uri": "^3.0.8", + "yaml-language-server": "~1.23.0" + }, + "peerDependencies": { + "@volar/language-service": "~2.4.0" + }, + "peerDependenciesMeta": { + "@volar/language-service": { + "optional": true + } + } + }, + "node_modules/vscode-css-languageservice": { + "version": "6.3.10", + "resolved": "https://registry.npmjs.org/vscode-css-languageservice/-/vscode-css-languageservice-6.3.10.tgz", + "integrity": "sha512-eq5N9Er3fC4vA9zd9EFhyBG90wtCCuXgRSpAndaOgXMh1Wgep5lBgRIeDgjZBW9pa+332yC9+49cZMW8jcL3MA==", + "license": "MIT", + "dependencies": { + "@vscode/l10n": "^0.0.18", + "vscode-languageserver-textdocument": "^1.0.12", + "vscode-languageserver-types": "3.17.5", + "vscode-uri": "^3.1.0" + } + }, + "node_modules/vscode-css-languageservice/node_modules/vscode-languageserver-types": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", + "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==", + "license": "MIT" + }, + "node_modules/vscode-html-languageservice": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/vscode-html-languageservice/-/vscode-html-languageservice-5.6.2.tgz", + "integrity": "sha512-ulCrSnFnfQ16YzvwnYUgEbUEl/ZG7u2eV27YhvLObSHKkb8fw1Z9cgsnUwjTEeDIdJDoTDTDpxuhQwoenoLNMg==", + "license": "MIT", + "dependencies": { + "@vscode/l10n": "^0.0.18", + "vscode-languageserver-textdocument": "^1.0.12", + "vscode-languageserver-types": "^3.17.5", + "vscode-uri": "^3.1.0" + } + }, + "node_modules/vscode-json-languageservice": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/vscode-json-languageservice/-/vscode-json-languageservice-4.1.8.tgz", + "integrity": "sha512-0vSpg6Xd9hfV+eZAaYN63xVVMOTmJ4GgHxXnkLCh+9RsQBkWKIghzLhW2B9ebfG+LQQg8uLtsQ2aUKjTgE+QOg==", + "license": "MIT", + "dependencies": { + "jsonc-parser": "^3.0.0", + "vscode-languageserver-textdocument": "^1.0.1", + "vscode-languageserver-types": "^3.16.0", + "vscode-nls": "^5.0.0", + "vscode-uri": "^3.0.2" + }, + "engines": { + "npm": ">=7.0.0" + } + }, + "node_modules/vscode-jsonrpc": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-9.0.1.tgz", + "integrity": "sha512-rfuA6T75H6m5EkbhtEPzre9pT0HPcDI2MMy4+nPFIBks5J8JBAUHD4tRYSgaBOijIEC7SRkC1kKyXTLqbmh9jw==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/vscode-languageserver": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-9.0.1.tgz", + "integrity": "sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==", + "license": "MIT", + "dependencies": { + "vscode-languageserver-protocol": "3.17.5" + }, + "bin": { + "installServerIntoExtension": "bin/installServerIntoExtension" + } + }, + "node_modules/vscode-languageserver-protocol": { + "version": "3.18.2", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.18.2.tgz", + "integrity": "sha512-XRyDbT0Pp3sSNti3JmxVEUMySWCSi1hhM+/KUlCy1hV1zmrqpM1OwO12EAki8blhmLuIMpaJrYbo0OzGVfK2Qg==", + "license": "MIT", + "dependencies": { + "vscode-jsonrpc": "9.0.1", + "vscode-languageserver-types": "3.18.0" + } + }, + "node_modules/vscode-languageserver-textdocument": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz", + "integrity": "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==", + "license": "MIT" + }, + "node_modules/vscode-languageserver-types": { + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.18.0.tgz", + "integrity": "sha512-8TsGPNMIMiiBdkORgRSvLjuiEIiAFtO+KssmYWxQ+uSVvlf7RjK8YKCOjPzZ+YA04jXEV7+7LvkSmHkhpNS99g==", + "license": "MIT" + }, + "node_modules/vscode-languageserver/node_modules/vscode-jsonrpc": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", + "integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/vscode-languageserver/node_modules/vscode-languageserver-protocol": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz", + "integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==", + "license": "MIT", + "dependencies": { + "vscode-jsonrpc": "8.2.0", + "vscode-languageserver-types": "3.17.5" + } + }, + "node_modules/vscode-languageserver/node_modules/vscode-languageserver-types": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", + "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==", + "license": "MIT" + }, + "node_modules/vscode-nls": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-5.2.0.tgz", + "integrity": "sha512-RAaHx7B14ZU04EU31pT+rKz2/zSl7xMsfIZuo8pd+KZO6PXtQmpevpq3vxvWNcrGbdmhM/rr5Uw5Mz+NBfhVng==", + "license": "MIT" + }, + "node_modules/vscode-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", + "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", + "license": "MIT" + }, + "node_modules/web-namespaces": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", + "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/wrap-ansi": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/xxhash-wasm": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/xxhash-wasm/-/xxhash-wasm-1.1.0.tgz", + "integrity": "sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA==", + "license": "MIT" + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "license": "ISC" + }, + "node_modules/yaml": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", + "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, + "node_modules/yaml-language-server": { + "version": "1.23.0", + "resolved": "https://registry.npmjs.org/yaml-language-server/-/yaml-language-server-1.23.0.tgz", + "integrity": "sha512-3qVyCOexLCWw06PQa5kRPwvMWMZ/eZeCRWUvgD6a0OkqL/4iCnxy2WumbWifa937Uo5xhyWJ0uxlU39ljhNh7A==", + "license": "MIT", + "dependencies": { + "@vscode/l10n": "^0.0.18", + "ajv": "^8.17.1", + "ajv-draft-04": "^1.0.0", + "ajv-i18n": "^4.2.0", + "prettier": "^3.8.1", + "request-light": "^0.5.7", + "vscode-json-languageservice": "4.1.8", + "vscode-languageserver": "^9.0.0", + "vscode-languageserver-textdocument": "^1.0.1", + "vscode-languageserver-types": "^3.16.0", + "vscode-uri": "^3.0.2", + "yaml": "2.8.3" + }, + "bin": { + "yaml-language-server": "bin/yaml-language-server" + } + }, + "node_modules/yaml-language-server/node_modules/request-light": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/request-light/-/request-light-0.5.8.tgz", + "integrity": "sha512-3Zjgh+8b5fhRJBQZoy+zbVKpAQGLyka0MPgW3zruTF4dFFJ8Fqcfu9YsAvi/rvdcaTeWG3MkbZv4WKxAn/84Lg==", + "license": "MIT" + }, + "node_modules/yaml-language-server/node_modules/yaml": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz", + "integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==", + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, + "node_modules/yargs": { + "version": "18.1.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.1.0.tgz", + "integrity": "sha512-2rAgRKu54VsHkqI0/tYkmluGXHD4KW7yZoycuqDQ15QOTnc2VVfy0nN/1eMhnQLO00A+dwtK20xuCnc1YGeUyg==", + "license": "MIT", + "dependencies": { + "cliui": "^9.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "string-width": "^8.2.1", + "y18n": "^5.0.5", + "yargs-parser": "^22.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, + "node_modules/yargs-parser": { + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz", + "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==", + "license": "ISC", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, + "node_modules/yocto-queue": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.2.tgz", + "integrity": "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==", + "license": "MIT", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", + "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + } + } +} diff --git a/legacy/package.json b/legacy/package.json new file mode 100644 index 0000000..f432565 --- /dev/null +++ b/legacy/package.json @@ -0,0 +1,39 @@ +{ + "name": "greg-kilmer-profile", + "version": "1.0.0", + "type": "module", + "private": true, + "description": "Personal profile / portfolio website for Greg Kilmer. Astro + React Islands + Node server.", + "scripts": { + "dev": "astro dev", + "start": "astro dev", + "build": "astro check && astro build", + "preview": "astro preview", + "serve": "node ./dist/server/entry.mjs", + "astro": "astro", + "check": "astro check", + "typecheck": "astro check && tsc --noEmit" + }, + "dependencies": { + "@astrojs/check": "^0.9.9", + "@astrojs/mdx": "^7.0.2", + "@astrojs/node": "^11.0.2", + "@astrojs/react": "^6.0.2", + "@fontsource-variable/cinzel": "^5.3.0", + "@fontsource-variable/jetbrains-mono": "^5.2.8", + "@fontsource-variable/montserrat": "^5.3.0", + "@iconify-json/mdi": "^1.2.3", + "@tailwindcss/vite": "^4.3.2", + "@types/nodemailer": "^8.0.1", + "@types/react": "^19.2.17", + "@types/react-dom": "^19.2.3", + "astro": "^7.2.0", + "framer-motion": "^12.42.2", + "nodemailer": "^9.0.3", + "react": "^19.2.7", + "react-dom": "^19.2.7", + "tailwindcss": "^4.3.2", + "typescript": "^6.0.3", + "zod": "^4.4.3" + } +} diff --git a/public/.well-known/security.txt b/legacy/public/.well-known/security.txt similarity index 100% rename from public/.well-known/security.txt rename to legacy/public/.well-known/security.txt diff --git a/public/apple-touch-icon.png b/legacy/public/apple-touch-icon.png similarity index 100% rename from public/apple-touch-icon.png rename to legacy/public/apple-touch-icon.png diff --git a/public/brand/patiun-seal.svg b/legacy/public/brand/patiun-seal.svg similarity index 100% rename from public/brand/patiun-seal.svg rename to legacy/public/brand/patiun-seal.svg diff --git a/public/favicon-16.png b/legacy/public/favicon-16.png similarity index 100% rename from public/favicon-16.png rename to legacy/public/favicon-16.png diff --git a/public/favicon-32.png b/legacy/public/favicon-32.png similarity index 100% rename from public/favicon-32.png rename to legacy/public/favicon-32.png diff --git a/public/favicon-48.png b/legacy/public/favicon-48.png similarity index 100% rename from public/favicon-48.png rename to legacy/public/favicon-48.png diff --git a/public/favicon.ico b/legacy/public/favicon.ico similarity index 100% rename from public/favicon.ico rename to legacy/public/favicon.ico diff --git a/public/favicon.svg b/legacy/public/favicon.svg similarity index 100% rename from public/favicon.svg rename to legacy/public/favicon.svg diff --git a/public/icon-192.png b/legacy/public/icon-192.png similarity index 100% rename from public/icon-192.png rename to legacy/public/icon-192.png diff --git a/public/icon-512.png b/legacy/public/icon-512.png similarity index 100% rename from public/icon-512.png rename to legacy/public/icon-512.png diff --git a/public/icon-maskable-192.png b/legacy/public/icon-maskable-192.png similarity index 100% rename from public/icon-maskable-192.png rename to legacy/public/icon-maskable-192.png diff --git a/public/icon-maskable-512.png b/legacy/public/icon-maskable-512.png similarity index 100% rename from public/icon-maskable-512.png rename to legacy/public/icon-maskable-512.png diff --git a/public/icon_2.png b/legacy/public/icon_2.png similarity index 100% rename from public/icon_2.png rename to legacy/public/icon_2.png diff --git a/public/images/OLDgaming-setup.jpg b/legacy/public/images/OLDgaming-setup.jpg similarity index 100% rename from public/images/OLDgaming-setup.jpg rename to legacy/public/images/OLDgaming-setup.jpg diff --git a/public/images/OLDhero-portrait.jpg b/legacy/public/images/OLDhero-portrait.jpg similarity index 100% rename from public/images/OLDhero-portrait.jpg rename to legacy/public/images/OLDhero-portrait.jpg diff --git a/public/images/OLDpro-workspace.jpg b/legacy/public/images/OLDpro-workspace.jpg similarity index 100% rename from public/images/OLDpro-workspace.jpg rename to legacy/public/images/OLDpro-workspace.jpg diff --git a/public/images/about-portrait.jpg b/legacy/public/images/about-portrait.jpg similarity index 100% rename from public/images/about-portrait.jpg rename to legacy/public/images/about-portrait.jpg diff --git a/public/images/anime-hero.jpg b/legacy/public/images/anime-hero.jpg similarity index 100% rename from public/images/anime-hero.jpg rename to legacy/public/images/anime-hero.jpg diff --git a/public/images/anime/attack-on-titan.jpg b/legacy/public/images/anime/attack-on-titan.jpg similarity index 100% rename from public/images/anime/attack-on-titan.jpg rename to legacy/public/images/anime/attack-on-titan.jpg diff --git a/public/images/anime/bleach.jpg b/legacy/public/images/anime/bleach.jpg similarity index 100% rename from public/images/anime/bleach.jpg rename to legacy/public/images/anime/bleach.jpg diff --git a/public/images/anime/code-geass.jpg b/legacy/public/images/anime/code-geass.jpg similarity index 100% rename from public/images/anime/code-geass.jpg rename to legacy/public/images/anime/code-geass.jpg diff --git a/public/images/anime/demon-slayer.jpg b/legacy/public/images/anime/demon-slayer.jpg similarity index 100% rename from public/images/anime/demon-slayer.jpg rename to legacy/public/images/anime/demon-slayer.jpg diff --git a/public/images/anime/frieren-beyond-journeys-end.jpg b/legacy/public/images/anime/frieren-beyond-journeys-end.jpg similarity index 100% rename from public/images/anime/frieren-beyond-journeys-end.jpg rename to legacy/public/images/anime/frieren-beyond-journeys-end.jpg diff --git a/public/images/anime/gundam-all-series.jpg b/legacy/public/images/anime/gundam-all-series.jpg similarity index 100% rename from public/images/anime/gundam-all-series.jpg rename to legacy/public/images/anime/gundam-all-series.jpg diff --git a/public/images/anime/jujutsu-kaisen.jpg b/legacy/public/images/anime/jujutsu-kaisen.jpg similarity index 100% rename from public/images/anime/jujutsu-kaisen.jpg rename to legacy/public/images/anime/jujutsu-kaisen.jpg diff --git a/public/images/anime/kimetsu-no-yaiba.jpg b/legacy/public/images/anime/kimetsu-no-yaiba.jpg similarity index 100% rename from public/images/anime/kimetsu-no-yaiba.jpg rename to legacy/public/images/anime/kimetsu-no-yaiba.jpg diff --git a/public/images/anime/magi-the-labyrinth-of-magic.jpg b/legacy/public/images/anime/magi-the-labyrinth-of-magic.jpg similarity index 100% rename from public/images/anime/magi-the-labyrinth-of-magic.jpg rename to legacy/public/images/anime/magi-the-labyrinth-of-magic.jpg diff --git a/public/images/anime/my-hero-academia.jpg b/legacy/public/images/anime/my-hero-academia.jpg similarity index 100% rename from public/images/anime/my-hero-academia.jpg rename to legacy/public/images/anime/my-hero-academia.jpg diff --git a/public/images/anime/one-piece.jpg b/legacy/public/images/anime/one-piece.jpg similarity index 100% rename from public/images/anime/one-piece.jpg rename to legacy/public/images/anime/one-piece.jpg diff --git a/public/images/anime/that-time-i-got-reincarnated-as-a-slime.jpg b/legacy/public/images/anime/that-time-i-got-reincarnated-as-a-slime.jpg similarity index 100% rename from public/images/anime/that-time-i-got-reincarnated-as-a-slime.jpg rename to legacy/public/images/anime/that-time-i-got-reincarnated-as-a-slime.jpg diff --git a/public/images/anime/vinland-saga.jpg b/legacy/public/images/anime/vinland-saga.jpg similarity index 100% rename from public/images/anime/vinland-saga.jpg rename to legacy/public/images/anime/vinland-saga.jpg diff --git a/public/images/boardgames/.gitkeep b/legacy/public/images/boardgames/.gitkeep similarity index 100% rename from public/images/boardgames/.gitkeep rename to legacy/public/images/boardgames/.gitkeep diff --git a/public/images/boardgames/wyrmspan.jpg b/legacy/public/images/boardgames/wyrmspan.jpg similarity index 100% rename from public/images/boardgames/wyrmspan.jpg rename to legacy/public/images/boardgames/wyrmspan.jpg diff --git a/public/images/cards.jpg b/legacy/public/images/cards.jpg similarity index 100% rename from public/images/cards.jpg rename to legacy/public/images/cards.jpg diff --git a/public/images/deckboxes-example-1.svg b/legacy/public/images/deckboxes-example-1.svg similarity index 100% rename from public/images/deckboxes-example-1.svg rename to legacy/public/images/deckboxes-example-1.svg diff --git a/public/images/deckboxes-example-2.svg b/legacy/public/images/deckboxes-example-2.svg similarity index 100% rename from public/images/deckboxes-example-2.svg rename to legacy/public/images/deckboxes-example-2.svg diff --git a/public/images/deckboxes.jpg b/legacy/public/images/deckboxes.jpg similarity index 100% rename from public/images/deckboxes.jpg rename to legacy/public/images/deckboxes.jpg diff --git a/public/images/dice_galaxy.jpg b/legacy/public/images/dice_galaxy.jpg similarity index 100% rename from public/images/dice_galaxy.jpg rename to legacy/public/images/dice_galaxy.jpg diff --git a/public/images/dice_golden_moss.jpg b/legacy/public/images/dice_golden_moss.jpg similarity index 100% rename from public/images/dice_golden_moss.jpg rename to legacy/public/images/dice_golden_moss.jpg diff --git a/public/images/dice_scarlet.jpg b/legacy/public/images/dice_scarlet.jpg similarity index 100% rename from public/images/dice_scarlet.jpg rename to legacy/public/images/dice_scarlet.jpg diff --git a/public/images/dice_seastone.jpg b/legacy/public/images/dice_seastone.jpg similarity index 100% rename from public/images/dice_seastone.jpg rename to legacy/public/images/dice_seastone.jpg diff --git a/public/images/fantasy-books.jpg b/legacy/public/images/fantasy-books.jpg similarity index 100% rename from public/images/fantasy-books.jpg rename to legacy/public/images/fantasy-books.jpg diff --git a/public/images/fav.png b/legacy/public/images/fav.png similarity index 100% rename from public/images/fav.png rename to legacy/public/images/fav.png diff --git a/public/images/games/.gitkeep b/legacy/public/images/games/.gitkeep similarity index 100% rename from public/images/games/.gitkeep rename to legacy/public/images/games/.gitkeep diff --git a/public/images/games/age-of-empires.jpg b/legacy/public/images/games/age-of-empires.jpg similarity index 100% rename from public/images/games/age-of-empires.jpg rename to legacy/public/images/games/age-of-empires.jpg diff --git a/public/images/games/ark-survival-evolved.jpg b/legacy/public/images/games/ark-survival-evolved.jpg similarity index 100% rename from public/images/games/ark-survival-evolved.jpg rename to legacy/public/images/games/ark-survival-evolved.jpg diff --git a/public/images/games/dave-the-diver.jpg b/legacy/public/images/games/dave-the-diver.jpg similarity index 100% rename from public/images/games/dave-the-diver.jpg rename to legacy/public/images/games/dave-the-diver.jpg diff --git a/public/images/games/elden-ring.jpg b/legacy/public/images/games/elden-ring.jpg similarity index 100% rename from public/images/games/elden-ring.jpg rename to legacy/public/images/games/elden-ring.jpg diff --git a/public/images/games/granblue-relink.jpg b/legacy/public/images/games/granblue-relink.jpg similarity index 100% rename from public/images/games/granblue-relink.jpg rename to legacy/public/images/games/granblue-relink.jpg diff --git a/public/images/games/minecraft.jpg b/legacy/public/images/games/minecraft.jpg similarity index 100% rename from public/images/games/minecraft.jpg rename to legacy/public/images/games/minecraft.jpg diff --git a/public/images/games/pok-mon-crystal-version.jpg b/legacy/public/images/games/pok-mon-crystal-version.jpg similarity index 100% rename from public/images/games/pok-mon-crystal-version.jpg rename to legacy/public/images/games/pok-mon-crystal-version.jpg diff --git a/public/images/games/satisfactory.jpg b/legacy/public/images/games/satisfactory.jpg similarity index 100% rename from public/images/games/satisfactory.jpg rename to legacy/public/images/games/satisfactory.jpg diff --git a/public/images/games/sly-3-honor-among-thieves.jpg b/legacy/public/images/games/sly-3-honor-among-thieves.jpg similarity index 100% rename from public/images/games/sly-3-honor-among-thieves.jpg rename to legacy/public/images/games/sly-3-honor-among-thieves.jpg diff --git a/public/images/games/sly-cooper-and-the-thievius-raccoonus.jpg b/legacy/public/images/games/sly-cooper-and-the-thievius-raccoonus.jpg similarity index 100% rename from public/images/games/sly-cooper-and-the-thievius-raccoonus.jpg rename to legacy/public/images/games/sly-cooper-and-the-thievius-raccoonus.jpg diff --git a/public/images/games/star-wars-knights-of-the-old-republic.jpg b/legacy/public/images/games/star-wars-knights-of-the-old-republic.jpg similarity index 100% rename from public/images/games/star-wars-knights-of-the-old-republic.jpg rename to legacy/public/images/games/star-wars-knights-of-the-old-republic.jpg diff --git a/public/images/games/stardew-valley.jpg b/legacy/public/images/games/stardew-valley.jpg similarity index 100% rename from public/images/games/stardew-valley.jpg rename to legacy/public/images/games/stardew-valley.jpg diff --git a/public/images/gaming-setup.jpg b/legacy/public/images/gaming-setup.jpg similarity index 100% rename from public/images/gaming-setup.jpg rename to legacy/public/images/gaming-setup.jpg diff --git a/public/images/hero-portrait.jpg b/legacy/public/images/hero-portrait.jpg similarity index 100% rename from public/images/hero-portrait.jpg rename to legacy/public/images/hero-portrait.jpg diff --git a/public/images/kashi_at_work.jpg b/legacy/public/images/kashi_at_work.jpg similarity index 100% rename from public/images/kashi_at_work.jpg rename to legacy/public/images/kashi_at_work.jpg diff --git a/public/images/making-print - Copy.jpg b/legacy/public/images/making-print - Copy.jpg similarity index 100% rename from public/images/making-print - Copy.jpg rename to legacy/public/images/making-print - Copy.jpg diff --git a/public/images/making-print.jpg b/legacy/public/images/making-print.jpg similarity index 100% rename from public/images/making-print.jpg rename to legacy/public/images/making-print.jpg diff --git a/public/images/making-resin.jpg b/legacy/public/images/making-resin.jpg similarity index 100% rename from public/images/making-resin.jpg rename to legacy/public/images/making-resin.jpg diff --git a/public/images/minis.jpg b/legacy/public/images/minis.jpg similarity index 100% rename from public/images/minis.jpg rename to legacy/public/images/minis.jpg diff --git a/public/images/movies-hero.jpg b/legacy/public/images/movies-hero.jpg similarity index 100% rename from public/images/movies-hero.jpg rename to legacy/public/images/movies-hero.jpg diff --git a/public/images/movies/avengers-endgame.jpg b/legacy/public/images/movies/avengers-endgame.jpg similarity index 100% rename from public/images/movies/avengers-endgame.jpg rename to legacy/public/images/movies/avengers-endgame.jpg diff --git a/public/images/movies/avengers-infinity-war.jpg b/legacy/public/images/movies/avengers-infinity-war.jpg similarity index 100% rename from public/images/movies/avengers-infinity-war.jpg rename to legacy/public/images/movies/avengers-infinity-war.jpg diff --git a/public/images/movies/contact.jpg b/legacy/public/images/movies/contact.jpg similarity index 100% rename from public/images/movies/contact.jpg rename to legacy/public/images/movies/contact.jpg diff --git a/public/images/movies/howls-moving-castle.jpg b/legacy/public/images/movies/howls-moving-castle.jpg similarity index 100% rename from public/images/movies/howls-moving-castle.jpg rename to legacy/public/images/movies/howls-moving-castle.jpg diff --git a/public/images/movies/jurassic-park.jpg b/legacy/public/images/movies/jurassic-park.jpg similarity index 100% rename from public/images/movies/jurassic-park.jpg rename to legacy/public/images/movies/jurassic-park.jpg diff --git a/public/images/movies/oceans-eleven.jpg b/legacy/public/images/movies/oceans-eleven.jpg similarity index 100% rename from public/images/movies/oceans-eleven.jpg rename to legacy/public/images/movies/oceans-eleven.jpg diff --git a/public/images/movies/star-wars-episode-v-the-empire-strikes-back.jpg b/legacy/public/images/movies/star-wars-episode-v-the-empire-strikes-back.jpg similarity index 100% rename from public/images/movies/star-wars-episode-v-the-empire-strikes-back.jpg rename to legacy/public/images/movies/star-wars-episode-v-the-empire-strikes-back.jpg diff --git a/public/images/movies/the-matrix.jpg b/legacy/public/images/movies/the-matrix.jpg similarity index 100% rename from public/images/movies/the-matrix.jpg rename to legacy/public/images/movies/the-matrix.jpg diff --git a/public/images/movies/the-princess-bride.jpg b/legacy/public/images/movies/the-princess-bride.jpg similarity index 100% rename from public/images/movies/the-princess-bride.jpg rename to legacy/public/images/movies/the-princess-bride.jpg diff --git a/public/images/movies/transformers.jpg b/legacy/public/images/movies/transformers.jpg similarity index 100% rename from public/images/movies/transformers.jpg rename to legacy/public/images/movies/transformers.jpg diff --git a/public/images/pokemon-cards.jpg b/legacy/public/images/pokemon-cards.jpg similarity index 100% rename from public/images/pokemon-cards.jpg rename to legacy/public/images/pokemon-cards.jpg diff --git a/public/images/pro-team.jpg b/legacy/public/images/pro-team.jpg similarity index 100% rename from public/images/pro-team.jpg rename to legacy/public/images/pro-team.jpg diff --git a/public/images/pro-workspace.jpg b/legacy/public/images/pro-workspace.jpg similarity index 100% rename from public/images/pro-workspace.jpg rename to legacy/public/images/pro-workspace.jpg diff --git a/public/images/proxies-example-1.svg b/legacy/public/images/proxies-example-1.svg similarity index 100% rename from public/images/proxies-example-1.svg rename to legacy/public/images/proxies-example-1.svg diff --git a/public/images/proxies-example-2.svg b/legacy/public/images/proxies-example-2.svg similarity index 100% rename from public/images/proxies-example-2.svg rename to legacy/public/images/proxies-example-2.svg diff --git a/public/images/proxies.jpg b/legacy/public/images/proxies.jpg similarity index 100% rename from public/images/proxies.jpg rename to legacy/public/images/proxies.jpg diff --git a/public/images/proxies2.jpg b/legacy/public/images/proxies2.jpg similarity index 100% rename from public/images/proxies2.jpg rename to legacy/public/images/proxies2.jpg diff --git a/public/images/reading-nook.jpg b/legacy/public/images/reading-nook.jpg similarity index 100% rename from public/images/reading-nook.jpg rename to legacy/public/images/reading-nook.jpg diff --git a/public/images/reading/brandon-sanderson-cosmere.jpg b/legacy/public/images/reading/brandon-sanderson-cosmere.jpg similarity index 100% rename from public/images/reading/brandon-sanderson-cosmere.jpg rename to legacy/public/images/reading/brandon-sanderson-cosmere.jpg diff --git a/public/images/reading/desolate-era.jpg b/legacy/public/images/reading/desolate-era.jpg similarity index 100% rename from public/images/reading/desolate-era.jpg rename to legacy/public/images/reading/desolate-era.jpg diff --git a/public/images/reading/douglas-preston-lincoln-child-pendergast.jpg b/legacy/public/images/reading/douglas-preston-lincoln-child-pendergast.jpg similarity index 100% rename from public/images/reading/douglas-preston-lincoln-child-pendergast.jpg rename to legacy/public/images/reading/douglas-preston-lincoln-child-pendergast.jpg diff --git a/public/images/reading/emily-rodda-deltora-quest.jpg b/legacy/public/images/reading/emily-rodda-deltora-quest.jpg similarity index 100% rename from public/images/reading/emily-rodda-deltora-quest.jpg rename to legacy/public/images/reading/emily-rodda-deltora-quest.jpg diff --git a/public/images/reading/frank-herbert-dune.jpg b/legacy/public/images/reading/frank-herbert-dune.jpg similarity index 100% rename from public/images/reading/frank-herbert-dune.jpg rename to legacy/public/images/reading/frank-herbert-dune.jpg diff --git a/public/images/reading/j-r-r-tolkien-the-lord-of-the-rings.jpg b/legacy/public/images/reading/j-r-r-tolkien-the-lord-of-the-rings.jpg similarity index 100% rename from public/images/reading/j-r-r-tolkien-the-lord-of-the-rings.jpg rename to legacy/public/images/reading/j-r-r-tolkien-the-lord-of-the-rings.jpg diff --git a/public/images/reading/omniscient-reader-s-viewpoint.jpg b/legacy/public/images/reading/omniscient-reader-s-viewpoint.jpg similarity index 100% rename from public/images/reading/omniscient-reader-s-viewpoint.jpg rename to legacy/public/images/reading/omniscient-reader-s-viewpoint.jpg diff --git a/public/images/reading/overgeared.jpg b/legacy/public/images/reading/overgeared.jpg similarity index 100% rename from public/images/reading/overgeared.jpg rename to legacy/public/images/reading/overgeared.jpg diff --git a/public/images/reading/solo-leveling.jpg b/legacy/public/images/reading/solo-leveling.jpg similarity index 100% rename from public/images/reading/solo-leveling.jpg rename to legacy/public/images/reading/solo-leveling.jpg diff --git a/public/images/reading/the-king-s-avatar.jpg b/legacy/public/images/reading/the-king-s-avatar.jpg similarity index 100% rename from public/images/reading/the-king-s-avatar.jpg rename to legacy/public/images/reading/the-king-s-avatar.jpg diff --git a/public/images/resin_rings.jpg b/legacy/public/images/resin_rings.jpg similarity index 100% rename from public/images/resin_rings.jpg rename to legacy/public/images/resin_rings.jpg diff --git a/public/images/store/cards/PXL_20260806_204112875.jpg b/legacy/public/images/store/cards/PXL_20260806_204112875.jpg similarity index 100% rename from public/images/store/cards/PXL_20260806_204112875.jpg rename to legacy/public/images/store/cards/PXL_20260806_204112875.jpg diff --git a/public/images/store/cards/PXL_20260806_204116184.jpg b/legacy/public/images/store/cards/PXL_20260806_204116184.jpg similarity index 100% rename from public/images/store/cards/PXL_20260806_204116184.jpg rename to legacy/public/images/store/cards/PXL_20260806_204116184.jpg diff --git a/public/images/store/cards/PXL_20260806_204128368.jpg b/legacy/public/images/store/cards/PXL_20260806_204128368.jpg similarity index 100% rename from public/images/store/cards/PXL_20260806_204128368.jpg rename to legacy/public/images/store/cards/PXL_20260806_204128368.jpg diff --git a/public/images/store/cards/PXL_20260806_204129979.jpg b/legacy/public/images/store/cards/PXL_20260806_204129979.jpg similarity index 100% rename from public/images/store/cards/PXL_20260806_204129979.jpg rename to legacy/public/images/store/cards/PXL_20260806_204129979.jpg diff --git a/public/images/store/cards/disclosure.jpg b/legacy/public/images/store/cards/disclosure.jpg similarity index 100% rename from public/images/store/cards/disclosure.jpg rename to legacy/public/images/store/cards/disclosure.jpg diff --git a/public/images/store/cards/foil_cloud_measured.jpg b/legacy/public/images/store/cards/foil_cloud_measured.jpg similarity index 100% rename from public/images/store/cards/foil_cloud_measured.jpg rename to legacy/public/images/store/cards/foil_cloud_measured.jpg diff --git a/public/images/store/cards/ghalta_duo.jpg b/legacy/public/images/store/cards/ghalta_duo.jpg similarity index 100% rename from public/images/store/cards/ghalta_duo.jpg rename to legacy/public/images/store/cards/ghalta_duo.jpg diff --git a/public/images/store/cards/proxies.jpg b/legacy/public/images/store/cards/proxies.jpg similarity index 100% rename from public/images/store/cards/proxies.jpg rename to legacy/public/images/store/cards/proxies.jpg diff --git a/public/images/store/cards/proxies2.jpg b/legacy/public/images/store/cards/proxies2.jpg similarity index 100% rename from public/images/store/cards/proxies2.jpg rename to legacy/public/images/store/cards/proxies2.jpg diff --git a/public/images/store/cards/ravenous_duo.jpg b/legacy/public/images/store/cards/ravenous_duo.jpg similarity index 100% rename from public/images/store/cards/ravenous_duo.jpg rename to legacy/public/images/store/cards/ravenous_duo.jpg diff --git a/public/images/store/cards/sleeved.jpg b/legacy/public/images/store/cards/sleeved.jpg similarity index 100% rename from public/images/store/cards/sleeved.jpg rename to legacy/public/images/store/cards/sleeved.jpg diff --git a/public/images/store/deckboxes/black_front.jpg b/legacy/public/images/store/deckboxes/black_front.jpg similarity index 100% rename from public/images/store/deckboxes/black_front.jpg rename to legacy/public/images/store/deckboxes/black_front.jpg diff --git a/public/images/store/deckboxes/black_side.jpg b/legacy/public/images/store/deckboxes/black_side.jpg similarity index 100% rename from public/images/store/deckboxes/black_side.jpg rename to legacy/public/images/store/deckboxes/black_side.jpg diff --git a/public/images/store/deckboxes/ff_back.jpg b/legacy/public/images/store/deckboxes/ff_back.jpg similarity index 100% rename from public/images/store/deckboxes/ff_back.jpg rename to legacy/public/images/store/deckboxes/ff_back.jpg diff --git a/public/images/store/deckboxes/ff_front.jpg b/legacy/public/images/store/deckboxes/ff_front.jpg similarity index 100% rename from public/images/store/deckboxes/ff_front.jpg rename to legacy/public/images/store/deckboxes/ff_front.jpg diff --git a/public/images/store/deckboxes/ff_open.jpg b/legacy/public/images/store/deckboxes/ff_open.jpg similarity index 100% rename from public/images/store/deckboxes/ff_open.jpg rename to legacy/public/images/store/deckboxes/ff_open.jpg diff --git a/public/images/store/deckboxes/ff_side.jpg b/legacy/public/images/store/deckboxes/ff_side.jpg similarity index 100% rename from public/images/store/deckboxes/ff_side.jpg rename to legacy/public/images/store/deckboxes/ff_side.jpg diff --git a/public/images/tea-gaiwan.jpg b/legacy/public/images/tea-gaiwan.jpg similarity index 100% rename from public/images/tea-gaiwan.jpg rename to legacy/public/images/tea-gaiwan.jpg diff --git a/public/images/ttrpg-boardgame-shelf.jpg b/legacy/public/images/ttrpg-boardgame-shelf.jpg similarity index 100% rename from public/images/ttrpg-boardgame-shelf.jpg rename to legacy/public/images/ttrpg-boardgame-shelf.jpg diff --git a/public/images/ttrpg-dice.jpg b/legacy/public/images/ttrpg-dice.jpg similarity index 100% rename from public/images/ttrpg-dice.jpg rename to legacy/public/images/ttrpg-dice.jpg diff --git a/public/images/ttrpg-mini.jpg b/legacy/public/images/ttrpg-mini.jpg similarity index 100% rename from public/images/ttrpg-mini.jpg rename to legacy/public/images/ttrpg-mini.jpg diff --git a/public/images/ttrpg-table - Copy.jpg b/legacy/public/images/ttrpg-table - Copy.jpg similarity index 100% rename from public/images/ttrpg-table - Copy.jpg rename to legacy/public/images/ttrpg-table - Copy.jpg diff --git a/public/images/ttrpg-table.jpg b/legacy/public/images/ttrpg-table.jpg similarity index 100% rename from public/images/ttrpg-table.jpg rename to legacy/public/images/ttrpg-table.jpg diff --git a/public/images/tv-hero.jpg b/legacy/public/images/tv-hero.jpg similarity index 100% rename from public/images/tv-hero.jpg rename to legacy/public/images/tv-hero.jpg diff --git a/public/images/tv/30-rock.jpg b/legacy/public/images/tv/30-rock.jpg similarity index 100% rename from public/images/tv/30-rock.jpg rename to legacy/public/images/tv/30-rock.jpg diff --git a/public/images/tv/arcane.jpg b/legacy/public/images/tv/arcane.jpg similarity index 100% rename from public/images/tv/arcane.jpg rename to legacy/public/images/tv/arcane.jpg diff --git a/public/images/tv/breaking-bad.jpg b/legacy/public/images/tv/breaking-bad.jpg similarity index 100% rename from public/images/tv/breaking-bad.jpg rename to legacy/public/images/tv/breaking-bad.jpg diff --git a/public/images/tv/brooklyn-nine-nine.jpg b/legacy/public/images/tv/brooklyn-nine-nine.jpg similarity index 100% rename from public/images/tv/brooklyn-nine-nine.jpg rename to legacy/public/images/tv/brooklyn-nine-nine.jpg diff --git a/public/images/tv/castlevania.jpg b/legacy/public/images/tv/castlevania.jpg similarity index 100% rename from public/images/tv/castlevania.jpg rename to legacy/public/images/tv/castlevania.jpg diff --git a/public/images/tv/cobra-kai.jpg b/legacy/public/images/tv/cobra-kai.jpg similarity index 100% rename from public/images/tv/cobra-kai.jpg rename to legacy/public/images/tv/cobra-kai.jpg diff --git a/public/images/tv/daredevil.jpg b/legacy/public/images/tv/daredevil.jpg similarity index 100% rename from public/images/tv/daredevil.jpg rename to legacy/public/images/tv/daredevil.jpg diff --git a/public/images/tv/game-of-thrones.jpg b/legacy/public/images/tv/game-of-thrones.jpg similarity index 100% rename from public/images/tv/game-of-thrones.jpg rename to legacy/public/images/tv/game-of-thrones.jpg diff --git a/public/images/tv/house.jpg b/legacy/public/images/tv/house.jpg similarity index 100% rename from public/images/tv/house.jpg rename to legacy/public/images/tv/house.jpg diff --git a/public/images/tv/invincible.jpg b/legacy/public/images/tv/invincible.jpg similarity index 100% rename from public/images/tv/invincible.jpg rename to legacy/public/images/tv/invincible.jpg diff --git a/public/images/tv/ozark.jpg b/legacy/public/images/tv/ozark.jpg similarity index 100% rename from public/images/tv/ozark.jpg rename to legacy/public/images/tv/ozark.jpg diff --git a/public/images/tv/parks-and-recreation.jpg b/legacy/public/images/tv/parks-and-recreation.jpg similarity index 100% rename from public/images/tv/parks-and-recreation.jpg rename to legacy/public/images/tv/parks-and-recreation.jpg diff --git a/public/images/tv/peaky-blinders.jpg b/legacy/public/images/tv/peaky-blinders.jpg similarity index 100% rename from public/images/tv/peaky-blinders.jpg rename to legacy/public/images/tv/peaky-blinders.jpg diff --git a/public/images/tv/succession.jpg b/legacy/public/images/tv/succession.jpg similarity index 100% rename from public/images/tv/succession.jpg rename to legacy/public/images/tv/succession.jpg diff --git a/public/images/tv/suits.jpg b/legacy/public/images/tv/suits.jpg similarity index 100% rename from public/images/tv/suits.jpg rename to legacy/public/images/tv/suits.jpg diff --git a/public/images/tv/the-bear.jpg b/legacy/public/images/tv/the-bear.jpg similarity index 100% rename from public/images/tv/the-bear.jpg rename to legacy/public/images/tv/the-bear.jpg diff --git a/public/images/tv/the-crown.jpg b/legacy/public/images/tv/the-crown.jpg similarity index 100% rename from public/images/tv/the-crown.jpg rename to legacy/public/images/tv/the-crown.jpg diff --git a/public/images/tv/the-great-british-baking-show.jpg b/legacy/public/images/tv/the-great-british-baking-show.jpg similarity index 100% rename from public/images/tv/the-great-british-baking-show.jpg rename to legacy/public/images/tv/the-great-british-baking-show.jpg diff --git a/public/images/tv/the-mandalorian.jpg b/legacy/public/images/tv/the-mandalorian.jpg similarity index 100% rename from public/images/tv/the-mandalorian.jpg rename to legacy/public/images/tv/the-mandalorian.jpg diff --git a/public/images/tv/the-queens-gambit.jpg b/legacy/public/images/tv/the-queens-gambit.jpg similarity index 100% rename from public/images/tv/the-queens-gambit.jpg rename to legacy/public/images/tv/the-queens-gambit.jpg diff --git a/public/images/tv/the-sopranos.jpg b/legacy/public/images/tv/the-sopranos.jpg similarity index 100% rename from public/images/tv/the-sopranos.jpg rename to legacy/public/images/tv/the-sopranos.jpg diff --git a/public/images/tv/the-wire.jpg b/legacy/public/images/tv/the-wire.jpg similarity index 100% rename from public/images/tv/the-wire.jpg rename to legacy/public/images/tv/the-wire.jpg diff --git a/public/images/tv/wandavision.jpg b/legacy/public/images/tv/wandavision.jpg similarity index 100% rename from public/images/tv/wandavision.jpg rename to legacy/public/images/tv/wandavision.jpg diff --git a/public/images/xmasgifts.jpg b/legacy/public/images/xmasgifts.jpg similarity index 100% rename from public/images/xmasgifts.jpg rename to legacy/public/images/xmasgifts.jpg diff --git a/public/motifs/astrolabe.svg b/legacy/public/motifs/astrolabe.svg similarity index 100% rename from public/motifs/astrolabe.svg rename to legacy/public/motifs/astrolabe.svg diff --git a/public/motifs/craft-seal-print-light.svg b/legacy/public/motifs/craft-seal-print-light.svg similarity index 100% rename from public/motifs/craft-seal-print-light.svg rename to legacy/public/motifs/craft-seal-print-light.svg diff --git a/public/motifs/kepler-nest-seal-light.svg b/legacy/public/motifs/kepler-nest-seal-light.svg similarity index 100% rename from public/motifs/kepler-nest-seal-light.svg rename to legacy/public/motifs/kepler-nest-seal-light.svg diff --git a/public/motifs/layered-seal.svg b/legacy/public/motifs/layered-seal.svg similarity index 100% rename from public/motifs/layered-seal.svg rename to legacy/public/motifs/layered-seal.svg diff --git a/public/motifs/squared-circle-seal-light.svg b/legacy/public/motifs/squared-circle-seal-light.svg similarity index 100% rename from public/motifs/squared-circle-seal-light.svg rename to legacy/public/motifs/squared-circle-seal-light.svg diff --git a/public/motifs/transmutation-seal-light.svg b/legacy/public/motifs/transmutation-seal-light.svg similarity index 100% rename from public/motifs/transmutation-seal-light.svg rename to legacy/public/motifs/transmutation-seal-light.svg diff --git a/public/motifs/tria-prima-array-seal-light.svg b/legacy/public/motifs/tria-prima-array-seal-light.svg similarity index 100% rename from public/motifs/tria-prima-array-seal-light.svg rename to legacy/public/motifs/tria-prima-array-seal-light.svg diff --git a/public/motifs/zodiac-seal-light.svg b/legacy/public/motifs/zodiac-seal-light.svg similarity index 100% rename from public/motifs/zodiac-seal-light.svg rename to legacy/public/motifs/zodiac-seal-light.svg diff --git a/public/robots.txt b/legacy/public/robots.txt similarity index 100% rename from public/robots.txt rename to legacy/public/robots.txt diff --git a/public/site.webmanifest b/legacy/public/site.webmanifest similarity index 100% rename from public/site.webmanifest rename to legacy/public/site.webmanifest diff --git a/scripts/fetch-anime-art.mjs b/legacy/scripts/fetch-anime-art.mjs similarity index 100% rename from scripts/fetch-anime-art.mjs rename to legacy/scripts/fetch-anime-art.mjs diff --git a/scripts/fetch-bgg-covers.mjs b/legacy/scripts/fetch-bgg-covers.mjs similarity index 100% rename from scripts/fetch-bgg-covers.mjs rename to legacy/scripts/fetch-bgg-covers.mjs diff --git a/scripts/fetch-book-covers.mjs b/legacy/scripts/fetch-book-covers.mjs similarity index 100% rename from scripts/fetch-book-covers.mjs rename to legacy/scripts/fetch-book-covers.mjs diff --git a/scripts/fetch-box-art.mjs b/legacy/scripts/fetch-box-art.mjs similarity index 100% rename from scripts/fetch-box-art.mjs rename to legacy/scripts/fetch-box-art.mjs diff --git a/scripts/fetch-tmdb-art.mjs b/legacy/scripts/fetch-tmdb-art.mjs similarity index 100% rename from scripts/fetch-tmdb-art.mjs rename to legacy/scripts/fetch-tmdb-art.mjs diff --git a/src/components/Icon.astro b/legacy/src/components/Icon.astro similarity index 100% rename from src/components/Icon.astro rename to legacy/src/components/Icon.astro diff --git a/src/components/ObfuscatedEmail.astro b/legacy/src/components/ObfuscatedEmail.astro similarity index 100% rename from src/components/ObfuscatedEmail.astro rename to legacy/src/components/ObfuscatedEmail.astro diff --git a/src/components/brand/BrandIcon.tsx b/legacy/src/components/brand/BrandIcon.tsx similarity index 100% rename from src/components/brand/BrandIcon.tsx rename to legacy/src/components/brand/BrandIcon.tsx diff --git a/src/components/brand/Motif.astro b/legacy/src/components/brand/Motif.astro similarity index 100% rename from src/components/brand/Motif.astro rename to legacy/src/components/brand/Motif.astro diff --git a/src/components/brand/PageSeal.astro b/legacy/src/components/brand/PageSeal.astro similarity index 100% rename from src/components/brand/PageSeal.astro rename to legacy/src/components/brand/PageSeal.astro diff --git a/src/components/cards/DeckCard.astro b/legacy/src/components/cards/DeckCard.astro similarity index 100% rename from src/components/cards/DeckCard.astro rename to legacy/src/components/cards/DeckCard.astro diff --git a/src/components/cards/GameCard.astro b/legacy/src/components/cards/GameCard.astro similarity index 100% rename from src/components/cards/GameCard.astro rename to legacy/src/components/cards/GameCard.astro diff --git a/src/components/cards/InterestCard.astro b/legacy/src/components/cards/InterestCard.astro similarity index 100% rename from src/components/cards/InterestCard.astro rename to legacy/src/components/cards/InterestCard.astro diff --git a/src/components/cards/MediaCard.astro b/legacy/src/components/cards/MediaCard.astro similarity index 100% rename from src/components/cards/MediaCard.astro rename to legacy/src/components/cards/MediaCard.astro diff --git a/src/components/hero/AnimatedBackground.tsx b/legacy/src/components/hero/AnimatedBackground.tsx similarity index 100% rename from src/components/hero/AnimatedBackground.tsx rename to legacy/src/components/hero/AnimatedBackground.tsx diff --git a/src/components/layout/Footer.astro b/legacy/src/components/layout/Footer.astro similarity index 100% rename from src/components/layout/Footer.astro rename to legacy/src/components/layout/Footer.astro diff --git a/src/components/layout/InterestHero.astro b/legacy/src/components/layout/InterestHero.astro similarity index 100% rename from src/components/layout/InterestHero.astro rename to legacy/src/components/layout/InterestHero.astro diff --git a/src/components/layout/Placeholder.astro b/legacy/src/components/layout/Placeholder.astro similarity index 100% rename from src/components/layout/Placeholder.astro rename to legacy/src/components/layout/Placeholder.astro diff --git a/src/components/layout/RelatedInterests.astro b/legacy/src/components/layout/RelatedInterests.astro similarity index 100% rename from src/components/layout/RelatedInterests.astro rename to legacy/src/components/layout/RelatedInterests.astro diff --git a/src/components/layout/SectionHeader.astro b/legacy/src/components/layout/SectionHeader.astro similarity index 100% rename from src/components/layout/SectionHeader.astro rename to legacy/src/components/layout/SectionHeader.astro diff --git a/src/components/layout/SplitTransition.astro b/legacy/src/components/layout/SplitTransition.astro similarity index 100% rename from src/components/layout/SplitTransition.astro rename to legacy/src/components/layout/SplitTransition.astro diff --git a/src/components/media/CategoryList.astro b/legacy/src/components/media/CategoryList.astro similarity index 100% rename from src/components/media/CategoryList.astro rename to legacy/src/components/media/CategoryList.astro diff --git a/src/components/motion/Reveal.astro b/legacy/src/components/motion/Reveal.astro similarity index 100% rename from src/components/motion/Reveal.astro rename to legacy/src/components/motion/Reveal.astro diff --git a/src/components/mystic/AlchemicalDivider.astro b/legacy/src/components/mystic/AlchemicalDivider.astro similarity index 100% rename from src/components/mystic/AlchemicalDivider.astro rename to legacy/src/components/mystic/AlchemicalDivider.astro diff --git a/src/components/mystic/CelestialFrame.astro b/legacy/src/components/mystic/CelestialFrame.astro similarity index 100% rename from src/components/mystic/CelestialFrame.astro rename to legacy/src/components/mystic/CelestialFrame.astro diff --git a/src/components/mystic/CelestialImage.astro b/legacy/src/components/mystic/CelestialImage.astro similarity index 100% rename from src/components/mystic/CelestialImage.astro rename to legacy/src/components/mystic/CelestialImage.astro diff --git a/src/components/mystic/CircuitField.tsx b/legacy/src/components/mystic/CircuitField.tsx similarity index 100% rename from src/components/mystic/CircuitField.tsx rename to legacy/src/components/mystic/CircuitField.tsx diff --git a/src/components/mystic/ConstellationField.tsx b/legacy/src/components/mystic/ConstellationField.tsx similarity index 100% rename from src/components/mystic/ConstellationField.tsx rename to legacy/src/components/mystic/ConstellationField.tsx diff --git a/src/components/mystic/StarBurst.astro b/legacy/src/components/mystic/StarBurst.astro similarity index 100% rename from src/components/mystic/StarBurst.astro rename to legacy/src/components/mystic/StarBurst.astro diff --git a/src/components/mystic/StarField.astro b/legacy/src/components/mystic/StarField.astro similarity index 100% rename from src/components/mystic/StarField.astro rename to legacy/src/components/mystic/StarField.astro diff --git a/src/components/nav/MobileMenu.tsx b/legacy/src/components/nav/MobileMenu.tsx similarity index 100% rename from src/components/nav/MobileMenu.tsx rename to legacy/src/components/nav/MobileMenu.tsx diff --git a/src/components/nav/NavBar.astro b/legacy/src/components/nav/NavBar.astro similarity index 100% rename from src/components/nav/NavBar.astro rename to legacy/src/components/nav/NavBar.astro diff --git a/src/components/nav/ThemeToggle.tsx b/legacy/src/components/nav/ThemeToggle.tsx similarity index 100% rename from src/components/nav/ThemeToggle.tsx rename to legacy/src/components/nav/ThemeToggle.tsx diff --git a/src/components/professional/Certifications.astro b/legacy/src/components/professional/Certifications.astro similarity index 100% rename from src/components/professional/Certifications.astro rename to legacy/src/components/professional/Certifications.astro diff --git a/src/components/professional/ContactForm.tsx b/legacy/src/components/professional/ContactForm.tsx similarity index 100% rename from src/components/professional/ContactForm.tsx rename to legacy/src/components/professional/ContactForm.tsx diff --git a/src/components/professional/EducationHistory.astro b/legacy/src/components/professional/EducationHistory.astro similarity index 100% rename from src/components/professional/EducationHistory.astro rename to legacy/src/components/professional/EducationHistory.astro diff --git a/src/components/professional/ProjectCard.astro b/legacy/src/components/professional/ProjectCard.astro similarity index 100% rename from src/components/professional/ProjectCard.astro rename to legacy/src/components/professional/ProjectCard.astro diff --git a/src/components/professional/SectionNav.astro b/legacy/src/components/professional/SectionNav.astro similarity index 100% rename from src/components/professional/SectionNav.astro rename to legacy/src/components/professional/SectionNav.astro diff --git a/src/components/professional/SkillGrid.astro b/legacy/src/components/professional/SkillGrid.astro similarity index 100% rename from src/components/professional/SkillGrid.astro rename to legacy/src/components/professional/SkillGrid.astro diff --git a/src/components/professional/Timeline.astro b/legacy/src/components/professional/Timeline.astro similarity index 100% rename from src/components/professional/Timeline.astro rename to legacy/src/components/professional/Timeline.astro diff --git a/src/components/store/Carousel.tsx b/legacy/src/components/store/Carousel.tsx similarity index 100% rename from src/components/store/Carousel.tsx rename to legacy/src/components/store/Carousel.tsx diff --git a/src/components/store/ColorGrid.astro b/legacy/src/components/store/ColorGrid.astro similarity index 100% rename from src/components/store/ColorGrid.astro rename to legacy/src/components/store/ColorGrid.astro diff --git a/src/components/store/OrderForm.tsx b/legacy/src/components/store/OrderForm.tsx similarity index 100% rename from src/components/store/OrderForm.tsx rename to legacy/src/components/store/OrderForm.tsx diff --git a/src/components/store/PricingTable.astro b/legacy/src/components/store/PricingTable.astro similarity index 100% rename from src/components/store/PricingTable.astro rename to legacy/src/components/store/PricingTable.astro diff --git a/src/components/store/ProductCard.astro b/legacy/src/components/store/ProductCard.astro similarity index 100% rename from src/components/store/ProductCard.astro rename to legacy/src/components/store/ProductCard.astro diff --git a/src/components/store/ProductTile.astro b/legacy/src/components/store/ProductTile.astro similarity index 100% rename from src/components/store/ProductTile.astro rename to legacy/src/components/store/ProductTile.astro diff --git a/src/components/store/RequirementList.astro b/legacy/src/components/store/RequirementList.astro similarity index 100% rename from src/components/store/RequirementList.astro rename to legacy/src/components/store/RequirementList.astro diff --git a/src/components/store/SlotMeter.astro b/legacy/src/components/store/SlotMeter.astro similarity index 100% rename from src/components/store/SlotMeter.astro rename to legacy/src/components/store/SlotMeter.astro diff --git a/src/components/store/StoreNav.astro b/legacy/src/components/store/StoreNav.astro similarity index 100% rename from src/components/store/StoreNav.astro rename to legacy/src/components/store/StoreNav.astro diff --git a/src/components/ttrpgs/BoardGameShelf.tsx b/legacy/src/components/ttrpgs/BoardGameShelf.tsx similarity index 100% rename from src/components/ttrpgs/BoardGameShelf.tsx rename to legacy/src/components/ttrpgs/BoardGameShelf.tsx diff --git a/src/content.config.ts b/legacy/src/content.config.ts similarity index 100% rename from src/content.config.ts rename to legacy/src/content.config.ts diff --git a/src/content/experience/comcast_1.md b/legacy/src/content/experience/comcast_1.md similarity index 100% rename from src/content/experience/comcast_1.md rename to legacy/src/content/experience/comcast_1.md diff --git a/src/content/experience/drexel.md b/legacy/src/content/experience/drexel.md similarity index 100% rename from src/content/experience/drexel.md rename to legacy/src/content/experience/drexel.md diff --git a/src/content/experience/implico.md b/legacy/src/content/experience/implico.md similarity index 100% rename from src/content/experience/implico.md rename to legacy/src/content/experience/implico.md diff --git a/src/content/games/hall-of-fame/age-of-empires.md b/legacy/src/content/games/hall-of-fame/age-of-empires.md similarity index 100% rename from src/content/games/hall-of-fame/age-of-empires.md rename to legacy/src/content/games/hall-of-fame/age-of-empires.md diff --git a/src/content/games/hall-of-fame/ark-survival-evolved.md b/legacy/src/content/games/hall-of-fame/ark-survival-evolved.md similarity index 100% rename from src/content/games/hall-of-fame/ark-survival-evolved.md rename to legacy/src/content/games/hall-of-fame/ark-survival-evolved.md diff --git a/src/content/games/hall-of-fame/dave-the-diver.md b/legacy/src/content/games/hall-of-fame/dave-the-diver.md similarity index 100% rename from src/content/games/hall-of-fame/dave-the-diver.md rename to legacy/src/content/games/hall-of-fame/dave-the-diver.md diff --git a/src/content/games/hall-of-fame/elden-ring.md b/legacy/src/content/games/hall-of-fame/elden-ring.md similarity index 100% rename from src/content/games/hall-of-fame/elden-ring.md rename to legacy/src/content/games/hall-of-fame/elden-ring.md diff --git a/src/content/games/hall-of-fame/granblue-relink.md b/legacy/src/content/games/hall-of-fame/granblue-relink.md similarity index 100% rename from src/content/games/hall-of-fame/granblue-relink.md rename to legacy/src/content/games/hall-of-fame/granblue-relink.md diff --git a/src/content/games/hall-of-fame/kotor.md b/legacy/src/content/games/hall-of-fame/kotor.md similarity index 100% rename from src/content/games/hall-of-fame/kotor.md rename to legacy/src/content/games/hall-of-fame/kotor.md diff --git a/src/content/games/hall-of-fame/minecraft.md b/legacy/src/content/games/hall-of-fame/minecraft.md similarity index 100% rename from src/content/games/hall-of-fame/minecraft.md rename to legacy/src/content/games/hall-of-fame/minecraft.md diff --git a/src/content/games/hall-of-fame/pokemon-crystal.md b/legacy/src/content/games/hall-of-fame/pokemon-crystal.md similarity index 100% rename from src/content/games/hall-of-fame/pokemon-crystal.md rename to legacy/src/content/games/hall-of-fame/pokemon-crystal.md diff --git a/src/content/games/hall-of-fame/satisfactory.md b/legacy/src/content/games/hall-of-fame/satisfactory.md similarity index 100% rename from src/content/games/hall-of-fame/satisfactory.md rename to legacy/src/content/games/hall-of-fame/satisfactory.md diff --git a/src/content/games/hall-of-fame/sly-cooper.md b/legacy/src/content/games/hall-of-fame/sly-cooper.md similarity index 100% rename from src/content/games/hall-of-fame/sly-cooper.md rename to legacy/src/content/games/hall-of-fame/sly-cooper.md diff --git a/src/content/games/hall-of-fame/stardew-valley.md b/legacy/src/content/games/hall-of-fame/stardew-valley.md similarity index 100% rename from src/content/games/hall-of-fame/stardew-valley.md rename to legacy/src/content/games/hall-of-fame/stardew-valley.md diff --git a/src/content/games/now-playing.yaml b/legacy/src/content/games/now-playing.yaml similarity index 100% rename from src/content/games/now-playing.yaml rename to legacy/src/content/games/now-playing.yaml diff --git a/src/content/projects/carrier-connect.md b/legacy/src/content/projects/carrier-connect.md similarity index 100% rename from src/content/projects/carrier-connect.md rename to legacy/src/content/projects/carrier-connect.md diff --git a/src/content/projects/cosmos-homelab.md b/legacy/src/content/projects/cosmos-homelab.md similarity index 100% rename from src/content/projects/cosmos-homelab.md rename to legacy/src/content/projects/cosmos-homelab.md diff --git a/src/content/projects/driver-app.md b/legacy/src/content/projects/driver-app.md similarity index 100% rename from src/content/projects/driver-app.md rename to legacy/src/content/projects/driver-app.md diff --git a/src/content/projects/placeholder-project.md b/legacy/src/content/projects/placeholder-project.md similarity index 100% rename from src/content/projects/placeholder-project.md rename to legacy/src/content/projects/placeholder-project.md diff --git a/src/data/boardgames.json b/legacy/src/data/boardgames.json similarity index 100% rename from src/data/boardgames.json rename to legacy/src/data/boardgames.json diff --git a/src/data/media/anime.json b/legacy/src/data/media/anime.json similarity index 100% rename from src/data/media/anime.json rename to legacy/src/data/media/anime.json diff --git a/src/data/media/movies.json b/legacy/src/data/media/movies.json similarity index 100% rename from src/data/media/movies.json rename to legacy/src/data/media/movies.json diff --git a/src/data/media/tv.json b/legacy/src/data/media/tv.json similarity index 100% rename from src/data/media/tv.json rename to legacy/src/data/media/tv.json diff --git a/src/data/reading.json b/legacy/src/data/reading.json similarity index 100% rename from src/data/reading.json rename to legacy/src/data/reading.json diff --git a/src/data/store-availability.json b/legacy/src/data/store-availability.json similarity index 100% rename from src/data/store-availability.json rename to legacy/src/data/store-availability.json diff --git a/src/data/store-availability.ts b/legacy/src/data/store-availability.ts similarity index 100% rename from src/data/store-availability.ts rename to legacy/src/data/store-availability.ts diff --git a/src/data/store-products.ts b/legacy/src/data/store-products.ts similarity index 100% rename from src/data/store-products.ts rename to legacy/src/data/store-products.ts diff --git a/src/env.d.ts b/legacy/src/env.d.ts similarity index 100% rename from src/env.d.ts rename to legacy/src/env.d.ts diff --git a/src/layouts/BaseLayout.astro b/legacy/src/layouts/BaseLayout.astro similarity index 100% rename from src/layouts/BaseLayout.astro rename to legacy/src/layouts/BaseLayout.astro diff --git a/src/layouts/StoreLayout.astro b/legacy/src/layouts/StoreLayout.astro similarity index 100% rename from src/layouts/StoreLayout.astro rename to legacy/src/layouts/StoreLayout.astro diff --git a/src/lib/archidekt.ts b/legacy/src/lib/archidekt.ts similarity index 100% rename from src/lib/archidekt.ts rename to legacy/src/lib/archidekt.ts diff --git a/src/lib/brand-icons.ts b/legacy/src/lib/brand-icons.ts similarity index 100% rename from src/lib/brand-icons.ts rename to legacy/src/lib/brand-icons.ts diff --git a/src/lib/env.ts b/legacy/src/lib/env.ts similarity index 100% rename from src/lib/env.ts rename to legacy/src/lib/env.ts diff --git a/src/lib/media.ts b/legacy/src/lib/media.ts similarity index 100% rename from src/lib/media.ts rename to legacy/src/lib/media.ts diff --git a/src/lib/motion.ts b/legacy/src/lib/motion.ts similarity index 100% rename from src/lib/motion.ts rename to legacy/src/lib/motion.ts diff --git a/src/lib/platforms.ts b/legacy/src/lib/platforms.ts similarity index 100% rename from src/lib/platforms.ts rename to legacy/src/lib/platforms.ts diff --git a/src/lib/site.ts b/legacy/src/lib/site.ts similarity index 100% rename from src/lib/site.ts rename to legacy/src/lib/site.ts diff --git a/src/middleware.ts b/legacy/src/middleware.ts similarity index 100% rename from src/middleware.ts rename to legacy/src/middleware.ts diff --git a/src/pages/about.astro b/legacy/src/pages/about.astro similarity index 100% rename from src/pages/about.astro rename to legacy/src/pages/about.astro diff --git a/src/pages/api/contact.ts b/legacy/src/pages/api/contact.ts similarity index 100% rename from src/pages/api/contact.ts rename to legacy/src/pages/api/contact.ts diff --git a/src/pages/api/order.ts b/legacy/src/pages/api/order.ts similarity index 100% rename from src/pages/api/order.ts rename to legacy/src/pages/api/order.ts diff --git a/src/pages/contact.astro b/legacy/src/pages/contact.astro similarity index 100% rename from src/pages/contact.astro rename to legacy/src/pages/contact.astro diff --git a/src/pages/index.astro b/legacy/src/pages/index.astro similarity index 100% rename from src/pages/index.astro rename to legacy/src/pages/index.astro diff --git a/src/pages/interests/anime.astro b/legacy/src/pages/interests/anime.astro similarity index 100% rename from src/pages/interests/anime.astro rename to legacy/src/pages/interests/anime.astro diff --git a/src/pages/interests/gaming.astro b/legacy/src/pages/interests/gaming.astro similarity index 100% rename from src/pages/interests/gaming.astro rename to legacy/src/pages/interests/gaming.astro diff --git a/src/pages/interests/index.astro b/legacy/src/pages/interests/index.astro similarity index 100% rename from src/pages/interests/index.astro rename to legacy/src/pages/interests/index.astro diff --git a/src/pages/interests/making.astro b/legacy/src/pages/interests/making.astro similarity index 100% rename from src/pages/interests/making.astro rename to legacy/src/pages/interests/making.astro diff --git a/src/pages/interests/movies.astro b/legacy/src/pages/interests/movies.astro similarity index 100% rename from src/pages/interests/movies.astro rename to legacy/src/pages/interests/movies.astro diff --git a/src/pages/interests/pokemon.astro b/legacy/src/pages/interests/pokemon.astro similarity index 100% rename from src/pages/interests/pokemon.astro rename to legacy/src/pages/interests/pokemon.astro diff --git a/src/pages/interests/reading.astro b/legacy/src/pages/interests/reading.astro similarity index 100% rename from src/pages/interests/reading.astro rename to legacy/src/pages/interests/reading.astro diff --git a/src/pages/interests/tcgs.astro b/legacy/src/pages/interests/tcgs.astro similarity index 100% rename from src/pages/interests/tcgs.astro rename to legacy/src/pages/interests/tcgs.astro diff --git a/src/pages/interests/ttrpgs.astro b/legacy/src/pages/interests/ttrpgs.astro similarity index 100% rename from src/pages/interests/ttrpgs.astro rename to legacy/src/pages/interests/ttrpgs.astro diff --git a/src/pages/interests/tv.astro b/legacy/src/pages/interests/tv.astro similarity index 100% rename from src/pages/interests/tv.astro rename to legacy/src/pages/interests/tv.astro diff --git a/src/pages/professional/index.astro b/legacy/src/pages/professional/index.astro similarity index 100% rename from src/pages/professional/index.astro rename to legacy/src/pages/professional/index.astro diff --git a/src/pages/store/cards.astro b/legacy/src/pages/store/cards.astro similarity index 100% rename from src/pages/store/cards.astro rename to legacy/src/pages/store/cards.astro diff --git a/src/pages/store/deck-boxes.astro b/legacy/src/pages/store/deck-boxes.astro similarity index 100% rename from src/pages/store/deck-boxes.astro rename to legacy/src/pages/store/deck-boxes.astro diff --git a/src/pages/store/index.astro b/legacy/src/pages/store/index.astro similarity index 100% rename from src/pages/store/index.astro rename to legacy/src/pages/store/index.astro diff --git a/src/pages/store/order.astro b/legacy/src/pages/store/order.astro similarity index 100% rename from src/pages/store/order.astro rename to legacy/src/pages/store/order.astro diff --git a/src/scripts/reveal-init.ts b/legacy/src/scripts/reveal-init.ts similarity index 100% rename from src/scripts/reveal-init.ts rename to legacy/src/scripts/reveal-init.ts diff --git a/src/scripts/theme-init.ts b/legacy/src/scripts/theme-init.ts similarity index 100% rename from src/scripts/theme-init.ts rename to legacy/src/scripts/theme-init.ts diff --git a/src/styles/animations.css b/legacy/src/styles/animations.css similarity index 100% rename from src/styles/animations.css rename to legacy/src/styles/animations.css diff --git a/src/styles/global.css b/legacy/src/styles/global.css similarity index 100% rename from src/styles/global.css rename to legacy/src/styles/global.css diff --git a/src/styles/themes.css b/legacy/src/styles/themes.css similarity index 100% rename from src/styles/themes.css rename to legacy/src/styles/themes.css diff --git a/tsconfig.json b/legacy/tsconfig.json similarity index 100% rename from tsconfig.json rename to legacy/tsconfig.json diff --git a/package-lock.json b/package-lock.json index 11d5bf2..f3fcd09 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,985 +1,32 @@ { - "name": "greg-kilmer-profile", - "version": "1.0.0", + "name": "gregkilmer-com", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "greg-kilmer-profile", - "version": "1.0.0", - "dependencies": { - "@astrojs/check": "^0.9.9", - "@astrojs/mdx": "^7.0.2", - "@astrojs/node": "^11.0.2", - "@astrojs/react": "^6.0.2", - "@fontsource-variable/cinzel": "^5.3.0", - "@fontsource-variable/jetbrains-mono": "^5.2.8", - "@fontsource-variable/montserrat": "^5.3.0", - "@iconify-json/mdi": "^1.2.3", - "@tailwindcss/vite": "^4.3.2", - "@types/nodemailer": "^8.0.1", - "@types/react": "^19.2.17", - "@types/react-dom": "^19.2.3", - "astro": "^7.2.0", - "framer-motion": "^12.42.2", - "nodemailer": "^9.0.3", - "react": "^19.2.7", - "react-dom": "^19.2.7", - "tailwindcss": "^4.3.2", - "typescript": "^6.0.3", - "zod": "^4.4.3" - } - }, - "node_modules/@astrojs/check": { - "version": "0.9.10", - "resolved": "https://registry.npmjs.org/@astrojs/check/-/check-0.9.10.tgz", - "integrity": "sha512-zgx/UQMozdjOa3bOxjgeCFdtpE3c9rRX6xHwa+2QXvy8z8Akifu2AtubHyv/zzC2znO8dl8fFWL4K+Ba9kS8HQ==", - "license": "MIT", - "dependencies": { - "@astrojs/language-server": "^2.16.7", - "chokidar": "^4.0.3", - "kleur": "^4.1.5", - "yargs": "^18.0.0" - }, - "bin": { - "astro-check": "bin/astro-check.js" - }, - "peerDependencies": { - "typescript": "^5.0.0 || ^6.0.0" - } - }, - "node_modules/@astrojs/compiler": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/@astrojs/compiler/-/compiler-2.13.1.tgz", - "integrity": "sha512-f3FN83d2G/v32ipNClRKgYv30onQlMZX1vCeZMjPsMMPl1mDpmbl0+N5BYo4S/ofzqJyS5hvwacEo0CCVDn/Qg==", - "license": "MIT" - }, - "node_modules/@astrojs/compiler-binding": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding/-/compiler-binding-0.3.2.tgz", - "integrity": "sha512-8w/9CWmYrAJJ8N0SY3O43ws2BgxoW6u3QsD8u2mE140lMYAlwh+tlNoUeSBq22wVheFuiBbR212l6ixZ2IIgCQ==", - "license": "MIT", - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "optionalDependencies": { - "@astrojs/compiler-binding-darwin-arm64": "0.3.2", - "@astrojs/compiler-binding-darwin-x64": "0.3.2", - "@astrojs/compiler-binding-linux-arm64-gnu": "0.3.2", - "@astrojs/compiler-binding-linux-arm64-musl": "0.3.2", - "@astrojs/compiler-binding-linux-x64-gnu": "0.3.2", - "@astrojs/compiler-binding-linux-x64-musl": "0.3.2", - "@astrojs/compiler-binding-wasm32-wasi": "0.3.2", - "@astrojs/compiler-binding-win32-arm64-msvc": "0.3.2", - "@astrojs/compiler-binding-win32-x64-msvc": "0.3.2" - } - }, - "node_modules/@astrojs/compiler-binding-darwin-arm64": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-darwin-arm64/-/compiler-binding-darwin-arm64-0.3.2.tgz", - "integrity": "sha512-MM8tn8CSimcfytaOla4b6acN8mKWiL/rlAA1fpT3/Wl7dNGSE4y8FjTN/zJVNnb63CsLWG5zZwCt01TXtDKh9g==", - "cpu": [ - "arm64" + "name": "gregkilmer-com", + "workspaces": [ + "packages/*", + "tools/*" ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@astrojs/compiler-binding-darwin-x64": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-darwin-x64/-/compiler-binding-darwin-x64-0.3.2.tgz", - "integrity": "sha512-2lXOlzf8xb7jLomRsf/aswh61/NnGusynB2OwFkK6k4pmOtpfXMYnG0PLfXrEvxXYj69NdCnmUYXtHDd+JOOag==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@astrojs/compiler-binding-linux-arm64-gnu": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-arm64-gnu/-/compiler-binding-linux-arm64-gnu-0.3.2.tgz", - "integrity": "sha512-BmU3kWj7qnLrd4vzm49zFEPJ5oFnn1tCT4Vt9hZbqdU5Cmb8GZl7fn6VFsnNfe7B18a2gIFtVzbLINtYl5kBjQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@astrojs/compiler-binding-linux-arm64-musl": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-arm64-musl/-/compiler-binding-linux-arm64-musl-0.3.2.tgz", - "integrity": "sha512-f0heT9ZZEseSu5bHCeb80eL2DH07ArE6U9xi1WT/PEusNjzPmEr3GJsjG1tRLo5VYUUYX7h3ScaqGmGrMOVGmw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@astrojs/compiler-binding-linux-x64-gnu": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-x64-gnu/-/compiler-binding-linux-x64-gnu-0.3.2.tgz", - "integrity": "sha512-M8fOUt0itRpqiGyoEA/ij184s8O+hqbCz3+YozRusOOM3osgGljpDThhbKAJjqh82wOo6FioQ4w8PBvU1XMD5Q==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@astrojs/compiler-binding-linux-x64-musl": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-x64-musl/-/compiler-binding-linux-x64-musl-0.3.2.tgz", - "integrity": "sha512-/Kebk8sO6HnLeSd691JkaAPfN7CqR9/KEXmWvyNPkaKNGmj8rTZ/lf2uXtnPu92Lan84UrKdIKVPy1fSo2encQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@astrojs/compiler-binding-wasm32-wasi": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-wasm32-wasi/-/compiler-binding-wasm32-wasi-0.3.2.tgz", - "integrity": "sha512-pUA6xbcOSB7DhfzIArB8BCAkFfAIqriiR7zl5zOStd6oU2G0kIKj+GUdGnyXyhfiv881Hffyk5tC0mR18sDjDw==", - "cpu": [ - "wasm32" - ], - "license": "MIT", - "optional": true, - "dependencies": { - "@napi-rs/wasm-runtime": "^1.2.0" + "devDependencies": { + "@types/node": "^22.10.0", + "tsx": "^4.19.0", + "typescript": "^5.7.0", + "vitest": "^5.0.3" }, "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@astrojs/compiler-binding-win32-arm64-msvc": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-win32-arm64-msvc/-/compiler-binding-win32-arm64-msvc-0.3.2.tgz", - "integrity": "sha512-ESruf+6Qkl1trHUFxI6GSf6t52j8yN2kCNSzMWdzt7V/T09tFHrYzrVaJQohb2C9bJUH76pNvX6Zb51+xCQc9Q==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@astrojs/compiler-binding-win32-x64-msvc": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-win32-x64-msvc/-/compiler-binding-win32-x64-msvc-0.3.2.tgz", - "integrity": "sha512-wzzVrEbOwbsLWOdEbocskjMRx2aZPxJ7ZbmL+jnpBamFwmigm+2M/wzuM6JWncocgYwLic1csSpalBh96kQKXA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@astrojs/compiler-rs": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@astrojs/compiler-rs/-/compiler-rs-0.3.2.tgz", - "integrity": "sha512-xlx/T7JovIKduu4ucbTQUxQ5+Q8wxkHxhLjnZk3VlJbhbQ9RLvvuDk1p2YYFYFQ5y14dVm3FGGO4isQXa4F+Tg==", - "license": "MIT", - "dependencies": { - "@astrojs/compiler-binding": "0.3.2" - }, - "engines": { - "node": ">=22.12.0" - } - }, - "node_modules/@astrojs/internal-helpers": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/@astrojs/internal-helpers/-/internal-helpers-0.10.2.tgz", - "integrity": "sha512-yt7fMgPYqSM4Tmr+taTW6Per+hjJ8Pk6lA1PAcDyqzOt8HzJ6Kje5WzCxA2Sd+9wsUW7uhkLeoTMK0cXPwH9rQ==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.4", - "@types/mdast": "^4.0.4", - "js-yaml": "^4.3.0", - "picomatch": "^4.0.4", - "retext-smartypants": "^6.2.0", - "shiki": "^4.0.2", - "smol-toml": "^1.6.0", - "unified": "^11.0.5" - } - }, - "node_modules/@astrojs/language-server": { - "version": "2.16.13", - "resolved": "https://registry.npmjs.org/@astrojs/language-server/-/language-server-2.16.13.tgz", - "integrity": "sha512-ekOa+CYprEq5n4EJC1qTIAhLk49HZIUQuFwrEuF+3JK/pdMaYnWoREFUI2A0KEPOJiFA2kamBzKzbYljDvUxLg==", - "license": "MIT", - "dependencies": { - "@astrojs/compiler": "^2.13.1", - "@astrojs/yaml2ts": "^0.2.4", - "@jridgewell/sourcemap-codec": "^1.5.5", - "@volar/kit": "~2.4.28", - "@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.16", - "volar-service-css": "0.0.71", - "volar-service-emmet": "0.0.71", - "volar-service-html": "0.0.71", - "volar-service-prettier": "0.0.71", - "volar-service-typescript": "0.0.71", - "volar-service-typescript-twoslash-queries": "0.0.71", - "volar-service-yaml": "0.0.71", - "vscode-html-languageservice": "^5.6.2", - "vscode-uri": "^3.1.0" - }, - "bin": { - "astro-ls": "bin/nodeServer.js" - }, - "peerDependencies": { - "prettier": "^3.0.0", - "prettier-plugin-astro": ">=0.11.0" - }, - "peerDependenciesMeta": { - "prettier": { - "optional": true - }, - "prettier-plugin-astro": { - "optional": true - } - } - }, - "node_modules/@astrojs/markdown-remark": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/@astrojs/markdown-remark/-/markdown-remark-7.2.2.tgz", - "integrity": "sha512-FGfmK84zSNcrsBd0dl1gXE9JvZYElp8EXQa2jpHVAxG4deGKAp43wspxFupjADJX7MSsMRHwYCnfT6EyVmgeFQ==", - "license": "MIT", - "dependencies": { - "@astrojs/internal-helpers": "0.10.2", - "@astrojs/prism": "4.0.2", - "github-slugger": "^2.0.0", - "hast-util-from-html": "^2.0.3", - "hast-util-to-text": "^4.0.2", - "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", - "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" - } - }, - "node_modules/@astrojs/markdown-satteri": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@astrojs/markdown-satteri/-/markdown-satteri-0.3.5.tgz", - "integrity": "sha512-CvWVEFAbay7YO+i9SaqDJubipA5ckiVB89QWoMJ5XC0m5CtFg8JwZ7Kau6X9sYY7FZURH0w2l03ISH2jOS/RDQ==", - "license": "MIT", - "dependencies": { - "@astrojs/internal-helpers": "0.10.2", - "@astrojs/prism": "4.0.2", - "github-slugger": "^2.0.0", - "hast-util-from-html": "^2.0.3", - "satteri": "^0.9.1" - } - }, - "node_modules/@astrojs/mdx": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/@astrojs/mdx/-/mdx-7.0.5.tgz", - "integrity": "sha512-wEM/HH1RiEntyPVagdiF+yArzfcYLKBB0C1RZspVidKZ97rRMbaqP1Nbl/GR0sJs8zwaceqxRymw8aOKKJRdYw==", - "license": "MIT", - "dependencies": { - "@astrojs/internal-helpers": "0.10.2", - "@astrojs/markdown-remark": "7.2.2", - "@mdx-js/mdx": "^3.1.1", - "acorn": "^8.16.0", - "es-module-lexer": "^2.0.0", - "estree-util-visit": "^2.0.0", - "hast-util-to-html": "^9.0.5", - "piccolore": "^0.1.3", - "rehype-raw": "^7.0.0", - "remark-gfm": "^4.0.1", - "remark-smartypants": "^3.0.2", - "source-map": "^0.7.6", - "unist-util-visit": "^5.1.0", - "vfile": "^6.0.3" - }, - "engines": { - "node": ">=22.12.0" - }, - "peerDependencies": { - "@astrojs/markdown-satteri": "^0.3.1", - "astro": "^7.0.0" - }, - "peerDependenciesMeta": { - "@astrojs/markdown-satteri": { - "optional": true - } - } - }, - "node_modules/@astrojs/node": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/@astrojs/node/-/node-11.1.0.tgz", - "integrity": "sha512-utAqBE1eIcAhyIkBUnNIWrzj4vyTiOlbbqcjUSE2Yz5XrMu+9sQdBwp/UmZXfJ9wjaEIHf26HInNLUUmTOFeEw==", - "license": "MIT", - "dependencies": { - "@astrojs/internal-helpers": "0.10.2", - "send": "^1.2.1", - "server-destroy": "^1.0.1" - }, - "peerDependencies": { - "astro": "^7.0.0" - } - }, - "node_modules/@astrojs/prism": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@astrojs/prism/-/prism-4.0.2.tgz", - "integrity": "sha512-KTivpmnz6lDsC6o9H4+DNm2SrE/GHzw8cNAvEJwAvUT+eoaEnn/4NtbDNfRRaxaJHdp15gf+tfHAWiXR4wB3BA==", - "license": "MIT", - "dependencies": { - "prismjs": "^1.30.0" - }, - "engines": { - "node": ">=22.12.0" - } - }, - "node_modules/@astrojs/react": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@astrojs/react/-/react-6.0.2.tgz", - "integrity": "sha512-Fvv2UqS7ajFL2Yjd5s4s5PcCBCSSfks4r04rbCHZO5z4pUPwCygV2ketI+iC+1P/sdHANLV3tgDL1wjzty4cJg==", - "license": "MIT", - "dependencies": { - "@astrojs/internal-helpers": "0.10.2", - "@vitejs/plugin-react": "^5.2.0", - "devalue": "^5.8.1", - "ultrahtml": "^1.6.0", - "vite": "^8.0.13" - }, - "engines": { - "node": ">=22.12.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" - } - }, - "node_modules/@astrojs/telemetry": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/@astrojs/telemetry/-/telemetry-3.3.3.tgz", - "integrity": "sha512-C1TLn5sPJr0x4vk56piHWKbnqlEB8BKyte5Y45V02U+D7BGO5eMqZDH5aPjnkXQWJggvmsTXxH03QMZ9NgWLzQ==", - "license": "MIT", - "dependencies": { - "ci-info": "^4.4.0", - "dset": "^3.1.4", - "is-docker": "^4.0.0", - "package-manager-detector": "^1.6.0" - }, - "engines": { - "node": "18.20.8 || ^20.3.0 || >=22.0.0" - } - }, - "node_modules/@astrojs/yaml2ts": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/@astrojs/yaml2ts/-/yaml2ts-0.2.4.tgz", - "integrity": "sha512-8oddpOae35pJsXPQXhTkM0ypfKPskVsh2bCxRtbf7e+/Epw2nReakFYpLKjZMEr75CsoF203PMnCocpfz0s69A==", - "license": "MIT", - "dependencies": { - "yaml": "^2.8.3" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", - "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.29.7", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", - "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", - "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", - "license": "MIT", - "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/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.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/generator": { - "version": "7.29.8", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.8.tgz", - "integrity": "sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==", - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.29.8", - "@babel/types": "^7.29.8", - "@jridgewell/gen-mapping": "^0.3.12", - "@jridgewell/trace-mapping": "^0.3.28", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", - "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.29.7", - "@babel/helper-validator-option": "^7.29.7", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-globals": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", - "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", - "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.29.7", - "@babel/types": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", - "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.29.7", - "@babel/helper-validator-identifier": "^7.29.7", - "@babel/traverse": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", - "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", - "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", - "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", - "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", - "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", - "license": "MIT", - "dependencies": { - "@babel/template": "^7.29.7", - "@babel/types": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.29.8", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.8.tgz", - "integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.29.8" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx-self": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.29.7.tgz", - "integrity": "sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx-source": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.29.7.tgz", - "integrity": "sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/template": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", - "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.29.7", - "@babel/parser": "^7.29.7", - "@babel/types": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.29.8", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.8.tgz", - "integrity": "sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.29.7", - "@babel/generator": "^7.29.8", - "@babel/helper-globals": "^7.29.7", - "@babel/parser": "^7.29.8", - "@babel/template": "^7.29.7", - "@babel/types": "^7.29.8", - "debug": "^4.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.29.8", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", - "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.29.7", - "@babel/helper-validator-identifier": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@bruits/satteri-darwin-arm64": { - "version": "0.9.5", - "resolved": "https://registry.npmjs.org/@bruits/satteri-darwin-arm64/-/satteri-darwin-arm64-0.9.5.tgz", - "integrity": "sha512-iw4nZgx9v30lWo/MTngQqi1pI78KI0DnkSm+lVJGYdmPLgAyDNJigVhpG42/Iq55A6c1Ll8q66ljyyRiQUxwow==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@bruits/satteri-darwin-x64": { - "version": "0.9.5", - "resolved": "https://registry.npmjs.org/@bruits/satteri-darwin-x64/-/satteri-darwin-x64-0.9.5.tgz", - "integrity": "sha512-6T26Z5Kf3cFW2PSlk9p7zT7yVxvuBSiJvYyz9u8KjYwMTqZyIDOj2wDyNpxKV4+6yUVG7rddq2QwvG/8LJA2+Q==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@bruits/satteri-linux-arm64-gnu": { - "version": "0.9.5", - "resolved": "https://registry.npmjs.org/@bruits/satteri-linux-arm64-gnu/-/satteri-linux-arm64-gnu-0.9.5.tgz", - "integrity": "sha512-u51id17uJwNEMK9nBlICsq6U31c+XVqQueVBkwRIzZG+gMpS8TOJctt5h5Wz33Z8xnMdTd+adtACVz0yHgGuOA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@bruits/satteri-linux-arm64-musl": { - "version": "0.9.5", - "resolved": "https://registry.npmjs.org/@bruits/satteri-linux-arm64-musl/-/satteri-linux-arm64-musl-0.9.5.tgz", - "integrity": "sha512-v39HxiwGC5Rqm01HksP6+5Y+xKLPlsuVFgIgpEAo+SiQ22c+mJVhS3u7Z6ePAKdhL5NJoK1xq70kLz3L13AhpQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@bruits/satteri-linux-x64-gnu": { - "version": "0.9.5", - "resolved": "https://registry.npmjs.org/@bruits/satteri-linux-x64-gnu/-/satteri-linux-x64-gnu-0.9.5.tgz", - "integrity": "sha512-F3uO8uFp3pAP5ZGXttwvh57GS7s0lL953tnNdyI2gRyP4kOOkp6pyGojNJzCjkDvWI2Cvb9iNrKok3aqQPauAw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@bruits/satteri-linux-x64-musl": { - "version": "0.9.5", - "resolved": "https://registry.npmjs.org/@bruits/satteri-linux-x64-musl/-/satteri-linux-x64-musl-0.9.5.tgz", - "integrity": "sha512-bicEqglLlz++mWyADaZoP0JY20s4vDfLjaPYgQqC+NI4zZLTOOg1T4GB8aqtc822Pqji8SQBmSrTb7CrP8i08Q==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@bruits/satteri-wasm32-wasi": { - "version": "0.9.5", - "resolved": "https://registry.npmjs.org/@bruits/satteri-wasm32-wasi/-/satteri-wasm32-wasi-0.9.5.tgz", - "integrity": "sha512-zauAuMwfPnKPUkd4AFixRFpXdgKwP2mKgxrIIo2gJzW0/ZneF9dbHnLkojSpaBnCCp7VUL1hIi5WWZvB1CqmAQ==", - "cpu": [ - "wasm32" - ], - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "1.11.1", - "@emnapi/runtime": "1.11.1", - "@napi-rs/wasm-runtime": "^1.1.6" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@bruits/satteri-wasm32-wasi/node_modules/@emnapi/core": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", - "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.2.2", - "tslib": "^2.4.0" - } - }, - "node_modules/@bruits/satteri-wasm32-wasi/node_modules/@emnapi/runtime": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", - "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@bruits/satteri-wasm32-wasi/node_modules/@emnapi/wasi-threads": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", - "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@bruits/satteri-win32-arm64-msvc": { - "version": "0.9.5", - "resolved": "https://registry.npmjs.org/@bruits/satteri-win32-arm64-msvc/-/satteri-win32-arm64-msvc-0.9.5.tgz", - "integrity": "sha512-SrfE7NEsgZjBvU3c+RR6oQRu0ToXY5uVJEbieXEF0YTctIV2zAVlbaMjWLts074QCgh3a+XHWkR/lWh2VH2LUg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@bruits/satteri-win32-x64-msvc": { - "version": "0.9.5", - "resolved": "https://registry.npmjs.org/@bruits/satteri-win32-x64-msvc/-/satteri-win32-x64-msvc-0.9.5.tgz", - "integrity": "sha512-5Kw9ZAtTGS8WHizyn+CJhjjfIQrw+7jcZodpmpXJjefnO15M8UexIi6JR2E5thyvsmHyhL6ZDDMUNR4bKJPd4g==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@capsizecss/unpack": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@capsizecss/unpack/-/unpack-4.0.1.tgz", - "integrity": "sha512-CuNiSqg7+e1cO/GjffyMOm5Tt2jUF9CWHHnvQ/UkqvtkGfHdgwEC0wpmq7fkN3gxwpRnrAN0WzO3vREKmNolMQ==", - "license": "MIT", - "dependencies": { - "fontkitten": "^1.0.3" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@clack/core": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/@clack/core/-/core-1.4.3.tgz", - "integrity": "sha512-/kr3UWNtdJfxZtPgDqUOmG2pvwlmcLGheex5yiZKdwbzZJxhV+HMNR9QNmyY5cGwTNV6LrR7Jtp+KjhUAP1qBQ==", - "license": "MIT", - "dependencies": { - "fast-wrap-ansi": "^0.2.0", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 20.12.0" - } - }, - "node_modules/@clack/prompts": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@clack/prompts/-/prompts-1.7.0.tgz", - "integrity": "sha512-y7/yvZ2TPAnR9+jnc00klvNNLkJiXFFrQA/hlLCcxA9a2A4zQIOimyFQ9XfwYKiGD1fb5GY8vbKIIgO8d5Tb2A==", - "license": "MIT", - "dependencies": { - "@clack/core": "1.4.3", - "fast-string-width": "^3.0.2", - "fast-wrap-ansi": "^0.2.0", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 20.12.0" - } - }, - "node_modules/@emmetio/abbreviation": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/@emmetio/abbreviation/-/abbreviation-2.3.3.tgz", - "integrity": "sha512-mgv58UrU3rh4YgbE/TzgLQwJ3pFsHHhCLqY20aJq+9comytTXUDNGG/SMtSeMJdkpxgXSXunBGLD8Boka3JyVA==", - "license": "MIT", - "dependencies": { - "@emmetio/scanner": "^1.0.4" - } - }, - "node_modules/@emmetio/css-abbreviation": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/@emmetio/css-abbreviation/-/css-abbreviation-2.1.8.tgz", - "integrity": "sha512-s9yjhJ6saOO/uk1V74eifykk2CBYi01STTK3WlXWGOepyKa23ymJ053+DNQjpFcy1ingpaO7AxCcwLvHFY9tuw==", - "license": "MIT", - "dependencies": { - "@emmetio/scanner": "^1.0.4" - } - }, - "node_modules/@emmetio/css-parser": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@emmetio/css-parser/-/css-parser-0.4.1.tgz", - "integrity": "sha512-2bC6m0MV/voF4CTZiAbG5MWKbq5EBmDPKu9Sb7s7nVcEzNQlrZP6mFFFlIaISM8X6514H9shWMme1fCm8cWAfQ==", - "license": "MIT", - "dependencies": { - "@emmetio/stream-reader": "^2.2.0", - "@emmetio/stream-reader-utils": "^0.1.0" - } - }, - "node_modules/@emmetio/html-matcher": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@emmetio/html-matcher/-/html-matcher-1.3.0.tgz", - "integrity": "sha512-NTbsvppE5eVyBMuyGfVu2CRrLvo7J4YHb6t9sBFLyY03WYhXET37qA4zOYUjBWFCRHO7pS1B9khERtY0f5JXPQ==", - "license": "ISC", - "dependencies": { - "@emmetio/scanner": "^1.0.0" - } - }, - "node_modules/@emmetio/scanner": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@emmetio/scanner/-/scanner-1.0.4.tgz", - "integrity": "sha512-IqRuJtQff7YHHBk4G8YZ45uB9BaAGcwQeVzgj/zj8/UdOhtQpEIupUhSk8dys6spFIWVZVeK20CzGEnqR5SbqA==", - "license": "MIT" - }, - "node_modules/@emmetio/stream-reader": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@emmetio/stream-reader/-/stream-reader-2.2.0.tgz", - "integrity": "sha512-fXVXEyFA5Yv3M3n8sUGT7+fvecGrZP4k6FnWWMSZVQf69kAq0LLpaBQLGcPR30m3zMmKYhECP4k/ZkzvhEW5kw==", - "license": "MIT" - }, - "node_modules/@emmetio/stream-reader-utils": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@emmetio/stream-reader-utils/-/stream-reader-utils-0.1.0.tgz", - "integrity": "sha512-ZsZ2I9Vzso3Ho/pjZFsmmZ++FWeEd/txqybHTm4OgaZzdS8V9V/YYWQwg5TC38Z7uLWUV1vavpLLbjJtKubR1A==", - "license": "MIT" - }, - "node_modules/@emnapi/core": { - "version": "1.11.3", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.3.tgz", - "integrity": "sha512-zLpS5asjEb7lq8jYLq37N6XKaE41DIexlY1rF/z4/tIl3wo13Sqm28fRyfIsKZD+NZ8mM5RoKkpW/rBcuoSZSg==", - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.2.3", - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.11.3", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.3.tgz", - "integrity": "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/wasi-threads": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.3.tgz", - "integrity": "sha512-ELEBe8PsLvvJ6QMr0zLt8ffvOHW/dc1m3CEzNMg7aJUv3bMaoDtw2TXyDAwkYBuroxxuHEwhRTLJSe5sya547g==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" + "node": ">=22" } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz", - "integrity": "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz", + "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==", "cpu": [ "ppc64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -990,12 +37,13 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.1.tgz", - "integrity": "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz", + "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==", "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1006,12 +54,13 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.1.tgz", - "integrity": "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz", + "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==", "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1022,12 +71,13 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.1.tgz", - "integrity": "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz", + "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==", "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1038,12 +88,13 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.1.tgz", - "integrity": "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz", + "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==", "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1054,12 +105,13 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.1.tgz", - "integrity": "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz", + "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==", "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1070,12 +122,13 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.1.tgz", - "integrity": "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz", + "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==", "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1086,12 +139,13 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.1.tgz", - "integrity": "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz", + "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==", "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1102,12 +156,13 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.1.tgz", - "integrity": "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz", + "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==", "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1118,12 +173,13 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.1.tgz", - "integrity": "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz", + "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==", "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1134,12 +190,13 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.1.tgz", - "integrity": "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz", + "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==", "cpu": [ "ia32" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1150,12 +207,13 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.1.tgz", - "integrity": "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz", + "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==", "cpu": [ "loong64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1166,12 +224,13 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.1.tgz", - "integrity": "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz", + "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==", "cpu": [ "mips64el" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1182,12 +241,13 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.1.tgz", - "integrity": "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz", + "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==", "cpu": [ "ppc64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1198,12 +258,13 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.1.tgz", - "integrity": "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz", + "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==", "cpu": [ "riscv64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1214,12 +275,13 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.1.tgz", - "integrity": "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz", + "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==", "cpu": [ "s390x" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1230,12 +292,13 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.1.tgz", - "integrity": "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz", + "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==", "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1246,12 +309,13 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.1.tgz", - "integrity": "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz", + "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==", "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1262,12 +326,13 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.1.tgz", - "integrity": "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz", + "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==", "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1278,12 +343,13 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.1.tgz", - "integrity": "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz", + "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==", "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1294,12 +360,13 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.1.tgz", - "integrity": "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz", + "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==", "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1310,12 +377,13 @@ } }, "node_modules/@esbuild/openharmony-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.1.tgz", - "integrity": "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz", + "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==", "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1326,12 +394,13 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.1.tgz", - "integrity": "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz", + "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==", "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1342,12 +411,13 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.1.tgz", - "integrity": "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz", + "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==", "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1358,12 +428,13 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.1.tgz", - "integrity": "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz", + "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==", "cpu": [ "ia32" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1374,12 +445,13 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.1.tgz", - "integrity": "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz", + "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==", "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1389,2436 +461,447 @@ "node": ">=18" } }, - "node_modules/@fontsource-variable/cinzel": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/@fontsource-variable/cinzel/-/cinzel-5.3.0.tgz", - "integrity": "sha512-eFcU0TwaAP5QESF2pV8VoOPJJXJ6sSGm/6mXMwqTdMBPAJMaLhfa6oISHuOtFeddsoHP9RYm4Io0j1j3mNEz2Q==", - "license": "OFL-1.1", - "funding": { - "url": "https://github.com/sponsors/ayuhito" - } + "node_modules/@gk/migrate": { + "resolved": "tools/migrate", + "link": true }, - "node_modules/@fontsource-variable/jetbrains-mono": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/@fontsource-variable/jetbrains-mono/-/jetbrains-mono-5.3.0.tgz", - "integrity": "sha512-F32xpS2NsGYoQi2ADSkKTgpJj7ozajsGgDJ8woTnqjmIB+dxDIqImjl4pXZVEExu8UFZ2ndhmX18EBS/hdz3Lw==", - "license": "OFL-1.1", - "funding": { - "url": "https://github.com/sponsors/ayuhito" - } - }, - "node_modules/@fontsource-variable/montserrat": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/@fontsource-variable/montserrat/-/montserrat-5.3.0.tgz", - "integrity": "sha512-7PaZoxaxrWLAyrhO46v65An9LhUhfkTExWLhfbywYZCnZEgg/W1rEHnlNmZKjNZ3nJTVYyicqxlJt10z/26yTA==", - "license": "OFL-1.1", - "funding": { - "url": "https://github.com/sponsors/ayuhito" - } - }, - "node_modules/@iconify-json/mdi": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@iconify-json/mdi/-/mdi-1.2.3.tgz", - "integrity": "sha512-O3cLwbDOK7NNDf2ihaQOH5F9JglnulNDFV7WprU2dSoZu3h3cWH//h74uQAB87brHmvFVxIOkuBX2sZSzYhScg==", - "license": "Apache-2.0", - "dependencies": { - "@iconify/types": "*" - } - }, - "node_modules/@iconify/types": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz", - "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==", - "license": "MIT" - }, - "node_modules/@img/colour": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz", - "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@img/sharp-darwin-arm64": { - "version": "0.35.3", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.35.3.tgz", - "integrity": "sha512-RMnFX7YQsMoh7lWfcM4NEHHymBX/rLuKNPVM84XE9ONPcaSCDgE7CHIHpSgPcO2xcRthgBy1HfNO319mwhIAkg==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=20.9.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.3.2" - } - }, - "node_modules/@img/sharp-darwin-x64": { - "version": "0.35.3", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.35.3.tgz", - "integrity": "sha512-Xo+5uFBtLN0BKqieTxiFzFPQAUlBbbH5iBKyRX/z1JrbnYsHTfKJnUfL8+p2TPXr1pXqao4eeL4Rl144uDpK9w==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=20.9.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.3.2" - } - }, - "node_modules/@img/sharp-freebsd-wasm32": { - "version": "0.35.3", - "resolved": "https://registry.npmjs.org/@img/sharp-freebsd-wasm32/-/sharp-freebsd-wasm32-0.35.3.tgz", - "integrity": "sha512-lUxcqWIj2wMQ9BrwNjngcr1gWUr5xgaGThBRqPPalIC2n67Cqj1uPh8NnA/ZhAg8hUbKl+kVHKwgUIwe6ZYPrg==", - "license": "Apache-2.0", - "optional": true, - "os": [ - "freebsd" - ], - "dependencies": { - "@img/sharp-wasm32": "0.35.3" - }, - "engines": { - "node": ">=20.9.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.3.2.tgz", - "integrity": "sha512-9J6ypZFpQBj4YnePGoq/S38w6nz+vqg5WZLrLGY4YuSemdMq47GMLBPO42MzwdGwpg/agZ7xzZcFHa48xlywfg==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.3.2.tgz", - "integrity": "sha512-m2pW1n6cns9VaubNwsZ+c3CRYjxNQWgJ5gPlnL1nbBcpkBvFm6SCFN5o0psFHI8w9n11NKhFkeEDns98tiqbEw==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.3.2.tgz", - "integrity": "sha512-1eMLzy92I4J6rmi4mAT8yC3HxOtniyGELlzGbNMLLeqe052ahFQ0h6LFq+lh5DsDIdYViIDst08abvSbcEdLXQ==", - "cpu": [ - "arm" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.3.2.tgz", - "integrity": "sha512-dqVSFynCox4C/J8kT16V7SIFAns0IjgLwkvYT7p8LQVmJ5OS5b6tI9IGflxTeuBS//zXeFIUbwt5dwxyZ17cnA==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-ppc64": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.3.2.tgz", - "integrity": "sha512-3z0NHDxD6n5I9gc05U1eW1AyRm+Gznzq3naMrthPNqE6oYykcogW0l/jfpJdjYnuNl8R7yI9pNbE1XiUeyq0Aw==", - "cpu": [ - "ppc64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-riscv64": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.3.2.tgz", - "integrity": "sha512-bsb4rI+NldGOsXuej2r8OdSS8+zXDVaCWxyWrcv6kneTOlgAHtZABRzBBCwdsPiD90J4myNJuHpg6kA20ImW/w==", - "cpu": [ - "riscv64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.3.2.tgz", - "integrity": "sha512-/ABshyj8gCpyIrNXnHn4LorDJ0HHm1VhXPBlxZ8zAtfVPAaSafXPGn+sUSIRiwaSBy0mmFjSjiXI5mkcwdChKQ==", - "cpu": [ - "s390x" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.3.2.tgz", - "integrity": "sha512-ITPEtgffGJ0S6G9dRyw/366tJQqFRcHWPHhC+Stpg3Z8AEMrDrTr2lhdz4f/Y/HMbRh//7Z5mBzEpVdi62Oc3w==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.3.2.tgz", - "integrity": "sha512-zE9EdiUzUmg5mDT5a1rk5fYJ6GWPloTwWBYDS14naqHsL+EaMpDj1AWnpLgh3u0YCORv2Tt50wrcrpYqkP97Kw==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.3.2.tgz", - "integrity": "sha512-m0lrLiUt+lBYnCFr8qV/65yMR4E/c7/wf78I5eKTdkEakFAlZ9QlzEM3QIhhAwVeUhLAHLcCq7a7Vszq/oFNZQ==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-linux-arm": { - "version": "0.35.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.35.3.tgz", - "integrity": "sha512-affVWCTLooy8TSxbDx2qkzuDeaWLNVBA+P//FNBirHsXpP2fuBhk5AuboYUnrDnzoXes8GFjpTx0SBFOCRg+FA==", - "cpu": [ - "arm" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=20.9.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.3.2" - } - }, - "node_modules/@img/sharp-linux-arm64": { - "version": "0.35.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.35.3.tgz", - "integrity": "sha512-QgKDspHPnrU+GQ55XPhGwyhC8acLVOOSyAvo1oVfFmrIXLkDNmGWzAfDZ4xK8oSA1qBQrALcHX0G5UZni/SuFQ==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=20.9.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.3.2" - } - }, - "node_modules/@img/sharp-linux-ppc64": { - "version": "0.35.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.35.3.tgz", - "integrity": "sha512-sMd8rDxmpLOwv/7N44klFjOD5DUO7FLdjiXDI0hoxYaf7Ar262dQIEkosE98bps+5HPLtp/EvNqeqQtOycP/IA==", - "cpu": [ - "ppc64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=20.9.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-ppc64": "1.3.2" - } - }, - "node_modules/@img/sharp-linux-riscv64": { - "version": "0.35.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.35.3.tgz", - "integrity": "sha512-0Eob78yjlYPfL5vMNWAW55l3R9Y6BQS/gOfe0ZcP9mEz9ohhKSt4im1hayiknXgf8AWrFqMvJcKIdmLmEe7yeQ==", - "cpu": [ - "riscv64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=20.9.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-riscv64": "1.3.2" - } - }, - "node_modules/@img/sharp-linux-s390x": { - "version": "0.35.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.35.3.tgz", - "integrity": "sha512-KgAxQ0DxpNOq1rG2t5cgTgShJFGSuU7XO45cqC+1NVOuZnP6tlgZRuSYOfNupGkHID0o3cJOsw4DVeJpMovcGw==", - "cpu": [ - "s390x" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=20.9.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.3.2" - } - }, - "node_modules/@img/sharp-linux-x64": { - "version": "0.35.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.35.3.tgz", - "integrity": "sha512-8pqvxubL2PGdhlPy6GLqzDYMUjyRmKAwKHYKixpdJYBUK7PJ0C029XdsnpFIdgRZG68fZiGdHVWcKPvtiPB4cA==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=20.9.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.3.2" - } - }, - "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.35.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.35.3.tgz", - "integrity": "sha512-Vz0iQjzzcSX3HCbfwFfCSG/9SCIqyO0mH2sXyiHaAYfBk0cRsCWXRyQYX0ovCK/PAQBbTzQ0dsPQHh5MAFL59w==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=20.9.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.3.2" - } - }, - "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.35.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.35.3.tgz", - "integrity": "sha512-6O1NPKcDVj9QEdg7Hx549EX8U0rp6yXQERqru6yRN7fGBn32UvIRJUlWnk+8xDCiG76hXVBbX82NZ/ZKr0euIg==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=20.9.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.3.2" - } - }, - "node_modules/@img/sharp-wasm32": { - "version": "0.35.3", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.35.3.tgz", - "integrity": "sha512-cZ0XkcYGpHZkqW6iCkqTcmUC0CD9DhD5d/qeZlZkfRBn6GnHniZXLUo5+9xw8Iv76YE6LQFN9YNBlKREcCG76w==", - "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", - "optional": true, - "dependencies": { - "@emnapi/runtime": "^1.11.1" - }, - "engines": { - "node": ">=20.9.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-webcontainers-wasm32": { - "version": "0.35.3", - "resolved": "https://registry.npmjs.org/@img/sharp-webcontainers-wasm32/-/sharp-webcontainers-wasm32-0.35.3.tgz", - "integrity": "sha512-2rnq7bX3NzeR2T4YWgz8qiG4h3TSdMe+vN1iQXpJleSJ3SM5zQ8Fy2SyyXAWlbxpEZ2Y+Z4u1BePgJEYbSy80Q==", - "cpu": [ - "wasm32" - ], - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "@img/sharp-wasm32": "0.35.3" - }, - "engines": { - "node": ">=20.9.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-arm64": { - "version": "0.35.3", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.35.3.tgz", - "integrity": "sha512-4bPwFdMbeC4JQ8L8LOyWp6nsHcboP5fxkp6iPOXz2Vg49R42TuMs2whkJ5OAP4/Ul035qOzy0AecOF9VOscn4w==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=20.9.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-ia32": { - "version": "0.35.3", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.35.3.tgz", - "integrity": "sha512-r53mXsBN6lFUDiST764SvgwUdHAqM4rPAiDzAmf4fLoB6X/rkfyTrLCg6+g17wJJiCmB3JYgHuUldCWUIRFSXw==", - "cpu": [ - "ia32" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^20.9.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-x64": { - "version": "0.35.3", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.35.3.tgz", - "integrity": "sha512-D4y1vNeZrIIJCN+uHaWVtH86B+aCrdMYYjicy9pXHvbGZeGYLLSd3wdVuC37FxVXlU1ARsk84eKWfWMXGYEqvA==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=20.9.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/remapping": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", - "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - } + "node_modules/@gk/schema": { + "resolved": "packages/schema", + "link": true }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, "license": "MIT", "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", + "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", + "dev": true, "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.31", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@mdx-js/mdx": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-3.1.1.tgz", - "integrity": "sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdx": "^2.0.0", - "acorn": "^8.0.0", - "collapse-white-space": "^2.0.0", - "devlop": "^1.0.0", - "estree-util-is-identifier-name": "^3.0.0", - "estree-util-scope": "^1.0.0", - "estree-walker": "^3.0.0", - "hast-util-to-jsx-runtime": "^2.0.0", - "markdown-extensions": "^2.0.0", - "recma-build-jsx": "^1.0.0", - "recma-jsx": "^1.0.0", - "recma-stringify": "^1.0.0", - "rehype-recma": "^1.0.0", - "remark-mdx": "^3.0.0", - "remark-parse": "^11.0.0", - "remark-rehype": "^11.0.0", - "source-map": "^0.7.0", - "unified": "^11.0.0", - "unist-util-position-from-estree": "^2.0.0", - "unist-util-stringify-position": "^4.0.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/@napi-rs/wasm-runtime": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.2.2.tgz", - "integrity": "sha512-JfB4kuJQjaoHuCTseIINHtHWeJnvgEcxjwA5t/Y00ZgaOO1Crz3fjT/p8kT28zA/Caz7oiUMn3d6H2yOVCVwuw==", - "license": "MIT", - "optional": true, - "dependencies": { - "@tybys/wasm-util": "^0.10.3" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=23.5.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - }, - "peerDependencies": { - "@emnapi/core": "^1.7.1 || ^2.0.0-alpha.3", - "@emnapi/runtime": "^1.7.1 || ^2.0.0-alpha.3" - } - }, - "node_modules/@oslojs/encoding": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@oslojs/encoding/-/encoding-1.1.0.tgz", - "integrity": "sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==", - "license": "MIT" - }, "node_modules/@oxc-project/types": { - "version": "0.143.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.143.0.tgz", - "integrity": "sha512-u6JZdLBTLotrNC9Vd6vPssINdzcCzleKAH6EJKImQb7GtYvX5keN2dxkoK44stCc4tffE6QQRtZTXVSzsLUlWA==", + "version": "0.153.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.153.0.tgz", + "integrity": "sha512-jSNrRIyvPVLL5E1z/tS5zLAprosGz/FuI1PWwEOz59j3E4TTTzSpqKb/RDGqJhhW0QHYISGXxq582wGx6T11Tw==", + "dev": true, "license": "MIT", + "peer": true, "funding": { - "url": "https://github.com/sponsors/Boshen" + "url": "https://github.com/sponsors/oxc-project" } }, - "node_modules/@rolldown/binding-android-arm64": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.3.tgz", - "integrity": "sha512-zrJtHDcaZJ1Fp7xf4hNl+7seH9Cn/N5TwLYkhgXREtBwAd/jaqW3uqeHxpDugJLVICWg4eW44kOQEGJ1r6jCGw==", + "node_modules/@rolldown/binding-android-arm-eabi": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.13.tgz", + "integrity": "sha512-Vu/NICrgdRvUOeaJZIyB2FP63s6fO/4ud5l8dXmxDx9yDyn+mN0y+gsvsVviSli+T361Aqo0h46EuMoobrwawA==", "cpu": [ - "arm64" + "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ "android" ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.13.tgz", + "integrity": "sha512-6LEoIrGy5vvCd2boNnv2soPSUZj/PRvhp8R/t9KlX+mIZxThIE/ZqxrGXxmuNX0FuvQFvLdZr7S4HrYpYBVhEQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.3.tgz", - "integrity": "sha512-ieIiibVCp0tX7TLu2cafoNPv8wJyYi01ekXpbf8q2j7F4rGAhhXb/eQh7ge9DRBY78GwmRQtvjZDux7EDbA8kA==", + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.13.tgz", + "integrity": "sha512-CaJSRzlQ6Q+zkDMVA9OkVxBjVoPRCDEE3v0FSJ9kIQBgRjFApA3Si4ijhbNVUji7ZJt+PfO0e46qy4nPInHUPA==", "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ "darwin" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.3.tgz", - "integrity": "sha512-Zh9tCon19eDXJoihx0rqKhMUlMYqzwj3aPsSuHmI4RWZh62dWUL+DJN4C5YQya5TcQBJU/Fe8+rY0jhXTQITqA==", + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.13.tgz", + "integrity": "sha512-e4r340I5hxSzJkD3YJBxgzq/rJ3wwxwdLLb2FPt1Zpyem20nrQPT11DDEEkazwxaHumH8wpglsp/Ozgj/wT6mw==", "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ "darwin" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.3.tgz", - "integrity": "sha512-nGbJWewA1wrXXZiQhjAT5rhibGfns5ZNkDVqxsO6zJ3f3YvpoDNNmGMSbbhLuXKjNScaBJVOAboztAWVespQMg==", + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.13.tgz", + "integrity": "sha512-hoBWDZ4/K/mJKVMbsgUd1jfpA2DiOrJ54mpegObU9TBHBBrtX4FLIXJJQRDPZ/2Dn91GvPob+Z2hwKJzf4E5SQ==", "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ "freebsd" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.3.tgz", - "integrity": "sha512-QNniJr5Kml0kDEB98jiDOJjXNroxIIi0IXIbdYzY26Xt1pVbeP62+KnoIZLwirOymX/0jDk/2gI/bNUv7A7OIw==", + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.13.tgz", + "integrity": "sha512-oF9fZu3vTWnTiVAkDFxU9PJLt1nh20K9RRpuHsR7ZnE2L7XookBbDBVLojCVSLGbvtSlxeUNnjAj6DbNsKyzbg==", "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ "linux" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.3.tgz", - "integrity": "sha512-TkqEAcmmvH3I/q4114NB4RVt6241Dao48pF45uLcFGrwAaIn0iITgTAKP/dLjbN0R4buJjGb91+UHSoFmpgIWw==", + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.13.tgz", + "integrity": "sha512-DTpb/+jMiqWMPgphn4ksW1zW+XLJ6dJJ9FMvnIk9I291LhCctMPhMYJrP5t9p6C9qa3KXadDdUEZHBaPh3kNxQ==", "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ "linux" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.3.tgz", - "integrity": "sha512-NHqjnxpsndf4MPymxteFAWHHfkTL8HjWh1KB7z23ofZ6QO2euONuxDXjat69dKZRALnGypg8k8SsK8vZJoXv1Q==", + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.13.tgz", + "integrity": "sha512-xU+Kvv9N2A3SY35zFh1FWWNjxbadE/BKeqnkXe7vScU4GZsw757BlVmDLITZEq8DPpk6m7uoDYkK7P+x/uU5Ow==", "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ "linux" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.3.tgz", - "integrity": "sha512-6tbrbwfz5GB9DQ4Jwo6hy9v+vR31xZlvzZ6n5Xut6Hhx5PvrA9q/HsK8KMaYQp063iqZGXwNvZtYNLD7EM/x0w==", + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.13.tgz", + "integrity": "sha512-XMk/hnhh25oQ756Lclb1Q11h4ll0xC0Esa5w4Vzp+mAE2S3AGJgeM0SonrIgY8fHhjarb6hcoTuwoFG0VVP2ig==", "cpu": [ "ppc64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ "linux" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.3.tgz", - "integrity": "sha512-oyuXxXmoZHjXC917IAPFAAv4wWAa0cM9afk8nx1+9/jNNOX1uPf8yDA6p7G0RypOfw/X0PQt5IfoquY1um+zSg==", + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.13.tgz", + "integrity": "sha512-U1BoTHYmlywE2n3HUWjzZ+8EQsKr+OwpxYuDWaubx42tUL8QSL7nOqRz/NVHanibbFP3M1Rxuprz/wNcbMKjqw==", "cpu": [ "s390x" ], + "dev": true, "license": "MIT", "optional": true, "os": [ "linux" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.3.tgz", - "integrity": "sha512-TytMwF2KVGqP2tgd0I1OY0PAv78dZRAYcF5ssDzjM34SUXCED3uXvSd5+lHoC0bTD6eEdFz7LdQNCO1y0oVk9w==", + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.13.tgz", + "integrity": "sha512-J4cm80Pc6wEwV5AZ42o62sIQgDk+Y7R2s646jIwuvO+4V46QB58N4vVuMPWCBaNgursXLpmUWJV//WQAkPvHBQ==", "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ "linux" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.3.tgz", - "integrity": "sha512-/E9m3qstrJFVPoULV25mVQblSNExY2+kBsYe4sy0Tn0yOOgJ8wZbZt3KnRbF/XeU2Gl1STKUQnDNTqhIE5MD4A==", + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.13.tgz", + "integrity": "sha512-W0euZS40U4V6JkmEnVCIX9mcsL6cbXQ4X8c2w+MMOIjFg68TXUW+u3RT+2e1t1pv9mg/EP3pDtK3POXdQ7Rxyw==", "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ "linux" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.3.tgz", - "integrity": "sha512-Kr0OcsoQI816i6HOl3vFHpd1K0eZyh76zgfj4c1nTyaTsd5r2Mj1lwM4R90y/qaCfmTn9eHy0SKwi98eitRxug==", + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.13.tgz", + "integrity": "sha512-do/AF/4n9MRshia3a5aXoy8xM2c6mgfT6D7H/dY6my5z+hfCIR4ab7mk4IRj++nsEQpKy39kd0DQnDq+r692tw==", "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ "openharmony" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.3.tgz", - "integrity": "sha512-hOtMwTqnME+/gJcH/PCZ0wn0zPUjiWOgkHpxbSJpfGKMezHltx1S7/k1SitzVa7Ww2cqrDDaFbZEhcJZO8o+Jw==", + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.13.tgz", + "integrity": "sha512-6FtUf2cJZobutJdxnGYX8z0vtbgqU0b2v8vfBhkXb9wp2MppMFtkunE+IaOntyi01rkIQoPW2GHABtfzsC0BRQ==", "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ "win32" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.3.tgz", - "integrity": "sha512-ekcqMMkI2PlhYnfzQnB/cEdYUVVJViWvoUyLrbzgDoi3Snfc1mVBwdnc306ufA5ejy8JSPjT2RlW1nQSjW7efg==", + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.13.tgz", + "integrity": "sha512-UKhFaZTeTcbKrb3iU82m17jVcPEAoqDkCN5MBqgPyZSwp/cQmib21crTS0DdH7T1B7wZRWEI89YQAOJMet5yhg==", "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ "win32" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-rc.3", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.3.tgz", - "integrity": "sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, "license": "MIT" }, - "node_modules/@shikijs/core": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-4.4.2.tgz", - "integrity": "sha512-StyzbAyxg2/tBGf78gwbBkGyeQ73lf8UiJArFaQhTQIDqQOCKPCQFanvrs4/Yv3Yfyc+ONInJM6K+FMIf+P+kA==", - "license": "MIT", - "dependencies": { - "@shikijs/primitive": "4.4.2", - "@shikijs/types": "4.4.2", - "@shikijs/vscode-textmate": "^10.0.2", - "@types/hast": "^3.0.5", - "hast-util-to-html": "^9.0.5" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/@shikijs/engine-javascript": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-4.4.2.tgz", - "integrity": "sha512-MnIkeqWdVPUWsxlx8gKLVCJFTsqrQJgpTPBPpQwaFeJ56lOnJxj5aN2LUFnfxUEcvOQuNocmbaMnVrCEln6rkw==", - "license": "MIT", - "dependencies": { - "@shikijs/types": "4.4.2", - "@shikijs/vscode-textmate": "^10.0.2", - "oniguruma-to-es": "^4.3.6" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/@shikijs/engine-oniguruma": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-4.4.2.tgz", - "integrity": "sha512-GLhowz1+jixjz+wiZ3wMnOn1jTxiFCGl2PkXufivbnwPHKuyw1AYqu5/hbWhZZ2oAb0NP05WUJhYeigY14drnw==", - "license": "MIT", - "dependencies": { - "@shikijs/types": "4.4.2", - "@shikijs/vscode-textmate": "^10.0.2" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/@shikijs/langs": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-4.4.2.tgz", - "integrity": "sha512-8DfeusD+Zdv/eYIDdXyJTUnSMHt+aAWjAOCXV20HNGAHRlInXpG8wh421v6B91WOm9TFwRLN+b/LG5F2NAIojg==", - "license": "MIT", - "dependencies": { - "@shikijs/types": "4.4.2" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/@shikijs/primitive": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/@shikijs/primitive/-/primitive-4.4.2.tgz", - "integrity": "sha512-l6fQQKsOMlz72n38fztmSgZ76MO6KSWuw8o+GJ+FhmqrpC9pIOJNQNXGgbb5yX2AwpzlEHwsaLPnk/8o4Fm+rA==", - "license": "MIT", - "dependencies": { - "@shikijs/types": "4.4.2", - "@shikijs/vscode-textmate": "^10.0.2", - "@types/hast": "^3.0.5" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/@shikijs/themes": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-4.4.2.tgz", - "integrity": "sha512-H0CFoL07ddDC2Dd6EdrPYNkRhUR6YCkJlnuYFceYYUJJA5TIm2b5B33qqiDYryBExgbKMndFJPb2u1gTuqO37g==", - "license": "MIT", - "dependencies": { - "@shikijs/types": "4.4.2" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/@shikijs/types": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-4.4.2.tgz", - "integrity": "sha512-PFYitV4vpDr/iPCIhnHp+Q4ftic5N5VeNJ3KQ1O8gn3h2ar8qgwMAXF7tq4m1CWaMS60fV4VqF6vfnWH4F7vqQ==", - "license": "MIT", - "dependencies": { - "@shikijs/vscode-textmate": "^10.0.2", - "@types/hast": "^3.0.5" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/@shikijs/vscode-textmate": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz", - "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==", - "license": "MIT" - }, - "node_modules/@tailwindcss/node": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.3.tgz", - "integrity": "sha512-/T8IKEsf9VTU6tLjgC7+sv2mOPtQxzE2jMw7u4Tt40Tx+QSZxpzh95/H6cMKoja9XuW7iMdLJYBB0o9G1CaAgg==", - "license": "MIT", - "dependencies": { - "@jridgewell/remapping": "^2.3.5", - "enhanced-resolve": "^5.24.1", - "jiti": "^2.7.0", - "lightningcss": "1.32.0", - "magic-string": "^0.30.21", - "source-map-js": "^1.2.1", - "tailwindcss": "4.3.3" - } - }, - "node_modules/@tailwindcss/oxide": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.3.3.tgz", - "integrity": "sha512-krXjAikiaFSPaK/FkAQT5UTx3VormQaiZ5hBFlJZ9UFQGB/rwg1MZIhHAG9smMQRTdyJxP6Qt5MwMtdyU5FWrA==", - "license": "MIT", - "engines": { - "node": ">= 20" - }, - "optionalDependencies": { - "@tailwindcss/oxide-android-arm64": "4.3.3", - "@tailwindcss/oxide-darwin-arm64": "4.3.3", - "@tailwindcss/oxide-darwin-x64": "4.3.3", - "@tailwindcss/oxide-freebsd-x64": "4.3.3", - "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.3", - "@tailwindcss/oxide-linux-arm64-gnu": "4.3.3", - "@tailwindcss/oxide-linux-arm64-musl": "4.3.3", - "@tailwindcss/oxide-linux-x64-gnu": "4.3.3", - "@tailwindcss/oxide-linux-x64-musl": "4.3.3", - "@tailwindcss/oxide-wasm32-wasi": "4.3.3", - "@tailwindcss/oxide-win32-arm64-msvc": "4.3.3", - "@tailwindcss/oxide-win32-x64-msvc": "4.3.3" - } - }, - "node_modules/@tailwindcss/oxide-android-arm64": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.3.tgz", - "integrity": "sha512-Y85A2gmPSkl5Ve5qR86GL4HT509cFqQh1aes9p3sSkyTPwt0Pppf3GkwGe4JPACcRYjgJIEhQgM6dBClnr0NYw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-darwin-arm64": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.3.tgz", - "integrity": "sha512-BiaWatpBcERQFDlOjRDpIVXuFK5PJez5SA4JMg6VYZdBYU+qKfV/vqjcIs+IYmtitf1xYQZTwXvU/8y4lfZUGw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-darwin-x64": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.3.tgz", - "integrity": "sha512-fAeUqfV5ndhxRwai8cXGzdLvul9utWOmeTkv69unv4ZXixjn61Z+p9lCWdwOwA3TYboG3BwdVuN/RDjhBRl0mw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-freebsd-x64": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.3.tgz", - "integrity": "sha512-iyf5bV6+wnAlflVeEy7R25dupxTNECZN5QMI0qNT6eT+EgaGdZcKhGkr5SdoaWiLJ3spLqIY9VCeSGrwmtg4kw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.3.tgz", - "integrity": "sha512-aAYUprJAJQWWbRrPvtjdroZ56Md+JM8pMiopS6xGEwDfLhqj+2ver2p4nU4Mb3CRqcMmNBjo8KkUgcxhkzVQGQ==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.3.tgz", - "integrity": "sha512-nDxldcEENOxZRzC2uu9jrutZdAAQtb+8WWDCSnWL1zvBk1+FN+x6MtDViPB5AJMfttVCUhehGWus3XBPgatM/w==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm64-musl": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.3.tgz", - "integrity": "sha512-Md44bD6veX/PC5iyF8cDVnw4HBIANZepRZZ7a8DQOvkfo5WUBwcp6iAuCUz23u+4SUkhJlD3eL7hNdW8ezd/kA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-linux-x64-gnu": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.3.tgz", - "integrity": "sha512-tx7us1muwOKAKWao2v/GaafFeQboE6aj88vC6ziN2NCGcRm8gWUhwjzg+YdVB1e4boAtdtma4L43onunI6NS4w==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-linux-x64-musl": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.3.tgz", - "integrity": "sha512-SJxX60smvHgasZoBy11dX6YRjXJFovwWBoedhbQPOBzgFWBHGB+TVPWB9BxzR7TTxU8FQZAI2AyiNCMzFm8Img==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.3.tgz", - "integrity": "sha512-jx1+rPhY/5Ympkktd656HBWEBLxP7dH06losBLjjf5vgCODXvi9KhtftWcMIwTFIDqBr7cRnQkdLnAG+IOlGvQ==", - "bundleDependencies": [ - "@napi-rs/wasm-runtime", - "@emnapi/core", - "@emnapi/runtime", - "@tybys/wasm-util", - "@emnapi/wasi-threads", - "tslib" - ], - "cpu": [ - "wasm32" - ], - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "^1.11.1", - "@emnapi/runtime": "^1.11.1", - "@emnapi/wasi-threads": "^1.2.2", - "@napi-rs/wasm-runtime": "^1.1.4", - "@tybys/wasm-util": "^0.10.2", - "tslib": "^2.8.1" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.3.tgz", - "integrity": "sha512-3rc292Ca2ceK6Ulcc/bAVnTs/3nDtoPhyEKlgPv+yQJQi/JS/AMJlqzxvlDacL1nekbrcf6bTqp/jV4qgnPxNQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-win32-x64-msvc": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.3.tgz", - "integrity": "sha512-yJ0pwIVc/nYeGoV02WtsN8KYyLQv7kyI2wDnkezyJlGGjkd4QLwDGAwl47YpPJeuI0M0ObaXGSPjvWDPeTPggw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/vite": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.3.3.tgz", - "integrity": "sha512-yYU8cogLeSh/ms2jh8Fj7jaba/EWa7Ja6GoUqYZaraEuCI5YS6ms6ObZgjjedm+jm6XZjdNRWBpPP6Z86oOxcw==", - "license": "MIT", - "dependencies": { - "@tailwindcss/node": "4.3.3", - "@tailwindcss/oxide": "4.3.3", - "tailwindcss": "4.3.3" - }, - "peerDependencies": { - "vite": "^5.2.0 || ^6 || ^7 || ^8" - } - }, - "node_modules/@tybys/wasm-util": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", - "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", - "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", - "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.28.2" - } - }, - "node_modules/@types/debug": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz", - "integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==", - "license": "MIT", - "dependencies": { - "@types/ms": "*" - } - }, "node_modules/@types/estree": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, "license": "MIT" }, - "node_modules/@types/estree-jsx": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", - "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", - "license": "MIT", - "dependencies": { - "@types/estree": "*" - } - }, - "node_modules/@types/hast": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.5.tgz", - "integrity": "sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==", - "license": "MIT", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/@types/mdast": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", - "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", - "license": "MIT", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/@types/mdx": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.14.tgz", - "integrity": "sha512-T48PeuJtvLosNTPVhfnIp3i/n3a4g4Bad7YCq5k64D4u7NwDrAotikQ+5+sjtUvBmxCMlbo3dVL+C2dP0rWHzg==", - "license": "MIT" - }, - "node_modules/@types/ms": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", - "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", - "license": "MIT" - }, - "node_modules/@types/nlcst": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/nlcst/-/nlcst-2.0.3.tgz", - "integrity": "sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA==", - "license": "MIT", - "dependencies": { - "@types/unist": "*" - } - }, "node_modules/@types/node": { - "version": "26.1.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-26.1.2.tgz", - "integrity": "sha512-Vu4a5UFA9rIIFJ7rB/Vaafh9lrCQszopTCx6KjFboXTGQbPNasehVR5TEiithSDGyd1DEiUByggTZsg8jukeIg==", + "version": "22.20.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.20.5.tgz", + "integrity": "sha512-U2+DNr+wSjpsTS/wZGYHq7GcwfuSmKiKvoPvK22zwTlRhU91yOniN4qRR5KhIjvif7ysw/dz/hKmfDH0Ris4aA==", + "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~8.3.0" + "undici-types": "~6.21.0" } }, - "node_modules/@types/nodemailer": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-8.0.1.tgz", - "integrity": "sha512-PxpaInm8V1JQDd4j0ds5HfvWQk8JupS1C0Picb96QJsrrRDjBH+DlK7L4ZdNSqNULhiZRQHc40nLVShaGxXAMw==", + "node_modules/@vitest/mocker": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-5.0.3.tgz", + "integrity": "sha512-T8sWAIbkSyAjkwTcaEc3Iu0o9A27X1/kdXrizhZkGuSKScRQtRzclfAMpOTcGdXCsqxeWlpGy3XjqaW8CpLORg==", + "dev": true, "license": "MIT", "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/react": { - "version": "19.2.18", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.18.tgz", - "integrity": "sha512-AnzbBERsrLKtk2XSfTbYRLjQPdy116Sty4q+T+Bp3IC4l6jNBvreVPAHmpq9qhXQM7CXZPjLVmGMw9sy+hxQ3w==", - "license": "MIT", - "dependencies": { - "csstype": "^3.2.2" - } - }, - "node_modules/@types/react-dom": { - "version": "19.2.4", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.4.tgz", - "integrity": "sha512-Bsc+QHgp+P/F02XDzNCY9jnZNCUuLki36KT7VKrTXXLdHf+vHMNZnW1rVu5DNW/rCK+fya3DATySbLM4yhtKUw==", - "license": "MIT", - "peerDependencies": { - "@types/react": "^19.2.0" - } - }, - "node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", - "license": "MIT" - }, - "node_modules/@ungap/structured-clone": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.3.tgz", - "integrity": "sha512-60YRaenCQcVjYEKOcG824+DRGGIQ3VKErcBoAEDJZz5bKIs2ZG+X/H9Nk+Q6EVkwJk5QNApxbrc5QtBSwtrXAg==", - "license": "ISC" - }, - "node_modules/@vitejs/plugin-react": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.2.0.tgz", - "integrity": "sha512-YmKkfhOAi3wsB1PhJq5Scj3GXMn3WvtQ/JC0xoopuHoXSdmtdStOpFrYaT1kie2YgFBcIe64ROzMYRjCrYOdYw==", - "license": "MIT", - "dependencies": { - "@babel/core": "^7.29.0", - "@babel/plugin-transform-react-jsx-self": "^7.27.1", - "@babel/plugin-transform-react-jsx-source": "^7.27.1", - "@rolldown/pluginutils": "1.0.0-rc.3", - "@types/babel__core": "^7.20.5", - "react-refresh": "^0.18.0" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "peerDependencies": { - "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/@volar/kit": { - "version": "2.4.28", - "resolved": "https://registry.npmjs.org/@volar/kit/-/kit-2.4.28.tgz", - "integrity": "sha512-cKX4vK9dtZvDRaAzeoUdaAJEew6IdxHNCRrdp5Kvcl6zZOqb6jTOfk3kXkIkG3T7oTFXguEMt5+9ptyqYR84Pg==", - "license": "MIT", - "dependencies": { - "@volar/language-service": "2.4.28", - "@volar/typescript": "2.4.28", - "typesafe-path": "^0.2.2", - "vscode-languageserver-textdocument": "^1.0.11", - "vscode-uri": "^3.0.8" - }, - "peerDependencies": { - "typescript": "*" - } - }, - "node_modules/@volar/language-core": { - "version": "2.4.28", - "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.28.tgz", - "integrity": "sha512-w4qhIJ8ZSitgLAkVay6AbcnC7gP3glYM3fYwKV3srj8m494E3xtrCv6E+bWviiK/8hs6e6t1ij1s2Endql7vzQ==", - "license": "MIT", - "dependencies": { - "@volar/source-map": "2.4.28" - } - }, - "node_modules/@volar/language-server": { - "version": "2.4.28", - "resolved": "https://registry.npmjs.org/@volar/language-server/-/language-server-2.4.28.tgz", - "integrity": "sha512-NqcLnE5gERKuS4PUFwlhMxf6vqYo7hXtbMFbViXcbVkbZ905AIVWhnSo0ZNBC2V127H1/2zP7RvVOVnyITFfBw==", - "license": "MIT", - "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.17.5", - "vscode-languageserver-textdocument": "^1.0.11", - "vscode-uri": "^3.0.8" - } - }, - "node_modules/@volar/language-service": { - "version": "2.4.28", - "resolved": "https://registry.npmjs.org/@volar/language-service/-/language-service-2.4.28.tgz", - "integrity": "sha512-Rh/wYCZJrI5vCwMk9xyw/Z+MsWxlJY1rmMZPsxUoJKfzIRjS/NF1NmnuEcrMbEVGja00aVpCsInJfixQTMdvLw==", - "license": "MIT", - "dependencies": { - "@volar/language-core": "2.4.28", - "vscode-languageserver-protocol": "^3.17.5", - "vscode-languageserver-textdocument": "^1.0.11", - "vscode-uri": "^3.0.8" - } - }, - "node_modules/@volar/source-map": { - "version": "2.4.28", - "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.28.tgz", - "integrity": "sha512-yX2BDBqJkRXfKw8my8VarTyjv48QwxdJtvRgUpNE5erCsgEUdI2DsLbpa+rOQVAJYshY99szEcRDmyHbF10ggQ==", - "license": "MIT" - }, - "node_modules/@volar/typescript": { - "version": "2.4.28", - "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.28.tgz", - "integrity": "sha512-Ja6yvWrbis2QtN4ClAKreeUZPVYMARDYZl9LMEv1iQ1QdepB6wn0jTRxA9MftYmYa4DQ4k/DaSZpFPUfxl8giw==", - "license": "MIT", - "dependencies": { - "@volar/language-core": "2.4.28", - "path-browserify": "^1.0.1", - "vscode-uri": "^3.0.8" - } - }, - "node_modules/@vscode/emmet-helper": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/@vscode/emmet-helper/-/emmet-helper-2.11.0.tgz", - "integrity": "sha512-QLxjQR3imPZPQltfbWRnHU6JecWTF1QSWhx3GAKQpslx7y3Dp6sIIXhKjiUJ/BR9FX8PVthjr9PD6pNwOJfAzw==", - "license": "MIT", - "dependencies": { - "emmet": "^2.4.3", - "jsonc-parser": "^2.3.0", - "vscode-languageserver-textdocument": "^1.0.1", - "vscode-languageserver-types": "^3.15.1", - "vscode-uri": "^3.0.8" - } - }, - "node_modules/@vscode/emmet-helper/node_modules/jsonc-parser": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-2.3.1.tgz", - "integrity": "sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg==", - "license": "MIT" - }, - "node_modules/@vscode/l10n": { - "version": "0.0.18", - "resolved": "https://registry.npmjs.org/@vscode/l10n/-/l10n-0.0.18.tgz", - "integrity": "sha512-KYSIHVmslkaCDyw013pphY+d7x1qV8IZupYfeIfzNA+nsaWHbn5uPuQRvdRFsa9zFzGeudPuoGoZ1Op4jrJXIQ==", - "license": "MIT" - }, - "node_modules/acorn": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz", - "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "license": "MIT", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/ajv": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", - "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" + "@jridgewell/trace-mapping": "0.3.31", + "@vitest/spy": "5.0.3", + "estree-walker": "^3.0.3", + "magic-string": "^1.2.3" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-draft-04": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz", - "integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==", - "license": "MIT", + "url": "https://opencollective.com/vitest" + }, "peerDependencies": { - "ajv": "^8.5.0" + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { - "ajv": { + "msw": { + "optional": true + }, + "vite": { "optional": true } } }, - "node_modules/ajv-i18n": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/ajv-i18n/-/ajv-i18n-4.2.0.tgz", - "integrity": "sha512-v/ei2UkCEeuKNXh8RToiFsUclmU+G57LO1Oo22OagNMENIw+Yb8eMwvHu7Vn9fmkjJyv6XclhJ8TbuigSglPkg==", + "node_modules/@vitest/spy": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-5.0.3.tgz", + "integrity": "sha512-XhFysQTB8AZ+P4gMi+Lpo99vg2AZi0qKpaB9yXQl37+CaMEAPO3iH/wGVnSyL5MPERiLezpqTVtrR6UZH5GCXg==", + "dev": true, "license": "MIT", - "peerDependencies": { - "ajv": "^8.0.0-beta.0" + "funding": { + "url": "https://opencollective.com/vitest" } }, - "node_modules/am-i-vibing": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/am-i-vibing/-/am-i-vibing-0.4.0.tgz", - "integrity": "sha512-MxT4XZL7pzLHpuvhDKdMaQHMGGkJDLluKBLsbstn+8wv9sWcFT6h+0ve9qkml95amVTZtZV83gQe2hY+ojgHLg==", - "license": "MIT", - "dependencies": { - "process-ancestry": "^0.1.0" - }, - "bin": { - "am-i-vibing": "dist/cli.mjs" - } - }, - "node_modules/ansi-regex": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, "license": "MIT", "engines": { "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/ansi-styles": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", - "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "node_modules/chai": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.3.0.tgz", + "integrity": "sha512-XWAtwJ6OHO+tj0EKCs0Y2UamnyOxseZWltU4x2U2wh8g4AigdjwvtUjvLP2tqkA/avxHEtzxNaqGq/YGNwckKg==", + "dev": true, "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/anymatch/node_modules/picomatch": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "license": "Python-2.0" - }, - "node_modules/aria-query": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", - "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", - "license": "Apache-2.0", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/array-iterate": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/array-iterate/-/array-iterate-2.0.1.tgz", - "integrity": "sha512-I1jXZMjAgCMmxT4qxXfPXa6SthSoE8h6gkSI9BGGNv8mP8G/v0blc+qFnZu6K42vTOiuME596QaLO0TP3Lk0xg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/astring": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/astring/-/astring-1.9.0.tgz", - "integrity": "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==", - "license": "MIT", - "bin": { - "astring": "bin/astring" - } - }, - "node_modules/astro": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/astro/-/astro-7.2.0.tgz", - "integrity": "sha512-lLTYzx3fOvCmtwD3JVBLQcbORbIOW1/j0R+3IvJx/XKwMGrk7mFnF0BYSOeRiNw1qHUR5mdA6+hRnyvyDfqrWQ==", - "license": "MIT", - "dependencies": { - "@astrojs/compiler-rs": "^0.3.2", - "@astrojs/internal-helpers": "0.10.2", - "@astrojs/markdown-satteri": "0.3.5", - "@astrojs/telemetry": "3.3.3", - "@capsizecss/unpack": "^4.0.0", - "@clack/prompts": "^1.1.0", - "@oslojs/encoding": "^1.1.0", - "am-i-vibing": "^0.4.0", - "aria-query": "^5.3.2", - "axobject-query": "^4.1.0", - "ci-info": "^4.4.0", - "clsx": "^2.1.1", - "common-ancestor-path": "^2.0.0", - "cookie": "^2.0.1", - "devalue": "^5.8.1", - "diff": "^8.0.3", - "dset": "^3.1.4", - "es-module-lexer": "^2.0.0", - "esbuild": "^0.28.0", - "flattie": "^1.1.1", - "fontace": "~0.4.1", - "get-tsconfig": "5.0.0-beta.4", - "github-slugger": "^2.0.0", - "html-escaper": "3.0.3", - "http-cache-semantics": "^4.2.0", - "js-yaml": "^4.3.0", - "jsonc-parser": "^3.3.1", - "magic-string": "^1.0.0", - "magicast": "^0.5.2", - "mrmime": "^2.0.1", - "neotraverse": "^1.0.1", - "obug": "^2.1.1", - "p-limit": "^7.3.0", - "p-queue": "^9.1.0", - "package-manager-detector": "^1.6.0", - "piccolore": "^0.1.3", - "picomatch": "^4.0.4", - "semver": "^7.7.4", - "shiki": "^4.0.2", - "smol-toml": "^1.6.0", - "svgo": "^4.0.1", - "tinyclip": "^0.1.12", - "tinyexec": "^1.0.4", - "tinyglobby": "^0.2.15", - "ultrahtml": "^1.6.0", - "unifont": "~0.7.4", - "unstorage": "^1.17.5", - "vite": "^8.0.13", - "vitefu": "^1.1.2", - "xxhash-wasm": "^1.1.0", - "yargs-parser": "^22.0.0", - "zod": "^4.3.6" - }, - "bin": { - "astro": "bin/astro.mjs" - }, - "engines": { - "node": ">=22.12.0", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/astrodotbuild" - }, - "optionalDependencies": { - "sharp": "^0.34.0 || ^0.35.0" - }, - "peerDependencies": { - "@astrojs/markdown-remark": "7.2.2" - }, - "peerDependenciesMeta": { - "@astrojs/markdown-remark": { - "optional": true - } - } - }, - "node_modules/astro/node_modules/magic-string": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-1.1.0.tgz", - "integrity": "sha512-kS3VHe0nEPST2saQV4Rbkchcd3UBRkVTQHo1D3h/ZTwFDhai/mfKkmtPAtD129EOI7K3HlHIsFOt0WrI2/oU9g==", - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" - } - }, - "node_modules/astro/node_modules/semver": { - "version": "7.8.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", - "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/axobject-query": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", - "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", - "license": "Apache-2.0", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/bail": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", - "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/baseline-browser-mapping": { - "version": "2.11.12", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.12.tgz", - "integrity": "sha512-r7WnVImvVCeFpf2DOXfy41aPWzeNg3H/A2X4dKmy1QL0MSyyk/e7z8ihJ3N6Nn2PsdhkVlqnEfnUE4a05P2aTA==", - "license": "Apache-2.0", - "bin": { - "baseline-browser-mapping": "dist/cli.cjs" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", - "license": "ISC" - }, - "node_modules/browserslist": { - "version": "4.28.7", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.7.tgz", - "integrity": "sha512-JxV13hNrFxqjOc8alRbq9dK1MM79NEXYpma2B2J4wAtpWS5zIEIKqWPGCl7N4o7Uc7B7itylh7SuDujATRyyTw==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "baseline-browser-mapping": "^2.10.44", - "caniuse-lite": "^1.0.30001806", - "electron-to-chromium": "^1.5.393", - "node-releases": "^2.0.51", - "update-browserslist-db": "^1.2.3" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001807", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001807.tgz", - "integrity": "sha512-daRXJ9EB/rdRgu7kV+TTl1YUKtlsMWblPl2sLnpg9DZae16QCegol6A1SmCE31Lm9mXC1sRWGt/krouH+/dl7Q==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/ccount": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", - "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-entities": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", - "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-entities-html4": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", - "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-entities-legacy": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", - "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-reference-invalid": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", - "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/chokidar": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", - "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", - "license": "MIT", - "dependencies": { - "readdirp": "^4.0.1" - }, - "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/ci-info": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", - "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz", - "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==", - "license": "ISC", - "dependencies": { - "string-width": "^7.2.0", - "strip-ansi": "^7.1.0", - "wrap-ansi": "^9.0.0" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/cliui/node_modules/string-width": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", - "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" - }, "engines": { "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/clsx": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", - "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/collapse-white-space": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-2.1.0.tgz", - "integrity": "sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/comma-separated-tokens": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", - "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/commander": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", - "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", - "license": "MIT", - "engines": { - "node": ">=16" - } - }, - "node_modules/common-ancestor-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/common-ancestor-path/-/common-ancestor-path-2.0.0.tgz", - "integrity": "sha512-dnN3ibLeoRf2HNC+OlCiNc5d2zxbLJXOtiZUudNFSXZrNSydxcCsSpRzXwfu7BBWCIfHPw+xTayeBvJCP/D8Ng==", - "license": "BlueOak-1.0.0", - "engines": { - "node": ">= 18" - } - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "license": "MIT" - }, - "node_modules/cookie": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-2.0.1.tgz", - "integrity": "sha512-yuToqVvRrj6pfDXREyQAAv8SkAEk/8GS3jQRTiUMm66TVtBYmqQeoEjL2Lmq8Rpo6271vH76InTChTitEAm65w==", - "license": "MIT", - "engines": { - "node": ">=22" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/cookie-es": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-1.2.3.tgz", - "integrity": "sha512-lXVyvUvrNXblMqzIRrxHb57UUVmqsSWlxqt3XIjCkUP0wDAf6uicO6KMbEgYrMNtEvWgWHwe42CKxPu9MYAnWw==", - "license": "MIT" - }, - "node_modules/crossws": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/crossws/-/crossws-0.3.5.tgz", - "integrity": "sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA==", - "license": "MIT", - "dependencies": { - "uncrypto": "^0.1.3" - } - }, - "node_modules/css-select": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", - "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==", - "license": "BSD-2-Clause", - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^6.1.0", - "domhandler": "^5.0.2", - "domutils": "^3.0.1", - "nth-check": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/css-tree": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz", - "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==", - "license": "MIT", - "dependencies": { - "mdn-data": "2.27.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" - } - }, - "node_modules/css-what": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", - "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", - "license": "BSD-2-Clause", - "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/csso": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", - "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", - "license": "MIT", - "dependencies": { - "css-tree": "~2.2.0" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", - "npm": ">=7.0.0" - } - }, - "node_modules/csso/node_modules/css-tree": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", - "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", - "license": "MIT", - "dependencies": { - "mdn-data": "2.0.28", - "source-map-js": "^1.0.1" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", - "npm": ">=7.0.0" - } - }, - "node_modules/csso/node_modules/mdn-data": { - "version": "2.0.28", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", - "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==", - "license": "CC0-1.0" - }, - "node_modules/csstype": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", - "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "license": "MIT" - }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decode-named-character-reference": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.3.0.tgz", - "integrity": "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==", - "license": "MIT", - "dependencies": { - "character-entities": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/defu": { - "version": "6.1.7", - "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.7.tgz", - "integrity": "sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==", - "license": "MIT" - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/destr": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz", - "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==", - "license": "MIT" - }, "node_modules/detect-libc": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, "license": "Apache-2.0", + "peer": true, "engines": { "node": ">=8" } }, - "node_modules/devalue": { - "version": "5.9.0", - "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.9.0.tgz", - "integrity": "sha512-RWrqdArjvPbsATEhOPUo6Wndc/iWnkWKlhIrdlF3zMMYo/c3CVtoaVAyLtWxz5h8nSlkHzxnzV2uLydPXmtF+A==", - "license": "MIT" - }, - "node_modules/devlop": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", - "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", - "license": "MIT", - "dependencies": { - "dequal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/diff": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.4.tgz", - "integrity": "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/dom-serializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", - "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", - "license": "MIT", - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", - "entities": "^4.2.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/dom-serializer/node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "license": "BSD-2-Clause" - }, - "node_modules/domhandler": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", - "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", - "license": "BSD-2-Clause", - "dependencies": { - "domelementtype": "^2.3.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/domutils": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", - "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", - "license": "BSD-2-Clause", - "dependencies": { - "dom-serializer": "^2.0.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/dset": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/dset/-/dset-3.1.4.tgz", - "integrity": "sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "license": "MIT" - }, - "node_modules/electron-to-chromium": { - "version": "1.5.402", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.402.tgz", - "integrity": "sha512-/oOpMaPT6Yg+6/1XQhyIPlzgj7Ye9zf+nNM2Uh6OcE2G2oNptWazFa+qB2Pdqqbsc9KnIDzgAntoYN0dbwOXwA==", - "license": "ISC" - }, - "node_modules/emmet": { - "version": "2.4.11", - "resolved": "https://registry.npmjs.org/emmet/-/emmet-2.4.11.tgz", - "integrity": "sha512-23QPJB3moh/U9sT4rQzGgeyyGIrcM+GH5uVYg2C6wZIxAIJq7Ng3QLT79tl8FUwDXhyq9SusfknOrofAKqvgyQ==", - "license": "MIT", - "workspaces": [ - "./packages/scanner", - "./packages/abbreviation", - "./packages/css-abbreviation", - "./" - ], - "dependencies": { - "@emmetio/abbreviation": "^2.3.3", - "@emmetio/css-abbreviation": "^2.1.8" - } - }, - "node_modules/emoji-regex": { - "version": "10.6.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", - "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", - "license": "MIT" - }, - "node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/enhanced-resolve": { - "version": "5.24.5", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.24.5.tgz", - "integrity": "sha512-L1l8TNvomm6UVW5B253AGxQagSQr+vGwhMlrrfRS2qmhx46AMpMVJKQYLvWYbysTMY8VoicOvzHzoHMbyzB+4A==", - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.3.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/entities": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", - "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, "node_modules/es-module-lexer": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.1.tgz", - "integrity": "sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.2.tgz", + "integrity": "sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==", + "dev": true, "license": "MIT" }, - "node_modules/esast-util-from-estree": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/esast-util-from-estree/-/esast-util-from-estree-2.0.0.tgz", - "integrity": "sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "devlop": "^1.0.0", - "estree-util-visit": "^2.0.0", - "unist-util-position-from-estree": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/esast-util-from-js": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/esast-util-from-js/-/esast-util-from-js-2.0.1.tgz", - "integrity": "sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "acorn": "^8.0.0", - "esast-util-from-estree": "^2.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/esbuild": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz", - "integrity": "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz", + "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==", + "dev": true, "hasInstallScript": true, "license": "MIT", "bin": { @@ -3828,223 +911,59 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.28.1", - "@esbuild/android-arm": "0.28.1", - "@esbuild/android-arm64": "0.28.1", - "@esbuild/android-x64": "0.28.1", - "@esbuild/darwin-arm64": "0.28.1", - "@esbuild/darwin-x64": "0.28.1", - "@esbuild/freebsd-arm64": "0.28.1", - "@esbuild/freebsd-x64": "0.28.1", - "@esbuild/linux-arm": "0.28.1", - "@esbuild/linux-arm64": "0.28.1", - "@esbuild/linux-ia32": "0.28.1", - "@esbuild/linux-loong64": "0.28.1", - "@esbuild/linux-mips64el": "0.28.1", - "@esbuild/linux-ppc64": "0.28.1", - "@esbuild/linux-riscv64": "0.28.1", - "@esbuild/linux-s390x": "0.28.1", - "@esbuild/linux-x64": "0.28.1", - "@esbuild/netbsd-arm64": "0.28.1", - "@esbuild/netbsd-x64": "0.28.1", - "@esbuild/openbsd-arm64": "0.28.1", - "@esbuild/openbsd-x64": "0.28.1", - "@esbuild/openharmony-arm64": "0.28.1", - "@esbuild/sunos-x64": "0.28.1", - "@esbuild/win32-arm64": "0.28.1", - "@esbuild/win32-ia32": "0.28.1", - "@esbuild/win32-x64": "0.28.1" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "license": "MIT" - }, - "node_modules/escape-string-regexp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/estree-util-attach-comments": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/estree-util-attach-comments/-/estree-util-attach-comments-3.0.0.tgz", - "integrity": "sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/estree-util-build-jsx": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/estree-util-build-jsx/-/estree-util-build-jsx-3.0.1.tgz", - "integrity": "sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "devlop": "^1.0.0", - "estree-util-is-identifier-name": "^3.0.0", - "estree-walker": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/estree-util-is-identifier-name": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", - "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/estree-util-scope": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/estree-util-scope/-/estree-util-scope-1.0.0.tgz", - "integrity": "sha512-2CAASclonf+JFWBNJPndcOpA8EMJwa0Q8LUFJEKqXLW6+qBvbFZuF5gItbQOs/umBUkjviCSDCbBwU2cXbmrhQ==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "devlop": "^1.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/estree-util-to-js": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/estree-util-to-js/-/estree-util-to-js-2.0.0.tgz", - "integrity": "sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "astring": "^1.8.0", - "source-map": "^0.7.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/estree-util-visit": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/estree-util-visit/-/estree-util-visit-2.0.0.tgz", - "integrity": "sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "@esbuild/aix-ppc64": "0.28.2", + "@esbuild/android-arm": "0.28.2", + "@esbuild/android-arm64": "0.28.2", + "@esbuild/android-x64": "0.28.2", + "@esbuild/darwin-arm64": "0.28.2", + "@esbuild/darwin-x64": "0.28.2", + "@esbuild/freebsd-arm64": "0.28.2", + "@esbuild/freebsd-x64": "0.28.2", + "@esbuild/linux-arm": "0.28.2", + "@esbuild/linux-arm64": "0.28.2", + "@esbuild/linux-ia32": "0.28.2", + "@esbuild/linux-loong64": "0.28.2", + "@esbuild/linux-mips64el": "0.28.2", + "@esbuild/linux-ppc64": "0.28.2", + "@esbuild/linux-riscv64": "0.28.2", + "@esbuild/linux-s390x": "0.28.2", + "@esbuild/linux-x64": "0.28.2", + "@esbuild/netbsd-arm64": "0.28.2", + "@esbuild/netbsd-x64": "0.28.2", + "@esbuild/openbsd-arm64": "0.28.2", + "@esbuild/openbsd-x64": "0.28.2", + "@esbuild/openharmony-arm64": "0.28.2", + "@esbuild/sunos-x64": "0.28.2", + "@esbuild/win32-arm64": "0.28.2", + "@esbuild/win32-ia32": "0.28.2", + "@esbuild/win32-x64": "0.28.2" } }, "node_modules/estree-walker": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, "license": "MIT", "dependencies": { "@types/estree": "^1.0.0" } }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "license": "MIT", + "node_modules/expect-type": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz", + "integrity": "sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==", + "dev": true, + "license": "Apache-2.0", "engines": { - "node": ">= 0.6" - } - }, - "node_modules/eventemitter3": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", - "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", - "license": "MIT" - }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "license": "MIT" - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "license": "MIT" - }, - "node_modules/fast-string-truncated-width": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/fast-string-truncated-width/-/fast-string-truncated-width-3.0.3.tgz", - "integrity": "sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==", - "license": "MIT" - }, - "node_modules/fast-string-width": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/fast-string-width/-/fast-string-width-3.0.2.tgz", - "integrity": "sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==", - "license": "MIT", - "dependencies": { - "fast-string-truncated-width": "^3.0.2" - } - }, - "node_modules/fast-uri": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.5.tgz", - "integrity": "sha512-gHwA1O9LDIcKunMKhObS/HimwtehO1nPUECKAu5TpKgaO19fcWEl4bliWe1jWxVFvIXztJjjQ4L8XQ1EU9f7Jw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "BSD-3-Clause" - }, - "node_modules/fast-wrap-ansi": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/fast-wrap-ansi/-/fast-wrap-ansi-0.2.2.tgz", - "integrity": "sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==", - "license": "MIT", - "dependencies": { - "fast-string-width": "^3.0.2" + "node": ">=12.0.0" } }, "node_modules/fdir": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, "license": "MIT", "engines": { "node": ">=12.0.0" @@ -4058,76 +977,11 @@ } } }, - "node_modules/flattie": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/flattie/-/flattie-1.1.1.tgz", - "integrity": "sha512-9UbaD6XdAL97+k/n+N7JwX46K/M6Zc6KcFYskrYL8wbBV/Uyk0CTAMY0VT+qiK5PM7AIc9aTWYtq65U7T+aCNQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/fontace": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/fontace/-/fontace-0.4.1.tgz", - "integrity": "sha512-lDMvbAzSnHmbYMTEld5qdtvNH2/pWpICOqpean9IgC7vUbUJc3k+k5Dokp85CegamqQpFbXf0rAVkbzpyTA8aw==", - "license": "MIT", - "dependencies": { - "fontkitten": "^1.0.2" - } - }, - "node_modules/fontkitten": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/fontkitten/-/fontkitten-1.0.3.tgz", - "integrity": "sha512-Wp1zXWPVUPBmfoa3Cqc9ctaKuzKAV6uLstRqlR56kSjplf5uAce+qeyYym7F+PHbGTk+tCEdkCW6RD7DX/gBZw==", - "license": "MIT", - "dependencies": { - "tiny-inflate": "^1.0.3" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/framer-motion": { - "version": "12.43.0", - "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.43.0.tgz", - "integrity": "sha512-1eaL3RvR/kAlbG7UYcpMptEyzPoENO0c6w7ZnB3/hh2vSAz/6uGAFn6fdoqTBguNstf3MsFhJHsD/0DHiclG+g==", - "license": "MIT", - "dependencies": { - "motion-dom": "^12.43.0", - "motion-utils": "^12.39.0", - "tslib": "^2.4.0" - }, - "peerDependencies": { - "@emotion/is-prop-valid": "*", - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@emotion/is-prop-valid": { - "optional": true - }, - "react": { - "optional": true - }, - "react-dom": { - "optional": true - } - } - }, - "node_modules/fresh": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", - "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -4138,533 +992,13 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-east-asian-width": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz", - "integrity": "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/get-tsconfig": { - "version": "5.0.0-beta.4", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-5.0.0-beta.4.tgz", - "integrity": "sha512-7nF7C9fIPFEMHgEMEfgIlO9wDdZ8CyHw27rWciFZfHvHDReIiPhsYuzPRXsfvBCqFy1l8RRyyWV7QLM+ZhUJsQ==", - "license": "MIT", - "dependencies": { - "resolve-pkg-maps": "^1.0.0" - }, - "engines": { - "node": ">=20.20.0" - }, - "funding": { - "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" - } - }, - "node_modules/github-slugger": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-2.0.0.tgz", - "integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==", - "license": "ISC" - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "license": "ISC" - }, - "node_modules/h3": { - "version": "1.15.11", - "resolved": "https://registry.npmjs.org/h3/-/h3-1.15.11.tgz", - "integrity": "sha512-L3THSe2MPeBwgIZVSH5zLdBBU90TOxarvhK9d04IDY2AmVS8j2Jz2LIWtwsGOU3lu2I5jCN7FNvVfY2+XyF+mg==", - "license": "MIT", - "dependencies": { - "cookie-es": "^1.2.3", - "crossws": "^0.3.5", - "defu": "^6.1.6", - "destr": "^2.0.5", - "iron-webcrypto": "^1.2.1", - "node-mock-http": "^1.0.4", - "radix3": "^1.1.2", - "ufo": "^1.6.3", - "uncrypto": "^0.1.3" - } - }, - "node_modules/hast-util-from-html": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/hast-util-from-html/-/hast-util-from-html-2.0.3.tgz", - "integrity": "sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "devlop": "^1.1.0", - "hast-util-from-parse5": "^8.0.0", - "parse5": "^7.0.0", - "vfile": "^6.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-from-parse5": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-8.0.3.tgz", - "integrity": "sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "devlop": "^1.0.0", - "hastscript": "^9.0.0", - "property-information": "^7.0.0", - "vfile": "^6.0.0", - "vfile-location": "^5.0.0", - "web-namespaces": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-is-element": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-3.0.0.tgz", - "integrity": "sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-parse-selector": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz", - "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-raw": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-9.1.0.tgz", - "integrity": "sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "@ungap/structured-clone": "^1.0.0", - "hast-util-from-parse5": "^8.0.0", - "hast-util-to-parse5": "^8.0.0", - "html-void-elements": "^3.0.0", - "mdast-util-to-hast": "^13.0.0", - "parse5": "^7.0.0", - "unist-util-position": "^5.0.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.0", - "web-namespaces": "^2.0.0", - "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-estree": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/hast-util-to-estree/-/hast-util-to-estree-3.1.3.tgz", - "integrity": "sha512-48+B/rJWAp0jamNbAAf9M7Uf//UVqAoMmgXhBdxTDJLGKY+LRnZ99qcG+Qjl5HfMpYNzS5v4EAwVEF34LeAj7w==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "comma-separated-tokens": "^2.0.0", - "devlop": "^1.0.0", - "estree-util-attach-comments": "^3.0.0", - "estree-util-is-identifier-name": "^3.0.0", - "hast-util-whitespace": "^3.0.0", - "mdast-util-mdx-expression": "^2.0.0", - "mdast-util-mdx-jsx": "^3.0.0", - "mdast-util-mdxjs-esm": "^2.0.0", - "property-information": "^7.0.0", - "space-separated-tokens": "^2.0.0", - "style-to-js": "^1.0.0", - "unist-util-position": "^5.0.0", - "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-html": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz", - "integrity": "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "ccount": "^2.0.0", - "comma-separated-tokens": "^2.0.0", - "hast-util-whitespace": "^3.0.0", - "html-void-elements": "^3.0.0", - "mdast-util-to-hast": "^13.0.0", - "property-information": "^7.0.0", - "space-separated-tokens": "^2.0.0", - "stringify-entities": "^4.0.0", - "zwitch": "^2.0.4" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-jsx-runtime": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz", - "integrity": "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "comma-separated-tokens": "^2.0.0", - "devlop": "^1.0.0", - "estree-util-is-identifier-name": "^3.0.0", - "hast-util-whitespace": "^3.0.0", - "mdast-util-mdx-expression": "^2.0.0", - "mdast-util-mdx-jsx": "^3.0.0", - "mdast-util-mdxjs-esm": "^2.0.0", - "property-information": "^7.0.0", - "space-separated-tokens": "^2.0.0", - "style-to-js": "^1.0.0", - "unist-util-position": "^5.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-parse5": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-8.0.1.tgz", - "integrity": "sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "comma-separated-tokens": "^2.0.0", - "devlop": "^1.0.0", - "property-information": "^7.0.0", - "space-separated-tokens": "^2.0.0", - "web-namespaces": "^2.0.0", - "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-text": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/hast-util-to-text/-/hast-util-to-text-4.0.2.tgz", - "integrity": "sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "hast-util-is-element": "^3.0.0", - "unist-util-find-after": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-whitespace": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", - "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hastscript": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-9.0.1.tgz", - "integrity": "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "comma-separated-tokens": "^2.0.0", - "hast-util-parse-selector": "^4.0.0", - "property-information": "^7.0.0", - "space-separated-tokens": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/html-escaper": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-3.0.3.tgz", - "integrity": "sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==", - "license": "MIT" - }, - "node_modules/html-void-elements": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", - "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/http-cache-semantics": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", - "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", - "license": "BSD-2-Clause" - }, - "node_modules/http-errors": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", - "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", - "license": "MIT", - "dependencies": { - "depd": "~2.0.0", - "inherits": "~2.0.4", - "setprototypeof": "~1.2.0", - "statuses": "~2.0.2", - "toidentifier": "~1.0.1" - }, - "engines": { - "node": ">= 0.8" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC" - }, - "node_modules/inline-style-parser": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.7.tgz", - "integrity": "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==", - "license": "MIT" - }, - "node_modules/iron-webcrypto": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/iron-webcrypto/-/iron-webcrypto-1.2.1.tgz", - "integrity": "sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/brc-dd" - } - }, - "node_modules/is-alphabetical": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", - "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-alphanumerical": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", - "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", - "license": "MIT", - "dependencies": { - "is-alphabetical": "^2.0.0", - "is-decimal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-decimal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", - "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-docker": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-4.0.0.tgz", - "integrity": "sha512-LHE+wROyG/Y/0ZnbktRCoTix2c1RhgWaZraMZ8o1Q7zCh0VSrICJQO5oqIIISrcSBtrXv0o233w1IYwsWCjTzA==", - "license": "MIT", - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-hexadecimal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", - "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-plain-obj": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", - "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jiti": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", - "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", - "license": "MIT", - "bin": { - "jiti": "lib/jiti-cli.mjs" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "license": "MIT" - }, - "node_modules/js-yaml": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.1.tgz", - "integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/puzrin" - }, - { - "type": "github", - "url": "https://github.com/sponsors/nodeca" - } - ], - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "license": "MIT" - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/jsonc-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", - "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", - "license": "MIT" - }, - "node_modules/kleur": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", - "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/lightningcss": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", - "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", + "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", + "dev": true, "license": "MPL-2.0", + "peer": true, "dependencies": { "detect-libc": "^2.0.3" }, @@ -4676,31 +1010,33 @@ "url": "https://opencollective.com/parcel" }, "optionalDependencies": { - "lightningcss-android-arm64": "1.32.0", - "lightningcss-darwin-arm64": "1.32.0", - "lightningcss-darwin-x64": "1.32.0", - "lightningcss-freebsd-x64": "1.32.0", - "lightningcss-linux-arm-gnueabihf": "1.32.0", - "lightningcss-linux-arm64-gnu": "1.32.0", - "lightningcss-linux-arm64-musl": "1.32.0", - "lightningcss-linux-x64-gnu": "1.32.0", - "lightningcss-linux-x64-musl": "1.32.0", - "lightningcss-win32-arm64-msvc": "1.32.0", - "lightningcss-win32-x64-msvc": "1.32.0" + "lightningcss-android-arm64": "1.33.0", + "lightningcss-darwin-arm64": "1.33.0", + "lightningcss-darwin-x64": "1.33.0", + "lightningcss-freebsd-x64": "1.33.0", + "lightningcss-linux-arm-gnueabihf": "1.33.0", + "lightningcss-linux-arm64-gnu": "1.33.0", + "lightningcss-linux-arm64-musl": "1.33.0", + "lightningcss-linux-x64-gnu": "1.33.0", + "lightningcss-linux-x64-musl": "1.33.0", + "lightningcss-win32-arm64-msvc": "1.33.0", + "lightningcss-win32-x64-msvc": "1.33.0" } }, "node_modules/lightningcss-android-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", - "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz", + "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", "cpu": [ "arm64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ "android" ], + "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -4710,17 +1046,19 @@ } }, "node_modules/lightningcss-darwin-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", - "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz", + "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", "cpu": [ "arm64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ "darwin" ], + "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -4730,17 +1068,19 @@ } }, "node_modules/lightningcss-darwin-x64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", - "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz", + "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", "cpu": [ "x64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ "darwin" ], + "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -4750,17 +1090,19 @@ } }, "node_modules/lightningcss-freebsd-x64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", - "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz", + "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", "cpu": [ "x64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ "freebsd" ], + "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -4770,17 +1112,19 @@ } }, "node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", - "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz", + "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", "cpu": [ "arm" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ "linux" ], + "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -4790,17 +1134,19 @@ } }, "node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", - "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz", + "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", "cpu": [ "arm64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ "linux" ], + "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -4810,17 +1156,19 @@ } }, "node_modules/lightningcss-linux-arm64-musl": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", - "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz", + "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", "cpu": [ "arm64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ "linux" ], + "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -4830,17 +1178,19 @@ } }, "node_modules/lightningcss-linux-x64-gnu": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", - "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz", + "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", "cpu": [ "x64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ "linux" ], + "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -4850,17 +1200,19 @@ } }, "node_modules/lightningcss-linux-x64-musl": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", - "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz", + "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", "cpu": [ "x64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ "linux" ], + "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -4870,17 +1222,19 @@ } }, "node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", - "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz", + "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", "cpu": [ "arm64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ "win32" ], + "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -4890,17 +1244,19 @@ } }, "node_modules/lightningcss-win32-x64-msvc": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", - "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz", + "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", "cpu": [ "x64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ "win32" ], + "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -4909,1157 +1265,21 @@ "url": "https://opencollective.com/parcel" } }, - "node_modules/longest-streak": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", - "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "license": "ISC", - "dependencies": { - "yallist": "^3.0.2" - } - }, "node_modules/magic-string": { - "version": "0.30.21", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", - "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-1.4.3.tgz", + "integrity": "sha512-z12OxmaPGE0F4xlpGdjuAUckipLpQlTAuAmyGhNoD7ODpYUzvDfvtxUbjM/jwznyP178oju/KDdyi220wNJ0RQ==", + "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" + "@jridgewell/sourcemap-codec": "^1.6.0" } }, - "node_modules/magicast": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.4.tgz", - "integrity": "sha512-llBEhWm1SacoRwgHUoQJYtwp4PBLF4faQi5TCpIGyGs9n4y5+juI0tDgyKIfpqxckRHaHzouUEph3THklWh03w==", - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.29.7", - "@babel/types": "^7.29.7", - "source-map-js": "^1.2.1" - } - }, - "node_modules/markdown-extensions": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/markdown-extensions/-/markdown-extensions-2.0.0.tgz", - "integrity": "sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==", - "license": "MIT", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/markdown-table": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", - "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/mdast-util-definitions": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-6.0.0.tgz", - "integrity": "sha512-scTllyX6pnYNZH/AIp/0ePz6s4cZtARxImwoPJ7kS42n+MnVsI4XbnG6d4ibehRIldYMWM2LD7ImQblVhUejVQ==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "unist-util-visit": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-find-and-replace": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz", - "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "escape-string-regexp": "^5.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-from-markdown": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.3.tgz", - "integrity": "sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "mdast-util-to-string": "^4.0.0", - "micromark": "^4.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-decode-string": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unist-util-stringify-position": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz", - "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==", - "license": "MIT", - "dependencies": { - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-gfm-autolink-literal": "^2.0.0", - "mdast-util-gfm-footnote": "^2.0.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.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-autolink-literal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz", - "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "ccount": "^2.0.0", - "devlop": "^1.0.0", - "mdast-util-find-and-replace": "^3.0.0", - "micromark-util-character": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-footnote": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz", - "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.1.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-strikethrough": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", - "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-table": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", - "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "markdown-table": "^3.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-task-list-item": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", - "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdx": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-mdx/-/mdast-util-mdx-3.0.0.tgz", - "integrity": "sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w==", - "license": "MIT", - "dependencies": { - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-mdx-expression": "^2.0.0", - "mdast-util-mdx-jsx": "^3.0.0", - "mdast-util-mdxjs-esm": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdx-expression": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz", - "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdx-jsx": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz", - "integrity": "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "ccount": "^2.0.0", - "devlop": "^1.1.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0", - "parse-entities": "^4.0.0", - "stringify-entities": "^4.0.0", - "unist-util-stringify-position": "^4.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdxjs-esm": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", - "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-phrasing": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", - "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-hast": { - "version": "13.2.1", - "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", - "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "@ungap/structured-clone": "^1.0.0", - "devlop": "^1.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "trim-lines": "^3.0.0", - "unist-util-position": "^5.0.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-markdown": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz", - "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "longest-streak": "^3.0.0", - "mdast-util-phrasing": "^4.0.0", - "mdast-util-to-string": "^4.0.0", - "micromark-util-classify-character": "^2.0.0", - "micromark-util-decode-string": "^2.0.0", - "unist-util-visit": "^5.0.0", - "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", - "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdn-data": { - "version": "2.27.1", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", - "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==", - "license": "CC0-1.0" - }, - "node_modules/micromark": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", - "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "@types/debug": "^4.0.0", - "debug": "^4.0.0", - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "micromark-core-commonmark": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-combine-extensions": "^2.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-encode": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-subtokenize": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-core-commonmark": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", - "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "micromark-factory-destination": "^2.0.0", - "micromark-factory-label": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-factory-title": "^2.0.0", - "micromark-factory-whitespace": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-classify-character": "^2.0.0", - "micromark-util-html-tag-name": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-subtokenize": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-gfm": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", - "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==", - "license": "MIT", - "dependencies": { - "micromark-extension-gfm-autolink-literal": "^2.0.0", - "micromark-extension-gfm-footnote": "^2.0.0", - "micromark-extension-gfm-strikethrough": "^2.0.0", - "micromark-extension-gfm-table": "^2.0.0", - "micromark-extension-gfm-tagfilter": "^2.0.0", - "micromark-extension-gfm-task-list-item": "^2.0.0", - "micromark-util-combine-extensions": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-autolink-literal": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", - "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-footnote": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", - "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-core-commonmark": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-strikethrough": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz", - "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-classify-character": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-table": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz", - "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-tagfilter": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", - "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==", - "license": "MIT", - "dependencies": { - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-task-list-item": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz", - "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-mdx-expression": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/micromark-extension-mdx-expression/-/micromark-extension-mdx-expression-3.0.1.tgz", - "integrity": "sha512-dD/ADLJ1AeMvSAKBwO22zG22N4ybhe7kFIZ3LsDI0GlsNr2A3KYxb0LdC1u5rj4Nw+CHKY0RVdnHX8vj8ejm4Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "devlop": "^1.0.0", - "micromark-factory-mdx-expression": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-events-to-acorn": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-mdx-jsx": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/micromark-extension-mdx-jsx/-/micromark-extension-mdx-jsx-3.0.2.tgz", - "integrity": "sha512-e5+q1DjMh62LZAJOnDraSSbDMvGJ8x3cbjygy2qFEi7HCeUT4BDKCvMozPozcD6WmOt6sVvYDNBKhFSz3kjOVQ==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "devlop": "^1.0.0", - "estree-util-is-identifier-name": "^3.0.0", - "micromark-factory-mdx-expression": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-events-to-acorn": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-mdx-md": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-mdx-md/-/micromark-extension-mdx-md-2.0.0.tgz", - "integrity": "sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ==", - "license": "MIT", - "dependencies": { - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-mdxjs": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs/-/micromark-extension-mdxjs-3.0.0.tgz", - "integrity": "sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ==", - "license": "MIT", - "dependencies": { - "acorn": "^8.0.0", - "acorn-jsx": "^5.0.0", - "micromark-extension-mdx-expression": "^3.0.0", - "micromark-extension-mdx-jsx": "^3.0.0", - "micromark-extension-mdx-md": "^2.0.0", - "micromark-extension-mdxjs-esm": "^3.0.0", - "micromark-util-combine-extensions": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-mdxjs-esm": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs-esm/-/micromark-extension-mdxjs-esm-3.0.0.tgz", - "integrity": "sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "devlop": "^1.0.0", - "micromark-core-commonmark": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-events-to-acorn": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unist-util-position-from-estree": "^2.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-factory-destination": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", - "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-label": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", - "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-mdx-expression": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/micromark-factory-mdx-expression/-/micromark-factory-mdx-expression-2.0.3.tgz", - "integrity": "sha512-kQnEtA3vzucU2BkrIa8/VaSAsP+EJ3CKOvhMuJgOEGg9KDC6OAY6nSnNDVRiVNRqj7Y4SlSzcStaH/5jge8JdQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "devlop": "^1.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-events-to-acorn": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unist-util-position-from-estree": "^2.0.0", - "vfile-message": "^4.0.0" - } - }, - "node_modules/micromark-factory-space": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", - "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-title": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", - "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-whitespace": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", - "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-chunked": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", - "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-classify-character": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", - "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-combine-extensions": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", - "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-chunked": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-decode-numeric-character-reference": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", - "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-decode-string": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", - "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "decode-named-character-reference": "^1.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-encode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", - "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-events-to-acorn": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/micromark-util-events-to-acorn/-/micromark-util-events-to-acorn-2.0.3.tgz", - "integrity": "sha512-jmsiEIiZ1n7X1Rr5k8wVExBQCg5jy4UXVADItHmNk1zkwEVhBuIUKRu3fqv+hs4nxLISi2DQGlqIOGiFxgbfHg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "@types/unist": "^3.0.0", - "devlop": "^1.0.0", - "estree-util-visit": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "vfile-message": "^4.0.0" - } - }, - "node_modules/micromark-util-html-tag-name": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", - "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-normalize-identifier": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", - "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-resolve-all": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", - "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-sanitize-uri": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", - "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-encode": "^2.0.0", - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-subtokenize": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", - "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-types": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", - "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/mime-db": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", - "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", - "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", - "license": "MIT", - "dependencies": { - "mime-db": "^1.54.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/motion-dom": { - "version": "12.43.0", - "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.43.0.tgz", - "integrity": "sha512-azKON4d9S65PEoFUiQTMTgPheEmzf2QngdRc50AKfJp9Q9mmcBVw22c8eMq9k8kxOFHdL7+WZY7N/5F/lwiDag==", - "license": "MIT", - "dependencies": { - "motion-utils": "^12.39.0" - } - }, - "node_modules/motion-utils": { - "version": "12.39.0", - "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.39.0.tgz", - "integrity": "sha512-8nadJAJjTtqRkmRF36FoJTrywK9nnFmnPwnSMyxaOCU7GDjN9RTMJIxx9De8ErM+vpPhMccr/6fo5WciyQLnMQ==", - "license": "MIT" - }, - "node_modules/mrmime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", - "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/muggle-string": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz", - "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==", - "license": "MIT" - }, "node_modules/nanoid": { - "version": "3.3.17", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.17.tgz", - "integrity": "sha512-xQLf0A3HOMlgHq0n247/LRuAOYmB7dXJ/DvAxGvsSBij45XtBSmQycu+F8ODbHwns/XyFZagyL1+J0Offw1E0g==", + "version": "3.3.20", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.20.tgz", + "integrity": "sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ==", + "dev": true, "funding": [ { "type": "github", @@ -6067,6 +1287,7 @@ } ], "license": "MIT", + "peer": true, "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -6074,83 +1295,11 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "node_modules/neotraverse": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/neotraverse/-/neotraverse-1.0.1.tgz", - "integrity": "sha512-WmmLty1YWwJl9yZi77v2dVIV6X2kuYV8YYBI/G3LWGKdGHmHUvL1z7FW0iDvEvGAwNEoc5x1tOOOyDnf5jJw/w==", - "license": "MIT", - "engines": { - "node": ">= 10" - } - }, - "node_modules/nlcst-to-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/nlcst-to-string/-/nlcst-to-string-4.0.0.tgz", - "integrity": "sha512-YKLBCcUYKAg0FNlOBT6aI91qFmSiFKiluk655WzPF+DDMA02qIyy8uiRqI8QXtcFpEvll12LpL5MXqEmAZ+dcA==", - "license": "MIT", - "dependencies": { - "@types/nlcst": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/node-fetch-native": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz", - "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==", - "license": "MIT" - }, - "node_modules/node-mock-http": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/node-mock-http/-/node-mock-http-1.0.5.tgz", - "integrity": "sha512-KQyt/wLjG3TAc7DOUhpqWzgd4ERxR80JOlTK5VE5R1S12IaPVN5qkj4klBce9HPG1Njuup4Sb5bljaT34lIyjw==", - "license": "MIT" - }, - "node_modules/node-releases": { - "version": "2.0.53", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.53.tgz", - "integrity": "sha512-D9UOmYG3UH1V+ENW56t5QXBwJw1YEY18ruVeus89Rw+SyIgjPkCO84bRzO3uNIYosJbNwiabWVn48o3uJLjxFQ==", - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/nodemailer": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-9.0.4.tgz", - "integrity": "sha512-LmJNRVRtfSCULxcZpy0Cpg4WWenlUZ9+zbmTO+S7v9wD6XreYLjXRFtDjtV/4F0HT5p1GyZfA0Ux/myxHb18CQ==", - "license": "MIT-0", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/nth-check": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", - "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", - "license": "BSD-2-Clause", - "dependencies": { - "boolbase": "^1.0.0" - }, - "funding": { - "url": "https://github.com/fb55/nth-check?sponsor=1" - } - }, "node_modules/obug": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.4.tgz", - "integrity": "sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.2.1.tgz", + "integrity": "sha512-XrsrhT5sybtKI6wakr2SPOlGZWWYbUXZ7a0jT8/QOeAPau+1X/bSegNe5YR75oJmEZQbKningirmGOEJCIk61Q==", + "dev": true, "funding": [ "https://github.com/sponsors/sxzz", "https://opencollective.com/debug" @@ -6160,178 +1309,19 @@ "node": ">=12.20.0" } }, - "node_modules/ofetch": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/ofetch/-/ofetch-1.5.1.tgz", - "integrity": "sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA==", - "license": "MIT", - "dependencies": { - "destr": "^2.0.5", - "node-fetch-native": "^1.6.7", - "ufo": "^1.6.1" - } - }, - "node_modules/ohash": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz", - "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==", - "license": "MIT" - }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/oniguruma-parser": { - "version": "0.12.2", - "resolved": "https://registry.npmjs.org/oniguruma-parser/-/oniguruma-parser-0.12.2.tgz", - "integrity": "sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw==", - "license": "MIT" - }, - "node_modules/oniguruma-to-es": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-4.3.6.tgz", - "integrity": "sha512-csuQ9x3Yr0cEIs/Zgx/OEt9iBw9vqIunAPQkx19R/fiMq2oGVTgcMqO/V3Ybqefr1TBvosI6jU539ksaBULJyA==", - "license": "MIT", - "dependencies": { - "oniguruma-parser": "^0.12.2", - "regex": "^6.1.0", - "regex-recursion": "^6.0.2" - } - }, - "node_modules/p-limit": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-7.3.1.tgz", - "integrity": "sha512-0trZaiG7Y7kN/Egy9a8j47t9osC0Tch4PaIWd9yGF6bvmlk7muExRvGNYb8sXBwEKMoNKsbNN9P8EefuQekE4Q==", - "license": "MIT", - "dependencies": { - "yocto-queue": "^1.2.1" - }, - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-queue": { - "version": "9.3.3", - "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-9.3.3.tgz", - "integrity": "sha512-NXAOdnEe5FsZJfT4oK84lE1Y5cFFdWlRuOo5tww8DyNMxyRXwn39fIkUtNLKppcPC+UYU/bXujNCUGDv01y7CA==", - "license": "MIT", - "dependencies": { - "eventemitter3": "^5.0.4", - "p-timeout": "^7.0.0" - }, - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-timeout": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-7.0.1.tgz", - "integrity": "sha512-AxTM2wDGORHGEkPCt8yqxOTMgpfbEHqF51f/5fJCmwFC3C/zNcGT63SymH2ttOAaiIws2zVg4+izQCjrakcwHg==", - "license": "MIT", - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/package-manager-detector": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.8.0.tgz", - "integrity": "sha512-yQA4H19AmPEoMUeavPMDIe1higySl/gH/yaQrkT/s07Qp+7pp2hYz30N3z2l5BkjVkF9Ow6o0wjJamm2y7Sn0A==", - "license": "MIT" - }, - "node_modules/parse-entities": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", - "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^2.0.0", - "character-entities-legacy": "^3.0.0", - "character-reference-invalid": "^2.0.0", - "decode-named-character-reference": "^1.0.0", - "is-alphanumerical": "^2.0.0", - "is-decimal": "^2.0.0", - "is-hexadecimal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/parse-entities/node_modules/@types/unist": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", - "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", - "license": "MIT" - }, - "node_modules/parse-latin": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/parse-latin/-/parse-latin-7.0.0.tgz", - "integrity": "sha512-mhHgobPPua5kZ98EF4HWiH167JWBfl4pvAIXXdbaVohtK7a6YBOy56kvhCqduqyo/f3yrHFWmqmiMg/BkBkYYQ==", - "license": "MIT", - "dependencies": { - "@types/nlcst": "^2.0.0", - "@types/unist": "^3.0.0", - "nlcst-to-string": "^4.0.0", - "unist-util-modify-children": "^4.0.0", - "unist-util-visit-children": "^3.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/parse5": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", - "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", - "license": "MIT", - "dependencies": { - "entities": "^6.0.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, - "node_modules/path-browserify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", - "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", - "license": "MIT" - }, - "node_modules/piccolore": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/piccolore/-/piccolore-0.1.3.tgz", - "integrity": "sha512-o8bTeDWjE086iwKrROaDf31K0qC/BENdm15/uH9usSC/uZjJOKb2YGiVHfLY4GhwsERiPI1jmwI2XrA7ACOxVw==", - "license": "ISC" - }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "license": "ISC" + "dev": true, + "license": "ISC", + "peer": true }, "node_modules/picomatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", - "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -6341,9 +1331,10 @@ } }, "node_modules/postcss": { - "version": "8.5.26", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.26.tgz", - "integrity": "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==", + "version": "8.5.29", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.29.tgz", + "integrity": "sha512-49cGhUbXj8Qenv0iTMxA1cFBzxXoctpC9Ujd77t1WcbJIr6nF/eI7g/8MgxrYldFRuAXvja7xQRwavoW7kgrxQ==", + "dev": true, "funding": [ { "type": "opencollective", @@ -6359,443 +1350,25 @@ } ], "license": "MIT", + "peer": true, "dependencies": { - "nanoid": "^3.3.17", + "nanoid": "^3.3.19", "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" + "source-map-js": "^1.2.2" }, "engines": { "node": "^10 || ^12 || >=14" } }, - "node_modules/prettier": { - "version": "3.9.6", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.6.tgz", - "integrity": "sha512-OpN0zzVdiaiAhxpuuj5efpIS4sY9j7bY6uR5mnj5yPzGkdkjNKSJeUThPb60Jw29QuAZgA4o+/iB49kFiaBX6g==", - "license": "MIT", - "bin": { - "prettier": "bin/prettier.cjs" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/prismjs": { - "version": "1.30.0", - "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", - "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/process-ancestry": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/process-ancestry/-/process-ancestry-0.1.0.tgz", - "integrity": "sha512-tGqJW/UnclpYASFcM6Xh8D8l/BMtaQ9+CSG0vlJSJTcdMM4lDRv4c6H0Pdcsfted+bVczdYSfk2fdukg2gQkZg==", - "license": "MIT", - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/property-information": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.2.0.tgz", - "integrity": "sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/radix3": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/radix3/-/radix3-1.1.2.tgz", - "integrity": "sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==", - "license": "MIT" - }, - "node_modules/range-parser": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.3.0.tgz", - "integrity": "sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/react": { - "version": "19.2.8", - "resolved": "https://registry.npmjs.org/react/-/react-19.2.8.tgz", - "integrity": "sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-dom": { - "version": "19.2.8", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.8.tgz", - "integrity": "sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ==", - "license": "MIT", - "dependencies": { - "scheduler": "^0.27.0" - }, - "peerDependencies": { - "react": "^19.2.8" - } - }, - "node_modules/react-refresh": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz", - "integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/readdirp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", - "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", - "license": "MIT", - "engines": { - "node": ">= 14.18.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/recma-build-jsx": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/recma-build-jsx/-/recma-build-jsx-1.0.0.tgz", - "integrity": "sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "estree-util-build-jsx": "^3.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/recma-jsx": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/recma-jsx/-/recma-jsx-1.0.1.tgz", - "integrity": "sha512-huSIy7VU2Z5OLv6oFLosQGGDqPqdO1iq6bWNAdhzMxSJP7RAso4fCZ1cKu8j9YHCZf3TPrq4dw3okhrylgcd7w==", - "license": "MIT", - "dependencies": { - "acorn-jsx": "^5.0.0", - "estree-util-to-js": "^2.0.0", - "recma-parse": "^1.0.0", - "recma-stringify": "^1.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - }, - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/recma-parse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/recma-parse/-/recma-parse-1.0.0.tgz", - "integrity": "sha512-OYLsIGBB5Y5wjnSnQW6t3Xg7q3fQ7FWbw/vcXtORTnyaSFscOtABg+7Pnz6YZ6c27fG1/aN8CjfwoUEUIdwqWQ==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "esast-util-from-js": "^2.0.0", - "unified": "^11.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/recma-stringify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/recma-stringify/-/recma-stringify-1.0.0.tgz", - "integrity": "sha512-cjwII1MdIIVloKvC9ErQ+OgAtwHBmcZ0Bg4ciz78FtbT8In39aAYbaA7zvxQ61xVMSPE8WxhLwLbhif4Js2C+g==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "estree-util-to-js": "^2.0.0", - "unified": "^11.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/regex/-/regex-6.1.0.tgz", - "integrity": "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==", - "license": "MIT", - "dependencies": { - "regex-utilities": "^2.3.0" - } - }, - "node_modules/regex-recursion": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/regex-recursion/-/regex-recursion-6.0.2.tgz", - "integrity": "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==", - "license": "MIT", - "dependencies": { - "regex-utilities": "^2.3.0" - } - }, - "node_modules/regex-utilities": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/regex-utilities/-/regex-utilities-2.3.0.tgz", - "integrity": "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==", - "license": "MIT" - }, - "node_modules/rehype-raw": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/rehype-raw/-/rehype-raw-7.0.0.tgz", - "integrity": "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "hast-util-raw": "^9.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/rehype-recma": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/rehype-recma/-/rehype-recma-1.0.0.tgz", - "integrity": "sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "@types/hast": "^3.0.0", - "hast-util-to-estree": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/rehype-stringify": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/rehype-stringify/-/rehype-stringify-10.0.1.tgz", - "integrity": "sha512-k9ecfXHmIPuFVI61B9DeLPN0qFHfawM6RsuX48hoqlaKSF61RskNjSm1lI8PhBEM0MRdLxVVm4WmTqJQccH9mA==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "hast-util-to-html": "^9.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-gfm": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz", - "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-gfm": "^3.0.0", - "micromark-extension-gfm": "^3.0.0", - "remark-parse": "^11.0.0", - "remark-stringify": "^11.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-mdx": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/remark-mdx/-/remark-mdx-3.1.1.tgz", - "integrity": "sha512-Pjj2IYlUY3+D8x00UJsIOg5BEvfMyeI+2uLPn9VO9Wg4MEtN/VTIq2NEJQfde9PnX15KgtHyl9S0BcTnWrIuWg==", - "license": "MIT", - "dependencies": { - "mdast-util-mdx": "^3.0.0", - "micromark-extension-mdxjs": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-parse": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", - "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-from-markdown": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-rehype": { - "version": "11.1.2", - "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.2.tgz", - "integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "mdast-util-to-hast": "^13.0.0", - "unified": "^11.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-smartypants": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/remark-smartypants/-/remark-smartypants-3.0.3.tgz", - "integrity": "sha512-gCaK+ndZ0hYezlqFegHFCVh2CQemsi0Npdh1qVM9bxlUFknjkbP6VmojWhddOCrbK0PbbacmYLWfTULRiT1eWA==", - "license": "MIT", - "dependencies": { - "retext": "^9.0.0", - "retext-smartypants": "^6.0.0", - "unified": "^11.0.4", - "unist-util-visit": "^5.0.0" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/remark-stringify": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz", - "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-to-markdown": "^2.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/request-light": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/request-light/-/request-light-0.7.0.tgz", - "integrity": "sha512-lMbBMrDoxgsyO+yB3sDcrDuX85yYt7sS8BfQd11jtbW/z5ZWgLZRcEGLsLoYw7I0WSUGQBs8CC8ScIxkTX1+6Q==", - "license": "MIT" - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve-pkg-maps": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", - "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", - "license": "MIT", - "funding": { - "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" - } - }, - "node_modules/retext": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/retext/-/retext-9.0.0.tgz", - "integrity": "sha512-sbMDcpHCNjvlheSgMfEcVrZko3cDzdbe1x/e7G66dFp0Ff7Mldvi2uv6JkJQzdRcvLYE8CA8Oe8siQx8ZOgTcA==", - "license": "MIT", - "dependencies": { - "@types/nlcst": "^2.0.0", - "retext-latin": "^4.0.0", - "retext-stringify": "^4.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/retext-latin": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/retext-latin/-/retext-latin-4.0.0.tgz", - "integrity": "sha512-hv9woG7Fy0M9IlRQloq/N6atV82NxLGveq+3H2WOi79dtIYWN8OaxogDm77f8YnVXJL2VD3bbqowu5E3EMhBYA==", - "license": "MIT", - "dependencies": { - "@types/nlcst": "^2.0.0", - "parse-latin": "^7.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/retext-smartypants": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/retext-smartypants/-/retext-smartypants-6.2.0.tgz", - "integrity": "sha512-kk0jOU7+zGv//kfjXEBjdIryL1Acl4i9XNkHxtM7Tm5lFiCog576fjNC9hjoR7LTKQ0DsPWy09JummSsH1uqfQ==", - "license": "MIT", - "dependencies": { - "@types/nlcst": "^2.0.0", - "nlcst-to-string": "^4.0.0", - "unist-util-visit": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/retext-stringify": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/retext-stringify/-/retext-stringify-4.0.0.tgz", - "integrity": "sha512-rtfN/0o8kL1e+78+uxPTqu1Klt0yPzKuQ2BfWwwfgIUSayyzxpM1PJzkKt4V8803uB9qSy32MvI7Xep9khTpiA==", - "license": "MIT", - "dependencies": { - "@types/nlcst": "^2.0.0", - "nlcst-to-string": "^4.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/rolldown": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.3.tgz", - "integrity": "sha512-rn9wpmxplLf7NLNyCk9FyWh3FM43DbY8jOzCdEPzH7uflhTftRbCEpqi6Ly2osgoU8OwObtmavMbWLaWy4LX7A==", + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.13.tgz", + "integrity": "sha512-/5apOkwWxtACcQtZ5WlrAsKS4JwXVThQPJZ3RqIruRY9UG9M8e0VlXN068xum/JRKA4aIUoJ13ghe/kGCvidiQ==", + "dev": true, "license": "MIT", + "peer": true, "dependencies": { - "@oxc-project/types": "=0.143.0", + "@oxc-project/types": "=0.153.0", "@rolldown/pluginutils": "^1.0.0" }, "bin": { @@ -6805,376 +1378,56 @@ "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.2.3", - "@rolldown/binding-darwin-arm64": "1.2.3", - "@rolldown/binding-darwin-x64": "1.2.3", - "@rolldown/binding-freebsd-x64": "1.2.3", - "@rolldown/binding-linux-arm-gnueabihf": "1.2.3", - "@rolldown/binding-linux-arm64-gnu": "1.2.3", - "@rolldown/binding-linux-arm64-musl": "1.2.3", - "@rolldown/binding-linux-ppc64-gnu": "1.2.3", - "@rolldown/binding-linux-s390x-gnu": "1.2.3", - "@rolldown/binding-linux-x64-gnu": "1.2.3", - "@rolldown/binding-linux-x64-musl": "1.2.3", - "@rolldown/binding-openharmony-arm64": "1.2.3", - "@rolldown/binding-win32-arm64-msvc": "1.2.3", - "@rolldown/binding-win32-x64-msvc": "1.2.3" - } - }, - "node_modules/rolldown/node_modules/@rolldown/pluginutils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", - "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", - "license": "MIT" - }, - "node_modules/satteri": { - "version": "0.9.5", - "resolved": "https://registry.npmjs.org/satteri/-/satteri-0.9.5.tgz", - "integrity": "sha512-ZuWVl+vnM64y+/TtX8Kosv2c00W+hLQiiwnEL6H0UKVVrxFqMw4D2CJHHQaouVd89OAhtBBfjWLqhKi3TVUV4w==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.5", - "@types/hast": "^3.0.4", - "@types/mdast": "^4.0.4", - "@types/unist": "^3.0.3" - }, - "optionalDependencies": { - "@bruits/satteri-darwin-arm64": "0.9.5", - "@bruits/satteri-darwin-x64": "0.9.5", - "@bruits/satteri-linux-arm64-gnu": "0.9.5", - "@bruits/satteri-linux-arm64-musl": "0.9.5", - "@bruits/satteri-linux-x64-gnu": "0.9.5", - "@bruits/satteri-linux-x64-musl": "0.9.5", - "@bruits/satteri-wasm32-wasi": "0.9.5", - "@bruits/satteri-win32-arm64-msvc": "0.9.5", - "@bruits/satteri-win32-x64-msvc": "0.9.5" - } - }, - "node_modules/sax": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.1.tgz", - "integrity": "sha512-42tBVwLWnaQvW5zc4HbZrTuWccECCZfBi92FDuwtqxasH+JbPB3/FOKb1m222K42R4WxuxzzMsTswfzgtSu64Q==", - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=11.0.0" - } - }, - "node_modules/scheduler": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", - "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", - "license": "MIT" - }, - "node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/send": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", - "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", - "license": "MIT", - "dependencies": { - "debug": "^4.4.3", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "etag": "^1.8.1", - "fresh": "^2.0.0", - "http-errors": "^2.0.1", - "mime-types": "^3.0.2", - "ms": "^2.1.3", - "on-finished": "^2.4.1", - "range-parser": "^1.2.1", - "statuses": "^2.0.2" - }, - "engines": { - "node": ">= 18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/server-destroy": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/server-destroy/-/server-destroy-1.0.1.tgz", - "integrity": "sha512-rb+9B5YBIEzYcD6x2VKidaa+cqYBJQKnU4oe4E3ANwRRN56yk/ua1YCJT1n21NTS8w6CcOclAKNP3PhdCXKYtQ==", - "license": "ISC" - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "license": "ISC" - }, - "node_modules/sharp": { - "version": "0.35.3", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.35.3.tgz", - "integrity": "sha512-ej0zVHuZGHCiABXcNxeYhpRnPNPAcvbG8RMdBAhDAxLKkCRVSpK3Iyu7qbqw3JMzoj0REeM6f3tJLtVwl0023Q==", - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "@img/colour": "^1.1.0", - "detect-libc": "^2.1.2", - "semver": "^7.8.5" - }, - "engines": { - "node": ">=20.9.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.35.3", - "@img/sharp-darwin-x64": "0.35.3", - "@img/sharp-freebsd-wasm32": "0.35.3", - "@img/sharp-libvips-darwin-arm64": "1.3.2", - "@img/sharp-libvips-darwin-x64": "1.3.2", - "@img/sharp-libvips-linux-arm": "1.3.2", - "@img/sharp-libvips-linux-arm64": "1.3.2", - "@img/sharp-libvips-linux-ppc64": "1.3.2", - "@img/sharp-libvips-linux-riscv64": "1.3.2", - "@img/sharp-libvips-linux-s390x": "1.3.2", - "@img/sharp-libvips-linux-x64": "1.3.2", - "@img/sharp-libvips-linuxmusl-arm64": "1.3.2", - "@img/sharp-libvips-linuxmusl-x64": "1.3.2", - "@img/sharp-linux-arm": "0.35.3", - "@img/sharp-linux-arm64": "0.35.3", - "@img/sharp-linux-ppc64": "0.35.3", - "@img/sharp-linux-riscv64": "0.35.3", - "@img/sharp-linux-s390x": "0.35.3", - "@img/sharp-linux-x64": "0.35.3", - "@img/sharp-linuxmusl-arm64": "0.35.3", - "@img/sharp-linuxmusl-x64": "0.35.3", - "@img/sharp-webcontainers-wasm32": "0.35.3", - "@img/sharp-win32-arm64": "0.35.3", - "@img/sharp-win32-ia32": "0.35.3", - "@img/sharp-win32-x64": "0.35.3" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/sharp/node_modules/semver": { - "version": "7.8.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", - "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", - "license": "ISC", - "optional": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/shiki": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-4.4.2.tgz", - "integrity": "sha512-P8F/dFhRevaw2uSdeIYlq/5SXZNY85DPtmXQ947gD1Zj2JqO5AkNvVVBar0Me9JkFx3uzVud/qOtP5ek9NEQGA==", - "license": "MIT", - "dependencies": { - "@shikijs/core": "4.4.2", - "@shikijs/engine-javascript": "4.4.2", - "@shikijs/engine-oniguruma": "4.4.2", - "@shikijs/langs": "4.4.2", - "@shikijs/themes": "4.4.2", - "@shikijs/types": "4.4.2", - "@shikijs/vscode-textmate": "^10.0.2", - "@types/hast": "^3.0.5" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "license": "MIT" - }, - "node_modules/smol-toml": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.7.1.tgz", - "integrity": "sha512-PPlsspAZ4jbMBu5DMFhfUGDQLu/vrL4SyBROVS37x8ynnVmFIs1VPBz1Co8Xks3TvpIaZXmU85y4DrQ+UyVFoQ==", - "license": "BSD-3-Clause", - "engines": { - "node": ">= 18" - }, - "funding": { - "url": "https://github.com/sponsors/cyyynthia" - } - }, - "node_modules/source-map": { - "version": "0.7.6", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", - "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", - "license": "BSD-3-Clause", - "engines": { - "node": ">= 12" + "@rolldown/binding-android-arm-eabi": "1.2.13", + "@rolldown/binding-android-arm64": "1.2.13", + "@rolldown/binding-darwin-arm64": "1.2.13", + "@rolldown/binding-darwin-x64": "1.2.13", + "@rolldown/binding-freebsd-x64": "1.2.13", + "@rolldown/binding-linux-arm-gnueabihf": "1.2.13", + "@rolldown/binding-linux-arm64-gnu": "1.2.13", + "@rolldown/binding-linux-arm64-musl": "1.2.13", + "@rolldown/binding-linux-ppc64-gnu": "1.2.13", + "@rolldown/binding-linux-s390x-gnu": "1.2.13", + "@rolldown/binding-linux-x64-gnu": "1.2.13", + "@rolldown/binding-linux-x64-musl": "1.2.13", + "@rolldown/binding-openharmony-arm64": "1.2.13", + "@rolldown/binding-win32-arm64-msvc": "1.2.13", + "@rolldown/binding-win32-x64-msvc": "1.2.13" } }, "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.2.tgz", + "integrity": "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==", + "dev": true, "license": "BSD-3-Clause", + "peer": true, "engines": { "node": ">=0.10.0" } }, - "node_modules/space-separated-tokens": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", - "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/statuses": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", - "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/string-width": { - "version": "8.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.2.tgz", - "integrity": "sha512-GaPUh5gfdrYzqeVNZvUfT23vYYxXzKYidUcnMtJg/3rxRV63EFZy3k6xfKlmfeJD0176lnUV/Usr3XcwSvFzpg==", - "license": "MIT", - "dependencies": { - "get-east-asian-width": "^1.5.0", - "strip-ansi": "^7.1.2" - }, - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/stringify-entities": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", - "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", - "license": "MIT", - "dependencies": { - "character-entities-html4": "^2.0.0", - "character-entities-legacy": "^3.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/strip-ansi": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", - "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.2.2" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/style-to-js": { - "version": "1.1.21", - "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.21.tgz", - "integrity": "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==", - "license": "MIT", - "dependencies": { - "style-to-object": "1.0.14" - } - }, - "node_modules/style-to-object": { - "version": "1.0.14", - "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.14.tgz", - "integrity": "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==", - "license": "MIT", - "dependencies": { - "inline-style-parser": "0.2.7" - } - }, - "node_modules/svgo": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-4.0.2.tgz", - "integrity": "sha512-ekx94z1rRc5LDi6oSUaeRnYhd0UOJxdtQCL2rF8xpWxD3TPAsISWOrxezqGovqS38GRZOdpDfvQe3ts6F7nsng==", - "license": "MIT", - "dependencies": { - "commander": "^11.1.0", - "css-select": "^5.1.0", - "css-tree": "^3.0.1", - "css-what": "^6.1.0", - "csso": "^5.0.5", - "picocolors": "^1.1.1", - "sax": "^1.5.0" - }, - "bin": { - "svgo": "bin/svgo.js" - }, - "engines": { - "node": ">=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/svgo" - } - }, - "node_modules/tailwindcss": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.3.tgz", - "integrity": "sha512-gOhV3P7ufE62QDGg1zVaTgCR+EtPv92k2nIhVcVKcLmxT1sUBsQGhnZj175j+MqRt4zLF7ic+sCYjfhxMxj7YQ==", + "node_modules/std-env": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.3.0.tgz", + "integrity": "sha512-OtU/EgQ1kIm5KwqQpBC6ZEMXrZRui11w8zgfTWp8cdO9B8OaPsbA8bTHO2P+HNo1VlUTGMVBwPhydu6poeXiag==", + "dev": true, "license": "MIT" }, - "node_modules/tapable": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", - "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", + "node_modules/tinybench": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-6.2.2.tgz", + "integrity": "sha512-nIZ2N2dFpjBsg2hgitKu7mA+FvsIJuGJOwysRngmVRRmWgR/UyPjwJj8c/fT/dhm8EkHnF5EOogRf+0Z2qrimA==", + "dev": true, "license": "MIT", "engines": { - "node": ">=6" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/tiny-inflate": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", - "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==", - "license": "MIT" - }, - "node_modules/tinyclip": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/tinyclip/-/tinyclip-0.1.15.tgz", - "integrity": "sha512-uo33abH+Ays0xYaDysoBt494Hb3hsEczMpcC0MwFl773pazORx4fmvKhclhR1wonUbB6vvpRsvVMwnhfqeMc+A==", - "license": "MIT", - "engines": { - "node": "^16.14.0 || >= 17.3.0" + "node": ">=20.0.0" } }, "node_modules/tinyexec": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.0.tgz", - "integrity": "sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.1.tgz", + "integrity": "sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==", + "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -7184,6 +1437,7 @@ "version": "0.2.17", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, "license": "MIT", "dependencies": { "fdir": "^6.5.0", @@ -7196,51 +1450,29 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "node_modules/tsx": { + "version": "4.23.15", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.23.15.tgz", + "integrity": "sha512-Yiex1Ovn8z2xPpOWckIiysV1SSyRMY9BkLF++q0yKiDxCqRhosKfMg3janKkiLBwZ5c/YryloKwGZcrEmtwxKw==", + "dev": true, "license": "MIT", + "dependencies": { + "esbuild": "~0.28.0" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, "engines": { - "node": ">=0.6" + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" } }, - "node_modules/trim-lines": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", - "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/trough": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", - "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/typesafe-path": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/typesafe-path/-/typesafe-path-0.2.2.tgz", - "integrity": "sha512-OJabfkAg1WLZSqJAJ0Z6Sdt3utnbzr/jh+NAHoyWHJe8CMSy79Gm085094M9nvTPy22KzTVn5Zq5mbapCI/hPA==", - "license": "MIT" - }, "node_modules/typescript": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", - "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", @@ -7250,432 +1482,25 @@ "node": ">=14.17" } }, - "node_modules/typescript-auto-import-cache": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/typescript-auto-import-cache/-/typescript-auto-import-cache-0.3.6.tgz", - "integrity": "sha512-RpuHXrknHdVdK7wv/8ug3Fr0WNsNi5l5aB8MYYuXhq2UH5lnEB1htJ1smhtD5VeCsGr2p8mUDtd83LCQDFVgjQ==", - "license": "MIT", - "dependencies": { - "semver": "^7.3.8" - } - }, - "node_modules/typescript-auto-import-cache/node_modules/semver": { - "version": "7.8.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", - "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/ufo": { - "version": "1.6.4", - "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.4.tgz", - "integrity": "sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==", - "license": "MIT" - }, - "node_modules/ultrahtml": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/ultrahtml/-/ultrahtml-1.7.0.tgz", - "integrity": "sha512-2xRd0VHoAQE4M+vF/DvFFB7pUV0ZxTW1TLi7lHQWnF/Sb5TPeEUV/l+hxcNnGO00ZXGnR0voCMmYRKQf+rvJ2g==", - "license": "MIT" - }, - "node_modules/uncrypto": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/uncrypto/-/uncrypto-0.1.3.tgz", - "integrity": "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==", - "license": "MIT" - }, "node_modules/undici-types": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.3.0.tgz", - "integrity": "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, "license": "MIT" }, - "node_modules/unified": { - "version": "11.0.5", - "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", - "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "bail": "^2.0.0", - "devlop": "^1.0.0", - "extend": "^3.0.0", - "is-plain-obj": "^4.0.0", - "trough": "^2.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unifont": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/unifont/-/unifont-0.7.4.tgz", - "integrity": "sha512-oHeis4/xl42HUIeHuNZRGEvxj5AaIKR+bHPNegRq5LV1gdc3jundpONbjglKpihmJf+dswygdMJn3eftGIMemg==", - "license": "MIT", - "dependencies": { - "css-tree": "^3.1.0", - "ofetch": "^1.5.1", - "ohash": "^2.0.11" - } - }, - "node_modules/unist-util-find-after": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-find-after/-/unist-util-find-after-5.0.0.tgz", - "integrity": "sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-is": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", - "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-modify-children": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/unist-util-modify-children/-/unist-util-modify-children-4.0.0.tgz", - "integrity": "sha512-+tdN5fGNddvsQdIzUF3Xx82CU9sMM+fA0dLgR9vOmT0oPT2jH+P1nd5lSqfCfXAw+93NhcXNY2qqvTUtE4cQkw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "array-iterate": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-position": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", - "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-position-from-estree": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unist-util-position-from-estree/-/unist-util-position-from-estree-2.0.0.tgz", - "integrity": "sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-remove-position": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-5.0.0.tgz", - "integrity": "sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-visit": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-stringify-position": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", - "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.1.0.tgz", - "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit-children": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unist-util-visit-children/-/unist-util-visit-children-3.0.0.tgz", - "integrity": "sha512-RgmdTfSBOg04sdPcpTSD1jzoNBjt9a80/ZCzp5cI9n1qPzLZWF9YdvWGN2zmTumP1HWhXKdUWexjy/Wy/lJ7tA==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit-parents": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", - "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unstorage": { - "version": "1.17.5", - "resolved": "https://registry.npmjs.org/unstorage/-/unstorage-1.17.5.tgz", - "integrity": "sha512-0i3iqvRfx29hkNntHyQvJTpf5W9dQ9ZadSoRU8+xVlhVtT7jAX57fazYO9EHvcRCfBCyi5YRya7XCDOsbTgkPg==", - "license": "MIT", - "dependencies": { - "anymatch": "^3.1.3", - "chokidar": "^5.0.0", - "destr": "^2.0.5", - "h3": "^1.15.10", - "lru-cache": "^11.2.7", - "node-fetch-native": "^1.6.7", - "ofetch": "^1.5.1", - "ufo": "^1.6.3" - }, - "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 - } - } - }, - "node_modules/unstorage/node_modules/chokidar": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", - "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", - "license": "MIT", - "dependencies": { - "readdirp": "^5.0.0" - }, - "engines": { - "node": ">= 20.19.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/unstorage/node_modules/lru-cache": { - "version": "11.5.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz", - "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==", - "license": "BlueOak-1.0.0", - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/unstorage/node_modules/readdirp": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.1.1.tgz", - "integrity": "sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA==", - "license": "MIT", - "engines": { - "node": ">= 20.19.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", - "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/vfile": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", - "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vfile-location": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-5.0.3.tgz", - "integrity": "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vfile-message": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", - "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-stringify-position": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/vite": { - "version": "8.2.1", - "resolved": "https://registry.npmjs.org/vite/-/vite-8.2.1.tgz", - "integrity": "sha512-EU/eS7BH3XROHh2YnBefjM6DBKA6ZeMZEYQbj7NLWg5wHYlhB8B/Mayd5XsgWq+NFYccDOTemRpdETWR6Ka/lw==", + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.3.4.tgz", + "integrity": "sha512-IftLE8phJhdNRIGNEV4JCCvfNScoZQVfcXaN8TQropWGJtOHcu+iAB9pHcuGvmEb/kddw0LBe/jS8ztdC6xdiQ==", + "dev": true, "license": "MIT", + "peer": true, "dependencies": { "lightningcss": "^1.33.0", - "picomatch": "^4.0.5", - "postcss": "^8.5.25", - "rolldown": "~1.2.1", + "picomatch": "^4.0.7", + "postcss": "^8.5.29", + "rolldown": "~1.2.12", "tinyglobby": "^0.2.17" }, "bin": { @@ -7692,7 +1517,7 @@ }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", - "@vitejs/devtools": "^0.4.0", + "@vitejs/devtools": "^0.7.1", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", @@ -7743,617 +1568,106 @@ } } }, - "node_modules/vite/node_modules/lightningcss": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", - "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", - "license": "MPL-2.0", - "dependencies": { - "detect-libc": "^2.0.3" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - }, - "optionalDependencies": { - "lightningcss-android-arm64": "1.33.0", - "lightningcss-darwin-arm64": "1.33.0", - "lightningcss-darwin-x64": "1.33.0", - "lightningcss-freebsd-x64": "1.33.0", - "lightningcss-linux-arm-gnueabihf": "1.33.0", - "lightningcss-linux-arm64-gnu": "1.33.0", - "lightningcss-linux-arm64-musl": "1.33.0", - "lightningcss-linux-x64-gnu": "1.33.0", - "lightningcss-linux-x64-musl": "1.33.0", - "lightningcss-win32-arm64-msvc": "1.33.0", - "lightningcss-win32-x64-msvc": "1.33.0" - } - }, - "node_modules/vite/node_modules/lightningcss-android-arm64": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz", - "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", - "cpu": [ - "arm64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/vite/node_modules/lightningcss-darwin-arm64": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz", - "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", - "cpu": [ - "arm64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/vite/node_modules/lightningcss-darwin-x64": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz", - "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", - "cpu": [ - "x64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/vite/node_modules/lightningcss-freebsd-x64": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz", - "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", - "cpu": [ - "x64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/vite/node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz", - "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", - "cpu": [ - "arm" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/vite/node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz", - "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", - "cpu": [ - "arm64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/vite/node_modules/lightningcss-linux-arm64-musl": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz", - "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", - "cpu": [ - "arm64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/vite/node_modules/lightningcss-linux-x64-gnu": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz", - "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", - "cpu": [ - "x64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/vite/node_modules/lightningcss-linux-x64-musl": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz", - "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", - "cpu": [ - "x64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/vite/node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz", - "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", - "cpu": [ - "arm64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/vite/node_modules/lightningcss-win32-x64-msvc": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz", - "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", - "cpu": [ - "x64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/vitefu": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.1.3.tgz", - "integrity": "sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg==", - "license": "MIT", - "workspaces": [ - "tests/deps/*", - "tests/projects/*", - "tests/projects/workspace/packages/*" - ], - "peerDependencies": { - "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "vite": { - "optional": true - } - } - }, - "node_modules/volar-service-css": { - "version": "0.0.71", - "resolved": "https://registry.npmjs.org/volar-service-css/-/volar-service-css-0.0.71.tgz", - "integrity": "sha512-wRRFt9BpjMKCazcgOh67MSjUjiWUCAh99DyYSDIOTuxaRjEtDC7PpB0k1Y1wbJIW/pVtMUSVbpPo3UGSm0Byxw==", + "node_modules/vitest": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-5.0.3.tgz", + "integrity": "sha512-xMw97S3rjdtj5dkVat7jCsqWBpvchs3RlpQctUqwJD0KkERk40vz2fJ77lDwW/Vzh/pk18eItYAzkodhSes3jQ==", + "dev": true, "license": "MIT", "dependencies": { - "vscode-css-languageservice": "^6.3.0", - "vscode-languageserver-textdocument": "^1.0.11", - "vscode-uri": "^3.0.8" + "@types/chai": "^5.2.2", + "@vitest/mocker": "5.0.3", + "chai": "^6.2.2", + "es-module-lexer": "^2.3.2", + "expect-type": "^1.4.0", + "magic-string": "^1.2.3", + "obug": "^2.1.4", + "picomatch": "^4.0.7", + "std-env": "^4.2.0", + "tinybench": "^6.1.4", + "tinyexec": "^1.3.0", + "tinyglobby": "^0.2.17", + "why-is-node-running": "3.2.1" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^22.12.0 || ^24.0.0 || >=26.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "@volar/language-service": "~2.4.0" + "@edge-runtime/vm": "*", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "5.0.3", + "@vitest/browser-preview": "5.0.3", + "@vitest/browser-webdriverio": "^5.0.0-beta.5 || >=5.0.0", + "@vitest/coverage-istanbul": "5.0.3", + "@vitest/coverage-v8": "5.0.3", + "@vitest/ui": "5.0.3", + "happy-dom": "*", + "jsdom": "*", + "vite": "^6.4.0 || ^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { - "@volar/language-service": { - "optional": true - } - } - }, - "node_modules/volar-service-emmet": { - "version": "0.0.71", - "resolved": "https://registry.npmjs.org/volar-service-emmet/-/volar-service-emmet-0.0.71.tgz", - "integrity": "sha512-zqjzt6bN95e3CUstBm0PBFAJnrfz0ZAARka87fart46/gNCLLuP3Vujy8V/J8HEziTFLnfkgIASLFYPUhonJcA==", - "license": "MIT", - "dependencies": { - "@emmetio/css-parser": "^0.4.1", - "@emmetio/html-matcher": "^1.3.0", - "@vscode/emmet-helper": "^2.9.3", - "vscode-uri": "^3.0.8" - }, - "peerDependencies": { - "@volar/language-service": "~2.4.0" - }, - "peerDependenciesMeta": { - "@volar/language-service": { - "optional": true - } - } - }, - "node_modules/volar-service-html": { - "version": "0.0.71", - "resolved": "https://registry.npmjs.org/volar-service-html/-/volar-service-html-0.0.71.tgz", - "integrity": "sha512-e8tHPhgQ7ooLfudAEIku+kgd9pWkq3SSz8RbnQDI1+Eb8wbenkLGHqoirLqz5ORLV6wIMr2Iv08RWBG5eOcgpw==", - "license": "MIT", - "dependencies": { - "vscode-html-languageservice": "^5.3.0", - "vscode-languageserver-textdocument": "^1.0.11", - "vscode-uri": "^3.0.8" - }, - "peerDependencies": { - "@volar/language-service": "~2.4.0" - }, - "peerDependenciesMeta": { - "@volar/language-service": { - "optional": true - } - } - }, - "node_modules/volar-service-prettier": { - "version": "0.0.71", - "resolved": "https://registry.npmjs.org/volar-service-prettier/-/volar-service-prettier-0.0.71.tgz", - "integrity": "sha512-Rz7JVH3qD108UCdmIEiZvOBNljMt2nLFdbN8AXcDfn7xD9F5I2aCIsDVqBbXw21PsnxG0b7MfwtNF+zPS/NKUg==", - "license": "MIT", - "dependencies": { - "vscode-uri": "^3.0.8" - }, - "peerDependencies": { - "@volar/language-service": "~2.4.0", - "prettier": "^2.2 || ^3.0" - }, - "peerDependenciesMeta": { - "@volar/language-service": { + "@edge-runtime/vm": { "optional": true }, - "prettier": { + "@opentelemetry/api": { "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/coverage-istanbul": { + "optional": true + }, + "@vitest/coverage-v8": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + }, + "vite": { + "optional": false } } }, - "node_modules/volar-service-typescript": { - "version": "0.0.71", - "resolved": "https://registry.npmjs.org/volar-service-typescript/-/volar-service-typescript-0.0.71.tgz", - "integrity": "sha512-yTtM/BVT6hoyEYnDtaCyAtNhdNeS/mhTTABlBOdw3NNiRBUin3IznFJpgfjer4c6RYopiPjjQjc9VFhxVl1mLw==", + "node_modules/why-is-node-running": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-3.2.1.tgz", + "integrity": "sha512-Tb2FUhB4vUsGQlfSquQLYkApkuPAFQXGFzxWKHHumVz2dK+X1RUm/HnID4+TfIGYJ1kTcwOaCk/buYCEJr6YjQ==", + "dev": true, "license": "MIT", - "dependencies": { - "path-browserify": "^1.0.1", - "semver": "^7.6.2", - "typescript-auto-import-cache": "^0.3.5", - "vscode-languageserver-textdocument": "^1.0.11", - "vscode-nls": "^5.2.0", - "vscode-uri": "^3.0.8" - }, - "peerDependencies": { - "@volar/language-service": "~2.4.0" - }, - "peerDependenciesMeta": { - "@volar/language-service": { - "optional": true - } - } - }, - "node_modules/volar-service-typescript-twoslash-queries": { - "version": "0.0.71", - "resolved": "https://registry.npmjs.org/volar-service-typescript-twoslash-queries/-/volar-service-typescript-twoslash-queries-0.0.71.tgz", - "integrity": "sha512-9K2k72s4n7rV9s4bX0MyjbX9iBribvKZbBJKuEmTCZfeWJXs6Yh7bGpY4eoc7UufAjvpheBqwyZCOIPBvxCv0A==", - "license": "MIT", - "dependencies": { - "vscode-uri": "^3.0.8" - }, - "peerDependencies": { - "@volar/language-service": "~2.4.0" - }, - "peerDependenciesMeta": { - "@volar/language-service": { - "optional": true - } - } - }, - "node_modules/volar-service-typescript/node_modules/semver": { - "version": "7.8.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", - "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", - "license": "ISC", "bin": { - "semver": "bin/semver.js" + "why-is-node-running": "cli.js" }, "engines": { - "node": ">=10" + "node": ">=20.11" } }, - "node_modules/volar-service-yaml": { - "version": "0.0.71", - "resolved": "https://registry.npmjs.org/volar-service-yaml/-/volar-service-yaml-0.0.71.tgz", - "integrity": "sha512-qYGWGuVpUTnZGu5P/CR4KLK4aIR8RrcVnmfZ2eRcj9q/I8VZCoC5yy9FtEvfNvnDp4MU17yhdJcvpQPIqhJS2Q==", - "license": "MIT", - "dependencies": { - "vscode-uri": "^3.0.8", - "yaml-language-server": "~1.23.0" - }, - "peerDependencies": { - "@volar/language-service": "~2.4.0" - }, - "peerDependenciesMeta": { - "@volar/language-service": { - "optional": true - } - } - }, - "node_modules/vscode-css-languageservice": { - "version": "6.3.10", - "resolved": "https://registry.npmjs.org/vscode-css-languageservice/-/vscode-css-languageservice-6.3.10.tgz", - "integrity": "sha512-eq5N9Er3fC4vA9zd9EFhyBG90wtCCuXgRSpAndaOgXMh1Wgep5lBgRIeDgjZBW9pa+332yC9+49cZMW8jcL3MA==", - "license": "MIT", - "dependencies": { - "@vscode/l10n": "^0.0.18", - "vscode-languageserver-textdocument": "^1.0.12", - "vscode-languageserver-types": "3.17.5", - "vscode-uri": "^3.1.0" - } - }, - "node_modules/vscode-css-languageservice/node_modules/vscode-languageserver-types": { - "version": "3.17.5", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", - "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==", - "license": "MIT" - }, - "node_modules/vscode-html-languageservice": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/vscode-html-languageservice/-/vscode-html-languageservice-5.6.2.tgz", - "integrity": "sha512-ulCrSnFnfQ16YzvwnYUgEbUEl/ZG7u2eV27YhvLObSHKkb8fw1Z9cgsnUwjTEeDIdJDoTDTDpxuhQwoenoLNMg==", - "license": "MIT", - "dependencies": { - "@vscode/l10n": "^0.0.18", - "vscode-languageserver-textdocument": "^1.0.12", - "vscode-languageserver-types": "^3.17.5", - "vscode-uri": "^3.1.0" - } - }, - "node_modules/vscode-json-languageservice": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/vscode-json-languageservice/-/vscode-json-languageservice-4.1.8.tgz", - "integrity": "sha512-0vSpg6Xd9hfV+eZAaYN63xVVMOTmJ4GgHxXnkLCh+9RsQBkWKIghzLhW2B9ebfG+LQQg8uLtsQ2aUKjTgE+QOg==", - "license": "MIT", - "dependencies": { - "jsonc-parser": "^3.0.0", - "vscode-languageserver-textdocument": "^1.0.1", - "vscode-languageserver-types": "^3.16.0", - "vscode-nls": "^5.0.0", - "vscode-uri": "^3.0.2" - }, - "engines": { - "npm": ">=7.0.0" - } - }, - "node_modules/vscode-jsonrpc": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-9.0.1.tgz", - "integrity": "sha512-rfuA6T75H6m5EkbhtEPzre9pT0HPcDI2MMy4+nPFIBks5J8JBAUHD4tRYSgaBOijIEC7SRkC1kKyXTLqbmh9jw==", - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/vscode-languageserver": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-9.0.1.tgz", - "integrity": "sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==", - "license": "MIT", - "dependencies": { - "vscode-languageserver-protocol": "3.17.5" - }, - "bin": { - "installServerIntoExtension": "bin/installServerIntoExtension" - } - }, - "node_modules/vscode-languageserver-protocol": { - "version": "3.18.2", - "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.18.2.tgz", - "integrity": "sha512-XRyDbT0Pp3sSNti3JmxVEUMySWCSi1hhM+/KUlCy1hV1zmrqpM1OwO12EAki8blhmLuIMpaJrYbo0OzGVfK2Qg==", - "license": "MIT", - "dependencies": { - "vscode-jsonrpc": "9.0.1", - "vscode-languageserver-types": "3.18.0" - } - }, - "node_modules/vscode-languageserver-textdocument": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz", - "integrity": "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==", - "license": "MIT" - }, - "node_modules/vscode-languageserver-types": { - "version": "3.18.0", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.18.0.tgz", - "integrity": "sha512-8TsGPNMIMiiBdkORgRSvLjuiEIiAFtO+KssmYWxQ+uSVvlf7RjK8YKCOjPzZ+YA04jXEV7+7LvkSmHkhpNS99g==", - "license": "MIT" - }, - "node_modules/vscode-languageserver/node_modules/vscode-jsonrpc": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", - "integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==", - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/vscode-languageserver/node_modules/vscode-languageserver-protocol": { - "version": "3.17.5", - "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz", - "integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==", - "license": "MIT", - "dependencies": { - "vscode-jsonrpc": "8.2.0", - "vscode-languageserver-types": "3.17.5" - } - }, - "node_modules/vscode-languageserver/node_modules/vscode-languageserver-types": { - "version": "3.17.5", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", - "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==", - "license": "MIT" - }, - "node_modules/vscode-nls": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-5.2.0.tgz", - "integrity": "sha512-RAaHx7B14ZU04EU31pT+rKz2/zSl7xMsfIZuo8pd+KZO6PXtQmpevpq3vxvWNcrGbdmhM/rr5Uw5Mz+NBfhVng==", - "license": "MIT" - }, - "node_modules/vscode-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", - "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", - "license": "MIT" - }, - "node_modules/web-namespaces": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", - "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/wrap-ansi": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", - "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.2.1", - "string-width": "^7.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/string-width": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", - "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/xxhash-wasm": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/xxhash-wasm/-/xxhash-wasm-1.1.0.tgz", - "integrity": "sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA==", - "license": "MIT" - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "license": "ISC" - }, "node_modules/yaml": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", - "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.1.tgz", + "integrity": "sha512-3NxN8+78OdzbT7C/WjGsyfPAtJaN3FNDsWxv7Y7mcDsT/oOmgW8BpyQQFFBnvZE3j9Y2Sdz1ULFLezL7Eb2yFw==", "license": "ISC", "bin": { "yaml": "bin.mjs" @@ -8365,105 +1679,30 @@ "url": "https://github.com/sponsors/eemeli" } }, - "node_modules/yaml-language-server": { - "version": "1.23.0", - "resolved": "https://registry.npmjs.org/yaml-language-server/-/yaml-language-server-1.23.0.tgz", - "integrity": "sha512-3qVyCOexLCWw06PQa5kRPwvMWMZ/eZeCRWUvgD6a0OkqL/4iCnxy2WumbWifa937Uo5xhyWJ0uxlU39ljhNh7A==", - "license": "MIT", - "dependencies": { - "@vscode/l10n": "^0.0.18", - "ajv": "^8.17.1", - "ajv-draft-04": "^1.0.0", - "ajv-i18n": "^4.2.0", - "prettier": "^3.8.1", - "request-light": "^0.5.7", - "vscode-json-languageservice": "4.1.8", - "vscode-languageserver": "^9.0.0", - "vscode-languageserver-textdocument": "^1.0.1", - "vscode-languageserver-types": "^3.16.0", - "vscode-uri": "^3.0.2", - "yaml": "2.8.3" - }, - "bin": { - "yaml-language-server": "bin/yaml-language-server" - } - }, - "node_modules/yaml-language-server/node_modules/request-light": { - "version": "0.5.8", - "resolved": "https://registry.npmjs.org/request-light/-/request-light-0.5.8.tgz", - "integrity": "sha512-3Zjgh+8b5fhRJBQZoy+zbVKpAQGLyka0MPgW3zruTF4dFFJ8Fqcfu9YsAvi/rvdcaTeWG3MkbZv4WKxAn/84Lg==", - "license": "MIT" - }, - "node_modules/yaml-language-server/node_modules/yaml": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz", - "integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==", - "license": "ISC", - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14.6" - }, - "funding": { - "url": "https://github.com/sponsors/eemeli" - } - }, - "node_modules/yargs": { - "version": "18.1.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.1.0.tgz", - "integrity": "sha512-2rAgRKu54VsHkqI0/tYkmluGXHD4KW7yZoycuqDQ15QOTnc2VVfy0nN/1eMhnQLO00A+dwtK20xuCnc1YGeUyg==", - "license": "MIT", - "dependencies": { - "cliui": "^9.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "string-width": "^8.2.1", - "y18n": "^5.0.5", - "yargs-parser": "^22.0.0" - }, - "engines": { - "node": "^20.19.0 || ^22.12.0 || >=23" - } - }, - "node_modules/yargs-parser": { - "version": "22.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz", - "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==", - "license": "ISC", - "engines": { - "node": "^20.19.0 || ^22.12.0 || >=23" - } - }, - "node_modules/yocto-queue": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.2.tgz", - "integrity": "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==", - "license": "MIT", - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/zod": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", - "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", + "version": "4.6.6", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.6.6.tgz", + "integrity": "sha512-4UmzQUF5fxvfcYHlbjBmejU+w+hbDnWFx4aS8kBx7nPBHcO2SqPbpAG8snbdg+Xse4pRefR98hgjFgIMCI/UhA==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" } }, - "node_modules/zwitch": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", - "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "packages/schema": { + "name": "@gk/schema", + "version": "0.1.0", + "dependencies": { + "yaml": "^2.6.0", + "zod": "^4.1.0" + } + }, + "tools/migrate": { + "name": "@gk/migrate", + "version": "0.1.0", + "dependencies": { + "@gk/schema": "*", + "typescript": "^5.7.0", + "yaml": "^2.6.0" } } } diff --git a/package.json b/package.json index f432565..b2aa82e 100644 --- a/package.json +++ b/package.json @@ -1,39 +1,27 @@ { - "name": "greg-kilmer-profile", - "version": "1.0.0", - "type": "module", + "name": "gregkilmer-com", "private": true, - "description": "Personal profile / portfolio website for Greg Kilmer. Astro + React Islands + Node server.", - "scripts": { - "dev": "astro dev", - "start": "astro dev", - "build": "astro check && astro build", - "preview": "astro preview", - "serve": "node ./dist/server/entry.mjs", - "astro": "astro", - "check": "astro check", - "typecheck": "astro check && tsc --noEmit" + "type": "module", + "description": "GregKilmer.com: the note schema, content service and site that serve Greg's vault notes. The old site lives in legacy/ for reference only.", + "workspaces": [ + "packages/*", + "tools/*" + ], + "engines": { + "node": ">=22" }, - "dependencies": { - "@astrojs/check": "^0.9.9", - "@astrojs/mdx": "^7.0.2", - "@astrojs/node": "^11.0.2", - "@astrojs/react": "^6.0.2", - "@fontsource-variable/cinzel": "^5.3.0", - "@fontsource-variable/jetbrains-mono": "^5.2.8", - "@fontsource-variable/montserrat": "^5.3.0", - "@iconify-json/mdi": "^1.2.3", - "@tailwindcss/vite": "^4.3.2", - "@types/nodemailer": "^8.0.1", - "@types/react": "^19.2.17", - "@types/react-dom": "^19.2.3", - "astro": "^7.2.0", - "framer-motion": "^12.42.2", - "nodemailer": "^9.0.3", - "react": "^19.2.7", - "react-dom": "^19.2.7", - "tailwindcss": "^4.3.2", - "typescript": "^6.0.3", - "zod": "^4.4.3" + "scripts": { + "typecheck": "tsc -p packages/schema && tsc -p tools/migrate", + "test": "vitest run", + "check": "npm run typecheck && npm test", + "schema:json": "npm run json -w @gk/schema", + "templates": "npm run templates -w @gk/schema", + "migrate": "npm run migrate -w @gk/migrate --" + }, + "devDependencies": { + "@types/node": "^22.10.0", + "tsx": "^4.19.0", + "typescript": "^5.7.0", + "vitest": "^5.0.3" } } diff --git a/packages/schema/package.json b/packages/schema/package.json new file mode 100644 index 0000000..b292a9e --- /dev/null +++ b/packages/schema/package.json @@ -0,0 +1,18 @@ +{ + "name": "@gk/schema", + "version": "0.1.0", + "private": true, + "type": "module", + "description": "The one definition of every GregKilmer.com note type, shared by the migration, the content service, the MCP server, the site and any future editor.", + "exports": { + ".": "./src/index.ts" + }, + "scripts": { + "json": "tsx scripts/emit-json-schema.ts", + "templates": "tsx scripts/emit-templates.ts" + }, + "dependencies": { + "yaml": "^2.6.0", + "zod": "^4.1.0" + } +} diff --git a/packages/schema/scripts/emit-json-schema.ts b/packages/schema/scripts/emit-json-schema.ts new file mode 100644 index 0000000..7048a8d --- /dev/null +++ b/packages/schema/scripts/emit-json-schema.ts @@ -0,0 +1,24 @@ +/** + * Writes one JSON Schema per note type to json/, with every field's title and + * description. The MCP server and future editors can read these without + * importing TypeScript. + */ +import { mkdirSync, writeFileSync } from "node:fs"; +import { z } from "zod"; +import { noteTypes } from "../src/types.ts"; + +const out = new URL("../json/", import.meta.url); +mkdirSync(out, { recursive: true }); +for (const def of Object.values(noteTypes)) { + const schema = z.toJSONSchema(def.schema, { io: "input", unrepresentable: "any" }); + const doc = { + ...schema, + title: def.label, + description: def.description, + "x-folder": typeof def.folder === "string" ? def.folder : "depends on the note", + "x-body": def.body, + "x-singleton": def.singleton?.fileName ?? null, + }; + writeFileSync(new URL(`${def.type}.schema.json`, out), `${JSON.stringify(doc, null, 2)}\n`); +} +console.log(`Wrote ${Object.keys(noteTypes).length} schemas to json/`); diff --git a/packages/schema/scripts/emit-templates.ts b/packages/schema/scripts/emit-templates.ts new file mode 100644 index 0000000..9320a5d --- /dev/null +++ b/packages/schema/scripts/emit-templates.ts @@ -0,0 +1,9 @@ +/** Writes the Obsidian templates into vault-kit/. */ +import { mkdirSync, readdirSync, rmSync, writeFileSync } from "node:fs"; +import { allTemplates } from "../src/templates.ts"; + +const dir = new URL("../../../vault-kit/24.01_PersonalWebsite/Site/_templates/", import.meta.url); +mkdirSync(dir, { recursive: true }); +for (const f of readdirSync(dir)) rmSync(new URL(f, dir)); +for (const t of allTemplates()) writeFileSync(new URL(`${t.name}.md`, dir), t.text); +console.log(`Wrote ${allTemplates().length} templates`); diff --git a/packages/schema/src/fields.ts b/packages/schema/src/fields.ts new file mode 100644 index 0000000..e58f3ee --- /dev/null +++ b/packages/schema/src/fields.ts @@ -0,0 +1,97 @@ +/** + * Field builders shared by every note type. + * + * Each field carries `title` and `description` metadata. The JSON Schema + * export keeps them, so the MCP server, the admin page and any later editor + * can render a form or explain a field without a second definition. + */ +import { z } from "zod"; +import { normalizeWikilinkInput, wikilinkTarget } from "./names.ts"; + +type Meta = { title: string; description?: string }; + +/** A calendar date written `YYYY-MM-DD`, as Obsidian writes dates. */ +export const isoDate = (meta: Meta) => + z + .preprocess( + (v) => (v instanceof Date ? v.toISOString().slice(0, 10) : v), + z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "Use a date like 2026-10-11"), + ) + .meta(meta); + +/** A month written `YYYY-MM`, for resume dates. */ +export const isoMonth = (meta: Meta) => + z.string().regex(/^\d{4}-\d{2}$/, "Use a month like 2019-03").meta(meta); + +/** A link to another note, written `[[Note name]]`. */ +export const wikilink = (meta: Meta) => + z + .preprocess( + normalizeWikilinkInput, + z.string().refine((v) => wikilinkTarget(v) !== null, "Write a link as [[Note name]]"), + ) + .meta(meta); + +/** A list of links to other notes. */ +export const wikilinks = (meta: Meta) => + z + .preprocess( + (v) => (Array.isArray(v) ? v.map(normalizeWikilinkInput) : v), + z.array(z.string().refine((v) => wikilinkTarget(v) !== null, "Write each link as [[Note name]]")), + ) + .meta(meta); + +/** An image: a vault attachment as `[[file.jpg]]`, or a full https URL. */ +export const image = (meta: Meta) => + z + .preprocess( + normalizeWikilinkInput, + z + .string() + .refine( + (v) => wikilinkTarget(v) !== null || /^https:\/\//.test(v), + "Use a vault image as [[file.jpg]] or a full https URL", + ), + ) + .meta(meta); + +/** One short line of text. */ +export const line = (meta: Meta) => z.string().trim().min(1).meta(meta); + +/** A list of short tags or names. */ +export const strings = (meta: Meta) => z.array(z.string().trim().min(1)).meta(meta); + +/** + * Fields every note shares. `type` is added per note type. + * + * `publish` defaults to false: a note goes live only when it says so. + */ +export const baseFields = { + title: line({ title: "Title" }), + slug: z + .string() + .regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, "Lowercase words joined by hyphens") + .meta({ title: "Slug", description: "The last part of the page address. Made from the title when left out." }) + .optional(), + publish: z + .boolean() + .default(false) + .meta({ title: "Publish", description: "Only notes set to true appear on the site." }), + favorite: z.boolean().meta({ title: "Favorite", description: "Starred on the site." }).optional(), + tags: strings({ title: "Tags" }).optional(), + links: wikilinks({ + title: "Links", + description: "Other notes this one is about. The site shows the link on both notes.", + }).optional(), + cover: image({ title: "Cover image" }).optional(), + created: isoDate({ title: "Created" }).optional(), + updated: isoDate({ title: "Updated" }).optional(), + updatedBy: z + .enum(["greg", "agent", "admin"]) + .meta({ title: "Last edited by", description: "Set by the content service; you never need to write it." }) + .optional(), + agentEdits: z + .boolean() + .meta({ title: "Agents may edit", description: "Let agents change this note without your review." }) + .optional(), +}; diff --git a/packages/schema/src/index.ts b/packages/schema/src/index.ts new file mode 100644 index 0000000..be075b4 --- /dev/null +++ b/packages/schema/src/index.ts @@ -0,0 +1,6 @@ +export * from "./names.ts"; +export * from "./fields.ts"; +export * from "./types.ts"; +export * from "./notes.ts"; +export * from "./vault.ts"; +export * from "./templates.ts"; diff --git a/packages/schema/src/names.ts b/packages/schema/src/names.ts new file mode 100644 index 0000000..0b89eb4 --- /dev/null +++ b/packages/schema/src/names.ts @@ -0,0 +1,64 @@ +/** + * Names, slugs and wikilinks. + * + * Every note is addressed three ways: its file name in the vault (what + * Obsidian wikilinks resolve), its slug (what the URL uses), and its type. + * These helpers are the only place those rules live. + */ + +/** Characters Obsidian or common file systems refuse in a file name. */ +const UNSAFE_FILE_CHARS = /[\\/:*?"<>|#^[\]]/g; + +/** A file name (without `.md`) safe for Obsidian on every platform. */ +export function safeFileName(title: string): string { + return title + .replace(UNSAFE_FILE_CHARS, " ") + .replace(/\s+/g, " ") + .trim() + .replace(/[. ]+$/, ""); +} + +/** URL slug from a title: lowercase ASCII words joined by hyphens. */ +export function slugify(text: string): string { + return text + .normalize("NFKD") + .replace(/[̀-ͯ]/g, "") + .toLowerCase() + .replace(/&/g, " and ") + .replace(/['’]/g, "") + .replace(/[^a-z0-9]+/g, "-") + .replace(/^-+|-+$/g, ""); +} + +const WIKILINK = /^\[\[([^[\]|#]+)(?:#[^[\]|]*)?(?:\|[^[\]]*)?\]\]$/; +const WIKILINK_GLOBAL = /\[\[([^[\]|#]+)(?:#[^[\]|]*)?(?:\|[^[\]]*)?\]\]/g; + +/** `Name` → `[[Name]]`. */ +export function toWikilink(target: string): string { + return `[[${target}]]`; +} + +/** `[[Name|alias]]` → `Name`, or null when the value is not a wikilink. */ +export function wikilinkTarget(value: string): string | null { + const m = WIKILINK.exec(value.trim()); + return m ? m[1]!.trim() : null; +} + +/** Every wikilink target in a block of Markdown, in order, without repeats. */ +export function wikilinksIn(markdown: string): string[] { + const seen = new Set(); + for (const m of markdown.matchAll(WIKILINK_GLOBAL)) seen.add(m[1]!.trim()); + return [...seen]; +} + +/** + * An unquoted `[[Name]]` in YAML parses as a nested list (`[["Name"]]`). + * Obsidian quotes them, but hand edits may not, so accept both shapes. + */ +export function normalizeWikilinkInput(value: unknown): unknown { + if (Array.isArray(value) && value.length === 1 && Array.isArray(value[0]) && value[0].length === 1) { + const inner = value[0][0]; + if (typeof inner === "string") return toWikilink(inner); + } + return value; +} diff --git a/packages/schema/src/notes.ts b/packages/schema/src/notes.ts new file mode 100644 index 0000000..f35bcb7 --- /dev/null +++ b/packages/schema/src/notes.ts @@ -0,0 +1,112 @@ +/** + * Reading, checking and writing one note. + * + * A note is Markdown with YAML frontmatter. These functions never touch the + * file system, so the migration, the content service and tests share them. + */ +import { parse as parseYaml, stringify as stringifyYaml } from "yaml"; +import type { z } from "zod"; +import { baseFields } from "./fields.ts"; +import { safeFileName } from "./names.ts"; +import { noteTypes, type NoteType, type NoteTypeDef } from "./types.ts"; + +export interface RawNote { + /** Parsed frontmatter, unchecked. */ + frontmatter: Record; + /** The Markdown under the frontmatter. */ + body: string; +} + +export interface NoteProblem { + /** Dotted path to the field, or "" for the whole note. */ + field: string; + message: string; +} + +export type CheckedNote = + | { ok: true; type: NoteType; data: Record; body: string } + | { ok: false; type: NoteType | null; problems: NoteProblem[] }; + +const FRONTMATTER = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/; + +/** Split a note's text into frontmatter and body. Throws on broken YAML. */ +export function splitNote(text: string): RawNote { + const m = FRONTMATTER.exec(text); + if (!m) return { frontmatter: {}, body: text }; + const parsed = parseYaml(m[1]!) ?? {}; + if (typeof parsed !== "object" || Array.isArray(parsed)) { + throw new Error("Frontmatter must be a set of fields"); + } + return { frontmatter: parsed as Record, body: text.slice(m[0].length) }; +} + +export function typeDef(type: string): NoteTypeDef | undefined { + return (noteTypes as Record)[type]; +} + +/** Check a note's text against its type. Never throws. */ +export function checkNote(text: string): CheckedNote { + let raw: RawNote; + try { + raw = splitNote(text); + } catch (e) { + return { ok: false, type: null, problems: [{ field: "", message: `Frontmatter is not valid YAML: ${(e as Error).message}` }] }; + } + const type = raw.frontmatter.type; + if (typeof type !== "string") { + return { ok: false, type: null, problems: [{ field: "type", message: "Missing type" }] }; + } + const def = typeDef(type); + if (!def) { + return { ok: false, type: null, problems: [{ field: "type", message: `Unknown type "${type}"` }] }; + } + const result = (def.schema as z.ZodType).safeParse(raw.frontmatter); + if (!result.success) { + return { + ok: false, + type: type as NoteType, + problems: result.error.issues.map((i) => ({ field: i.path.join("."), message: i.message })), + }; + } + return { ok: true, type: type as NoteType, data: result.data as Record, body: raw.body }; +} + +/** + * Frontmatter key order: type, title, slug and publish, then the type's own + * fields in schema order, then the other shared fields, then anything else. + */ +function orderKeys(def: NoteTypeDef, data: Record): Record { + const shape = (def.schema as unknown as { shape: Record }).shape; + const first = ["type", "title", "slug", "publish"]; + const shared = Object.keys(baseFields).filter((k) => !first.includes(k)); + const own = Object.keys(shape).filter((k) => !first.includes(k) && !shared.includes(k)); + const keys = [...first, ...own, ...shared]; + const out: Record = {}; + for (const k of keys) if (data[k] !== undefined) out[k] = data[k]; + for (const k of Object.keys(data)) if (!(k in out) && data[k] !== undefined) out[k] = data[k]; + return out; +} + +/** Write a note's text. The data is checked first; this throws when it fails. */ +export function writeNote(data: Record, body = ""): string { + const def = typeDef(String(data.type)); + if (!def) throw new Error(`Unknown type "${String(data.type)}"`); + const result = (def.schema as z.ZodType).safeParse(data); + if (!result.success) { + const problems = result.error.issues.map((i) => `${i.path.join(".") || "note"}: ${i.message}`).join("; "); + throw new Error(`"${String(data.title)}" is not a valid ${def.label}: ${problems}`); + } + // Write what was given (not schema defaults), so notes stay short. + const yaml = stringifyYaml(orderKeys(def, data), { lineWidth: 0 }).trimEnd(); + const text = body.trim() ? `\n${body.trim()}\n` : ""; + return `---\n${yaml}\n---\n${text}`; +} + +/** Where a note of this type lives, relative to the Site folder, with `.md`. */ +export function notePath(data: Record): string { + const def = typeDef(String(data.type)); + if (!def) throw new Error(`Unknown type "${String(data.type)}"`); + const folder = typeof def.folder === "function" ? def.folder(data) : def.folder; + const name = def.singleton?.fileName ?? (def.fileName ? def.fileName(data) : safeFileName(String(data.title))); + return `${folder ? `${folder}/` : ""}${name}.md`; +} diff --git a/packages/schema/src/templates.ts b/packages/schema/src/templates.ts new file mode 100644 index 0000000..0b541dd --- /dev/null +++ b/packages/schema/src/templates.ts @@ -0,0 +1,60 @@ +/** + * Obsidian templates, one per note type, made from the schema so they can + * never drift from what the content service accepts. + */ +import { z } from "zod"; +import { noteTypes } from "./types.ts"; +import type { NoteTypeDef } from "./types.ts"; + +type Prop = { type?: string; enum?: unknown[]; const?: unknown; description?: string; title?: string; default?: unknown; items?: Prop }; + +/** Shared fields that stay out of templates: the service or the title fills them. */ +const SKIP = new Set(["slug", "updatedBy", "agentEdits", "updated", "cover", "links", "tags", "favorite"]); + +function placeholder(key: string, p: Prop): string { + if (key === "created" || key === "date") return '"{{date:YYYY-MM-DD}}"'; + if (p.const !== undefined) return JSON.stringify(p.const); + if (p.default !== undefined) return JSON.stringify(p.default); + if (p.type === "array") return "[]"; + if (p.type === "boolean") return "false"; + return ""; +} + +function hint(p: Prop): string { + const parts: string[] = []; + if (p.enum) parts.push(`one of: ${p.enum.join(", ")}`); + else if (p.items?.enum) parts.push(`any of: ${p.items.enum.join(", ")}`); + if (p.description) parts.push(p.description); + return parts.join(" "); +} + +export function templateFor(def: NoteTypeDef): string { + const schema = z.toJSONSchema(def.schema, { io: "input", unrepresentable: "any" }) as { + properties: Record; + required?: string[]; + }; + const required = new Set(schema.required ?? []); + const lines: string[] = []; + for (const [key, p] of Object.entries(schema.properties)) { + if (SKIP.has(key)) continue; + if (key === "title") { + lines.push(`title: "{{title}}"`); + continue; + } + // Optional object lists (specs, prices...) are easier to add when needed. + if (!required.has(key) && p.type === "array" && p.items?.type === "object") continue; + const value = placeholder(key, p); + const optional = !required.has(key) && key !== "publish" && key !== "type"; + const h = [optional ? "optional" : "", hint(p)].filter(Boolean).join(". "); + lines.push(`${key}: ${value}`.trimEnd() + (h ? ` # ${h}` : "")); + } + const body = def.body ? `\n\n` : ""; + return `---\n${lines.join("\n")}\n---\n${body}`; +} + +/** File name (without `.md`) and text for every template. */ +export function allTemplates(): { name: string; text: string }[] { + return Object.values(noteTypes) + .filter((d) => !d.singleton) + .map((d) => ({ name: d.label, text: templateFor(d as NoteTypeDef) })); +} diff --git a/packages/schema/src/types.ts b/packages/schema/src/types.ts new file mode 100644 index 0000000..bcf7b6b --- /dev/null +++ b/packages/schema/src/types.ts @@ -0,0 +1,508 @@ +/** + * Every note type the site understands. + * + * A note type is one Markdown note shape: its frontmatter fields, the folder + * it lives in under the Site folder, and how its file is named. This file is + * the single definition; the migration, the content service, the MCP tools, + * the admin page and the templates all read it. + */ +import { z } from "zod"; +import { baseFields, image, isoDate, isoMonth, line, strings, wikilink, wikilinks } from "./fields.ts"; +import { safeFileName } from "./names.ts"; + +export interface NoteTypeDef { + /** The value of the note's `type` field. */ + type: string; + /** What people call it. */ + label: string; + /** One sentence for editors and agents. */ + description: string; + /** Folder under the Site folder. A function when the folder depends on the note. */ + folder: string | ((data: any) => string); + /** A single note at a fixed path, such as site settings. */ + singleton?: { fileName: string }; + /** File name (without `.md`) for a new note of this type. Defaults to the title. */ + fileName?: (data: any) => string; + /** What the Markdown body under the frontmatter is for, or null when unused. */ + body: string | null; + schema: S; +} + +const note = (type: T, fields: F) => + z.looseObject({ type: z.literal(type), ...baseFields, ...fields }); + +const def = (d: NoteTypeDef) => d; + +// ---------------------------------------------------------------- Blog + +export const topics = ["Making", "Tabletop", "Library", "Work and tech", "Life"] as const; + +export const post = def({ + type: "post", + label: "Post", + description: "A blog post. Case studies are posts with postType: case study and show on the Work page too.", + folder: "Posts", + body: "The post itself.", + schema: note("post", { + date: isoDate({ title: "Publish date" }), + topic: z.enum(topics).meta({ title: "Topic" }), + postType: z + .enum(["article", "build log", "case study"]) + .default("article") + .meta({ title: "Post type" }), + summary: line({ title: "Summary", description: "One or two sentences for lists and link previews." }), + pinned: z.boolean().meta({ title: "Featured", description: "Takes the top slot on the Blog." }).optional(), + // Case study fields + employer: line({ title: "Employer", description: "Case studies only. Leave out for personal projects." }).optional(), + role: line({ title: "Role", description: "Case studies only." }).optional(), + period: line({ title: "Period", description: "Case studies only, like 2021 to now." }).optional(), + tech: strings({ title: "Tech" }).optional(), + facts: z + .array(z.object({ label: line({ title: "Label" }), value: line({ title: "Value" }) })) + .meta({ title: "Key facts", description: "Case studies only, like Users: 1,000+." }) + .optional(), + }).superRefine((v, ctx) => { + if (v.postType === "case study" && !v.role) { + ctx.addIssue({ code: "custom", path: ["role"], message: "A case study needs a role" }); + } + }), +}); + +// ---------------------------------------------------------------- Library + +export const media = [ + "book", + "light novel", + "webnovel", + "manga", + "manhwa", + "video game", + "anime", + "TV", + "movie", +] as const; + +const mediaFolder: Record<(typeof media)[number], string> = { + book: "Books", + "light novel": "Light novels", + webnovel: "Webnovels", + manga: "Manga", + manhwa: "Manhwa", + "video game": "Video games", + anime: "Anime", + TV: "TV", + movie: "Movies", +}; + +export const libraryStates = ["now", "up next", "played", "finished", "caught up", "dropped"] as const; + +export const libraryItem = def({ + type: "library-item", + label: "Library item", + description: "A book, series, show, film or video game, with its state and a short verdict.", + folder: (d: { medium: (typeof media)[number] }) => `Library/${mediaFolder[d.medium]}`, + body: "Optional longer thoughts.", + schema: note("library-item", { + medium: z.enum(media).meta({ title: "Medium" }), + state: z.enum(libraryStates).meta({ title: "State" }), + creator: line({ title: "Author or creator" }).optional(), + group: line({ + title: "Group", + description: "Where it sits in the list, like Cosmere, Marvel Cinematic Universe or Prestige Drama.", + }).optional(), + year: z.number().int().meta({ title: "Year" }).optional(), + platform: line({ title: "Platform", description: "Video games only, like Steam or PlayStation 5." }).optional(), + verdict: line({ title: "Verdict", description: "A short review. No score." }).optional(), + note: line({ title: "Note", description: "Context, like where you are or how you came to it." }).optional(), + progress: line({ title: "Progress", description: "Like 16/18 volumes. Synced from PersonalReader when linked." }).optional(), + anilistId: z.number().int().meta({ title: "AniList id", description: "Links the item to PersonalReader's progress." }).optional(), + }), +}); + +// ---------------------------------------------------------------- The Shelf + +export const boardGame = def({ + type: "board-game", + label: "Board game", + description: "A board game on The Shelf. Players, time and art sync from BoardGameGeek.", + folder: "Shelf", + body: "Optional longer thoughts.", + schema: note("board-game", { + bggId: z.number().int().meta({ title: "BoardGameGeek id" }), + blurb: line({ title: "Blurb" }), + howItPlays: strings({ title: "How it plays", description: "Like engine building, social deduction, co-op." }).optional(), + owned: z.boolean().default(true).meta({ title: "Owned" }), + wantToPlay: z.boolean().meta({ title: "Want to play" }).optional(), + lastPlayed: isoDate({ title: "Last played" }).optional(), + }), +}); + +// ---------------------------------------------------------------- Workshop + +export const craftStatuses = ["Practicing", "Learning", "Getting into", "Paused"] as const; + +export const craft = def({ + type: "craft", + label: "Craft", + description: "A craft in the Workshop, with its status, station and tools.", + folder: "Workshop/Crafts", + body: "What you make with it, for the craft page.", + schema: note("craft", { + status: z.enum(craftStatuses).meta({ title: "Status" }), + blurb: line({ title: "Blurb" }).optional(), + station: wikilink({ title: "Station" }).optional(), + tools: wikilinks({ title: "Tools and materials" }).optional(), + order: z.number().int().meta({ title: "Order" }).optional(), + }), +}); + +export const station = def({ + type: "station", + label: "Station", + description: "A place in the workspace where tools live.", + folder: "Workshop/Stations", + body: null, + schema: note("station", { + where: line({ title: "Where", description: "Like the work counter and filament wall." }), + goodFor: line({ title: "Good for" }).optional(), + order: z.number().int().meta({ title: "Order" }), + }), +}); + +export const tool = def({ + type: "tool", + label: "Tool or material", + description: "A tool, machine or material, placed at a station.", + folder: "Workshop/Tools", + body: null, + schema: note("tool", { + kind: z.enum(["printer", "cutter", "casting", "finishing", "material", "other"]).meta({ title: "Kind" }), + station: wikilink({ title: "Station" }).optional(), + blurb: line({ title: "Blurb" }).optional(), + }), +}); + +export const make = def({ + type: "make", + label: "Make", + description: "Something finished in the Workshop.", + folder: "Workshop/Makes", + body: "Optional story of the make.", + schema: note("make", { + craft: wikilink({ title: "Craft" }), + date: isoDate({ title: "Finished" }).optional(), + photos: z.array(image({ title: "Photo" })).meta({ title: "Photos" }).optional(), + blurb: line({ title: "Blurb" }).optional(), + }), +}); + +// ---------------------------------------------------------------- Tabletop + +export const tabletopSystem = def({ + type: "tabletop-system", + label: "Tabletop system", + description: "A tabletop RPG system you run, have played or want to try.", + folder: "Tabletop/Systems", + body: null, + schema: note("tabletop-system", { + state: z.enum(["running", "played", "want to run", "want to play", "want to try"]).meta({ title: "State" }), + blurb: line({ title: "Blurb" }), + order: z.number().int().meta({ title: "Order" }).optional(), + }), +}); + +export const campaign = def({ + type: "campaign", + label: "Campaign", + description: "A campaign or playtest at the table now or in the past.", + folder: "Tabletop/Campaigns", + body: "Optional campaign notes for the page.", + schema: note("campaign", { + system: wikilink({ title: "System", description: "Leave out for a playtest of an unreleased game." }).optional(), + status: z.enum(["active", "playtest", "paused", "finished"]).meta({ title: "Status" }), + schedule: line({ title: "Schedule", description: "Like Thursdays, every other week." }).optional(), + platform: line({ title: "Platform", description: "Like Foundry VTT." }).optional(), + blurb: line({ title: "Blurb" }).optional(), + }), +}); + +export const cardGame = def({ + type: "card-game", + label: "Card game", + description: "A trading card game you play or follow.", + folder: "Tabletop/Card games", + body: null, + schema: note("card-game", { + stance: line({ title: "Where you are with it", description: "Like Main event, Collecting, Getting into." }), + blurb: line({ title: "Blurb" }).optional(), + order: z.number().int().meta({ title: "Order" }).optional(), + }), +}); + +export const deck = def({ + type: "deck", + label: "Deck", + description: "A deck for any card game. Magic decks pull their details from Archidekt.", + folder: "Tabletop/Decks", + body: null, + schema: note("deck", { + game: wikilink({ title: "Card game" }), + format: line({ title: "Format", description: "Like Commander." }).optional(), + archidektId: z.number().int().meta({ title: "Archidekt deck id" }).optional(), + listUrl: z.url().meta({ title: "Deck list link", description: "For games not on Archidekt." }).optional(), + take: line({ title: "Your take", description: "One line on the deck." }).optional(), + }), +}); + +// ---------------------------------------------------------------- Resume + +export const experience = def({ + type: "experience", + label: "Experience", + description: "One role on the resume. The page and the PDF both come from these.", + folder: "Resume/Experience", + fileName: (d: { company: string; start: string }) => safeFileName(`${d.company} ${d.start.slice(0, 4)}`), + body: "Optional longer notes; the resume uses the bullets.", + schema: note("experience", { + company: line({ title: "Company" }), + role: line({ title: "Role" }), + start: isoMonth({ title: "Start" }), + end: isoMonth({ title: "End", description: "Leave out while it is your current role." }).optional(), + location: line({ title: "Location" }).optional(), + focus: line({ title: "Focus", description: "One line under the role." }).optional(), + bullets: strings({ title: "Bullets" }), + tech: strings({ title: "Tech" }).optional(), + }), +}); + +export const skills = def({ + type: "skills", + label: "Skills", + description: "The resume's skills sidebar.", + folder: "Resume", + singleton: { fileName: "Skills" }, + body: null, + schema: note("skills", { + core: strings({ title: "Core strengths" }), + learning: strings({ title: "Learning now" }).optional(), + stack: z + .array(z.object({ label: line({ title: "Group" }), items: strings({ title: "Items" }) })) + .meta({ title: "Technical stack" }), + }), +}); + +export const education = def({ + type: "education", + label: "Education", + description: "Degrees and certifications on the resume.", + folder: "Resume", + singleton: { fileName: "Education" }, + body: null, + schema: note("education", { + degrees: z + .array( + z.object({ + credential: line({ title: "Credential" }), + institution: line({ title: "Institution" }), + period: line({ title: "Period" }), + detail: line({ title: "Detail" }).optional(), + }), + ) + .meta({ title: "Degrees" }), + certifications: z + .array( + z.object({ + name: line({ title: "Name" }), + issuer: line({ title: "Issuer" }), + year: line({ title: "Year" }), + status: z.enum(["Active", "Expired", "In progress"]).meta({ title: "Status" }).optional(), + }), + ) + .meta({ title: "Certifications" }) + .optional(), + }), +}); + +// ---------------------------------------------------------------- Now + +export const status = def({ + type: "status", + label: "Status", + description: "One line in the status log: what you are reading, playing, making or learning.", + folder: "Now", + fileName: (d: { date: string; kind: string; title: string }) => safeFileName(`${d.date} ${d.kind} ${d.title}`), + body: null, + schema: note("status", { + kind: line({ title: "Kind", description: "One of the kinds listed in Now/_kinds." }), + date: isoDate({ title: "Date" }), + subject: wikilink({ title: "About", description: "The note this status is about, if there is one." }).optional(), + note: line({ title: "Note" }).optional(), + pinned: z.boolean().meta({ title: "Pinned" }).optional(), + }), +}); + +export const statusKinds = def({ + type: "status-kinds", + label: "Status kinds", + description: "The kinds a status can have. Adding a kind is one line here.", + folder: "Now", + singleton: { fileName: "_kinds" }, + body: null, + schema: note("status-kinds", { + kinds: z + .array( + z.object({ + label: line({ title: "Label" }), + icon: line({ title: "Icon" }), + page: line({ title: "Page", description: "The page this kind belongs to, like /library." }).optional(), + }), + ) + .meta({ title: "Kinds" }), + }), +}); + +// ---------------------------------------------------------------- Store + +export const productLine = def({ + type: "product-line", + label: "Product line", + description: "One line in the Store, made to order or in stock.", + folder: "Store/Lines", + body: "Optional longer description for the product page.", + schema: note("product-line", { + category: z.enum(["Tabletop", "Paper", "Leather"]).meta({ title: "Category" }), + mode: z.enum(["made to order", "in stock"]).meta({ title: "Mode" }), + channel: z.enum(["here", "Etsy", "Patiun.com", "coming soon"]).meta({ title: "Where to buy" }), + channelUrl: z.url().meta({ title: "Shop link", description: "For Etsy or Patiun.com lines." }).optional(), + summary: line({ title: "Summary" }), + icon: line({ title: "Icon" }).optional(), + order: z.number().int().meta({ title: "Order" }).optional(), + slots: z + .object({ open: z.number().int().min(0), total: z.number().int().min(1) }) + .meta({ title: "Order slots", description: "Made to order lines only." }) + .optional(), + quantity: z.number().int().min(0).meta({ title: "In stock", description: "In stock lines only." }).optional(), + images: z + .array(z.object({ file: image({ title: "Image" }), alt: line({ title: "Alt text" }), label: line({ title: "Caption" }) })) + .meta({ title: "Images" }) + .optional(), + specs: z.array(z.object({ label: line({ title: "Label" }), value: line({ title: "Value" }) })).meta({ title: "Specs" }).optional(), + priceTiers: z + .array(z.object({ band: line({ title: "Band" }), price: line({ title: "Price" }), note: line({ title: "Note" }).optional() })) + .meta({ title: "Prices" }), + options: z + .array( + z.object({ + group: line({ title: "Option group", description: "Like Filament, Leather or Thread." }), + name: line({ title: "Name" }), + hex: z.string().regex(/^#[0-9a-fA-F]{6}$/).meta({ title: "Color" }).optional(), + inStock: z.boolean().meta({ title: "In stock" }), + }), + ) + .meta({ title: "Options" }) + .optional(), + terms: z + .array( + z.object({ + kind: z.enum(["info", "warning", "rule"]).meta({ title: "Kind" }), + title: line({ title: "Title" }), + body: line({ title: "Text" }), + }), + ) + .meta({ title: "Order terms" }) + .optional(), + }).superRefine((v, ctx) => { + if (v.mode === "made to order" && v.channel === "here" && !v.slots) { + ctx.addIssue({ code: "custom", path: ["slots"], message: "A made to order line sold here needs slots" }); + } + }), +}); + +export const storeSettings = def({ + type: "store-settings", + label: "Store settings", + description: "Whether each category takes orders, and the terms for every order.", + folder: "Store", + singleton: { fileName: "_settings" }, + body: null, + schema: note("store-settings", { + categories: z + .array( + z.object({ + name: z.enum(["Tabletop", "Paper", "Leather"]).meta({ title: "Category" }), + ordersOpen: z.boolean().meta({ title: "Orders open" }), + reopens: isoDate({ title: "Reopens" }).optional(), + turnaround: line({ title: "Turnaround" }).optional(), + }), + ) + .meta({ title: "Categories" }), + capacityNote: line({ title: "Capacity note" }).optional(), + terms: productLine.schema.shape.terms, + }), +}); + +// ---------------------------------------------------------------- Site + +export const siteSettings = def({ + type: "site-settings", + label: "Site settings", + description: "Name, tagline, availability and links used across the site.", + folder: "", + singleton: { fileName: "_site" }, + body: null, + schema: note("site-settings", { + name: line({ title: "Name" }), + tagline: line({ title: "Tagline" }), + lockup: line({ title: "Lockup line" }), + location: line({ title: "Location" }).optional(), + availability: line({ title: "Availability", description: "Shown on the resume and Inquiries, like Open to freelance web work." }).optional(), + socials: z + .array(z.object({ label: line({ title: "Label" }), handle: line({ title: "Handle" }), url: z.url().meta({ title: "Link" }) })) + .meta({ title: "Social links" }), + footer: line({ title: "Footer line" }).optional(), + }), +}); + +export const page = def({ + type: "page", + label: "Page text", + description: "The written introduction for one page of the site.", + folder: "Pages", + body: "The page's introduction.", + schema: note("page", { + page: line({ title: "Page", description: "The page address this text belongs to, like /about." }), + lede: line({ title: "Lede", description: "The one line under the page title." }).optional(), + quickFacts: z + .array(z.object({ icon: line({ title: "Icon" }), text: line({ title: "Text" }) })) + .meta({ title: "Quick facts" }) + .optional(), + }), +}); + +/** Every note type, keyed by the `type` value. */ +export const noteTypes = { + post, + "library-item": libraryItem, + "board-game": boardGame, + craft, + station, + tool, + make, + "tabletop-system": tabletopSystem, + campaign, + "card-game": cardGame, + deck, + experience, + skills, + education, + status, + "status-kinds": statusKinds, + "product-line": productLine, + "store-settings": storeSettings, + "site-settings": siteSettings, + page, +} as const; + +export type NoteType = keyof typeof noteTypes; +export type NoteData = z.output<(typeof noteTypes)[T]["schema"]>; +export const noteTypeNames = Object.keys(noteTypes) as NoteType[]; diff --git a/packages/schema/src/vault.ts b/packages/schema/src/vault.ts new file mode 100644 index 0000000..3fea52d --- /dev/null +++ b/packages/schema/src/vault.ts @@ -0,0 +1,75 @@ +/** + * Checks that need every note at once: unique names, unique slugs and links + * that resolve. The content service runs these on each change; the migration + * runs them on its output. + */ +import { slugify, wikilinkTarget, wikilinksIn } from "./names.ts"; +import { typeDef } from "./notes.ts"; + +export interface VaultNote { + /** Path under the Site folder, like `Library/Books/Cosmere.md`. */ + path: string; + type: string; + data: Record; + body: string; +} + +export interface VaultProblem { + path: string; + message: string; +} + +/** The name Obsidian resolves a wikilink against: the file name without `.md`. */ +export const linkName = (path: string) => path.replace(/^.*\//, "").replace(/\.md$/, ""); + +/** Every wikilink a note makes, from frontmatter and body. */ +export function outgoingLinks(note: VaultNote): string[] { + const found = new Set(); + const visit = (v: unknown) => { + if (typeof v === "string") { + const t = wikilinkTarget(v); + if (t) found.add(t); + } else if (Array.isArray(v)) v.forEach(visit); + else if (v && typeof v === "object") Object.values(v).forEach(visit); + }; + visit(note.data); + for (const t of wikilinksIn(note.body)) found.add(t); + return [...found]; +} + +const IMAGE = /\.(jpe?g|png|gif|webp|svg|avif)$/i; + +/** + * Vault-wide problems. `attachments` lists attachment file names so image + * links can be checked too. + */ +export function checkVault(notes: VaultNote[], attachments: string[] = []): VaultProblem[] { + const problems: VaultProblem[] = []; + const byName = new Map(); + for (const n of notes) { + const name = linkName(n.path).toLowerCase(); + const other = byName.get(name); + if (other) problems.push({ path: n.path, message: `Same file name as ${other.path}, so links to it are ambiguous` }); + else byName.set(name, n); + } + + const slugs = new Map(); + for (const n of notes) { + const def = typeDef(n.type); + if (!def || def.singleton) continue; + const slug = String(n.data.slug ?? slugify(String(n.data.title))); + const key = `${n.type}:${slug}`; + const other = slugs.get(key); + if (other) problems.push({ path: n.path, message: `Slug "${slug}" is already used by ${other}` }); + else slugs.set(key, n.path); + } + + const files = new Set(attachments.map((a) => a.toLowerCase())); + for (const n of notes) { + for (const target of outgoingLinks(n)) { + const known = IMAGE.test(target) ? files.has(target.toLowerCase()) : byName.has(target.toLowerCase()); + if (!known) problems.push({ path: n.path, message: `Links to [[${target}]], which does not exist` }); + } + } + return problems; +} diff --git a/packages/schema/test/schema.test.ts b/packages/schema/test/schema.test.ts new file mode 100644 index 0000000..243d92e --- /dev/null +++ b/packages/schema/test/schema.test.ts @@ -0,0 +1,89 @@ +import { readFileSync, readdirSync } from "node:fs"; +import { describe, expect, it } from "vitest"; +import { allTemplates, checkNote, checkVault, notePath, slugify, wikilinkTarget, writeNote } from "../src/index.ts"; + +describe("names", () => { + it("slugs titles", () => { + expect(slugify("Pokémon TCG")).toBe("pokemon-tcg"); + expect(slugify("The Queen's Gambit")).toBe("the-queens-gambit"); + expect(slugify("Dungeons & Dragons")).toBe("dungeons-and-dragons"); + }); + it("reads wikilinks with aliases and headings", () => { + expect(wikilinkTarget("[[Galaxy dice|the galaxy set]]")).toBe("Galaxy dice"); + expect(wikilinkTarget("[[Resin#Tools]]")).toBe("Resin"); + expect(wikilinkTarget("Resin")).toBeNull(); + }); +}); + +describe("checkNote", () => { + const status = `---\ntype: status\ntitle: Throne of Glass\npublish: true\nkind: Reading\ndate: 2026-10-11\nsubject: [[Throne of Glass]]\n---\n`; + + it("accepts an unquoted wikilink, as a hand edit might write it", () => { + const r = checkNote(status); + expect(r.ok).toBe(true); + if (r.ok) expect(r.data.subject).toBe("[[Throne of Glass]]"); + }); + + it("names the field that is wrong", () => { + const r = checkNote(status.replace("date: 2026-10-11", "date: last tuesday")); + expect(r.ok).toBe(false); + if (!r.ok) expect(r.problems.map((p) => p.field)).toContain("date"); + }); + + it("rejects unknown types and broken YAML without throwing", () => { + expect(checkNote("---\ntype: recipe\ntitle: x\n---\n").ok).toBe(false); + expect(checkNote("---\ntitle: [unclosed\n---\n").ok).toBe(false); + }); + + it("defaults publish to false", () => { + const r = checkNote("---\ntype: card-game\ntitle: Riftbound\nstance: Exploring\n---\n"); + expect(r.ok && r.data.publish).toBe(false); + }); + + it("requires a role on case studies", () => { + const post = "---\ntype: post\ntitle: X\ndate: 2026-10-11\ntopic: Work and tech\npostType: case study\nsummary: Y\n---\n"; + expect(checkNote(post).ok).toBe(false); + expect(checkNote(post.replace("summary: Y", "summary: Y\nrole: Lead")).ok).toBe(true); + }); +}); + +describe("writeNote", () => { + it("round-trips through checkNote", () => { + const data = { type: "deck", title: "Kynaios and Tiro", publish: true, game: "[[Magic: The Gathering]]", format: "Commander" }; + const r = checkNote(writeNote(data)); + expect(r.ok && r.data).toMatchObject(data); + }); + it("refuses invalid data", () => { + expect(() => writeNote({ type: "deck", title: "No game" })).toThrow(/game/); + }); + it("places notes by type", () => { + expect(notePath({ type: "library-item", title: "Re:Zero", medium: "light novel" })).toBe("Library/Light novels/Re Zero.md"); + expect(notePath({ type: "site-settings", title: "Site settings" })).toBe("_site.md"); + }); +}); + +describe("checkVault", () => { + const n = (path: string, type: string, data: Record, body = "") => ({ path, type, data: { title: path, ...data }, body }); + it("finds duplicate names, duplicate slugs and dangling links", () => { + const problems = checkVault([ + n("Shelf/Coup.md", "board-game", { slug: "coup" }), + n("Library/Movies/Coup.md", "library-item", {}), + n("Shelf/Other.md", "board-game", { slug: "coup" }), + n("Posts/A.md", "post", { links: ["[[Missing]]"] }, "See [[Coup]] and ![[photo.jpg]]"), + ], ["photo.jpg"]); + const messages = problems.map((p) => p.message).join("\n"); + expect(messages).toMatch(/Same file name/); + expect(messages).toMatch(/Slug "coup"/); + expect(messages).toMatch(/\[\[Missing\]\]/); + expect(messages).not.toMatch(/photo\.jpg/); + }); +}); + +describe("templates", () => { + it("match the committed vault kit", () => { + const dir = new URL("../../../vault-kit/24.01_PersonalWebsite/Site/_templates/", import.meta.url); + const committed = Object.fromEntries(readdirSync(dir).map((f) => [f.replace(/\.md$/, ""), readFileSync(new URL(f, dir), "utf8")])); + for (const t of allTemplates()) expect(committed[t.name], `Run npm run templates -w @gk/schema`).toBe(t.text); + expect(Object.keys(committed).length).toBe(allTemplates().length); + }); +}); diff --git a/packages/schema/tsconfig.json b/packages/schema/tsconfig.json new file mode 100644 index 0000000..8a8f3fb --- /dev/null +++ b/packages/schema/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { "rootDir": "." }, + "include": ["src", "scripts", "test"] +} diff --git a/tools/migrate/package.json b/tools/migrate/package.json new file mode 100644 index 0000000..9fee301 --- /dev/null +++ b/tools/migrate/package.json @@ -0,0 +1,15 @@ +{ + "name": "@gk/migrate", + "version": "0.1.0", + "private": true, + "type": "module", + "description": "One-time migration of the legacy site's data into Site folder notes.", + "scripts": { + "migrate": "tsx src/cli.ts" + }, + "dependencies": { + "@gk/schema": "*", + "yaml": "^2.6.0", + "typescript": "^5.7.0" + } +} diff --git a/tools/migrate/src/build.ts b/tools/migrate/src/build.ts new file mode 100644 index 0000000..cf7d449 --- /dev/null +++ b/tools/migrate/src/build.ts @@ -0,0 +1,402 @@ +/** + * Turns the legacy site's data into Site folder notes. + * + * Pure apart from reading legacy/: it returns the notes and the attachments + * to copy, and the CLI writes them. Every note goes through the schema's + * writeNote, so nothing invalid can be produced. + */ +import { readFileSync, readdirSync } from "node:fs"; +import { basename, extname, join } from "node:path"; +import ts from "typescript"; +import { notePath, safeFileName, slugify, splitNote, toWikilink, writeNote } from "@gk/schema"; +import * as seed from "./seed.ts"; + +export interface BuiltNote { + path: string; + text: string; + data: Record; + body: string; +} + +export interface Attachment { + /** Absolute path of the legacy file. */ + from: string; + /** Path under the Site folder. */ + to: string; +} + +export interface BuildResult { + notes: BuiltNote[]; + attachments: Attachment[]; + /** Plain-language notes about choices the migration made, for the report. */ + log: string[]; +} + +const today = seed.MIGRATED_ON; + +export function build(legacyDir: string): BuildResult { + const notes: BuiltNote[] = []; + const attachments = new Map(); + const usedNames = new Set(); + const log: string[] = []; + const json = (p: string) => JSON.parse(readFileSync(join(legacyDir, p), "utf8")); + + /** Copy a legacy public image into _attachments// and return its wikilink. */ + const attach = (publicPath: string, area: string): string => { + const from = join(legacyDir, "public", publicPath); + const existing = attachments.get(from); + if (existing) return toWikilink(basename(existing.to)); + const ext = extname(publicPath).toLowerCase(); + let name = `${slugify(basename(publicPath, extname(publicPath)))}${ext}`; + for (let i = 2; usedNames.has(name); i++) name = `${slugify(basename(publicPath, extname(publicPath)))}-${i}${ext}`; + usedNames.add(name); + attachments.set(from, { from, to: `_attachments/${area}/${name}` }); + return toWikilink(name); + }; + + const noteNames = new Set(); + const add = (data: Record, body = "") => { + let path = notePath(data); + let name = basename(path, ".md").toLowerCase(); + if (noteNames.has(name)) { + // Same title as an earlier note: keep both linkable by naming the type. + const qualifier = String(data.medium ?? data.type); + const renamed = notePath({ ...data, title: `${String(data.title)} (${qualifier})` }); + const fixedPath = renamed.replace(/[^/]+\.md$/, `${safeFileName(`${String(data.title)} (${qualifier})`)}.md`); + log.push(`Two notes are called "${String(data.title)}", so one file is named "${basename(fixedPath, ".md")}".`); + path = fixedPath; + name = basename(path, ".md").toLowerCase(); + data = { ...data, slug: slugify(`${String(data.title)} ${qualifier}`) }; + } + noteNames.add(name); + notes.push({ path, text: writeNote(data, body), data, body }); + return basename(path, ".md"); + }; + const link = (title: string) => toWikilink(safeFileName(title)); + + // ------------------------------------------------------------ Site, pages, kinds + add({ type: "site-settings", publish: true, ...seed.site }); + add({ type: "status-kinds", title: "Status kinds", publish: true, kinds: seed.statusKinds }); + for (const p of seed.pages) { + const { body, source: _source, ...rest } = p; + add({ type: "page", created: today, ...rest }, body); + } + + // ------------------------------------------------------------ Library: reading + const readingMedium: Record = { + Books: "book", + "Light Novels": "light novel", + Webnovels: "webnovel", + Webtoons: "manhwa", + }; + // The legacy list grouped manga and manhwa together; these two are Japanese manga. + const mangaTitles = new Set(["Tokyo Revengers", "Wistoria: Wand and Sword"]); + const readingState: Record = { + Complete: { state: "finished" }, + Reading: { state: "now", kind: "Reading" }, + Listening: { state: "now", note: "Listening to the audiobooks.", kind: "Listening" }, + Rereading: { state: "now", note: "Rereading.", kind: "Reading" }, + "Reread-pending": { state: "up next", note: "Planning a reread." }, + Next: { state: "up next" }, + New: { state: "up next" }, + }; + const nowItems: { title: string; kind: string; note?: string }[] = []; + for (const r of json("src/data/reading.json") as any[]) { + let title: string = r.title; + let creator: string | undefined; + const dash = /^(.+?) — (.+)$/.exec(title); + const paren = /^(.+?) \(([^)]+)\)$/.exec(title); + if (dash) [creator, title] = [dash[1]!, dash[2]!]; + else if (paren) [title, creator] = [paren[1]!, paren[2]!]; + const medium = mangaTitles.has(title) ? "manga" : r.category === "Manga & Manhwa" ? "manhwa" : readingMedium[r.category]; + if (!medium) throw new Error(`Unknown reading category ${r.category}`); + const s = readingState[r.status]; + if (!s) throw new Error(`Unknown reading status ${r.status}`); + const note = [s.note, r.note].filter(Boolean).join(" ") || undefined; + const name = add({ + type: "library-item", + title, + publish: true, + favorite: r.favorite || undefined, + medium, + state: s.state, + creator, + verdict: r.blurb, + note, + cover: r.image ? attach(r.image, "Library") : undefined, + created: today, + }); + if (s.kind) nowItems.push({ title: name, kind: s.kind }); + } + log.push("Reading: \"Author — Series\" titles were split into a title and a creator."); + + // ------------------------------------------------------------ Library: screen + const merged = (file: string) => { + // The legacy files list a favorite twice: once with its blurb and once in its group. + const byTitle = new Map(); + for (const item of json(file) as any[]) { + const prev = byTitle.get(item.title); + if (prev) log.push(`${item.title}: two legacy entries were merged into one note.`); + byTitle.set(item.title, { ...prev, ...Object.fromEntries(Object.entries(item).filter(([, v]) => v != null)) }); + } + return [...byTitle.values()]; + }; + const screenState: Record = { Completed: "finished", "Caught up": "caught up", Watching: "now" }; + for (const [file, medium] of [ + ["src/data/media/anime.json", "anime"], + ["src/data/media/tv.json", "TV"], + ["src/data/media/movies.json", "movie"], + ] as const) { + for (const m of merged(file)) { + const state = m.status ? screenState[m.status] : "finished"; + if (!state) throw new Error(`Unknown status ${m.status}`); + const name = add({ + type: "library-item", + title: m.title, + publish: true, + favorite: m.favorite || undefined, + medium, + state, + group: m.category, + year: m.year, + verdict: m.blurb, + note: m.note, + cover: m.image ? attach(m.image, "Library") : undefined, + created: today, + }); + if (state === "now") nowItems.push({ title: name, kind: "Watching", note: m.note }); + } + } + log.push("Movies and shows with no status in the legacy lists were marked finished, since the lists were what Greg had watched."); + + // ------------------------------------------------------------ Library: video games + const hallDir = join(legacyDir, "src/content/games/hall-of-fame"); + for (const file of readdirSync(hallDir).sort()) { + const { frontmatter: g } = splitNote(readFileSync(join(hallDir, file), "utf8")) as { frontmatter: any }; + add({ + type: "library-item", + title: g.title, + publish: true, + favorite: true, + medium: "video game", + state: "played", + platform: g.platform, + year: g.year, + verdict: String(g.blurb).trim(), + cover: g.art ? attach(g.art, "Library") : undefined, + created: today, + }); + } + const nowPlaying = splitNote(`---\n${readFileSync(join(legacyDir, "src/content/games/now-playing.yaml"), "utf8")}\n---\n`) + .frontmatter as { games: { title: string; platform: string }[] }; + for (const g of nowPlaying.games) { + const name = add({ type: "library-item", title: g.title, publish: true, medium: "video game", state: "now", platform: g.platform, created: today }); + nowItems.push({ title: name, kind: "Playing" }); + } + log.push("The Gaming Hall of Fame became favorite video games with the state played."); + + // ------------------------------------------------------------ The Shelf + for (const b of json("src/data/boardgames.json") as any[]) { + if (/^hhttps:/.test(b.image)) { + b.image = b.image.slice(1); + log.push(`${b.name}: fixed a typo in its cover link (hhttps).`); + } + add({ + type: "board-game", + title: b.name, + publish: true, + favorite: seed.favoriteBoardGames.includes(b.name) || undefined, + bggId: b.bggId, + blurb: b.note, + owned: true, + cover: b.image, + created: today, + }); + } + + // ------------------------------------------------------------ Workshop + for (const s of seed.stations) add({ type: "station", publish: true, created: today, ...s }); + for (const t of seed.tools) add({ type: "tool", publish: true, created: today, ...t, station: link(t.station) }); + seed.crafts.forEach((c, i) => + add({ + type: "craft", + publish: true, + created: today, + title: c.title, + status: c.status, + blurb: "blurb" in c ? c.blurb : undefined, + station: "station" in c ? link(c.station) : undefined, + tools: "tools" in c ? c.tools.map(link) : undefined, + order: i + 1, + }), + ); + for (const m of seed.makes) { + add({ + type: "make", + title: m.title, + publish: m.publish ?? true, + created: today, + craft: link(m.craft), + photos: m.photo ? [attach(m.photo, "Workshop")] : undefined, + cover: m.photo ? attach(m.photo, "Workshop") : undefined, + blurb: m.blurb, + tags: m.tags, + links: m.links?.map(link), + }); + } + + // ------------------------------------------------------------ Tabletop + seed.systems.forEach((s, i) => add({ type: "tabletop-system", publish: true, created: today, ...s, order: i + 1 })); + for (const c of seed.campaigns) { + const { body, system, ...rest } = c as typeof c & { body?: string; system?: string }; + add({ type: "campaign", created: today, ...rest, system: system ? link(system) : undefined }, body); + } + seed.cardGames.forEach((g, i) => add({ type: "card-game", publish: true, created: today, ...g, order: i + 1 })); + for (const d of seed.decks) add({ type: "deck", publish: true, created: today, ...d, game: link(d.game) }); + + // ------------------------------------------------------------ Resume + const expDir = join(legacyDir, "src/content/experience"); + const months: Record = { Jan: "01", Feb: "02", Mar: "03", Apr: "04", May: "05", Jun: "06", Jul: "07", Aug: "08", Sep: "09", Oct: "10", Nov: "11", Dec: "12" }; + const month = (s: string) => { + const m = /^([A-Z][a-z]{2}) (\d{4})$/.exec(s.trim()); + return m ? `${m[2]}-${months[m[1]!]}` : undefined; + }; + for (const file of readdirSync(expDir).sort()) { + const { frontmatter: e, body } = splitNote(readFileSync(join(expDir, file), "utf8")) as { frontmatter: any; body: string }; + const [start, end] = String(e.period).split(" - "); + const bullets = body.split("\n").filter((l) => l.startsWith("- ")).map((l) => l.slice(2).trim()); + const extra = body.split("\n").filter((l) => l.trim() && !l.startsWith("- ") && !/^Key contributions:?$/.test(l.trim())).join("\n"); + add( + { + type: "experience", + title: `${e.title} at ${e.company}`, + publish: true, + company: e.company, + role: e.title, + start: month(start!), + end: end && end.trim() !== "Present" ? month(end) : undefined, + focus: e.focus, + bullets, + created: today, + }, + extra, + ); + } + add({ type: "skills", title: "Skills", publish: true, ...seed.skills }); + add({ type: "education", title: "Education", publish: true, ...seed.education }); + + // Projects become case study posts. Their bodies were never written, so they + // stay unpublished until the professional pass writes them. + const projDir = join(legacyDir, "src/content/projects"); + for (const file of readdirSync(projDir).sort()) { + const { frontmatter: p } = splitNote(readFileSync(join(projDir, file), "utf8")) as { frontmatter: any }; + const key = basename(file, ".md"); + const extra = seed.caseStudies[key] ?? {}; + const summary = String(p.summary).trim(); + const firstSentence = /^.+?[.!?](\s|$)/.exec(summary)?.[0].trim() ?? summary; + add( + { + type: "post", + title: p.title, + publish: false, + date: today, + topic: "Work and tech", + postType: "case study", + summary: firstSentence, + role: p.role, + tech: p.tech, + employer: extra.employer, + facts: extra.facts, + created: today, + }, + `${summary}\n\n`, + ); + } + log.push("The four legacy projects became case study posts, unpublished until each one is written up."); + + // ------------------------------------------------------------ Store + const store = json("src/data/store-availability.json"); + const products = readProducts(legacyDir); + const tabletopTerms = products.requirements.map((r: any) => ({ + kind: r.icon === "information" ? "info" : r.icon === "alert" ? "warning" : "rule", + title: r.title, + body: r.body, + })); + const general = new Set(["All sales are final", "Limited capacity"]); + products.items.forEach((p: any, i: number) => { + add( + { + type: "product-line", + title: p.title, + publish: true, + category: "Tabletop", + mode: "made to order", + channel: "here", + summary: p.summary, + icon: p.icon, + order: i + 1, + slots: store.slots[p.slotKey], + images: p.images.map((im: any) => ({ file: attach(im.src, "Store"), alt: im.alt, label: im.label })), + specs: p.specs, + priceTiers: p.pricing, + options: p.colors?.map((c: any) => ({ group: "Filament", name: c.name, hex: c.hex, inStock: c.inStock })), + // The legacy terms were written for the proxy cards. + terms: p.slotKey === "cards" ? tabletopTerms.filter((t: any) => !general.has(t.title)) : undefined, + created: today, + }, + ); + }); + seed.futureLines.forEach((l, i) => + add({ + type: "product-line", + title: l.title, + publish: false, + category: l.category, + mode: "made to order", + channel: "coming soon", + summary: "Coming soon.", + order: products.items.length + i + 1, + priceTiers: [], + created: today, + }), + ); + add({ + type: "store-settings", + title: "Store settings", + publish: true, + categories: [ + { name: "Tabletop", ordersOpen: store.ordersOpen, turnaround: store.turnaround }, + { name: "Paper", ordersOpen: false }, + { name: "Leather", ordersOpen: false }, + ], + capacityNote: store.maxNote, + terms: tabletopTerms.filter((t: any) => general.has(t.title)), + }); + + // ------------------------------------------------------------ Status log + for (const item of nowItems) { + add({ type: "status", title: item.title, publish: true, kind: item.kind, date: today, subject: toWikilink(item.title), note: item.note }); + } + for (const system of ["Dungeons & Dragons", "Daggerheart"]) { + add({ type: "status", title: system, publish: true, kind: "Running", date: today, subject: link(system) }); + } + log.push(`Every status is dated ${today}, the day of the migration, because the legacy site did not record when each one started.`); + + return { notes, attachments: [...attachments.values()], log }; +} + +/** The legacy store data is a TypeScript module; loadProducts reads it. */ +function readProducts(legacyDir: string): { items: any[]; requirements: any[] } { + return productsCache.get(legacyDir) ?? (() => { + throw new Error("Call loadProducts(legacyDir) before build()"); + })(); +} +const productsCache = new Map(); +export async function loadProducts(legacyDir: string) { + // Transpile on its own, so nothing from the legacy project's config or + // dependencies is loaded. The module only declares data. + const source = readFileSync(join(legacyDir, "src/data/store-products.ts"), "utf8"); + const js = ts.transpileModule(source, { compilerOptions: { module: ts.ModuleKind.ESNext, target: ts.ScriptTarget.ES2022 } }).outputText; + const mod = await import(`data:text/javascript;base64,${Buffer.from(js).toString("base64")}`); + productsCache.set(legacyDir, { items: mod.products, requirements: mod.requirements }); +} diff --git a/tools/migrate/src/cli.ts b/tools/migrate/src/cli.ts new file mode 100644 index 0000000..f98b6d8 --- /dev/null +++ b/tools/migrate/src/cli.ts @@ -0,0 +1,73 @@ +/** + * npm run migrate -- [outDir] + * + * Writes the migrated Site folder to out/24.01_PersonalWebsite/Site (or the + * given folder), with the vault kit's templates, then checks every note and + * the vault as a whole and writes MIGRATION-REPORT.md beside it. + */ +import { cpSync, existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs"; +import { basename, dirname, join, resolve } from "node:path"; +import { checkNote, checkVault } from "@gk/schema"; +import { build, loadProducts } from "./build.ts"; + +const root = resolve(import.meta.dirname, "../../.."); +const legacy = join(root, "legacy"); +const outRoot = resolve(process.argv[2] ?? join(root, "out")); +const area = join(outRoot, "24.01_PersonalWebsite"); +const site = join(area, "Site"); + +await loadProducts(legacy); +const result = build(legacy); + +rmSync(area, { recursive: true, force: true }); +cpSync(join(root, "vault-kit/24.01_PersonalWebsite"), area, { recursive: true }); +for (const n of result.notes) { + mkdirSync(dirname(join(site, n.path)), { recursive: true }); + writeFileSync(join(site, n.path), n.text); +} +for (const a of result.attachments) { + if (!existsSync(a.from)) throw new Error(`Missing legacy image ${a.from}`); + mkdirSync(dirname(join(site, a.to)), { recursive: true }); + cpSync(a.from, join(site, a.to)); +} + +// Check what was written, the way the content service will read it. +const problems: string[] = []; +const vaultNotes = result.notes.map((n) => { + const checked = checkNote(n.text); + if (!checked.ok) problems.push(`${n.path}: ${checked.problems.map((p) => `${p.field} ${p.message}`).join("; ")}`); + return { path: n.path, type: String(n.data.type), data: checked.ok ? checked.data : n.data, body: n.body }; +}); +for (const p of checkVault(vaultNotes, result.attachments.map((a) => basename(a.to)))) problems.push(`${p.path}: ${p.message}`); + +const counts = new Map(); +for (const n of result.notes) { + const c = counts.get(String(n.data.type)) ?? { all: 0, published: 0 }; + c.all++; + if (n.data.publish) c.published++; + counts.set(String(n.data.type), c); +} +const unpublished = result.notes.filter((n) => !n.data.publish).map((n) => `- ${n.path}`); +const report = `# Migration report + +${result.notes.length} notes and ${result.attachments.length} images, checked against the schema: ${problems.length ? `**${problems.length} problems**` : "no problems"}. + +| Type | Notes | Published | +| --- | --- | --- | +${[...counts].map(([t, c]) => `| ${t} | ${c.all} | ${c.published} |`).join("\n")} + +## Problems + +${problems.length ? problems.map((p) => `- ${p}`).join("\n") : "None."} + +## Choices the migration made + +${result.log.map((l) => `- ${l}`).join("\n")} + +## Unpublished, waiting on you + +${unpublished.join("\n")} +`; +writeFileSync(join(outRoot, "MIGRATION-REPORT.md"), report); +console.log(report.split("\n").slice(0, 3).join("\n")); +if (problems.length) process.exit(1); diff --git a/tools/migrate/src/seed.ts b/tools/migrate/src/seed.ts new file mode 100644 index 0000000..334da5a --- /dev/null +++ b/tools/migrate/src/seed.ts @@ -0,0 +1,278 @@ +/** + * Content that lived inside the legacy site's page code rather than in data + * files, extracted by hand once. Every entry names where it came from: + * a legacy file, or Greg's own answers during the 2026-10-11 redesign. Nothing + * here is invented; anything not known yet is left out or kept unpublished. + */ + +export const MIGRATED_ON = "2026-10-11"; + +// ---------------------------------------------------------------- Site + +/** legacy/src/lib/site.ts, legacy/src/pages/about.astro, legacy/src/lib/archidekt.ts */ +export const site = { + title: "Site settings", + name: "Greg Kilmer", + tagline: "Developer. Maker. Storyteller.", + lockup: "Web Development · a Patiun LLC company", + location: "Philadelphia, PA", + socials: [ + { label: "GitHub", handle: "Patiun", url: "https://github.com/patiun" }, + { label: "LinkedIn", handle: "Gregory Kilmer", url: "https://www.linkedin.com/in/gregory-kilmer-36646bb5/" }, + { label: "Archidekt", handle: "Patiun", url: "https://archidekt.com/search/decks?ownerUsername=Patiun" }, + ], + footer: "Built with intention. Powered by caffeine.", +}; + +/** The status log's kinds. Icons are mdi names until the brand icon set lands. */ +export const statusKinds = [ + { label: "Reading", icon: "book-open-variant", page: "/library" }, + { label: "Listening", icon: "headphones", page: "/library" }, + { label: "Watching", icon: "television-classic", page: "/library" }, + { label: "Playing", icon: "gamepad-variant", page: "/library" }, + { label: "Running", icon: "dice-multiple", page: "/tabletop" }, + { label: "Making", icon: "hammer-wrench", page: "/workshop" }, + { label: "Learning", icon: "school", page: "/workshop" }, +]; + +// ---------------------------------------------------------------- Pages + +/** Written introductions, verbatim from the legacy pages. */ +export const pages = [ + { + title: "Landing", + page: "/", + publish: true, + body: "I build things with code during the day and with resin, plastic, and imagination at night. I lead cloud and mobile development at Implico Inc., run D&D campaigns that span years, and fill my shelves with Brandon Sanderson novels, custom dice, and board games. This is the place where all of those worlds meet.", + source: "legacy/src/pages/index.astro", + }, + { + title: "About me", + page: "/about", + publish: true, + quickFacts: [ + { icon: "map-marker", text: "Philadelphia, PA" }, + { icon: "briefcase", text: "BTP, Cloud, and Mobile App Lead at Implico Inc." }, + { icon: "dog", text: "Dog dad to Kashi the mini dachshund" }, + { icon: "dice-multiple", text: "Running D&D & Daggerheart campaigns" }, + { icon: "book-open-variant", text: "Currently tandem reading: Heir of Fire and Tower of Dawn by Sarah J. Maas" }, + { icon: "tea", text: "Fueled by loose-leaf Chinese tea and Mountain Dew" }, + ], + body: `I am Greg: a software developer and technical team lead based in Philadelphia who spends his free time running tabletop RPGs, making resin dice and custom cards, 3D printing anything I can, and reading his way through fantasy series at an alarming rate. I live with my partner Krystina, our mini dachshund Kashi, and more board games than any reasonable apartment should hold. + +## The longer arc + +I grew up loving books and games, and not much has changed. Technology pulled me into a career building cloud and mobile applications, but I never stopped collecting dice, designing campaigns, or losing weekends to Sanderson reads. I am currently expanding into SAP's Business Technology Platform, exploring AI and machine learning, and trying to convince my group that yes, we can fit one more board game into game night. + +I believe the best work happens when curiosity isn't compartmentalized. The same system-thinking that makes a good developer makes a good gamemaster. The patience that produces a clean resin cast is the same patience that debugs a stubborn deployment. This site is where I don't have to pretend those things are separate.`, + source: "legacy/src/pages/about.astro", + }, + { + title: "Workshop", + page: "/workshop", + publish: true, + lede: "A magnet for super glue and nice cardboard.", + body: `Making is part of me. Ever since I can remember, I had been dreaming of making inventions and things from cardboard and clay. I took things apart to see how they worked, and rarely got them back together. But now I have the tools and the skills to take ideas from my imagination and physically produce them. I cast resin dice using pressure pots and custom molds, print miniatures and props on my Elegoo Mars 4 (resin) and Bambu Labs P1S (FDM), and generally fill my workspace with projects-in-progress. I make beautiful cards as gifts and even make proxy mockups of trading cards to enjoy games casually. + +There's something deeply satisfying about taking raw material through a process and holding the finished piece, whether that's a perfect polyhedral set or a hook that fits just right in that weird spot in the bathroom. The ability to blend my technical skills and my creative impulses into tangible objects is a fantasy come true my child self only dreamed of.`, + source: "legacy/src/pages/interests/making.astro", + }, + { + title: "Tabletop", + page: "/tabletop", + publish: true, + lede: "Long-running D&D and Daggerheart campaigns on Foundry VTT, the MCDM Crows playtest, and a growing board game collection.", + body: `I love playing tabletop games from trading card games to role-playing games. I run Dungeons & Dragons and Daggerheart campaigns that span years of real-time play, maintaining worlds, NPCs, and narrative threads that my players remember years later. I host everything on Foundry VTT, which lets me build the kind of dynamic, visually rich experiences that keep players invested session after session and lets me flex my technical skills. + +I also love board games, from strategy to social deduction. My collection is growing, and I enjoy hosting game nights with friends. I have a particular fondness for games that challenge players to think critically and creatively, and I appreciate the social aspect of gaming as much as the gameplay itself. Although, I do love the ability to play some of the engine building and deck-building games solo. Spirit Island and Wyrmspan have been my favorites lately, and I am always on the lookout for new games to add to my collection. + +Trading card games are where my collecting, deckbuilding, and making instincts all collide. I primarily play Magic: The Gathering, mostly Commander (Elder Dragon Highlander), where a hundred cards becomes a personal expression and the brewing never really ends. But I keep a hand in the Pokémon TCG, and I'm watching Riftbound and the new Gundam card game closely. If it has nice cardboard, pretty art, and a decision tree, I'm interested. + +The hobby overlaps the rest of the fun wing: the same workshop that produces resin dice and 3D prints also turns out proxies, tokens, and deckboxes and more. And the deckbuilding itch is a close relation of the RPG character-builder in me. Optimizing a list tells a story about how I want to play and the theme I build the deck around.`, + source: "legacy/src/pages/interests/ttrpgs.astro, legacy/src/pages/interests/tcgs.astro", + }, + { + title: "Video games", + page: "/library", + publish: false, + tags: ["legacy text"], + body: `Gaming has always been a passion in my life. I span console generations from the Sega Genesis to the modern era. I play mainly with a PlayStation 5, Nintendo Switch 2, and a Steam library that has gotten politely out of hand. I'm particularly drawn to RPGs, strategy games, and anything with a creature to tame or a deeply layered world to explore. But a good game loop will get me sucked in harder than the sunk cost fallacy of a League of Legends streak. + +I love to play with friends, but have found myself leaning more towards single-player experiences. Someone should have told younger me that scheduling would be the final boss! + +## Platforms + +- **PlayStation 5.** Mainly for single player and exclusives. +- **Nintendo Switch / Switch 2.** Handheld and docked. Pokemon, Mario, and many other Nintendo and indie gems. +- **Steam.** The primary game library. Strategy, RPGs, Adventure, oh my! And an ever-growing backlog. + +## Favorite genres + +- **RPGs (Western & JRPG).** Character progression and stories I can live in for 100+ hours and then turn around and do it again. +- **Creature Collecting.** If I can catch, train, or befriend a roster, I'm sold. +- **Strategy & Tactics.** Turn-based puzzles and tactical challenges where the right plan is oh so rewarding. +- **Building & Crafting.** Give me a grid, resources, and a goal, and watch the evening vanish. +- **Automation.** Optimizing a production line until its perfect is my zen. +- **Open-world Exploration.** The map IS the point. I will try to climb every mountain and I will check every waterfall for treasure. +- **Indie Darlings.** The small, weird, passionate projects that punch far above their size.`, + source: "legacy/src/pages/interests/gaming.astro", + }, + { + title: "Pokémon", + page: "/library", + publish: false, + tags: ["legacy text"], + body: `Pokémon has been a lifelong obsession. From Red and Blue on the original Game Boy to modern competitive formats, I've followed the franchise through every generation. My favorite? Metagross, the iron leg Pokémon. Steel/Psychic, beautiful design, absolutely devastating in battle. No further questions.`, + source: "legacy/src/pages/interests/pokemon.astro", + }, +]; + +// ---------------------------------------------------------------- Workshop + +/** Greg's workspace description, 2026-10-11 redesign interview. */ +export const stations = [ + { title: "Print station", where: "Work counter and filament wall", goodFor: "Deck boxes, terrain, props", order: 1 }, + { title: "Print and cut desk", where: "The smaller desk", goodFor: "Proxy cards, stickers, decals", order: 2 }, + { title: "Main desk", where: "Monitors plus a large cutting mat", goodFor: "Card finishing, leatherwork soon", order: 3 }, + { title: "Fume hood", where: "Custom built for resin", goodFor: "Minis, dice, rings", order: 4 }, +]; + +/** Blurbs from legacy/src/pages/interests/making.astro; placement from the interview. */ +export const tools = [ + { title: "Bambu Labs P1S", kind: "printer", station: "Print station", blurb: "FDM 3D printer. Functional prints, terrain pieces, and larger prototypes." }, + { title: "Filament wall", kind: "material", station: "Print station" }, + { title: "Epson 8500", kind: "printer", station: "Print and cut desk" }, + { title: "Silhouette Cameo 5a", kind: "cutter", station: "Print and cut desk", blurb: "Multi material cutting machine. Silly stickers, custom vinyl decals, and custom/proxy TCG cards." }, + { title: "Cutting mat", kind: "finishing", station: "Main desk" }, + { title: "Elegoo Mars 4", kind: "printer", station: "Fume hood", blurb: "Resin 3D printer. Miniatures, terrain, and high-detail props." }, + { title: "Pressure pot", kind: "casting", station: "Fume hood" }, + { title: "Dice molds", kind: "casting", station: "Fume hood" }, +] as const; + +/** Crafts and statuses from the interview; the resin blurb from making.astro. */ +export const crafts = [ + { title: "Resin", status: "Practicing", station: "Fume hood", tools: ["Pressure pot", "Dice molds"], blurb: "Custom molds, pressure pot curing, pigment experimentation. Producing dice sets for personal use and gifts." }, + { title: "3D printing", status: "Practicing", station: "Print station", tools: ["Bambu Labs P1S", "Elegoo Mars 4", "Filament wall"] }, + { title: "Cards and decals", status: "Practicing", station: "Print and cut desk", tools: ["Silhouette Cameo 5a", "Epson 8500"] }, + { title: "Paper and bookbinding", status: "Learning" }, + { title: "Leather", status: "Getting into", station: "Main desk", tools: ["Cutting mat"] }, + { title: "Props and gifts", status: "Practicing" }, +] as const; + +/** The legacy gallery (making.astro) with its captions, plus the sword rack from the interview. */ +export const makes = [ + { title: "Teal and gold leaf dice", craft: "Resin", photo: "/images/making-resin.jpg", blurb: "Resin dice set, teal with gold leaf inclusions." }, + { title: "Golden moss dice", craft: "Resin", photo: "/images/dice_golden_moss.jpg", blurb: "Resin dice set, a moss-like pattern with gold lettering." }, + { title: "Scarlet dice", craft: "Resin", photo: "/images/dice_scarlet.jpg", blurb: "Resin dice set, scarlet with gold lettering." }, + { title: "Galaxy dice", craft: "Resin", photo: "/images/dice_galaxy.jpg", blurb: "Resin dice set, a dark galaxy pattern with gold powder swirls." }, + { title: "Seastone dice", craft: "Resin", photo: "/images/dice_seastone.jpg", publish: false }, + { title: "Resin and painted rings", craft: "Resin", photo: "/images/resin_rings.jpg" }, + { title: "Miniature", craft: "3D printing", photo: "/images/minis.jpg", blurb: "3D printed miniature." }, + { title: "Deck boxes", craft: "3D printing", photo: "/images/deckboxes.jpg", blurb: "Design by @Iceenchanted on MakerWorld." }, + { title: "Kynaios and Tiro proxies", craft: "Cards and decals", photo: "/images/proxies.jpg", blurb: "Proxy cards for my Kynaios and Tiro deck.", links: ["Kynaios and Tiro"] }, + { title: "Foil proxies", craft: "Cards and decals", photo: "/images/proxies2.jpg", blurb: "Successful foil proxies for Magic." }, + { title: "Christmas gifts for family", craft: "Props and gifts", photo: "/images/xmasgifts.jpg", blurb: "CS:GO cases, Nightblood, a Runescape helmet and ACOTAR drawers." }, + // The sword rack, 3D printed (interview, round 1) + { title: "Nightblood", craft: "3D printing", blurb: "From the Cosmere.", tags: ["sword rack"] }, + { title: "Ronin lightsaber", craft: "3D printing", blurb: "From Star Wars Visions.", tags: ["sword rack"] }, + { title: "Wado Ichimonji", craft: "3D printing", blurb: "Zoro's blade, One Piece.", tags: ["sword rack"] }, + { title: "Enma", craft: "3D printing", blurb: "Zoro's blade, One Piece.", tags: ["sword rack"] }, + { title: "Sandai Kitetsu", craft: "3D printing", blurb: "Zoro's blade, One Piece.", tags: ["sword rack"] }, + { title: "Shusui", craft: "3D printing", blurb: "Zoro's blade, One Piece.", tags: ["sword rack"] }, + { title: "Tensa Zangetsu", craft: "3D printing", blurb: "Ichigo's blade, Bleach.", tags: ["sword rack"] }, + { title: "Tanjiro's Nichirin blade", craft: "3D printing", blurb: "Tanjiro's final blade, Demon Slayer.", tags: ["sword rack"] }, +]; + +// ---------------------------------------------------------------- Tabletop + +/** Blurbs from legacy/src/pages/interests/ttrpgs.astro; states from the interview. */ +export const systems = [ + { title: "Dungeons & Dragons", state: "running", blurb: "Lately 5th Edition. Long-running campaigns on and off Foundry VTT. Focus on rich worldbuilding, meaningful NPC relationships, and player-driven narrative." }, + { title: "Daggerheart", state: "running", blurb: "Darrington Press's dice-building RPG. Currently running a campaign and exploring how its mechanics create different story rhythms than D&D." }, + { title: "Cypher System", state: "played", blurb: "Monte Cook's narrative-focused system. Played a few one-shots and short form campaigns." }, + { title: "Draw Steel", state: "want to run", blurb: "MCDM's fantasy tactical cinematic combat game. Interested in running it when I get a chance." }, + { title: "Blades in the Dark", state: "want to play", blurb: "A gritty, cinematic system for heists and criminal enterprises. Interested in playing short campaigns in it one day." }, + { title: "Avatar Legends", state: "want to try", blurb: "A narrative-focused system set in the world of Avatar. Interested in exploring bending mechanics and character-driven stories." }, +] as const; + +/** + * The campaigns are real, but their names and schedules are not known yet, + * so the D&D and Daggerheart ones stay unpublished until Greg fills them in. + */ +export const campaigns = [ + { title: "D&D campaign", system: "Dungeons & Dragons", status: "active", platform: "Foundry VTT", publish: false, body: "Name, schedule and a one-line status to fill in before publishing." }, + { title: "Daggerheart campaign", system: "Daggerheart", status: "active", platform: "Foundry VTT", publish: false, body: "Name, schedule and a one-line status to fill in before publishing." }, + { title: "MCDM Crows playtest", status: "playtest", publish: true }, +] as const; + +/** Blurbs from legacy/src/pages/interests/tcgs.astro; stances and Flesh and Blood from the interview. */ +export const cardGames = [ + { title: "Magic: The Gathering", stance: "Main event", blurb: "Mostly Commander (EDH), with a deckbuilding obsession that Archidekt enables far too well." }, + { title: "Pokémon TCG", stance: "Collecting", blurb: "Collector energy and casual play. Beautiful art pulls, deck drafts, and a soft spot for Steel/Psychic. Mostly on Pocket TCG." }, + { title: "Flesh and Blood", stance: "Getting into" }, + { title: "Riftbound", stance: "Exploring", blurb: "Riot's League of Legends TCG. A new game with a lot of potential. Interested in exploring deckbuilding and the evolving meta." }, + { title: "Gundam Card Game", stance: "Watching", blurb: "Bandai's Gundam TCG. A new game with a lot of potential. Interested in how some of the figures will come into play with the other table top pieces for this entry." }, +] as const; + +/** The deck behind the legacy proxy photo. Its Archidekt id gets filled in by the sync. */ +export const decks = [{ title: "Kynaios and Tiro", game: "Magic: The Gathering", format: "Commander", favorite: true }]; + +/** "Spirit Island and Wyrmspan have been my favorites lately" (ttrpgs.astro). */ +export const favoriteBoardGames = ["Spirit Island", "Wyrmspan"]; + +// ---------------------------------------------------------------- Resume + +/** legacy/src/components/professional/SkillGrid.astro */ +export const skills = { + core: [ + "Cloud Architecture & Development", + "Mobile Application Development", + "SAP Business Technology Platform (BTP)", + "Team Leadership & Technical Mentorship", + "Cross-functional Communication", + ], + learning: [ + "SAP BTP deep-dive (active expansion)", + "Cutting edge AI-powered development tooling", + "Advanced automation and workflow engineering", + ], + stack: [ + { label: "Languages", items: ["JavaScript", "TypeScript", "Java", "Python", "C#", "C", "SQL", "HTML", "CSS"] }, + { label: "Frameworks", items: ["React", "Vue", "Astro", "Flask"] }, + { label: "Cloud Platforms", items: ["SAP BTP", "Google Cloud Platform", "AWS"] }, + { label: "Tools", items: ["PostgreSQL", "VSCode", "Unity", "XCode", "Android Studio", "Git", "Gitea", "CI/CD pipelines"] }, + { label: "AI/ML", items: ["Machine learning frameworks", "LLM integration", "Claude Code", "Hermes", "OpenClaw"] }, + ], +}; + +/** legacy/src/components/professional/EducationHistory.astro and Certifications.astro */ +export const education = { + degrees: [ + { + credential: "B.S., Computer Science", + institution: "Drexel University", + period: "2014 – 2018", + detail: "Foundational coursework in software engineering, algorithms, and systems. Focuses in AI and human-computer interaction.", + }, + ], + certifications: [{ name: "Planet 9 Development", issuer: "Neptune", year: "2019", status: "Active" as const }], +}; + +/** Employer and facts for the legacy projects; facts from the Implico and Cosmos entries. */ +export const caseStudies: Record = { + "carrier-connect": { employer: "Implico Inc.", facts: [{ label: "Active users", value: "1,000+" }, { label: "Companies", value: "100" }] }, + "driver-app": { employer: "Implico Inc." }, + "cosmos-homelab": { facts: [{ label: "AI agents", value: "17" }] }, + "placeholder-project": {}, +}; + +// ---------------------------------------------------------------- Store + +/** Lines Greg named on 2026-10-11 that are not made yet; kept unpublished. */ +export const futureLines = [ + { title: "Book-bound journals", category: "Paper" }, + { title: "Field notes journals", category: "Paper" }, + { title: "Leather bracelets", category: "Leather" }, + { title: "Journal folios", category: "Leather" }, + { title: "Card holders", category: "Leather" }, +] as const; diff --git a/tools/migrate/test/migrate.test.ts b/tools/migrate/test/migrate.test.ts new file mode 100644 index 0000000..25d7588 --- /dev/null +++ b/tools/migrate/test/migrate.test.ts @@ -0,0 +1,62 @@ +import { basename, join, resolve } from "node:path"; +import { beforeAll, describe, expect, it } from "vitest"; +import { checkNote, checkVault } from "@gk/schema"; +import { build, loadProducts, type BuildResult } from "../src/build.ts"; + +const legacy = resolve(import.meta.dirname, "../../../legacy"); +let result: BuildResult; + +beforeAll(async () => { + await loadProducts(legacy); + result = build(legacy); +}); + +const find = (type: string, title: string) => result.notes.find((n) => n.data.type === type && n.data.title === title); + +describe("migration", () => { + it("produces only valid notes and a consistent vault", () => { + const vault = result.notes.map((n) => { + const r = checkNote(n.text); + expect(r.ok, n.path).toBe(true); + return { path: n.path, type: String(n.data.type), data: n.data, body: n.body }; + }); + expect(checkVault(vault, result.attachments.map((a) => basename(a.to)))).toEqual([]); + }); + + it("carries every legacy list item over", () => { + const count = (t: string) => result.notes.filter((n) => n.data.type === t).length; + expect(count("board-game")).toBe(50); + // 24 reading + 108 anime + 103 TV + 152 movies + 11 hall of fame + 5 now playing + expect(count("library-item")).toBe(403); + expect(count("experience")).toBe(3); + }); + + it("merges a favorite listed twice into one note", () => { + const demonSlayer = result.notes.filter((n) => n.data.title === "Demon Slayer"); + expect(demonSlayer).toHaveLength(1); + expect(demonSlayer[0]!.data).toMatchObject({ favorite: true, group: "Major Modern Hits", state: "finished" }); + }); + + it("splits author from series", () => { + expect(find("library-item", "Cosmere")?.data).toMatchObject({ creator: "Brandon Sanderson", medium: "book" }); + }); + + it("keeps the store's real prices and slots", () => { + const proxies = find("product-line", "Proxy MTG Cards")!.data as any; + expect(proxies.slots).toEqual({ open: 1, total: 3 }); + expect(proxies.priceTiers).toContainEqual({ band: "100 card deck", price: "$40.00", note: "per set of cards" }); + }); + + it("logs status for everything in progress", () => { + const kinds = result.notes.filter((n) => n.data.type === "status").map((n) => `${n.data.kind}: ${n.data.title}`); + expect(kinds).toContain("Playing: Slay the Spire 2"); + expect(kinds).toContain("Watching: House of the Dragon"); + expect(kinds).toContain("Listening: The Lord of the Rings"); + }); + + it("never publishes anything still waiting on Greg", () => { + for (const title of ["D&D campaign", "Daggerheart campaign", "Carrier Connect", "Book-bound journals"]) { + expect(result.notes.find((n) => n.data.title === title)?.data.publish, title).toBe(false); + } + }); +}); diff --git a/tools/migrate/tsconfig.json b/tools/migrate/tsconfig.json new file mode 100644 index 0000000..7680f99 --- /dev/null +++ b/tools/migrate/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.base.json", + "include": ["src", "test"] +} diff --git a/tsconfig.base.json b/tsconfig.base.json new file mode 100644 index 0000000..e041118 --- /dev/null +++ b/tsconfig.base.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "target": "ES2023", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "strict": true, + "noUncheckedIndexedAccess": true, + "allowImportingTsExtensions": true, + "noEmit": true, + "skipLibCheck": true, + "types": ["node"], + "verbatimModuleSyntax": true, + "isolatedModules": true + } +} diff --git a/vault-kit/24.01_PersonalWebsite/Site guide.md b/vault-kit/24.01_PersonalWebsite/Site guide.md new file mode 100644 index 0000000..d0ed427 --- /dev/null +++ b/vault-kit/24.01_PersonalWebsite/Site guide.md @@ -0,0 +1,19 @@ +# Site guide + +Everything in the `Site` folder can appear on GregKilmer.com. Nothing outside +it ever can. + +- **Going live.** A note is on the site only when its `publish` property is + true. Turn it off and the page comes down; nothing is deleted. +- **New notes.** Use a template from `Site/_templates`. Each note type has its + own folder; the template's comments list the allowed values. +- **Links.** Link notes the usual way, `[[Note name]]`. A post that links to + a make shows up on that make, its craft and the Workshop page. +- **Status updates.** Add a note in `Now/` with a kind from `Now/_kinds`, a + date and the `[[note]]` it is about. The newest one per note wins. +- **Images.** Put them in `Site/_attachments/` and link them by file name. +- **Settings.** `_site.md`, `Now/_kinds.md` and `Store/_settings.md`. + Opening and closing store orders is one property in `Store/_settings.md`. + +If a change has a problem, the site keeps showing the last good version and +the admin page explains what to fix. diff --git a/vault-kit/24.01_PersonalWebsite/Site/_templates/Board game.md b/vault-kit/24.01_PersonalWebsite/Site/_templates/Board game.md new file mode 100644 index 0000000..6dfd4d4 --- /dev/null +++ b/vault-kit/24.01_PersonalWebsite/Site/_templates/Board game.md @@ -0,0 +1,14 @@ +--- +type: "board-game" +title: "{{title}}" +publish: false # Only notes set to true appear on the site. +created: "{{date:YYYY-MM-DD}}" # optional +bggId: +blurb: +howItPlays: [] # optional. Like engine building, social deduction, co-op. +owned: true # optional +wantToPlay: false # optional +lastPlayed: # optional +--- + + diff --git a/vault-kit/24.01_PersonalWebsite/Site/_templates/Campaign.md b/vault-kit/24.01_PersonalWebsite/Site/_templates/Campaign.md new file mode 100644 index 0000000..bc134e1 --- /dev/null +++ b/vault-kit/24.01_PersonalWebsite/Site/_templates/Campaign.md @@ -0,0 +1,13 @@ +--- +type: "campaign" +title: "{{title}}" +publish: false # Only notes set to true appear on the site. +created: "{{date:YYYY-MM-DD}}" # optional +system: # optional. Leave out for a playtest of an unreleased game. +status: # one of: active, playtest, paused, finished +schedule: # optional. Like Thursdays, every other week. +platform: # optional. Like Foundry VTT. +blurb: # optional +--- + + diff --git a/vault-kit/24.01_PersonalWebsite/Site/_templates/Card game.md b/vault-kit/24.01_PersonalWebsite/Site/_templates/Card game.md new file mode 100644 index 0000000..5a70159 --- /dev/null +++ b/vault-kit/24.01_PersonalWebsite/Site/_templates/Card game.md @@ -0,0 +1,9 @@ +--- +type: "card-game" +title: "{{title}}" +publish: false # Only notes set to true appear on the site. +created: "{{date:YYYY-MM-DD}}" # optional +stance: # Like Main event, Collecting, Getting into. +blurb: # optional +order: # optional +--- diff --git a/vault-kit/24.01_PersonalWebsite/Site/_templates/Craft.md b/vault-kit/24.01_PersonalWebsite/Site/_templates/Craft.md new file mode 100644 index 0000000..13cbcd5 --- /dev/null +++ b/vault-kit/24.01_PersonalWebsite/Site/_templates/Craft.md @@ -0,0 +1,13 @@ +--- +type: "craft" +title: "{{title}}" +publish: false # Only notes set to true appear on the site. +created: "{{date:YYYY-MM-DD}}" # optional +status: # one of: Practicing, Learning, Getting into, Paused +blurb: # optional +station: # optional +tools: [] # optional +order: # optional +--- + + diff --git a/vault-kit/24.01_PersonalWebsite/Site/_templates/Deck.md b/vault-kit/24.01_PersonalWebsite/Site/_templates/Deck.md new file mode 100644 index 0000000..a227dc2 --- /dev/null +++ b/vault-kit/24.01_PersonalWebsite/Site/_templates/Deck.md @@ -0,0 +1,11 @@ +--- +type: "deck" +title: "{{title}}" +publish: false # Only notes set to true appear on the site. +created: "{{date:YYYY-MM-DD}}" # optional +game: +format: # optional. Like Commander. +archidektId: # optional +listUrl: # optional. For games not on Archidekt. +take: # optional. One line on the deck. +--- diff --git a/vault-kit/24.01_PersonalWebsite/Site/_templates/Experience.md b/vault-kit/24.01_PersonalWebsite/Site/_templates/Experience.md new file mode 100644 index 0000000..dc961a5 --- /dev/null +++ b/vault-kit/24.01_PersonalWebsite/Site/_templates/Experience.md @@ -0,0 +1,16 @@ +--- +type: "experience" +title: "{{title}}" +publish: false # Only notes set to true appear on the site. +created: "{{date:YYYY-MM-DD}}" # optional +company: +role: +start: +end: # optional. Leave out while it is your current role. +location: # optional +focus: # optional. One line under the role. +bullets: [] +tech: [] # optional +--- + + diff --git a/vault-kit/24.01_PersonalWebsite/Site/_templates/Library item.md b/vault-kit/24.01_PersonalWebsite/Site/_templates/Library item.md new file mode 100644 index 0000000..a87a90e --- /dev/null +++ b/vault-kit/24.01_PersonalWebsite/Site/_templates/Library item.md @@ -0,0 +1,18 @@ +--- +type: "library-item" +title: "{{title}}" +publish: false # Only notes set to true appear on the site. +created: "{{date:YYYY-MM-DD}}" # optional +medium: # one of: book, light novel, webnovel, manga, manhwa, video game, anime, TV, movie +state: # one of: now, up next, played, finished, caught up, dropped +creator: # optional +group: # optional. Where it sits in the list, like Cosmere, Marvel Cinematic Universe or Prestige Drama. +year: # optional +platform: # optional. Video games only, like Steam or PlayStation 5. +verdict: # optional. A short review. No score. +note: # optional. Context, like where you are or how you came to it. +progress: # optional. Like 16/18 volumes. Synced from PersonalReader when linked. +anilistId: # optional. Links the item to PersonalReader's progress. +--- + + diff --git a/vault-kit/24.01_PersonalWebsite/Site/_templates/Make.md b/vault-kit/24.01_PersonalWebsite/Site/_templates/Make.md new file mode 100644 index 0000000..720a5e3 --- /dev/null +++ b/vault-kit/24.01_PersonalWebsite/Site/_templates/Make.md @@ -0,0 +1,12 @@ +--- +type: "make" +title: "{{title}}" +publish: false # Only notes set to true appear on the site. +created: "{{date:YYYY-MM-DD}}" # optional +craft: +date: "{{date:YYYY-MM-DD}}" # optional +photos: [] # optional +blurb: # optional +--- + + diff --git a/vault-kit/24.01_PersonalWebsite/Site/_templates/Page text.md b/vault-kit/24.01_PersonalWebsite/Site/_templates/Page text.md new file mode 100644 index 0000000..a75c47a --- /dev/null +++ b/vault-kit/24.01_PersonalWebsite/Site/_templates/Page text.md @@ -0,0 +1,10 @@ +--- +type: "page" +title: "{{title}}" +publish: false # Only notes set to true appear on the site. +created: "{{date:YYYY-MM-DD}}" # optional +page: # The page address this text belongs to, like /about. +lede: # optional. The one line under the page title. +--- + + diff --git a/vault-kit/24.01_PersonalWebsite/Site/_templates/Post.md b/vault-kit/24.01_PersonalWebsite/Site/_templates/Post.md new file mode 100644 index 0000000..052879d --- /dev/null +++ b/vault-kit/24.01_PersonalWebsite/Site/_templates/Post.md @@ -0,0 +1,17 @@ +--- +type: "post" +title: "{{title}}" +publish: false # Only notes set to true appear on the site. +created: "{{date:YYYY-MM-DD}}" # optional +date: "{{date:YYYY-MM-DD}}" +topic: # one of: Making, Tabletop, Library, Work and tech, Life +postType: "article" # optional. one of: article, build log, case study +summary: # One or two sentences for lists and link previews. +pinned: false # optional. Takes the top slot on the Blog. +employer: # optional. Case studies only. Leave out for personal projects. +role: # optional. Case studies only. +period: # optional. Case studies only, like 2021 to now. +tech: [] # optional +--- + + diff --git a/vault-kit/24.01_PersonalWebsite/Site/_templates/Product line.md b/vault-kit/24.01_PersonalWebsite/Site/_templates/Product line.md new file mode 100644 index 0000000..2315c0b --- /dev/null +++ b/vault-kit/24.01_PersonalWebsite/Site/_templates/Product line.md @@ -0,0 +1,18 @@ +--- +type: "product-line" +title: "{{title}}" +publish: false # Only notes set to true appear on the site. +created: "{{date:YYYY-MM-DD}}" # optional +category: # one of: Tabletop, Paper, Leather +mode: # one of: made to order, in stock +channel: # one of: here, Etsy, Patiun.com, coming soon +channelUrl: # optional. For Etsy or Patiun.com lines. +summary: +icon: # optional +order: # optional +slots: # optional. Made to order lines only. +quantity: # optional. In stock lines only. +priceTiers: [] +--- + + diff --git a/vault-kit/24.01_PersonalWebsite/Site/_templates/Station.md b/vault-kit/24.01_PersonalWebsite/Site/_templates/Station.md new file mode 100644 index 0000000..d97acc3 --- /dev/null +++ b/vault-kit/24.01_PersonalWebsite/Site/_templates/Station.md @@ -0,0 +1,9 @@ +--- +type: "station" +title: "{{title}}" +publish: false # Only notes set to true appear on the site. +created: "{{date:YYYY-MM-DD}}" # optional +where: # Like the work counter and filament wall. +goodFor: # optional +order: +--- diff --git a/vault-kit/24.01_PersonalWebsite/Site/_templates/Status.md b/vault-kit/24.01_PersonalWebsite/Site/_templates/Status.md new file mode 100644 index 0000000..87c3f7d --- /dev/null +++ b/vault-kit/24.01_PersonalWebsite/Site/_templates/Status.md @@ -0,0 +1,11 @@ +--- +type: "status" +title: "{{title}}" +publish: false # Only notes set to true appear on the site. +created: "{{date:YYYY-MM-DD}}" # optional +kind: # One of the kinds listed in Now/_kinds. +date: "{{date:YYYY-MM-DD}}" +subject: # optional. The note this status is about, if there is one. +note: # optional +pinned: false # optional +--- diff --git a/vault-kit/24.01_PersonalWebsite/Site/_templates/Tabletop system.md b/vault-kit/24.01_PersonalWebsite/Site/_templates/Tabletop system.md new file mode 100644 index 0000000..1a4f0fc --- /dev/null +++ b/vault-kit/24.01_PersonalWebsite/Site/_templates/Tabletop system.md @@ -0,0 +1,9 @@ +--- +type: "tabletop-system" +title: "{{title}}" +publish: false # Only notes set to true appear on the site. +created: "{{date:YYYY-MM-DD}}" # optional +state: # one of: running, played, want to run, want to play, want to try +blurb: +order: # optional +--- diff --git a/vault-kit/24.01_PersonalWebsite/Site/_templates/Tool or material.md b/vault-kit/24.01_PersonalWebsite/Site/_templates/Tool or material.md new file mode 100644 index 0000000..7261085 --- /dev/null +++ b/vault-kit/24.01_PersonalWebsite/Site/_templates/Tool or material.md @@ -0,0 +1,9 @@ +--- +type: "tool" +title: "{{title}}" +publish: false # Only notes set to true appear on the site. +created: "{{date:YYYY-MM-DD}}" # optional +kind: # one of: printer, cutter, casting, finishing, material, other +station: # optional +blurb: # optional +--- diff --git a/vitest.config.ts b/vitest.config.ts new file mode 100644 index 0000000..9ca545d --- /dev/null +++ b/vitest.config.ts @@ -0,0 +1,5 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { include: ["packages/*/test/**/*.test.ts", "tools/*/test/**/*.test.ts"] }, +}); From 3f646048a290cb4be474ffdbcbea729859f94372 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 11 Oct 2026 19:10:38 +0000 Subject: [PATCH 02/16] Add the content service and MCP server (phase 2) services/content watches the vault's Site folder, runs every change through fixed rules, keeps the last accepted version of each note with its history, and serves one write API. The HTTP API, the MCP server for agents, and the admin page and editing tools to come all write through it. - SQLite store with history, review queue, redirects, links and full-text search - Rules: invalid, misplaced, duplicate name or slug, text loss, secrets, agent contact details, agent edits, agent publishing, agent batches - Watcher with debounce and a periodic full scan - HTTP API with reader, agent, editor and admin tokens - MCP tools: search, get_note, get_schema, list_held, set_status, add_library_item, update_fields, draft_post - Tests, including taking in all 545 migrated notes with nothing held Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_012aSbo2D1qP8cMFQCykf7kx --- .gitignore | 1 + README.md | 8 +- docs/content-service.md | 120 +++ package-lock.json | 1235 +++++++++++++++++++++++- package.json | 6 +- packages/schema/src/notes.ts | 18 +- services/content/package.json | 17 + services/content/src/config.ts | 65 ++ services/content/src/http.ts | 262 +++++ services/content/src/main.ts | 54 ++ services/content/src/mcp.ts | 229 +++++ services/content/src/rules.ts | 153 +++ services/content/src/service.ts | 335 +++++++ services/content/src/store.ts | 382 ++++++++ services/content/src/vault.ts | 95 ++ services/content/src/views.ts | 61 ++ services/content/src/watcher.ts | 76 ++ services/content/test/helpers.ts | 78 ++ services/content/test/http.test.ts | 96 ++ services/content/test/migrated.test.ts | 35 + services/content/test/service.test.ts | 188 ++++ services/content/tsconfig.json | 4 + vitest.config.ts | 2 +- 23 files changed, 3482 insertions(+), 38 deletions(-) create mode 100644 docs/content-service.md create mode 100644 services/content/package.json create mode 100644 services/content/src/config.ts create mode 100644 services/content/src/http.ts create mode 100644 services/content/src/main.ts create mode 100644 services/content/src/mcp.ts create mode 100644 services/content/src/rules.ts create mode 100644 services/content/src/service.ts create mode 100644 services/content/src/store.ts create mode 100644 services/content/src/vault.ts create mode 100644 services/content/src/views.ts create mode 100644 services/content/src/watcher.ts create mode 100644 services/content/test/helpers.ts create mode 100644 services/content/test/http.test.ts create mode 100644 services/content/test/migrated.test.ts create mode 100644 services/content/test/service.test.ts create mode 100644 services/content/tsconfig.json diff --git a/.gitignore b/.gitignore index 226df75..e97e18d 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,4 @@ npm-debug.log* !.vscode/extensions.json !.vscode/settings.json .idea/ +data/ diff --git a/README.md b/README.md index bccf14d..ced09bb 100644 --- a/README.md +++ b/README.md @@ -12,12 +12,12 @@ with the decisions behind it, is the | --- | --- | | `packages/schema` | The one definition of every note type: fields, folders, file names, and the checks for one note and for the whole folder. The migration, the content service, the MCP server, the site and any later editing tool all use it. | | `vault-kit` | What goes into the vault: the Obsidian templates (generated from the schema) and the Site guide. | +| `services/content` | The content service: watches the Site folder, runs the rules, keeps the last good version and its history, and serves the one write API. The MCP server for agents is part of it. See [docs/content-service.md](docs/content-service.md). | | `tools/migrate` | The one-time migration from the old site's data into Site folder notes. | | `docs` | How the pieces fit. Start with [docs/content-model.md](docs/content-model.md). | | `legacy` | The old site, kept for reference only. Nothing outside it imports from it except the migration. | -Coming in later phases: `services/content` (the content service and its MCP -server), the admin page, and the new site. +Coming in later phases: the admin page, the component library, and the new site. ## Commands @@ -29,6 +29,10 @@ npm run check # typecheck and tests npm run migrate # writes out/24.01_PersonalWebsite and out/MIGRATION-REPORT.md npm run templates # regenerates vault-kit templates after a schema change npm run schema:json # writes a JSON Schema per note type to packages/schema/json/ + +# the content service, against a copy of the Site folder +SITE_DIR=out/24.01_PersonalWebsite/Site DATA_DIR=data \ +CONTENT_TOKENS="reader:<24+ chars>,admin:<24+ chars>" npm run content ``` ## The old site diff --git a/docs/content-service.md b/docs/content-service.md new file mode 100644 index 0000000..be91a21 --- /dev/null +++ b/docs/content-service.md @@ -0,0 +1,120 @@ +# Content service + +The content service sits between the vault and everything that shows or +changes content. It is the only thing that reads or writes the Site folder. + +``` +Obsidian ──edits──▶ Site folder ──watch──▶ rules ──▶ accepted store ──▶ site, admin page + ▲ ▲ + └──── accepted API changes ────────┤ + write API (HTTP) + ▲ ▲ + MCP server admin page, later editors +``` + +- **The vault is the source of truth.** Greg edits in Obsidian; the service + watches the folder and takes each change in. +- **Every change runs the same rules**, whether it came from Obsidian, an + agent or the admin page. A change that passes is accepted. A change that + fails is held for review, and the site keeps showing the last accepted + version, so a bad edit never reaches a page. +- **Changes through the API are written back into the vault** once accepted, + so Obsidian always shows what the site shows. The service marks its own + writes as seen before writing, so the watcher does not take them in twice. +- **Deleting or moving a file unpublishes the note.** Its history stays, and + any version can be rolled back. + +## The rules + +Pure functions in [`services/content/src/rules.ts`](../services/content/src/rules.ts), +run in this order. The rules live in code, not in the vault, so no agent can +loosen them. + +| Code | Holds a change when | +| --- | --- | +| `invalid` | The note fails its type's schema. | +| `misplaced` | A settings note (`_site`, `Now/_kinds`, `Store/_settings`) is not at its fixed path. | +| `duplicate-name`, `duplicate-slug` | Another live note already has the file name, or the same type already uses the address. | +| `text-loss` | A body of 400 or more characters loses more than half in one edit. | +| `secret` | New text looks like a key, token or password. | +| `contact` | An agent adds an email address or phone number. | +| `agent-edit` | An agent changes a note it did not write, unless the note has `agentEdits: true`. | +| `agent-publish` | An agent publishes anything but a status or a Library item. | +| `batch` | An agent makes more than 10 changes in 10 minutes. | + +Invalid input through the API is turned down at once with the problems, rather +than held, so callers fix it themselves. A change from Obsidian that fails is +held, because the file is already on disk. + +## Addresses + +A slug change or a rename keeps the old address working as a redirect. When a +note disappears and a note of the same type with the same text or title +appears within ten minutes, that is treated as a move: redirects to the old +path are pointed at the new one, so redirects never chain. A slug that comes +back into use belongs to its note, not to the redirect. + +## The write API + +`ContentService.apply` takes one change, `create` (type, fields, body) or +`update` (path, the fields to change, `null` to remove one, and optionally +the body), along with who made it. Every client uses it: + +- the MCP server, as `agent`; +- the admin page and later editing tools, as `greg` (editor token) or `admin`; +- the review queue: `acceptHeld`, `rejectHeld` (optionally restoring the live + version into the vault), and `rollback` to any earlier revision. + +An update first takes in any unsaved Obsidian edit to that file, so an API +change never overwrites one. A note with a change waiting for review takes no +other API change until that one is decided. A note's type never changes. + +### HTTP + +Every route but `/health` needs `Authorization: Bearer `. Tokens map to +roles in `CONTENT_TOKENS`. + +| Route | Who | What | +| --- | --- | --- | +| `GET /health` | anyone | Note, published and held counts. | +| `GET /api/schema` | all | Every note type with its JSON Schema. | +| `GET /api/notes?type=&q=&limit=&offset=&full=` | all | List or search. Readers see published notes only. | +| `GET /api/note?path=` or `?type=&slug=` | all | One note with its backlinks. An old slug answers with `{ redirect }`. | +| `GET /api/backlinks?name=` | all | Notes linking to a note. | +| `GET /api/redirects` | all | Every redirect. | +| `GET /attachments/` | all | An image from `_attachments`. | +| `GET /api/note/history?path=`, `/api/note/revision?path=&rev=` | agent, editor, admin | History and old versions. | +| `GET /api/problems` | agent, editor, admin | Broken links and other whole-folder problems. | +| `GET /api/held`, `/api/held/` | agent, editor, admin | The review queue; one entry comes with the current text for comparison. | +| `POST /api/changes` | agent, editor, admin | One change. 422 with reasons when turned down. | +| `POST /api/held//accept`, `/reject` | editor, admin | Decide a held change. `{ "restore": true }` on reject puts the live version back into the vault. | +| `POST /api/note/rollback` | admin | `{ path, rev }` | +| `POST /api/scan` | editor, admin | Compare the whole folder now. | +| `/mcp` | agent, editor, admin | The MCP server. | + +### MCP + +Streamable HTTP at `/mcp`, stateless. Tools: `search`, `get_note`, +`get_schema`, `list_held`, `set_status`, `add_library_item`, `update_fields` +and `draft_post` (always unpublished). There is no tool to delete, rename, +move or write a raw file. + +## Running it + +| Variable | Default | | +| --- | --- | --- | +| `SITE_DIR` | required | The vault's Site folder. | +| `DATA_DIR` | `data` | Holds `content.db`. Back it up: history and the review queue cannot be rebuilt from the vault. | +| `PORT`, `HOST` | `4310`, `0.0.0.0` | | +| `CONTENT_TOKENS` | none | `role:token` pairs, comma separated. Roles: reader, agent, editor, admin. Tokens are 24 characters or more. | +| `ALLOWED_EMAILS` | none | Addresses an agent may add without a hold. | +| `HEALTHCHECK_URL` | none | Pinged after every full scan. | +| `NOTIFY_WEBHOOK_URL` | none | Receives `{ event: "held", held }` when a change is held. | +| `SCAN_MINUTES` | `5` | Full scan interval, on top of the file watch. | + +```sh +SITE_DIR=/path/to/Site CONTENT_TOKENS="reader:…,agent:…,editor:…,admin:…" npm run content +``` + +The store is SQLite (`node:sqlite`) with full-text search, so the service has +no database server to run. diff --git a/package-lock.json b/package-lock.json index f3fcd09..7506337 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,6 +7,7 @@ "name": "gregkilmer-com", "workspaces": [ "packages/*", + "services/*", "tools/*" ], "devDependencies": { @@ -26,7 +27,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -43,7 +43,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -60,7 +59,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -77,7 +75,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -94,7 +91,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -111,7 +107,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -128,7 +123,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -145,7 +139,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -162,7 +155,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -179,7 +171,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -196,7 +187,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -213,7 +203,6 @@ "cpu": [ "loong64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -230,7 +219,6 @@ "cpu": [ "mips64el" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -247,7 +235,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -264,7 +251,6 @@ "cpu": [ "riscv64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -281,7 +267,6 @@ "cpu": [ "s390x" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -298,7 +283,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -315,7 +299,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -332,7 +315,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -349,7 +331,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -366,7 +347,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -383,7 +363,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -400,7 +379,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -417,7 +395,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -434,7 +411,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -451,7 +427,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -461,6 +436,10 @@ "node": ">=18" } }, + "node_modules/@gk/content": { + "resolved": "services/content", + "link": true + }, "node_modules/@gk/migrate": { "resolved": "tools/migrate", "link": true @@ -469,6 +448,18 @@ "resolved": "packages/schema", "link": true }, + "node_modules/@hono/node-server": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-2.1.4.tgz", + "integrity": "sha512-2g1qeHl4BduwDn6L8nkAvDrPUWPMlkQ//a5NhP/zIyaXKMjZNRV+cG9Csu+BiJFf6+68pqzjf4Hi5sJGKHuS1g==", + "license": "MIT", + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "hono": "^4 || ^5.0.0-0" + } + }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", @@ -497,6 +488,46 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@modelcontextprotocol/sdk": { + "version": "1.32.1", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.32.1.tgz", + "integrity": "sha512-2DdE+SJDtzLEEWzY1ZjY7Q+VcPhcV1KisD3zI4u0XZyktsjHum1mwbMI+JaulUBi2OZk+KJAi2uPXzxichPkdw==", + "license": "MIT", + "dependencies": { + "@hono/node-server": "^1.19.9 || ^2.0.5", + "ajv": "^8.17.1", + "ajv-formats": "^3.0.1", + "content-type": "^1.0.5", + "cors": "^2.8.5", + "cross-spawn": "^7.0.5", + "eventsource": "^3.0.2", + "eventsource-parser": "^3.0.0", + "express": "^5.2.1", + "express-rate-limit": "^8.2.1", + "hono": "^4.11.4", + "jose": "^6.1.3", + "json-schema-typed": "^8.0.2", + "pkce-challenge": "^5.0.0", + "raw-body": "^3.0.0", + "zod": "^3.25 || ^4.0", + "zod-to-json-schema": "^3.25.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@cfworker/json-schema": "^4.1.1", + "zod": "^3.25 || ^4.0" + }, + "peerDependenciesMeta": { + "@cfworker/json-schema": { + "optional": true + }, + "zod": { + "optional": false + } + } + }, "node_modules/@oxc-project/types": { "version": "0.153.0", "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.153.0.tgz", @@ -859,6 +890,52 @@ "url": "https://opencollective.com/vitest" } }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, "node_modules/assertion-error": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", @@ -869,6 +946,81 @@ "node": ">=12" } }, + "node_modules/body-parser": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.3.0.tgz", + "integrity": "sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^2.0.0", + "debug": "^4.4.3", + "http-errors": "^2.0.1", + "iconv-lite": "^0.7.2", + "on-finished": "^2.4.1", + "qs": "^6.15.2", + "raw-body": "^3.0.2", + "type-is": "^2.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/body-parser/node_modules/content-type": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz", + "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/chai": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/chai/-/chai-6.3.0.tgz", @@ -879,6 +1031,103 @@ "node": ">=18" } }, + "node_modules/content-disposition": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-2.0.1.tgz", + "integrity": "sha512-e+H0ZXHSWYrENhQzw1LPuP4oF5MzVKmDU6d3hxlvaPEYLLg62MxtQNPRx4SYSuYJSBUgnQIG4HIN2tEtNv7Dog==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/cors": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", + "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/detect-libc": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", @@ -890,6 +1139,53 @@ "node": ">=8" } }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es-module-lexer": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.2.tgz", @@ -897,11 +1193,22 @@ "dev": true, "license": "MIT" }, + "node_modules/es-object-atoms": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/esbuild": { "version": "0.28.2", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz", "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==", - "dev": true, "hasInstallScript": true, "license": "MIT", "bin": { @@ -939,6 +1246,12 @@ "@esbuild/win32-x64": "0.28.2" } }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, "node_modules/estree-walker": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", @@ -949,6 +1262,36 @@ "@types/estree": "^1.0.0" } }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eventsource": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz", + "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==", + "license": "MIT", + "dependencies": { + "eventsource-parser": "^3.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/eventsource-parser": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.1.1.tgz", + "integrity": "sha512-EKN1vKAMcZ8MlYMpaNuxN6R9yakzH6uajHcHVTqWJzvu5pWw9DyhbP35HH8MVBQ+dZjAfDxk+A8NiR9KWaXiyQ==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/expect-type": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz", @@ -959,6 +1302,103 @@ "node": ">=12.0.0" } }, + "node_modules/express": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/express/-/express-5.3.0.tgz", + "integrity": "sha512-qCqy1xSoaugxOD4IVHb1ZWdkvCF2W3aE2okKouWj8VwJ+ztskSiXoBnCSJqfKT+cVPJhuJqe9YLbY425+Zh00g==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.3.0", + "content-disposition": "^2.0.1", + "content-type": "^2.0.0", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.8", + "qs": "^6.16.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.1.0", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express-rate-limit": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.7.1.tgz", + "integrity": "sha512-2FXbqkygil3mZiXzEtM0IjE/UBmBNot0J1GEVrJT4JbrvwOSoEtqtO+wHbiMQELWe1eNvHoYRg2dkjOid+6JkA==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "ip-address": "^10.2.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/express-rate-limit" + }, + "peerDependencies": { + "express": ">= 4.11" + } + }, + "node_modules/express/node_modules/content-type": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz", + "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.8.tgz", + "integrity": "sha512-GZMtZUTNRpOVIECoXwLNZS5xUGE+mVNbTB8h/7Rwh2TFWcBQiPzTgyZi05BF9UMZKkLJv8XBRJTlU7zg8+ZfMg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/fdir": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", @@ -977,11 +1417,49 @@ } } }, + "node_modules/finalhandler": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -992,6 +1470,190 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hono": { + "version": "4.13.13", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.13.13.tgz", + "integrity": "sha512-CQ46U0ZkAGmbT/4UxdzzGJpacP2IeKgY4a5/tOI9AABbpOMfK739wfDXmv1usCk+3RkKj1hQy4/fjhiwa2xlrA==", + "license": "MIT", + "engines": { + "node": ">=16.9.0" + } + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/iconv-lite": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.3.tgz", + "integrity": "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ip-address": { + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.7.3.tgz", + "integrity": "sha512-A1kdq/tSb5QjvKvAMgIoEvDBIgL7qaqVP/jkvSwYYRZ9iEzvPpopxp2wQfu3SuZRHtpHNxMn8Fs0bS+gf5Xmwg==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/jose": { + "version": "6.2.12", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.12.tgz", + "integrity": "sha512-9NiFmJEex0sy2Dk58j2UGBSHgUs2ypF9eZSu4L6vjOX3Dp96Sw1F3uL+H+D1sx02jZZdzUT0HgvCy59CuvXcWw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/json-schema-typed": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.2.tgz", + "integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==", + "license": "BSD-2-Clause" + }, "node_modules/lightningcss": { "version": "1.33.0", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", @@ -1275,6 +1937,71 @@ "@jridgewell/sourcemap-codec": "^1.6.0" } }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.1.tgz", + "integrity": "sha512-yz3xRaG20c6/BOzvYoDaGtPmGscs7YivItZEEqe6GbwNfHuxu9YNmvnEkMzKldAGY4/80pRcQRZSEnhquk9XuQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, "node_modules/nanoid": { "version": "3.3.20", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.20.tgz", @@ -1295,6 +2022,56 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/negotiator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.1.0.tgz", + "integrity": "sha512-NMPBRMJgiQHjbd8phG3Vebdx4kZ1H121rbl5IkMqeOsahptB9BKo/d7oJ3zTXqTgagn2bWlNSXkh0QUGM31RYg==", + "license": "MIT", + "dependencies": { + "content-type": "^2.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/negotiator/node_modules/content-type": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz", + "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/obug": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/obug/-/obug-2.2.1.tgz", @@ -1309,6 +2086,55 @@ "node": ">=12.20.0" } }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-to-regexp": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", + "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -1330,6 +2156,15 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pkce-challenge": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.1.tgz", + "integrity": "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==", + "license": "MIT", + "engines": { + "node": ">=16.20.0" + } + }, "node_modules/postcss": { "version": "8.5.29", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.29.tgz", @@ -1360,6 +2195,76 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/proxy-addr": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.8.tgz", + "integrity": "sha512-5nnx0yGyVUcY6t9RnWcARWtwT9F1D8O9rt08htPvnd49W1IgZtmLkhu9WfMzQj1cFxjHIO6connUNVW5k7AVyQ==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/qs": { + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.16.0.tgz", + "integrity": "sha512-h6fhOIaRrID2CbEY2fqs+7t+UXZo+MLAnU5gRIq85uFtdiUPCdsApMlHhXogKVM4HM2DVbIjGNTTYH2OcmP1vA==", + "license": "BSD-3-Clause", + "dependencies": { + "es-define-property": "^1.0.1", + "side-channel": "^1.1.1" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.3.0.tgz", + "integrity": "sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/raw-body": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/rolldown": { "version": "1.2.13", "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.13.tgz", @@ -1395,6 +2300,172 @@ "@rolldown/binding-win32-x64-msvc": "1.2.13" } }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/serve-static": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", + "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4", + "side-channel-list": "^1.0.1", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/source-map-js": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.2.tgz", @@ -1406,6 +2477,15 @@ "node": ">=0.10.0" } }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/std-env": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.3.0.tgz", @@ -1450,11 +2530,19 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, "node_modules/tsx": { "version": "4.23.15", "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.23.15.tgz", "integrity": "sha512-Yiex1Ovn8z2xPpOWckIiysV1SSyRMY9BkLF++q0yKiDxCqRhosKfMg3janKkiLBwZ5c/YryloKwGZcrEmtwxKw==", - "dev": true, "license": "MIT", "dependencies": { "esbuild": "~0.28.0" @@ -1469,6 +2557,37 @@ "fsevents": "~2.3.3" } }, + "node_modules/type-is": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz", + "integrity": "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==", + "license": "MIT", + "dependencies": { + "content-type": "^2.0.0", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/type-is/node_modules/content-type": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz", + "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/typescript": { "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", @@ -1489,6 +2608,24 @@ "dev": true, "license": "MIT" }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/vite": { "version": "8.3.4", "resolved": "https://registry.npmjs.org/vite/-/vite-8.3.4.tgz", @@ -1651,6 +2788,21 @@ } } }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/why-is-node-running": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-3.2.1.tgz", @@ -1664,6 +2816,12 @@ "node": ">=20.11" } }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, "node_modules/yaml": { "version": "2.9.1", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.1.tgz", @@ -1688,6 +2846,15 @@ "url": "https://github.com/sponsors/colinhacks" } }, + "node_modules/zod-to-json-schema": { + "version": "3.25.2", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.2.tgz", + "integrity": "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==", + "license": "ISC", + "peerDependencies": { + "zod": "^3.25.28 || ^4" + } + }, "packages/schema": { "name": "@gk/schema", "version": "0.1.0", @@ -1696,6 +2863,16 @@ "zod": "^4.1.0" } }, + "services/content": { + "name": "@gk/content", + "version": "0.1.0", + "dependencies": { + "@gk/schema": "*", + "@modelcontextprotocol/sdk": "^1.32.1", + "tsx": "^4.19.0", + "zod": "^4.1.0" + } + }, "tools/migrate": { "name": "@gk/migrate", "version": "0.1.0", diff --git a/package.json b/package.json index b2aa82e..707ec47 100644 --- a/package.json +++ b/package.json @@ -5,18 +5,20 @@ "description": "GregKilmer.com: the note schema, content service and site that serve Greg's vault notes. The old site lives in legacy/ for reference only.", "workspaces": [ "packages/*", + "services/*", "tools/*" ], "engines": { "node": ">=22" }, "scripts": { - "typecheck": "tsc -p packages/schema && tsc -p tools/migrate", + "typecheck": "tsc -p packages/schema && tsc -p tools/migrate && tsc -p services/content", "test": "vitest run", "check": "npm run typecheck && npm test", "schema:json": "npm run json -w @gk/schema", "templates": "npm run templates -w @gk/schema", - "migrate": "npm run migrate -w @gk/migrate --" + "migrate": "npm run migrate -w @gk/migrate --", + "content": "npm run start -w @gk/content" }, "devDependencies": { "@types/node": "^22.10.0", diff --git a/packages/schema/src/notes.ts b/packages/schema/src/notes.ts index f35bcb7..84e3f48 100644 --- a/packages/schema/src/notes.ts +++ b/packages/schema/src/notes.ts @@ -87,6 +87,19 @@ function orderKeys(def: NoteTypeDef, data: Record): Record, body = ""): string { + const def = typeDef(String(data.type)); + const ordered = def ? orderKeys(def, data) : data; + // Write what was given (not schema defaults), so notes stay short. + const yaml = stringifyYaml(ordered, { lineWidth: 0 }).trimEnd(); + const text = body.trim() ? `\n${body.trim()}\n` : ""; + return `---\n${yaml}\n---\n${text}`; +} + /** Write a note's text. The data is checked first; this throws when it fails. */ export function writeNote(data: Record, body = ""): string { const def = typeDef(String(data.type)); @@ -96,10 +109,7 @@ export function writeNote(data: Record, body = ""): string { const problems = result.error.issues.map((i) => `${i.path.join(".") || "note"}: ${i.message}`).join("; "); throw new Error(`"${String(data.title)}" is not a valid ${def.label}: ${problems}`); } - // Write what was given (not schema defaults), so notes stay short. - const yaml = stringifyYaml(orderKeys(def, data), { lineWidth: 0 }).trimEnd(); - const text = body.trim() ? `\n${body.trim()}\n` : ""; - return `---\n${yaml}\n---\n${text}`; + return formatNote(data, body); } /** Where a note of this type lives, relative to the Site folder, with `.md`. */ diff --git a/services/content/package.json b/services/content/package.json new file mode 100644 index 0000000..d7d1d52 --- /dev/null +++ b/services/content/package.json @@ -0,0 +1,17 @@ +{ + "name": "@gk/content", + "version": "0.1.0", + "private": true, + "type": "module", + "description": "The content service: watches the vault's Site folder, checks every change, keeps the last good version of each note, and serves the one write API that the MCP server, the admin page and later editing tools share.", + "scripts": { + "start": "node --disable-warning=ExperimentalWarning --import tsx src/main.ts", + "dev": "node --disable-warning=ExperimentalWarning --watch --import tsx src/main.ts" + }, + "dependencies": { + "@gk/schema": "*", + "@modelcontextprotocol/sdk": "^1.32.1", + "tsx": "^4.19.0", + "zod": "^4.1.0" + } +} diff --git a/services/content/src/config.ts b/services/content/src/config.ts new file mode 100644 index 0000000..3dd63e3 --- /dev/null +++ b/services/content/src/config.ts @@ -0,0 +1,65 @@ +/** + * Settings come from the environment, so the same image runs on Cosmos and in + * tests. Nothing here is read from the vault: an agent that can edit notes + * cannot change who may write or what the rules allow. + */ +import { resolve } from "node:path"; + +export type Role = "reader" | "agent" | "editor" | "admin"; + +export interface Config { + /** The vault's Site folder. */ + siteDir: string; + /** The SQLite file holding accepted notes, history and the review queue. */ + dbFile: string; + port: number; + host: string; + /** Bearer token to role. */ + tokens: Map; + /** Pinged after every full scan, so a stopped watcher is noticed. */ + healthcheckUrl?: string; + /** Receives a JSON message whenever a change is held. */ + notifyWebhookUrl?: string; + allowedEmails: string[]; + scanMinutes: number; +} + +const roles = new Set(["reader", "agent", "editor", "admin"]); + +/** CONTENT_TOKENS is a comma separated list of role:token pairs. */ +export function parseTokens(raw: string | undefined): Map { + const tokens = new Map(); + for (const pair of (raw ?? "").split(",").map((s) => s.trim()).filter(Boolean)) { + const at = pair.indexOf(":"); + const role = pair.slice(0, at) as Role; + const token = pair.slice(at + 1); + if (at < 1 || !roles.has(role)) throw new Error(`CONTENT_TOKENS: "${pair.slice(0, Math.max(at, 0))}" is not a role`); + if (token.length < 24) throw new Error(`CONTENT_TOKENS: the ${role} token is shorter than 24 characters`); + tokens.set(token, role); + } + return tokens; +} + +export function loadConfig(env: NodeJS.ProcessEnv = process.env): Config { + if (!env.SITE_DIR) throw new Error("SITE_DIR is required: the vault's Site folder"); + const dataDir = resolve(env.DATA_DIR ?? "data"); + return { + siteDir: resolve(env.SITE_DIR), + dbFile: resolve(dataDir, "content.db"), + port: Number(env.PORT ?? 4310), + host: env.HOST ?? "0.0.0.0", + tokens: parseTokens(env.CONTENT_TOKENS), + healthcheckUrl: env.HEALTHCHECK_URL || undefined, + notifyWebhookUrl: env.NOTIFY_WEBHOOK_URL || undefined, + allowedEmails: (env.ALLOWED_EMAILS ?? "").split(",").map((s) => s.trim().toLowerCase()).filter(Boolean), + scanMinutes: Number(env.SCAN_MINUTES ?? 5), + }; +} + +/** What each role may do. Checked by the HTTP API and the MCP server alike. */ +export const can = { + readDrafts: (r: Role) => r !== "reader", + write: (r: Role) => r === "agent" || r === "editor" || r === "admin", + review: (r: Role) => r === "editor" || r === "admin", + rollback: (r: Role) => r === "admin", +}; diff --git a/services/content/src/http.ts b/services/content/src/http.ts new file mode 100644 index 0000000..b137fd6 --- /dev/null +++ b/services/content/src/http.ts @@ -0,0 +1,262 @@ +/** + * The HTTP API. The site reads through it with the reader token; the admin + * page and later editing tools read and write through it; agents use the MCP + * server mounted at /mcp. Every write lands in `ContentService.apply` or a + * review method, so the rules are the same whichever door a change comes in by. + */ +import { createReadStream } from "node:fs"; +import { createServer, type IncomingMessage, type ServerResponse } from "node:http"; +import { extname } from "node:path"; +import { z } from "zod"; +import { noteTypes, typeDef } from "@gk/schema"; +import { can, type Config, type Role } from "./config.ts"; +import { handleMcp } from "./mcp.ts"; +import type { Change, ContentService } from "./service.ts"; +import type { Author, StoredNote } from "./store.ts"; +import { heldView, noteView, outcomeView, revisionView, summaryView } from "./views.ts"; + +const MAX_BODY = 1_000_000; + +class HttpError extends Error { + constructor(readonly status: number, message: string) { + super(message); + } +} + +export const authorFor = (role: Role): Author => (role === "agent" ? "agent" : role === "admin" ? "admin" : "greg"); + +const changeSchema = z.discriminatedUnion("op", [ + z.object({ op: z.literal("create"), data: z.record(z.string(), z.unknown()), body: z.string().optional() }), + z.object({ + op: z.literal("update"), + path: z.string(), + fields: z.record(z.string(), z.unknown()).optional(), + body: z.string().optional(), + }), +]); + +const types = { + ".png": "image/png", + ".jpg": "image/jpeg", + ".jpeg": "image/jpeg", + ".webp": "image/webp", + ".gif": "image/gif", + ".svg": "image/svg+xml", + ".avif": "image/avif", + ".pdf": "application/pdf", +} as Record; + +/** Every note type with its JSON Schema, for the admin page, editors and agents. */ +export function schemaList() { + return Object.values(noteTypes).map((def) => ({ + type: def.type, + label: def.label, + description: def.description, + folder: typeof def.folder === "string" ? def.folder : null, + singleton: def.singleton?.fileName ?? null, + body: def.body, + schema: z.toJSONSchema(def.schema, { io: "input", unrepresentable: "any" }), + })); +} + +export function createHttpServer(service: ContentService, config: Pick) { + const { store } = service; + + const visible = (n: StoredNote | null, role: Role) => (n && !n.removed && (n.publish || can.readDrafts(role)) ? n : null); + + async function route(req: IncomingMessage, res: ServerResponse) { + const url = new URL(req.url ?? "/", "http://localhost"); + const path = url.pathname; + const q = (k: string) => url.searchParams.get(k) ?? undefined; + const method = req.method ?? "GET"; + + if (path === "/health") { + return send(res, 200, { ok: true, ...store.count() }); + } + + const role = authenticate(req, config.tokens); + if (!role) throw new HttpError(401, "A valid bearer token is required"); + const need = (ok: boolean) => { + if (!ok) throw new HttpError(403, `The ${role} token cannot do this`); + }; + + if (path === "/mcp") { + need(can.write(role)); + const body = method === "POST" ? await readJson(req) : undefined; + return handleMcp(req, res, body, service, authorFor(role)); + } + + if (method === "GET") { + if (path === "/api/schema") return send(res, 200, schemaList()); + + if (path === "/api/notes") { + const limit = Math.min(Number(q("limit") ?? 100), 1000); + const publishedOnly = !can.readDrafts(role) || q("published") === "true"; + const type = q("type"); + if (type && !typeDef(type)) throw new HttpError(400, `Unknown type "${type}"`); + const query = q("q"); + const notes = query + ? store.search(query, { publishedOnly, limit }).filter((n) => !type || n.type === type) + : store.list({ type, publishedOnly, limit, offset: Number(q("offset") ?? 0) }); + // full=true returns whole notes, so a list page needs one request. + return send(res, 200, notes.map((n) => (q("full") === "true" ? noteView(n) : summaryView(n)))); + } + + if (path === "/api/note") { + const p = q("path"); + const type = q("type"); + const slug = q("slug"); + let note: StoredNote | null = null; + if (p) note = store.get(p); + else if (type && slug) { + note = store.bySlug(type, slug); + if (!visible(note, role)) { + const to = store.redirect(type, slug); + const target = to ? visible(store.get(to), role) : null; + if (target) return send(res, 200, { redirect: { type: target.type, slug: target.slug, path: target.path } }); + } + } else throw new HttpError(400, "Give path, or type and slug"); + const found = visible(note, role); + if (!found) throw new HttpError(404, "No such note"); + return send(res, 200, { + ...noteView(found, { text: can.readDrafts(role) }), + backlinks: store.backlinks(found.name).filter((b) => visible(b, role)).map(summaryView), + ...(can.readDrafts(role) ? { pending: store.pendingFor(found.path) ? heldView(store.pendingFor(found.path)!) : null } : {}), + }); + } + + if (path === "/api/note/history") { + need(can.readDrafts(role)); + return send(res, 200, store.history(required(q("path"), "path")).map((r) => revisionView(r))); + } + + if (path === "/api/note/revision") { + need(can.readDrafts(role)); + const r = store.revision(required(q("path"), "path"), Number(required(q("rev"), "rev"))); + if (!r) throw new HttpError(404, "No such revision"); + return send(res, 200, revisionView(r, { text: true })); + } + + if (path === "/api/backlinks") { + return send(res, 200, store.backlinks(required(q("name"), "name")).filter((b) => visible(b, role)).map(summaryView)); + } + + if (path === "/api/redirects") return send(res, 200, store.redirects()); + + if (path === "/api/problems") { + need(can.readDrafts(role)); + return send(res, 200, service.problems()); + } + + if (path === "/api/held") { + need(can.readDrafts(role)); + const status = (q("status") ?? "pending") as "pending"; + return send(res, 200, store.heldList(status).map((h) => heldView(h))); + } + + const heldOne = path.match(/^\/api\/held\/(\d+)$/); + if (heldOne) { + need(can.readDrafts(role)); + const h = store.heldById(Number(heldOne[1])); + if (!h) throw new HttpError(404, "No such change"); + const current = store.get(h.path); + // The current text comes along so a reviewer can see the difference. + return send(res, 200, { ...heldView(h, { text: true }), current: current && !current.removed ? current.text : null }); + } + + const attachment = path.match(/^\/attachments\/([^/]+)$/); + if (attachment) { + let name = ""; + try { + name = decodeURIComponent(attachment[1] ?? ""); + } catch { + throw new HttpError(400, "Bad attachment name"); + } + const file = name.includes("..") ? null : service.vault.attachmentPath(name); + if (!file) throw new HttpError(404, "No such attachment"); + res.writeHead(200, { + "content-type": types[extname(file).toLowerCase()] ?? "application/octet-stream", + "cache-control": "public, max-age=300", + "x-content-type-options": "nosniff", + }); + createReadStream(file).pipe(res); + return; + } + } + + if (method === "POST") { + if (path === "/api/changes") { + need(can.write(role)); + const parsed = changeSchema.safeParse(await readJson(req)); + if (!parsed.success) throw new HttpError(400, z.prettifyError(parsed.error)); + const outcome = service.apply(parsed.data as Change, authorFor(role)); + return send(res, outcome.status === "rejected" ? 422 : 200, outcomeView(outcome)); + } + + const decide = path.match(/^\/api\/held\/(\d+)\/(accept|reject)$/); + if (decide) { + need(can.review(role)); + const id = Number(decide[1]); + const body = (await readJson(req).catch(() => ({}))) as { restore?: boolean } | undefined; + const outcome = decide[2] === "accept" ? service.acceptHeld(id) : service.rejectHeld(id, { restore: body?.restore === true }); + return send(res, outcome.status === "rejected" ? 422 : 200, outcomeView(outcome)); + } + + if (path === "/api/note/rollback") { + need(can.rollback(role)); + const body = z.object({ path: z.string(), rev: z.number().int() }).safeParse(await readJson(req)); + if (!body.success) throw new HttpError(400, z.prettifyError(body.error)); + const outcome = service.rollback(body.data.path, body.data.rev); + return send(res, outcome.status === "rejected" ? 422 : 200, outcomeView(outcome)); + } + + if (path === "/api/scan") { + need(can.review(role)); + return send(res, 200, service.scan()); + } + } + + throw new HttpError(404, "Not found"); + } + + return createServer((req, res) => { + route(req, res).catch((err: unknown) => { + if (res.headersSent) return res.end(); + if (err instanceof HttpError) return send(res, err.status, { error: err.message }); + console.error(err); + send(res, 500, { error: "Something went wrong in the content service" }); + }); + }); +} + +function authenticate(req: IncomingMessage, tokens: Map): Role | null { + const m = /^Bearer\s+(.+)$/i.exec(req.headers.authorization ?? ""); + return m ? (tokens.get((m[1] ?? "").trim()) ?? null) : null; +} + +function required(v: string | undefined, name: string): string { + if (!v) throw new HttpError(400, `${name} is required`); + return v; +} + +async function readJson(req: IncomingMessage): Promise { + let size = 0; + const chunks: Buffer[] = []; + for await (const chunk of req) { + size += (chunk as Buffer).length; + if (size > MAX_BODY) throw new HttpError(413, "The request is too large"); + chunks.push(chunk as Buffer); + } + const text = Buffer.concat(chunks).toString("utf8"); + if (!text) return undefined; + try { + return JSON.parse(text); + } catch { + throw new HttpError(400, "The body is not valid JSON"); + } +} + +function send(res: ServerResponse, status: number, body: unknown) { + res.writeHead(status, { "content-type": "application/json; charset=utf-8", "cache-control": "no-store" }); + res.end(JSON.stringify(body)); +} diff --git a/services/content/src/main.ts b/services/content/src/main.ts new file mode 100644 index 0000000..b290a89 --- /dev/null +++ b/services/content/src/main.ts @@ -0,0 +1,54 @@ +/** + * Starts the content service: open the store, scan the Site folder, watch it, + * and serve the API and the MCP server. + */ +import { loadConfig } from "./config.ts"; +import { createHttpServer } from "./http.ts"; +import { ContentService } from "./service.ts"; +import { Store } from "./store.ts"; +import { Vault } from "./vault.ts"; +import { Watcher } from "./watcher.ts"; +import { heldView } from "./views.ts"; + +const config = loadConfig(); +const store = new Store(config.dbFile); +const service = new ContentService({ + store, + vault: new Vault(config.siteDir), + rules: { allowedEmails: config.allowedEmails }, + onHold: (held) => { + console.log(`Held ${held.path}: ${held.reasons.map((r) => r.code).join(", ") || "waiting for review"}`); + if (config.notifyWebhookUrl) { + fetch(config.notifyWebhookUrl, { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify({ event: "held", held: heldView(held) }), + }).catch((err) => console.error("Notify failed:", err)); + } + }, +}); + +const watcher = new Watcher(service, config.siteDir, { + scanMinutes: config.scanMinutes, + onScan: (r) => { + if (r.changed) console.log(`Scan: ${r.changed} changed, ${r.held} held`); + if (config.healthcheckUrl) fetch(config.healthcheckUrl).catch(() => {}); + }, + onError: (err) => console.error("Watcher:", err), +}); +watcher.start(); + +const server = createHttpServer(service, config); +server.listen(config.port, config.host, () => { + const c = store.count(); + console.log(`Content service on ${config.host}:${config.port}: ${c.notes} notes, ${c.published} published, ${c.held} held`); +}); + +const stop = () => { + watcher.stop(); + server.close(); + store.close(); + process.exit(0); +}; +process.on("SIGTERM", stop); +process.on("SIGINT", stop); diff --git a/services/content/src/mcp.ts b/services/content/src/mcp.ts new file mode 100644 index 0000000..fcfe11e --- /dev/null +++ b/services/content/src/mcp.ts @@ -0,0 +1,229 @@ +/** + * The MCP server agents use. It is one more client of the write API: every + * tool that changes something calls `ContentService.apply`, so agents meet + * the same rules as everyone else, plus the agent rules. + * + * Agents can find, read and add. They cannot delete, rename, move or write a + * raw file, and a draft post always starts unpublished. + */ +import type { IncomingMessage, ServerResponse } from "node:http"; +import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; +import { z } from "zod"; +import { libraryStates, media, noteTypes, topics, typeDef } from "@gk/schema"; +import type { Change, ContentService, Outcome } from "./service.ts"; +import type { Author } from "./store.ts"; +import { heldView, noteView, outcomeView, summaryView } from "./views.ts"; + +const json = (value: unknown) => ({ content: [{ type: "text" as const, text: JSON.stringify(value, null, 2) }] }); +const fail = (message: string) => ({ content: [{ type: "text" as const, text: message }], isError: true }); + +/** Turn an outcome into a tool result an agent can act on. */ +function result(o: Outcome) { + if (o.status === "rejected") return fail(`Not saved:\n${o.reasons.map((r) => `- ${r.code}: ${r.message}`).join("\n")}`); + if (o.status === "held") { + return json({ + ...outcomeView(o), + message: "Saved for Greg to review. It is not live yet. Do not retry; the change is waiting.", + }); + } + return json(outcomeView(o)); +} + +const today = () => new Date().toISOString().slice(0, 10); + +export function createMcpServer(service: ContentService, author: Author) { + const { store } = service; + const server = new McpServer( + { name: "gregkilmer-content", version: "0.1.0" }, + { + instructions: + "Content for GregKilmer.com. Search and read freely. Writes go through rules: some are saved straight away, " + + "others wait for Greg's review, and a held change should not be retried. Call get_schema before writing a type " + + "for the first time. Never invent facts, numbers or dates you were not given.", + }, + ); + const apply = (change: Change) => result(service.apply(change, author)); + + server.registerTool( + "search", + { + title: "Search notes", + description: "Full-text search over every note's title and body, drafts included. Optionally limit to one type.", + inputSchema: { + query: z.string().min(1), + type: z.enum(Object.keys(noteTypes) as [string, ...string[]]).optional(), + limit: z.number().int().min(1).max(50).optional(), + }, + annotations: { readOnlyHint: true }, + }, + async ({ query, type, limit }) => + json(store.search(query, { limit: (limit ?? 10) * (type ? 5 : 1) }).filter((n) => !type || n.type === type).slice(0, limit ?? 10).map(summaryView)), + ); + + server.registerTool( + "get_note", + { + title: "Read a note", + description: "One note by its path (like Library/Books/Mistborn.md) or its name (the file name without .md), with what links to it.", + inputSchema: { path: z.string().optional(), name: z.string().optional() }, + annotations: { readOnlyHint: true }, + }, + async ({ path, name }) => { + const n = path ? store.get(path) : name ? store.byName(name) : null; + if (!n || n.removed) return fail("No such note. Use search to find it."); + const pending = store.pendingFor(n.path); + return json({ + ...noteView(n), + backlinks: store.backlinks(n.name).map(summaryView), + pending: pending ? heldView(pending) : null, + }); + }, + ); + + server.registerTool( + "get_schema", + { + title: "Note types", + description: "Without a type: every note type and what it is for. With a type: its fields as JSON Schema.", + inputSchema: { type: z.enum(Object.keys(noteTypes) as [string, ...string[]]).optional() }, + annotations: { readOnlyHint: true }, + }, + async ({ type }) => { + if (!type) return json(Object.values(noteTypes).map((d) => ({ type: d.type, label: d.label, description: d.description }))); + const def = typeDef(type)!; + return json({ + type: def.type, + description: def.description, + body: def.body, + schema: z.toJSONSchema(def.schema, { io: "input", unrepresentable: "any" }), + }); + }, + ); + + server.registerTool( + "list_held", + { + title: "Changes waiting for review", + description: "Changes held for Greg's review, with the reasons. Useful before retrying anything.", + inputSchema: {}, + annotations: { readOnlyHint: true }, + }, + async () => json(store.heldList("pending").map((h) => heldView(h))), + ); + + server.registerTool( + "set_status", + { + title: "Post a status", + description: + "Add a line to the Now status log, like reading a book or starting a build. kind must be one of the kinds in Now/_kinds. " + + "subject links the note it is about, by name.", + inputSchema: { + kind: z.string().min(1), + title: z.string().min(1), + note: z.string().optional(), + subject: z.string().optional().describe("Name of the note this is about, without brackets"), + date: z.string().optional().describe("YYYY-MM-DD, default today"), + publish: z.boolean().optional().describe("Default true"), + }, + }, + async ({ kind, title, note, subject, date, publish }) => { + const kinds = store.list({ type: "status-kinds" })[0]?.data.kinds as { label: string }[] | undefined; + if (kinds && !kinds.some((k) => k.label.toLowerCase() === kind.toLowerCase())) { + return fail(`Unknown kind "${kind}". Kinds: ${kinds.map((k) => k.label).join(", ")}`); + } + const label = kinds?.find((k) => k.label.toLowerCase() === kind.toLowerCase())?.label ?? kind; + return apply({ + op: "create", + data: { + type: "status", + title, + kind: label, + date: date ?? today(), + ...(note ? { note } : {}), + ...(subject ? { subject: `[[${subject.replace(/^\[\[|\]\]$/g, "")}]]` } : {}), + publish: publish ?? true, + }, + }); + }, + ); + + server.registerTool( + "add_library_item", + { + title: "Add to the Library", + description: "Add a book, series, show, film or game. To change one that exists, use update_fields.", + inputSchema: { + title: z.string().min(1), + medium: z.enum(media), + state: z.enum(libraryStates), + creator: z.string().optional(), + group: z.string().optional(), + year: z.number().int().optional(), + platform: z.string().optional(), + verdict: z.string().optional(), + note: z.string().optional(), + progress: z.string().optional(), + anilistId: z.number().int().optional(), + publish: z.boolean().optional().describe("Default true"), + }, + }, + async ({ publish, ...fields }) => { + const existing = store.search(fields.title, { limit: 5 }).find((n) => n.type === "library-item" && n.title.toLowerCase() === fields.title.toLowerCase()); + if (existing) return fail(`Already in the Library at ${existing.path}. Use update_fields to change it.`); + const data = Object.fromEntries(Object.entries(fields).filter(([, v]) => v !== undefined)); + return apply({ op: "create", data: { type: "library-item", ...data, publish: publish ?? true } }); + }, + ); + + server.registerTool( + "update_fields", + { + title: "Change a note's fields", + description: + "Change some fields of an existing note; fields not named stay as they are, and null removes one. " + + "Give body only to replace the whole body. Greg's own notes need agentEdits: true before an agent may change them.", + inputSchema: { + path: z.string(), + fields: z.record(z.string(), z.unknown()), + body: z.string().optional(), + }, + }, + async ({ path, fields, body }) => apply({ op: "update", path, fields, ...(body !== undefined ? { body } : {}) }), + ); + + server.registerTool( + "draft_post", + { + title: "Draft a blog post", + description: "Start a blog post as an unpublished draft for Greg to finish. It never goes live from here.", + inputSchema: { + title: z.string().min(1), + topic: z.enum(topics), + summary: z.string().min(1), + body: z.string().min(1), + postType: z.enum(["article", "build log", "case study"]).optional(), + tags: z.array(z.string()).optional(), + }, + }, + async ({ body, ...fields }) => { + const data = Object.fromEntries(Object.entries(fields).filter(([, v]) => v !== undefined)); + return apply({ op: "create", data: { type: "post", date: today(), ...data, publish: false }, body }); + }, + ); + + return server; +} + +/** One stateless MCP exchange per request: nothing is kept between calls. */ +export async function handleMcp(req: IncomingMessage, res: ServerResponse, body: unknown, service: ContentService, author: Author) { + const server = createMcpServer(service, author); + const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined, enableJsonResponse: true }); + res.on("close", () => { + void transport.close(); + void server.close(); + }); + await server.connect(transport); + await transport.handleRequest(req, res, body); +} diff --git a/services/content/src/rules.ts b/services/content/src/rules.ts new file mode 100644 index 0000000..782ed8c --- /dev/null +++ b/services/content/src/rules.ts @@ -0,0 +1,153 @@ +/** + * The rules every change runs, in a fixed order. Pure functions: the same + * change against the same accepted state always gets the same answer, and no + * model decides anything. + * + * The rules live here in code, not in the vault, so no agent can loosen them. + */ +import { notePath, typeDef, type CheckedNote } from "@gk/schema"; +import type { Author, HeldReason, StoredNote } from "./store.ts"; + +export interface RuleSettings { + /** A note with at least this much body text… */ + textLossMinChars: number; + /** …is held when an edit keeps less than this share of it. */ + textLossKeepRatio: number; + /** An agent making more changes than this inside the window is held. */ + agentBatchLimit: number; + agentBatchWindowMinutes: number; + /** Note types an agent may publish without review. */ + agentAutoPublish: string[]; + /** Email addresses that may appear in notes. */ + allowedEmails: string[]; +} + +export const defaultRules: RuleSettings = { + textLossMinChars: 400, + textLossKeepRatio: 0.5, + agentBatchLimit: 10, + agentBatchWindowMinutes: 10, + agentAutoPublish: ["status", "library-item"], + allowedEmails: [], +}; + +export interface Proposed { + path: string; + name: string; + slug: string; + text: string; + author: Author; + checked: CheckedNote; +} + +export interface RuleContext { + /** The accepted version at this path, if any. */ + current: StoredNote | null; + /** Another live note with the same file name, if any. */ + nameTakenBy: string | null; + /** Another live note of the same type with the same slug, if any. */ + slugTakenBy: string | null; + /** The agent's changes inside the batch window, before this one. */ + recentAgentChanges: number; + settings: RuleSettings; +} + +export type Verdict = { decision: "accept" } | { decision: "hold"; reasons: HeldReason[] }; + +/** Strings that look like credentials. Matching is on new text only. */ +const SECRET_PATTERNS: [string, RegExp][] = [ + ["a private key", /-----BEGIN [A-Z ]*PRIVATE KEY-----/], + ["an AWS key", /\bAKIA[0-9A-Z]{16}\b/], + ["a GitHub token", /\b(?:ghp|gho|ghs|ghu|ghr)_[A-Za-z0-9]{30,}\b|\bgithub_pat_[A-Za-z0-9_]{30,}\b/], + ["a Slack token", /\bxox[abpors]-[A-Za-z0-9-]{10,}\b/], + ["an API key", /\bsk-[A-Za-z0-9_-]{20,}\b/], + ["a bearer token", /\bBearer\s+[A-Za-z0-9._~+/-]{20,}=*/], + ["a password or key value", /\b(?:password|passwd|secret|api[_-]?key|token)\s*[:=]\s*["']?[^\s"']{8,}/i], +]; +const EMAIL = /[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}/g; +const PHONE = /(?:\+?1[\s.-]?)?\(?\b\d{3}\)?[\s.-]\d{3}[\s.-]\d{4}\b/g; + +function newMatches(re: RegExp, text: string, before: string): string[] { + const flags = re.flags.includes("g") ? re.flags : `${re.flags}g`; + const g = new RegExp(re.source, flags); + const old = new Set(before.match(g) ?? []); + return [...new Set(text.match(g) ?? [])].filter((m) => !old.has(m)); +} + +const bodyOf = (text: string) => text.replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/, "").trim(); + +export function evaluate(p: Proposed, ctx: RuleContext): Verdict { + const reasons: HeldReason[] = []; + const { current, settings } = ctx; + const live = current && !current.removed ? current : null; + + // 1. The note must be valid. Nothing else is judged on a broken note. + if (!p.checked.ok) { + return { + decision: "hold", + reasons: [ + { + code: "invalid", + message: p.checked.problems.map((x) => (x.field ? `${x.field}: ${x.message}` : x.message)).join("; "), + }, + ], + }; + } + const data = p.checked.data; + + // 2. Settings notes live at one fixed path, so there is only ever one. + if (typeDef(p.checked.type)?.singleton && notePath(data) !== p.path) { + reasons.push({ code: "misplaced", message: `A ${p.checked.type} note belongs at ${notePath(data)}` }); + } + + // 3. Names and slugs must stay unambiguous. + if (ctx.nameTakenBy) { + reasons.push({ code: "duplicate-name", message: `Another note already uses this file name: ${ctx.nameTakenBy}` }); + } + if (ctx.slugTakenBy) { + reasons.push({ code: "duplicate-slug", message: `Another ${p.checked.type} already uses the address "${p.slug}": ${ctx.slugTakenBy}` }); + } + + // 4. A large loss of text in one edit. + if (live) { + const before = bodyOf(live.text).length; + const after = bodyOf(p.text).length; + if (before >= settings.textLossMinChars && after < before * settings.textLossKeepRatio) { + reasons.push({ + code: "text-loss", + message: `The text shrank from ${before} to ${after} characters in one edit`, + }); + } + } + + // 5. Anything that looks like a secret, or contact details from an agent. + const before = live?.text ?? ""; + for (const [what, re] of SECRET_PATTERNS) { + if (newMatches(re, p.text, before).length) reasons.push({ code: "secret", message: `The note now contains what looks like ${what}` }); + } + if (p.author === "agent") { + const allowed = new Set(settings.allowedEmails.map((e) => e.toLowerCase())); + const emails = newMatches(EMAIL, p.text, before).filter((e) => !allowed.has(e.toLowerCase())); + if (emails.length) reasons.push({ code: "contact", message: `An agent added an email address: ${emails.join(", ")}` }); + if (newMatches(PHONE, p.text, before).length) reasons.push({ code: "contact", message: "An agent added a phone number" }); + } + + // 6. Agents stay inside their lane. + if (p.author === "agent") { + if (live && live.author !== "agent" && live.data.agentEdits !== true) { + reasons.push({ code: "agent-edit", message: "An agent changed a note you wrote, which needs your review" }); + } + const publishing = data.publish === true && !(live?.publish ?? false); + if (publishing && !settings.agentAutoPublish.includes(p.checked.type)) { + reasons.push({ code: "agent-publish", message: `An agent wants to publish a ${p.checked.type}, which needs your approval` }); + } + if (ctx.recentAgentChanges >= settings.agentBatchLimit) { + reasons.push({ + code: "batch", + message: `More than ${settings.agentBatchLimit} agent changes in ${settings.agentBatchWindowMinutes} minutes`, + }); + } + } + + return reasons.length ? { decision: "hold", reasons } : { decision: "accept" }; +} diff --git a/services/content/src/service.ts b/services/content/src/service.ts new file mode 100644 index 0000000..c5ba942 --- /dev/null +++ b/services/content/src/service.ts @@ -0,0 +1,335 @@ +/** + * The content service core. Every change, from the vault or from the write + * API, goes through `propose`, which runs the rules and either accepts the + * change into the store or holds it for review. + * + * The HTTP API, the MCP server, the admin page and any later editing tool all + * call these methods; none of them write notes any other way. + */ +import { basename } from "node:path"; +import { checkNote, checkVault, formatNote, notePath, outgoingLinks, slugify, typeDef, type CheckedNote } from "@gk/schema"; +import { defaultRules, evaluate, type RuleSettings } from "./rules.ts"; +import type { Author, HeldChange, HeldReason, StoredNote, Store } from "./store.ts"; +import { hashText, isNotePath, type Vault } from "./vault.ts"; + +export type Outcome = + | { status: "accepted"; path: string; note: StoredNote } + | { status: "held"; path: string; held: HeldChange } + | { status: "rejected"; path: string | null; reasons: HeldReason[] } + | { status: "unchanged"; path: string }; + +export type Change = + | { op: "create"; data: Record; body?: string } + | { op: "update"; path: string; fields?: Record; body?: string }; + +export interface ServiceOptions { + store: Store; + vault: Vault; + rules?: Partial; + /** Injectable clock, for tests. */ + now?: () => Date; + /** Called whenever a change is held, for notifications. */ + onHold?: (held: HeldChange) => void; +} + +/** How long after a note disappears a new note can still claim its old address. */ +const MOVE_WINDOW_MS = 10 * 60_000; + +export class ContentService { + readonly store: Store; + readonly vault: Vault; + readonly rules: RuleSettings; + private readonly now: () => Date; + private readonly onHold?: (held: HeldChange) => void; + + constructor(o: ServiceOptions) { + this.store = o.store; + this.vault = o.vault; + this.rules = { ...defaultRules, ...o.rules }; + this.now = o.now ?? (() => new Date()); + this.onHold = o.onHold; + } + + private iso = () => this.now().toISOString(); + private today = () => this.iso().slice(0, 10); + + // ------------------------------------------------------------ from the vault + + /** Read one file from the vault and take it in if it changed. */ + ingest(path: string): Outcome { + const text = this.vault.read(path); + if (text === null) return this.removed(path); + const hash = hashText(text); + if (this.store.seenHash(path) === hash) return { status: "unchanged", path }; + this.store.markSeen(path, hash); + const current = this.store.get(path); + if (current && !current.removed && current.hash === hash) return { status: "unchanged", path }; + return this.propose(path, text, "greg", "vault"); + } + + /** The file is gone. The note leaves the site; its history stays. */ + removed(path: string): Outcome { + this.store.markSeen(path, null); + this.store.supersede(path, this.iso()); + const note = this.store.remove(path, "greg", this.iso()); + return note ? { status: "accepted", path, note } : { status: "unchanged", path }; + } + + /** + * Compare the whole folder with what was seen before. Removals go first, so + * a note moved between folders frees its name before it is read again. + */ + scan(): { changed: number; held: number } { + const present = new Set(this.vault.listNotes()); + let changed = 0; + let held = 0; + for (const path of this.store.seenPaths()) { + if (!present.has(path)) { + this.removed(path); + changed++; + } + } + for (const path of present) { + const o = this.ingest(path); + if (o.status !== "unchanged") changed++; + if (o.status === "held") held++; + } + return { changed, held }; + } + + // ------------------------------------------------------------ the write API + + /** One change from the API, made as `author`. Accepted changes are written into the vault. */ + apply(change: Change, author: Author): Outcome { + let data: Record; + let body: string; + let path: string; + + if (change.op === "create") { + data = { ...change.data, publish: change.data.publish ?? false, created: change.data.created ?? this.today() }; + body = change.body ?? ""; + if (!typeDef(String(data.type))) return this.reject(null, "invalid", `Unknown type "${String(data.type)}"`); + if (typeof data.title !== "string" || !data.title.trim()) return this.reject(null, "invalid", "title: A title is required"); + path = notePath(data); + if (this.vault.exists(path) || (this.store.get(path) && !this.store.get(path)!.removed)) { + return this.reject(path, "exists", `A note already exists at ${path}; update it instead`); + } + } else { + path = change.path; + if (!isNotePath(path) || path.startsWith("/") || path.split("/").includes("..")) { + return this.reject(null, "invalid", "path: Not a note path inside the Site folder"); + } + // Take in any edit made in Obsidian first, so it is never overwritten. + this.ingest(path); + const current = this.store.get(path); + if (!current || current.removed) return this.reject(path, "missing", `No note at ${path}`); + if (this.store.pendingFor(path)) { + return this.reject(path, "waiting", "This note has a change waiting for review; decide on that first"); + } + data = { ...current.data }; + for (const [k, v] of Object.entries(change.fields ?? {})) { + if (k === "type") return this.reject(path, "invalid", "type: A note's type cannot change"); + if (v === null) delete data[k]; + else data[k] = v; + } + body = change.body ?? current.body; + } + + data.updated = this.today(); + data.updatedBy = author; + const text = formatNote(data, body); + return this.propose(path, text, author, "api"); + } + + // ------------------------------------------------------------ review + + acceptHeld(id: number): Outcome { + const h = this.store.heldById(id); + if (!h || h.status !== "pending") return this.reject(h?.path ?? null, "missing", "Nothing is waiting with that id"); + const checked = checkNote(h.text); + if (!checked.ok) { + return this.reject(h.path, "invalid", "This change is not a valid note, so it cannot go live. Fix the note in Obsidian."); + } + const clash = this.clashes(h.path, checked); + if (clash.length) return { status: "rejected", path: h.path, reasons: clash }; + if (h.source === "api") { + const onDisk = this.vault.read(h.path); + const current = this.store.get(h.path); + if (onDisk !== null && hashText(onDisk) !== (current && !current.removed ? current.hash : null)) { + return this.reject(h.path, "conflict", "The note changed in Obsidian since this was proposed"); + } + this.writeToVault(h.path, h.text); + } + const note = this.acceptText(h.path, h.text, checked, h.author, "accept"); + this.store.decide(id, "accepted", this.iso()); + return { status: "accepted", path: h.path, note }; + } + + /** + * Turn a held change down. With `restore`, a held edit from Obsidian is + * replaced in the vault by the version the site is showing. + */ + rejectHeld(id: number, opts: { restore?: boolean } = {}): Outcome { + const h = this.store.heldById(id); + if (!h || h.status !== "pending") return this.reject(h?.path ?? null, "missing", "Nothing is waiting with that id"); + this.store.decide(id, "rejected", this.iso()); + const current = this.store.get(h.path); + if (opts.restore && h.source === "vault" && current && !current.removed) this.writeToVault(h.path, current.text); + return current ? { status: "accepted", path: h.path, note: current } : { status: "unchanged", path: h.path }; + } + + /** Put an earlier accepted version back, in the vault and on the site. */ + rollback(path: string, rev: number): Outcome { + const r = this.store.revision(path, rev); + if (!r) return this.reject(path, "missing", `No revision ${rev} for ${path}`); + const checked = checkNote(r.text); + if (!checked.ok) return this.reject(path, "invalid", "That version no longer passes the note rules"); + const clash = this.clashes(path, checked); + if (clash.length) return { status: "rejected", path, reasons: clash }; + this.store.supersede(path, this.iso()); + this.writeToVault(path, r.text); + const note = this.acceptText(path, r.text, checked, "admin", "rollback"); + return { status: "accepted", path, note }; + } + + // ------------------------------------------------------------ reading + + /** Problems across the whole folder: links to notes that do not exist, and the like. */ + problems() { + const notes = this.store.list().map((n) => ({ path: n.path, type: n.type, data: n.data, body: n.body })); + return checkVault(notes, this.vault.listAttachments()); + } + + // ------------------------------------------------------------ internals + + private reject(path: string | null, code: string, message: string): Outcome { + return { status: "rejected", path, reasons: [{ code, message }] }; + } + + private slugOf(checked: CheckedNote): string { + if (!checked.ok) return ""; + return String(checked.data.slug ?? slugify(String(checked.data.title))); + } + + /** Name and slug clashes with other live notes. */ + private clashes(path: string, checked: CheckedNote): HeldReason[] { + const reasons: HeldReason[] = []; + if (!checked.ok) return reasons; + const other = this.liveOther(this.store.byName(basename(path, ".md")), path); + if (other) reasons.push({ code: "duplicate-name", message: `Another note already uses this file name: ${other}` }); + const def = typeDef(checked.type); + if (!def?.singleton) { + const s = this.liveOther(this.store.bySlug(checked.type, this.slugOf(checked)), path); + if (s) reasons.push({ code: "duplicate-slug", message: `Another ${checked.type} already uses this address: ${s}` }); + } + return reasons; + } + + /** Another live note's path, after making sure that note's file still exists. */ + private liveOther(note: StoredNote | null, path: string): string | null { + if (!note || note.path === path) return null; + if (!this.vault.exists(note.path)) { + // Its file is gone (moved or deleted) and the watcher has not caught up. + this.removed(note.path); + return null; + } + return note.path; + } + + private propose(path: string, text: string, author: Author, source: "vault" | "api"): Outcome { + const checked = checkNote(text); + const name = basename(path, ".md"); + const slug = this.slugOf(checked); + const window = new Date(this.now().getTime() - this.rules.agentBatchWindowMinutes * 60_000).toISOString(); + const current = this.store.get(path); + const nameTakenBy = checked.ok ? this.liveOther(this.store.byName(name), path) : null; + const slugTakenBy = + checked.ok && !typeDef(checked.type)?.singleton ? this.liveOther(this.store.bySlug(checked.type, slug), path) : null; + + const verdict = evaluate( + { path, name, slug, text, author, checked }, + { + current, + nameTakenBy, + slugTakenBy, + recentAgentChanges: author === "agent" ? this.store.changesSince("agent", window) : 0, + settings: this.rules, + }, + ); + + if (verdict.decision === "accept" && checked.ok) { + if (source === "api") this.writeToVault(path, text); + const note = this.acceptText(path, text, checked, author, "accept"); + return { status: "accepted", path, note }; + } + if (verdict.decision === "hold" && source === "api" && verdict.reasons.some((r) => r.code === "invalid")) { + // An API caller gets the problems back at once instead of a broken note in the queue. + return { status: "rejected", path, reasons: verdict.reasons }; + } + const reasons = verdict.decision === "hold" ? verdict.reasons : []; + const held = this.store.hold({ + path, + author, + source, + text, + baseHash: current && !current.removed ? current.hash : null, + reasons, + at: this.iso(), + }); + this.onHold?.(held); + return { status: "held", path, held }; + } + + private writeToVault(path: string, text: string) { + // Mark it seen first, so the watcher recognises the service's own write. + this.store.markSeen(path, hashText(text)); + this.vault.write(path, text); + } + + private acceptText(path: string, text: string, checked: CheckedNote & { ok: true }, author: Author, action: "accept" | "rollback"): StoredNote { + const at = this.iso(); + const data = checked.data; + const slug = this.slugOf(checked); + const hash = hashText(text); + const vaultNote = { path, type: checked.type, data, body: checked.body }; + + return this.store.transaction(() => { + const prev = this.store.get(path); + // The old address keeps working after a rename or a slug change. + if (prev && !prev.removed && prev.type === checked.type && prev.slug !== slug) { + this.store.addRedirect(prev.type, prev.slug, path, at); + } + if (!prev || prev.removed) { + const since = new Date(this.now().getTime() - MOVE_WINDOW_MS).toISOString(); + const moved = this.store + .recentlyRemoved(since) + .find((n) => n.path !== path && n.type === checked.type && (n.hash === hash || n.title === data.title)); + if (moved) { + this.store.retarget(moved.path, path); + if (moved.slug !== slug) this.store.addRedirect(moved.type, moved.slug, path, at); + } + } + // A slug in use again belongs to its note, not to a redirect. + this.store.dropRedirect(checked.type, slug); + this.store.supersede(path, at); + return this.store.accept( + { + path, + type: checked.type, + name: basename(path, ".md"), + slug, + title: String(data.title), + publish: data.publish === true, + data, + body: checked.body, + text, + hash, + author, + acceptedAt: at, + }, + action, + outgoingLinks(vaultNote), + ); + }); + } +} diff --git a/services/content/src/store.ts b/services/content/src/store.ts new file mode 100644 index 0000000..f64569b --- /dev/null +++ b/services/content/src/store.ts @@ -0,0 +1,382 @@ +/** + * The accepted store: the last good version of every note, its history, + * changes waiting for review, redirects, links and search. + * + * Everything the site shows comes from here, never from the vault directly. + * The history and review queue cannot be rebuilt from the vault, so this + * database carries the backup.dump=sqlite label on Cosmos. + */ +import { DatabaseSync } from "node:sqlite"; +import { mkdirSync } from "node:fs"; +import { dirname } from "node:path"; + +export type Author = "greg" | "agent" | "admin"; + +export interface StoredNote { + path: string; + type: string; + name: string; + slug: string; + title: string; + publish: boolean; + removed: boolean; + data: Record; + body: string; + text: string; + hash: string; + author: Author; + acceptedAt: string; + rev: number; +} + +export interface Revision { + rev: number; + path: string; + hash: string; + text: string; + author: Author; + at: string; + action: "accept" | "remove" | "rollback" | "restore"; +} + +export interface HeldReason { + code: string; + message: string; +} + +export interface HeldChange { + id: number; + path: string; + author: Author; + source: "vault" | "api"; + /** The proposed note text. */ + text: string; + /** Hash of the accepted version this change was made against, if any. */ + baseHash: string | null; + reasons: HeldReason[]; + at: string; + status: "pending" | "accepted" | "rejected" | "superseded"; + decidedAt: string | null; +} + +const SCHEMA = ` +create table if not exists notes ( + path text primary key, + type text not null, + name text not null, + slug text not null, + title text not null, + publish integer not null, + removed integer not null default 0, + data text not null, + body text not null, + text text not null, + hash text not null, + author text not null, + accepted_at text not null, + rev integer not null +); +create index if not exists notes_type on notes(type, publish, removed); +create index if not exists notes_name on notes(name collate nocase); +create table if not exists revisions ( + path text not null, + rev integer not null, + hash text not null, + text text not null, + author text not null, + at text not null, + action text not null, + primary key (path, rev) +); +create table if not exists held ( + id integer primary key autoincrement, + path text not null, + author text not null, + source text not null, + text text not null, + base_hash text, + reasons text not null, + at text not null, + status text not null default 'pending', + decided_at text +); +create index if not exists held_status on held(status, path); +create table if not exists redirects ( + type text not null, + from_slug text not null, + to_path text not null, + at text not null, + primary key (type, from_slug) +); +create table if not exists links ( + from_path text not null, + to_name text not null collate nocase, + primary key (from_path, to_name) +); +create table if not exists seen ( + path text primary key, + hash text not null +); +create virtual table if not exists search using fts5(path unindexed, title, body, tokenize = 'porter unicode61'); +`; + +type Row = Record; + +const toNote = (r: Row): StoredNote => ({ + path: r.path, + type: r.type, + name: r.name, + slug: r.slug, + title: r.title, + publish: !!r.publish, + removed: !!r.removed, + data: JSON.parse(r.data), + body: r.body, + text: r.text, + hash: r.hash, + author: r.author, + acceptedAt: r.accepted_at, + rev: r.rev, +}); + +const toHeld = (r: Row): HeldChange => ({ + id: r.id, + path: r.path, + author: r.author, + source: r.source, + text: r.text, + baseHash: r.base_hash, + reasons: JSON.parse(r.reasons), + at: r.at, + status: r.status, + decidedAt: r.decided_at, +}); + +export class Store { + readonly db: DatabaseSync; + + constructor(file: string) { + if (file !== ":memory:") mkdirSync(dirname(file), { recursive: true }); + this.db = new DatabaseSync(file); + this.db.exec("pragma journal_mode = wal; pragma foreign_keys = on;"); + this.db.exec(SCHEMA); + } + + close() { + this.db.close(); + } + + transaction(fn: () => T): T { + this.db.exec("begin"); + try { + const out = fn(); + this.db.exec("commit"); + return out; + } catch (e) { + this.db.exec("rollback"); + throw e; + } + } + + // ------------------------------------------------------------ notes + + get(path: string): StoredNote | null { + const r = this.db.prepare("select * from notes where path = ?").get(path) as Row | undefined; + return r ? toNote(r) : null; + } + + byName(name: string): StoredNote | null { + const r = this.db.prepare("select * from notes where name = ? collate nocase and removed = 0").get(name) as Row | undefined; + return r ? toNote(r) : null; + } + + bySlug(type: string, slug: string): StoredNote | null { + const r = this.db.prepare("select * from notes where type = ? and slug = ? and removed = 0").get(type, slug) as Row | undefined; + return r ? toNote(r) : null; + } + + list(opts: { type?: string; publishedOnly?: boolean; includeRemoved?: boolean; limit?: number; offset?: number } = {}): StoredNote[] { + const where: string[] = []; + const args: (string | number)[] = []; + if (opts.type) { + where.push("type = ?"); + args.push(opts.type); + } + if (opts.publishedOnly) where.push("publish = 1"); + if (!opts.includeRemoved) where.push("removed = 0"); + const sql = `select * from notes ${where.length ? `where ${where.join(" and ")}` : ""} order by path limit ? offset ?`; + return (this.db.prepare(sql).all(...args, opts.limit ?? 10_000, opts.offset ?? 0) as Row[]).map(toNote); + } + + count(): { notes: number; published: number; held: number } { + const c = this.db.prepare("select count(*) n, sum(publish) p from notes where removed = 0").get() as Row; + const h = this.db.prepare("select count(*) n from held where status = 'pending'").get() as Row; + return { notes: c.n, published: c.p ?? 0, held: h.n }; + } + + /** Save an accepted version and its revision. */ + accept(note: Omit, action: Revision["action"], links: string[]): StoredNote { + const prev = this.get(note.path); + const rev = (prev?.rev ?? 0) + 1; + this.db + .prepare( + `insert into notes (path, type, name, slug, title, publish, removed, data, body, text, hash, author, accepted_at, rev) + values (?, ?, ?, ?, ?, ?, 0, ?, ?, ?, ?, ?, ?, ?) + on conflict(path) do update set type = excluded.type, name = excluded.name, slug = excluded.slug, + title = excluded.title, publish = excluded.publish, removed = 0, data = excluded.data, body = excluded.body, + text = excluded.text, hash = excluded.hash, author = excluded.author, accepted_at = excluded.accepted_at, rev = excluded.rev`, + ) + .run( + note.path, note.type, note.name, note.slug, note.title, note.publish ? 1 : 0, JSON.stringify(note.data), + note.body, note.text, note.hash, note.author, note.acceptedAt, rev, + ); + this.db + .prepare("insert into revisions (path, rev, hash, text, author, at, action) values (?, ?, ?, ?, ?, ?, ?)") + .run(note.path, rev, note.hash, note.text, note.author, note.acceptedAt, action); + this.db.prepare("delete from links where from_path = ?").run(note.path); + const addLink = this.db.prepare("insert or ignore into links (from_path, to_name) values (?, ?)"); + for (const l of links) addLink.run(note.path, l); + this.db.prepare("delete from search where path = ?").run(note.path); + this.db.prepare("insert into search (path, title, body) values (?, ?, ?)").run(note.path, note.title, note.body); + return this.get(note.path)!; + } + + /** The file is gone from the vault: hide the note, keep its history. */ + remove(path: string, author: Author, at: string): StoredNote | null { + const prev = this.get(path); + if (!prev || prev.removed) return prev; + const rev = prev.rev + 1; + this.db.prepare("update notes set removed = 1, rev = ?, accepted_at = ?, author = ? where path = ?").run(rev, at, author, path); + this.db + .prepare("insert into revisions (path, rev, hash, text, author, at, action) values (?, ?, ?, ?, ?, ?, 'remove')") + .run(path, rev, prev.hash, prev.text, author, at); + this.db.prepare("delete from links where from_path = ?").run(path); + this.db.prepare("delete from search where path = ?").run(path); + return this.get(path); + } + + /** Notes removed recently, newest first; used to recognise a move. */ + recentlyRemoved(since: string): StoredNote[] { + return (this.db.prepare("select * from notes where removed = 1 and accepted_at >= ? order by accepted_at desc").all(since) as Row[]).map(toNote); + } + + history(path: string): Revision[] { + return this.db.prepare("select * from revisions where path = ? order by rev desc").all(path) as unknown as Revision[]; + } + + revision(path: string, rev: number): Revision | null { + return (this.db.prepare("select * from revisions where path = ? and rev = ?").get(path, rev) as unknown as Revision) ?? null; + } + + /** Accepted changes by an author since a time, for the batch guard. */ + changesSince(author: Author, since: string): number { + const r = this.db.prepare("select count(*) n from revisions where author = ? and at >= ?").get(author, since) as Row; + const h = this.db.prepare("select count(*) n from held where author = ? and at >= ?").get(author, since) as Row; + return r.n + h.n; + } + + // ------------------------------------------------------------ links, search, redirects + + backlinks(name: string): StoredNote[] { + return ( + this.db + .prepare("select n.* from links l join notes n on n.path = l.from_path where l.to_name = ? collate nocase and n.removed = 0 order by n.path") + .all(name) as Row[] + ).map(toNote); + } + + search(query: string, opts: { publishedOnly?: boolean; limit?: number } = {}): StoredNote[] { + // Quote each word so punctuation in a query can never become FTS syntax. + const fts = query + .split(/\s+/) + .filter(Boolean) + .map((w) => `"${w.replace(/"/g, '""')}"*`) + .join(" "); + if (!fts) return []; + const rows = this.db + .prepare( + `select n.* from search s join notes n on n.path = s.path + where search match ? and n.removed = 0 ${opts.publishedOnly ? "and n.publish = 1" : ""} + order by bm25(search, 0, 4, 1) limit ?`, + ) + .all(fts, opts.limit ?? 25) as Row[]; + return rows.map(toNote); + } + + addRedirect(type: string, fromSlug: string, toPath: string, at: string) { + this.db + .prepare("insert into redirects (type, from_slug, to_path, at) values (?, ?, ?, ?) on conflict(type, from_slug) do update set to_path = excluded.to_path, at = excluded.at") + .run(type, fromSlug, toPath, at); + } + + /** Point every redirect that led to `fromPath` at `toPath`, so redirects never chain. */ + retarget(fromPath: string, toPath: string) { + this.db.prepare("update redirects set to_path = ? where to_path = ?").run(toPath, fromPath); + } + + dropRedirect(type: string, slug: string) { + this.db.prepare("delete from redirects where type = ? and from_slug = ?").run(type, slug); + } + + redirect(type: string, slug: string): string | null { + const r = this.db.prepare("select to_path from redirects where type = ? and from_slug = ?").get(type, slug) as Row | undefined; + return (r?.to_path as string) ?? null; + } + + redirects(): { type: string; fromSlug: string; toPath: string; at: string }[] { + return (this.db.prepare("select * from redirects order by at desc").all() as Row[]).map((r) => ({ + type: r.type, + fromSlug: r.from_slug, + toPath: r.to_path, + at: r.at, + })); + } + + // ------------------------------------------------------------ held changes + + hold(h: Omit): HeldChange { + this.supersede(h.path, h.at); + const r = this.db + .prepare("insert into held (path, author, source, text, base_hash, reasons, at) values (?, ?, ?, ?, ?, ?, ?)") + .run(h.path, h.author, h.source, h.text, h.baseHash, JSON.stringify(h.reasons), h.at); + return this.heldById(Number(r.lastInsertRowid))!; + } + + /** A newer change to the same note replaces whatever was waiting. */ + supersede(path: string, at: string) { + this.db.prepare("update held set status = 'superseded', decided_at = ? where path = ? and status = 'pending'").run(at, path); + } + + heldById(id: number): HeldChange | null { + const r = this.db.prepare("select * from held where id = ?").get(id) as Row | undefined; + return r ? toHeld(r) : null; + } + + pendingFor(path: string): HeldChange | null { + const r = this.db.prepare("select * from held where path = ? and status = 'pending' order by id desc").get(path) as Row | undefined; + return r ? toHeld(r) : null; + } + + heldList(status: HeldChange["status"] = "pending"): HeldChange[] { + return (this.db.prepare("select * from held where status = ? order by id desc").all(status) as Row[]).map(toHeld); + } + + decide(id: number, status: "accepted" | "rejected", at: string) { + this.db.prepare("update held set status = ?, decided_at = ? where id = ?").run(status, at, id); + } + + // ------------------------------------------------------------ what the watcher has seen + + seenHash(path: string): string | null { + return ((this.db.prepare("select hash from seen where path = ?").get(path) as Row | undefined)?.hash as string) ?? null; + } + + markSeen(path: string, hash: string | null) { + if (hash === null) this.db.prepare("delete from seen where path = ?").run(path); + else this.db.prepare("insert into seen (path, hash) values (?, ?) on conflict(path) do update set hash = excluded.hash").run(path, hash); + } + + seenPaths(): string[] { + return (this.db.prepare("select path from seen").all() as Row[]).map((r) => r.path); + } +} diff --git a/services/content/src/vault.ts b/services/content/src/vault.ts new file mode 100644 index 0000000..fbac3e8 --- /dev/null +++ b/services/content/src/vault.ts @@ -0,0 +1,95 @@ +/** + * The Site folder on disk. Only the content service reads or writes it. + */ +import { createHash } from "node:crypto"; +import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, statSync, writeFileSync } from "node:fs"; +import { basename, dirname, join, relative, sep } from "node:path"; + +export const hashText = (text: string) => createHash("sha256").update(text).digest("hex"); + +/** + * Whether a path under the Site folder can hold a note. Folders starting with + * `_` or `.` hold templates, attachments and Obsidian's own files; files + * starting with `_` are settings notes and do count. Sync conflict copies + * never count, so a conflict can never overwrite a page. + */ +export function isNotePath(rel: string): boolean { + const parts = rel.split(/[\\/]/); + const file = parts.pop() ?? ""; + if (!file.endsWith(".md") || file.startsWith(".")) return false; + if (parts.some((p) => p.startsWith("_") || p.startsWith("."))) return false; + if (/\(conflicted copy|\.sync-conflict-/i.test(file)) return false; + return true; +} + +export class Vault { + constructor(readonly dir: string) {} + + read(rel: string): string | null { + try { + return readFileSync(join(this.dir, rel), "utf8"); + } catch { + return null; + } + } + + exists(rel: string): boolean { + return existsSync(join(this.dir, rel)); + } + + /** Write through a hidden temp file and a rename, so a reader never sees half a note. */ + write(rel: string, text: string) { + const full = join(this.dir, rel); + mkdirSync(dirname(full), { recursive: true }); + const tmp = join(dirname(full), `.${basename(full)}.${process.pid}.tmp`); + writeFileSync(tmp, text); + renameSync(tmp, full); + } + + /** Every note path under the Site folder, relative, with `/` separators. */ + listNotes(): string[] { + const out: string[] = []; + const walk = (d: string) => { + for (const e of readdirSync(d, { withFileTypes: true })) { + if (e.name.startsWith(".")) continue; + const full = join(d, e.name); + const rel = relative(this.dir, full).split(sep).join("/"); + if (e.isDirectory()) { + if (!e.name.startsWith("_")) walk(full); + } else if (isNotePath(rel)) out.push(rel); + } + }; + if (existsSync(this.dir)) walk(this.dir); + return out.sort(); + } + + /** File names in `_attachments`, for checking image links. */ + listAttachments(): string[] { + const out: string[] = []; + const walk = (d: string) => { + if (!existsSync(d)) return; + for (const e of readdirSync(d, { withFileTypes: true })) { + if (e.isDirectory()) walk(join(d, e.name)); + else out.push(e.name); + } + }; + walk(join(this.dir, "_attachments")); + return out; + } + + /** Absolute path of an attachment by file name, for serving images. */ + attachmentPath(name: string): string | null { + const walk = (d: string): string | null => { + if (!existsSync(d)) return null; + for (const e of readdirSync(d, { withFileTypes: true })) { + const full = join(d, e.name); + if (e.isDirectory()) { + const found = walk(full); + if (found) return found; + } else if (e.name === name && statSync(full).isFile()) return full; + } + return null; + }; + return walk(join(this.dir, "_attachments")); + } +} diff --git a/services/content/src/views.ts b/services/content/src/views.ts new file mode 100644 index 0000000..b203187 --- /dev/null +++ b/services/content/src/views.ts @@ -0,0 +1,61 @@ +/** + * The shapes the HTTP API and the MCP server hand out. Both use these, so a + * note looks the same to the site, the admin page and an agent. + */ +import type { HeldChange, Revision, StoredNote } from "./store.ts"; +import type { Outcome } from "./service.ts"; + +export const noteView = (n: StoredNote, opts: { text?: boolean } = {}) => ({ + path: n.path, + type: n.type, + name: n.name, + slug: n.slug, + title: n.title, + publish: n.publish, + data: n.data, + body: n.body, + rev: n.rev, + acceptedAt: n.acceptedAt, + author: n.author, + ...(opts.text ? { text: n.text } : {}), +}); + +export const summaryView = (n: StoredNote) => ({ + path: n.path, + type: n.type, + title: n.title, + slug: n.slug, + publish: n.publish, + acceptedAt: n.acceptedAt, +}); + +export const revisionView = (r: Revision, opts: { text?: boolean } = {}) => ({ + rev: r.rev, + author: r.author, + at: r.at, + action: r.action, + ...(opts.text ? { text: r.text } : {}), +}); + +export const heldView = (h: HeldChange, opts: { text?: boolean } = {}) => ({ + id: h.id, + path: h.path, + author: h.author, + source: h.source, + reasons: h.reasons, + at: h.at, + status: h.status, + decidedAt: h.decidedAt, + ...(opts.text ? { text: h.text } : {}), +}); + +export const outcomeView = (o: Outcome) => { + switch (o.status) { + case "accepted": + return { status: o.status, path: o.path, note: summaryView(o.note) }; + case "held": + return { status: o.status, path: o.path, held: heldView(o.held) }; + default: + return o; + } +}; diff --git a/services/content/src/watcher.ts b/services/content/src/watcher.ts new file mode 100644 index 0000000..2ebf74d --- /dev/null +++ b/services/content/src/watcher.ts @@ -0,0 +1,76 @@ +/** + * Watches the Site folder and hands each changed file to the service after it + * has been quiet for a moment, so a sync tool writing a file in pieces is read + * once, whole. A full scan at start and on a timer catches anything the + * operating system's watch missed. + */ +import { watch, type FSWatcher } from "node:fs"; +import { isNotePath } from "./vault.ts"; +import type { ContentService } from "./service.ts"; + +const SCAN = "\0scan"; + +export interface WatcherOptions { + debounceMs?: number; + scanMinutes?: number; + /** Called after every full scan. */ + onScan?: (result: { changed: number; held: number }) => void; + onError?: (err: unknown) => void; +} + +export class Watcher { + private fsw?: FSWatcher; + private timer?: NodeJS.Timeout; + private readonly pending = new Map(); + + constructor( + private readonly service: ContentService, + private readonly dir: string, + private readonly o: WatcherOptions = {}, + ) {} + + start() { + this.scan(); + this.fsw = watch(this.dir, { recursive: true }, (_event, file) => { + if (!file) return this.scan(); + const rel = file.toString().split("\\").join("/"); + if (isNotePath(rel)) this.queue(rel); + // A folder renamed or moved reports only the folder, so look at everything. + else if (!rel.endsWith(".md") && !rel.split("/").some((p) => p.startsWith(".") || p.startsWith("_"))) this.queue(SCAN); + }); + this.fsw.on("error", (err) => this.o.onError?.(err)); + const every = (this.o.scanMinutes ?? 5) * 60_000; + if (every > 0) this.timer = setInterval(() => this.scan(), every); + } + + stop() { + this.fsw?.close(); + clearInterval(this.timer); + for (const t of this.pending.values()) clearTimeout(t); + this.pending.clear(); + } + + scan() { + try { + this.o.onScan?.(this.service.scan()); + } catch (err) { + this.o.onError?.(err); + } + } + + private queue(rel: string) { + clearTimeout(this.pending.get(rel)); + this.pending.set( + rel, + setTimeout(() => { + this.pending.delete(rel); + if (rel === SCAN) return this.scan(); + try { + this.service.ingest(rel); + } catch (err) { + this.o.onError?.(err); + } + }, this.o.debounceMs ?? 2000), + ); + } +} diff --git a/services/content/test/helpers.ts b/services/content/test/helpers.ts new file mode 100644 index 0000000..3f6bac2 --- /dev/null +++ b/services/content/test/helpers.ts @@ -0,0 +1,78 @@ +import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { dirname, join } from "node:path"; +import { notePath, writeNote } from "@gk/schema"; +import { ContentService } from "../src/service.ts"; +import { Store } from "../src/store.ts"; +import { Vault } from "../src/vault.ts"; + +export interface Rig { + dir: string; + site: string; + store: Store; + vault: Vault; + service: ContentService; + clock: { t: number; advance(minutes: number): void }; + /** Write a note into the vault the way Obsidian would, returning its path. */ + put(data: Record, body?: string): string; + putText(path: string, text: string): void; + done(): void; +} + +export function rig(): Rig { + const dir = mkdtempSync(join(tmpdir(), "gk-content-")); + const site = join(dir, "Site"); + mkdirSync(site); + const store = new Store(join(dir, "content.db")); + const vault = new Vault(site); + const clock = { + t: Date.parse("2026-10-11T12:00:00Z"), + advance(minutes: number) { + this.t += minutes * 60_000; + }, + }; + const service = new ContentService({ store, vault, now: () => new Date(clock.t) }); + const putText = (path: string, text: string) => { + mkdirSync(dirname(join(site, path)), { recursive: true }); + writeFileSync(join(site, path), text); + }; + return { + dir, + site, + store, + vault, + service, + clock, + put(data, body = "") { + const path = notePath(data); + putText(path, writeNote(data, body)); + return path; + }, + putText, + done() { + store.close(); + rmSync(dir, { recursive: true, force: true }); + }, + }; +} + +export const book = (title: string, extra: Record = {}) => ({ + type: "library-item", + title, + medium: "book", + state: "finished", + publish: true, + ...extra, +}); + +export const post = (title: string, extra: Record = {}) => ({ + type: "post", + title, + date: "2026-10-01", + topic: "Making", + summary: "A summary.", + publish: true, + ...extra, +}); + +export const long = (n = 600) => "Paragraph of real writing. ".repeat(Math.ceil(n / 27)).slice(0, n); diff --git a/services/content/test/http.test.ts b/services/content/test/http.test.ts new file mode 100644 index 0000000..a1b5331 --- /dev/null +++ b/services/content/test/http.test.ts @@ -0,0 +1,96 @@ +import type { AddressInfo } from "node:net"; +import { afterAll, beforeAll, describe, expect, it } from "vitest"; +import { Client } from "@modelcontextprotocol/sdk/client/index.js"; +import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js"; +import { parseTokens } from "../src/config.ts"; +import { createHttpServer } from "../src/http.ts"; +import { book, post, rig, type Rig } from "./helpers.ts"; + +const T = { reader: "r".repeat(24), agent: "a".repeat(24), editor: "e".repeat(24), admin: "d".repeat(24) }; +let r: Rig; +let base: string; +let server: ReturnType; + +beforeAll(async () => { + r = rig(); + r.put({ type: "status-kinds", title: "Status kinds", publish: true, kinds: [{ label: "Reading", icon: "book" }] }); + r.put(book("Mistborn"), "About [[Elantris]]."); + r.put(book("Elantris")); + r.put(post("Secret draft", { publish: false })); + r.service.scan(); + server = createHttpServer(r.service, { tokens: parseTokens(Object.entries(T).map(([k, v]) => `${k}:${v}`).join(",")) }); + await new Promise((ok) => server.listen(0, "127.0.0.1", ok)); + base = `http://127.0.0.1:${(server.address() as AddressInfo).port}`; +}); +afterAll(() => { + server.close(); + r.done(); +}); + +const get = (path: string, token?: string) => + fetch(base + path, { headers: token ? { authorization: `Bearer ${token}` } : {} }); +const post_ = (path: string, token: string, body: unknown) => + fetch(base + path, { method: "POST", headers: { authorization: `Bearer ${token}`, "content-type": "application/json" }, body: JSON.stringify(body) }); + +describe("HTTP API", () => { + it("needs a token for everything but health", async () => { + expect((await get("/health")).status).toBe(200); + expect((await get("/api/notes")).status).toBe(401); + expect((await get("/api/notes", "wrong".repeat(6))).status).toBe(401); + }); + + it("shows readers published notes only", async () => { + const reader = (await (await get("/api/notes?type=post", T.reader)).json()) as unknown[]; + const editor = (await (await get("/api/notes?type=post", T.editor)).json()) as unknown[]; + expect(reader).toHaveLength(0); + expect(editor).toHaveLength(1); + expect((await get("/api/note?path=Posts/Secret%20draft.md", T.reader)).status).toBe(404); + }); + + it("finds a note by slug with its backlinks", async () => { + const n = (await (await get("/api/note?type=library-item&slug=elantris", T.reader)).json()) as { title: string; backlinks: { title: string }[] }; + expect(n.title).toBe("Elantris"); + expect(n.backlinks.map((b) => b.title)).toEqual(["Mistborn"]); + }); + + it("keeps readers from writing and agents from reviewing", async () => { + expect((await post_("/api/changes", T.reader, { op: "create", data: book("X") })).status).toBe(403); + expect((await post_("/api/held/1/accept", T.agent, {})).status).toBe(403); + }); + + it("applies a change and reports rejections plainly", async () => { + const ok = await post_("/api/changes", T.editor, { op: "create", data: book("Warbreaker") }); + expect(ok.status).toBe(200); + const bad = await post_("/api/changes", T.editor, { op: "create", data: { type: "library-item", title: "Y" } }); + expect(bad.status).toBe(422); + }); +}); + +describe("MCP server", () => { + it("lets an agent search, post a status and draft a post, but not publish it", async () => { + const client = new Client({ name: "test", version: "1.0.0" }); + await client.connect( + new StreamableHTTPClientTransport(new URL(`${base}/mcp`), { requestInit: { headers: { authorization: `Bearer ${T.agent}` } } }), + ); + const tools = (await client.listTools()).tools.map((t) => t.name).sort(); + expect(tools).toEqual(["add_library_item", "draft_post", "get_note", "get_schema", "list_held", "search", "set_status", "update_fields"]); + + const found = await client.callTool({ name: "search", arguments: { query: "mistborn" } }); + expect(JSON.stringify(found.content)).toContain("Library/Books/Mistborn.md"); + + const status = await client.callTool({ name: "set_status", arguments: { kind: "reading", title: "Mistborn", subject: "Mistborn" } }); + expect(status.isError).toBeFalsy(); + const badKind = await client.callTool({ name: "set_status", arguments: { kind: "Juggling", title: "Balls" } }); + expect(badKind.isError).toBe(true); + + const draft = await client.callTool({ + name: "draft_post", + arguments: { title: "Agent draft", topic: "Making", summary: "S.", body: "Text." }, + }); + expect(draft.isError).toBeFalsy(); + const publish = await client.callTool({ name: "update_fields", arguments: { path: "Posts/Agent draft.md", fields: { publish: true } } }); + expect(JSON.stringify(publish.content)).toContain("agent-publish"); + expect(r.store.get("Posts/Agent draft.md")!.publish).toBe(false); + await client.close(); + }); +}); diff --git a/services/content/test/migrated.test.ts b/services/content/test/migrated.test.ts new file mode 100644 index 0000000..5c8ef39 --- /dev/null +++ b/services/content/test/migrated.test.ts @@ -0,0 +1,35 @@ +import { copyFileSync, mkdirSync } from "node:fs"; +import { dirname, join, resolve } from "node:path"; +import { afterAll, beforeAll, describe, expect, it } from "vitest"; +import { build, loadProducts } from "../../../tools/migrate/src/build.ts"; +import { rig, type Rig } from "./helpers.ts"; + +let r: Rig; +let total = 0; + +beforeAll(async () => { + const legacy = resolve(import.meta.dirname, "../../../legacy"); + await loadProducts(legacy); + const result = build(legacy); + r = rig(); + for (const n of result.notes) r.putText(n.path, n.text); + for (const a of result.attachments) { + mkdirSync(dirname(join(r.site, a.to)), { recursive: true }); + copyFileSync(a.from, join(r.site, a.to)); + } + total = result.notes.length; +}); +afterAll(() => r.done()); + +describe("the migrated Site folder", () => { + it("is taken in whole, with nothing held and no problems", () => { + expect(r.service.scan()).toEqual({ changed: total, held: 0 }); + expect(r.store.count().notes).toBe(total); + expect(r.service.problems()).toEqual([]); + }); + + it("is searchable and linked", () => { + expect(r.store.search("Spirit Island").some((n) => n.type === "board-game")).toBe(true); + expect(r.store.backlinks("Magic The Gathering").length).toBeGreaterThan(0); + }); +}); diff --git a/services/content/test/service.test.ts b/services/content/test/service.test.ts new file mode 100644 index 0000000..1af58c1 --- /dev/null +++ b/services/content/test/service.test.ts @@ -0,0 +1,188 @@ +import { readFileSync, renameSync, rmSync } from "node:fs"; +import { join } from "node:path"; +import { afterEach, beforeEach, describe, expect, it } from "vitest"; +import { evaluate, defaultRules } from "../src/rules.ts"; +import { book, long, post, rig, type Rig } from "./helpers.ts"; + +let r: Rig; +beforeEach(() => { + r = rig(); +}); +afterEach(() => r.done()); + +const ok = (o: { status: string }) => expect(o.status).toBe("accepted"); + +describe("from the vault", () => { + it("takes in valid notes and indexes them for search and links", () => { + r.put(book("Mistborn", { creator: "Brandon Sanderson" }), "Allomancy, heists and [[Elantris]]."); + r.put(book("Elantris")); + expect(r.service.scan()).toEqual({ changed: 2, held: 0 }); + expect(r.store.search("allomancy").map((n) => n.title)).toEqual(["Mistborn"]); + expect(r.store.backlinks("Elantris").map((n) => n.title)).toEqual(["Mistborn"]); + expect(r.service.scan()).toEqual({ changed: 0, held: 0 }); + }); + + it("holds a broken edit and keeps showing the last good version", () => { + const path = r.put(book("Mistborn")); + ok(r.service.ingest(path)); + r.putText(path, "---\ntype: library-item\ntitle: Mistborn\nmedium: scroll\n---\n"); + const o = r.service.ingest(path); + expect(o.status).toBe("held"); + expect(o.status === "held" && o.held.reasons[0]!.code).toBe("invalid"); + expect(r.store.get(path)!.data.medium).toBe("book"); + }); + + it("holds a large loss of text until it is accepted", () => { + const path = r.put(post("Building a bench"), long()); + ok(r.service.ingest(path)); + r.put(post("Building a bench"), "Short now."); + const o = r.service.ingest(path); + expect(o.status === "held" && o.held.reasons.map((x) => x.code)).toEqual(["text-loss"]); + expect(r.store.get(path)!.body.trim()).toBe(long()); + ok(r.service.acceptHeld(o.status === "held" ? o.held.id : 0)); + expect(r.store.get(path)!.body.trim()).toBe("Short now."); + }); + + it("holds a secret pasted into a note", () => { + const path = r.put(post("Homelab notes"), "Setup."); + ok(r.service.ingest(path)); + r.put(post("Homelab notes"), "Setup. api_key = abcd1234efgh5678"); + expect(r.service.ingest(path).status).toBe("held"); + }); + + it("unpublishes a deleted note but keeps its history for a rollback", () => { + const path = r.put(book("Mistborn")); + ok(r.service.ingest(path)); + rmSync(join(r.site, path)); + r.service.scan(); + expect(r.store.get(path)!.removed).toBe(true); + expect(r.store.list({ publishedOnly: true })).toHaveLength(0); + ok(r.service.rollback(path, 1)); + expect(r.store.get(path)!.removed).toBe(false); + expect(r.vault.exists(path)).toBe(true); + }); + + it("keeps old addresses working after a slug change and a move", () => { + const path = r.put(post("Bench build", { slug: "bench" })); + ok(r.service.ingest(path)); + r.put(post("Bench build", { slug: "workbench" })); + ok(r.service.ingest(path)); + expect(r.store.redirect("post", "bench")).toBe(path); + + // Renaming the file: the old path goes, the same note appears under a new name. + renameSync(join(r.site, path), join(r.site, "Posts/Workbench build.md")); + r.service.scan(); + expect(r.store.redirect("post", "bench")).toBe("Posts/Workbench build.md"); + }); + + it("holds a second note that takes a used address", () => { + ok(r.service.ingest(r.put(post("Bench build", { slug: "bench" })))); + const o = r.service.ingest(r.put(post("Another bench", { slug: "bench" }))); + expect(o.status === "held" && o.held.reasons.map((x) => x.code)).toEqual(["duplicate-slug"]); + }); + + it("ignores conflict copies and template folders", () => { + r.putText("Posts/Bench (conflicted copy 2026-10-11).md", "junk"); + r.putText("_templates/Post.md", "junk"); + expect(r.service.scan()).toEqual({ changed: 0, held: 0 }); + }); +}); + +describe("the write API", () => { + it("writes an accepted change into the vault and skips it when the watcher sees it", () => { + const o = r.service.apply({ op: "create", data: book("Warbreaker") }, "agent"); + ok(o); + const text = readFileSync(join(r.site, o.path!), "utf8"); + expect(text).toContain("updatedBy: agent"); + expect(r.service.ingest(o.path!)).toEqual({ status: "unchanged", path: o.path }); + }); + + it("rejects invalid input at once instead of holding it", () => { + const o = r.service.apply({ op: "create", data: { type: "library-item", title: "X", medium: "scroll", state: "now" } }, "agent"); + expect(o.status).toBe("rejected"); + expect(r.store.heldList()).toHaveLength(0); + }); + + it("refuses paths outside the Site folder and type changes", () => { + expect(r.service.apply({ op: "update", path: "../secrets.md", fields: {} }, "admin").status).toBe("rejected"); + const path = r.put(book("Mistborn")); + r.service.ingest(path); + expect(r.service.apply({ op: "update", path, fields: { type: "post" } }, "admin").status).toBe("rejected"); + }); + + it("takes in an Obsidian edit before applying an API change on top of it", () => { + const path = r.put(book("Mistborn")); + r.service.ingest(path); + r.put(book("Mistborn", { verdict: "Great heist." })); + ok(r.service.apply({ op: "update", path, fields: { state: "now" } }, "greg")); + expect(r.store.get(path)!.data).toMatchObject({ verdict: "Great heist.", state: "now" }); + }); + + it("holds an agent editing Greg's note, and writes it to the vault only once accepted", () => { + const path = r.put(book("Mistborn")); + r.service.ingest(path); + const before = readFileSync(join(r.site, path), "utf8"); + const o = r.service.apply({ op: "update", path, fields: { verdict: "Meh." } }, "agent"); + expect(o.status === "held" && o.held.reasons.map((x) => x.code)).toEqual(["agent-edit"]); + expect(readFileSync(join(r.site, path), "utf8")).toBe(before); + ok(r.service.acceptHeld(o.status === "held" ? o.held.id : 0)); + expect(readFileSync(join(r.site, path), "utf8")).toContain("verdict: Meh."); + }); + + it("lets an agent edit a note marked agentEdits, and its own notes", () => { + const path = r.put(book("Mistborn", { agentEdits: true })); + r.service.ingest(path); + ok(r.service.apply({ op: "update", path, fields: { progress: "2/3" } }, "agent")); + const own = r.service.apply({ op: "create", data: book("Warbreaker") }, "agent"); + ok(r.service.apply({ op: "update", path: own.path!, fields: { state: "now" } }, "agent")); + }); + + it("holds an agent publishing a post", () => { + const o = r.service.apply({ op: "create", data: post("Draft", { publish: false }), body: "Hi." }, "agent"); + ok(o); + const pub = r.service.apply({ op: "update", path: o.path!, fields: { publish: true } }, "agent"); + expect(pub.status === "held" && pub.held.reasons.map((x) => x.code)).toEqual(["agent-publish"]); + }); + + it("holds an agent adding contact details", () => { + const o = r.service.apply({ op: "create", data: post("Draft", { publish: false }), body: "Mail me at someone@example.com" }, "agent"); + expect(o.status === "held" && o.held.reasons.map((x) => x.code)).toEqual(["contact"]); + }); + + it("holds an agent making too many changes at once", () => { + for (let i = 0; i < 10; i++) ok(r.service.apply({ op: "create", data: book(`Book ${i}`) }, "agent")); + expect(r.service.apply({ op: "create", data: book("Book 10") }, "agent").status).toBe("held"); + r.clock.advance(11); + ok(r.service.apply({ op: "create", data: book("Book 11") }, "agent")); + }); + + it("refuses a change while another waits for review", () => { + const path = r.put(book("Mistborn")); + r.service.ingest(path); + r.service.apply({ op: "update", path, fields: { verdict: "Meh." } }, "agent"); + expect(r.service.apply({ op: "update", path, fields: { verdict: "Good." } }, "greg").status).toBe("rejected"); + }); + + it("rejecting a held vault edit can restore the live version into the vault", () => { + const path = r.put(book("Mistborn")); + r.service.ingest(path); + const good = readFileSync(join(r.site, path), "utf8"); + r.putText(path, "---\ntype: library-item\n---\n"); + const o = r.service.ingest(path); + r.service.rejectHeld(o.status === "held" ? o.held.id : 0, { restore: true }); + expect(readFileSync(join(r.site, path), "utf8")).toBe(good); + }); +}); + +describe("rules", () => { + it("only flags secrets that are new in this edit", () => { + const text = "---\ntype: post\n---\npassword = hunter2hunter2"; + const checked = { ok: true as const, type: "post", data: { type: "post", title: "x", publish: false }, body: "" }; + const current = { text, removed: false, author: "greg", data: {}, publish: false } as never; + const v = evaluate( + { path: "Posts/x.md", name: "x", slug: "x", text, author: "greg", checked: checked as never }, + { current, nameTakenBy: null, slugTakenBy: null, recentAgentChanges: 0, settings: defaultRules }, + ); + expect(v.decision).toBe("accept"); + }); +}); diff --git a/services/content/tsconfig.json b/services/content/tsconfig.json new file mode 100644 index 0000000..7680f99 --- /dev/null +++ b/services/content/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.base.json", + "include": ["src", "test"] +} diff --git a/vitest.config.ts b/vitest.config.ts index 9ca545d..54d4e2a 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -1,5 +1,5 @@ import { defineConfig } from "vitest/config"; export default defineConfig({ - test: { include: ["packages/*/test/**/*.test.ts", "tools/*/test/**/*.test.ts"] }, + test: { include: ["packages/*/test/**/*.test.ts", "services/*/test/**/*.test.ts", "tools/*/test/**/*.test.ts"] }, }); From 32cf13a5cb809e38396efa0b4eab6cd1ccefe06e Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 11 Oct 2026 19:59:21 +0000 Subject: [PATCH 03/16] Add the admin page (phase 3) services/admin is a client of the content service's HTTP API, served on the tailnet only. It renders on the server with plain forms and no client scripts, and holds the admin token so the browser never sees it. - Review queue: reasons, line diff against the live version, accept, reject, and reject with the live version put back into Obsidian - Notes and drafts: search, preview, fields, backlinks, publish toggle - History with compare and roll back - Quick capture for a status or a Library item (optionally both) - Store: orders open or closed per category, open slots and stock - Overview: counts, last scan and error, scan now, sync job health - Same-origin check on every post, strict CSP, sanitized previews The content service gains /api/status, sync run reports (/api/syncs) and lookup of a note by name. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_012aSbo2D1qP8cMFQCykf7kx --- README.md | 6 +- docs/admin.md | 49 ++++ docs/content-service.md | 5 +- package-lock.json | 25 ++ package.json | 5 +- services/admin/package.json | 16 ++ services/admin/public/admin.css | 79 +++++++ services/admin/src/client.ts | 136 +++++++++++ services/admin/src/diff.ts | 60 +++++ services/admin/src/html.ts | 46 ++++ services/admin/src/layout.ts | 49 ++++ services/admin/src/main.ts | 19 ++ services/admin/src/markdown.ts | 28 +++ services/admin/src/pages.ts | 289 +++++++++++++++++++++++ services/admin/src/server.ts | 357 +++++++++++++++++++++++++++++ services/admin/test/admin.test.ts | 152 ++++++++++++ services/admin/test/units.test.ts | 50 ++++ services/admin/tsconfig.json | 4 + services/content/src/http.ts | 30 ++- services/content/src/main.ts | 5 +- services/content/src/service.ts | 12 + services/content/src/store.ts | 47 ++++ services/content/test/http.test.ts | 11 + 23 files changed, 1474 insertions(+), 6 deletions(-) create mode 100644 docs/admin.md create mode 100644 services/admin/package.json create mode 100644 services/admin/public/admin.css create mode 100644 services/admin/src/client.ts create mode 100644 services/admin/src/diff.ts create mode 100644 services/admin/src/html.ts create mode 100644 services/admin/src/layout.ts create mode 100644 services/admin/src/main.ts create mode 100644 services/admin/src/markdown.ts create mode 100644 services/admin/src/pages.ts create mode 100644 services/admin/src/server.ts create mode 100644 services/admin/test/admin.test.ts create mode 100644 services/admin/test/units.test.ts create mode 100644 services/admin/tsconfig.json diff --git a/README.md b/README.md index ced09bb..167333b 100644 --- a/README.md +++ b/README.md @@ -13,11 +13,12 @@ with the decisions behind it, is the | `packages/schema` | The one definition of every note type: fields, folders, file names, and the checks for one note and for the whole folder. The migration, the content service, the MCP server, the site and any later editing tool all use it. | | `vault-kit` | What goes into the vault: the Obsidian templates (generated from the schema) and the Site guide. | | `services/content` | The content service: watches the Site folder, runs the rules, keeps the last good version and its history, and serves the one write API. The MCP server for agents is part of it. See [docs/content-service.md](docs/content-service.md). | +| `services/admin` | The admin page: the review queue with diffs, history and rollback, quick capture, store orders, draft previews and sync health. A client of the content service, on the tailnet only. See [docs/admin.md](docs/admin.md). | | `tools/migrate` | The one-time migration from the old site's data into Site folder notes. | | `docs` | How the pieces fit. Start with [docs/content-model.md](docs/content-model.md). | | `legacy` | The old site, kept for reference only. Nothing outside it imports from it except the migration. | -Coming in later phases: the admin page, the component library, and the new site. +Coming in later phases: the component library and the new site. ## Commands @@ -33,6 +34,9 @@ npm run schema:json # writes a JSON Schema per note type to packages/schema/jso # the content service, against a copy of the Site folder SITE_DIR=out/24.01_PersonalWebsite/Site DATA_DIR=data \ CONTENT_TOKENS="reader:<24+ chars>,admin:<24+ chars>" npm run content + +# the admin page, in a second terminal +ADMIN_TOKEN= npm run admin # http://localhost:4320 ``` ## The old site diff --git a/docs/admin.md b/docs/admin.md new file mode 100644 index 0000000..71d3a43 --- /dev/null +++ b/docs/admin.md @@ -0,0 +1,49 @@ +# Admin page + +`services/admin` is where Greg decides what the rules held, puts back an +earlier version, captures a status or a Library item, opens and closes store +orders, previews drafts, and checks that the content service and the sync +jobs are running. + +It is one more client of the content service's HTTP API, like the MCP server +and any editing tool built later. It writes nothing itself: every action is a +call to the write API, so the same rules apply, and changes are written into +the vault so Obsidian shows them too. Its changes are recorded with the author +`admin` ("Admin page" in history). + +## Pages + +| Page | What it does | +| --- | --- | +| Overview | Live and total notes, what waits for review, folder problems, the service's last scan and last error, a Scan now button, and each sync job's last run and last success. | +| Review | Held changes with the rules' reasons and a line diff against the live version. Accept puts it live (and into the vault, for API changes). Reject keeps the live version; for an edit made in Obsidian, "Reject and put the live version back" also rewrites the file. Earlier decisions are listed by status. | +| Notes, Drafts | Browse and search every note, or only the unpublished ones. | +| A note | A preview of the body, its fields, what links to it, Publish or Unpublish, and its history. Any earlier version can be compared with the live one and rolled back. | +| Capture | A status for the Now log, or a Library item with the option to post a status about it in the same step. | +| Store | Orders open or closed per category, with the reopen date and turnaround, and open slots or stock per product line. | +| Problems | Whole-folder problems, like links to notes that do not exist. | + +## Safety + +- **Tailnet only.** Publish it behind the "Tailnet only" access list; it has no + login of its own. +- **The token stays on the server.** The admin page holds the content service's + admin token and calls the API itself; the browser never sees it. +- **No posts from other sites.** Every form post must carry `Sec-Fetch-Site: + same-origin` (or a matching `Origin`), so a page elsewhere cannot make the + browser act on the admin page. +- **Drafts cannot run code.** Previews show raw HTML as text and drop links that + are not http, https, mailto or relative. The page sends a content security + policy that allows no scripts at all; it works entirely with plain forms. + +## Running it + +| Variable | Default | | +| --- | --- | --- | +| `CONTENT_URL` | `http://localhost:4310` | The content service. | +| `ADMIN_TOKEN` | required | The content service's admin token. | +| `PORT`, `HOST` | `4320`, `0.0.0.0` | | + +```sh +ADMIN_TOKEN=… npm run admin +``` diff --git a/docs/content-service.md b/docs/content-service.md index be91a21..f6512fd 100644 --- a/docs/content-service.md +++ b/docs/content-service.md @@ -79,13 +79,16 @@ roles in `CONTENT_TOKENS`. | `GET /health` | anyone | Note, published and held counts. | | `GET /api/schema` | all | Every note type with its JSON Schema. | | `GET /api/notes?type=&q=&limit=&offset=&full=` | all | List or search. Readers see published notes only. | -| `GET /api/note?path=` or `?type=&slug=` | all | One note with its backlinks. An old slug answers with `{ redirect }`. | +| `GET /api/note?path=`, `?name=` or `?type=&slug=` | all | One note with its backlinks. An old slug answers with `{ redirect }`. | | `GET /api/backlinks?name=` | all | Notes linking to a note. | | `GET /api/redirects` | all | Every redirect. | | `GET /attachments/` | all | An image from `_attachments`. | | `GET /api/note/history?path=`, `/api/note/revision?path=&rev=` | agent, editor, admin | History and old versions. | | `GET /api/problems` | agent, editor, admin | Broken links and other whole-folder problems. | | `GET /api/held`, `/api/held/` | agent, editor, admin | The review queue; one entry comes with the current text for comparison. | +| `GET /api/status` | agent, editor, admin | When the service started, its last full scan and last error, counts, and each sync job's latest run. | +| `GET /api/syncs`, `?job=` | agent, editor, admin | Each sync job's latest run, or one job's recent runs. | +| `POST /api/syncs` | agent, editor, admin | A sync job reports a run: `{ job, ok, message?, counts? }`. The last 50 runs per job are kept. | | `POST /api/changes` | agent, editor, admin | One change. 422 with reasons when turned down. | | `POST /api/held//accept`, `/reject` | editor, admin | Decide a held change. `{ "restore": true }` on reject puts the live version back into the vault. | | `POST /api/note/rollback` | admin | `{ path, rev }` | diff --git a/package-lock.json b/package-lock.json index 7506337..44fe4d5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -436,6 +436,10 @@ "node": ">=18" } }, + "node_modules/@gk/admin": { + "resolved": "services/admin", + "link": true + }, "node_modules/@gk/content": { "resolved": "services/content", "link": true @@ -1937,6 +1941,18 @@ "@jridgewell/sourcemap-codec": "^1.6.0" } }, + "node_modules/marked": { + "version": "18.0.14", + "resolved": "https://registry.npmjs.org/marked/-/marked-18.0.14.tgz", + "integrity": "sha512-mBHK6FBHuBAlhgRe88w9F0O1AbwwXJUcQibUbC/QcdTbVGAD7aWza+xt3N6oT/jCZx3/OMeS+8rnuiHZcQ9s7A==", + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 20" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -2863,6 +2879,15 @@ "zod": "^4.1.0" } }, + "services/admin": { + "name": "@gk/admin", + "version": "0.1.0", + "dependencies": { + "@gk/schema": "*", + "marked": "18.0.14", + "tsx": "^4.19.0" + } + }, "services/content": { "name": "@gk/content", "version": "0.1.0", diff --git a/package.json b/package.json index 707ec47..295eaa8 100644 --- a/package.json +++ b/package.json @@ -12,13 +12,14 @@ "node": ">=22" }, "scripts": { - "typecheck": "tsc -p packages/schema && tsc -p tools/migrate && tsc -p services/content", + "typecheck": "tsc -p packages/schema && tsc -p tools/migrate && tsc -p services/content && tsc -p services/admin", "test": "vitest run", "check": "npm run typecheck && npm test", "schema:json": "npm run json -w @gk/schema", "templates": "npm run templates -w @gk/schema", "migrate": "npm run migrate -w @gk/migrate --", - "content": "npm run start -w @gk/content" + "content": "npm run start -w @gk/content", + "admin": "npm run start -w @gk/admin" }, "devDependencies": { "@types/node": "^22.10.0", diff --git a/services/admin/package.json b/services/admin/package.json new file mode 100644 index 0000000..fa5d793 --- /dev/null +++ b/services/admin/package.json @@ -0,0 +1,16 @@ +{ + "name": "@gk/admin", + "version": "0.1.0", + "private": true, + "type": "module", + "description": "The admin page: the review queue, history and rollback, quick capture, store orders, draft previews and sync health. A client of the content service's HTTP API, reachable on the tailnet only.", + "scripts": { + "start": "node --import tsx src/main.ts", + "dev": "node --watch --import tsx src/main.ts" + }, + "dependencies": { + "@gk/schema": "*", + "marked": "18.0.14", + "tsx": "^4.19.0" + } +} diff --git a/services/admin/public/admin.css b/services/admin/public/admin.css new file mode 100644 index 0000000..fc25df6 --- /dev/null +++ b/services/admin/public/admin.css @@ -0,0 +1,79 @@ +/* The admin page follows the site's all-dark brand, kept plain: it is a tool. */ +:root { + --sky: #0b111c; + --midnight: #131c2b; + --panel: #182335; + --line: #2a3a52; + --text: #f4f2ed; + --muted: #a9b4c2; + --teal: #14a39a; + --teal-bright: #2bc2b7; + --gold: #c9a24a; + --gold-bright: #d8b35a; + --red: #f0857a; + --add: #12382f; + --remove: #45202a; + color-scheme: dark; +} +* { box-sizing: border-box; } +body { margin: 0; background: var(--sky); color: var(--text); font: 15px/1.55 Montserrat, system-ui, sans-serif; } +a { color: var(--teal-bright); text-underline-offset: 3px; } +h1, h2, h3 { font-family: Cinzel, Georgia, serif; font-weight: 600; letter-spacing: 0.02em; } +h1 { font-size: 1.6rem; margin: 0 0 4px; } +h2 { font-size: 1.15rem; margin: 28px 0 10px; color: var(--gold-bright); } +code, pre, .mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 13px; } +header.top { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 18px; padding: 12px 20px; background: var(--midnight); border-bottom: 1px solid var(--gold); } +header.top .brand { font-family: Cinzel, serif; color: var(--gold-bright); text-decoration: none; margin-right: 8px; } +header.top nav { display: flex; flex-wrap: wrap; gap: 4px 14px; } +header.top nav a { color: var(--text); text-decoration: none; padding: 4px 0; border-bottom: 2px solid transparent; } +header.top nav a[aria-current="page"] { border-color: var(--teal); } +.count { display: inline-block; min-width: 20px; padding: 0 6px; margin-left: 4px; border-radius: 10px; background: var(--gold); color: var(--midnight); font-size: 12px; font-weight: 700; text-align: center; } +main { max-width: 1100px; margin: 0 auto; padding: 24px 16px 64px; } +.lede { color: var(--muted); margin: 0 0 18px; } +.flash { padding: 10px 14px; border-left: 3px solid var(--teal); background: var(--panel); margin-bottom: 18px; } +.flash.err { border-color: var(--red); } +.flash ul { margin: 6px 0 0; padding-left: 20px; } +.panel { background: var(--panel); border: 1px solid var(--line); border-radius: 10px; padding: 16px; margin-bottom: 16px; } +.grid { display: grid; gap: 16px; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); } +.stat { font-size: 1.9rem; font-family: Cinzel, serif; color: var(--gold-bright); } +.muted { color: var(--muted); } +table { width: 100%; border-collapse: collapse; } +th, td { text-align: left; padding: 8px 10px; border-bottom: 1px solid var(--line); vertical-align: top; } +th { color: var(--muted); font-weight: 600; font-size: 13px; } +.table-wrap { overflow-x: auto; } +.pill { display: inline-block; padding: 1px 10px; border-radius: 999px; font-size: 12px; border: 1px solid var(--line); white-space: nowrap; } +.pill.live { border-color: var(--teal); color: var(--teal-bright); } +.pill.draft { border-color: var(--gold); color: var(--gold-bright); } +.pill.bad { border-color: var(--red); color: var(--red); } +.reason { margin: 4px 0; } +.reason code { color: var(--gold-bright); } +form.inline { display: inline; } +.actions { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; margin: 14px 0; } +button, .button { font: inherit; cursor: pointer; min-height: 36px; padding: 6px 18px; border-radius: 999px; border: 1px solid var(--gold); background: transparent; color: var(--text); text-decoration: none; display: inline-flex; align-items: center; } +button.primary { background: var(--teal); border-color: var(--teal); color: var(--midnight); font-weight: 600; } +button.danger { border-color: var(--red); color: var(--red); } +button:focus-visible, a:focus-visible, input:focus-visible, select:focus-visible, textarea:focus-visible { outline: 2px solid var(--gold); outline-offset: 3px; } +label { display: block; margin: 10px 0 4px; font-size: 13px; color: var(--muted); } +label.check { display: flex; gap: 8px; align-items: center; color: var(--text); font-size: 15px; } +input, select, textarea { font: inherit; width: 100%; padding: 8px 10px; border-radius: 10px; border: 1px solid var(--line); background: var(--sky); color: var(--text); } +input[type="checkbox"] { width: auto; } +input:focus, select:focus, textarea:focus { border-color: var(--teal); } +.row { display: grid; gap: 0 14px; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); } +.filters { display: flex; flex-wrap: wrap; gap: 10px; align-items: end; margin-bottom: 16px; } +.filters > * { flex: 1 1 160px; } +.filters button { flex: 0 0 auto; } +.diff { background: var(--sky); border: 1px solid var(--line); border-radius: 10px; overflow-x: auto; padding: 8px 0; } +.diff div { white-space: pre-wrap; word-break: break-word; padding: 0 12px; font-family: ui-monospace, monospace; font-size: 13px; } +.diff .add { background: var(--add); } +.diff .add::before { content: "+ "; color: var(--teal-bright); } +.diff .remove { background: var(--remove); } +.diff .remove::before { content: "- "; color: var(--red); } +.diff .same::before { content: " "; } +.diff .skip { color: var(--muted); font-style: italic; } +.preview { background: var(--midnight); border: 1px solid var(--gold); border-radius: 10px; padding: 8px 22px; } +.preview img { max-width: 100%; } +dl.fields { display: grid; grid-template-columns: minmax(110px, max-content) 1fr; gap: 6px 16px; margin: 0; } +dl.fields dt { color: var(--muted); } +dl.fields dd { margin: 0; word-break: break-word; } +dl.fields pre { margin: 0; white-space: pre-wrap; } +input.num { width: 90px; } diff --git a/services/admin/src/client.ts b/services/admin/src/client.ts new file mode 100644 index 0000000..9798941 --- /dev/null +++ b/services/admin/src/client.ts @@ -0,0 +1,136 @@ +/** + * The content service's HTTP API, as the admin page uses it. The admin page + * holds the admin token on the server side; the browser never sees it. + */ +export class ApiError extends Error { + constructor(readonly status: number, message: string, readonly body: unknown) { + super(message); + } +} + +export interface Summary { + path: string; + type: string; + title: string; + slug: string; + publish: boolean; + acceptedAt: string; +} + +export interface Note extends Summary { + name: string; + data: Record; + body: string; + rev: number; + author: string; + text: string; + backlinks: Summary[]; + pending: Held | null; +} + +export interface Reason { + code: string; + message: string; +} + +export interface Held { + id: number; + path: string; + author: string; + source: "vault" | "api"; + reasons: Reason[]; + at: string; + status: string; + decidedAt: string | null; + text?: string; + current?: string | null; +} + +export interface Revision { + rev: number; + author: string; + at: string; + action: string; + text?: string; +} + +export interface SyncJob { + job: string; + at: string; + ok: boolean; + message: string; + counts: Record; + lastOkAt?: string | null; +} + +export interface Status { + startedAt: string; + lastScan: { at: string; changed: number; held: number } | null; + lastError: { at: string; message: string } | null; + counts: { notes: number; published: number; held: number }; + syncs: SyncJob[]; +} + +export interface Problem { + path: string; + message: string; +} + +export type Outcome = + | { status: "accepted"; path: string } + | { status: "held"; path: string; held: Held } + | { status: "rejected"; path: string | null; reasons: Reason[] } + | { status: "unchanged"; path: string }; + +export type Change = + | { op: "create"; data: Record; body?: string } + | { op: "update"; path: string; fields?: Record; body?: string }; + +export type FullNote = Omit; + +export class ContentApi { + constructor(private readonly base: string, private readonly token: string) {} + + private async call(method: "GET" | "POST", path: string, body?: unknown, okStatuses = [200]): Promise { + const res = await fetch(new URL(path, this.base), { + method, + headers: { authorization: `Bearer ${this.token}`, ...(body !== undefined ? { "content-type": "application/json" } : {}) }, + body: body !== undefined ? JSON.stringify(body) : undefined, + }); + const data = (await res.json().catch(() => null)) as unknown; + if (!okStatuses.includes(res.status)) { + throw new ApiError(res.status, (data as { error?: string } | null)?.error ?? `The content service answered ${res.status}`, data); + } + return data as T; + } + + private get = (path: string, params: Record = {}) => { + const u = new URLSearchParams(); + for (const [k, v] of Object.entries(params)) if (v !== undefined && v !== "") u.set(k, String(v)); + const s = u.toString(); + return this.call("GET", s ? `${path}?${s}` : path); + }; + + status = () => this.get("/api/status"); + notes = (p: { type?: string; q?: string; limit?: number; full?: boolean } = {}) => + this.get("/api/notes", { type: p.type, q: p.q, limit: p.limit, full: p.full ? "true" : undefined }); + fullNotes = (type: string) => this.get("/api/notes", { type, full: "true", limit: 1000 }); + note = (path: string) => this.get("/api/note", { path }); + noteByName = (name: string) => this.get("/api/note", { name }); + history = (path: string) => this.get("/api/note/history", { path }); + revision = (path: string, rev: number) => this.get("/api/note/revision", { path, rev }); + held = (status = "pending") => this.get("/api/held", { status }); + heldOne = (id: number) => this.get(`/api/held/${id}`); + problems = () => this.get("/api/problems"); + syncRuns = (job: string) => this.get("/api/syncs", { job }); + + apply = (change: Change) => this.call("POST", "/api/changes", change, [200, 422]); + accept = (id: number) => this.call("POST", `/api/held/${id}/accept`, {}, [200, 422]); + reject = (id: number, restore: boolean) => this.call("POST", `/api/held/${id}/reject`, { restore }, [200, 422]); + rollback = (path: string, rev: number) => this.call("POST", "/api/note/rollback", { path, rev }, [200, 422]); + scan = () => this.call<{ changed: number; held: number }>("POST", "/api/scan", {}); + + /** Fetch an attachment to pass through to the browser. */ + attachment = (name: string) => + fetch(new URL(`/attachments/${encodeURIComponent(name)}`, this.base), { headers: { authorization: `Bearer ${this.token}` } }); +} diff --git a/services/admin/src/diff.ts b/services/admin/src/diff.ts new file mode 100644 index 0000000..b1dc19b --- /dev/null +++ b/services/admin/src/diff.ts @@ -0,0 +1,60 @@ +/** + * A line diff for the review queue and history: what a change adds and + * removes, with unchanged lines folded down to a little context. + */ +export type DiffLine = { kind: "same" | "add" | "remove"; text: string } | { kind: "skip"; count: number }; + +/** Notes are short, so a plain longest-common-subsequence table is enough. */ +const MAX_CELLS = 4_000_000; + +export function diffLines(before: string, after: string, context = 3): DiffLine[] { + const a = before === "" ? [] : before.replace(/\r\n/g, "\n").split("\n"); + const b = after === "" ? [] : after.replace(/\r\n/g, "\n").split("\n"); + let full: DiffLine[]; + if (a.length * b.length > MAX_CELLS) { + // Too large to compare line by line: show it as replaced. + full = [...a.map((text) => ({ kind: "remove" as const, text })), ...b.map((text) => ({ kind: "add" as const, text }))]; + } else { + const n = a.length; + const m = b.length; + const lcs = Array.from({ length: n + 1 }, () => new Uint32Array(m + 1)); + for (let i = n - 1; i >= 0; i--) { + for (let j = m - 1; j >= 0; j--) { + lcs[i]![j] = a[i] === b[j] ? lcs[i + 1]![j + 1]! + 1 : Math.max(lcs[i + 1]![j]!, lcs[i]![j + 1]!); + } + } + full = []; + let i = 0; + let j = 0; + while (i < n && j < m) { + if (a[i] === b[j]) { + full.push({ kind: "same", text: a[i]! }); + i++; + j++; + } else if (lcs[i + 1]![j]! >= lcs[i]![j + 1]!) full.push({ kind: "remove", text: a[i++]! }); + else full.push({ kind: "add", text: b[j++]! }); + } + while (i < n) full.push({ kind: "remove", text: a[i++]! }); + while (j < m) full.push({ kind: "add", text: b[j++]! }); + } + return fold(full, context); +} + +function fold(lines: DiffLine[], context: number): DiffLine[] { + const changed = lines.map((l) => l.kind !== "same"); + const keep = lines.map((_, i) => { + for (let k = Math.max(0, i - context); k <= Math.min(lines.length - 1, i + context); k++) if (changed[k]) return true; + return false; + }); + const out: DiffLine[] = []; + let skipped = 0; + lines.forEach((l, i) => { + if (keep[i]) { + if (skipped) out.push({ kind: "skip", count: skipped }); + skipped = 0; + out.push(l); + } else skipped++; + }); + if (skipped) out.push({ kind: "skip", count: skipped }); + return out; +} diff --git a/services/admin/src/html.ts b/services/admin/src/html.ts new file mode 100644 index 0000000..81ef0eb --- /dev/null +++ b/services/admin/src/html.ts @@ -0,0 +1,46 @@ +/** + * A tagged template that escapes every value unless it is already HTML. The + * admin page shows text agents wrote, so nothing reaches the page unescaped. + */ +export class Html { + constructor(readonly value: string) {} + toString() { + return this.value; + } +} + +export const raw = (s: string) => new Html(s); + +export function escape(s: unknown): string { + return String(s ?? "") + .replace(/&/g, "&") + .replace(//g, ">") + .replace(/"/g, """) + .replace(/'/g, "'"); +} + +const part = (v: unknown): string => { + if (v instanceof Html) return v.value; + if (Array.isArray(v)) return v.map(part).join(""); + if (v === null || v === undefined || v === false) return ""; + return escape(v); +}; + +export function html(strings: TemplateStringsArray, ...values: unknown[]): Html { + let out = strings[0] ?? ""; + values.forEach((v, i) => { + out += part(v) + (strings[i + 1] ?? ""); + }); + return new Html(out); +} + +/** A query string from the set values only. */ +export function qs(params: Record): string { + const u = new URLSearchParams(); + for (const [k, v] of Object.entries(params)) if (v !== undefined && v !== null && v !== "" && v !== false) u.set(k, String(v)); + const s = u.toString(); + return s ? `?${s}` : ""; +} + +export const when = (d: string | null | undefined) => (d ? d.replace("T", " ").slice(0, 16) + " UTC" : "never"); diff --git a/services/admin/src/layout.ts b/services/admin/src/layout.ts new file mode 100644 index 0000000..7b8dc0c --- /dev/null +++ b/services/admin/src/layout.ts @@ -0,0 +1,49 @@ +import { html, type Html } from "./html.ts"; + +export interface Flash { + msg?: string; + err?: string; + reasons?: string[]; +} + +const nav: [string, string][] = [ + ["/", "Overview"], + ["/review", "Review"], + ["/notes", "Notes"], + ["/drafts", "Drafts"], + ["/capture", "Capture"], + ["/store", "Store"], + ["/problems", "Problems"], +]; + +export function layout(p: { title: string; path: string; held: number; flash?: Flash; body: Html }): Html { + const current = (href: string) => (href === "/" ? p.path === "/" : p.path.startsWith(href)); + return html` + + + + + +${p.title} · GregKilmer.com admin + + + + + +
+ GregKilmer.com admin + +
+
+${p.flash?.msg ? html`
${p.flash.msg}
` : ""} +${p.flash?.err + ? html`` + : ""} +${p.body} +
+ +`; +} diff --git a/services/admin/src/main.ts b/services/admin/src/main.ts new file mode 100644 index 0000000..93dbe58 --- /dev/null +++ b/services/admin/src/main.ts @@ -0,0 +1,19 @@ +/** + * Starts the admin page. It talks to the content service with the admin + * token and must be published on the tailnet only. + */ +import { ContentApi } from "./client.ts"; +import { createAdminServer } from "./server.ts"; + +const contentUrl = process.env.CONTENT_URL ?? "http://localhost:4310"; +const token = process.env.ADMIN_TOKEN; +if (!token) throw new Error("ADMIN_TOKEN is required: the content service's admin token"); +const port = Number(process.env.PORT ?? 4320); +const host = process.env.HOST ?? "0.0.0.0"; + +const server = createAdminServer(new ContentApi(contentUrl, token)); +server.listen(port, host, () => console.log(`Admin page on ${host}:${port}, using ${contentUrl}`)); + +const stop = () => server.close(() => process.exit(0)); +process.on("SIGTERM", stop); +process.on("SIGINT", stop); diff --git a/services/admin/src/markdown.ts b/services/admin/src/markdown.ts new file mode 100644 index 0000000..ea2628b --- /dev/null +++ b/services/admin/src/markdown.ts @@ -0,0 +1,28 @@ +/** + * Draft previews. Raw HTML in a note is shown as text and only http, https, + * mailto and relative links survive, so a draft an agent wrote cannot run + * anything on the admin page. Wikilinks become links to the note. + */ +import { Marked, type Tokens } from "marked"; +import { escape } from "./html.ts"; + +const safeUrl = (href: string) => (/^(https?:|mailto:|\/|#|\.\/|\.\.\/)/i.test(href.trim()) || !/^[a-z][a-z0-9+.-]*:/i.test(href.trim()) ? href : "#"); + +export function renderMarkdown(body: string, opts: { noteHref: (name: string) => string; imageSrc: (file: string) => string }): string { + const marked = new Marked({ + gfm: true, + renderer: { + html: ({ text }: Tokens.HTML | Tokens.Tag) => escape(text), + link({ href, title, tokens }: Tokens.Link) { + const t = title ? ` title="${escape(title)}"` : ""; + return `${this.parser.parseInline(tokens)}`; + }, + image: ({ href, text }: Tokens.Image) => `${escape(text)}`, + }, + }); + // ![[file.png]] embeds and [[Note|label]] links, before Markdown sees them. + const withLinks = body + .replace(/!\[\[([^\]|]+)(?:\|[^\]]*)?\]\]/g, (_, f: string) => `![${f}](${opts.imageSrc(f.trim())})`) + .replace(/\[\[([^\]|#]+)(?:#[^\]|]*)?(?:\|([^\]]+))?\]\]/g, (_, n: string, label?: string) => `[${label ?? n}](${opts.noteHref(n.trim())})`); + return marked.parse(withLinks, { async: false }) as string; +} diff --git a/services/admin/src/pages.ts b/services/admin/src/pages.ts new file mode 100644 index 0000000..a752ec9 --- /dev/null +++ b/services/admin/src/pages.ts @@ -0,0 +1,289 @@ +/** + * Page bodies. Each takes data already fetched from the content service and + * returns HTML; nothing here talks to the network. + */ +import { libraryStates, media } from "@gk/schema"; +import type { FullNote, Held, Note, Problem, Revision, Status, Summary, SyncJob } from "./client.ts"; +import { diffLines } from "./diff.ts"; +import { html, qs, raw, when, type Html } from "./html.ts"; +import { renderMarkdown } from "./markdown.ts"; + +export const noteHref = (path: string) => `/note${qs({ path })}`; + +const authorLabel: Record = { greg: "You", agent: "An agent", admin: "Admin page" }; +const who = (a: string) => authorLabel[a] ?? a; + +const publishPill = (publish: boolean) => (publish ? html`Live` : html`Draft`); + +const reasonList = (reasons: Held["reasons"]) => + reasons.length + ? reasons.map((r) => html`

${r.code} ${r.message}

`) + : html`

Waiting for review: no rule flagged it.

`; + +export function diffView(before: string, after: string): Html { + const lines = diffLines(before, after); + if (!lines.some((l) => l.kind === "add" || l.kind === "remove")) return html`

No differences.

`; + return html`
${lines.map((l) => + l.kind === "skip" ? html`` : html`
${l.text}
`, + )}
`; +} + +export function preview(body: string): Html { + if (!body.trim()) return html`

This note has no body text.

`; + return html`
${raw( + renderMarkdown(body, { + noteHref: (name) => `/go${qs({ name })}`, + imageSrc: (file) => `/attachments/${encodeURIComponent(file)}`, + }), + )}
`; +} + +// ------------------------------------------------------------ overview + +export function overview(s: Status, held: Held[], problems: Problem[]): Html { + const syncs = s.syncs; + return html` +

Overview

+

What the site is serving, what is waiting for you, and whether the jobs that feed it are running.

+
+
${s.counts.published}
live notes of ${s.counts.notes}
+
${held.length}
waiting for review
+
${problems.length}
problems across the folder
+
+

Content service

+
+
+
Running since
${when(s.startedAt)}
+
Last full scan
${s.lastScan ? html`${when(s.lastScan.at)}: ${s.lastScan.changed} changed, ${s.lastScan.held} held` : "not yet"}
+
Last error
${s.lastError ? html`${when(s.lastError.at)} ${s.lastError.message}` : "none"}
+
+
+
+

Sync jobs

+${syncs.length + ? html`
+ +${syncs.map(syncRow)}
JobLast runResultLast successCounts
` + : html`

No sync job has reported yet. PersonalReader, BoardGameGeek and the store syncs report here once they run.

`}`; +} + +const syncRow = (j: SyncJob) => html` + ${j.job} + ${when(j.at)} + ${j.ok ? html`OK` : html`Failed`} ${j.message} + ${when(j.lastOkAt)} + ${Object.entries(j.counts).map(([k, v]) => `${k} ${v}`).join(", ")} +`; + +export function syncHistory(job: string, runs: SyncJob[]): Html { + return html`

${job}

Back to the overview

+
+${runs.map((r) => html``)} +
WhenResultCounts
${when(r.at)}${r.ok ? html`OK` : html`Failed`} ${r.message}${Object.entries(r.counts).map(([k, v]) => `${k} ${v}`).join(", ")}
`; +} + +// ------------------------------------------------------------ review + +const statuses = ["pending", "accepted", "rejected", "superseded"]; + +export function reviewList(held: Held[], status: string): Html { + return html`

Review

+

Changes the rules held. The site keeps showing the last accepted version until you decide.

+

${statuses.map((s) => (s === status ? html`${label(s)}` : html`${label(s)}`))}

+${held.length + ? html`
+${held.map((h) => html` + + + + `)} +
NoteFromWhyWhen
${h.path}${who(h.author)}${h.source === "vault" ? " in Obsidian" : ""}${h.reasons.map((r) => r.code).join(", ") || "review"}${when(h.at)}
` + : html`

${status === "pending" ? "Nothing is waiting." : "None."}

`}`; +} + +const label = (s: string) => s[0]!.toUpperCase() + s.slice(1); + +export function reviewOne(h: Held): Html { + const pending = h.status === "pending"; + return html`

${h.path}

+

${who(h.author)}${h.source === "vault" ? " in Obsidian" : " through the API"}, ${when(h.at)}. ${pending ? "" : html`${label(h.status)} ${when(h.decidedAt)}`}

+
${reasonList(h.reasons)}
+${pending + ? html`
+
+
+ ${h.source === "vault" && h.current + ? html`
` + : ""} +
` + : ""} +

${h.current === null ? "New note" : "Changes against the live version"}

+${diffView(h.current ?? "", h.text ?? "")} +

Open the live note

`; +} + +// ------------------------------------------------------------ notes + +export function notesList(p: { title: string; lede: string; action: string; notes: Summary[]; types: string[]; type?: string; q?: string }): Html { + return html`

${p.title}

+

${p.lede}

+
+
+
+ +
+${p.notes.length + ? html`
+${p.notes.map((n) => html``)} +
TitleTypeStateChanged
${n.title}
${n.path}
${n.type}${publishPill(n.publish)}${when(n.acceptedAt)}
` + : html`

No notes match.

`}`; +} + +const fieldValue = (v: unknown): Html => + typeof v === "object" && v !== null ? html`
${JSON.stringify(v, null, 2)}
` : html`${String(v)}`; + +export function noteView(n: Note, history: Revision[]): Html { + const shown = Object.entries(n.data).filter(([k]) => !["type", "title"].includes(k)); + return html`

${n.title}

+

${publishPill(n.publish)} ${n.type} · ${n.path} · last changed by ${who(n.author).toLowerCase()} ${when(n.acceptedAt)}

+${n.pending ? html`
A change to this note is waiting: review it.
` : ""} +
+
+ + + +
+
+

Preview

+${preview(n.body)} +

Fields

+
${shown.map(([k, v]) => html`
${k}
${fieldValue(v)}
`)}
+

Linked from

+${n.backlinks.length ? html`
    ${n.backlinks.map((b) => html`
  • ${b.title} ${b.type}
  • `)}
` : html`

Nothing links here yet.

`} +

History

+${historyTable(n, history)}`; +} + +function historyTable(n: Note, history: Revision[]): Html { + return html`
+${history.map((r) => html` + + + +`)} +
VersionWhoWhatWhen
${r.rev}${r.rev === n.rev ? html` current` : ""}${who(r.author)}${r.action}${when(r.at)}${r.rev === n.rev || r.action === "remove" ? "" : html`Compare and roll back`}
`; +} + +export function revisionView(n: Note, r: Revision): Html { + return html`

${n.title}, version ${r.rev}

+

${who(r.author)}, ${when(r.at)}. Showing what rolling back would change in the live version ${n.rev}.

+
+
+ + +
+ Back to the note +
+${diffView(n.text, r.text ?? "")}`; +} + +// ------------------------------------------------------------ capture + +export function capture(kinds: string[], today: string): Html { + return html`

Capture

+

Add a line to the Now log or something to the Library. Both are written into the vault, so they show up in Obsidian too.

+
+
+

Status

+
+
+
+
+ + + + + +
+
+
+

Library item

+ +
+
+
+
+
+
+
+
+ + + + + + +
+
+
`; +} + +// ------------------------------------------------------------ store + +interface Category { + name: string; + ordersOpen: boolean; + reopens?: string; + turnaround?: string; +} + +export function store(settings: FullNote | undefined, lines: FullNote[]): Html { + const categories = (settings?.data.categories as Category[] | undefined) ?? []; + return html`

Store

+

Open and close orders by category, and set how many order slots each made to order line has open.

+

Categories

+${settings + ? html`
${categories.map( + (c) => html`
+

${c.name} ${c.ordersOpen ? html`Open` : html`Closed`}

+ + + + +
+
`, + )}
` + : html`

There is no Store/_settings note yet.

`} +

Product lines

+
+${lines.map((l) => { + const slots = l.data.slots as { open: number; total: number } | undefined; + const qty = l.data.quantity as number | undefined; + return html` + + + + +`; + })} +
LineCategorySoldOpen slots or stock
${l.title} ${publishPill(l.publish)}${String(l.data.category)}${String(l.data.mode)}, ${String(l.data.channel)}${slots + ? html`
+ of ${slots.total}
` + : qty !== undefined + ? html`
+ in stock
` + : html`Not counted here`}
`; +} + +// ------------------------------------------------------------ problems + +export function problemsView(problems: Problem[]): Html { + return html`

Problems

+

Things that are wrong across the folder as a whole, like links to notes that do not exist. They do not stop a note going live.

+${problems.length + ? html`
+${problems.map((p) => html``)}
NoteProblem
${p.path}${p.message}
` + : html`

No problems.

`}`; +} diff --git a/services/admin/src/server.ts b/services/admin/src/server.ts new file mode 100644 index 0000000..27bdaaa --- /dev/null +++ b/services/admin/src/server.ts @@ -0,0 +1,357 @@ +/** + * The admin page's server. It renders HTML on the server and every action is + * a plain form post, so it works without client scripts. It holds the + * content service's admin token; the browser never sees it. + * + * It must only be reachable on the tailnet. On top of that, every form post + * has to come from this page itself, so another site cannot post to it. + */ +import { readFileSync } from "node:fs"; +import { createServer, type IncomingMessage, type ServerResponse } from "node:http"; +import { basename } from "node:path"; +import { Readable } from "node:stream"; +import { noteTypeNames } from "@gk/schema"; +import { ApiError, type ContentApi, type Outcome } from "./client.ts"; +import { html, qs, type Html } from "./html.ts"; +import { layout, type Flash } from "./layout.ts"; +import * as pages from "./pages.ts"; + +const css = readFileSync(new URL("../public/admin.css", import.meta.url), "utf8"); + +class Redirect { + constructor(readonly to: string) {} +} + +type Form = URLSearchParams; +type Page = { title: string; body: Html }; + +export function createAdminServer(api: ContentApi, opts: { now?: () => Date } = {}) { + const today = () => (opts.now?.() ?? new Date()).toISOString().slice(0, 10); + + /** Where to go after an action, carrying what happened. */ + const after = (to: string, o: Outcome, saved: string) => { + const [path, query = ""] = to.split("?"); + const p = new URLSearchParams(query); + if (o.status === "accepted" || o.status === "unchanged") p.set("msg", saved); + else if (o.status === "held") { + p.set("msg", "Saved for review. It is not live until you accept it."); + } else { + p.set("err", "Not saved."); + for (const r of o.reasons) p.append("r", r.message); + } + return new Redirect(`${path}?${p}`); + }; + + const pathOfOutcome = (o: Outcome) => (o.status === "rejected" ? null : o.path); + + async function get(path: string, q: URLSearchParams): Promise { + switch (path) { + case "/": { + const [status, held, problems] = await Promise.all([api.status(), api.held(), api.problems()]); + return { title: "Overview", body: pages.overview(status, held, problems) }; + } + case "/syncs": { + const job = q.get("job") ?? ""; + return { title: job, body: pages.syncHistory(job, await api.syncRuns(job)) }; + } + case "/review": { + const status = q.get("status") ?? "pending"; + return { title: "Review", body: pages.reviewList(await api.held(status), status) }; + } + case "/notes": + case "/drafts": { + const drafts = path === "/drafts"; + const type = q.get("type") || undefined; + const text = q.get("q") || undefined; + let notes = await api.notes({ type, q: text, limit: 1000 }); + if (drafts) notes = notes.filter((n) => !n.publish); + if (!text) notes = notes.sort((a, b) => b.acceptedAt.localeCompare(a.acceptedAt)).slice(0, 300); + return { + title: drafts ? "Drafts" : "Notes", + body: pages.notesList({ + title: drafts ? "Drafts" : "Notes", + lede: drafts + ? "Notes that are not on the site yet. Open one to preview it and publish it." + : "Every note the site knows about, most recently changed first.", + action: path, + notes, + types: noteTypeNames, + type, + q: text, + }), + }; + } + case "/note": { + const p = required(q, "path"); + const [note, history] = await Promise.all([api.note(p), api.history(p)]); + return { title: note.title, body: pages.noteView(note, history) }; + } + case "/note/revision": { + const p = required(q, "path"); + const [note, rev] = await Promise.all([api.note(p), api.revision(p, Number(required(q, "rev")))]); + return { title: `${note.title}, version ${rev.rev}`, body: pages.revisionView(note, rev) }; + } + case "/go": { + const note = await api.noteByName(required(q, "name")); + return new Redirect(pages.noteHref(note.path)); + } + case "/capture": { + const kinds = (await api.fullNotes("status-kinds"))[0]?.data.kinds as { label: string }[] | undefined; + return { title: "Capture", body: pages.capture((kinds ?? []).map((k) => k.label), today()) }; + } + case "/store": { + const [settings, lines] = await Promise.all([api.fullNotes("store-settings"), api.fullNotes("product-line")]); + return { title: "Store", body: pages.store(settings[0], lines.sort((a, b) => Number(a.data.order ?? 99) - Number(b.data.order ?? 99))) }; + } + case "/problems": + return { title: "Problems", body: pages.problemsView(await api.problems()) }; + } + const review = path.match(/^\/review\/(\d+)$/); + if (review) { + const h = await api.heldOne(Number(review[1])); + return { title: `Review ${h.path}`, body: pages.reviewOne(h) }; + } + throw new ApiError(404, "There is no page here.", null); + } + + async function post(path: string, f: Form): Promise { + switch (path) { + case "/scan": { + const r = await api.scan(); + return new Redirect(`/${qs({ msg: `Scanned: ${r.changed} changed, ${r.held} held.` })}`); + } + case "/note/rollback": { + const p = required(f, "path"); + const o = await api.rollback(p, Number(required(f, "rev"))); + return after(pages.noteHref(p), o, "Rolled back. The site and the vault show that version again."); + } + case "/note/publish": { + const p = required(f, "path"); + const publish = f.get("publish") === "1"; + const o = await api.apply({ op: "update", path: p, fields: { publish } }); + return after(pages.noteHref(p), o, publish ? "Published." : "Taken off the site."); + } + case "/capture/status": { + const subject = (f.get("subject") ?? "").trim().replace(/^\[\[|\]\]$/g, ""); + const o = await api.apply({ + op: "create", + data: { + type: "status", + title: required(f, "title"), + kind: required(f, "kind"), + date: f.get("date") || today(), + ...optional(f, ["note"]), + ...(subject ? { subject: `[[${subject}]]` } : {}), + ...(f.get("pinned") === "1" ? { pinned: true } : {}), + publish: f.get("publish") === "1", + }, + }); + return after("/capture", o, "Status posted."); + } + case "/capture/library": { + const year = f.get("year"); + const o = await api.apply({ + op: "create", + data: { + type: "library-item", + title: required(f, "title"), + medium: required(f, "medium"), + state: required(f, "state"), + ...optional(f, ["creator", "group", "verdict", "note"]), + ...(year ? { year: Number(year) } : {}), + publish: f.get("publish") === "1", + }, + }); + const added = pathOfOutcome(o); + if (f.get("alsoStatus") === "1" && o.status === "accepted" && added) { + const s = await api.apply({ + op: "create", + data: { + type: "status", + title: required(f, "title"), + kind: required(f, "kind"), + date: today(), + subject: `[[${basename(added, ".md")}]]`, + publish: f.get("publish") === "1", + }, + }); + return after("/capture", s, "Added to the Library and posted a status."); + } + return after("/capture", o, "Added to the Library."); + } + case "/store/category": { + const [settings] = await api.fullNotes("store-settings"); + if (!settings) throw new ApiError(404, "There is no Store/_settings note yet.", null); + const name = required(f, "name"); + const categories = (settings.data.categories as Record[]).map((c) => { + if (c.name !== name) return c; + const next: Record = { ...c, ordersOpen: f.get("ordersOpen") === "1" }; + for (const k of ["reopens", "turnaround"]) { + const v = (f.get(k) ?? "").trim(); + if (v) next[k] = v; + else delete next[k]; + } + return next; + }); + const o = await api.apply({ op: "update", path: settings.path, fields: { categories } }); + return after("/store", o, `${name} saved.`); + } + case "/store/stock": { + const p = required(f, "path"); + let fields: Record; + if (f.has("open")) { + const note = await api.note(p); + fields = { slots: { ...(note.data.slots as object), open: Number(f.get("open")) } }; + } else fields = { quantity: Number(required(f, "quantity")) }; + const o = await api.apply({ op: "update", path: p, fields }); + return after("/store", o, "Saved."); + } + } + const decide = path.match(/^\/review\/(\d+)\/(accept|reject)$/); + if (decide) { + const id = Number(decide[1]); + const o = decide[2] === "accept" ? await api.accept(id) : await api.reject(id, f.get("restore") === "1"); + if (o.status === "rejected") return after(`/review/${id}`, o, ""); + return new Redirect(`/review${qs({ msg: decide[2] === "accept" ? "Accepted. It is live." : "Rejected. The site keeps the live version." })}`); + } + throw new ApiError(404, "There is no action here.", null); + } + + async function handle(req: IncomingMessage, res: ServerResponse) { + const url = new URL(req.url ?? "/", "http://admin"); + const method = req.method ?? "GET"; + res.setHeader("x-frame-options", "DENY"); + res.setHeader("referrer-policy", "same-origin"); + res.setHeader( + "content-security-policy", + "default-src 'self'; img-src 'self' data: https:; style-src 'self' https://fonts.googleapis.com; font-src https://fonts.gstatic.com; script-src 'none'; form-action 'self'; frame-ancestors 'none'", + ); + + if (url.pathname === "/admin.css") { + res.writeHead(200, { "content-type": "text/css; charset=utf-8", "cache-control": "max-age=300" }); + return res.end(css); + } + if (url.pathname === "/health") { + res.writeHead(200, { "content-type": "application/json" }); + return res.end('{"ok":true}'); + } + + const attachment = url.pathname.match(/^\/attachments\/([^/]+)$/); + if (method === "GET" && attachment) { + const r = await api.attachment(decodeURIComponent(attachment[1] ?? "")); + res.writeHead(r.status, { "content-type": r.headers.get("content-type") ?? "application/octet-stream", "x-content-type-options": "nosniff" }); + if (r.body) Readable.fromWeb(r.body as never).pipe(res); + else res.end(); + return; + } + + if (method === "POST") { + if (!sameOrigin(req)) return text(res, 403, "Form posts must come from the admin page itself."); + const form = new URLSearchParams(await readBody(req)); + const r = await post(url.pathname, form); + res.writeHead(303, { location: r.to }); + return res.end(); + } + if (method !== "GET") return text(res, 405, "Method not allowed"); + + const out = await get(url.pathname, url.searchParams); + if (out instanceof Redirect) { + res.writeHead(303, { location: out.to }); + return res.end(); + } + const held = (await api.held().catch(() => [])).length; + page(res, 200, out, url, held); + } + + function page(res: ServerResponse, status: number, p: Page, url: URL, held: number) { + const flash: Flash = { + msg: url.searchParams.get("msg") ?? undefined, + err: url.searchParams.get("err") ?? undefined, + reasons: url.searchParams.getAll("r"), + }; + res.writeHead(status, { "content-type": "text/html; charset=utf-8", "cache-control": "no-store" }); + res.end(layout({ title: p.title, path: url.pathname, held, flash, body: p.body }).toString()); + } + + return createServer((req, res) => { + handle(req, res).catch(async (err: unknown) => { + if (res.headersSent) return res.end(); + const url = new URL(req.url ?? "/", "http://admin"); + if (err instanceof ApiError) { + const reasons = (err.body as { reasons?: { message: string }[] } | null)?.reasons?.map((r) => r.message) ?? []; + const message = err.status === 404 ? "Not found." : err.message; + // A failed form post goes back where it came from, with the reason. + if (req.method === "POST") { + const back = safeBack(req.headers.referer) ?? new URL("/", "http://admin"); + for (const k of ["msg", "err", "r"]) back.searchParams.delete(k); + back.searchParams.set("err", message); + for (const r of reasons) back.searchParams.append("r", r); + res.writeHead(303, { location: back.pathname + back.search }); + return res.end(); + } + return page(res, err.status === 404 ? 404 : err.status === 400 ? 400 : 502, { title: "Not available", body: errorBody(message) }, url, 0); + } + console.error(err); + page(res, 500, { title: "Error", body: errorBody("Something went wrong. Is the content service running?") }, url, 0); + }); + }); +} + +const errorBody = (message: string) => html`

Not available

${message}

Back to the overview

`; + +function required(f: URLSearchParams, k: string): string { + const v = (f.get(k) ?? "").trim(); + if (!v) throw new ApiError(400, `${k} is required`, null); + return v; +} + +function optional(f: URLSearchParams, keys: string[]): Record { + const out: Record = {}; + for (const k of keys) { + const v = (f.get(k) ?? "").trim(); + if (v) out[k] = v; + } + return out; +} + +/** Browsers send Sec-Fetch-Site or Origin with every form post; both must say this page. */ +function sameOrigin(req: IncomingMessage): boolean { + const site = req.headers["sec-fetch-site"]; + if (site) return site === "same-origin" || site === "none"; + const origin = req.headers.origin; + if (origin) { + try { + return new URL(origin).host === req.headers.host; + } catch { + return false; + } + } + // Neither header: not a browser (a script on the tailnet), which the network already limits. + return true; +} + +/** The page a failed post came from, as a local path only. */ +function safeBack(referer: string | undefined): URL | null { + if (!referer) return null; + try { + return new URL(new URL(referer).pathname + new URL(referer).search, "http://admin"); + } catch { + return null; + } +} + +async function readBody(req: IncomingMessage): Promise { + const chunks: Buffer[] = []; + let size = 0; + for await (const c of req) { + size += (c as Buffer).length; + if (size > 200_000) throw new ApiError(413, "The form is too large", null); + chunks.push(c as Buffer); + } + return Buffer.concat(chunks).toString("utf8"); +} + +function text(res: ServerResponse, status: number, body: string) { + res.writeHead(status, { "content-type": "text/plain; charset=utf-8" }); + res.end(body); +} diff --git a/services/admin/test/admin.test.ts b/services/admin/test/admin.test.ts new file mode 100644 index 0000000..8a8c415 --- /dev/null +++ b/services/admin/test/admin.test.ts @@ -0,0 +1,152 @@ +import { existsSync, readFileSync } from "node:fs"; +import type { AddressInfo } from "node:net"; +import type { Server } from "node:http"; +import { join } from "node:path"; +import { afterAll, beforeAll, describe, expect, it } from "vitest"; +import { parseTokens } from "../../content/src/config.ts"; +import { createHttpServer } from "../../content/src/http.ts"; +import { book, rig, type Rig } from "../../content/test/helpers.ts"; +import { ContentApi } from "../src/client.ts"; +import { createAdminServer } from "../src/server.ts"; + +const ADMIN = "d".repeat(24); +let r: Rig; +let content: Server; +let admin: Server; +let base: string; + +const listen = async (s: Server) => { + await new Promise((ok) => s.listen(0, "127.0.0.1", ok)); + return `http://127.0.0.1:${(s.address() as AddressInfo).port}`; +}; + +beforeAll(async () => { + r = rig(); + r.put({ type: "status-kinds", title: "Status kinds", publish: true, kinds: [{ label: "Reading", icon: "book" }, { label: "Playing", icon: "dice" }] }); + r.put( + { + type: "store-settings", + title: "Store settings", + publish: true, + categories: [ + { name: "Tabletop", ordersOpen: true, turnaround: "2 weeks" }, + { name: "Paper", ordersOpen: false }, + ], + }, + ); + r.put({ + type: "product-line", + title: "Proxy cards", + publish: true, + category: "Tabletop", + mode: "made to order", + channel: "here", + summary: "Cards.", + slots: { open: 3, total: 5 }, + priceTiers: [{ band: "1-10", price: "$1" }], + }); + r.put(book("Mistborn"), "Allomancy."); + r.service.scan(); + content = createHttpServer(r.service, { tokens: parseTokens(`admin:${ADMIN}`) }); + const contentUrl = await listen(content); + admin = createAdminServer(new ContentApi(contentUrl, ADMIN), { now: () => new Date("2026-10-11T12:00:00Z") }); + base = await listen(admin); +}); +afterAll(() => { + admin.close(); + content.close(); + r.done(); +}); + +const get = async (path: string) => { + const res = await fetch(base + path, { redirect: "manual" }); + return { status: res.status, text: await res.text(), location: res.headers.get("location") }; +}; +const post = async (path: string, form: Record, headers: Record = {}) => { + const res = await fetch(base + path, { + method: "POST", + redirect: "manual", + headers: { "content-type": "application/x-www-form-urlencoded", "sec-fetch-site": "same-origin", ...headers }, + body: new URLSearchParams(form).toString(), + }); + return { status: res.status, location: res.headers.get("location") ?? "" }; +}; + +describe("admin page", () => { + it("shows the overview", async () => { + const page = await get("/"); + expect(page.status).toBe(200); + expect(page.text).toContain("live notes of 4"); + expect(page.text).toContain("No sync job has reported yet"); + }); + + it("refuses a form posted from another site", async () => { + expect((await post("/scan", {}, { "sec-fetch-site": "cross-site" })).status).toBe(403); + }); + + it("captures a Library item with a status, written into the vault", async () => { + const res = await post("/capture/library", { title: "Warbreaker", medium: "book", state: "now", publish: "1", alsoStatus: "1", kind: "Reading" }); + expect(res.location).toContain("msg="); + expect(existsSync(join(r.site, "Library/Books/Warbreaker.md"))).toBe(true); + const status = r.store.list({ type: "status" })[0]!; + expect(status.data).toMatchObject({ kind: "Reading", subject: "[[Warbreaker]]", date: "2026-10-11" }); + expect(status.author).toBe("admin"); + }); + + it("sends a rejected capture back with the reasons", async () => { + const res = await post("/capture/library", { title: "Warbreaker", medium: "book", state: "now" }); + expect(res.location).toMatch(/err=.*r=/); + }); + + it("reviews a held change with a diff and accepts it", async () => { + const o = r.service.apply({ op: "update", path: "Library/Books/Mistborn.md", fields: { verdict: "A heist." } }, "agent"); + expect(o.status).toBe("held"); + const id = o.status === "held" ? o.held.id : 0; + const list = await get("/review"); + expect(list.text).toContain(`/review/${id}`); + const one = await get(`/review/${id}`); + expect(one.text).toContain('
verdict: A heist.
'); + expect(one.text).toContain("agent-edit"); + const res = await post(`/review/${id}/accept`, {}); + expect(res.location).toContain("Accepted"); + expect(readFileSync(join(r.site, "Library/Books/Mistborn.md"), "utf8")).toContain("verdict: A heist."); + }); + + it("previews a note, shows its history and rolls it back", async () => { + const page = await get("/note?path=Library%2FBooks%2FMistborn.md"); + expect(page.text).toContain("

Allomancy.

"); + expect(page.text).toContain("Compare and roll back"); + const rev = await get("/note/revision?path=Library%2FBooks%2FMistborn.md&rev=1"); + expect(rev.text).toContain('
verdict: A heist.
'); + await post("/note/rollback", { path: "Library/Books/Mistborn.md", rev: "1" }); + expect(r.store.get("Library/Books/Mistborn.md")!.data.verdict).toBeUndefined(); + }); + + it("publishes a draft", async () => { + r.put(book("Elantris", { publish: false })); + r.service.scan(); + expect((await get("/drafts")).text).toContain("Elantris"); + await post("/note/publish", { path: "Library/Books/Elantris.md", publish: "1" }); + expect(r.store.get("Library/Books/Elantris.md")!.publish).toBe(true); + }); + + it("opens and closes orders and sets slots", async () => { + expect((await get("/store")).text).toContain("Proxy cards"); + await post("/store/category", { name: "Paper", ordersOpen: "1", turnaround: "3 weeks" }); + await post("/store/category", { name: "Tabletop" }); + const cats = r.store.get("Store/_settings.md")!.data.categories; + expect(cats).toEqual([ + { name: "Tabletop", ordersOpen: false }, + { name: "Paper", ordersOpen: true, turnaround: "3 weeks" }, + ]); + await post("/store/stock", { path: "Store/Lines/Proxy cards.md", open: "1" }); + expect(r.store.get("Store/Lines/Proxy cards.md")!.data.slots).toEqual({ open: 1, total: 5 }); + }); + + it("shows sync jobs once they report", async () => { + r.store.reportSync({ job: "personalreader", at: "2026-10-11T11:00:00Z", ok: false, message: "Suwayomi did not answer", counts: { updated: 0 } }); + const page = await get("/"); + expect(page.text).toContain("personalreader"); + expect(page.text).toContain("Suwayomi did not answer"); + }); +}); diff --git a/services/admin/test/units.test.ts b/services/admin/test/units.test.ts new file mode 100644 index 0000000..662e559 --- /dev/null +++ b/services/admin/test/units.test.ts @@ -0,0 +1,50 @@ +import { describe, expect, it } from "vitest"; +import { diffLines } from "../src/diff.ts"; +import { html, raw } from "../src/html.ts"; +import { renderMarkdown } from "../src/markdown.ts"; + +const md = (s: string) => renderMarkdown(s, { noteHref: (n) => `/go?name=${encodeURIComponent(n)}`, imageSrc: (f) => `/attachments/${f}` }); + +describe("diff", () => { + it("marks added and removed lines and folds the rest", () => { + const before = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"].join("\n"); + const after = ["a", "b", "c", "d", "e", "F", "g", "h", "i", "j"].join("\n"); + const d = diffLines(before, after, 1); + expect(d).toEqual([ + { kind: "skip", count: 4 }, + { kind: "same", text: "e" }, + { kind: "remove", text: "f" }, + { kind: "add", text: "F" }, + { kind: "same", text: "g" }, + { kind: "skip", count: 3 }, + ]); + }); + + it("shows a new note as all added", () => { + expect(diffLines("", "x\ny")).toEqual([ + { kind: "add", text: "x" }, + { kind: "add", text: "y" }, + ]); + }); +}); + +describe("html", () => { + it("escapes values but not nested html", () => { + expect(html`

${""}${html`${"&"}`}${raw("


")}

`.toString()).toBe("

<b>&


"); + }); +}); + +describe("draft previews", () => { + it("never lets a draft run script", () => { + const out = md('\n\n[x](javascript:alert(1)) '); + expect(out).not.toMatch(/ { + const out = md("See [[Mistborn|the book]] and ![[cover.png]]."); + expect(out).toContain('"); + expect(out).toContain(' {}); }, - onError: (err) => console.error("Watcher:", err), + onError: (err) => { + service.noteError(err); + console.error("Watcher:", err); + }, }); watcher.start(); diff --git a/services/content/src/service.ts b/services/content/src/service.ts index c5ba942..f3e632c 100644 --- a/services/content/src/service.ts +++ b/services/content/src/service.ts @@ -50,6 +50,17 @@ export class ContentService { this.onHold = o.onHold; } + /** What the admin page shows as the service's own health. */ + readonly health: { + startedAt: string; + lastScan: { at: string; changed: number; held: number } | null; + lastError: { at: string; message: string } | null; + } = { startedAt: new Date().toISOString(), lastScan: null, lastError: null }; + + noteError(err: unknown) { + this.health.lastError = { at: this.iso(), message: err instanceof Error ? err.message : String(err) }; + } + private iso = () => this.now().toISOString(); private today = () => this.iso().slice(0, 10); @@ -94,6 +105,7 @@ export class ContentService { if (o.status !== "unchanged") changed++; if (o.status === "held") held++; } + this.health.lastScan = { at: this.iso(), changed, held }; return { changed, held }; } diff --git a/services/content/src/store.ts b/services/content/src/store.ts index f64569b..1c4dce4 100644 --- a/services/content/src/store.ts +++ b/services/content/src/store.ts @@ -117,6 +117,15 @@ create table if not exists seen ( path text primary key, hash text not null ); +create table if not exists sync_runs ( + id integer primary key autoincrement, + job text not null, + at text not null, + ok integer not null, + message text not null, + counts text not null +); +create index if not exists sync_runs_job on sync_runs(job, id); create virtual table if not exists search using fts5(path unindexed, title, body, tokenize = 'porter unicode61'); `; @@ -152,6 +161,19 @@ const toHeld = (r: Row): HeldChange => ({ decidedAt: r.decided_at, }); +export interface SyncRun { + job: string; + at: string; + ok: boolean; + message: string; + counts: Record; +} + +const toSyncRun = (r: Row): SyncRun => ({ job: r.job, at: r.at, ok: !!r.ok, message: r.message, counts: JSON.parse(r.counts) }); + +/** Runs kept per sync job; older ones are dropped. */ +const SYNC_RUNS_KEPT = 50; + export class Store { readonly db: DatabaseSync; @@ -379,4 +401,29 @@ export class Store { seenPaths(): string[] { return (this.db.prepare("select path from seen").all() as Row[]).map((r) => r.path); } + + // ------------------------------------------------------------ sync jobs + + /** A sync job (PersonalReader, BoardGameGeek and the like) reports how its last run went. */ + reportSync(run: SyncRun) { + this.db + .prepare("insert into sync_runs (job, at, ok, message, counts) values (?, ?, ?, ?, ?)") + .run(run.job, run.at, run.ok ? 1 : 0, run.message, JSON.stringify(run.counts)); + this.db + .prepare("delete from sync_runs where job = ? and id not in (select id from sync_runs where job = ? order by id desc limit ?)") + .run(run.job, run.job, SYNC_RUNS_KEPT); + } + + /** Each job's latest run, with when it last succeeded. */ + syncJobs(): (SyncRun & { lastOkAt: string | null })[] { + const rows = this.db.prepare("select * from sync_runs where id in (select max(id) from sync_runs group by job) order by job").all() as Row[]; + return rows.map((r) => ({ + ...toSyncRun(r), + lastOkAt: ((this.db.prepare("select max(at) at from sync_runs where job = ? and ok = 1").get(r.job) as Row).at as string) ?? null, + })); + } + + syncRuns(job: string, limit = 20): SyncRun[] { + return (this.db.prepare("select * from sync_runs where job = ? order by id desc limit ?").all(job, limit) as Row[]).map(toSyncRun); + } } diff --git a/services/content/test/http.test.ts b/services/content/test/http.test.ts index a1b5331..3931aa2 100644 --- a/services/content/test/http.test.ts +++ b/services/content/test/http.test.ts @@ -66,6 +66,17 @@ describe("HTTP API", () => { }); }); +describe("sync reports", () => { + it("records a run and shows it in the status", async () => { + expect((await post_("/api/syncs", T.agent, { job: "personalreader", ok: true, counts: { updated: 3 } })).status).toBe(200); + expect((await post_("/api/syncs", T.agent, { job: "Bad Name", ok: true })).status).toBe(400); + const status = (await (await get("/api/status", T.admin)).json()) as { syncs: { job: string; lastOkAt: string }[] }; + expect(status.syncs[0]).toMatchObject({ job: "personalreader" }); + expect(status.syncs[0]!.lastOkAt).toBeTruthy(); + expect((await get("/api/status", T.reader)).status).toBe(403); + }); +}); + describe("MCP server", () => { it("lets an agent search, post a status and draft a post, but not publish it", async () => { const client = new Client({ name: "test", version: "1.0.0" }); From 807528a613b90b59c22353b1fc2980fcade2b4ab Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 11 Oct 2026 20:44:16 +0000 Subject: [PATCH 04/16] Phase 4: component library The approved element rules and ornament kit as Astro components in packages/ui: buttons, pills, chips, filters, kickers, notices, fields and cards; star paths, the header divider, tick scales and rings, seals and edge seals, gilt corners, dust and placeholder art; the site header, footer and page shell. Brand fonts are self-hosted. A gallery page shows everything, and astro check now runs with the root typecheck. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_012aSbo2D1qP8cMFQCykf7kx --- .gitignore | 4 + README.md | 6 +- docs/components.md | 79 + package-lock.json | 3877 ++++++++++++++++- package.json | 6 +- packages/ui/astro.config.mjs | 12 + packages/ui/package.json | 25 + packages/ui/src/assets/brand/gk-monogram.svg | 2 + .../ui/src/assets/brand/patiun-glyph-gold.svg | 2 + .../ui/src/assets/brand/patiun-glyph-teal.svg | 2 + .../ui/src/assets/fonts/Cinzel-Bold.woff2 | Bin 0 -> 20932 bytes .../ui/src/assets/fonts/Cinzel-Regular.woff2 | Bin 0 -> 19448 bytes .../ui/src/assets/fonts/Cinzel-SemiBold.woff2 | Bin 0 -> 20984 bytes packages/ui/src/assets/fonts/LICENSE.md | 10 + .../ui/src/assets/fonts/Montserrat-Bold.woff2 | Bin 0 -> 26904 bytes .../src/assets/fonts/Montserrat-Medium.woff2 | Bin 0 -> 26672 bytes .../src/assets/fonts/Montserrat-Regular.woff2 | Bin 0 -> 26408 bytes .../assets/fonts/Montserrat-SemiBold.woff2 | Bin 0 -> 26532 bytes packages/ui/src/assets/icons/alert.svg | 1 + packages/ui/src/assets/icons/animation.svg | 1 + packages/ui/src/assets/icons/arrow-left.svg | 1 + packages/ui/src/assets/icons/arrow-right.svg | 1 + packages/ui/src/assets/icons/bookshelf.svg | 1 + packages/ui/src/assets/icons/brain.svg | 1 + packages/ui/src/assets/icons/briefcase.svg | 1 + packages/ui/src/assets/icons/calendar.svg | 1 + packages/ui/src/assets/icons/castle.svg | 1 + packages/ui/src/assets/icons/chart-bar.svg | 1 + packages/ui/src/assets/icons/chart-line.svg | 1 + packages/ui/src/assets/icons/check.svg | 1 + packages/ui/src/assets/icons/chess.svg | 1 + packages/ui/src/assets/icons/chevron-down.svg | 1 + packages/ui/src/assets/icons/chevron-left.svg | 1 + .../ui/src/assets/icons/chevron-right.svg | 1 + packages/ui/src/assets/icons/clapper.svg | 1 + .../ui/src/assets/icons/clipboard-check.svg | 1 + packages/ui/src/assets/icons/clipboard.svg | 1 + packages/ui/src/assets/icons/clock.svg | 1 + packages/ui/src/assets/icons/close.svg | 1 + packages/ui/src/assets/icons/cloud.svg | 1 + packages/ui/src/assets/icons/cog.svg | 1 + packages/ui/src/assets/icons/commission.svg | 1 + packages/ui/src/assets/icons/contact.svg | 1 + packages/ui/src/assets/icons/cube.svg | 1 + packages/ui/src/assets/icons/devices.svg | 1 + packages/ui/src/assets/icons/dice.svg | 1 + packages/ui/src/assets/icons/dog.svg | 1 + packages/ui/src/assets/icons/external.svg | 1 + packages/ui/src/assets/icons/factory.svg | 1 + packages/ui/src/assets/icons/favorite.svg | 1 + packages/ui/src/assets/icons/film.svg | 1 + packages/ui/src/assets/icons/gallery.svg | 1 + packages/ui/src/assets/icons/gamepad.svg | 1 + packages/ui/src/assets/icons/gauge.svg | 1 + packages/ui/src/assets/icons/gem.svg | 1 + packages/ui/src/assets/icons/graduation.svg | 1 + packages/ui/src/assets/icons/grid.svg | 1 + packages/ui/src/assets/icons/hammer.svg | 1 + packages/ui/src/assets/icons/heart.svg | 1 + packages/ui/src/assets/icons/hearth.svg | 1 + packages/ui/src/assets/icons/home.svg | 1 + packages/ui/src/assets/icons/info.svg | 1 + packages/ui/src/assets/icons/leather.svg | 1 + packages/ui/src/assets/icons/link.svg | 1 + packages/ui/src/assets/icons/list-text.svg | 1 + packages/ui/src/assets/icons/list.svg | 1 + packages/ui/src/assets/icons/location.svg | 1 + packages/ui/src/assets/icons/lock.svg | 1 + packages/ui/src/assets/icons/menu.svg | 1 + packages/ui/src/assets/icons/monitor.svg | 1 + packages/ui/src/assets/icons/our-story.svg | 1 + packages/ui/src/assets/icons/palette.svg | 1 + packages/ui/src/assets/icons/people.svg | 1 + .../ui/src/assets/icons/person-details.svg | 1 + packages/ui/src/assets/icons/person.svg | 1 + packages/ui/src/assets/icons/play.svg | 1 + packages/ui/src/assets/icons/price.svg | 1 + packages/ui/src/assets/icons/print.svg | 1 + packages/ui/src/assets/icons/proxy-cards.svg | 1 + packages/ui/src/assets/icons/robot.svg | 1 + packages/ui/src/assets/icons/round-knife.svg | 1 + packages/ui/src/assets/icons/ruler.svg | 1 + packages/ui/src/assets/icons/scissors.svg | 1 + packages/ui/src/assets/icons/search.svg | 1 + packages/ui/src/assets/icons/server.svg | 1 + packages/ui/src/assets/icons/shapes.svg | 1 + packages/ui/src/assets/icons/shield-moon.svg | 1 + packages/ui/src/assets/icons/shop.svg | 1 + packages/ui/src/assets/icons/split.svg | 1 + packages/ui/src/assets/icons/staff.svg | 1 + packages/ui/src/assets/icons/swords.svg | 1 + packages/ui/src/assets/icons/target.svg | 1 + packages/ui/src/assets/icons/tea.svg | 1 + packages/ui/src/assets/icons/timeline.svg | 1 + packages/ui/src/assets/icons/toolbox.svg | 1 + packages/ui/src/assets/icons/translate.svg | 1 + packages/ui/src/assets/icons/trophy.svg | 1 + packages/ui/src/assets/icons/tv.svg | 1 + packages/ui/src/assets/icons/unavailable.svg | 1 + packages/ui/src/assets/icons/verified.svg | 1 + packages/ui/src/assets/icons/wand.svg | 1 + packages/ui/src/assets/icons/web.svg | 1 + packages/ui/src/assets/icons/wind.svg | 1 + packages/ui/src/assets/icons/wrench.svg | 1 + packages/ui/src/assets/motifs/astrolabe.svg | 2 + .../ui/src/assets/motifs/craft-seal-dice.svg | 2 + .../src/assets/motifs/craft-seal-leather.svg | 2 + .../ui/src/assets/motifs/craft-seal-print.svg | 2 + .../ui/src/assets/motifs/filament-banner.svg | 2 + .../assets/motifs/filament-transmutation.svg | 2 + .../ui/src/assets/motifs/kepler-nest-seal.svg | 2 + .../ui/src/assets/motifs/layered-seal.svg | 2 + .../ui/src/assets/motifs/mini-astrolabe.svg | 1 + .../ui/src/assets/motifs/mini-layered.svg | 2 + packages/ui/src/assets/motifs/mini-tome.svg | 2 + .../src/assets/motifs/mini-transmutation.svg | 2 + .../src/assets/motifs/seven-metals-dipper.svg | 2 + .../src/assets/motifs/squared-circle-seal.svg | 2 + .../motifs/transmutation-seal-star-paths.svg | 2 + .../src/assets/motifs/transmutation-seal.svg | 2 + .../assets/motifs/tria-prima-array-seal.svg | 2 + packages/ui/src/assets/motifs/zodiac-seal.svg | 2 + .../ui/src/assets/ornaments/card-ticks.svg | 1 + packages/ui/src/components/Button.astro | 37 + packages/ui/src/components/Card.astro | 29 + packages/ui/src/components/Chip.astro | 27 + .../ui/src/components/ConstellationDust.astro | 19 + packages/ui/src/components/Divider.astro | 36 + packages/ui/src/components/EdgeSeals.astro | 65 + packages/ui/src/components/Field.astro | 44 + packages/ui/src/components/FilterGroup.astro | 51 + packages/ui/src/components/GiltCorners.astro | 33 + packages/ui/src/components/HeroHorizon.astro | 25 + packages/ui/src/components/Icon.astro | 33 + packages/ui/src/components/Kicker.astro | 18 + packages/ui/src/components/Notice.astro | 24 + packages/ui/src/components/PageShell.astro | 79 + packages/ui/src/components/Seal.astro | 12 + packages/ui/src/components/SearchField.astro | 16 + .../ui/src/components/SectionHeading.astro | 41 + packages/ui/src/components/SiteFooter.astro | 48 + packages/ui/src/components/SiteHeader.astro | 247 ++ packages/ui/src/components/SlotMeter.astro | 49 + packages/ui/src/components/Star.astro | 15 + packages/ui/src/components/StarChartArt.astro | 57 + packages/ui/src/components/StarPath.astro | 38 + packages/ui/src/components/StatusPill.astro | 20 + packages/ui/src/components/TickRing.astro | 48 + packages/ui/src/components/TickScale.astro | 35 + packages/ui/src/components/nav.ts | 19 + packages/ui/src/components/seals.ts | 67 + packages/ui/src/lib/ornaments.ts | 162 + packages/ui/src/pages/index.astro | 272 ++ packages/ui/src/styles/base.css | 89 + packages/ui/src/styles/elements.css | 338 ++ packages/ui/src/styles/fonts.css | 8 + packages/ui/src/styles/tokens.css | 57 + packages/ui/test/ornaments.test.ts | 44 + packages/ui/tsconfig.json | 8 + 159 files changed, 6270 insertions(+), 87 deletions(-) create mode 100644 docs/components.md create mode 100644 packages/ui/astro.config.mjs create mode 100644 packages/ui/package.json create mode 100644 packages/ui/src/assets/brand/gk-monogram.svg create mode 100644 packages/ui/src/assets/brand/patiun-glyph-gold.svg create mode 100644 packages/ui/src/assets/brand/patiun-glyph-teal.svg create mode 100644 packages/ui/src/assets/fonts/Cinzel-Bold.woff2 create mode 100644 packages/ui/src/assets/fonts/Cinzel-Regular.woff2 create mode 100644 packages/ui/src/assets/fonts/Cinzel-SemiBold.woff2 create mode 100644 packages/ui/src/assets/fonts/LICENSE.md create mode 100644 packages/ui/src/assets/fonts/Montserrat-Bold.woff2 create mode 100644 packages/ui/src/assets/fonts/Montserrat-Medium.woff2 create mode 100644 packages/ui/src/assets/fonts/Montserrat-Regular.woff2 create mode 100644 packages/ui/src/assets/fonts/Montserrat-SemiBold.woff2 create mode 100644 packages/ui/src/assets/icons/alert.svg create mode 100644 packages/ui/src/assets/icons/animation.svg create mode 100644 packages/ui/src/assets/icons/arrow-left.svg create mode 100644 packages/ui/src/assets/icons/arrow-right.svg create mode 100644 packages/ui/src/assets/icons/bookshelf.svg create mode 100644 packages/ui/src/assets/icons/brain.svg create mode 100644 packages/ui/src/assets/icons/briefcase.svg create mode 100644 packages/ui/src/assets/icons/calendar.svg create mode 100644 packages/ui/src/assets/icons/castle.svg create mode 100644 packages/ui/src/assets/icons/chart-bar.svg create mode 100644 packages/ui/src/assets/icons/chart-line.svg create mode 100644 packages/ui/src/assets/icons/check.svg create mode 100644 packages/ui/src/assets/icons/chess.svg create mode 100644 packages/ui/src/assets/icons/chevron-down.svg create mode 100644 packages/ui/src/assets/icons/chevron-left.svg create mode 100644 packages/ui/src/assets/icons/chevron-right.svg create mode 100644 packages/ui/src/assets/icons/clapper.svg create mode 100644 packages/ui/src/assets/icons/clipboard-check.svg create mode 100644 packages/ui/src/assets/icons/clipboard.svg create mode 100644 packages/ui/src/assets/icons/clock.svg create mode 100644 packages/ui/src/assets/icons/close.svg create mode 100644 packages/ui/src/assets/icons/cloud.svg create mode 100644 packages/ui/src/assets/icons/cog.svg create mode 100644 packages/ui/src/assets/icons/commission.svg create mode 100644 packages/ui/src/assets/icons/contact.svg create mode 100644 packages/ui/src/assets/icons/cube.svg create mode 100644 packages/ui/src/assets/icons/devices.svg create mode 100644 packages/ui/src/assets/icons/dice.svg create mode 100644 packages/ui/src/assets/icons/dog.svg create mode 100644 packages/ui/src/assets/icons/external.svg create mode 100644 packages/ui/src/assets/icons/factory.svg create mode 100644 packages/ui/src/assets/icons/favorite.svg create mode 100644 packages/ui/src/assets/icons/film.svg create mode 100644 packages/ui/src/assets/icons/gallery.svg create mode 100644 packages/ui/src/assets/icons/gamepad.svg create mode 100644 packages/ui/src/assets/icons/gauge.svg create mode 100644 packages/ui/src/assets/icons/gem.svg create mode 100644 packages/ui/src/assets/icons/graduation.svg create mode 100644 packages/ui/src/assets/icons/grid.svg create mode 100644 packages/ui/src/assets/icons/hammer.svg create mode 100644 packages/ui/src/assets/icons/heart.svg create mode 100644 packages/ui/src/assets/icons/hearth.svg create mode 100644 packages/ui/src/assets/icons/home.svg create mode 100644 packages/ui/src/assets/icons/info.svg create mode 100644 packages/ui/src/assets/icons/leather.svg create mode 100644 packages/ui/src/assets/icons/link.svg create mode 100644 packages/ui/src/assets/icons/list-text.svg create mode 100644 packages/ui/src/assets/icons/list.svg create mode 100644 packages/ui/src/assets/icons/location.svg create mode 100644 packages/ui/src/assets/icons/lock.svg create mode 100644 packages/ui/src/assets/icons/menu.svg create mode 100644 packages/ui/src/assets/icons/monitor.svg create mode 100644 packages/ui/src/assets/icons/our-story.svg create mode 100644 packages/ui/src/assets/icons/palette.svg create mode 100644 packages/ui/src/assets/icons/people.svg create mode 100644 packages/ui/src/assets/icons/person-details.svg create mode 100644 packages/ui/src/assets/icons/person.svg create mode 100644 packages/ui/src/assets/icons/play.svg create mode 100644 packages/ui/src/assets/icons/price.svg create mode 100644 packages/ui/src/assets/icons/print.svg create mode 100644 packages/ui/src/assets/icons/proxy-cards.svg create mode 100644 packages/ui/src/assets/icons/robot.svg create mode 100644 packages/ui/src/assets/icons/round-knife.svg create mode 100644 packages/ui/src/assets/icons/ruler.svg create mode 100644 packages/ui/src/assets/icons/scissors.svg create mode 100644 packages/ui/src/assets/icons/search.svg create mode 100644 packages/ui/src/assets/icons/server.svg create mode 100644 packages/ui/src/assets/icons/shapes.svg create mode 100644 packages/ui/src/assets/icons/shield-moon.svg create mode 100644 packages/ui/src/assets/icons/shop.svg create mode 100644 packages/ui/src/assets/icons/split.svg create mode 100644 packages/ui/src/assets/icons/staff.svg create mode 100644 packages/ui/src/assets/icons/swords.svg create mode 100644 packages/ui/src/assets/icons/target.svg create mode 100644 packages/ui/src/assets/icons/tea.svg create mode 100644 packages/ui/src/assets/icons/timeline.svg create mode 100644 packages/ui/src/assets/icons/toolbox.svg create mode 100644 packages/ui/src/assets/icons/translate.svg create mode 100644 packages/ui/src/assets/icons/trophy.svg create mode 100644 packages/ui/src/assets/icons/tv.svg create mode 100644 packages/ui/src/assets/icons/unavailable.svg create mode 100644 packages/ui/src/assets/icons/verified.svg create mode 100644 packages/ui/src/assets/icons/wand.svg create mode 100644 packages/ui/src/assets/icons/web.svg create mode 100644 packages/ui/src/assets/icons/wind.svg create mode 100644 packages/ui/src/assets/icons/wrench.svg create mode 100644 packages/ui/src/assets/motifs/astrolabe.svg create mode 100644 packages/ui/src/assets/motifs/craft-seal-dice.svg create mode 100644 packages/ui/src/assets/motifs/craft-seal-leather.svg create mode 100644 packages/ui/src/assets/motifs/craft-seal-print.svg create mode 100644 packages/ui/src/assets/motifs/filament-banner.svg create mode 100644 packages/ui/src/assets/motifs/filament-transmutation.svg create mode 100644 packages/ui/src/assets/motifs/kepler-nest-seal.svg create mode 100644 packages/ui/src/assets/motifs/layered-seal.svg create mode 100644 packages/ui/src/assets/motifs/mini-astrolabe.svg create mode 100644 packages/ui/src/assets/motifs/mini-layered.svg create mode 100644 packages/ui/src/assets/motifs/mini-tome.svg create mode 100644 packages/ui/src/assets/motifs/mini-transmutation.svg create mode 100644 packages/ui/src/assets/motifs/seven-metals-dipper.svg create mode 100644 packages/ui/src/assets/motifs/squared-circle-seal.svg create mode 100644 packages/ui/src/assets/motifs/transmutation-seal-star-paths.svg create mode 100644 packages/ui/src/assets/motifs/transmutation-seal.svg create mode 100644 packages/ui/src/assets/motifs/tria-prima-array-seal.svg create mode 100644 packages/ui/src/assets/motifs/zodiac-seal.svg create mode 100644 packages/ui/src/assets/ornaments/card-ticks.svg create mode 100644 packages/ui/src/components/Button.astro create mode 100644 packages/ui/src/components/Card.astro create mode 100644 packages/ui/src/components/Chip.astro create mode 100644 packages/ui/src/components/ConstellationDust.astro create mode 100644 packages/ui/src/components/Divider.astro create mode 100644 packages/ui/src/components/EdgeSeals.astro create mode 100644 packages/ui/src/components/Field.astro create mode 100644 packages/ui/src/components/FilterGroup.astro create mode 100644 packages/ui/src/components/GiltCorners.astro create mode 100644 packages/ui/src/components/HeroHorizon.astro create mode 100644 packages/ui/src/components/Icon.astro create mode 100644 packages/ui/src/components/Kicker.astro create mode 100644 packages/ui/src/components/Notice.astro create mode 100644 packages/ui/src/components/PageShell.astro create mode 100644 packages/ui/src/components/Seal.astro create mode 100644 packages/ui/src/components/SearchField.astro create mode 100644 packages/ui/src/components/SectionHeading.astro create mode 100644 packages/ui/src/components/SiteFooter.astro create mode 100644 packages/ui/src/components/SiteHeader.astro create mode 100644 packages/ui/src/components/SlotMeter.astro create mode 100644 packages/ui/src/components/Star.astro create mode 100644 packages/ui/src/components/StarChartArt.astro create mode 100644 packages/ui/src/components/StarPath.astro create mode 100644 packages/ui/src/components/StatusPill.astro create mode 100644 packages/ui/src/components/TickRing.astro create mode 100644 packages/ui/src/components/TickScale.astro create mode 100644 packages/ui/src/components/nav.ts create mode 100644 packages/ui/src/components/seals.ts create mode 100644 packages/ui/src/lib/ornaments.ts create mode 100644 packages/ui/src/pages/index.astro create mode 100644 packages/ui/src/styles/base.css create mode 100644 packages/ui/src/styles/elements.css create mode 100644 packages/ui/src/styles/fonts.css create mode 100644 packages/ui/src/styles/tokens.css create mode 100644 packages/ui/test/ornaments.test.ts create mode 100644 packages/ui/tsconfig.json diff --git a/.gitignore b/.gitignore index e97e18d..8bbb8ff 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,7 @@ npm-debug.log* !.vscode/settings.json .idea/ data/ + +# Astro build output and generated types +packages/ui/dist/ +packages/ui/.astro/ diff --git a/README.md b/README.md index 167333b..9a3ec68 100644 --- a/README.md +++ b/README.md @@ -14,11 +14,12 @@ with the decisions behind it, is the | `vault-kit` | What goes into the vault: the Obsidian templates (generated from the schema) and the Site guide. | | `services/content` | The content service: watches the Site folder, runs the rules, keeps the last good version and its history, and serves the one write API. The MCP server for agents is part of it. See [docs/content-service.md](docs/content-service.md). | | `services/admin` | The admin page: the review queue with diffs, history and rollback, quick capture, store orders, draft previews and sync health. A client of the content service, on the tailnet only. See [docs/admin.md](docs/admin.md). | +| `packages/ui` | The component library: the approved element rules and ornament kit as Astro components, with the brand fonts, seals and icons, and a gallery page that shows them all. See [docs/components.md](docs/components.md). | | `tools/migrate` | The one-time migration from the old site's data into Site folder notes. | | `docs` | How the pieces fit. Start with [docs/content-model.md](docs/content-model.md). | | `legacy` | The old site, kept for reference only. Nothing outside it imports from it except the migration. | -Coming in later phases: the component library and the new site. +Coming in later phases: the new site's pages, integrations and deploy. ## Commands @@ -26,7 +27,8 @@ Node 22 or newer. ```sh npm install -npm run check # typecheck and tests +npm run check # typecheck (with astro check) and tests +npm run ui # the component gallery at http://localhost:4321 npm run migrate # writes out/24.01_PersonalWebsite and out/MIGRATION-REPORT.md npm run templates # regenerates vault-kit templates after a schema change npm run schema:json # writes a JSON Schema per note type to packages/schema/json/ diff --git a/docs/components.md b/docs/components.md new file mode 100644 index 0000000..a4cecb6 --- /dev/null +++ b/docs/components.md @@ -0,0 +1,79 @@ +# Component library + +`packages/ui` (`@gk/ui`) holds the approved element rules and ornament kit as +Astro components. The site imports them; nothing else defines how a button, +chip or seal looks. The rules come from the design canvas's Element rules and +Ornament kit boards, approved Oct 11. + +Run the gallery with `npm run ui` (or `npm run ui:build` for a static copy in +`packages/ui/dist`). It shows every component inside the real page shell. + +## Layout + +| Path | What it is | +| --- | --- | +| `src/styles/tokens.css` | Colors, fonts, spacing, radii, control sizes and the page width as `--gk-*` variables. | +| `src/styles/fonts.css` | Cinzel and Montserrat, self-hosted as Latin-subset WOFF2 (OFL, see `src/assets/fonts/LICENSE.md`). | +| `src/styles/base.css` | Page defaults: body, headings, body links, the focus ring, `.gk-wrap`, reduced motion. | +| `src/styles/elements.css` | The element rules as global `gk-*` classes, so Markdown output and plain HTML can use them too. | +| `src/lib/ornaments.ts` | The seeded geometry behind star paths, tick scales, tick rings and dust. Tested in `test/ornaments.test.ts`. | +| `src/components/` | The Astro components below. | +| `src/assets/` | Seals and motifs, the monogram, 86 stroke icons, the card tick strip, and the fonts. | + +## Element rules + +| Rule | Component | Notes | +| --- | --- | --- | +| I Buttons | `Button` | `variant` primary (teal pill), secondary (gold hairline) or text (filament underline on hover); `size="compact"` for 36px; `href` makes it a link. One primary per section. | +| II States | `StatusPill` | Round pill. `live` teal with a lit dot, `state` without the dot, `favorite` and `current` gold with a star. | +| II Labels | `Chip` | Square 6px chip. `core` gives the teal edge; `href` makes it a page link led by a violet star. | +| III Filters | `FilterGroup` | Separate 36px pills. Items with `href` are links (`aria-current`); items without are toggle buttons (`aria-pressed`) a page script drives. | +| IV Kickers | `Kicker`, `SectionHeading` | Gold capitals over a tick scale. `SectionHeading` adds the Cinzel heading and the filament underline. | +| IV Notices | `Notice` | The ledger note: soft gold panel, gold rule on the left. | +| V Fields | `Field`, `SearchField` | Input, select or text area in the 10px box, filament across the base on focus, with `hint` and `error` wired to `aria-describedby`. | +| VI Cards | `Card` | Linked cards turn gold on hover with a tick scale along the top. `featured` adds the gold border and gilt corners; one per page. | +| VII Links | (base.css) | Body links are teal and always underlined. | + +Shared: controls are 44px (36px compact), chips 26px, focus is a 2px gold +ring 3px out, disabled is 40% opacity. + +## Ornaments + +| Component | Use | +| --- | --- | +| `Star` | The four-point star. Gold, violet (only ever a star), teal, starlight or outline. | +| `StarPath` | Greg's star path line. `strength` calm, standard or wild; the same `seed` always draws the same path (it defaults to the page path). Two per page at most. | +| `Divider` | The header divider: gold line, large center star, star paths both sides. Header and footer only. | +| `HeroHorizon` | Gold hairline with a violet star, where a hero has no divider. | +| `TickScale` | Astrolabe ticks; with `value` it is a meter. | +| `TickRing` | A tick ring around a step number or small seal; `medallion` adds the disc and glow. | +| `SlotMeter` | Open order slots as stars over a tick meter. | +| `GiltCorners` | Corner brackets for the one featured panel per page. | +| `Seal`, `EdgeSeals` | A seal as an image; `EdgeSeals` places the page family's seal at the page edges at 6 to 7%. | +| `ConstellationDust` | Sparse static specks, about one in five gold. | +| `StarChartArt` | Placeholder art and the fallback for a missing image: star-chart grid, gold mat, a seal. | +| `Icon` | One of the 86 brand icons, in the current text color. | + +Each page family's edge seal is in `src/components/seals.ts` (`pageSeals`): +landing uses transmutation star paths, resume the Kepler nest, work the +squared circle, and so on. A craft page passes its own craft seal. + +## Page shell + +`PageShell` is every page's frame: the head (title, description), dust and +edge seals, `SiteHeader` (nebula, filament banner, the primary nav with the My +world menu, an optional hero in the `hero` slot, the divider), the page +content, and `SiteFooter`. The nav entries live in `src/components/nav.ts`; +pass `current` to mark the page's entry. + +```astro +--- +import PageShell from "@gk/ui/components/PageShell.astro"; +--- + + … + … + +``` + +The site is dark only for now; light mode comes in a later phase. diff --git a/package-lock.json b/package-lock.json index 44fe4d5..6b9ca22 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,6 +20,672 @@ "node": ">=22" } }, + "node_modules/@astrojs/astro2tsx": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@astrojs/astro2tsx/-/astro2tsx-0.1.2.tgz", + "integrity": "sha512-Uj/M9zbrhu5cIZEvWAGTbQJRKSxXWJ/E59BZwt7F+chrKhbfttW+9rkS/l+1QxoJP1KU3UnpM62M7yszfAI0WA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@emnapi/core": "1.11.3", + "@napi-rs/wasm-runtime": "1.2.4" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/check": { + "version": "0.9.10", + "resolved": "https://registry.npmjs.org/@astrojs/check/-/check-0.9.10.tgz", + "integrity": "sha512-zgx/UQMozdjOa3bOxjgeCFdtpE3c9rRX6xHwa+2QXvy8z8Akifu2AtubHyv/zzC2znO8dl8fFWL4K+Ba9kS8HQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@astrojs/language-server": "^2.16.7", + "chokidar": "^4.0.3", + "kleur": "^4.1.5", + "yargs": "^18.0.0" + }, + "bin": { + "astro-check": "bin/astro-check.js" + }, + "peerDependencies": { + "typescript": "^5.0.0 || ^6.0.0" + } + }, + "node_modules/@astrojs/compiler-binding": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding/-/compiler-binding-0.5.1.tgz", + "integrity": "sha512-oczyolLUl9mNrtuBuNjuvn5thpHrFLXAHAWGTRg0JDHQ8cGVsVeFekOu2uSoQGgainJIi+A3/9m0DyYwz3Nlnw==", + "license": "MIT", + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@astrojs/compiler-binding-android-arm64": "0.5.1", + "@astrojs/compiler-binding-darwin-arm64": "0.5.1", + "@astrojs/compiler-binding-darwin-x64": "0.5.1", + "@astrojs/compiler-binding-linux-arm64-gnu": "0.5.1", + "@astrojs/compiler-binding-linux-arm64-musl": "0.5.1", + "@astrojs/compiler-binding-linux-x64-gnu": "0.5.1", + "@astrojs/compiler-binding-linux-x64-musl": "0.5.1", + "@astrojs/compiler-binding-wasm32-wasi": "0.5.1", + "@astrojs/compiler-binding-win32-arm64-msvc": "0.5.1", + "@astrojs/compiler-binding-win32-x64-msvc": "0.5.1" + } + }, + "node_modules/@astrojs/compiler-binding-android-arm64": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-android-arm64/-/compiler-binding-android-arm64-0.5.1.tgz", + "integrity": "sha512-nMb2nMIJtcWLNH+fV75Enmi+jp4tIOYOfsv4XtpgdOWkqc9P4uSSdK5u1xb340AzrXo5V18SxLl/nc3vcqtamQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-darwin-arm64": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-darwin-arm64/-/compiler-binding-darwin-arm64-0.5.1.tgz", + "integrity": "sha512-5k4+nZ/WRt+dcVow4z7rBuI7iv9A58h/uQ91YJe1PrTivbcCkq3u2Ynx/AZJwII4CsZawBCeGpojkFjutZBhBQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-darwin-x64": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-darwin-x64/-/compiler-binding-darwin-x64-0.5.1.tgz", + "integrity": "sha512-0Buw+YST9Ff2EqQyhRMQujuFq/dijLw3zXWhsvI0dJ2pGrT38iVoxTEca2eBX/PfhlwkVWSRJEKO5gMqKHyeHg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-linux-arm64-gnu": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-arm64-gnu/-/compiler-binding-linux-arm64-gnu-0.5.1.tgz", + "integrity": "sha512-x5LRpyCoMKuu3INr18cn42YhwSOaRpp9b5Ny3eJJ/sWVeejC16Zeshu1JlvlSmEZACLeWxU5JM0RX6T6nI+YyA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-linux-arm64-musl": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-arm64-musl/-/compiler-binding-linux-arm64-musl-0.5.1.tgz", + "integrity": "sha512-mL7tQEuDk3rGfaKmrO63nw6WBy1TPVioQGmVyjcAr+C+a/k3SVMYBytNSMqH9mZPT/Swj30JaZ2N842Z1NnOEQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-linux-x64-gnu": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-x64-gnu/-/compiler-binding-linux-x64-gnu-0.5.1.tgz", + "integrity": "sha512-uLGc9jtxwiDe4cQyhNPQim4ECiB/YzbWlXssfLAv8r374kArfTwBHjwR2+j+XVyF71yCx5mF9YNRclRqdNBgeg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-linux-x64-musl": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-x64-musl/-/compiler-binding-linux-x64-musl-0.5.1.tgz", + "integrity": "sha512-G0cywinaQUc8v1MrytvYKUX7svTDMmYtOCZfxjwKPbJi59zIqM+Ll0rWgO0GQWLZH3XoPYo19ZwlP3Xt9ep9YA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-wasm32-wasi": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-wasm32-wasi/-/compiler-binding-wasm32-wasi-0.5.1.tgz", + "integrity": "sha512-yMhLL8asmLydoAdnEZxAw6eLfNDs3YJl6jqn0pjPt6zrb2U5qIDLnii4/twThpIFODKkQ6YDpBRTOz/vSfZCxw==", + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.11.3", + "@emnapi/runtime": "1.11.3", + "@napi-rs/wasm-runtime": "^1.2.4" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-win32-arm64-msvc": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-win32-arm64-msvc/-/compiler-binding-win32-arm64-msvc-0.5.1.tgz", + "integrity": "sha512-aClIG6poFt3Wf/bZykrEAX2nAnKvBHW6RXiKUa69QjBtWH0/FOlOAjnFcxRNlhej2AMWNYFx0o+LGjwMcH8P7Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-win32-x64-msvc": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-win32-x64-msvc/-/compiler-binding-win32-x64-msvc-0.5.1.tgz", + "integrity": "sha512-/E6uBCagY70YpWqssBC+/FuniNSloeQPRgj7l3LDWiqjxIyTLnIgX1sTt18FU7jrigetpSnC9cHAM6b6IxSgPw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-rs": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-rs/-/compiler-rs-0.5.1.tgz", + "integrity": "sha512-1k1acnX7DepIiea9U9/Y+d1hdAFfylu1ZQQkkYgRSwBOBAsXInolyZuh/2tax+83vu/A+hOYP6P3+265RvMTvw==", + "license": "MIT", + "dependencies": { + "@astrojs/compiler-binding": "0.5.1" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@astrojs/internal-helpers": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@astrojs/internal-helpers/-/internal-helpers-0.11.0.tgz", + "integrity": "sha512-3rzxJ+xbo0+8YyqOzLziIN32wmsHdCjEVz2sGOpRxJ+Ben/KiLph4ItxBy1abEL+E8fkRzqjg0rfXmaHJGw9JA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.4", + "@types/mdast": "^4.0.4", + "js-yaml": "^4.3.0", + "picomatch": "^4.0.4", + "retext-smartypants": "^6.2.0", + "shiki": "^4.0.2", + "smol-toml": "^1.6.0", + "unified": "^11.0.5" + } + }, + "node_modules/@astrojs/language-server": { + "version": "2.17.2", + "resolved": "https://registry.npmjs.org/@astrojs/language-server/-/language-server-2.17.2.tgz", + "integrity": "sha512-WzqCBYo0RtKrkPLPMnTz2+UOQPosaFhoG/PvoSyPIgBU0BKeS6qeaYv7Rscq7P1LxJdoN0VosQF2h2TZTsIGHg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@astrojs/astro2tsx": "^0.1.2", + "@astrojs/yaml2ts": "^0.2.4", + "@volar/kit": "~2.4.28", + "@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-service-emmet": "0.0.71", + "volar-service-html": "0.0.71", + "volar-service-prettier": "0.0.71", + "volar-service-typescript": "0.0.71", + "volar-service-typescript-twoslash-queries": "0.0.71", + "volar-service-yaml": "0.0.71", + "vscode-html-languageservice": "^5.6.2", + "vscode-uri": "^3.2.0" + }, + "bin": { + "astro-ls": "bin/nodeServer.js" + }, + "peerDependencies": { + "prettier": "^3.0.0", + "prettier-plugin-astro": ">=0.11.0" + }, + "peerDependenciesMeta": { + "prettier": { + "optional": true + }, + "prettier-plugin-astro": { + "optional": true + } + } + }, + "node_modules/@astrojs/markdown-satteri": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@astrojs/markdown-satteri/-/markdown-satteri-0.4.2.tgz", + "integrity": "sha512-g4QehXnB/MzxQWnSf17NciFInMqEJy9948p8cc7zGg/r7995m0Ln5PMr3Xlnj24iey1LchaoZ3SzTOa3D4P/3w==", + "license": "MIT", + "dependencies": { + "@astrojs/internal-helpers": "0.11.0", + "@astrojs/prism": "4.0.2", + "github-slugger": "^2.0.0", + "satteri": "^0.10.3" + } + }, + "node_modules/@astrojs/prism": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@astrojs/prism/-/prism-4.0.2.tgz", + "integrity": "sha512-KTivpmnz6lDsC6o9H4+DNm2SrE/GHzw8cNAvEJwAvUT+eoaEnn/4NtbDNfRRaxaJHdp15gf+tfHAWiXR4wB3BA==", + "license": "MIT", + "dependencies": { + "prismjs": "^1.30.0" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@astrojs/telemetry": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/@astrojs/telemetry/-/telemetry-3.3.3.tgz", + "integrity": "sha512-C1TLn5sPJr0x4vk56piHWKbnqlEB8BKyte5Y45V02U+D7BGO5eMqZDH5aPjnkXQWJggvmsTXxH03QMZ9NgWLzQ==", + "license": "MIT", + "dependencies": { + "ci-info": "^4.4.0", + "dset": "^3.1.4", + "is-docker": "^4.0.0", + "package-manager-detector": "^1.6.0" + }, + "engines": { + "node": "18.20.8 || ^20.3.0 || >=22.0.0" + } + }, + "node_modules/@astrojs/yaml2ts": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@astrojs/yaml2ts/-/yaml2ts-0.2.4.tgz", + "integrity": "sha512-8oddpOae35pJsXPQXhTkM0ypfKPskVsh2bCxRtbf7e+/Epw2nReakFYpLKjZMEr75CsoF203PMnCocpfz0s69A==", + "dev": true, + "license": "MIT", + "dependencies": { + "yaml": "^2.8.3" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.9", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.9.tgz", + "integrity": "sha512-CjXrNHTnvqBVqHgdBysY3vk2T8tpJHb5/RMeHJBTyVa9xgugCB0CJTx/3oO8RV2QRQP391RWpB7D6hLjm8V9uA==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.8" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", + "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bruits/satteri-darwin-arm64": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-darwin-arm64/-/satteri-darwin-arm64-0.10.5.tgz", + "integrity": "sha512-27KTVl4TJkVahMy/ohyA7qd4938G5UNneFUz/PsScYfpIhj0IVAS23mpcJXdPF44sa6nva198lmV/cKIb2YPyA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@bruits/satteri-darwin-x64": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-darwin-x64/-/satteri-darwin-x64-0.10.5.tgz", + "integrity": "sha512-IjnLe3nKspq6qaeqGgjT7MT8VrTV74yWRlaag7ZdNsI8TDAYZ0iPxMCo+9KQZHUk5EyVB+reBI/PFWL5KuFw9Q==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@bruits/satteri-linux-arm64-gnu": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-linux-arm64-gnu/-/satteri-linux-arm64-gnu-0.10.5.tgz", + "integrity": "sha512-glkYXZCJywjP13v67eAyAMSJdF+ncvEbYvgi/wOtffL9tQ27lr/zsyzUfgs+ovjJ9d8JNQKiXeiArJcX8PJL9w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@bruits/satteri-linux-arm64-musl": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-linux-arm64-musl/-/satteri-linux-arm64-musl-0.10.5.tgz", + "integrity": "sha512-yWdgG1g17Nh2QyGVlFUxGRa3FEFwiMcpZEyMNWkbM3deC94cmVc+/i9OuyFpdKuWo3GkgoCtYVOoxk1uCnCZIA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@bruits/satteri-linux-x64-gnu": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-linux-x64-gnu/-/satteri-linux-x64-gnu-0.10.5.tgz", + "integrity": "sha512-FVaLoPT1fBgGl0J+AYebyyXJYBachGl8Oyyrf1lye4RTqCB4S0Gwkj1uM9RJyThUOvx5VUmAT1CnNh1SFHA+kw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@bruits/satteri-linux-x64-musl": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-linux-x64-musl/-/satteri-linux-x64-musl-0.10.5.tgz", + "integrity": "sha512-EHpVAx2bqW3GINHTKkljtxVfQmVDGWIuwOYOP5YghTj+0PkBa2o8oKPRtQ9Kbsr1Fye8jtUcDjhwj2jMNugZKg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@bruits/satteri-wasm32-wasi": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-wasm32-wasi/-/satteri-wasm32-wasi-0.10.5.tgz", + "integrity": "sha512-ypz8c/Zmipxp4IoeDa228Gstv6TLzVmNs3yC6wKCoNSOjx1iwpgzu87Y3hTkXFdwChVGU85qeUDuOIarGUZQLw==", + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.11.1", + "@emnapi/runtime": "1.11.1", + "@napi-rs/wasm-runtime": "^1.2.3" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@bruits/satteri-wasm32-wasi/node_modules/@emnapi/core": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", + "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.2", + "tslib": "^2.4.0" + } + }, + "node_modules/@bruits/satteri-wasm32-wasi/node_modules/@emnapi/runtime": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", + "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@bruits/satteri-wasm32-wasi/node_modules/@emnapi/wasi-threads": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", + "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@bruits/satteri-win32-arm64-msvc": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-win32-arm64-msvc/-/satteri-win32-arm64-msvc-0.10.5.tgz", + "integrity": "sha512-siTV88nb0LRqNpkL2gXboqCwVdq95sLtzMHS1/3eONV2gLbB3NAK46wmSMvCO/yquBvI2lvaFIfd8P12ecsxBw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@bruits/satteri-win32-x64-msvc": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-win32-x64-msvc/-/satteri-win32-x64-msvc-0.10.5.tgz", + "integrity": "sha512-C3IfPvfvMXmlzBxaMPKFS1XiuV9pu2mC7YqkPk7PSvTgPZ8gbdASIpHpztDLvTTQjqZ0z1Ol8tK5X+V6XXC0wQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@capsizecss/unpack": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@capsizecss/unpack/-/unpack-4.0.1.tgz", + "integrity": "sha512-CuNiSqg7+e1cO/GjffyMOm5Tt2jUF9CWHHnvQ/UkqvtkGfHdgwEC0wpmq7fkN3gxwpRnrAN0WzO3vREKmNolMQ==", + "license": "MIT", + "dependencies": { + "fontkitten": "^1.0.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@clack/core": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@clack/core/-/core-1.5.1.tgz", + "integrity": "sha512-iHTrHA8MtVuLl2TfZySmcKv1qO2PoyC9Z7pfSDozEuV5vtY3/wcOPKJXlqJ5Oq2Cx5DDGQGAMVx6HZfRRoVEbQ==", + "license": "MIT", + "dependencies": { + "fast-wrap-ansi": "^0.2.0", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 20.12.0" + } + }, + "node_modules/@clack/prompts": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@clack/prompts/-/prompts-1.8.1.tgz", + "integrity": "sha512-dlT1m5e/0yUL0kRNcQn7yGLVThkgbB0Ga/1AmfDDC/8ik6AIiSf2QLQO2zPYvefsHP0aFgxO93cVLCCfDp7kzQ==", + "license": "MIT", + "dependencies": { + "@clack/core": "1.5.1", + "fast-string-width": "^3.0.2", + "fast-wrap-ansi": "^0.2.0", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 20.12.0" + } + }, + "node_modules/@emmetio/abbreviation": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/@emmetio/abbreviation/-/abbreviation-2.3.3.tgz", + "integrity": "sha512-mgv58UrU3rh4YgbE/TzgLQwJ3pFsHHhCLqY20aJq+9comytTXUDNGG/SMtSeMJdkpxgXSXunBGLD8Boka3JyVA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@emmetio/scanner": "^1.0.4" + } + }, + "node_modules/@emmetio/css-abbreviation": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@emmetio/css-abbreviation/-/css-abbreviation-2.1.8.tgz", + "integrity": "sha512-s9yjhJ6saOO/uk1V74eifykk2CBYi01STTK3WlXWGOepyKa23ymJ053+DNQjpFcy1ingpaO7AxCcwLvHFY9tuw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@emmetio/scanner": "^1.0.4" + } + }, + "node_modules/@emmetio/css-parser": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@emmetio/css-parser/-/css-parser-0.4.1.tgz", + "integrity": "sha512-2bC6m0MV/voF4CTZiAbG5MWKbq5EBmDPKu9Sb7s7nVcEzNQlrZP6mFFFlIaISM8X6514H9shWMme1fCm8cWAfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@emmetio/stream-reader": "^2.2.0", + "@emmetio/stream-reader-utils": "^0.1.0" + } + }, + "node_modules/@emmetio/html-matcher": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@emmetio/html-matcher/-/html-matcher-1.3.0.tgz", + "integrity": "sha512-NTbsvppE5eVyBMuyGfVu2CRrLvo7J4YHb6t9sBFLyY03WYhXET37qA4zOYUjBWFCRHO7pS1B9khERtY0f5JXPQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "@emmetio/scanner": "^1.0.0" + } + }, + "node_modules/@emmetio/scanner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@emmetio/scanner/-/scanner-1.0.4.tgz", + "integrity": "sha512-IqRuJtQff7YHHBk4G8YZ45uB9BaAGcwQeVzgj/zj8/UdOhtQpEIupUhSk8dys6spFIWVZVeK20CzGEnqR5SbqA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@emmetio/stream-reader": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@emmetio/stream-reader/-/stream-reader-2.2.0.tgz", + "integrity": "sha512-fXVXEyFA5Yv3M3n8sUGT7+fvecGrZP4k6FnWWMSZVQf69kAq0LLpaBQLGcPR30m3zMmKYhECP4k/ZkzvhEW5kw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@emmetio/stream-reader-utils": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@emmetio/stream-reader-utils/-/stream-reader-utils-0.1.0.tgz", + "integrity": "sha512-ZsZ2I9Vzso3Ho/pjZFsmmZ++FWeEd/txqybHTm4OgaZzdS8V9V/YYWQwg5TC38Z7uLWUV1vavpLLbjJtKubR1A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@emnapi/core": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.3.tgz", + "integrity": "sha512-zLpS5asjEb7lq8jYLq37N6XKaE41DIexlY1rF/z4/tIl3wo13Sqm28fRyfIsKZD+NZ8mM5RoKkpW/rBcuoSZSg==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@emnapi/wasi-threads": "1.2.3", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.3.tgz", + "integrity": "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.3.tgz", + "integrity": "sha512-ELEBe8PsLvvJ6QMr0zLt8ffvOHW/dc1m3CEzNMg7aJUv3bMaoDtw2TXyDAwkYBuroxxuHEwhRTLJSe5sya547g==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.28.2", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz", @@ -452,6 +1118,10 @@ "resolved": "packages/schema", "link": true }, + "node_modules/@gk/ui": { + "resolved": "packages/ui", + "link": true + }, "node_modules/@hono/node-server": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-2.1.4.tgz", @@ -464,6 +1134,507 @@ "hono": "^4 || ^5.0.0-0" } }, + "node_modules/@img/colour": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz", + "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.35.5.tgz", + "integrity": "sha512-QRUlFQ0WxvdWyqqG/WtI3iupfD5rBzmCHXSdPsY91sAtVtTo7Q4cb6zOccZ3gqEqkr0f1As1ehLqmEpDsRf+lg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.3.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.35.5.tgz", + "integrity": "sha512-+BR255RhDlpygUpOc/Jdt1nT6DQ3XG/ERo5wbcdOf5Q320dKtPCKPLR1LJs9VGXRaMa8l1uUa0tkCNOXiAxZUw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.3.4" + } + }, + "node_modules/@img/sharp-freebsd-wasm32": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-freebsd-wasm32/-/sharp-freebsd-wasm32-0.35.5.tgz", + "integrity": "sha512-Y/z91nEZ4uIBX5X3nfTovjU9lHNKFYbL2lpHCLVNmXQK03VIZvXBBt0KxbPGp2SdGSF+2mQU4e+hQaWOt86iAw==", + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "dependencies": { + "@img/sharp-wasm32": "0.35.5" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.3.4.tgz", + "integrity": "sha512-5R89nBYiRdUlSWJxPhO+GVtaXzXSxKnRu/xqMn3KTA3L9EB9Oy/P+Nn2f2vlhPuUdy/Zusb2DarbyTpGCfEDuw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.3.4.tgz", + "integrity": "sha512-iR2OKH80yi0U+dUplyh3/xdpFvps6YkCwsXenIJxqxR1v9o+xtKTGbS9H7cps+2Vxjc8B1j96p75NmTGjIhtpQ==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.3.4.tgz", + "integrity": "sha512-LmRtTsOHuvM2+wlO2Db37dx5MiZhB0FvSunciw48YjdOkZz9KAiRbm8ujeMOA1INqmei5NapFxYEK1D1ZSidmw==", + "cpu": [ + "arm" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.3.4.tgz", + "integrity": "sha512-Y3dgX/6lE2QhQb+Gxy0WZxfg9MEm/JBjamZpS2IklP7xIQoKN4hzAm7KcMVGtaVDt3neE9OKBC7vAfonA/Lr1A==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.3.4.tgz", + "integrity": "sha512-Le6boB8Tai0Nis+gIxIpKx68UDVVIqdR8Tin5Yf1z2LJJQLDJvCDRqRu+jC2qCoD+eIomonmOwB4smBRxfVpYQ==", + "cpu": [ + "ppc64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.3.4.tgz", + "integrity": "sha512-aHkkIEHPRdQEegJN20MLmGtxYD9R2wQr3Cwpddnu5+YKMt6Uzax7S9h5gpZTo8wyrGuZSlfQ63OevL5mTyOC7Q==", + "cpu": [ + "riscv64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.3.4.tgz", + "integrity": "sha512-ra/mB6MikESDUO7Yg+Mi95bFBb9GsObURuhnOv3OqknjGe9sZrG8tCe9q0xSIGrtLgvgw0gKnFWcK4blSgQOuQ==", + "cpu": [ + "s390x" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.3.4.tgz", + "integrity": "sha512-GJ//SSXbnwSDes02umB3nDJLFcQzw8a18V8fyhqr6tV515tOEMdImjjxj1AoafMRz56F3PHgftnj1QEKSU1zkw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.3.4.tgz", + "integrity": "sha512-hvulFwtjUcagsis6BBxHwGFwWoNZjgYmULGVrZcyfNbjA8hKILbRxGg15/7w5HDyXHXUos/j6baAWqnCyQ2DWA==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.3.4.tgz", + "integrity": "sha512-6zXKeE/p39I1AmA3cJG35eyBGNqNddLnUXjhwBnsGjFPWqf5VKkDBEqaEkPDoTEtkxwi2vv8Tcr2mDyP4So7Fg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.35.5.tgz", + "integrity": "sha512-LEaXK2WdXVK5ykcw0buWyPMsmLLL2vpHLD6yrNSW+JGEL3BZPA4tpKN6iaMc4AxTTAoaX/sU1rOL51lcIz48ZQ==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.3.4" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.35.5.tgz", + "integrity": "sha512-LYVx5JTsOM2CBzmxreh+nl64/3H6Xb09iSLknqH47z2T2DFFxDeFLP5y4dJwe6H7uGQlHPyEEtIqyo3DYsRwdQ==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.3.4" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.35.5.tgz", + "integrity": "sha512-QVxAAq8evVRI9ia2vqgwrmWucn5Dfv+JdWzj75pD8omHLPSP7f8p20O8jxzjCcuCEQEOtYOZUmX1hkiZ0kdevA==", + "cpu": [ + "ppc64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.3.4" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.35.5.tgz", + "integrity": "sha512-LtdreXguaavKODPIfzJ4kffx7UNt1omwtK0rch4EBbbSTXPnxWmYSayXdLJw0fJzQ97kHt1gL/yh4tvU+nCyRQ==", + "cpu": [ + "riscv64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.3.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.35.5.tgz", + "integrity": "sha512-UZasTOFiYzotTsGOCu42BfUzP6Tu6Do/947iRm1RsLKvlllxwGcn4RN27LibGWceix4Y+Pmw3jsnTcCQIgWjqA==", + "cpu": [ + "s390x" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.3.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.35.5.tgz", + "integrity": "sha512-SxFtLTeJInhAA9Q836kux2vZNeOBQEx658qvbboZScr0wIARym3IcGmW7KpVD5sbVg0Ojy+udFQdayYIZyoNog==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.3.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.35.5.tgz", + "integrity": "sha512-9HbMclmI1zlNkFRs3z9/eBtDjfD0sGlrX1z6b1qwmiFY5ElDLh4BC0LPBdVp7z1DXFiKlIcznf+ZlsuZzLxQqg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.3.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.35.5.tgz", + "integrity": "sha512-4KOphqB035HrVdqLZfCgMzzERrQkkzOwRhl4OAkRO1YCldbaFjySXMaK534Mo0V+LndnlJk+sbUyLeU0ULyD1A==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.3.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.35.5.tgz", + "integrity": "sha512-Ptsga1su4tQx+LLF1ECS9U6nz5kmrXKo6XVbtR48Ke3ZRxxgaWBu7IDtEe1quo8hiupwm6WFqxVlXaSf7IINGQ==", + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.11.3" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-webcontainers-wasm32": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-webcontainers-wasm32/-/sharp-webcontainers-wasm32-0.35.5.tgz", + "integrity": "sha512-hfhF/FmoQyTUkA0bIKFOtw536BQSeBMe6BF6QyWlrPxT754+TFLaZ7sKKTfvvM0yJgKgaYTwnFCIZ/GuDw5SUA==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@img/sharp-wasm32": "0.35.5" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.35.5.tgz", + "integrity": "sha512-X4t7g+7ZA5DKblCBEXGjUqqemj4vczING/5viFwAL8h4N3qYeyjwdCvRLHi4EdOUI+2Z7UFlp1VM+p/AuEtm6Q==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.35.5.tgz", + "integrity": "sha512-5Zm82LoBc43nhwNybZlG7Y1KO//Zhsn306fQl29ZOuStHLGTo3BWL83q3cznX0poxSAMuYL1On/BHBxkBeKr6A==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.35.5.tgz", + "integrity": "sha512-x76eH0vEiHlcMQu8Y8IenntaACtddpT6W0wmXtWrnKcnKI7ME5DdgqhAD6SEWOEl1v2zDvkZDhFA9KnURwpfqg==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", @@ -478,7 +1649,6 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", - "dev": true, "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { @@ -532,13 +1702,38 @@ } } }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.2.4.tgz", + "integrity": "sha512-AJxoUD2/15ESHbvpcyjU274nsAPLuOtPHCk0vKJM5pj//Fg/B1FXNWjPnXTT9PymCYYiHo4zPj0ZomXBKhoy7g==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@tybys/wasm-util": "^0.10.3" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=23.5.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1 || ^2.0.0-alpha.4", + "@emnapi/runtime": "^1.7.1 || ^2.0.0-alpha.4" + } + }, + "node_modules/@oslojs/encoding": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@oslojs/encoding/-/encoding-1.1.0.tgz", + "integrity": "sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==", + "license": "MIT" + }, "node_modules/@oxc-project/types": { "version": "0.153.0", "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.153.0.tgz", "integrity": "sha512-jSNrRIyvPVLL5E1z/tS5zLAprosGz/FuI1PWwEOz59j3E4TTTzSpqKb/RDGqJhhW0QHYISGXxq582wGx6T11Tw==", - "dev": true, "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/oxc-project" } @@ -550,13 +1745,11 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ "android" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -568,13 +1761,11 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ "android" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -586,13 +1777,11 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ "darwin" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -604,13 +1793,11 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ "darwin" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -622,13 +1809,11 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ "freebsd" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -640,13 +1825,11 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ "linux" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -658,13 +1841,11 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ "linux" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -676,13 +1857,11 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ "linux" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -694,13 +1873,11 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ "linux" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -712,13 +1889,11 @@ "cpu": [ "s390x" ], - "dev": true, "license": "MIT", "optional": true, "os": [ "linux" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -730,13 +1905,11 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ "linux" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -748,13 +1921,11 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ "linux" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -766,13 +1937,11 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ "openharmony" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -784,13 +1953,11 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ "win32" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -802,13 +1969,11 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ "win32" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -817,9 +1982,117 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", - "dev": true, + "license": "MIT" + }, + "node_modules/@shikijs/core": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-4.5.0.tgz", + "integrity": "sha512-6CIgwq+bt2JZ3OSjb/nBkkwEf/CfpHcbam+Mg5Pllco7HAGRQ2/EFhZrrJtbyNTgKwknfVS+llsXGygiDGwJWw==", "license": "MIT", - "peer": true + "dependencies": { + "@shikijs/primitive": "4.5.0", + "@shikijs/types": "4.5.0", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.5", + "hast-util-to-html": "^9.0.5" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/engine-javascript": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-4.5.0.tgz", + "integrity": "sha512-Ed6UdEj84LyYkwnxM5CvSMWAD+id9ExTYri0/+x41U9zrgNifPSS9lZm0JqBP0VfYI8yr8tY5nbNq9+p9R0kFA==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.5.0", + "@shikijs/vscode-textmate": "^10.0.2", + "oniguruma-to-es": "^4.3.6" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/engine-oniguruma": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-4.5.0.tgz", + "integrity": "sha512-dKVmbJecVdB9recl8a/CU4jdpNXyjJQrIcesIkpS70s5HS02ohpnCSpMUbotPnEUzbAcLQ+S4hwZYbkBBiTcJw==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.5.0", + "@shikijs/vscode-textmate": "^10.0.2" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/langs": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-4.5.0.tgz", + "integrity": "sha512-wwBTqwduXSz0X4v9Qa2EWeFaYJEw1clCncWjoRNQRbVDu3SPRTRWucnouYapKj3b8Q+sk/41Vbc4B5c8nuYzLA==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.5.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/primitive": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/primitive/-/primitive-4.5.0.tgz", + "integrity": "sha512-UM7ShlA8oz0OWxLLO8BmO9Ip6fVqdkt8yF0EH7nOCqHfrx1eEAzF0ZUVK7L+I8NEgeu1PEWSRlxaiPPeCb4iDg==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.5.0", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.5" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/themes": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-4.5.0.tgz", + "integrity": "sha512-zGb87H3UBbpuI4yoh5UguB1XbU+2BDU3kflM7UfZ+Cy2hWo9lBtqjSVK20gmLY6ukYl040hAypGXMgud9vul8Q==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.5.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/types": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-4.5.0.tgz", + "integrity": "sha512-drtPrtD8N0DENyD2cyt6Ajy+IWsiCLgMxODwLTpvH1UsN6a9jf90tNTPjqwHvA+0Lzu9e0txk8DjfqH1p9UJRw==", + "license": "MIT", + "dependencies": { + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.5" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/vscode-textmate": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz", + "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==", + "license": "MIT" + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.4.tgz", + "integrity": "sha512-W3c4gRigFS0T/Ma4qIYF3GDAc5AQdHb1yL5znJT1Zv1YaD9Kitx656wBjvr19qbiosmZT8lWDM5BEMynUqX65A==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.4.0" + } }, "node_modules/@types/chai": { "version": "5.2.3", @@ -843,19 +2116,66 @@ "version": "1.0.9", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", - "dev": true, "license": "MIT" }, + "node_modules/@types/estree-jsx": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", + "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/@types/hast": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.5.tgz", + "integrity": "sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/nlcst": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/nlcst/-/nlcst-2.0.3.tgz", + "integrity": "sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, "node_modules/@types/node": { "version": "22.20.5", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.20.5.tgz", "integrity": "sha512-U2+DNr+wSjpsTS/wZGYHq7GcwfuSmKiKvoPvK22zwTlRhU91yOniN4qRR5KhIjvif7ysw/dz/hKmfDH0Ris4aA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "undici-types": "~6.21.0" } }, + "node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/@ungap/structured-clone": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.4.0.tgz", + "integrity": "sha512-1mEZtMKPM09vDmQt5y7YvmN2+DFTP7Tg0EWXdic8/C6VRnpb33e4ghisCIE3WZjsE2N8mf+QV1Zqh7ZFYLWInQ==", + "license": "ISC" + }, "node_modules/@vitest/mocker": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-5.0.3.tgz", @@ -894,6 +2214,111 @@ "url": "https://opencollective.com/vitest" } }, + "node_modules/@volar/kit": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/kit/-/kit-2.4.28.tgz", + "integrity": "sha512-cKX4vK9dtZvDRaAzeoUdaAJEew6IdxHNCRrdp5Kvcl6zZOqb6jTOfk3kXkIkG3T7oTFXguEMt5+9ptyqYR84Pg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-service": "2.4.28", + "@volar/typescript": "2.4.28", + "typesafe-path": "^0.2.2", + "vscode-languageserver-textdocument": "^1.0.11", + "vscode-uri": "^3.0.8" + }, + "peerDependencies": { + "typescript": "*" + } + }, + "node_modules/@volar/language-core": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.28.tgz", + "integrity": "sha512-w4qhIJ8ZSitgLAkVay6AbcnC7gP3glYM3fYwKV3srj8m494E3xtrCv6E+bWviiK/8hs6e6t1ij1s2Endql7vzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/source-map": "2.4.28" + } + }, + "node_modules/@volar/language-server": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/language-server/-/language-server-2.4.28.tgz", + "integrity": "sha512-NqcLnE5gERKuS4PUFwlhMxf6vqYo7hXtbMFbViXcbVkbZ905AIVWhnSo0ZNBC2V127H1/2zP7RvVOVnyITFfBw==", + "dev": true, + "license": "MIT", + "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.17.5", + "vscode-languageserver-textdocument": "^1.0.11", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/@volar/language-service": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/language-service/-/language-service-2.4.28.tgz", + "integrity": "sha512-Rh/wYCZJrI5vCwMk9xyw/Z+MsWxlJY1rmMZPsxUoJKfzIRjS/NF1NmnuEcrMbEVGja00aVpCsInJfixQTMdvLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.28", + "vscode-languageserver-protocol": "^3.17.5", + "vscode-languageserver-textdocument": "^1.0.11", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/@volar/source-map": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.28.tgz", + "integrity": "sha512-yX2BDBqJkRXfKw8my8VarTyjv48QwxdJtvRgUpNE5erCsgEUdI2DsLbpa+rOQVAJYshY99szEcRDmyHbF10ggQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@volar/typescript": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.28.tgz", + "integrity": "sha512-Ja6yvWrbis2QtN4ClAKreeUZPVYMARDYZl9LMEv1iQ1QdepB6wn0jTRxA9MftYmYa4DQ4k/DaSZpFPUfxl8giw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.28", + "path-browserify": "^1.0.1", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/@vscode/emmet-helper": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@vscode/emmet-helper/-/emmet-helper-2.11.0.tgz", + "integrity": "sha512-QLxjQR3imPZPQltfbWRnHU6JecWTF1QSWhx3GAKQpslx7y3Dp6sIIXhKjiUJ/BR9FX8PVthjr9PD6pNwOJfAzw==", + "dev": true, + "license": "MIT", + "dependencies": { + "emmet": "^2.4.3", + "jsonc-parser": "^2.3.0", + "vscode-languageserver-textdocument": "^1.0.1", + "vscode-languageserver-types": "^3.15.1", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/@vscode/emmet-helper/node_modules/jsonc-parser": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-2.3.1.tgz", + "integrity": "sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vscode/l10n": { + "version": "0.0.18", + "resolved": "https://registry.npmjs.org/@vscode/l10n/-/l10n-0.0.18.tgz", + "integrity": "sha512-KYSIHVmslkaCDyw013pphY+d7x1qV8IZupYfeIfzNA+nsaWHbn5uPuQRvdRFsa9zFzGeudPuoGoZ1Op4jrJXIQ==", + "dev": true, + "license": "MIT" + }, "node_modules/accepts": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", @@ -923,6 +2348,21 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ajv-draft-04": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz", + "integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "ajv": "^8.5.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, "node_modules/ajv-formats": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", @@ -940,6 +2380,94 @@ } } }, + "node_modules/ajv-i18n": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/ajv-i18n/-/ajv-i18n-4.2.0.tgz", + "integrity": "sha512-v/ei2UkCEeuKNXh8RToiFsUclmU+G57LO1Oo22OagNMENIw+Yb8eMwvHu7Vn9fmkjJyv6XclhJ8TbuigSglPkg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "ajv": "^8.0.0-beta.0" + } + }, + "node_modules/am-i-vibing": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/am-i-vibing/-/am-i-vibing-0.4.0.tgz", + "integrity": "sha512-MxT4XZL7pzLHpuvhDKdMaQHMGGkJDLluKBLsbstn+8wv9sWcFT6h+0ve9qkml95amVTZtZV83gQe2hY+ojgHLg==", + "license": "MIT", + "dependencies": { + "process-ancestry": "^0.1.0" + }, + "bin": { + "am-i-vibing": "dist/cli.mjs" + } + }, + "node_modules/ansi-regex": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.4.0.tgz", + "integrity": "sha512-KzTVk2tCWAHtYrvvvaP8bJKJq2pVinhLcGEQdtLIYPbmNGNyYe8QwNaTUYQp2J7/vIsUKt5QCqAfUkYyG9DkOw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, + "node_modules/aria-query": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/assertion-error": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", @@ -950,6 +2478,135 @@ "node": ">=12" } }, + "node_modules/astro": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/astro/-/astro-7.3.5.tgz", + "integrity": "sha512-1p/ernaq/RuESbM5NMjWzbO6SiebMGrGcYlGjMfBhzzgM+n/X7AUmVyw8BqW2QYCdivpsHwJBcD1fqmlTZjZHg==", + "license": "MIT", + "dependencies": { + "@astrojs/compiler-rs": "^0.5.0", + "@astrojs/internal-helpers": "0.11.0", + "@astrojs/markdown-satteri": "0.4.2", + "@astrojs/telemetry": "3.3.3", + "@capsizecss/unpack": "^4.0.0", + "@clack/prompts": "^1.1.0", + "@oslojs/encoding": "^1.1.0", + "am-i-vibing": "^0.4.0", + "aria-query": "^5.3.2", + "axobject-query": "^4.1.0", + "ci-info": "^4.4.0", + "clsx": "^2.1.1", + "common-ancestor-path": "^2.0.0", + "cookie": "^2.0.1", + "devalue": "^5.8.1", + "diff": "^9.0.0", + "dset": "^3.1.4", + "es-module-lexer": "^2.0.0", + "esbuild": "^0.28.0", + "find-proc": "0.2.0", + "flattie": "^1.1.1", + "fontace": "~0.4.1", + "get-tsconfig": "5.0.0-beta.4", + "github-slugger": "^2.0.0", + "html-escaper": "3.0.3", + "http-cache-semantics": "^4.2.0", + "js-yaml": "^4.3.2", + "jsonc-parser": "^3.3.1", + "magic-string": "^1.0.0", + "magicast": "^0.5.2", + "mrmime": "^2.0.1", + "neotraverse": "^1.0.1", + "obug": "^3.0.0", + "p-limit": "^7.3.0", + "p-queue": "^9.1.0", + "package-manager-detector": "^1.6.0", + "piccolore": "^0.1.3", + "picomatch": "^4.0.4", + "shiki": "^4.0.2", + "smol-toml": "^1.8.0", + "svgo": "^4.1.0", + "tinyclip": "^1.0.1", + "tinyexec": "^1.0.4", + "tinyglobby": "^0.2.15", + "ultrahtml": "^1.6.0", + "unifont": "~0.7.5", + "unstorage": "^1.17.5", + "verkit": "^0.4.0", + "vite": "^8.0.13", + "vitefu": "^1.1.2", + "xxhash-wasm": "^1.1.0", + "yargs-parser": "^22.0.0", + "zod": "^4.5.4" + }, + "bin": { + "astro": "bin/astro.mjs" + }, + "engines": { + "node": ">=22.12.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/astrodotbuild" + }, + "optionalDependencies": { + "sharp": "^0.35.4" + }, + "peerDependencies": { + "@astrojs/markdown-remark": "^7.3.0" + }, + "peerDependenciesMeta": { + "@astrojs/markdown-remark": { + "optional": true + } + } + }, + "node_modules/astro/node_modules/cookie": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-2.0.1.tgz", + "integrity": "sha512-yuToqVvRrj6pfDXREyQAAv8SkAEk/8GS3jQRTiUMm66TVtBYmqQeoEjL2Lmq8Rpo6271vH76InTChTitEAm65w==", + "license": "MIT", + "engines": { + "node": ">=22" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/astro/node_modules/obug": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/obug/-/obug-3.0.0.tgz", + "integrity": "sha512-5vvB5+W7ePv+p3uqxi+RcW1XAzLW0/hxt3/4X4Lc4qHudzOhmBiBwOY6DRob4WnanAEGvNcLjF+KNOufrUoEQw==", + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/bail": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/body-parser": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.3.0.tgz", @@ -987,6 +2644,12 @@ "url": "https://opencollective.com/express" } }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "license": "ISC" + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -1025,6 +2688,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/chai": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/chai/-/chai-6.3.0.tgz", @@ -1035,6 +2708,127 @@ "node": ">=18" } }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/ci-info": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", + "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz", + "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/cliui/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/commander": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", + "license": "MIT", + "engines": { + "node": ">=16" + } + }, + "node_modules/common-ancestor-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/common-ancestor-path/-/common-ancestor-path-2.0.0.tgz", + "integrity": "sha512-dnN3ibLeoRf2HNC+OlCiNc5d2zxbLJXOtiZUudNFSXZrNSydxcCsSpRzXwfu7BBWCIfHPw+xTayeBvJCP/D8Ng==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">= 18" + } + }, "node_modules/content-disposition": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-2.0.1.tgz", @@ -1066,6 +2860,12 @@ "node": ">= 0.6" } }, + "node_modules/cookie-es": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-1.2.3.tgz", + "integrity": "sha512-lXVyvUvrNXblMqzIRrxHb57UUVmqsSWlxqt3XIjCkUP0wDAf6uicO6KMbEgYrMNtEvWgWHwe42CKxPu9MYAnWw==", + "license": "MIT" + }, "node_modules/cookie-signature": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", @@ -1106,6 +2906,89 @@ "node": ">= 8" } }, + "node_modules/crossws": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/crossws/-/crossws-0.3.5.tgz", + "integrity": "sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA==", + "license": "MIT", + "dependencies": { + "uncrypto": "^0.1.3" + } + }, + "node_modules/css-select": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-6.0.0.tgz", + "integrity": "sha512-rZZVSLle8v0+EY8QAkDWrKhpgt6SA5OtHsgBnsj6ZaLb5dmDVOWUDtQitd9ydxxvEjhewNudS6eTVU7uOyzvXw==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^7.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.2.2", + "nth-check": "^2.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-tree": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz", + "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==", + "license": "MIT", + "dependencies": { + "mdn-data": "2.27.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/css-what": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-7.0.0.tgz", + "integrity": "sha512-wD5oz5xibMOPHzy13CyGmogB3phdvcDaB5t0W/Nr5Z2O/agcB8YwOz6e2Lsp10pNDzBoDO9nVa3RGs/2BttpHQ==", + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/csso": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", + "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", + "license": "MIT", + "dependencies": { + "css-tree": "~2.2.0" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/csso/node_modules/css-tree": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", + "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", + "license": "MIT", + "dependencies": { + "mdn-data": "2.0.28", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/csso/node_modules/mdn-data": { + "version": "2.0.28", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", + "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==", + "license": "CC0-1.0" + }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", @@ -1123,6 +3006,12 @@ } } }, + "node_modules/defu": { + "version": "6.1.7", + "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.7.tgz", + "integrity": "sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==", + "license": "MIT" + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -1132,17 +3021,122 @@ "node": ">= 0.8" } }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/destr": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz", + "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==", + "license": "MIT" + }, "node_modules/detect-libc": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", - "dev": true, "license": "Apache-2.0", - "peer": true, "engines": { "node": ">=8" } }, + "node_modules/devalue": { + "version": "5.9.4", + "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.9.4.tgz", + "integrity": "sha512-sPAT4pztbu6586/hrhOnMKS17IJrvg12mXiSPSS3W5qDeN2RGgvZ0diZCm31dBbnevfVmujNO3IM2wrS4Y2Rhg==", + "license": "MIT" + }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/diff": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-9.0.0.tgz", + "integrity": "sha512-svtcdpS8CgJyqAjEQIXdb3OjhFVVYjzGAPO8WGCmRbrml64SPw/jJD4GoE98aR7r25A0XcgrK3F02yw9R/vhQw==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dset": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/dset/-/dset-3.1.4.tgz", + "integrity": "sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -1163,6 +3157,30 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", "license": "MIT" }, + "node_modules/emmet": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/emmet/-/emmet-2.4.11.tgz", + "integrity": "sha512-23QPJB3moh/U9sT4rQzGgeyyGIrcM+GH5uVYg2C6wZIxAIJq7Ng3QLT79tl8FUwDXhyq9SusfknOrofAKqvgyQ==", + "dev": true, + "license": "MIT", + "workspaces": [ + "./packages/scanner", + "./packages/abbreviation", + "./packages/css-abbreviation", + "./" + ], + "dependencies": { + "@emmetio/abbreviation": "^2.3.3", + "@emmetio/css-abbreviation": "^2.1.8" + } + }, + "node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "dev": true, + "license": "MIT" + }, "node_modules/encodeurl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", @@ -1172,6 +3190,18 @@ "node": ">= 0.8" } }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/es-define-property": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", @@ -1194,7 +3224,6 @@ "version": "2.3.2", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.2.tgz", "integrity": "sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==", - "dev": true, "license": "MIT" }, "node_modules/es-object-atoms": { @@ -1250,6 +3279,16 @@ "@esbuild/win32-x64": "0.28.2" } }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -1275,6 +3314,12 @@ "node": ">= 0.6" } }, + "node_modules/eventemitter3": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", + "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", + "license": "MIT" + }, "node_modules/eventsource": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz", @@ -1381,12 +3426,33 @@ "url": "https://opencollective.com/express" } }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "license": "MIT" }, + "node_modules/fast-string-truncated-width": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fast-string-truncated-width/-/fast-string-truncated-width-3.0.3.tgz", + "integrity": "sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==", + "license": "MIT" + }, + "node_modules/fast-string-width": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/fast-string-width/-/fast-string-width-3.0.2.tgz", + "integrity": "sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==", + "license": "MIT", + "dependencies": { + "fast-string-truncated-width": "^3.0.2" + } + }, "node_modules/fast-uri": { "version": "3.1.8", "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.8.tgz", @@ -1403,11 +3469,19 @@ ], "license": "BSD-3-Clause" }, + "node_modules/fast-wrap-ansi": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/fast-wrap-ansi/-/fast-wrap-ansi-0.2.2.tgz", + "integrity": "sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==", + "license": "MIT", + "dependencies": { + "fast-string-width": "^3.0.2" + } + }, "node_modules/fdir": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, "license": "MIT", "engines": { "node": ">=12.0.0" @@ -1442,6 +3516,45 @@ "url": "https://opencollective.com/express" } }, + "node_modules/find-proc": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/find-proc/-/find-proc-0.2.0.tgz", + "integrity": "sha512-a6h2nTrgB3g5Wrn4au9Ux4kMk7IJtyF6oA8uGn+mk3weLZssUtc1cp6meHIzzuW2o1srrBAxzkmXLbqjRkgB6w==", + "license": "MIT", + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/flattie": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/flattie/-/flattie-1.1.1.tgz", + "integrity": "sha512-9UbaD6XdAL97+k/n+N7JwX46K/M6Zc6KcFYskrYL8wbBV/Uyk0CTAMY0VT+qiK5PM7AIc9aTWYtq65U7T+aCNQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/fontace": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/fontace/-/fontace-0.4.1.tgz", + "integrity": "sha512-lDMvbAzSnHmbYMTEld5qdtvNH2/pWpICOqpean9IgC7vUbUJc3k+k5Dokp85CegamqQpFbXf0rAVkbzpyTA8aw==", + "license": "MIT", + "dependencies": { + "fontkitten": "^1.0.2" + } + }, + "node_modules/fontkitten": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/fontkitten/-/fontkitten-1.0.3.tgz", + "integrity": "sha512-Wp1zXWPVUPBmfoa3Cqc9ctaKuzKAV6uLstRqlR56kSjplf5uAce+qeyYym7F+PHbGTk+tCEdkCW6RD7DX/gBZw==", + "license": "MIT", + "dependencies": { + "tiny-inflate": "^1.0.3" + }, + "engines": { + "node": ">=20" + } + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -1483,6 +3596,29 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.7.0.tgz", + "integrity": "sha512-XjH1AECxf0giL2V1aU8vKyRR2ppRUb5c0EvT7zuJTokQ74bNo52zOtghqdWIqrhUD79fo3x0WfKZdOqxF6LG1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", @@ -1520,6 +3656,27 @@ "node": ">= 0.4" } }, + "node_modules/get-tsconfig": { + "version": "5.0.0-beta.4", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-5.0.0-beta.4.tgz", + "integrity": "sha512-7nF7C9fIPFEMHgEMEfgIlO9wDdZ8CyHw27rWciFZfHvHDReIiPhsYuzPRXsfvBCqFy1l8RRyyWV7QLM+ZhUJsQ==", + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "engines": { + "node": ">=20.20.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/github-slugger": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-2.0.0.tgz", + "integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==", + "license": "ISC" + }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", @@ -1532,6 +3689,23 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/h3": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/h3/-/h3-1.15.11.tgz", + "integrity": "sha512-L3THSe2MPeBwgIZVSH5zLdBBU90TOxarvhK9d04IDY2AmVS8j2Jz2LIWtwsGOU3lu2I5jCN7FNvVfY2+XyF+mg==", + "license": "MIT", + "dependencies": { + "cookie-es": "^1.2.3", + "crossws": "^0.3.5", + "defu": "^6.1.6", + "destr": "^2.0.5", + "iron-webcrypto": "^1.2.1", + "node-mock-http": "^1.0.4", + "radix3": "^1.1.2", + "ufo": "^1.6.3", + "uncrypto": "^0.1.3" + } + }, "node_modules/has-symbols": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", @@ -1556,6 +3730,42 @@ "node": ">= 0.4" } }, + "node_modules/hast-util-to-html": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz", + "integrity": "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-whitespace": "^3.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "stringify-entities": "^4.0.0", + "zwitch": "^2.0.4" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/hono": { "version": "4.13.13", "resolved": "https://registry.npmjs.org/hono/-/hono-4.13.13.tgz", @@ -1565,6 +3775,28 @@ "node": ">=16.9.0" } }, + "node_modules/html-escaper": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-3.0.3.tgz", + "integrity": "sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==", + "license": "MIT" + }, + "node_modules/html-void-elements": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", + "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.3.0.tgz", + "integrity": "sha512-M5t5LlJpS1UHMjvwRQVdFHvPISGeLAxNcrWuJkeGh0KxsqCHZ1O3NXZU/8x7cD0BDcGW8kapxMKTvwlqrNkHkA==", + "license": "BSD-2-Clause" + }, "node_modules/http-errors": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", @@ -1625,6 +3857,42 @@ "node": ">= 0.10" } }, + "node_modules/iron-webcrypto": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/iron-webcrypto/-/iron-webcrypto-1.2.1.tgz", + "integrity": "sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/brc-dd" + } + }, + "node_modules/is-docker": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-4.0.0.tgz", + "integrity": "sha512-LHE+wROyG/Y/0ZnbktRCoTix2c1RhgWaZraMZ8o1Q7zCh0VSrICJQO5oqIIISrcSBtrXv0o233w1IYwsWCjTzA==", + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-promise": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", @@ -1646,6 +3914,28 @@ "url": "https://github.com/sponsors/panva" } }, + "node_modules/js-yaml": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz", + "integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, "node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", @@ -1658,13 +3948,27 @@ "integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==", "license": "BSD-2-Clause" }, + "node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "license": "MIT" + }, + "node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/lightningcss": { "version": "1.33.0", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", - "dev": true, "license": "MPL-2.0", - "peer": true, "dependencies": { "detect-libc": "^2.0.3" }, @@ -1696,13 +4000,11 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ "android" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -1718,13 +4020,11 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ "darwin" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -1740,13 +4040,11 @@ "cpu": [ "x64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ "darwin" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -1762,13 +4060,11 @@ "cpu": [ "x64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ "freebsd" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -1784,13 +4080,11 @@ "cpu": [ "arm" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ "linux" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -1806,13 +4100,11 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ "linux" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -1828,13 +4120,11 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ "linux" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -1850,13 +4140,11 @@ "cpu": [ "x64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ "linux" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -1872,13 +4160,11 @@ "cpu": [ "x64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ "linux" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -1894,13 +4180,11 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ "win32" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -1916,13 +4200,11 @@ "cpu": [ "x64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ "win32" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -1931,16 +4213,35 @@ "url": "https://opencollective.com/parcel" } }, + "node_modules/lru-cache": { + "version": "11.5.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.3.tgz", + "integrity": "sha512-U4N8FgzmWxc8k1VH8Kr6lQg18U7Fjvby6wXHVRX/ZZ7IwWbRMgrRbP0Wrb5q5NVinryp4SQampHKdvtecItxUg==", + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, "node_modules/magic-string": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-1.4.3.tgz", "integrity": "sha512-z12OxmaPGE0F4xlpGdjuAUckipLpQlTAuAmyGhNoD7ODpYUzvDfvtxUbjM/jwznyP178oju/KDdyi220wNJ0RQ==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.6.0" } }, + "node_modules/magicast": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.5.tgz", + "integrity": "sha512-UicdXN8zQ3JHlxVq+28afMXPr1z7WNY6+7EJnzTdQWkTAlMLF5fNCCKxJHBQwGaNGR11581EiQmQzx73+MvszA==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7", + "source-map-js": "^1.2.1" + } + }, "node_modules/marked": { "version": "18.0.14", "resolved": "https://registry.npmjs.org/marked/-/marked-18.0.14.tgz", @@ -1962,6 +4263,33 @@ "node": ">= 0.4" } }, + "node_modules/mdast-util-to-hast": { + "version": "13.2.1", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", + "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdn-data": { + "version": "2.27.1", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", + "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==", + "license": "CC0-1.0" + }, "node_modules/media-typer": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.1.tgz", @@ -1987,6 +4315,95 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-types": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.3.tgz", + "integrity": "sha512-oxB2Ik03hI0gv+VNn9tnh1t1YEe9MDPptViAEgfdf3YQHsn0pzGTgCdlSCJXcwhqm8phaEuM7zeEu3QQzVBrPg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, "node_modules/mime-db": { "version": "1.54.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", @@ -2012,17 +4429,32 @@ "url": "https://opencollective.com/express" } }, + "node_modules/mrmime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", + "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, + "node_modules/muggle-string": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz", + "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==", + "dev": true, + "license": "MIT" + }, "node_modules/nanoid": { "version": "3.3.20", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.20.tgz", "integrity": "sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ==", - "dev": true, "funding": [ { "type": "github", @@ -2030,7 +4462,6 @@ } ], "license": "MIT", - "peer": true, "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -2067,6 +4498,61 @@ "url": "https://opencollective.com/express" } }, + "node_modules/neotraverse": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/neotraverse/-/neotraverse-1.0.1.tgz", + "integrity": "sha512-WmmLty1YWwJl9yZi77v2dVIV6X2kuYV8YYBI/G3LWGKdGHmHUvL1z7FW0iDvEvGAwNEoc5x1tOOOyDnf5jJw/w==", + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/nlcst-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/nlcst-to-string/-/nlcst-to-string-4.0.0.tgz", + "integrity": "sha512-YKLBCcUYKAg0FNlOBT6aI91qFmSiFKiluk655WzPF+DDMA02qIyy8uiRqI8QXtcFpEvll12LpL5MXqEmAZ+dcA==", + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/node-fetch-native": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz", + "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==", + "license": "MIT" + }, + "node_modules/node-mock-http": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/node-mock-http/-/node-mock-http-1.0.5.tgz", + "integrity": "sha512-KQyt/wLjG3TAc7DOUhpqWzgd4ERxR80JOlTK5VE5R1S12IaPVN5qkj4klBce9HPG1Njuup4Sb5bljaT34lIyjw==", + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -2102,6 +4588,23 @@ "node": ">=12.20.0" } }, + "node_modules/ofetch": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/ofetch/-/ofetch-1.5.1.tgz", + "integrity": "sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA==", + "license": "MIT", + "dependencies": { + "destr": "^2.0.5", + "node-fetch-native": "^1.6.7", + "ufo": "^1.6.1" + } + }, + "node_modules/ohash": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.12.tgz", + "integrity": "sha512-65S/5gk9YSsaRjcyf7Nfa6h/d3E8/1gslpXfI4W7Dxn/oap8IKRuNT5VXkLQ1YFKIEg4apRY4Pj6aiwFzrDdmw==", + "license": "MIT" + }, "node_modules/on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", @@ -2123,6 +4626,72 @@ "wrappy": "1" } }, + "node_modules/oniguruma-parser": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/oniguruma-parser/-/oniguruma-parser-0.12.2.tgz", + "integrity": "sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw==", + "license": "MIT" + }, + "node_modules/oniguruma-to-es": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-4.3.6.tgz", + "integrity": "sha512-csuQ9x3Yr0cEIs/Zgx/OEt9iBw9vqIunAPQkx19R/fiMq2oGVTgcMqO/V3Ybqefr1TBvosI6jU539ksaBULJyA==", + "license": "MIT", + "dependencies": { + "oniguruma-parser": "^0.12.2", + "regex": "^6.1.0", + "regex-recursion": "^6.0.2" + } + }, + "node_modules/p-limit": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-7.3.3.tgz", + "integrity": "sha512-SKgVvDU5TNIxQ4r30U75BNLC6a+6GOEjngrk/ysM5+Zu3oJuk1SC5ApzIzY/7PjCEjoVPMWJ0zho/4QUiEB7TQ==", + "license": "MIT", + "dependencies": { + "yocto-queue": "^1.2.1" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-queue": { + "version": "9.3.5", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-9.3.5.tgz", + "integrity": "sha512-Lki/zhXFVmDhesBUn/yHRLW9Bte7mE7xoFHzTR8sIomV7a2z+LxYAPkGvdfidNiu4fKSfjtawixxSHcZci5QcQ==", + "license": "MIT", + "dependencies": { + "eventemitter3": "^5.0.4", + "p-timeout": "^7.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-timeout": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-7.0.2.tgz", + "integrity": "sha512-prbX4Z3YszrFNgH+MW5Zoeq3baXrMtP/MQnFeET90UB/GtGcGDQ5Usg9OCy6ETjTTntOw1SL2z9fMPUppN3Guw==", + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-manager-detector": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.9.0.tgz", + "integrity": "sha512-zAMfbta9nPA8AKkA4WdmNAT8R8s6Dk0/xwPsqNnRidMFcLo1YkLuPDTgs2f64XdzS8vbLNnoJ+uUIkgbz7oRHA==", + "license": "MIT" + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -2132,6 +4701,13 @@ "node": ">= 0.8" } }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true, + "license": "MIT" + }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -2151,19 +4727,22 @@ "url": "https://opencollective.com/express" } }, + "node_modules/piccolore": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/piccolore/-/piccolore-0.1.3.tgz", + "integrity": "sha512-o8bTeDWjE086iwKrROaDf31K0qC/BENdm15/uH9usSC/uZjJOKb2YGiVHfLY4GhwsERiPI1jmwI2XrA7ACOxVw==", + "license": "ISC" + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, - "license": "ISC", - "peer": true + "license": "ISC" }, "node_modules/picomatch": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", - "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -2185,7 +4764,6 @@ "version": "8.5.29", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.29.tgz", "integrity": "sha512-49cGhUbXj8Qenv0iTMxA1cFBzxXoctpC9Ujd77t1WcbJIr6nF/eI7g/8MgxrYldFRuAXvja7xQRwavoW7kgrxQ==", - "dev": true, "funding": [ { "type": "opencollective", @@ -2201,7 +4779,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.19", "picocolors": "^1.1.1", @@ -2211,6 +4788,50 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/prettier": { + "version": "3.9.10", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.10.tgz", + "integrity": "sha512-skGHTH1Yb9QvaTC4hWnRTzEYtCpApF0UMBPz+t/0ZPzP8ihefo/btKd+QA7pUE0fEwA1SbS/NmhyEcstdXOIdA==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prismjs": { + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", + "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/process-ancestry": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/process-ancestry/-/process-ancestry-0.1.0.tgz", + "integrity": "sha512-tGqJW/UnclpYASFcM6Xh8D8l/BMtaQ9+CSG0vlJSJTcdMM4lDRv4c6H0Pdcsfted+bVczdYSfk2fdukg2gQkZg==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/property-information": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.2.0.tgz", + "integrity": "sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/proxy-addr": { "version": "2.0.8", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.8.tgz", @@ -2244,6 +4865,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/radix3": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/radix3/-/radix3-1.1.2.tgz", + "integrity": "sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==", + "license": "MIT" + }, "node_modules/range-parser": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.3.0.tgz", @@ -2272,6 +4899,51 @@ "node": ">= 0.10" } }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/regex/-/regex-6.1.0.tgz", + "integrity": "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==", + "license": "MIT", + "dependencies": { + "regex-utilities": "^2.3.0" + } + }, + "node_modules/regex-recursion": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/regex-recursion/-/regex-recursion-6.0.2.tgz", + "integrity": "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==", + "license": "MIT", + "dependencies": { + "regex-utilities": "^2.3.0" + } + }, + "node_modules/regex-utilities": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/regex-utilities/-/regex-utilities-2.3.0.tgz", + "integrity": "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==", + "license": "MIT" + }, + "node_modules/request-light": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/request-light/-/request-light-0.7.0.tgz", + "integrity": "sha512-lMbBMrDoxgsyO+yB3sDcrDuX85yYt7sS8BfQd11jtbW/z5ZWgLZRcEGLsLoYw7I0WSUGQBs8CC8ScIxkTX1+6Q==", + "dev": true, + "license": "MIT" + }, "node_modules/require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", @@ -2281,13 +4953,35 @@ "node": ">=0.10.0" } }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/retext-smartypants": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/retext-smartypants/-/retext-smartypants-6.2.0.tgz", + "integrity": "sha512-kk0jOU7+zGv//kfjXEBjdIryL1Acl4i9XNkHxtM7Tm5lFiCog576fjNC9hjoR7LTKQ0DsPWy09JummSsH1uqfQ==", + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0", + "nlcst-to-string": "^4.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/rolldown": { "version": "1.2.13", "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.13.tgz", "integrity": "sha512-/5apOkwWxtACcQtZ5WlrAsKS4JwXVThQPJZ3RqIruRY9UG9M8e0VlXN068xum/JRKA4aIUoJ13ghe/kGCvidiQ==", - "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@oxc-project/types": "=0.153.0", "@rolldown/pluginutils": "^1.0.0" @@ -2338,6 +5032,51 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "license": "MIT" }, + "node_modules/satteri": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/satteri/-/satteri-0.10.5.tgz", + "integrity": "sha512-Ao1LKpAEa9Wdg0otgbVKViZHEq9ebdXe4DMrp3s9vQAU0HNIuHnFEuMuOcm0ZIXyV0Yzxj91NvhLpvXZJO/5ZQ==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.5", + "@types/hast": "^3.0.5", + "@types/mdast": "^4.0.4", + "@types/unist": "^3.0.3" + }, + "optionalDependencies": { + "@bruits/satteri-darwin-arm64": "0.10.5", + "@bruits/satteri-darwin-x64": "0.10.5", + "@bruits/satteri-linux-arm64-gnu": "0.10.5", + "@bruits/satteri-linux-arm64-musl": "0.10.5", + "@bruits/satteri-linux-x64-gnu": "0.10.5", + "@bruits/satteri-linux-x64-musl": "0.10.5", + "@bruits/satteri-wasm32-wasi": "0.10.5", + "@bruits/satteri-win32-arm64-msvc": "0.10.5", + "@bruits/satteri-win32-x64-msvc": "0.10.5" + } + }, + "node_modules/sax": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.1.tgz", + "integrity": "sha512-42tBVwLWnaQvW5zc4HbZrTuWccECCZfBi92FDuwtqxasH+JbPB3/FOKb1m222K42R4WxuxzzMsTswfzgtSu64Q==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=11.0.0" + } + }, + "node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "devOptional": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/send": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", @@ -2389,6 +5128,56 @@ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", "license": "ISC" }, + "node_modules/sharp": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.35.5.tgz", + "integrity": "sha512-Ywn4OnzGukp7CDMrp08RQ50YKmuwG47brZgIVPTvBaaAfQlRlygrRqSrxdCiL9M+LlzLBiJ68IR1QqvzHyjC7g==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@img/colour": "^1.1.0", + "detect-libc": "^2.1.2", + "semver": "^7.8.5" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.35.5", + "@img/sharp-darwin-x64": "0.35.5", + "@img/sharp-freebsd-wasm32": "0.35.5", + "@img/sharp-libvips-darwin-arm64": "1.3.4", + "@img/sharp-libvips-darwin-x64": "1.3.4", + "@img/sharp-libvips-linux-arm": "1.3.4", + "@img/sharp-libvips-linux-arm64": "1.3.4", + "@img/sharp-libvips-linux-ppc64": "1.3.4", + "@img/sharp-libvips-linux-riscv64": "1.3.4", + "@img/sharp-libvips-linux-s390x": "1.3.4", + "@img/sharp-libvips-linux-x64": "1.3.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.3.4", + "@img/sharp-libvips-linuxmusl-x64": "1.3.4", + "@img/sharp-linux-arm": "0.35.5", + "@img/sharp-linux-arm64": "0.35.5", + "@img/sharp-linux-ppc64": "0.35.5", + "@img/sharp-linux-riscv64": "0.35.5", + "@img/sharp-linux-s390x": "0.35.5", + "@img/sharp-linux-x64": "0.35.5", + "@img/sharp-linuxmusl-arm64": "0.35.5", + "@img/sharp-linuxmusl-x64": "0.35.5", + "@img/sharp-webcontainers-wasm32": "0.35.5", + "@img/sharp-win32-arm64": "0.35.5", + "@img/sharp-win32-ia32": "0.35.5", + "@img/sharp-win32-x64": "0.35.5" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -2410,6 +5199,25 @@ "node": ">=8" } }, + "node_modules/shiki": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-4.5.0.tgz", + "integrity": "sha512-OcRW72ngQvc+MSVWBRIovC/BTW589iUE/Vxav7VeM4RcY9MUjqB+IR6ILG1k9nuW6LiG/nct9uma4/MBtnZbmQ==", + "license": "MIT", + "dependencies": { + "@shikijs/core": "4.5.0", + "@shikijs/engine-javascript": "4.5.0", + "@shikijs/engine-oniguruma": "4.5.0", + "@shikijs/langs": "4.5.0", + "@shikijs/themes": "4.5.0", + "@shikijs/types": "4.5.0", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.5" + }, + "engines": { + "node": ">=20" + } + }, "node_modules/side-channel": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", @@ -2482,17 +5290,43 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "license": "MIT" + }, + "node_modules/smol-toml": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.9.1.tgz", + "integrity": "sha512-j7WyPdf0CJoutMzMoPLF/bVXHtKZIT9Rb4eGeEbWw8X2t5oirjPgpL+OpaENZrILYaXqYwnVH+qgUwVbl+pUJQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">= 18" + }, + "funding": { + "url": "https://github.com/sponsors/cyyynthia" + } + }, "node_modules/source-map-js": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.2.tgz", "integrity": "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==", - "dev": true, "license": "BSD-3-Clause", - "peer": true, "engines": { "node": ">=0.10.0" } }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/statuses": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", @@ -2509,6 +5343,84 @@ "dev": true, "license": "MIT" }, + "node_modules/string-width": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.3.0.tgz", + "integrity": "sha512-ZbmZM0JCihQN91dWnxoipT2KOEyHqEyfRXUyjuRhW8b/xnqPDoq4gWEVApTVa9db2wN8mmoikgFBbjh71+cGeQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.5.0", + "strip-ansi": "^7.1.2" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/stringify-entities": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", + "license": "MIT", + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/svgo": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-4.1.0.tgz", + "integrity": "sha512-bkxnTg1kSU0guhIBmibA6UUhrQmPVA1XsQLN+ylCd+UWzbnLkySOcXpyk1mrl05f+pcaCx2eHb+sp6BgMZWX+Q==", + "license": "MIT", + "dependencies": { + "commander": "^11.1.0", + "css-select": "^6.0.0", + "css-tree": "^3.0.1", + "css-what": "^7.0.0", + "csso": "^5.0.5", + "picocolors": "^1.1.1", + "sax": "1.6.1" + }, + "bin": { + "svgo": "bin/svgo.js" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/svgo" + } + }, + "node_modules/tiny-inflate": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", + "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==", + "license": "MIT" + }, "node_modules/tinybench": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-6.2.2.tgz", @@ -2519,11 +5431,19 @@ "node": ">=20.0.0" } }, + "node_modules/tinyclip": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tinyclip/-/tinyclip-1.0.3.tgz", + "integrity": "sha512-nTv2raKFMzGG6JJp0lpNDsRPdfFE6UHlu2rVEjXo8vkx1ZS3LW2CVXqIaLu8xZlu+6tbFs9V58Qc/Jmk/Ii7wA==", + "license": "MIT", + "engines": { + "node": "^16.14.0 || >= 17.3.0" + } + }, "node_modules/tinyexec": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.1.tgz", "integrity": "sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==", - "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -2533,7 +5453,6 @@ "version": "0.2.17", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", - "dev": true, "license": "MIT", "dependencies": { "fdir": "^6.5.0", @@ -2555,6 +5474,33 @@ "node": ">=0.6" } }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/trough": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", + "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "devOptional": true, + "license": "0BSD" + }, "node_modules/tsx": { "version": "4.23.15", "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.23.15.tgz", @@ -2604,6 +5550,13 @@ "url": "https://opencollective.com/express" } }, + "node_modules/typesafe-path": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/typesafe-path/-/typesafe-path-0.2.2.tgz", + "integrity": "sha512-OJabfkAg1WLZSqJAJ0Z6Sdt3utnbzr/jh+NAHoyWHJe8CMSy79Gm085094M9nvTPy22KzTVn5Zq5mbapCI/hPA==", + "dev": true, + "license": "MIT" + }, "node_modules/typescript": { "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", @@ -2617,13 +5570,148 @@ "node": ">=14.17" } }, + "node_modules/typescript-auto-import-cache": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/typescript-auto-import-cache/-/typescript-auto-import-cache-0.3.6.tgz", + "integrity": "sha512-RpuHXrknHdVdK7wv/8ug3Fr0WNsNi5l5aB8MYYuXhq2UH5lnEB1htJ1smhtD5VeCsGr2p8mUDtd83LCQDFVgjQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.3.8" + } + }, + "node_modules/ufo": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.4.tgz", + "integrity": "sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==", + "license": "MIT" + }, + "node_modules/ultrahtml": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ultrahtml/-/ultrahtml-1.7.0.tgz", + "integrity": "sha512-2xRd0VHoAQE4M+vF/DvFFB7pUV0ZxTW1TLi7lHQWnF/Sb5TPeEUV/l+hxcNnGO00ZXGnR0voCMmYRKQf+rvJ2g==", + "license": "MIT" + }, + "node_modules/uncrypto": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/uncrypto/-/uncrypto-0.1.3.tgz", + "integrity": "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==", + "license": "MIT" + }, + "node_modules/undici": { + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/undici/-/undici-8.11.2.tgz", + "integrity": "sha512-u4UB2/IrKdU6lFxumHmmo1a3fCQO5tzQllRorfoRS63txhrB7xTpSn1PftwC4qEHkOaqP95fCWW4lJzwErwzhQ==", + "license": "MIT", + "engines": { + "node": ">=22.19.0" + } + }, "node_modules/undici-types": { "version": "6.21.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "dev": true, + "devOptional": true, "license": "MIT" }, + "node_modules/unified": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", + "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "bail": "^2.0.0", + "devlop": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unifont": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/unifont/-/unifont-0.7.5.tgz", + "integrity": "sha512-ULe/Cs+ZIsq+dcFofNkhqielCrUJnb5mr+Yc4EBM2VlL+6OZR6+cjtI2mT1bJvRBrVncqHAbLURxmPLcCXzWMg==", + "license": "MIT", + "dependencies": { + "css-tree": "^3.1.0", + "ohash": "^2.0.11", + "undici": "^8.0.0" + } + }, + "node_modules/unist-util-is": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", + "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.1.0.tgz", + "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", + "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -2633,6 +5721,130 @@ "node": ">= 0.8" } }, + "node_modules/unstorage": { + "version": "1.17.5", + "resolved": "https://registry.npmjs.org/unstorage/-/unstorage-1.17.5.tgz", + "integrity": "sha512-0i3iqvRfx29hkNntHyQvJTpf5W9dQ9ZadSoRU8+xVlhVtT7jAX57fazYO9EHvcRCfBCyi5YRya7XCDOsbTgkPg==", + "license": "MIT", + "dependencies": { + "anymatch": "^3.1.3", + "chokidar": "^5.0.0", + "destr": "^2.0.5", + "h3": "^1.15.10", + "lru-cache": "^11.2.7", + "node-fetch-native": "^1.6.7", + "ofetch": "^1.5.1", + "ufo": "^1.6.3" + }, + "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 + } + } + }, + "node_modules/unstorage/node_modules/chokidar": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", + "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", + "license": "MIT", + "dependencies": { + "readdirp": "^5.0.0" + }, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/unstorage/node_modules/readdirp": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.1.1.tgz", + "integrity": "sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA==", + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -2642,13 +5854,51 @@ "node": ">= 0.8" } }, + "node_modules/verkit": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/verkit/-/verkit-0.4.1.tgz", + "integrity": "sha512-mMVzj0TXExtVdlDEq+Mzp0eyOuyznNpFobNM3uAqe3HVm/Ps2c+u17BligMkZjZCA6EiXpoon8iRTer3iu40SQ==", + "license": "MIT", + "engines": { + "node": ">=18.12.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + } + }, + "node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", + "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/vite": { "version": "8.3.4", "resolved": "https://registry.npmjs.org/vite/-/vite-8.3.4.tgz", "integrity": "sha512-IftLE8phJhdNRIGNEV4JCCvfNScoZQVfcXaN8TQropWGJtOHcu+iAB9pHcuGvmEb/kddw0LBe/jS8ztdC6xdiQ==", - "dev": true, "license": "MIT", - "peer": true, "dependencies": { "lightningcss": "^1.33.0", "picomatch": "^4.0.7", @@ -2721,6 +5971,25 @@ } } }, + "node_modules/vitefu": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.1.3.tgz", + "integrity": "sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg==", + "license": "MIT", + "workspaces": [ + "tests/deps/*", + "tests/projects/*", + "tests/projects/workspace/packages/*" + ], + "peerDependencies": { + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } + } + }, "node_modules/vitest": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/vitest/-/vitest-5.0.3.tgz", @@ -2804,6 +6073,289 @@ } } }, + "node_modules/volar-service-css": { + "version": "0.0.71", + "resolved": "https://registry.npmjs.org/volar-service-css/-/volar-service-css-0.0.71.tgz", + "integrity": "sha512-wRRFt9BpjMKCazcgOh67MSjUjiWUCAh99DyYSDIOTuxaRjEtDC7PpB0k1Y1wbJIW/pVtMUSVbpPo3UGSm0Byxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "vscode-css-languageservice": "^6.3.0", + "vscode-languageserver-textdocument": "^1.0.11", + "vscode-uri": "^3.0.8" + }, + "peerDependencies": { + "@volar/language-service": "~2.4.0" + }, + "peerDependenciesMeta": { + "@volar/language-service": { + "optional": true + } + } + }, + "node_modules/volar-service-emmet": { + "version": "0.0.71", + "resolved": "https://registry.npmjs.org/volar-service-emmet/-/volar-service-emmet-0.0.71.tgz", + "integrity": "sha512-zqjzt6bN95e3CUstBm0PBFAJnrfz0ZAARka87fart46/gNCLLuP3Vujy8V/J8HEziTFLnfkgIASLFYPUhonJcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@emmetio/css-parser": "^0.4.1", + "@emmetio/html-matcher": "^1.3.0", + "@vscode/emmet-helper": "^2.9.3", + "vscode-uri": "^3.0.8" + }, + "peerDependencies": { + "@volar/language-service": "~2.4.0" + }, + "peerDependenciesMeta": { + "@volar/language-service": { + "optional": true + } + } + }, + "node_modules/volar-service-html": { + "version": "0.0.71", + "resolved": "https://registry.npmjs.org/volar-service-html/-/volar-service-html-0.0.71.tgz", + "integrity": "sha512-e8tHPhgQ7ooLfudAEIku+kgd9pWkq3SSz8RbnQDI1+Eb8wbenkLGHqoirLqz5ORLV6wIMr2Iv08RWBG5eOcgpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "vscode-html-languageservice": "^5.3.0", + "vscode-languageserver-textdocument": "^1.0.11", + "vscode-uri": "^3.0.8" + }, + "peerDependencies": { + "@volar/language-service": "~2.4.0" + }, + "peerDependenciesMeta": { + "@volar/language-service": { + "optional": true + } + } + }, + "node_modules/volar-service-prettier": { + "version": "0.0.71", + "resolved": "https://registry.npmjs.org/volar-service-prettier/-/volar-service-prettier-0.0.71.tgz", + "integrity": "sha512-Rz7JVH3qD108UCdmIEiZvOBNljMt2nLFdbN8AXcDfn7xD9F5I2aCIsDVqBbXw21PsnxG0b7MfwtNF+zPS/NKUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "vscode-uri": "^3.0.8" + }, + "peerDependencies": { + "@volar/language-service": "~2.4.0", + "prettier": "^2.2 || ^3.0" + }, + "peerDependenciesMeta": { + "@volar/language-service": { + "optional": true + }, + "prettier": { + "optional": true + } + } + }, + "node_modules/volar-service-typescript": { + "version": "0.0.71", + "resolved": "https://registry.npmjs.org/volar-service-typescript/-/volar-service-typescript-0.0.71.tgz", + "integrity": "sha512-yTtM/BVT6hoyEYnDtaCyAtNhdNeS/mhTTABlBOdw3NNiRBUin3IznFJpgfjer4c6RYopiPjjQjc9VFhxVl1mLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-browserify": "^1.0.1", + "semver": "^7.6.2", + "typescript-auto-import-cache": "^0.3.5", + "vscode-languageserver-textdocument": "^1.0.11", + "vscode-nls": "^5.2.0", + "vscode-uri": "^3.0.8" + }, + "peerDependencies": { + "@volar/language-service": "~2.4.0" + }, + "peerDependenciesMeta": { + "@volar/language-service": { + "optional": true + } + } + }, + "node_modules/volar-service-typescript-twoslash-queries": { + "version": "0.0.71", + "resolved": "https://registry.npmjs.org/volar-service-typescript-twoslash-queries/-/volar-service-typescript-twoslash-queries-0.0.71.tgz", + "integrity": "sha512-9K2k72s4n7rV9s4bX0MyjbX9iBribvKZbBJKuEmTCZfeWJXs6Yh7bGpY4eoc7UufAjvpheBqwyZCOIPBvxCv0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "vscode-uri": "^3.0.8" + }, + "peerDependencies": { + "@volar/language-service": "~2.4.0" + }, + "peerDependenciesMeta": { + "@volar/language-service": { + "optional": true + } + } + }, + "node_modules/volar-service-yaml": { + "version": "0.0.71", + "resolved": "https://registry.npmjs.org/volar-service-yaml/-/volar-service-yaml-0.0.71.tgz", + "integrity": "sha512-qYGWGuVpUTnZGu5P/CR4KLK4aIR8RrcVnmfZ2eRcj9q/I8VZCoC5yy9FtEvfNvnDp4MU17yhdJcvpQPIqhJS2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "vscode-uri": "^3.0.8", + "yaml-language-server": "~1.23.0" + }, + "peerDependencies": { + "@volar/language-service": "~2.4.0" + }, + "peerDependenciesMeta": { + "@volar/language-service": { + "optional": true + } + } + }, + "node_modules/vscode-css-languageservice": { + "version": "6.3.10", + "resolved": "https://registry.npmjs.org/vscode-css-languageservice/-/vscode-css-languageservice-6.3.10.tgz", + "integrity": "sha512-eq5N9Er3fC4vA9zd9EFhyBG90wtCCuXgRSpAndaOgXMh1Wgep5lBgRIeDgjZBW9pa+332yC9+49cZMW8jcL3MA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vscode/l10n": "^0.0.18", + "vscode-languageserver-textdocument": "^1.0.12", + "vscode-languageserver-types": "3.17.5", + "vscode-uri": "^3.1.0" + } + }, + "node_modules/vscode-css-languageservice/node_modules/vscode-languageserver-types": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", + "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==", + "dev": true, + "license": "MIT" + }, + "node_modules/vscode-html-languageservice": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/vscode-html-languageservice/-/vscode-html-languageservice-5.6.2.tgz", + "integrity": "sha512-ulCrSnFnfQ16YzvwnYUgEbUEl/ZG7u2eV27YhvLObSHKkb8fw1Z9cgsnUwjTEeDIdJDoTDTDpxuhQwoenoLNMg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vscode/l10n": "^0.0.18", + "vscode-languageserver-textdocument": "^1.0.12", + "vscode-languageserver-types": "^3.17.5", + "vscode-uri": "^3.1.0" + } + }, + "node_modules/vscode-json-languageservice": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/vscode-json-languageservice/-/vscode-json-languageservice-4.1.8.tgz", + "integrity": "sha512-0vSpg6Xd9hfV+eZAaYN63xVVMOTmJ4GgHxXnkLCh+9RsQBkWKIghzLhW2B9ebfG+LQQg8uLtsQ2aUKjTgE+QOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "jsonc-parser": "^3.0.0", + "vscode-languageserver-textdocument": "^1.0.1", + "vscode-languageserver-types": "^3.16.0", + "vscode-nls": "^5.0.0", + "vscode-uri": "^3.0.2" + }, + "engines": { + "npm": ">=7.0.0" + } + }, + "node_modules/vscode-jsonrpc": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-9.0.3.tgz", + "integrity": "sha512-qETYf9PCl1lVfwR+oBmMfc4vP2nqLNA35ABxorw3j06+xlKRgDVxbLgKm4Y/xPL0YWdeDjLoI4ImICIT3nB0zQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/vscode-languageserver": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-9.0.1.tgz", + "integrity": "sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "vscode-languageserver-protocol": "3.17.5" + }, + "bin": { + "installServerIntoExtension": "bin/installServerIntoExtension" + } + }, + "node_modules/vscode-languageserver-protocol": { + "version": "3.18.4", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.18.4.tgz", + "integrity": "sha512-CCXbyr99dafRJzJH/0Ryk7ly5T8XPmSR0//OuF2w4m+9JiOin7zcAT5iET8GRE7Z9taFBejb3GbM0a0mbjdbrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "vscode-jsonrpc": "9.0.3", + "vscode-languageserver-types": "3.18.4" + } + }, + "node_modules/vscode-languageserver-textdocument": { + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.15.tgz", + "integrity": "sha512-Gsfr9S8LU3S6885cS+NaleasJ0n3V5y/SOTqHCifQiXWFw3G9w9/YItDfwHrlCz+dSZWWZ8ilYVMcJe7Sz7Sxw==", + "dev": true, + "license": "MIT" + }, + "node_modules/vscode-languageserver-types": { + "version": "3.18.4", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.18.4.tgz", + "integrity": "sha512-nQsacoiijvl0fRf2q28uFDrAVwRhdCa3QveiF7/CX0r18mLR5FU/1WskSsCKOjmEbtteKUnY8x8Yzxop+k0zwg==", + "dev": true, + "license": "MIT" + }, + "node_modules/vscode-languageserver/node_modules/vscode-jsonrpc": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", + "integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/vscode-languageserver/node_modules/vscode-languageserver-protocol": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz", + "integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==", + "dev": true, + "license": "MIT", + "dependencies": { + "vscode-jsonrpc": "8.2.0", + "vscode-languageserver-types": "3.17.5" + } + }, + "node_modules/vscode-languageserver/node_modules/vscode-languageserver-types": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", + "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==", + "dev": true, + "license": "MIT" + }, + "node_modules/vscode-nls": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-5.2.0.tgz", + "integrity": "sha512-RAaHx7B14ZU04EU31pT+rKz2/zSl7xMsfIZuo8pd+KZO6PXtQmpevpq3vxvWNcrGbdmhM/rr5Uw5Mz+NBfhVng==", + "dev": true, + "license": "MIT" + }, + "node_modules/vscode-uri": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.2.0.tgz", + "integrity": "sha512-m2gXo3bn0G1kT9InzMf07fTbqMbGtyckj3bH5ktLO+1Ssv+yiATZ4dhwaQv9UZWxJh6E9IFGnQyjgWVDWVBDrg==", + "dev": true, + "license": "MIT" + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -2832,12 +6384,64 @@ "node": ">=20.11" } }, + "node_modules/wrap-ansi": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "license": "ISC" }, + "node_modules/xxhash-wasm": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/xxhash-wasm/-/xxhash-wasm-1.1.0.tgz", + "integrity": "sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA==", + "license": "MIT" + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, "node_modules/yaml": { "version": "2.9.1", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.1.tgz", @@ -2853,6 +6457,92 @@ "url": "https://github.com/sponsors/eemeli" } }, + "node_modules/yaml-language-server": { + "version": "1.23.0", + "resolved": "https://registry.npmjs.org/yaml-language-server/-/yaml-language-server-1.23.0.tgz", + "integrity": "sha512-3qVyCOexLCWw06PQa5kRPwvMWMZ/eZeCRWUvgD6a0OkqL/4iCnxy2WumbWifa937Uo5xhyWJ0uxlU39ljhNh7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vscode/l10n": "^0.0.18", + "ajv": "^8.17.1", + "ajv-draft-04": "^1.0.0", + "ajv-i18n": "^4.2.0", + "prettier": "^3.8.1", + "request-light": "^0.5.7", + "vscode-json-languageservice": "4.1.8", + "vscode-languageserver": "^9.0.0", + "vscode-languageserver-textdocument": "^1.0.1", + "vscode-languageserver-types": "^3.16.0", + "vscode-uri": "^3.0.2", + "yaml": "2.8.3" + }, + "bin": { + "yaml-language-server": "bin/yaml-language-server" + } + }, + "node_modules/yaml-language-server/node_modules/request-light": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/request-light/-/request-light-0.5.8.tgz", + "integrity": "sha512-3Zjgh+8b5fhRJBQZoy+zbVKpAQGLyka0MPgW3zruTF4dFFJ8Fqcfu9YsAvi/rvdcaTeWG3MkbZv4WKxAn/84Lg==", + "dev": true, + "license": "MIT" + }, + "node_modules/yaml-language-server/node_modules/yaml": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz", + "integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, + "node_modules/yargs": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.2.0.tgz", + "integrity": "sha512-9OpKOLeaoNFecEp7P6iYbzze/5CqWoH7N3SMs/6y1XF4nMvFMspEGZzJ6uFi9MmQwXhyzqYoSEKO3tvA3Z/o2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^9.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "string-width": "^8.2.1", + "y18n": "^5.0.5", + "yargs-parser": "^22.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, + "node_modules/yargs-parser": { + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz", + "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==", + "license": "ISC", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, + "node_modules/yocto-queue": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.2.tgz", + "integrity": "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==", + "license": "MIT", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/zod": { "version": "4.6.6", "resolved": "https://registry.npmjs.org/zod/-/zod-4.6.6.tgz", @@ -2871,6 +6561,16 @@ "zod": "^3.25.28 || ^4" } }, + "node_modules/zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "packages/schema": { "name": "@gk/schema", "version": "0.1.0", @@ -2879,6 +6579,17 @@ "zod": "^4.1.0" } }, + "packages/ui": { + "name": "@gk/ui", + "version": "0.1.0", + "dependencies": { + "astro": "7.3.5" + }, + "devDependencies": { + "@astrojs/check": "0.9.10", + "typescript": "^5.9.0" + } + }, "services/admin": { "name": "@gk/admin", "version": "0.1.0", diff --git a/package.json b/package.json index 295eaa8..ffd0059 100644 --- a/package.json +++ b/package.json @@ -12,14 +12,16 @@ "node": ">=22" }, "scripts": { - "typecheck": "tsc -p packages/schema && tsc -p tools/migrate && tsc -p services/content && tsc -p services/admin", + "typecheck": "tsc -p packages/schema && tsc -p tools/migrate && tsc -p services/content && tsc -p services/admin && npm run check -w @gk/ui", "test": "vitest run", "check": "npm run typecheck && npm test", "schema:json": "npm run json -w @gk/schema", "templates": "npm run templates -w @gk/schema", "migrate": "npm run migrate -w @gk/migrate --", "content": "npm run start -w @gk/content", - "admin": "npm run start -w @gk/admin" + "admin": "npm run start -w @gk/admin", + "ui": "npm run dev -w @gk/ui", + "ui:build": "npm run build -w @gk/ui" }, "devDependencies": { "@types/node": "^22.10.0", diff --git a/packages/ui/astro.config.mjs b/packages/ui/astro.config.mjs new file mode 100644 index 0000000..a794175 --- /dev/null +++ b/packages/ui/astro.config.mjs @@ -0,0 +1,12 @@ +// The gallery: one static page that shows every component, for review +// against the design canvas. The site itself imports the components. +import { defineConfig } from "astro/config"; + +export default defineConfig({ + output: "static", + build: { format: "file", assets: "assets" }, + devToolbar: { enabled: false }, + // Bundle astro's own cookie (2.x) into the prerender step; left external it + // resolves to the older copy express hoists to the workspace root. + vite: { environments: { prerender: { resolve: { noExternal: ["cookie"] } } } }, +}); diff --git a/packages/ui/package.json b/packages/ui/package.json new file mode 100644 index 0000000..34c39d1 --- /dev/null +++ b/packages/ui/package.json @@ -0,0 +1,25 @@ +{ + "name": "@gk/ui", + "version": "0.1.0", + "private": true, + "type": "module", + "description": "The GregKilmer.com component library: the approved element rules and the ornament kit as Astro components, plus a gallery page to review them.", + "exports": { + "./components/*": "./src/components/*", + "./styles/*": "./src/styles/*", + "./lib/*": "./src/lib/*", + "./assets/*": "./src/assets/*" + }, + "scripts": { + "dev": "astro dev", + "build": "astro build", + "check": "astro check" + }, + "dependencies": { + "astro": "7.3.5" + }, + "devDependencies": { + "@astrojs/check": "0.9.10", + "typescript": "^5.9.0" + } +} diff --git a/packages/ui/src/assets/brand/gk-monogram.svg b/packages/ui/src/assets/brand/gk-monogram.svg new file mode 100644 index 0000000..dba06ad --- /dev/null +++ b/packages/ui/src/assets/brand/gk-monogram.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/packages/ui/src/assets/brand/patiun-glyph-gold.svg b/packages/ui/src/assets/brand/patiun-glyph-gold.svg new file mode 100644 index 0000000..37360af --- /dev/null +++ b/packages/ui/src/assets/brand/patiun-glyph-gold.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/packages/ui/src/assets/brand/patiun-glyph-teal.svg b/packages/ui/src/assets/brand/patiun-glyph-teal.svg new file mode 100644 index 0000000..2d9be7b --- /dev/null +++ b/packages/ui/src/assets/brand/patiun-glyph-teal.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/packages/ui/src/assets/fonts/Cinzel-Bold.woff2 b/packages/ui/src/assets/fonts/Cinzel-Bold.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..dfcf8400e6a54f1baa61ac2d38d58fd14e1efee8 GIT binary patch literal 20932 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%0D@j-SfOU#>k{L}PL`HF`Y|Pa4F^>QQ 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 PiCXQaGCuDiECB!jp(Men literal 0 HcmV?d00001 diff --git a/packages/ui/src/assets/fonts/Cinzel-SemiBold.woff2 b/packages/ui/src/assets/fonts/Cinzel-SemiBold.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..b8d785c88e60aad17c516c8d121c8ba1a405a62b GIT binary patch literal 20984 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 literal 0 HcmV?d00001 diff --git a/packages/ui/src/assets/fonts/LICENSE.md b/packages/ui/src/assets/fonts/LICENSE.md new file mode 100644 index 0000000..adc5b00 --- /dev/null +++ b/packages/ui/src/assets/fonts/LICENSE.md @@ -0,0 +1,10 @@ +# Fonts + +Subset to Latin and served as WOFF2 from the static TTFs in the brand source +(`branding/source/patiun-brand-source.zip`, `fonts/static`). + +- **Cinzel**, copyright 2012 The Cinzel Project Authors (Natanael Gama). +- **Montserrat**, copyright 2011 The Montserrat Project Authors. + +Both are licensed under the SIL Open Font License, Version 1.1: +https://openfontlicense.org/open-font-license-official-text/ diff --git a/packages/ui/src/assets/fonts/Montserrat-Bold.woff2 b/packages/ui/src/assets/fonts/Montserrat-Bold.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..da978ab7bf2bdd41ad24ca056eb002dbf8aa2699 GIT binary patch literal 26904 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 + + diff --git a/packages/ui/src/components/Divider.astro b/packages/ui/src/components/Divider.astro new file mode 100644 index 0000000..bfb82ac --- /dev/null +++ b/packages/ui/src/components/Divider.astro @@ -0,0 +1,36 @@ +--- +/** + * The header divider, Greg's favorite: a gold line broken by a large gold + * center star, gold stars at the ends, and a star path riding the line on + * both sides. The footer uses the same divider. + */ +import { seedOf, starPath, starPoints } from "../lib/ornaments.ts"; + +interface Props { + seed?: string; + class?: string; +} +const { seed = Astro.url.pathname, class: className } = Astro.props; +const W = 1200; +const y = 20; +const s = seedOf(seed); +const left = starPath({ x1: 540, x2: 76, y, steps: 8, seed: s, openEnds: false, dot: 2, star: 4.5 }); +const right = starPath({ x1: 660, x2: 1124, y, steps: 8, seed: s + 1, openEnds: false, dot: 2, star: 4.5 }); +--- + diff --git a/packages/ui/src/components/EdgeSeals.astro b/packages/ui/src/components/EdgeSeals.astro new file mode 100644 index 0000000..0482078 --- /dev/null +++ b/packages/ui/src/components/EdgeSeals.astro @@ -0,0 +1,65 @@ +--- +/** + * One or two seals per page, cropped by the page edge at 5 to 8% opacity, on + * alternating sides. Never more than two, and a section with a photo gets no + * seal: place this in the page background, not in a photo section. + */ +import { pageSeals, seals, type PageFamily, type SealName } from "./seals.ts"; + +interface Props { + family?: PageFamily; + /** Override the family's seal, as a craft page does with its own. */ + seal?: SealName; + /** The second seal; defaults to the first. Pass false for just one. */ + second?: SealName | false; +} +const { family = "landing", seal, second } = Astro.props; +const first = seal ?? pageSeals[family]; +const other = second === false ? null : (second ?? first); +--- + + + diff --git a/packages/ui/src/components/Field.astro b/packages/ui/src/components/Field.astro new file mode 100644 index 0000000..67caac7 --- /dev/null +++ b/packages/ui/src/components/Field.astro @@ -0,0 +1,44 @@ +--- +/** + * Rule V. A labelled field: input, select or text area in the 10px box, with + * the filament drawn across its base on focus. + */ +import type { HTMLAttributes } from "astro/types"; + +type Props = { + label: string; + name: string; + as?: "input" | "select" | "textarea"; + hint?: string; + error?: string; + options?: (string | { value: string; label: string })[]; + /** Rows for a text area. */ + rows?: number; + class?: string; +} & HTMLAttributes<"input">; + +const { label, name, as = "input", hint, error, options = [], rows, class: className, id = `f-${name}`, ...rest } = Astro.props; +const describedBy = [hint && `${id}-hint`, error && `${id}-error`].filter(Boolean).join(" ") || undefined; +const common = { id, name, class: "gk-field", "aria-describedby": describedBy, "aria-invalid": error ? ("true" as const) : undefined }; +--- +
+ + {as === "select" ? ( + + ) : as === "textarea" ? ( + + For cards, paste your full card list, one per line. + {err.details && {err.details}} +
+
+ + + {err.notes && {err.notes}} +
{ (lines.some((l) => l.data.terms?.length) || storeTerms.length > 0) && ( @@ -102,9 +111,18 @@ const slugPart = (x: string) => x.toLowerCase().replace(/[^a-z0-9]+/g, "-"); I have read and accept the order terms for this request. + {err.terms && {err.terms}} + + + + {turnstileKey &&
} + {turnstileKey && }
+ {turnstileKey && Protected by Cloudflare Turnstile.}
@@ -280,6 +298,22 @@ const slugPart = (x: string) => x.toLowerCase().replace(/[^a-z0-9]+/g, "-"); align-items: center; gap: 12px; } + .err { + font-size: 12px; + font-weight: 400; + color: #f0857a; + } + .tnote { + font-size: 13px; + color: var(--gk-muted); + } + .hp { + position: absolute; + left: -10000px; + width: 1px; + height: 1px; + overflow: hidden; + } @media (max-width: 640px) { .order { padding: 22px 16px; diff --git a/services/site/src/lib/content.ts b/services/site/src/lib/content.ts index 42c7f24..4ad6b07 100644 --- a/services/site/src/lib/content.ts +++ b/services/site/src/lib/content.ts @@ -20,6 +20,24 @@ export interface Note { data: NoteData; body: string; acceptedAt: string; + /** + * What the content service's sync jobs fetched for this note (BoardGameGeek, + * Archidekt, cover art). Already merged under `data` by `withSynced`, so + * pages read `data`; kept here so a page can tell a synced value apart. + */ + synced?: Record; +} + +/** + * Lay the synced values under the note's own: a value set in the note always + * wins, and a note with nothing synced is returned as it came. + */ +export function withSynced(note: T): T { + const synced = note.synced; + if (!synced || !Object.keys(synced).length) return note; + const data: Record = { ...synced }; + for (const [k, v] of Object.entries(note.data as Record)) if (v !== undefined && v !== null) data[k] = v; + return { ...note, data: data as T["data"] }; } export interface Redirect { @@ -48,7 +66,7 @@ async function loadAll(): Promise { for (let offset = 0; ; offset += 1000) { const published = includeDrafts() ? "" : "&published=true"; const page = await api<(Note & { publish?: boolean })[]>(`/api/notes?full=true${published}&limit=1000&offset=${offset}`); - notes.push(...page); + notes.push(...page.map(withSynced)); if (page.length < 1000) break; } const redirects = await api("/api/redirects").catch(() => []); diff --git a/services/site/src/lib/forms-server.ts b/services/site/src/lib/forms-server.ts new file mode 100644 index 0000000..65765f8 --- /dev/null +++ b/services/site/src/lib/forms-server.ts @@ -0,0 +1,155 @@ +/** + * What the form endpoints do outside the process: Turnstile, the content + * service (live store notes, saving the copy, recording delivery) and SMTP. + * Settings come from process.env through env(); see services/site/.env.example. + */ +import type { Note } from "./content.ts"; +import { env } from "./env.ts"; +import { DEFAULT_ATTACHMENT_MB, RateLimiter, type Row, type Upload } from "./forms.ts"; +import { SiteIndex } from "./site.ts"; +import { storeState, type StoreState } from "./store.ts"; + +const contentUrl = () => env("CONTENT_URL", "http://localhost:4310").replace(/\/$/, ""); + +export const maxAttachmentBytes = () => Math.round(Number(env("FORM_MAX_ATTACHMENT_MB", String(DEFAULT_ATTACHMENT_MB))) * 1024 * 1024); + +export const inquiryLimiter = new RateLimiter( + () => Number(env("CONTACT_RATE_LIMIT", "5")), + () => Number(env("CONTACT_RATE_WINDOW_MIN", "15")) * 60_000, +); +export const orderLimiter = new RateLimiter( + () => Number(env("ORDER_RATE_LIMIT", env("CONTACT_RATE_LIMIT", "5"))), + () => Number(env("ORDER_RATE_WINDOW_MIN", env("CONTACT_RATE_WINDOW_MIN", "15"))) * 60_000, +); + +/** The public site key, for the widget. */ +export const turnstileSiteKey = () => env("PUBLIC_TURNSTILE_SITE_KEY") ?? env("TURNSTILE_SITE_KEY"); + +/** + * Cloudflare Turnstile. Skipped when TURNSTILE_SECRET_KEY is unset (local + * development); when it is set, a missing token, a bad token or an error + * talking to Cloudflare all turn the post down. + */ +export async function verifyTurnstile(token: string, ip: string, fetcher: typeof fetch = fetch): Promise { + const secret = env("TURNSTILE_SECRET_KEY"); + if (!secret) return true; + if (!token) return false; + try { + const body = new URLSearchParams({ secret, response: token }); + if (ip && ip !== "unknown") body.set("remoteip", ip); + const res = await fetcher("https://challenges.cloudflare.com/turnstile/v0/siteverify", { + method: "POST", + body, + signal: AbortSignal.timeout(8000), + }); + const data = (await res.json()) as { success?: boolean }; + return data.success === true; + } catch (err) { + console.error("Turnstile verification failed; turning the post down.", err); + return false; + } +} + +/** + * The store's notes as they are right now, straight from the content + * service rather than the site's cached copy, so an order is checked against + * what Greg last saved. Throws when the service cannot answer. + */ +export async function liveStore(): Promise { + const get = async (type: string) => { + const res = await fetch(`${contentUrl()}/api/notes?type=${type}&full=true&published=true&limit=1000`, { + headers: { authorization: `Bearer ${env("CONTENT_TOKEN", "")}` }, + signal: AbortSignal.timeout(8000), + }); + if (!res.ok) throw new Error(`Content service answered ${res.status} for ${type}`); + return (await res.json()) as Note[]; + }; + const [settings, lines] = await Promise.all([get("store-settings"), get("product-line")]); + return storeState(new SiteIndex([...settings, ...lines])); +} + +export interface SubmissionInput { + kind: "inquiry" | "order"; + type: string; + name: string; + email: string; + fields: Row[]; + attachment?: Upload | null; +} + +const intake = async (path: string, body: unknown) => { + const token = env("CONTENT_INTAKE_TOKEN"); + if (!token) throw new Error("CONTENT_INTAKE_TOKEN is not set"); + const res = await fetch(`${contentUrl()}${path}`, { + method: "POST", + headers: { authorization: `Bearer ${token}`, "content-type": "application/json" }, + body: JSON.stringify(body), + signal: AbortSignal.timeout(20_000), + }); + const data = (await res.json().catch(() => null)) as Record | null; + if (!res.ok) throw new Error(`Content service answered ${res.status}: ${String(data?.error ?? "")}`); + return data; +}; + +/** Save the copy with the intake token. Returns the id and the receipt for reporting delivery. */ +export async function saveSubmission(s: SubmissionInput): Promise<{ id: number; receipt: string }> { + const attachment = s.attachment ? { name: s.attachment.name, data: Buffer.from(await s.attachment.arrayBuffer()).toString("base64") } : undefined; + const out = (await intake("/api/submissions", { ...s, attachment })) as { id: number; receipt: string }; + return { id: out.id, receipt: out.receipt }; +} + +export async function reportDelivery(id: number, receipt: string, status: "sent" | "skipped" | "failed", error?: string) { + await intake(`/api/submissions/${id}/delivery`, { receipt, status, ...(error ? { error } : {}) }); +} + +export interface Mail { + to: string; + replyTo: { name: string; address: string }; + subject: string; + text: string; + attachment?: Upload | null; +} + +/** + * Send through SMTP (nodemailer). "skipped" when SMTP_HOST is unset, so a + * site without mail still saves copies. Throws when sending fails. + */ +export async function sendMail(m: Mail): Promise<"sent" | "skipped"> { + const host = env("SMTP_HOST"); + if (!host) return "skipped"; + const nodemailer = (await import("nodemailer")).default; + const port = Number(env("SMTP_PORT", "587")); + const user = env("SMTP_USER"); + const transport = nodemailer.createTransport({ + host, + port, + secure: port === 465, + auth: user ? { user, pass: env("SMTP_PASS", "") } : undefined, + // Only for a relay on the same private network that presents a self-signed certificate. + ...(env("SMTP_ALLOW_SELFSIGNED") === "true" ? { tls: { rejectUnauthorized: false } } : {}), + connectionTimeout: 10_000, + greetingTimeout: 10_000, + socketTimeout: 20_000, + }); + await transport.sendMail({ + from: env("SMTP_FROM", "GregKilmer.com "), + to: m.to, + replyTo: m.replyTo, + subject: m.subject.replace(/[\r\n]+/g, " "), + text: m.text, + attachments: m.attachment ? [{ filename: m.attachment.name, content: Buffer.from(await m.attachment.arrayBuffer()) }] : undefined, + }); + return "sent"; +} + +export const contactTo = () => env("CONTACT_TO", "contact@gregkilmer.com"); +export const orderTo = () => env("ORDER_TO", "orders@gregkilmer.com"); + +/** Short-lived cookie settings for the confirmation and try-again cookies. */ +export const flashCookie = (url: URL) => ({ + path: "/", + httpOnly: true, + sameSite: "lax" as const, + secure: import.meta.env.PROD || url.protocol === "https:", + maxAge: 600, +}); diff --git a/services/site/src/lib/forms.ts b/services/site/src/lib/forms.ts new file mode 100644 index 0000000..5335ba9 --- /dev/null +++ b/services/site/src/lib/forms.ts @@ -0,0 +1,476 @@ +/** + * The inquiry and order forms' rules, as plain functions: what a valid post + * looks like, what an order may be accepted for, what the confirmation and + * the "try again" pages are handed, and how saving and emailing combine. + * Nothing here touches the network, so tests run it without Astro. + */ +import type { InquiryType, SentSummary } from "./work.ts"; +import { parseInquiryType } from "./work.ts"; +import { channelOf, hasCapacity, optionGroups, type Line, type StoreState } from "./store.ts"; + +export interface Row { + label: string; + value: string; +} + +/** What a form post gives: FormData, or anything shaped like it in tests. */ +export interface FormLike { + get(name: string): FormDataEntryValue | null; +} + +export type FieldErrors = Record; + +export type Invalid = { ok: false; code: ErrorCode; errors: FieldErrors }; + +export type ErrorCode = "invalid" | "bot" | "rate" | "closed" | "full" | "unavailable" | "failed"; + +/** What the visitor reads for each kind of failure. Nothing here leaks internals. */ +export const errorMessages: Record = { + invalid: "Some answers need another look. Nothing was sent yet.", + bot: "The bot check did not go through. Please try again.", + rate: "Too many requests from here in a short time. Please try again a little later.", + closed: "Orders for that are closed right now, so nothing was sent.", + full: "That line has no open slots right now, so nothing was sent.", + unavailable: "The store could not confirm availability just now. Please try again in a few minutes.", + failed: "Your request could not be sent just now. Please try again a little later.", +}; + +export const parseErrorCode = (raw: string | null | undefined): ErrorCode | undefined => + raw && raw in errorMessages ? (raw as ErrorCode) : undefined; + +const text = (form: FormLike, key: string): string => { + const v = form.get(key); + return typeof v === "string" ? v.replace(/\r\n/g, "\n").trim() : ""; +}; + +const EMAIL = /^[^\s@<>()[\]\\,;:"]+@[^\s@<>()[\]\\,;:"]+\.[^\s@<>()[\]\\,;:"]{2,}$/; +export const isEmail = (s: string) => s.length <= 200 && EMAIL.test(s); + +export const isHttpUrl = (s: string) => { + try { + const u = new URL(s); + return (u.protocol === "http:" || u.protocol === "https:") && !!u.hostname; + } catch { + return false; + } +}; + +const isDate = (s: string) => /^\d{4}-\d{2}-\d{2}$/.test(s) && !Number.isNaN(Date.parse(`${s}T00:00:00Z`)); + +/** The hidden field bots fill and people never see. */ +export const HONEYPOT = "leave_blank"; + +export const isBot = (form: FormLike) => text(form, HONEYPOT) !== ""; + +/** The Turnstile widget adds this field to the form it sits in. */ +export const TURNSTILE_FIELD = "cf-turnstile-response"; + +// ---------------------------------------------------------------- attachments + +/** Matches the content service's list (services/content/src/submissions.ts). */ +export const ATTACHMENT_EXTENSIONS = ["png", "jpg", "jpeg", "gif", "webp", "avif", "heic", "pdf", "txt", "csv", "dek", "dec"]; + +export const DEFAULT_ATTACHMENT_MB = 10; + +export interface Upload { + name: string; + size: number; + type: string; + arrayBuffer(): Promise; +} + +const isUpload = (v: unknown): v is Upload => + typeof v === "object" && v !== null && typeof (v as Upload).arrayBuffer === "function" && typeof (v as Upload).size === "number"; + +/** The attached file, or null for none; an error message when it cannot be taken. */ +export function checkAttachment(form: FormLike, key: string, maxBytes: number): { file: Upload | null; error?: string } { + const v = form.get(key); + if (!isUpload(v) || v.size === 0) return { file: null }; + const ext = /\.([A-Za-z0-9]{1,8})$/.exec(v.name)?.[1]?.toLowerCase(); + if (!ext || !ATTACHMENT_EXTENSIONS.includes(ext)) { + return { file: null, error: "Attach an image, a PDF, or a text, CSV or deck list file." }; + } + if (v.size > maxBytes) return { file: null, error: `Attachments can be up to ${Math.floor(maxBytes / 1024 / 1024)} MB.` }; + return { file: v }; +} + +// ---------------------------------------------------------------- inquiries + +export const choices = { + job_kind: ["Full-time", "Contract", "Not sure yet"], + freelance_need: ["A new site", "An app", "An integration", "Something else"], + patiun_item: ["Proxy cards", "Deck box", "Something else"], +} as const; + +export const timing = [ + { value: "no-rush", label: "No rush" }, + { value: "within-a-month", label: "Within a month" }, + { value: "date", label: "A specific date" }, +]; + +type Question = + | { key: string; label: string; kind: "text"; max: number } + | { key: string; label: string; kind: "url" } + | { key: string; label: string; kind: "choice"; options: readonly string[] } + | { key: string; label: string; kind: "timing"; dateKey: string }; + +/** Each type's own questions, in form order. Fields of other types are ignored. */ +export const questions: Record = { + job: [ + { key: "job_company", label: "Company", kind: "text", max: 160 }, + { key: "job_role", label: "Role", kind: "text", max: 160 }, + { key: "job_kind", label: "Kind of role", kind: "choice", options: choices.job_kind }, + { key: "job_link", label: "Posting", kind: "url" }, + ], + freelance: [ + { key: "freelance_need", label: "Needs", kind: "choice", options: choices.freelance_need }, + { key: "freelance_site", label: "Current site", kind: "url" }, + { key: "freelance_timing", label: "Timeline", kind: "timing", dateKey: "freelance_date" }, + ], + patiun: [ + { key: "patiun_item", label: "Item", kind: "choice", options: choices.patiun_item }, + { key: "patiun_quantity", label: "How many", kind: "text", max: 120 }, + { key: "patiun_timing", label: "Need it by", kind: "timing", dateKey: "patiun_date" }, + ], +}; + +export interface Inquiry { + type: InquiryType; + name: string; + email: string; + details: string; + /** The chosen type's answers, in form order, empty ones left out. */ + rows: Row[]; + attachment: Upload | null; +} + +const formatDay = (d: string) => new Date(`${d}T00:00:00Z`).toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric", timeZone: "UTC" }); + +export function validateInquiry(form: FormLike, opts: { maxBytes: number }): { ok: true; value: Inquiry } | Invalid { + const errors: FieldErrors = {}; + const type = parseInquiryType(text(form, "type")); + if (!type) errors.type = "Pick what this is about."; + const name = text(form, "name"); + if (!name) errors.name = "Add your name."; + else if (name.length > 120) errors.name = "Keep your name under 120 characters."; + const email = text(form, "email"); + if (!isEmail(email)) errors.email = "Add an email address I can reply to."; + const details = text(form, "details"); + if (details.length < 10) errors.details = "Tell me a little more: a sentence or two is plenty."; + else if (details.length > 5000) errors.details = "Keep the details under 5,000 characters, or attach a file."; + + const rows: Row[] = []; + for (const q of type ? questions[type] : []) { + const v = text(form, q.key); + if (!v) continue; + if (q.kind === "text") { + if (v.length > q.max) errors[q.key] = `Keep this under ${q.max} characters.`; + else rows.push({ label: q.label, value: v }); + } else if (q.kind === "url") { + if (v.length > 500 || !isHttpUrl(v)) errors[q.key] = "Use a full link starting with https://."; + else rows.push({ label: q.label, value: v }); + } else if (q.kind === "choice") { + if (!q.options.includes(v)) errors[q.key] = "Pick one of the options."; + else rows.push({ label: q.label, value: v }); + } else { + const t = timing.find((x) => x.value === v); + if (!t) errors[q.key] = "Pick one of the options."; + else if (t.value === "date") { + const d = text(form, q.dateKey); + if (!isDate(d)) errors[q.dateKey] = "Pick the date."; + else rows.push({ label: q.label, value: formatDay(d) }); + } else rows.push({ label: q.label, value: t.label }); + } + } + + const att = checkAttachment(form, "attachment", opts.maxBytes); + if (att.error) errors.attachment = att.error; + + if (Object.keys(errors).length || !type) return { ok: false, code: "invalid", errors }; + return { ok: true, value: { type, name, email, details, rows, attachment: att.file } }; +} + +const inquiryTypeLabels: Record = { job: "A job or role", freelance: "Freelance web work", patiun: "Patiun custom order" }; + +/** Everything the saved copy and the email carry, in order. */ +export const inquiryFields = (q: Inquiry): Row[] => [...q.rows, { label: "Details", value: q.details }]; + +export function inquiryEmail(q: Inquiry, savedId: number | null): { subject: string; text: string } { + return { + subject: `Inquiry: ${inquiryTypeLabels[q.type]} from ${q.name}`, + text: [ + `New inquiry through gregkilmer.com`, + ``, + `Type: ${inquiryTypeLabels[q.type]}`, + `Name: ${q.name}`, + `Email: ${q.email}`, + ...q.rows.map((r) => `${r.label}: ${r.value}`), + ``, + `Details:`, + q.details, + ``, + q.attachment ? `Attached: ${q.attachment.name}` : "", + savedId ? `Saved in the admin inbox as #${savedId}.` : `Not saved in the admin inbox: the content service did not take it. This email is the only copy.`, + ].join("\n"), + }; +} + +/** The `gk-inquiry` cookie the sent page reads: what the visitor sent, briefly. */ +export function inquirySent(q: Inquiry): SentSummary { + const short = (v: string) => (v.length > 120 ? `${v.slice(0, 119)}…` : v); + return { + type: q.type, + name: q.name, + email: q.email, + rows: [...q.rows.map((r) => ({ label: r.label, value: short(r.value) })), ...(q.attachment ? [{ label: "Attachment", value: short(q.attachment.name) }] : [])].slice(0, 6), + }; +} + +// ---------------------------------------------------------------- orders + +export interface OrderRequest { + name: string; + email: string; + line: string; + quantity: string; + details: string; + notes: string; +} + +/** Check the fields that do not depend on the store. */ +export function validateOrderFields(form: FormLike): { ok: true; value: OrderRequest } | Invalid { + const errors: FieldErrors = {}; + const name = text(form, "name"); + if (!name) errors.name = "Add your name."; + else if (name.length > 120) errors.name = "Keep your name under 120 characters."; + const email = text(form, "email"); + if (!isEmail(email)) errors.email = "Add an email address I can reply to."; + const line = text(form, "line"); + if (!/^[a-z0-9-]{1,120}$/.test(line)) errors.line = "Pick what you would like."; + const quantity = text(form, "quantity"); + if (!quantity) errors.quantity = "Give a rough quantity."; + else if (quantity.length > 200) errors.quantity = "Keep this under 200 characters."; + const details = text(form, "details"); + if (details.length > 20_000) errors.details = "Keep this under 20,000 characters."; + const notes = text(form, "notes"); + if (notes.length > 5000) errors.notes = "Keep notes under 5,000 characters."; + if (text(form, "terms") !== "accepted") errors.terms = "Read and accept the order terms to send a request."; + if (Object.keys(errors).length) return { ok: false, code: "invalid", errors }; + return { ok: true, value: { name, email, line, quantity, details, notes } }; +} + +export interface CheckedOrder extends OrderRequest { + lineNote: Line; + /** The chosen options, as { label: group, value: option }. */ + options: Row[]; +} + +/** + * Whether the store takes this order now, against the live notes. An order + * is accepted only for a line sold here, in a category with ordersOpen true, + * with an open slot or stock, and only with options that exist and are in + * stock. Anything else is turned down, whatever the form said. + */ +export function checkOrder(req: OrderRequest, form: FormLike, st: StoreState): { ok: true; value: CheckedOrder } | Invalid { + const line = st.lines.find((l) => l.slug === req.line); + if (!line || channelOf(line) !== "here") return { ok: false, code: "invalid", errors: { line: "That is not something you can order here." } }; + if (!st.category(line.data.category).ordersOpen) return { ok: false, code: "closed", errors: {} }; + if (!hasCapacity(line)) return { ok: false, code: "full", errors: {} }; + const options: Row[] = []; + const errors: FieldErrors = {}; + for (const [group, opts] of optionGroups(line)) { + const key = `option.${line.slug}.${group}`; + const v = text(form, key); + if (!v) continue; + const o = opts.find((x) => x.name === v); + if (!o) errors[key] = `Pick one of the ${group.toLowerCase()} options.`; + else if (!o.inStock) errors[key] = `${o.name} is out of stock. Pick another.`; + else options.push({ label: group, value: o.name }); + } + if (Object.keys(errors).length) return { ok: false, code: "invalid", errors }; + return { ok: true, value: { ...req, lineNote: line, options } }; +} + +export const orderFields = (o: CheckedOrder): Row[] => [ + { label: "Line", value: o.lineNote.title }, + ...o.options, + { label: "Quantity", value: o.quantity }, + ...(o.details ? [{ label: "Details", value: o.details }] : []), + ...(o.notes ? [{ label: "Notes", value: o.notes }] : []), + { label: "Terms", value: "Accepted" }, +]; + +export function orderEmail(o: CheckedOrder, savedId: number | null): { subject: string; text: string } { + return { + subject: `Order request: ${o.lineNote.title} from ${o.name}`, + text: [ + `New order request through gregkilmer.com. No payment was taken.`, + ``, + `Name: ${o.name}`, + `Email: ${o.email}`, + ...orderFields(o).flatMap((r) => (r.value.includes("\n") ? [``, `${r.label}:`, r.value, ``] : [`${r.label}: ${r.value}`])), + ``, + savedId ? `Saved in the admin inbox as #${savedId}.` : `Not saved in the admin inbox: the content service did not take it. This email is the only copy.`, + ].join("\n"), + }; +} + +export interface OrderSent { + name: string; + email: string; + line: string; + lineSlug: string; + rows: Row[]; +} + +/** The `gk-order` cookie the order confirmation reads. */ +export function orderSent(o: CheckedOrder): OrderSent { + const short = (v: string) => (v.length > 120 ? `${v.slice(0, 119)}…` : v); + return { + name: o.name, + email: o.email, + line: o.lineNote.title, + lineSlug: o.lineNote.slug, + rows: [...o.options, { label: "Quantity", value: o.quantity }].map((r) => ({ label: r.label, value: short(r.value) })).slice(0, 6), + }; +} + +export function parseOrderSent(raw: string | undefined): OrderSent | undefined { + if (!raw) return undefined; + try { + const v = JSON.parse(raw.startsWith("%7B") || raw.startsWith("%7b") ? decodeURIComponent(raw) : raw) as Record; + const str = (x: unknown) => (typeof x === "string" && x.trim() ? x.trim().slice(0, 300) : ""); + const rows = Array.isArray(v.rows) + ? v.rows + .map((r) => ({ label: str((r as Row)?.label), value: str((r as Row)?.value) })) + .filter((r) => r.label && r.value) + .slice(0, 8) + : []; + return { name: str(v.name), email: str(v.email), line: str(v.line), lineSlug: str(v.lineSlug), rows }; + } catch { + return undefined; + } +} + +// ---------------------------------------------------------------- try again + +/** + * What goes back to the form when a post is turned down: the error, which + * fields to mark, and the visitor's answers. It travels as a short-lived + * cookie, so it has to fit: the longest answers are cut first. + */ +export interface Draft { + code: ErrorCode; + errors: FieldErrors; + values: Record; +} + +export const DRAFT_LIMIT = 3600; + +/** Every text value a form posted, files and the bot fields left out. */ +export function formValues(form: { entries(): IterableIterator<[string, FormDataEntryValue]> } | FormData): Record { + const out: Record = {}; + for (const [k, v] of (form as FormData).entries()) { + if (typeof v !== "string" || k === HONEYPOT || k === TURNSTILE_FIELD || k === "terms") continue; + if (k.length > 160) continue; + out[k] = v.slice(0, 20_000); + } + return out; +} + +export function encodeDraft(d: Draft, limit = DRAFT_LIMIT): string { + const values = { ...d.values }; + const enc = () => encodeURIComponent(JSON.stringify({ ...d, values })); + let out = enc(); + while (out.length > limit) { + const longest = Object.entries(values).sort((a, b) => b[1].length - a[1].length)[0]; + if (!longest || longest[1].length === 0) break; + const [k, v] = longest; + values[k] = v.length > 200 ? v.slice(0, Math.floor(v.length / 2)) : ""; + out = enc(); + } + return JSON.stringify({ ...d, values }); +} + +export function parseDraft(raw: string | undefined): Draft | undefined { + if (!raw) return undefined; + try { + const v = JSON.parse(raw.startsWith("%7B") || raw.startsWith("%7b") ? decodeURIComponent(raw) : raw) as Record; + const code = parseErrorCode(typeof v.code === "string" ? v.code : undefined); + if (!code) return undefined; + const strings = (x: unknown) => + Object.fromEntries(Object.entries(typeof x === "object" && x ? x : {}).filter((e): e is [string, string] => typeof e[1] === "string")); + return { code, errors: strings(v.errors), values: strings(v.values) }; + } catch { + return undefined; + } +} + +// ---------------------------------------------------------------- send + +export type EmailResult = "sent" | "skipped" | "failed"; + +/** + * Save the copy, then email, then record how the email went. The visitor + * hears it worked when either the saved copy or the email made it; when + * neither did, the form shows an error. + */ +export async function saveAndSend(deps: { + save(): Promise<{ id: number; receipt: string }>; + email(savedId: number | null): Promise<"sent" | "skipped">; + report(id: number, receipt: string, status: EmailResult, error?: string): Promise; + log?(message: string, err: unknown): void; +}): Promise<{ ok: boolean; savedId: number | null; email: EmailResult }> { + const log = deps.log ?? ((m, e) => console.error(m, e)); + let saved: { id: number; receipt: string } | null = null; + try { + saved = await deps.save(); + } catch (err) { + log("Saving the copy failed; trying the email anyway.", err); + } + let email: EmailResult; + let error: string | undefined; + try { + email = await deps.email(saved?.id ?? null); + } catch (err) { + email = "failed"; + error = err instanceof Error ? err.message.slice(0, 500) : String(err).slice(0, 500); + log("Sending the email failed.", err); + } + if (saved) { + await deps.report(saved.id, saved.receipt, email, error).catch((err: unknown) => log("Recording the email result failed.", err)); + } + return { ok: !!saved || email === "sent", savedId: saved?.id ?? null, email }; +} + +// ---------------------------------------------------------------- rate limit + +/** N posts per address per window, in memory. Enough for one site process. */ +export class RateLimiter { + private readonly hits = new Map(); + constructor( + private readonly limit: () => number, + private readonly windowMs: () => number, + private readonly now: () => number = Date.now, + ) {} + + /** Counts this post; true when it is over the limit. */ + hit(key: string): boolean { + const t = this.now(); + if (this.hits.size > 5000) for (const [k, e] of this.hits) if (t > e.reset) this.hits.delete(k); + const e = this.hits.get(key); + if (!e || t > e.reset) { + this.hits.set(key, { count: 1, reset: t + this.windowMs() }); + return false; + } + e.count += 1; + return e.count > this.limit(); + } +} + +/** The visitor's address: the proxy's X-Forwarded-For first, else the socket's. */ +export function clientIp(headers: Headers, fallback?: string): string { + const fwd = headers.get("x-forwarded-for"); + if (fwd) return fwd.split(",")[0]!.trim(); + return headers.get("x-real-ip") ?? fallback ?? "unknown"; +} diff --git a/services/site/src/lib/library.ts b/services/site/src/lib/library.ts index fd24c7f..91e5cd1 100644 --- a/services/site/src/lib/library.ts +++ b/services/site/src/lib/library.ts @@ -5,6 +5,7 @@ */ import type { Note } from "./content.ts"; import type { SiteIndex } from "./site.ts"; +import { formatDate } from "./site.ts"; export type Item = Note<"library-item">; export type Game = Note<"board-game">; @@ -47,6 +48,29 @@ export function byStanding(a: Item, b: Item): number { ); } +/** Progress PersonalReader syncs onto a linked item (see withSynced in content.ts). */ +interface ReaderSynced { + chapterProgress?: { chapter?: number; readAt?: string }; + bookProgress?: { percent?: number; readAt?: string }; +} + +/** "Chapter 42, read Oct 3, 2026" or "62% read", from PersonalReader; undefined when nothing is synced. */ +export function readerProgress(item: Item): string | undefined { + const d = item.data as ReaderSynced; + const c = d.chapterProgress; + const b = d.bookProgress; + const what = c?.chapter ? `Chapter ${c.chapter}` : b && typeof b.percent === "number" && b.percent > 0 ? `${b.percent}% read` : undefined; + if (!what) return undefined; + const at = (c?.chapter ? c.readAt : b?.readAt)?.slice(0, 10); + return at ? `${what}, read ${formatDate(at)}` : what; +} + +/** The row's details line. The note's own `progress` wins over the synced one. */ +export const itemDetails = (item: Item): (string | number)[] => { + const d = item.data; + return [d.creator, d.year, d.platform, d.progress || readerProgress(item), d.group].filter((x): x is string | number => x !== undefined && x !== ""); +}; + export interface LibraryFilter { type?: MediaKey; q: string; @@ -152,6 +176,31 @@ export function gameShows(g: Game, f: ShelfFilter, text: string): boolean { return !f.q || text.includes(f.q); } +/** Values BoardGameGeek syncs onto a game (see withSynced in content.ts). */ +interface GameSynced { + minPlayers?: number; + maxPlayers?: number; + playTime?: number; + minPlayTime?: number; + maxPlayTime?: number; +} + +const range = (lo?: number, hi?: number) => (lo && hi && lo !== hi ? `${lo}–${hi}` : String(lo || hi || "")); + +/** "2–4 players", "1 player" or undefined when BoardGameGeek has not been synced. */ +export function gamePlayers(g: Game): string | undefined { + const d = g.data as GameSynced; + const r = range(d.minPlayers, d.maxPlayers); + return r ? `${r} ${r === "1" ? "player" : "players"}` : undefined; +} + +/** "60–90 min", "45 min" or undefined. */ +export function gamePlayTime(g: Game): string | undefined { + const d = g.data as GameSynced; + const r = d.minPlayTime && d.maxPlayTime ? range(d.minPlayTime, d.maxPlayTime) : range(d.playTime); + return r ? `${r} min` : undefined; +} + /** Every "how it plays" value on the shelf, A to Z. */ export const playStyles = (games: Game[]) => [...new Set(games.flatMap((g) => g.data.howItPlays ?? []))].sort((a, b) => a.localeCompare(b)); diff --git a/services/site/src/lib/store.ts b/services/site/src/lib/store.ts index 18bcc9d..d0e853b 100644 --- a/services/site/src/lib/store.ts +++ b/services/site/src/lib/store.ts @@ -118,10 +118,43 @@ export function priceSummary(line: Line): { from: string; note?: string; more: s return { from: low.price, note: low.note, more }; } -/** Option groups in note order, with their options. */ -export function optionGroups(line: Line): [string, NonNullable][] { - const map = new Map>(); - for (const o of line.data.options ?? []) map.set(o.group, [...(map.get(o.group) ?? []), o]); +export type Option = NonNullable[number] & { inStock: boolean }; + +/** Stock the content service syncs onto a line: Bambuddy filament, Homebox leather and paper. */ +type StockGroup = { group: string; options: { name: string; hex?: string; inStock: boolean }[] }; +interface StockSynced { + filamentStock?: StockGroup[]; + materialStock?: StockGroup[]; +} + +/** + * Option groups in note order, with their options. Synced stock sits under + * the note's own values: a note option keeps every value it sets, takes the + * synced `inStock` and `hex` only where it leaves them out (out of stock + * when the source does not have it, in stock when nothing is synced), and + * in-stock colors the note does not list follow its own. + */ +export function optionGroups(line: Line): [string, Option[]][] { + const d = line.data as Line["data"] & StockSynced; + const synced = new Map(); + for (const g of [...(d.filamentStock ?? []), ...(d.materialStock ?? [])]) synced.set(g.group.toLowerCase(), g.options); + const map = new Map(); + for (const o of line.data.options ?? []) { + const stock = synced.get(o.group.toLowerCase()); + const match = stock?.find((x) => x.name.toLowerCase() === o.name.toLowerCase() || (!!o.hex && x.hex?.toLowerCase() === o.hex.toLowerCase())); + const hex = o.hex ?? match?.hex; + map.set(o.group, [...(map.get(o.group) ?? []), { ...o, ...(hex ? { hex } : {}), inStock: o.inStock ?? (stock ? !!match?.inStock : true) }]); + } + for (const [group, opts] of map) { + const stock = synced.get(group.toLowerCase()) ?? []; + const listed = new Set(opts.map((o) => o.name.toLowerCase())); + const listedHex = new Set(opts.map((o) => o.hex?.toLowerCase()).filter(Boolean)); + for (const x of stock) { + if (!x.inStock || listed.has(x.name.toLowerCase()) || (x.hex && listedHex.has(x.hex.toLowerCase()))) continue; + opts.push({ group, name: x.name, ...(x.hex ? { hex: x.hex } : {}), inStock: true }); + listed.add(x.name.toLowerCase()); + } + } return [...map.entries()]; } diff --git a/services/site/src/lib/world.ts b/services/site/src/lib/world.ts index 80cf142..59994e0 100644 --- a/services/site/src/lib/world.ts +++ b/services/site/src/lib/world.ts @@ -91,6 +91,29 @@ export function statusesFor(s: SiteIndex, page: string) { /** Archidekt deck address. */ export const archidektUrl = (id: number) => `https://archidekt.com/decks/${id}`; +/** Values Archidekt syncs onto a deck (see withSynced in content.ts). */ +interface DeckSynced { + commanderArt?: string; + colors?: string[]; + commanders?: string[]; +} + +/** The deck's own cover, else its synced commander art. */ +export function deckArt(deck: Note<"deck">): string | undefined { + const d = deck.data as DeckSynced & { cover?: string }; + return mediaSrc(d.cover ?? d.commanderArt); +} + +const COLOR_NAMES: Record = { W: "White", U: "Blue", B: "Black", R: "Red", G: "Green" }; + +/** The deck's color identity as names, WUBRG order; empty when not synced. */ +export function deckColors(deck: Note<"deck">): string[] { + return ((deck.data as DeckSynced).colors ?? []).map((c) => COLOR_NAMES[c]).filter((c): c is string => !!c); +} + +/** The deck's commanders, when synced. */ +export const deckCommanders = (deck: Note<"deck">): string[] => (deck.data as DeckSynced).commanders ?? []; + /** Deck's list address: Archidekt when set, else its own list link. */ export function deckUrl(deck: Note<"deck">): string | undefined { return deck.data.archidektId ? archidektUrl(deck.data.archidektId) : deck.data.listUrl; diff --git a/services/site/src/pages/api/inquiries.ts b/services/site/src/pages/api/inquiries.ts new file mode 100644 index 0000000..641d241 --- /dev/null +++ b/services/site/src/pages/api/inquiries.ts @@ -0,0 +1,76 @@ +/** + * POST /api/inquiries: the Inquiries form (multipart, works without script). + * Checks the post, the rate limit and Turnstile, saves a copy through the + * content service, emails Greg, and redirects (303) to /inquiries/sent with + * the gk-inquiry cookie. A post that is turned down goes back to /inquiries + * with the reason and the visitor's answers. + */ +import type { APIRoute } from "astro"; +import { + clientIp, + encodeDraft, + formValues, + inquiryEmail, + inquiryFields, + inquirySent, + isBot, + saveAndSend, + TURNSTILE_FIELD, + validateInquiry, + type ErrorCode, + type FieldErrors, +} from "../../lib/forms.ts"; +import { + contactTo, + flashCookie, + inquiryLimiter, + maxAttachmentBytes, + reportDelivery, + saveSubmission, + sendMail, + verifyTurnstile, +} from "../../lib/forms-server.ts"; + +export const prerender = false; + +export const POST: APIRoute = async ({ request, cookies, redirect, url, clientAddress }) => { + let form: FormData; + try { + form = await request.formData(); + } catch { + return redirect("/inquiries?error=invalid", 303); + } + const type = String(form.get("type") ?? ""); + const back = (code: ErrorCode, errors: FieldErrors = {}) => { + cookies.set("gk-inquiry-draft", encodeDraft({ code, errors, values: formValues(form) }), flashCookie(url)); + const q = new URLSearchParams({ error: code }); + if (/^[a-z]{1,20}$/.test(type)) q.set("type", type); + return redirect(`/inquiries?${q}`, 303); + }; + + // A bot filled the hidden field: answer as if it worked, keep nothing. + if (isBot(form)) return redirect("/inquiries/sent", 303); + + const checked = validateInquiry(form, { maxBytes: maxAttachmentBytes() }); + if (!checked.ok) return back(checked.code, checked.errors); + const q = checked.value; + + let ip = "unknown"; + try { + ip = clientIp(request.headers, clientAddress); + } catch { + ip = clientIp(request.headers); + } + if (inquiryLimiter.hit(ip)) return back("rate"); + if (!(await verifyTurnstile(String(form.get(TURNSTILE_FIELD) ?? ""), ip))) return back("bot"); + + const result = await saveAndSend({ + save: () => saveSubmission({ kind: "inquiry", type: q.type, name: q.name, email: q.email, fields: inquiryFields(q), attachment: q.attachment }), + email: (savedId) => sendMail({ to: contactTo(), replyTo: { name: q.name, address: q.email }, ...inquiryEmail(q, savedId), attachment: q.attachment }), + report: reportDelivery, + }); + if (!result.ok) return back("failed"); + + cookies.set("gk-inquiry", JSON.stringify(inquirySent(q)), flashCookie(url)); + return redirect("/inquiries/sent", 303); +}; diff --git a/services/site/src/pages/api/orders.ts b/services/site/src/pages/api/orders.ts new file mode 100644 index 0000000..9fc00e9 --- /dev/null +++ b/services/site/src/pages/api/orders.ts @@ -0,0 +1,80 @@ +/** + * POST /api/orders: the Store's order request form (works without script). + * Checks the post, the rate limit and Turnstile, then re-checks the line + * against the live store notes: an order is taken only when its category's + * ordersOpen is true and the line has an open slot or stock, whatever the + * post says. Then it saves a copy, emails Greg, and redirects (303) to + * /store/order/sent with the gk-order cookie. No payment is taken. + */ +import type { APIRoute } from "astro"; +import { + checkOrder, + clientIp, + encodeDraft, + formValues, + isBot, + orderEmail, + orderFields, + orderSent, + saveAndSend, + TURNSTILE_FIELD, + validateOrderFields, + type ErrorCode, + type FieldErrors, +} from "../../lib/forms.ts"; +import { flashCookie, liveStore, orderLimiter, orderTo, reportDelivery, saveSubmission, sendMail, verifyTurnstile } from "../../lib/forms-server.ts"; +import type { StoreState } from "../../lib/store.ts"; + +export const prerender = false; + +export const POST: APIRoute = async ({ request, cookies, redirect, url, clientAddress }) => { + let form: FormData; + try { + form = await request.formData(); + } catch { + return redirect("/store/order?error=invalid", 303); + } + const line = String(form.get("line") ?? ""); + const back = (code: ErrorCode, errors: FieldErrors = {}) => { + cookies.set("gk-order-draft", encodeDraft({ code, errors, values: formValues(form) }), flashCookie(url)); + const q = new URLSearchParams({ error: code }); + if (/^[a-z0-9-]{1,120}$/.test(line)) q.set("line", line); + return redirect(`/store/order?${q}`, 303); + }; + + if (isBot(form)) return redirect("/store/order/sent", 303); + + const fields = validateOrderFields(form); + if (!fields.ok) return back(fields.code, fields.errors); + + let ip = "unknown"; + try { + ip = clientIp(request.headers, clientAddress); + } catch { + ip = clientIp(request.headers); + } + if (orderLimiter.hit(ip)) return back("rate"); + if (!(await verifyTurnstile(String(form.get(TURNSTILE_FIELD) ?? ""), ip))) return back("bot"); + + // The gate: always the live notes, never the cached copy or the form. + let st: StoreState; + try { + st = await liveStore(); + } catch (err) { + console.error("Could not read the live store notes; turning the order down.", err); + return back("unavailable"); + } + const checked = checkOrder(fields.value, form, st); + if (!checked.ok) return back(checked.code, checked.errors); + const o = checked.value; + + const result = await saveAndSend({ + save: () => saveSubmission({ kind: "order", type: o.lineNote.slug, name: o.name, email: o.email, fields: orderFields(o) }), + email: (savedId) => sendMail({ to: orderTo(), replyTo: { name: o.name, address: o.email }, ...orderEmail(o, savedId) }), + report: reportDelivery, + }); + if (!result.ok) return back("failed"); + + cookies.set("gk-order", JSON.stringify(orderSent(o)), flashCookie(url)); + return redirect("/store/order/sent", 303); +}; diff --git a/services/site/src/pages/inquiries/index.astro b/services/site/src/pages/inquiries/index.astro index abfeb8f..2f27ea2 100644 --- a/services/site/src/pages/inquiries/index.astro +++ b/services/site/src/pages/inquiries/index.astro @@ -1,20 +1,31 @@ --- import Button from "@gk/ui/components/Button.astro"; import Field from "@gk/ui/components/Field.astro"; +import Notice from "@gk/ui/components/Notice.astro"; import HeroHorizon from "@gk/ui/components/HeroHorizon.astro"; import PageShell from "@gk/ui/components/PageShell.astro"; import Seal from "@gk/ui/components/Seal.astro"; import Star from "@gk/ui/components/Star.astro"; import StatusPill from "@gk/ui/components/StatusPill.astro"; import ChoicePills from "../../components/work/ChoicePills.astro"; -import { env } from "../../lib/env.ts"; +import { choices, errorMessages, HONEYPOT, parseDraft, parseErrorCode, timing } from "../../lib/forms.ts"; +import { turnstileSiteKey } from "../../lib/forms-server.ts"; import { site } from "../../lib/page.ts"; import { inquiryTypes, parseInquiryType, type InquiryType } from "../../lib/work.ts"; const s = await site(); const settings = s.settings(); const text = s.pageText("/inquiries"); -const selected = parseInquiryType(Astro.url.searchParams.get("type")); +// A post that was turned down comes back with its reason and the answers given. +const draft = parseDraft(Astro.cookies.get("gk-inquiry-draft")?.value); +if (draft) { + Astro.cookies.delete("gk-inquiry-draft", { path: "/" }); + Astro.response.headers.set("Cache-Control", "private, no-store"); +} +const errorCode = draft?.code ?? parseErrorCode(Astro.url.searchParams.get("error")); +const v = draft?.values ?? {}; +const err = draft?.errors ?? {}; +const selected = parseInquiryType(v.type ?? Astro.url.searchParams.get("type")); const lede = text?.data.lede ?? "Roles, freelance web work or a custom Patiun order. Pick one and the form asks the right questions."; const quickFacts = text?.data.quickFacts?.map((f) => f.text) ?? [`Replies within ${s.settings()?.replyTime ?? "a few days"}`, "Read by me, not a bot"]; const socials = settings?.socials ?? []; @@ -25,12 +36,7 @@ const placeholders: Record = { freelance: "What you need built, who it is for, and any links.", patiun: "Card list, deck colors, theme, or anything I should know.", }; -const timing = [ - { value: "no-rush", label: "No rush" }, - { value: "within-a-month", label: "Within a month" }, - { value: "date", label: "A specific date" }, -]; -const turnstileKey = env("PUBLIC_TURNSTILE_SITE_KEY") ?? env("TURNSTILE_SITE_KEY"); +const turnstileKey = turnstileSiteKey(); --- {turnstileKey && } @@ -74,8 +80,13 @@ const turnstileKey = env("PUBLIC_TURNSTILE_SITE_KEY") ?? env("TURNSTILE_SITE_KEY
+ {errorCode && ( + + {Object.keys(err).length > 0 ? "The marked answers are below." : ""} + + )}
- What's this about? + What's this about?{err.type && {err.type}}
{ inquiryTypes.map((t) => ( @@ -91,46 +102,68 @@ const turnstileKey = env("PUBLIC_TURNSTILE_SITE_KEY") ?? env("TURNSTILE_SITE_KEY
- - + +
- - + +
- - + +
- +
- +
- +
- +
- +
- +
- +
+ + + {err.details && {err.details}} +
+ {err.attachment && {err.attachment}} + {draft && !err.attachment && If you attached a file, add it again.} - {turnstileKey &&
} + + + {turnstileKey &&
} + {turnstileKey && }
@@ -406,6 +439,18 @@ const turnstileKey = env("PUBLIC_TURNSTILE_SITE_KEY") ?? env("TURNSTILE_SITE_KEY font-size: 13px; color: var(--gk-muted); } + .err { + font-size: 12px; + font-weight: 400; + color: #f0857a; + } + .hp { + position: absolute; + left: -10000px; + width: 1px; + height: 1px; + overflow: hidden; + } @media (max-width: 640px) { .tiles { grid-template-columns: 1fr; diff --git a/services/site/src/pages/library/shelf.astro b/services/site/src/pages/library/shelf.astro index 04fe24c..bcbbf19 100644 --- a/services/site/src/pages/library/shelf.astro +++ b/services/site/src/pages/library/shelf.astro @@ -3,7 +3,8 @@ * The Shelf: every board game. What is on the table (from the status log), * then the collection, filtered by how it plays, owned, want to play and * favorites, and sorted by last played, A to Z or recently added. Player - * counts and play times come from BoardGameGeek later; no filter for them yet. + * counts, play times and box art sync from BoardGameGeek (a cover set in the + * note wins); no filter for them yet. */ import Divider from "@gk/ui/components/Divider.astro"; import FilterGroup from "@gk/ui/components/FilterGroup.astro"; diff --git a/services/site/src/pages/store/order.astro b/services/site/src/pages/store/order.astro index 4e7b33d..220942c 100644 --- a/services/site/src/pages/store/order.astro +++ b/services/site/src/pages/store/order.astro @@ -15,10 +15,20 @@ import OrderForm from "../../components/store/OrderForm.astro"; import { site } from "../../lib/page.ts"; import { formatDate } from "../../lib/site.ts"; import { canOrder, channelOf, storeState } from "../../lib/store.ts"; +import { errorMessages, parseDraft, parseErrorCode } from "../../lib/forms.ts"; +import { turnstileSiteKey } from "../../lib/forms-server.ts"; const s = await site(); const st = storeState(s); -const selected = Astro.url.searchParams.get("line") ?? undefined; +// A post that was turned down comes back with its reason and the answers given. +const draft = parseDraft(Astro.cookies.get("gk-order-draft")?.value); +if (draft) { + Astro.cookies.delete("gk-order-draft", { path: "/" }); + Astro.response.headers.set("Cache-Control", "private, no-store"); +} +const errorCode = draft?.code ?? parseErrorCode(Astro.url.searchParams.get("error")); +const turnstileKey = turnstileSiteKey(); +const selected = draft?.values.line ?? Astro.url.searchParams.get("line") ?? undefined; const orderable = st.lines.filter((l) => canOrder(l, st)); const selling = st.lines.filter((l) => channelOf(l) === "here"); const shown = orderable.length ? orderable : selling; @@ -26,6 +36,7 @@ const turnarounds = [...new Set(st.groups.map(([c]) => st.category(c).turnaround const reopens = st.closed.filter((c) => c.reopens).map((c) => `${c.name} reopens ${formatDate(c.reopens)}.`); --- + {turnstileKey && }

Order request

@@ -34,9 +45,16 @@ const reopens = st.closed.filter((c) => c.reopens).map((c) => `${c.name} reopens
+ {errorCode && ( +
+ + {draft && Object.keys(draft.errors).length > 0 ? "The marked answers are below." : ""} + +
+ )} { orderable.length > 0 ? ( - + ) : (
{st.anyOpen ? ( @@ -104,6 +122,9 @@ const reopens = st.closed.filter((c) => c.reopens).map((c) => `${c.name} reopens flex: 3 1 600px; min-width: 0; } + .problem { + margin-bottom: 16px; + } .closed { display: flex; flex-direction: column; diff --git a/services/site/src/pages/store/order/sent.astro b/services/site/src/pages/store/order/sent.astro new file mode 100644 index 0000000..d0ee191 --- /dev/null +++ b/services/site/src/pages/store/order/sent.astro @@ -0,0 +1,204 @@ +--- +/** + * The confirmation after an order request. /api/orders sets the short-lived + * `gk-order` cookie (URL-encoded JSON: name, email, line, rows) before it + * redirects here; the page shows it once and clears it. Without the cookie + * the page still reads correctly. + */ +import Button from "@gk/ui/components/Button.astro"; +import PageShell from "@gk/ui/components/PageShell.astro"; +import Star from "@gk/ui/components/Star.astro"; +import TickRing from "@gk/ui/components/TickRing.astro"; +import TickScale from "@gk/ui/components/TickScale.astro"; +import Breadcrumb from "../../../components/store/Breadcrumb.astro"; +import { parseOrderSent } from "../../../lib/forms.ts"; +import { site } from "../../../lib/page.ts"; +import { storeState } from "../../../lib/store.ts"; + +const sent = parseOrderSent(Astro.cookies.get("gk-order")?.value); +if (sent) Astro.cookies.delete("gk-order", { path: "/" }); +Astro.response.headers.set("Cache-Control", "private, no-store"); + +const s = await site().catch(() => null); +const replyTime = s?.settings()?.replyTime ?? "a few days"; +const line = sent?.lineSlug && s ? storeState(s).lines.find((l) => l.slug === sent.lineSlug) : undefined; +const turnaround = line && s ? storeState(s).category(line.data.category).turnaround : undefined; +const rows = [ + ...(sent?.line ? [{ label: "Line", value: sent.line }] : []), + ...(sent?.rows ?? []), + ...(sent?.email ? [{ label: "Reply to", value: sent.email }] : []), +]; +const firstName = sent?.name?.split(/\s+/)[0]; +const steps = [ + { title: "I check the request", text: "The details, the options and my queue." }, + { title: "I confirm with you", text: "The total and the timing, by email. No payment is taken through the site." }, + { title: "I start once you say yes", text: turnaround ?? "Nothing is made until we agree." }, +]; +--- + + +
+ +

Your request is in

+

Thanks{firstName ? `, ${firstName}` : ""}. I read every order request myself and reply within {replyTime}.

+
+ +
+
+
    + { + steps.map((st, i) => ( +
  1. + {i + 1} + + {st.title} + {st.text} + +
  2. + )) + } +
+
+ + +
+
+ + { + rows.length > 0 && ( + + ) + } +
+
+ + diff --git a/services/site/src/pages/tabletop.astro b/services/site/src/pages/tabletop.astro index 2df6a6d..d100093 100644 --- a/services/site/src/pages/tabletop.astro +++ b/services/site/src/pages/tabletop.astro @@ -11,7 +11,7 @@ import WorldSection from "../components/world/WorldSection.astro"; import type { Note } from "../lib/content.ts"; import { plural, site } from "../lib/page.ts"; import { slugify } from "@gk/schema"; -import { byOrder, deckUrl, makePhotos, sentence, stancePill, systemPill, systemStates } from "../lib/world.ts"; +import { byOrder, deckArt, deckColors, deckCommanders, deckUrl, makePhotos, sentence, stancePill, systemPill, systemStates } from "../lib/world.ts"; const s = await site(); const text = s.pageText("/tabletop"); @@ -151,7 +151,7 @@ const workshopImg = s.all("make").flatMap((m) => makePhotos(m))[0]; const game = gameOf(d); return (
  • - +
    {[shortGame(game.title), d.data.format].filter(Boolean).join(" · ")}
    @@ -167,6 +167,7 @@ const workshopImg = s.all("make").flatMap((m) => makePhotos(m))[0]; {deckOfMoment?.path === d.path && Playing now}
    + {deckColors(d).length > 0 &&

    {deckColors(d).join(" · ")}

    } {d.data.take &&

    {d.data.take}

    } {url && ( @@ -401,6 +402,11 @@ const workshopImg = s.all("make").flatMap((m) => makePhotos(m))[0]; .d-name a:hover { color: var(--gk-gold-bright); } + .d-colors { + margin: 0; + font-size: 12px; + color: var(--gk-muted); + } .d-link { align-self: flex-start; font-size: 13px; diff --git a/services/site/test/forms.test.ts b/services/site/test/forms.test.ts new file mode 100644 index 0000000..0632f4a --- /dev/null +++ b/services/site/test/forms.test.ts @@ -0,0 +1,261 @@ +import { describe, expect, it, vi } from "vitest"; +import type { Note } from "../src/lib/content.ts"; +import { + checkOrder, + clientIp, + DRAFT_LIMIT, + encodeDraft, + formValues, + inquiryFields, + inquirySent, + isBot, + orderFields, + orderSent, + parseDraft, + parseOrderSent, + RateLimiter, + saveAndSend, + validateInquiry, + validateOrderFields, +} from "../src/lib/forms.ts"; +import { SiteIndex } from "../src/lib/site.ts"; +import { storeState } from "../src/lib/store.ts"; +import { parseSentSummary } from "../src/lib/work.ts"; + +const MB = 1024 * 1024; + +const form = (fields: Record) => { + const f = new FormData(); + for (const [k, v] of Object.entries(fields)) f.append(k, v); + return f; +}; + +const base = { name: "Jane Doe", email: "jane@example.com", details: "A senior role on the platform team." }; + +describe("inquiry validation", () => { + it("takes a job inquiry and only the job questions", () => { + const r = validateInquiry( + form({ ...base, type: "job", job_company: "Acme", job_kind: "Contract", job_link: "https://acme.example/jobs/1", patiun_quantity: "60", freelance_need: "An app" }), + { maxBytes: MB }, + ); + expect(r.ok).toBe(true); + if (!r.ok) return; + expect(r.value.rows).toEqual([ + { label: "Company", value: "Acme" }, + { label: "Kind of role", value: "Contract" }, + { label: "Posting", value: "https://acme.example/jobs/1" }, + ]); + expect(inquiryFields(r.value).at(-1)).toEqual({ label: "Details", value: base.details }); + }); + + it("marks what is wrong", () => { + const r = validateInquiry(form({ type: "nope", name: "", email: "x@", details: "hi", job_link: "javascript:alert(1)" }), { maxBytes: MB }); + expect(r.ok).toBe(false); + if (r.ok) return; + expect(Object.keys(r.errors).sort()).toEqual(["details", "email", "name", "type"]); + }); + + it("checks choices, links and dates for the chosen type", () => { + const bad = validateInquiry( + form({ ...base, type: "freelance", freelance_need: "World peace", freelance_site: "ftp://x", freelance_timing: "date", freelance_date: "soon" }), + { maxBytes: MB }, + ); + expect(bad.ok).toBe(false); + if (!bad.ok) expect(Object.keys(bad.errors).sort()).toEqual(["freelance_date", "freelance_need", "freelance_site"]); + const good = validateInquiry(form({ ...base, type: "patiun", patiun_item: "Deck box", patiun_timing: "date", patiun_date: "2026-12-01" }), { maxBytes: MB }); + expect(good.ok && good.value.rows).toEqual([ + { label: "Item", value: "Deck box" }, + { label: "Need it by", value: "Dec 1, 2026" }, + ]); + }); + + it("limits attachments by type and size", () => { + const ok = validateInquiry(form({ ...base, type: "patiun", attachment: new File(["1 Sol Ring"], "deck.dek") }), { maxBytes: MB }); + expect(ok.ok && ok.value.attachment?.name).toBe("deck.dek"); + const exe = validateInquiry(form({ ...base, type: "patiun", attachment: new File(["MZ"], "x.exe") }), { maxBytes: MB }); + expect(!exe.ok && exe.errors.attachment).toBeTruthy(); + const big = validateInquiry(form({ ...base, type: "patiun", attachment: new File([new Uint8Array(MB + 1)], "big.png") }), { maxBytes: MB }); + expect(!big.ok && big.errors.attachment).toContain("1 MB"); + const empty = validateInquiry(form({ ...base, type: "patiun", attachment: new File([], "") }), { maxBytes: MB }); + expect(empty.ok && empty.value.attachment).toBeNull(); + }); + + it("builds the sent cookie the sent page reads", () => { + const r = validateInquiry(form({ ...base, type: "patiun", patiun_quantity: "60 cards" }), { maxBytes: MB }); + if (!r.ok) throw new Error("should pass"); + const parsed = parseSentSummary(encodeURIComponent(JSON.stringify(inquirySent(r.value)))); + expect(parsed).toEqual({ type: "patiun", name: "Jane Doe", email: "jane@example.com", rows: [{ label: "How many", value: "60 cards" }] }); + }); + + it("spots the honeypot", () => { + expect(isBot(form({ leave_blank: "http://spam" }))).toBe(true); + expect(isBot(form({ leave_blank: "" }))).toBe(false); + }); +}); + +// Sample store notes, written for these tests. +const settings = (open: Record) => + ({ + path: "Store/_settings.md", + type: "store-settings", + name: "_settings", + slug: "settings", + title: "Store settings", + body: "", + acceptedAt: "2026-10-01", + data: { type: "store-settings", title: "Store settings", categories: Object.entries(open).map(([name, ordersOpen]) => ({ name, ordersOpen })) }, + }) as unknown as Note; + +const line = (slug: string, data: Record) => + ({ + path: `Store/${slug}.md`, + type: "product-line", + name: slug, + slug, + title: slug.replace(/-/g, " "), + body: "", + acceptedAt: "2026-10-01", + data: { type: "product-line", title: slug, category: "Tabletop", channel: "here", mode: "made to order", summary: "", priceTiers: [], ...data }, + }) as unknown as Note; + +const store = (open: Record, lines: Note[]) => storeState(new SiteIndex([settings(open), ...lines])); + +const cards = line("proxy-cards", { + slots: { open: 2, total: 5 }, + options: [ + { group: "Finish", name: "Matte", inStock: true }, + { group: "Finish", name: "Foil", inStock: false }, + ], +}); +const orderForm = (extra: Record = {}) => + form({ name: "Bo", email: "bo@example.com", line: "proxy-cards", quantity: "75 cards", terms: "accepted", ...extra }); + +const fieldsOf = (f: FormData) => { + const r = validateOrderFields(f); + if (!r.ok) throw new Error(JSON.stringify(r.errors)); + return r.value; +}; + +describe("order validation", () => { + it("needs the terms, a line, a quantity and contact details", () => { + const r = validateOrderFields(form({ line: "../x" })); + expect(!r.ok && Object.keys(r.errors).sort()).toEqual(["email", "line", "name", "quantity", "terms"]); + }); + + it("takes an order for an open line with room and an in-stock option", () => { + const f = orderForm({ "option.proxy-cards.Finish": "Matte", "option.other-line.Color": "Red" }); + const r = checkOrder(fieldsOf(f), f, store({ Tabletop: true }, [cards])); + expect(r.ok).toBe(true); + if (!r.ok) return; + expect(r.value.options).toEqual([{ label: "Finish", value: "Matte" }]); + expect(orderFields(r.value).map((x) => x.label)).toEqual(["Line", "Finish", "Quantity", "Terms"]); + expect(parseOrderSent(encodeURIComponent(JSON.stringify(orderSent(r.value))))?.rows).toEqual([ + { label: "Finish", value: "Matte" }, + { label: "Quantity", value: "75 cards" }, + ]); + }); + + it("turns down a closed category, whatever the post says", () => { + const f = orderForm(); + expect(checkOrder(fieldsOf(f), f, store({ Tabletop: false }, [cards]))).toMatchObject({ ok: false, code: "closed" }); + // No settings note at all counts as closed. + expect(checkOrder(fieldsOf(f), f, storeState(new SiteIndex([cards])))).toMatchObject({ ok: false, code: "closed" }); + }); + + it("turns down a line with no open slot or no stock", () => { + const f = orderForm(); + const full = line("proxy-cards", { slots: { open: 0, total: 5 } }); + expect(checkOrder(fieldsOf(f), f, store({ Tabletop: true }, [full]))).toMatchObject({ ok: false, code: "full" }); + const sold = line("proxy-cards", { mode: "in stock", quantity: 0 }); + expect(checkOrder(fieldsOf(f), f, store({ Tabletop: true }, [sold]))).toMatchObject({ ok: false, code: "full" }); + const stocked = line("proxy-cards", { mode: "in stock", quantity: 3 }); + expect(checkOrder(fieldsOf(f), f, store({ Tabletop: true }, [stocked])).ok).toBe(true); + }); + + it("turns down lines sold elsewhere or unknown, and out-of-stock options", () => { + const f = orderForm(); + const etsy = line("proxy-cards", { channel: "Patiun.com", slots: { open: 2, total: 5 } }); + expect(checkOrder(fieldsOf(f), f, store({ Tabletop: true }, [etsy]))).toMatchObject({ ok: false, code: "invalid" }); + expect(checkOrder(fieldsOf(f), f, store({ Tabletop: true }, []))).toMatchObject({ ok: false, code: "invalid" }); + const foil = orderForm({ "option.proxy-cards.Finish": "Foil" }); + expect(checkOrder(fieldsOf(foil), foil, store({ Tabletop: true }, [cards]))).toMatchObject({ ok: false, code: "invalid" }); + const made = orderForm({ "option.proxy-cards.Finish": "Gold leaf" }); + expect(checkOrder(fieldsOf(made), made, store({ Tabletop: true }, [cards]))).toMatchObject({ ok: false, code: "invalid" }); + }); +}); + +describe("try-again drafts", () => { + it("round-trips and fits in a cookie", () => { + const values = formValues(form({ name: "Jane", details: "x".repeat(20_000), leave_blank: "", "cf-turnstile-response": "tok", terms: "accepted" })); + expect(Object.keys(values)).toEqual(["name", "details"]); + const raw = encodeDraft({ code: "invalid", errors: { email: "Add an email" }, values }); + expect(encodeURIComponent(raw).length).toBeLessThanOrEqual(DRAFT_LIMIT); + const d = parseDraft(encodeURIComponent(raw)); + expect(d?.code).toBe("invalid"); + expect(d?.values.name).toBe("Jane"); + expect(d?.errors.email).toBe("Add an email"); + expect(parseDraft("garbage")).toBeUndefined(); + expect(parseDraft(JSON.stringify({ code: "nope" }))).toBeUndefined(); + }); +}); + +describe("saving and emailing", () => { + const quiet = { log: () => {} }; + + it("records a failed email and still succeeds when the copy was saved", async () => { + const report = vi.fn(async () => {}); + const r = await saveAndSend({ + ...quiet, + save: async () => ({ id: 7, receipt: "rcpt" }), + email: async () => { + throw new Error("ECONNREFUSED"); + }, + report, + }); + expect(r).toEqual({ ok: true, savedId: 7, email: "failed" }); + expect(report).toHaveBeenCalledWith(7, "rcpt", "failed", "ECONNREFUSED"); + }); + + it("still emails when saving fails, and fails only when both do", async () => { + const email = vi.fn(async (id: number | null) => (id === null ? ("sent" as const) : ("skipped" as const))); + const report = vi.fn(async () => {}); + const failSave = async (): Promise<{ id: number; receipt: string }> => { + throw new Error("down"); + }; + expect(await saveAndSend({ ...quiet, save: failSave, email, report })).toEqual({ ok: true, savedId: null, email: "sent" }); + expect(report).not.toHaveBeenCalled(); + const r = await saveAndSend({ + ...quiet, + save: failSave, + email: async () => { + throw new Error("smtp down"); + }, + report, + }); + expect(r.ok).toBe(false); + // Nothing configured for mail and nothing saved: not a success either. + expect((await saveAndSend({ ...quiet, save: failSave, email: async () => "skipped", report })).ok).toBe(false); + }); + + it("counts a skipped email as fine when the copy was saved", async () => { + const report = vi.fn(async () => {}); + const r = await saveAndSend({ ...quiet, save: async () => ({ id: 1, receipt: "r" }), email: async () => "skipped", report }); + expect(r.ok).toBe(true); + expect(report).toHaveBeenCalledWith(1, "r", "skipped", undefined); + }); +}); + +describe("rate limit and address", () => { + it("allows N posts per window per address", () => { + let t = 0; + const rl = new RateLimiter(() => 2, () => 1000, () => t); + expect([rl.hit("a"), rl.hit("a"), rl.hit("a"), rl.hit("b")]).toEqual([false, false, true, false]); + t = 1001; + expect(rl.hit("a")).toBe(false); + }); + + it("reads the forwarded address first", () => { + expect(clientIp(new Headers({ "x-forwarded-for": "203.0.113.9, 10.0.0.1" }), "10.0.0.2")).toBe("203.0.113.9"); + expect(clientIp(new Headers(), "10.0.0.2")).toBe("10.0.0.2"); + }); +}); diff --git a/services/site/test/homelab-sync.test.ts b/services/site/test/homelab-sync.test.ts new file mode 100644 index 0000000..3945237 --- /dev/null +++ b/services/site/test/homelab-sync.test.ts @@ -0,0 +1,86 @@ +/** + * PersonalReader progress on Library rows and synced stock on Store options. + * Synced values are the shapes the content service's jobs store (see + * services/content/src/sync/jobs/); a note's own values always win, and a + * note with nothing synced renders as before. + */ +import { describe, expect, it } from "vitest"; +import { withSynced, type Note } from "../src/lib/content.ts"; +import { itemDetails, readerProgress, type Item } from "../src/lib/library.ts"; +import { optionGroups, type Line } from "../src/lib/store.ts"; + +const note = (type: string, data: Record, synced?: Record) => + withSynced({ path: `${type}/x.md`, type, name: "x", slug: "x", title: "X", body: "", acceptedAt: "", data: { type, ...data }, synced } as unknown as T); + +describe("Library progress from PersonalReader", () => { + const base = { medium: "manhwa", state: "now", creator: "Someone", year: 2020 }; + + it("adds the chapter line to the details", () => { + const i = note("library-item", base, { chapterProgress: { title: "X", status: "now", chapter: 142, readAt: "2026-10-03T05:00:00.000Z" } }); + expect(readerProgress(i)).toBe("Chapter 142, read Oct 3, 2026"); + expect(itemDetails(i)).toEqual(["Someone", 2020, "Chapter 142, read Oct 3, 2026"]); + const book = note("library-item", { ...base, medium: "light novel" }, { bookProgress: { title: "X", percent: 62 } }); + expect(readerProgress(book)).toBe("62% read"); + }); + + it("lets the note's own progress win, and shows the same line when nothing is synced", () => { + const own = note("library-item", { ...base, progress: "16/18 volumes" }, { chapterProgress: { chapter: 3 } }); + expect(itemDetails(own)).toEqual(["Someone", 2020, "16/18 volumes"]); + const bare = note("library-item", base); + expect(itemDetails(bare)).toEqual(["Someone", 2020]); + expect(readerProgress(note("library-item", base, { bookProgress: { percent: 0 } }))).toBeUndefined(); + }); +}); + +describe("Store options with synced stock", () => { + const lineData = (options: unknown[]) => ({ category: "Tabletop", mode: "made to order", channel: "here", summary: "", priceTiers: [], options }); + const stock = { + filamentStock: [ + { + group: "Filament", + options: [ + { name: "Jade White", hex: "#f5f5dc", inStock: true }, + { name: "Bambu Green", hex: "#00ae42", inStock: false }, + { name: "Charcoal", hex: "#2b2b2b", inStock: true }, + ], + }, + ], + }; + + it("is exactly the note's options when nothing is synced", () => { + const opts = [ + { group: "Filament", name: "Jade White", hex: "#ffffff", inStock: true }, + { group: "Finish", name: "Matte", inStock: false }, + ]; + expect(optionGroups(note("product-line", lineData(opts)))).toEqual([ + ["Filament", [opts[0]]], + ["Finish", [opts[1]]], + ]); + // Left out with nothing synced: in stock, as before the field was optional. + expect(optionGroups(note("product-line", lineData([{ group: "Finish", name: "Gloss" }])))[0]![1][0]!.inStock).toBe(true); + }); + + it("sits under the note: note values win, gaps take the synced value, unlisted in-stock colors follow", () => { + const l = note( + "product-line", + lineData([ + { group: "Filament", name: "Jade White" }, + { group: "Filament", name: "Bambu Green", inStock: true }, + { group: "Filament", name: "Galaxy Black" }, + { group: "Finish", name: "Matte", inStock: true }, + ]), + stock, + ); + const [[g, filament], [, finish]] = optionGroups(l) as [string, ReturnType[number][1]][]; + expect(g).toBe("Filament"); + expect(filament).toEqual([ + { group: "Filament", name: "Jade White", hex: "#f5f5dc", inStock: true }, + // The note says in stock: the note wins over the source. + { group: "Filament", name: "Bambu Green", hex: "#00ae42", inStock: true }, + // Not at the source: out of stock. + { group: "Filament", name: "Galaxy Black", inStock: false }, + { group: "Filament", name: "Charcoal", hex: "#2b2b2b", inStock: true }, + ]); + expect(finish).toEqual([{ group: "Finish", name: "Matte", inStock: true }]); + }); +}); diff --git a/services/site/test/synced.test.ts b/services/site/test/synced.test.ts new file mode 100644 index 0000000..0878866 --- /dev/null +++ b/services/site/test/synced.test.ts @@ -0,0 +1,38 @@ +import { describe, expect, it } from "vitest"; +import { withSynced, type Note } from "../src/lib/content.ts"; +import { gamePlayers, gamePlayTime, type Game } from "../src/lib/library.ts"; +import { deckArt, deckColors } from "../src/lib/world.ts"; + +const note = (type: string, data: Record, synced?: Record) => + ({ path: `${type}/x.md`, type, name: "x", slug: "x", title: "X", body: "", acceptedAt: "", data: { type, ...data }, synced }) as unknown as T; + +describe("synced values", () => { + it("sit under the note's own values: the note always wins", () => { + const n = withSynced(note("library-item", { cover: "[[mine.jpg]]", title: "X" }, { cover: "https://x/cover.jpg", year: 2006 })); + expect(n.data).toMatchObject({ cover: "[[mine.jpg]]", year: 2006 }); + const bare = withSynced(note("library-item", { title: "X" }, { cover: "https://x/cover.jpg" })); + expect((bare.data as { cover?: string }).cover).toBe("https://x/cover.jpg"); + }); + + it("leave a note with nothing synced exactly as it came", () => { + const n = note("board-game", { bggId: 1, blurb: "b" }, {}); + expect(withSynced(n)).toBe(n); + const none = note("board-game", { bggId: 1, blurb: "b" }); + expect(withSynced(none)).toBe(none); + expect([gamePlayers(none), gamePlayTime(none)]).toEqual([undefined, undefined]); + expect(deckArt(note("deck", { game: "[[Magic]]" }))).toBeUndefined(); + expect(deckColors(note("deck", { game: "[[Magic]]" }))).toEqual([]); + }); + + it("give the Shelf players and play time, and the decks art and colors", () => { + const g = withSynced(note("board-game", { bggId: 1, blurb: "b" }, { minPlayers: 1, maxPlayers: 5, playTime: 70, minPlayTime: 40, maxPlayTime: 70 })); + expect([gamePlayers(g), gamePlayTime(g)]).toEqual(["1–5 players", "40–70 min"]); + const solo = withSynced(note("board-game", { bggId: 1, blurb: "b" }, { minPlayers: 1, maxPlayers: 1, playTime: 30 })); + expect([gamePlayers(solo), gamePlayTime(solo)]).toEqual(["1 player", "30 min"]); + const d = withSynced(note>("deck", { game: "[[Magic]]" }, { commanderArt: "https://cards.scryfall.io/a.jpg", colors: ["W", "U", "G"] })); + expect(deckArt(d)).toBe("https://cards.scryfall.io/a.jpg"); + expect(deckColors(d)).toEqual(["White", "Blue", "Green"]); + const own = withSynced(note>("deck", { game: "[[Magic]]", cover: "[[deck.jpg]]" }, { commanderArt: "https://cards.scryfall.io/a.jpg" })); + expect(deckArt(own)).toBe("/media/deck.jpg"); + }); +}); diff --git a/vault-kit/24.01_PersonalWebsite/Site/_templates/Library item.md b/vault-kit/24.01_PersonalWebsite/Site/_templates/Library item.md index a87a90e..1ddd20e 100644 --- a/vault-kit/24.01_PersonalWebsite/Site/_templates/Library item.md +++ b/vault-kit/24.01_PersonalWebsite/Site/_templates/Library item.md @@ -13,6 +13,7 @@ verdict: # optional. A short review. No score. note: # optional. Context, like where you are or how you came to it. progress: # optional. Like 16/18 volumes. Synced from PersonalReader when linked. anilistId: # optional. Links the item to PersonalReader's progress. +kavitaSeriesId: # optional. Links a book or light novel to its series in Kavita, for PersonalReader's progress. --- From f9a8fb1acd83789c889e81c7a2d0c66f9842286a Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 12 Oct 2026 00:03:58 +0000 Subject: [PATCH 15/16] Resume PDF on the personal letterhead (no Patiun LLC) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Swaps the letterhead copy for Greg's personal version: "Software Developer · System Architect · Team Lead" in the header and "Greg Kilmer ✦ GregKilmer.com" in the footer. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_012aSbo2D1qP8cMFQCykf7kx --- docs/site.md | 6 +++--- ...df => Greg-Kilmer-Personal-Letterhead.pdf} | Bin 36235 -> 35468 bytes services/site/src/lib/resume-pdf.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) rename services/site/src/assets/letterhead/{Greg-Kilmer-Letterhead.pdf => Greg-Kilmer-Personal-Letterhead.pdf} (60%) diff --git a/docs/site.md b/docs/site.md index cbc4da2..a59cf96 100644 --- a/docs/site.md +++ b/docs/site.md @@ -87,9 +87,9 @@ or admin token. Never set it where the public can reach the site. `/resume.pdf` is drawn with pdfkit from the same `resumeData()` as `/resume` (`src/lib/resume.ts`), so the two never disagree. Each page is laid over the -Greg Kilmer letterhead (`src/assets/letterhead`, a copy of -`branding/letterhead/Greg-Kilmer-Letterhead.pdf`; re-copy it when the letterhead -changes) with pdf-lib, which keeps both as vectors. It reads top to bottom so +personal Greg Kilmer letterhead, the version without Patiun LLC +(`src/assets/letterhead`, a copy of `Greg-Kilmer-Personal-Letterhead.pdf` from the +branding letterheads; re-copy it when that letterhead changes) with pdf-lib, which keeps both as vectors. It reads top to bottom so applicant tracking systems take it in order, aims for one page (roles marked `earlier` print as a short list; skills share a row with education and projects), uses the brand TTFs in diff --git a/services/site/src/assets/letterhead/Greg-Kilmer-Letterhead.pdf b/services/site/src/assets/letterhead/Greg-Kilmer-Personal-Letterhead.pdf similarity index 60% rename from services/site/src/assets/letterhead/Greg-Kilmer-Letterhead.pdf rename to services/site/src/assets/letterhead/Greg-Kilmer-Personal-Letterhead.pdf index 7594dca34f0bc5c313f85eeac8524204397fe787..df52175e259139725ae43b815441b363c2598024 100644 GIT binary patch delta 13436 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 WADk#0DS^h#1?5Ghr Date: Mon, 12 Oct 2026 00:43:03 +0000 Subject: [PATCH 16/16] Phase 7: Cosmos deployment - Dockerfile with a check target (typecheck and tests) and one image per service on a pinned Node 22.20 Alpine base, each with only its own production dependencies and a health check. - deploy/compose.yaml: gk-content, gk-admin and gk-site on homelab_network with no host ports, read-only root filesystems, no capabilities, the vault's Site folder mounted into the content service, and backup.dump=sqlite on it. - deploy/deploy.sh builds and checks the commit, restarts the stack, and goes back to the last good tag if it doesn't come up healthy; rollback.sh swaps tags. .gitea/workflows/deploy.yml runs it on a host runner on Cosmos. - docs/deploy.md runbook (settings, runner, mirror, NPM hosts, Turnstile, Healthchecks, backups, cutover) and the stack's inventory note in vault-kit. - Site: trusts X-Forwarded-Host/Proto for gregkilmer.com hosts so form posts pass Astro's origin check behind the proxy; rate limits read CF-Connecting-IP; site URL set for absolute links. - Content service: the held-change webhook carries a Discord-ready message; .env.example lists the core settings. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_012aSbo2D1qP8cMFQCykf7kx --- .dockerignore | 13 ++ .gitea/workflows/deploy.yml | 24 ++++ Dockerfile | 93 ++++++++++++++ README.md | 6 +- deploy/admin.env.example | 3 + deploy/compose.yaml | 66 ++++++++++ deploy/deploy.sh | 61 ++++++++++ deploy/rollback.sh | 12 ++ deploy/stack.env.example | 9 ++ docs/content-service.md | 2 +- docs/deploy.md | 114 ++++++++++++++++++ services/content/.env.example | 15 +++ services/content/src/main.ts | 7 +- services/site/astro.config.mjs | 10 ++ services/site/src/lib/forms.ts | 5 +- services/site/test/forms.test.ts | 1 + .../GregKilmer.com stack.md | 36 ++++++ 17 files changed, 472 insertions(+), 5 deletions(-) create mode 100644 .dockerignore create mode 100644 .gitea/workflows/deploy.yml create mode 100644 Dockerfile create mode 100644 deploy/admin.env.example create mode 100644 deploy/compose.yaml create mode 100755 deploy/deploy.sh create mode 100755 deploy/rollback.sh create mode 100644 deploy/stack.env.example create mode 100644 docs/deploy.md create mode 100644 vault-kit/24.01_PersonalWebsite/GregKilmer.com stack.md diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..5733a53 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,13 @@ +**/node_modules +**/dist +**/.astro +/data +services/*/data +out +.git +.env +**/.env +**/.env.* +!**/.env.example +legacy/node_modules +legacy/dist diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml new file mode 100644 index 0000000..445b091 --- /dev/null +++ b/.gitea/workflows/deploy.yml @@ -0,0 +1,24 @@ +# Gitea Actions on Cosmos: Gitea mirrors GitHub, and this builds, checks and +# restarts the GregKilmer.com stack on each new commit to master. +# It runs on a host-mode runner on Cosmos labelled "cosmos" (docs/deploy.md). +name: Deploy GregKilmer.com + +on: + push: + branches: [master] + # A pull mirror's sync may not raise a push event; this catches new commits + # anyway. deploy.sh does nothing when the commit is already running. + schedule: + - cron: "*/15 * * * *" + workflow_dispatch: + +concurrency: + group: gregkilmer-deploy + cancel-in-progress: false + +jobs: + deploy: + runs-on: cosmos + steps: + - uses: actions/checkout@v4 + - run: ./deploy/deploy.sh diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..fecebf7 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,93 @@ +# Images for the GregKilmer.com stack on Cosmos. One file, one target per service: +# +# docker build --target check . runs `npm run check` (typecheck and tests) +# docker build --target content . the content service and MCP server +# docker build --target admin . the admin page (tailnet only) +# docker build --target site . the public site +# +# The Gitea Actions workflow builds check first, so a red check stops a deploy. +# See docs/deploy.md. + +# Pinned by version (Node 22.20, released 2025-09). Override with --build-arg to +# pull through a mirror. +ARG NODE_IMAGE=node:22.20.0-alpine + +# ---------- package manifests, then every workspace's dependencies ---------- +FROM ${NODE_IMAGE} AS manifests +WORKDIR /app +COPY package.json package-lock.json ./ +COPY packages/schema/package.json packages/schema/ +COPY packages/ui/package.json packages/ui/ +COPY services/content/package.json services/content/ +COPY services/admin/package.json services/admin/ +COPY services/site/package.json services/site/ +COPY tools/migrate/package.json tools/migrate/ + +FROM manifests AS deps +RUN npm ci --no-audit --no-fund + +# ---------- typecheck and tests ---------- +FROM deps AS check +COPY . . +RUN npm run check + +# ---------- the site's server build ---------- +FROM deps AS site-build +COPY packages packages +COPY services/site services/site +RUN npm run build -w @gk/site + +# ---------- each service's production dependencies only ---------- +FROM manifests AS content-deps +RUN npm ci --omit=dev --no-audit --no-fund -w @gk/content && npm cache clean --force + +FROM manifests AS admin-deps +RUN npm ci --omit=dev --no-audit --no-fund -w @gk/admin && npm cache clean --force + +FROM manifests AS site-deps +RUN npm ci --omit=dev --no-audit --no-fund -w @gk/site && npm cache clean --force + +# ---------- shared runtime base ---------- +FROM ${NODE_IMAGE} AS runtime +WORKDIR /app +ENV NODE_ENV=production HOST=0.0.0.0 +COPY package.json ./ +COPY packages/schema/package.json packages/schema/ +COPY packages/schema/src packages/schema/src + +# ---------- content service ---------- +FROM runtime AS content +COPY --from=content-deps /app/node_modules node_modules +COPY services/content/package.json services/content/ +COPY services/content/src services/content/src +# The vault's Site folder and the data folder (SQLite and saved form attachments). +ENV PORT=4310 SITE_DIR=/vault/Site DATA_DIR=/data +EXPOSE 4310 +HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 \ + CMD wget -qO- http://127.0.0.1:4310/health >/dev/null || exit 1 +USER node +CMD ["node", "--disable-warning=ExperimentalWarning", "--import", "tsx", "services/content/src/main.ts"] + +# ---------- admin page ---------- +FROM runtime AS admin +COPY --from=admin-deps /app/node_modules node_modules +COPY services/admin/package.json services/admin/ +COPY services/admin/src services/admin/src +COPY services/admin/public services/admin/public +ENV PORT=4320 +EXPOSE 4320 +HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 \ + CMD wget -qO- http://127.0.0.1:4320/health >/dev/null || exit 1 +USER node +CMD ["node", "--import", "tsx", "services/admin/src/main.ts"] + +# ---------- public site ---------- +FROM runtime AS site +COPY --from=site-deps /app/node_modules node_modules +COPY --from=site-build /app/services/site/dist services/site/dist +ENV PORT=4321 +EXPOSE 4321 +HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 \ + CMD wget -qO- http://127.0.0.1:4321/ >/dev/null || exit 1 +USER node +CMD ["node", "services/site/dist/server/entry.mjs"] diff --git a/README.md b/README.md index 9a3ec68..91e93d4 100644 --- a/README.md +++ b/README.md @@ -11,15 +11,17 @@ with the decisions behind it, is the | Path | What it is | | --- | --- | | `packages/schema` | The one definition of every note type: fields, folders, file names, and the checks for one note and for the whole folder. The migration, the content service, the MCP server, the site and any later editing tool all use it. | -| `vault-kit` | What goes into the vault: the Obsidian templates (generated from the schema) and the Site guide. | +| `vault-kit` | What goes into the vault: the Obsidian templates (generated from the schema), the Site guide and the stack's inventory note. | | `services/content` | The content service: watches the Site folder, runs the rules, keeps the last good version and its history, and serves the one write API. The MCP server for agents is part of it. See [docs/content-service.md](docs/content-service.md). | | `services/admin` | The admin page: the review queue with diffs, history and rollback, quick capture, store orders, draft previews and sync health. A client of the content service, on the tailnet only. See [docs/admin.md](docs/admin.md). | | `packages/ui` | The component library: the approved element rules and ornament kit as Astro components, with the brand fonts, seals and icons, and a gallery page that shows them all. See [docs/components.md](docs/components.md). | +| `services/site` | The public site: every page rendered from the content service's published notes, the resume PDF, and the inquiry and order forms. See [docs/site.md](docs/site.md). | +| `deploy`, `Dockerfile`, `.gitea` | The Cosmos stack: images, compose file, deploy and rollback scripts, and the Gitea Actions workflow. See [docs/deploy.md](docs/deploy.md). | | `tools/migrate` | The one-time migration from the old site's data into Site folder notes. | | `docs` | How the pieces fit. Start with [docs/content-model.md](docs/content-model.md). | | `legacy` | The old site, kept for reference only. Nothing outside it imports from it except the migration. | -Coming in later phases: the new site's pages, integrations and deploy. +Coming next: the cutover from the old site (phase 8). ## Commands diff --git a/deploy/admin.env.example b/deploy/admin.env.example new file mode 100644 index 0000000..0dea73d --- /dev/null +++ b/deploy/admin.env.example @@ -0,0 +1,3 @@ +# Admin page settings. On Cosmos: /home/greg/homelab/gregkilmer/admin.env +# The content service's admin token (the admin:… entry in content.env's CONTENT_TOKENS). +ADMIN_TOKEN= diff --git a/deploy/compose.yaml b/deploy/compose.yaml new file mode 100644 index 0000000..6f110cb --- /dev/null +++ b/deploy/compose.yaml @@ -0,0 +1,66 @@ +# The GregKilmer.com stack on Cosmos: content service, admin page and public site. +# +# Lives at /home/greg/homelab/gregkilmer/compose.yaml (the Gitea Actions deploy +# copies it there from deploy/compose.yaml on every build). Settings: +# .env stack settings: GK_TAG, VAULT_SITE, PUID, PGID (deploy/stack.env.example) +# content.env content service secrets (services/content/.env.example) +# admin.env admin page settings (deploy/admin.env.example) +# site.env public site secrets (services/site/.env.example) +# +# No service publishes a host port. Nginx Proxy Manager reaches them by +# container name on homelab_network. See docs/deploy.md. + +name: gregkilmer + +x-common: &common + restart: unless-stopped + user: "${PUID:-1000}:${PGID:-1000}" + networks: [homelab_network] + read_only: true + tmpfs: [/tmp] + security_opt: ["no-new-privileges:true"] + cap_drop: [ALL] + +services: + gk-content: + <<: *common + image: gregkilmer/content:${GK_TAG:?set GK_TAG in .env} + container_name: gk-content + env_file: content.env + environment: + SITE_DIR: /vault/Site + DATA_DIR: /data + volumes: + # The vault's Site folder. Read-write: accepted API and MCP changes are + # written back into the notes. + - ${VAULT_SITE:?set VAULT_SITE in .env}:/vault/Site + # content.db (notes, history, review queue, saved forms, sync cache) and + # submissions/ (form attachments). Both are backed up. + - ./data/content:/data + labels: + backup.dump: sqlite + + gk-admin: + <<: *common + image: gregkilmer/admin:${GK_TAG} + container_name: gk-admin + env_file: admin.env + environment: + CONTENT_URL: http://gk-content:4310 + depends_on: + gk-content: { condition: service_healthy } + + gk-site: + <<: *common + image: gregkilmer/site:${GK_TAG} + container_name: gk-site + env_file: site.env + environment: + CONTENT_URL: http://gk-content:4310 + depends_on: + gk-content: { condition: service_healthy } + +networks: + homelab_network: + name: homelab_network + external: true diff --git a/deploy/deploy.sh b/deploy/deploy.sh new file mode 100755 index 0000000..d9af268 --- /dev/null +++ b/deploy/deploy.sh @@ -0,0 +1,61 @@ +#!/usr/bin/env bash +# Builds the three images for the checked-out commit, runs the checks, and +# restarts the stack on it. If the new containers don't come up healthy, the +# stack goes back to the last good tag. Run from the repo root on Cosmos, by the +# Gitea Actions workflow or by hand. See docs/deploy.md. +set -euo pipefail + +STACK="${GK_STACK_DIR:-/home/greg/homelab/gregkilmer}" +ENV_FILE="$STACK/.env" +TAG="$(git rev-parse --short=12 HEAD)" +compose() { docker compose --project-directory "$STACK" -f "$STACK/compose.yaml" "$@"; } +setting() { grep -E "^$1=" "$ENV_FILE" | tail -n 1 | cut -d= -f2- || true; } +set_setting() { + if grep -qE "^$1=" "$ENV_FILE"; then sed -i "s|^$1=.*|$1=$2|" "$ENV_FILE"; else echo "$1=$2" >> "$ENV_FILE"; fi +} + +for f in .env content.env admin.env site.env; do + [ -f "$STACK/$f" ] || { echo "Missing $STACK/$f (see docs/deploy.md)"; exit 1; } +done + +CURRENT="$(setting GK_TAG)" +PREVIOUS="$(setting GK_PREVIOUS_TAG)" +if [ "$CURRENT" = "$TAG" ] && [ "${FORCE:-}" != "1" ]; then + echo "Already running $TAG; nothing to do (FORCE=1 to rebuild)." + exit 0 +fi + +echo "== Checks" +docker build --target check . + +echo "== Images for $TAG" +for t in content admin site; do + docker build --target "$t" -t "gregkilmer/$t:$TAG" . +done + +echo "== Deploy" +mkdir -p "$STACK/data/content" +cp deploy/compose.yaml "$STACK/compose.yaml" +set_setting GK_TAG "$TAG" +[ -n "$CURRENT" ] && set_setting GK_PREVIOUS_TAG "$CURRENT" + +if ! compose up -d --remove-orphans --wait --wait-timeout 120; then + echo "!! $TAG did not come up healthy" + compose ps + compose logs --tail 50 || true + if [ -n "$CURRENT" ]; then + echo "== Rolling back to $CURRENT" + set_setting GK_TAG "$CURRENT" + set_setting GK_PREVIOUS_TAG "$PREVIOUS" + compose up -d --wait --wait-timeout 120 + fi + exit 1 +fi + +echo "== Prune old images (keeping $TAG and ${CURRENT:-none})" +for t in content admin site; do + docker images "gregkilmer/$t" --format '{{.Tag}}' | while read -r old; do + [ "$old" = "$TAG" ] || [ "$old" = "$CURRENT" ] || docker rmi "gregkilmer/$t:$old" >/dev/null || true + done +done +echo "Deployed $TAG" diff --git a/deploy/rollback.sh b/deploy/rollback.sh new file mode 100755 index 0000000..79a4fec --- /dev/null +++ b/deploy/rollback.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash +# Puts the stack back on the previous tag (GK_PREVIOUS_TAG) and swaps the two, +# so running it again rolls forward. See docs/deploy.md. +set -euo pipefail +STACK="${GK_STACK_DIR:-/home/greg/homelab/gregkilmer}" +ENV_FILE="$STACK/.env" +cur="$(grep -E '^GK_TAG=' "$ENV_FILE" | cut -d= -f2-)" +prev="$(grep -E '^GK_PREVIOUS_TAG=' "$ENV_FILE" | cut -d= -f2-)" +[ -n "$prev" ] || { echo "No GK_PREVIOUS_TAG in $ENV_FILE"; exit 1; } +sed -i "s|^GK_TAG=.*|GK_TAG=$prev|; s|^GK_PREVIOUS_TAG=.*|GK_PREVIOUS_TAG=$cur|" "$ENV_FILE" +docker compose --project-directory "$STACK" -f "$STACK/compose.yaml" up -d --wait --wait-timeout 120 +echo "Now running $prev (previous: $cur)" diff --git a/deploy/stack.env.example b/deploy/stack.env.example new file mode 100644 index 0000000..d7ad733 --- /dev/null +++ b/deploy/stack.env.example @@ -0,0 +1,9 @@ +# Stack settings for compose.yaml. On Cosmos: /home/greg/homelab/gregkilmer/.env +# The Gitea Actions deploy rewrites GK_TAG on every build; GK_PREVIOUS_TAG is the rollback. +GK_TAG= +GK_PREVIOUS_TAG= +# The vault's Site folder on the host. +VAULT_SITE=/home/greg/homelab/obsidian-vault/20-29_Makerspace/24_Homelab_Infrastructure/24.01_PersonalWebsite/Site +# The user the containers run as: the owner of the vault files (id -u greg, id -g greg). +PUID=1000 +PGID=1000 diff --git a/docs/content-service.md b/docs/content-service.md index d15a12c..acceec6 100644 --- a/docs/content-service.md +++ b/docs/content-service.md @@ -120,7 +120,7 @@ move or write a raw file. | `CONTENT_TOKENS` | none | `role:token` pairs, comma separated. Roles: reader, agent, editor, admin, intake. Tokens are 24 characters or more. | | `ALLOWED_EMAILS` | none | Addresses an agent may add without a hold. | | `HEALTHCHECK_URL` | none | Pinged after every full scan. | -| `NOTIFY_WEBHOOK_URL` | none | Receives `{ event: "held", held }` when a change is held. | +| `NOTIFY_WEBHOOK_URL` | none | Receives `{ content, event: "held", held }` when a change is held. `content` is a one-line message, so a Discord webhook URL works as is. | | `SCAN_MINUTES` | `5` | Full scan interval, on top of the file watch. | | `SUBMISSION_MAX_MB` | `10` | Largest attachment a form submission may carry. | | `SYNC_JOBS` | none | Public sync jobs to run, comma separated: `archidekt`, `bgg`, `covers`. See [Sync jobs](#sync-jobs). | diff --git a/docs/deploy.md b/docs/deploy.md new file mode 100644 index 0000000..368c9d5 --- /dev/null +++ b/docs/deploy.md @@ -0,0 +1,114 @@ +# Deploying on Cosmos + +The stack is three containers on `homelab_network`, none with a host port: + +| Container | Image target | Port | Reached as | Who can reach it | +| --- | --- | --- | --- | --- | +| `gk-content` | `content` | 4310 | `content.gregkilmer.com` (API and `/mcp`), `http://gk-content:4310` on homelab_network | Tailnet only | +| `gk-admin` | `admin` | 4320 | `admin.gregkilmer.com` | Tailnet only | +| `gk-site` | `site` | 4321 | `beta.gregkilmer.com` until cutover, then `gregkilmer.com` | Tailnet only until cutover, then public through Cloudflare | + +The old site keeps serving gregkilmer.com until phase 8 (cutover). + +Files in the repo: + +- `Dockerfile`: one target per service, plus `check` (typecheck and every test). Node is pinned to `node:22.20.0-alpine`. +- `deploy/compose.yaml`: the stack. Containers run as the vault's owner, with a read-only root filesystem, no capabilities and `no-new-privileges`. +- `deploy/deploy.sh`: builds `check`, then the three images tagged with the commit, and restarts the stack. If the new containers aren't healthy within two minutes, it puts the last good tag back. Run it again on the same commit and it does nothing (`FORCE=1` rebuilds). +- `deploy/rollback.sh`: switches to the previous tag. Run it again to roll forward. +- `.gitea/workflows/deploy.yml`: runs `deploy.sh` on Cosmos for each new commit to master. + +## On Cosmos + +Everything lives in `/home/greg/homelab/gregkilmer`: + +``` +/home/greg/homelab/gregkilmer/ + compose.yaml copied from deploy/compose.yaml by every deploy + .env GK_TAG, GK_PREVIOUS_TAG, VAULT_SITE, PUID, PGID + content.env content service secrets and sync settings + admin.env ADMIN_TOKEN + site.env site secrets: tokens, Turnstile, SMTP + data/content/ content.db and submissions/ (backed up) +``` + +Kopia already covers `/home/greg`, so the `.env` files and `data/` are in the encrypted backups. + +## First-time setup + +1. **Folder and settings.** Run these from a checkout of the repo: + + ```sh + mkdir -p /home/greg/homelab/gregkilmer/data/content + cd /home/greg/homelab/gregkilmer + cp /deploy/stack.env.example .env + cp /services/content/.env.example content.env + cp /deploy/admin.env.example admin.env + cp /services/site/.env.example site.env + chmod 600 .env *.env + ``` + + In `.env`, check that `PUID` and `PGID` match `id -u greg` and `id -g greg`, so the content service can write notes back into the vault. + +2. **Tokens.** Make one per role with `openssl rand -hex 24`: + - In `content.env`, set `CONTENT_TOKENS=reader:,admin:,intake:,agent:`. + - In `admin.env`, set `ADMIN_TOKEN=`. + - In `site.env`, set `CONTENT_TOKEN=` and `CONTENT_INTAKE_TOKEN=`. + - Give agents ``. + + Leave `CONTENT_URL` out of `site.env`; compose sets it. + +3. **The rest of the settings.** Fill in Healthchecks, the Discord webhook, Turnstile, SMTP and the sync logins. The list is in `/mnt/project-files/website/phase6/setup-on-cosmos.md` and in each `.env.example`. + +4. **Gitea runner.** The workflow needs an `act_runner` on Cosmos with the label `cosmos` running in host mode (`cosmos:host`). It runs as a user in the `docker` group with write access to `/home/greg/homelab/gregkilmer`. + - A runner with the Docker socket is root-equivalent. Register it for this repository only, not for the whole instance. + +5. **Gitea mirror.** Mirror `github.com/Patiun/com.gregkilmer.www` into Gitea as a pull mirror and turn Actions on for it. + - A pull mirror's sync may not raise a push event. The workflow therefore also runs every 15 minutes and deploys only when master has moved. + - Check once that a new commit reaches Cosmos. If the schedule doesn't fire on a mirror either, run the workflow by hand from the Actions tab. + +6. **First deploy.** Run it from the Actions tab, or by hand from a checkout: `./deploy/deploy.sh`. Then check: + - `docker compose -f /home/greg/homelab/gregkilmer/compose.yaml ps` shows three healthy containers. + - `docker logs gk-content` says `Content service on 0.0.0.0:4310: N notes`. + +7. **Nginx Proxy Manager.** Add a proxy host for each hostname. Force SSL on each. For `content`, add `proxy_buffering off;` under Advanced, because MCP streams its responses. + + | Host | Forward to | Access list | + | --- | --- | --- | + | `admin.gregkilmer.com` | `http://gk-admin:4320` | Tailnet only | + | `content.gregkilmer.com` | `http://gk-content:4310` | Tailnet only | + | `beta.gregkilmer.com` | `http://gk-site:4321` | Tailnet only | + + - DNS: an A record for each pointing at 100.99.111.16 (TTL 300), the same as other tailnet hosts. + - Certificates: Let's Encrypt can't reach a tailnet-only host over HTTP, so use NPM's DNS challenge with your DNS provider. This is the same open item as the `*.patiun.com` certificate. + - Agents already on homelab_network can skip NPM and use `http://gk-content:4310/mcp`. + +8. **Turnstile.** Add `beta.gregkilmer.com` and `gregkilmer.com` to the widget's hostnames. + +9. **Healthchecks.** Create these checks: + - one for the content service's full scan (`HEALTHCHECK_URL`), every 5 minutes; + - one per sync job you turn on (Archidekt, Suwayomi, Bambuddy and Homebox hourly; BGG and Kavita daily; covers every 15 minutes). + +10. **Inventory note.** Copy `vault-kit/24.01_PersonalWebsite/GregKilmer.com stack.md` into the vault's `24.01_PersonalWebsite` folder, so `cosmos-inventory` stops listing the containers as undocumented. + +## Backups + +- `gk-content` carries `backup.dump=sqlite`. `cosmos-dump` finds `content.db` in its `/data` mount and copies it with `VACUUM INTO` before Kopia runs. + - `content.db` holds note history, the review queue, saved inquiries and orders, and the sync cache. + - The vault mount has no SQLite files, so nothing else is dumped. +- Form attachments are plain files in `data/content/submissions/`, which Kopia picks up with the rest of `/home/greg`. +- The plan called for a cache folder in `exclusions.conf`. None is needed: the sync cache and search index live inside `content.db` and are small. +- `gk-admin` and `gk-site` keep no data. + +## Cutover (phase 8) + +1. In NPM, point `gregkilmer.com` and `www.gregkilmer.com` at `http://gk-site:4321` with no access list, and retire the old `greg-profile-site` container. +2. Turn on Cloudflare's proxy for both records with SSL mode Full (strict). Cloudflare doesn't cache HTML by default, which is what the site needs: pages change within a minute of a note changing. +3. The site already trusts `X-Forwarded-Host` and `X-Forwarded-Proto` for gregkilmer.com and its subdomains (`security.allowedDomains` in `services/site/astro.config.mjs`), so form posts pass Astro's origin check behind the proxy. It reads the visitor's address from `CF-Connecting-IP` for the form rate limits. + +## Day to day + +- **Deploy:** merge to master on GitHub; Gitea mirrors it and the workflow deploys it. +- **Roll back:** `GK_STACK_DIR=/home/greg/homelab/gregkilmer ./deploy/rollback.sh` from a checkout, or set `GK_TAG` in `.env` to any image tag still on the host and run `docker compose up -d`. +- **Logs:** `docker logs gk-content`. Sync runs also appear on the admin page's Overview. +- **Restart one service:** `docker compose -f /home/greg/homelab/gregkilmer/compose.yaml restart gk-site`. diff --git a/services/content/.env.example b/services/content/.env.example index 6b4b803..3e016ce 100644 --- a/services/content/.env.example +++ b/services/content/.env.example @@ -1,5 +1,20 @@ # Content service settings. Copy to .env (never commit it) or set them in the # container's environment. See docs/content-service.md, "Running it". +# On Cosmos this file is /home/greg/homelab/gregkilmer/content.env (docs/deploy.md). + +# --- Core ----------------------------------------------------------------------- +# role:token pairs, 24+ random characters each (openssl rand -hex 24). +# reader: the site; admin: the admin page; intake: the site's forms; +# agent: AI agents over MCP; editor: future editing tools. +CONTENT_TOKENS= +# Healthchecks ping after every full scan of the Site folder. +HEALTHCHECK_URL= +# Discord (or any) webhook, told whenever a change is held for review. +NOTIFY_WEBHOOK_URL= +# Email addresses an agent may add to a note without a hold. +ALLOWED_EMAILS=greg@gregkilmer.com,contact@gregkilmer.com,orders@gregkilmer.com +# SCAN_MINUTES=5 +# SUBMISSION_MAX_MB=10 # --- Sync jobs (docs/content-service.md, "Sync jobs") ------------------------ # Public sources to run: archidekt (hourly), bgg (daily), covers (every 15 min). diff --git a/services/content/src/main.ts b/services/content/src/main.ts index d03a0e8..b1b7b2b 100644 --- a/services/content/src/main.ts +++ b/services/content/src/main.ts @@ -23,7 +23,12 @@ const service = new ContentService({ fetch(config.notifyWebhookUrl, { method: "POST", headers: { "content-type": "application/json" }, - body: JSON.stringify({ event: "held", held: heldView(held) }), + // `content` is what a Discord webhook shows; other receivers read `held`. + body: JSON.stringify({ + content: `GregKilmer.com held ${held.path} (${held.reasons.map((r) => r.code).join(", ") || "waiting for review"}). Review it on the admin page.`, + event: "held", + held: heldView(held), + }), }).catch((err) => console.error("Notify failed:", err)); } }, diff --git a/services/site/astro.config.mjs b/services/site/astro.config.mjs index 7a616de..5bca5ad 100644 --- a/services/site/astro.config.mjs +++ b/services/site/astro.config.mjs @@ -4,11 +4,21 @@ import node from "@astrojs/node"; import { defineConfig } from "astro/config"; export default defineConfig({ + site: "https://gregkilmer.com", output: "server", adapter: node({ mode: "standalone" }), trailingSlash: "never", build: { format: "file", assets: "assets" }, devToolbar: { enabled: false }, + // Behind Nginx Proxy Manager the request arrives over plain http. Trusting + // X-Forwarded-Host/Proto for these hosts only lets the form posts pass + // Astro's same-origin check without trusting any other host a client sends. + security: { + allowedDomains: [ + { hostname: "gregkilmer.com", protocol: "https" }, + { hostname: "*.gregkilmer.com", protocol: "https" }, + ], + }, server: { host: true, port: Number(process.env.PORT ?? 4330) }, vite: { // cookie is bundled so the server never picks up express's older copy hoisted to the root. diff --git a/services/site/src/lib/forms.ts b/services/site/src/lib/forms.ts index 5335ba9..acccafe 100644 --- a/services/site/src/lib/forms.ts +++ b/services/site/src/lib/forms.ts @@ -468,8 +468,11 @@ export class RateLimiter { } } -/** The visitor's address: the proxy's X-Forwarded-For first, else the socket's. */ +/** The visitor's address: Cloudflare's CF-Connecting-IP, then the proxy's X-Forwarded-For, else the socket's. */ export function clientIp(headers: Headers, fallback?: string): string { + // Cloudflare sets CF-Connecting-IP to the visitor's address and overwrites any copy a client sends. + const cf = headers.get("cf-connecting-ip"); + if (cf) return cf.trim(); const fwd = headers.get("x-forwarded-for"); if (fwd) return fwd.split(",")[0]!.trim(); return headers.get("x-real-ip") ?? fallback ?? "unknown"; diff --git a/services/site/test/forms.test.ts b/services/site/test/forms.test.ts index 0632f4a..c73f916 100644 --- a/services/site/test/forms.test.ts +++ b/services/site/test/forms.test.ts @@ -257,5 +257,6 @@ describe("rate limit and address", () => { it("reads the forwarded address first", () => { expect(clientIp(new Headers({ "x-forwarded-for": "203.0.113.9, 10.0.0.1" }), "10.0.0.2")).toBe("203.0.113.9"); expect(clientIp(new Headers(), "10.0.0.2")).toBe("10.0.0.2"); + expect(clientIp(new Headers({ "cf-connecting-ip": "198.51.100.7", "x-forwarded-for": "1.2.3.4, 10.0.0.1" }))).toBe("198.51.100.7"); }); }); diff --git a/vault-kit/24.01_PersonalWebsite/GregKilmer.com stack.md b/vault-kit/24.01_PersonalWebsite/GregKilmer.com stack.md new file mode 100644 index 0000000..295df81 --- /dev/null +++ b/vault-kit/24.01_PersonalWebsite/GregKilmer.com stack.md @@ -0,0 +1,36 @@ +--- +tags: [homelab, service] +--- + +# GregKilmer.com stack + +The new GregKilmer.com: a content service that reads this folder's `Site/` notes, a tailnet-only admin page, and the public site. Repo: `github.com/Patiun/com.gregkilmer.www` (mirrored to Gitea, deployed by Gitea Actions). Runbook: `docs/deploy.md` in the repo. + +## Containers + +| Container | Role | Network | Host port | +| --- | --- | --- | --- | +| `gk-content` | Content service, sync jobs and MCP server | homelab_network | none | +| `gk-admin` | Admin page | homelab_network | none | +| `gk-site` | Public site | homelab_network | none | + +- Compose: `/home/greg/homelab/gregkilmer/compose.yaml` (copied from the repo's `deploy/compose.yaml` on each deploy) +- Settings and secrets: `/home/greg/homelab/gregkilmer/{.env,content.env,admin.env,site.env}` (600) + +## Data and backups + +- `/home/greg/homelab/gregkilmer/data/content/content.db`: note history, review queue, saved inquiries and orders, sync cache. Label `backup.dump=sqlite` on `gk-content`. +- `/home/greg/homelab/gregkilmer/data/content/submissions/`: form attachments, covered by Kopia. +- The vault's `Site/` folder is mounted read-write into `gk-content`, which writes accepted API and MCP changes back as notes. + +## Access + +- `admin.gregkilmer.com` to `gk-admin:4320`: Tailnet only. +- `content.gregkilmer.com` to `gk-content:4310` (API and `/mcp`): Tailnet only. +- `beta.gregkilmer.com` to `gk-site:4321`: Tailnet only until cutover. After cutover, `gregkilmer.com` is public through Cloudflare. + +## Health + +- Healthchecks: the content service's full scan every 5 minutes, plus one check per sync job. +- A Discord webhook is sent whenever a change is held for review. +- Each container has a Docker health check on `/health` (site: `/`).
  • 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 LlpxW|F)RQ8&Rw;) literal 0 HcmV?d00001 diff --git a/packages/ui/src/assets/fonts/Montserrat-Regular.woff2 b/packages/ui/src/assets/fonts/Montserrat-Regular.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..819b8ad45f15a6247ac2e7f6cd25123f54368993 GIT binary patch literal 26408 zcmV(>K-j-`Pew8T0RR910B0xw5&!@I0azda0A{`b2><{900000000000000000000 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 Hm^uIeJj@o; literal 0 HcmV?d00001 diff --git a/packages/ui/src/assets/fonts/Montserrat-SemiBold.woff2 b/packages/ui/src/assets/fonts/Montserrat-SemiBold.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..720ddb2204c37037cf40503d65d8f88a6256d6a4 GIT binary patch literal 26532 zcmV)IK)k<{900000000000000000000 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 literal 0 HcmV?d00001 diff --git a/packages/ui/src/assets/icons/alert.svg b/packages/ui/src/assets/icons/alert.svg new file mode 100644 index 0000000..6ed51d3 --- /dev/null +++ b/packages/ui/src/assets/icons/alert.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/animation.svg b/packages/ui/src/assets/icons/animation.svg new file mode 100644 index 0000000..47a1124 --- /dev/null +++ b/packages/ui/src/assets/icons/animation.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/arrow-left.svg b/packages/ui/src/assets/icons/arrow-left.svg new file mode 100644 index 0000000..3d55b09 --- /dev/null +++ b/packages/ui/src/assets/icons/arrow-left.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/arrow-right.svg b/packages/ui/src/assets/icons/arrow-right.svg new file mode 100644 index 0000000..d6cc291 --- /dev/null +++ b/packages/ui/src/assets/icons/arrow-right.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/bookshelf.svg b/packages/ui/src/assets/icons/bookshelf.svg new file mode 100644 index 0000000..7bcbab1 --- /dev/null +++ b/packages/ui/src/assets/icons/bookshelf.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/brain.svg b/packages/ui/src/assets/icons/brain.svg new file mode 100644 index 0000000..e3462e5 --- /dev/null +++ b/packages/ui/src/assets/icons/brain.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/briefcase.svg b/packages/ui/src/assets/icons/briefcase.svg new file mode 100644 index 0000000..552922f --- /dev/null +++ b/packages/ui/src/assets/icons/briefcase.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/calendar.svg b/packages/ui/src/assets/icons/calendar.svg new file mode 100644 index 0000000..1fd22d3 --- /dev/null +++ b/packages/ui/src/assets/icons/calendar.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/castle.svg b/packages/ui/src/assets/icons/castle.svg new file mode 100644 index 0000000..c8f8531 --- /dev/null +++ b/packages/ui/src/assets/icons/castle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/chart-bar.svg b/packages/ui/src/assets/icons/chart-bar.svg new file mode 100644 index 0000000..0e2e2a4 --- /dev/null +++ b/packages/ui/src/assets/icons/chart-bar.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/chart-line.svg b/packages/ui/src/assets/icons/chart-line.svg new file mode 100644 index 0000000..3888848 --- /dev/null +++ b/packages/ui/src/assets/icons/chart-line.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/check.svg b/packages/ui/src/assets/icons/check.svg new file mode 100644 index 0000000..283f68d --- /dev/null +++ b/packages/ui/src/assets/icons/check.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/chess.svg b/packages/ui/src/assets/icons/chess.svg new file mode 100644 index 0000000..c1267af --- /dev/null +++ b/packages/ui/src/assets/icons/chess.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/chevron-down.svg b/packages/ui/src/assets/icons/chevron-down.svg new file mode 100644 index 0000000..092ecf1 --- /dev/null +++ b/packages/ui/src/assets/icons/chevron-down.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/chevron-left.svg b/packages/ui/src/assets/icons/chevron-left.svg new file mode 100644 index 0000000..1dda76b --- /dev/null +++ b/packages/ui/src/assets/icons/chevron-left.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/chevron-right.svg b/packages/ui/src/assets/icons/chevron-right.svg new file mode 100644 index 0000000..95e22a1 --- /dev/null +++ b/packages/ui/src/assets/icons/chevron-right.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/clapper.svg b/packages/ui/src/assets/icons/clapper.svg new file mode 100644 index 0000000..6bf345e --- /dev/null +++ b/packages/ui/src/assets/icons/clapper.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/clipboard-check.svg b/packages/ui/src/assets/icons/clipboard-check.svg new file mode 100644 index 0000000..bf00624 --- /dev/null +++ b/packages/ui/src/assets/icons/clipboard-check.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/clipboard.svg b/packages/ui/src/assets/icons/clipboard.svg new file mode 100644 index 0000000..34ae58c --- /dev/null +++ b/packages/ui/src/assets/icons/clipboard.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/clock.svg b/packages/ui/src/assets/icons/clock.svg new file mode 100644 index 0000000..320ef86 --- /dev/null +++ b/packages/ui/src/assets/icons/clock.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/close.svg b/packages/ui/src/assets/icons/close.svg new file mode 100644 index 0000000..023ed40 --- /dev/null +++ b/packages/ui/src/assets/icons/close.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/cloud.svg b/packages/ui/src/assets/icons/cloud.svg new file mode 100644 index 0000000..280a147 --- /dev/null +++ b/packages/ui/src/assets/icons/cloud.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/cog.svg b/packages/ui/src/assets/icons/cog.svg new file mode 100644 index 0000000..184934f --- /dev/null +++ b/packages/ui/src/assets/icons/cog.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/commission.svg b/packages/ui/src/assets/icons/commission.svg new file mode 100644 index 0000000..ef0771f --- /dev/null +++ b/packages/ui/src/assets/icons/commission.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/contact.svg b/packages/ui/src/assets/icons/contact.svg new file mode 100644 index 0000000..c181ae4 --- /dev/null +++ b/packages/ui/src/assets/icons/contact.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/cube.svg b/packages/ui/src/assets/icons/cube.svg new file mode 100644 index 0000000..e6e2b58 --- /dev/null +++ b/packages/ui/src/assets/icons/cube.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/devices.svg b/packages/ui/src/assets/icons/devices.svg new file mode 100644 index 0000000..5461fc8 --- /dev/null +++ b/packages/ui/src/assets/icons/devices.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/dice.svg b/packages/ui/src/assets/icons/dice.svg new file mode 100644 index 0000000..7861738 --- /dev/null +++ b/packages/ui/src/assets/icons/dice.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/dog.svg b/packages/ui/src/assets/icons/dog.svg new file mode 100644 index 0000000..e6144ed --- /dev/null +++ b/packages/ui/src/assets/icons/dog.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/external.svg b/packages/ui/src/assets/icons/external.svg new file mode 100644 index 0000000..3811d02 --- /dev/null +++ b/packages/ui/src/assets/icons/external.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/factory.svg b/packages/ui/src/assets/icons/factory.svg new file mode 100644 index 0000000..0074a8d --- /dev/null +++ b/packages/ui/src/assets/icons/factory.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/favorite.svg b/packages/ui/src/assets/icons/favorite.svg new file mode 100644 index 0000000..4f87ef5 --- /dev/null +++ b/packages/ui/src/assets/icons/favorite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/film.svg b/packages/ui/src/assets/icons/film.svg new file mode 100644 index 0000000..455d170 --- /dev/null +++ b/packages/ui/src/assets/icons/film.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/gallery.svg b/packages/ui/src/assets/icons/gallery.svg new file mode 100644 index 0000000..14526dd --- /dev/null +++ b/packages/ui/src/assets/icons/gallery.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/gamepad.svg b/packages/ui/src/assets/icons/gamepad.svg new file mode 100644 index 0000000..4a8f3ec --- /dev/null +++ b/packages/ui/src/assets/icons/gamepad.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/gauge.svg b/packages/ui/src/assets/icons/gauge.svg new file mode 100644 index 0000000..63f146c --- /dev/null +++ b/packages/ui/src/assets/icons/gauge.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/gem.svg b/packages/ui/src/assets/icons/gem.svg new file mode 100644 index 0000000..d81f880 --- /dev/null +++ b/packages/ui/src/assets/icons/gem.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/graduation.svg b/packages/ui/src/assets/icons/graduation.svg new file mode 100644 index 0000000..3e1e353 --- /dev/null +++ b/packages/ui/src/assets/icons/graduation.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/grid.svg b/packages/ui/src/assets/icons/grid.svg new file mode 100644 index 0000000..79ef3cc --- /dev/null +++ b/packages/ui/src/assets/icons/grid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/hammer.svg b/packages/ui/src/assets/icons/hammer.svg new file mode 100644 index 0000000..7b560f2 --- /dev/null +++ b/packages/ui/src/assets/icons/hammer.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/heart.svg b/packages/ui/src/assets/icons/heart.svg new file mode 100644 index 0000000..12d8feb --- /dev/null +++ b/packages/ui/src/assets/icons/heart.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/hearth.svg b/packages/ui/src/assets/icons/hearth.svg new file mode 100644 index 0000000..2748662 --- /dev/null +++ b/packages/ui/src/assets/icons/hearth.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/home.svg b/packages/ui/src/assets/icons/home.svg new file mode 100644 index 0000000..066389c --- /dev/null +++ b/packages/ui/src/assets/icons/home.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/info.svg b/packages/ui/src/assets/icons/info.svg new file mode 100644 index 0000000..c6011a3 --- /dev/null +++ b/packages/ui/src/assets/icons/info.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/leather.svg b/packages/ui/src/assets/icons/leather.svg new file mode 100644 index 0000000..328c618 --- /dev/null +++ b/packages/ui/src/assets/icons/leather.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/link.svg b/packages/ui/src/assets/icons/link.svg new file mode 100644 index 0000000..18eecc7 --- /dev/null +++ b/packages/ui/src/assets/icons/link.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/list-text.svg b/packages/ui/src/assets/icons/list-text.svg new file mode 100644 index 0000000..b9b216f --- /dev/null +++ b/packages/ui/src/assets/icons/list-text.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/list.svg b/packages/ui/src/assets/icons/list.svg new file mode 100644 index 0000000..df76b9a --- /dev/null +++ b/packages/ui/src/assets/icons/list.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/location.svg b/packages/ui/src/assets/icons/location.svg new file mode 100644 index 0000000..e626fa1 --- /dev/null +++ b/packages/ui/src/assets/icons/location.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/lock.svg b/packages/ui/src/assets/icons/lock.svg new file mode 100644 index 0000000..f61bcba --- /dev/null +++ b/packages/ui/src/assets/icons/lock.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/menu.svg b/packages/ui/src/assets/icons/menu.svg new file mode 100644 index 0000000..0c62e6a --- /dev/null +++ b/packages/ui/src/assets/icons/menu.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/monitor.svg b/packages/ui/src/assets/icons/monitor.svg new file mode 100644 index 0000000..2d68708 --- /dev/null +++ b/packages/ui/src/assets/icons/monitor.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/our-story.svg b/packages/ui/src/assets/icons/our-story.svg new file mode 100644 index 0000000..2fc2230 --- /dev/null +++ b/packages/ui/src/assets/icons/our-story.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/palette.svg b/packages/ui/src/assets/icons/palette.svg new file mode 100644 index 0000000..eb1ee9b --- /dev/null +++ b/packages/ui/src/assets/icons/palette.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/people.svg b/packages/ui/src/assets/icons/people.svg new file mode 100644 index 0000000..32690e1 --- /dev/null +++ b/packages/ui/src/assets/icons/people.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/person-details.svg b/packages/ui/src/assets/icons/person-details.svg new file mode 100644 index 0000000..888d050 --- /dev/null +++ b/packages/ui/src/assets/icons/person-details.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/person.svg b/packages/ui/src/assets/icons/person.svg new file mode 100644 index 0000000..13f5005 --- /dev/null +++ b/packages/ui/src/assets/icons/person.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/play.svg b/packages/ui/src/assets/icons/play.svg new file mode 100644 index 0000000..8523a47 --- /dev/null +++ b/packages/ui/src/assets/icons/play.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/price.svg b/packages/ui/src/assets/icons/price.svg new file mode 100644 index 0000000..149c88c --- /dev/null +++ b/packages/ui/src/assets/icons/price.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/print.svg b/packages/ui/src/assets/icons/print.svg new file mode 100644 index 0000000..8a8e658 --- /dev/null +++ b/packages/ui/src/assets/icons/print.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/proxy-cards.svg b/packages/ui/src/assets/icons/proxy-cards.svg new file mode 100644 index 0000000..6861ec3 --- /dev/null +++ b/packages/ui/src/assets/icons/proxy-cards.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/robot.svg b/packages/ui/src/assets/icons/robot.svg new file mode 100644 index 0000000..011a19d --- /dev/null +++ b/packages/ui/src/assets/icons/robot.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/round-knife.svg b/packages/ui/src/assets/icons/round-knife.svg new file mode 100644 index 0000000..6b1338c --- /dev/null +++ b/packages/ui/src/assets/icons/round-knife.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/ruler.svg b/packages/ui/src/assets/icons/ruler.svg new file mode 100644 index 0000000..9fcbd20 --- /dev/null +++ b/packages/ui/src/assets/icons/ruler.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/scissors.svg b/packages/ui/src/assets/icons/scissors.svg new file mode 100644 index 0000000..62dd57d --- /dev/null +++ b/packages/ui/src/assets/icons/scissors.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/search.svg b/packages/ui/src/assets/icons/search.svg new file mode 100644 index 0000000..66ac09e --- /dev/null +++ b/packages/ui/src/assets/icons/search.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/server.svg b/packages/ui/src/assets/icons/server.svg new file mode 100644 index 0000000..a3f85f7 --- /dev/null +++ b/packages/ui/src/assets/icons/server.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/shapes.svg b/packages/ui/src/assets/icons/shapes.svg new file mode 100644 index 0000000..3e7c91b --- /dev/null +++ b/packages/ui/src/assets/icons/shapes.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/shield-moon.svg b/packages/ui/src/assets/icons/shield-moon.svg new file mode 100644 index 0000000..39b996f --- /dev/null +++ b/packages/ui/src/assets/icons/shield-moon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/shop.svg b/packages/ui/src/assets/icons/shop.svg new file mode 100644 index 0000000..ef50438 --- /dev/null +++ b/packages/ui/src/assets/icons/shop.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/split.svg b/packages/ui/src/assets/icons/split.svg new file mode 100644 index 0000000..4eaae0a --- /dev/null +++ b/packages/ui/src/assets/icons/split.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/staff.svg b/packages/ui/src/assets/icons/staff.svg new file mode 100644 index 0000000..f0afc14 --- /dev/null +++ b/packages/ui/src/assets/icons/staff.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/swords.svg b/packages/ui/src/assets/icons/swords.svg new file mode 100644 index 0000000..136448d --- /dev/null +++ b/packages/ui/src/assets/icons/swords.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/target.svg b/packages/ui/src/assets/icons/target.svg new file mode 100644 index 0000000..303f4da --- /dev/null +++ b/packages/ui/src/assets/icons/target.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/tea.svg b/packages/ui/src/assets/icons/tea.svg new file mode 100644 index 0000000..1a7421d --- /dev/null +++ b/packages/ui/src/assets/icons/tea.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/timeline.svg b/packages/ui/src/assets/icons/timeline.svg new file mode 100644 index 0000000..787326e --- /dev/null +++ b/packages/ui/src/assets/icons/timeline.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/toolbox.svg b/packages/ui/src/assets/icons/toolbox.svg new file mode 100644 index 0000000..883ca46 --- /dev/null +++ b/packages/ui/src/assets/icons/toolbox.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/translate.svg b/packages/ui/src/assets/icons/translate.svg new file mode 100644 index 0000000..85c5007 --- /dev/null +++ b/packages/ui/src/assets/icons/translate.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/trophy.svg b/packages/ui/src/assets/icons/trophy.svg new file mode 100644 index 0000000..f4f9c72 --- /dev/null +++ b/packages/ui/src/assets/icons/trophy.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/tv.svg b/packages/ui/src/assets/icons/tv.svg new file mode 100644 index 0000000..a19d096 --- /dev/null +++ b/packages/ui/src/assets/icons/tv.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/unavailable.svg b/packages/ui/src/assets/icons/unavailable.svg new file mode 100644 index 0000000..0fc9fa7 --- /dev/null +++ b/packages/ui/src/assets/icons/unavailable.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/verified.svg b/packages/ui/src/assets/icons/verified.svg new file mode 100644 index 0000000..e00d469 --- /dev/null +++ b/packages/ui/src/assets/icons/verified.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/wand.svg b/packages/ui/src/assets/icons/wand.svg new file mode 100644 index 0000000..7ee2cea --- /dev/null +++ b/packages/ui/src/assets/icons/wand.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/web.svg b/packages/ui/src/assets/icons/web.svg new file mode 100644 index 0000000..4253d1b --- /dev/null +++ b/packages/ui/src/assets/icons/web.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/wind.svg b/packages/ui/src/assets/icons/wind.svg new file mode 100644 index 0000000..a1a1084 --- /dev/null +++ b/packages/ui/src/assets/icons/wind.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/icons/wrench.svg b/packages/ui/src/assets/icons/wrench.svg new file mode 100644 index 0000000..d134340 --- /dev/null +++ b/packages/ui/src/assets/icons/wrench.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/motifs/astrolabe.svg b/packages/ui/src/assets/motifs/astrolabe.svg new file mode 100644 index 0000000..9e90190 --- /dev/null +++ b/packages/ui/src/assets/motifs/astrolabe.svg @@ -0,0 +1,2 @@ +0306090120150180210240270300330♈︎♉︎♊︎♋︎♌︎♍︎♎︎♏︎♐︎♑︎♒︎♓︎ + \ No newline at end of file diff --git a/packages/ui/src/assets/motifs/craft-seal-dice.svg b/packages/ui/src/assets/motifs/craft-seal-dice.svg new file mode 100644 index 0000000..dc7ab1d --- /dev/null +++ b/packages/ui/src/assets/motifs/craft-seal-dice.svg @@ -0,0 +1,2 @@ +DICE ✦ AQUA ✦ RESIN ✦ PATIUN ✦ DICE ✦ AQUA ✦ RESIN ✦ PATIUN ✦ 20🜄 + \ No newline at end of file diff --git a/packages/ui/src/assets/motifs/craft-seal-leather.svg b/packages/ui/src/assets/motifs/craft-seal-leather.svg new file mode 100644 index 0000000..6782fb6 --- /dev/null +++ b/packages/ui/src/assets/motifs/craft-seal-leather.svg @@ -0,0 +1,2 @@ +LEATHER ✦ TERRA ✦ HIDE ✦ PATIUN ✦ LEATHER ✦ TERRA ✦ HIDE ✦ PATIUN ✦ 🜃 + \ No newline at end of file diff --git a/packages/ui/src/assets/motifs/craft-seal-print.svg b/packages/ui/src/assets/motifs/craft-seal-print.svg new file mode 100644 index 0000000..5501cb6 --- /dev/null +++ b/packages/ui/src/assets/motifs/craft-seal-print.svg @@ -0,0 +1,2 @@ +PRINT ✦ IGNIS ✦ LAYER ✦ PATIUN ✦ PRINT ✦ IGNIS ✦ LAYER ✦ PATIUN ✦ 🜂 + \ No newline at end of file diff --git a/packages/ui/src/assets/motifs/filament-banner.svg b/packages/ui/src/assets/motifs/filament-banner.svg new file mode 100644 index 0000000..5f9de4d --- /dev/null +++ b/packages/ui/src/assets/motifs/filament-banner.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/packages/ui/src/assets/motifs/filament-transmutation.svg b/packages/ui/src/assets/motifs/filament-transmutation.svg new file mode 100644 index 0000000..208377c --- /dev/null +++ b/packages/ui/src/assets/motifs/filament-transmutation.svg @@ -0,0 +1,2 @@ +SOLVE ET COAGULA ✦ DICE · LEATHER · PRINT ✦ PATIUN ✦ SOLVE ET COAGULA ✦ DICE · LEATHER · PRINT ✦ PATIUN ✦ ☉☽☿♀♂♃♄ + \ No newline at end of file diff --git a/packages/ui/src/assets/motifs/kepler-nest-seal.svg b/packages/ui/src/assets/motifs/kepler-nest-seal.svg new file mode 100644 index 0000000..3fc1a42 --- /dev/null +++ b/packages/ui/src/assets/motifs/kepler-nest-seal.svg @@ -0,0 +1,2 @@ +HARMONICES MUNDI ✦ PATIUN ✦ DICE · LEATHER · PRINT ✦ HARMONICES MUNDI ✦ PATIUN ✦ DICE · LEATHER · PRINT ✦ + \ No newline at end of file diff --git a/packages/ui/src/assets/motifs/layered-seal.svg b/packages/ui/src/assets/motifs/layered-seal.svg new file mode 100644 index 0000000..cba54ca --- /dev/null +++ b/packages/ui/src/assets/motifs/layered-seal.svg @@ -0,0 +1,2 @@ +♈︎♉︎♊︎♋︎♌︎♍︎♎︎♏︎♐︎♑︎♒︎♓︎PATIUN ✦ PATIUN ✦ PATIUN ✦ PATIUN ✦ PATIUN ✦ PATIUN ✦ 🜂🜄🜁🜃 + \ No newline at end of file diff --git a/packages/ui/src/assets/motifs/mini-astrolabe.svg b/packages/ui/src/assets/motifs/mini-astrolabe.svg new file mode 100644 index 0000000..e94d01c --- /dev/null +++ b/packages/ui/src/assets/motifs/mini-astrolabe.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/ui/src/assets/motifs/mini-layered.svg b/packages/ui/src/assets/motifs/mini-layered.svg new file mode 100644 index 0000000..5856587 --- /dev/null +++ b/packages/ui/src/assets/motifs/mini-layered.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/packages/ui/src/assets/motifs/mini-tome.svg b/packages/ui/src/assets/motifs/mini-tome.svg new file mode 100644 index 0000000..31ab718 --- /dev/null +++ b/packages/ui/src/assets/motifs/mini-tome.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/packages/ui/src/assets/motifs/mini-transmutation.svg b/packages/ui/src/assets/motifs/mini-transmutation.svg new file mode 100644 index 0000000..8b285c0 --- /dev/null +++ b/packages/ui/src/assets/motifs/mini-transmutation.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/packages/ui/src/assets/motifs/seven-metals-dipper.svg b/packages/ui/src/assets/motifs/seven-metals-dipper.svg new file mode 100644 index 0000000..03f556f --- /dev/null +++ b/packages/ui/src/assets/motifs/seven-metals-dipper.svg @@ -0,0 +1,2 @@ +♄♃♂♀☿☽☉ +POLARIS · PLATINUM \ No newline at end of file diff --git a/packages/ui/src/assets/motifs/squared-circle-seal.svg b/packages/ui/src/assets/motifs/squared-circle-seal.svg new file mode 100644 index 0000000..4a03b8e --- /dev/null +++ b/packages/ui/src/assets/motifs/squared-circle-seal.svg @@ -0,0 +1,2 @@ +QUADRATURA CIRCULI ✦ SOLVE ET COAGULA ✦ PATIUN ✦ QUADRATURA CIRCULI ✦ SOLVE ET COAGULA ✦ PATIUN ✦ 🜂🜁🜄🜃🜍☿🜔 + \ No newline at end of file diff --git a/packages/ui/src/assets/motifs/transmutation-seal-star-paths.svg b/packages/ui/src/assets/motifs/transmutation-seal-star-paths.svg new file mode 100644 index 0000000..a3f4485 --- /dev/null +++ b/packages/ui/src/assets/motifs/transmutation-seal-star-paths.svg @@ -0,0 +1,2 @@ +SOLVE ET COAGULA ✦ DICE · LEATHER · PRINT ✦ PATIUN ✦ SOLVE ET COAGULA ✦ DICE · LEATHER · PRINT ✦ PATIUN ✦ ☉☽☿♀♂♃♄ + \ No newline at end of file diff --git a/packages/ui/src/assets/motifs/transmutation-seal.svg b/packages/ui/src/assets/motifs/transmutation-seal.svg new file mode 100644 index 0000000..8c7e5ff --- /dev/null +++ b/packages/ui/src/assets/motifs/transmutation-seal.svg @@ -0,0 +1,2 @@ +SOLVE ET COAGULA ✦ DICE · LEATHER · PRINT ✦ PATIUN ✦ SOLVE ET COAGULA ✦ DICE · LEATHER · PRINT ✦ PATIUN ✦ ☉☽☿♀♂♃♄ + \ No newline at end of file diff --git a/packages/ui/src/assets/motifs/tria-prima-array-seal.svg b/packages/ui/src/assets/motifs/tria-prima-array-seal.svg new file mode 100644 index 0000000..b324133 --- /dev/null +++ b/packages/ui/src/assets/motifs/tria-prima-array-seal.svg @@ -0,0 +1,2 @@ +SAL ✦ SULPHUR ✦ MERCURIUS ✦ PATIUN ✦ SAL ✦ SULPHUR ✦ MERCURIUS ✦ PATIUN ✦ 🜍☿🜔 + \ No newline at end of file diff --git a/packages/ui/src/assets/motifs/zodiac-seal.svg b/packages/ui/src/assets/motifs/zodiac-seal.svg new file mode 100644 index 0000000..871f1c8 --- /dev/null +++ b/packages/ui/src/assets/motifs/zodiac-seal.svg @@ -0,0 +1,2 @@ +QUOD EST SUPERIUS EST SICUT QUOD INFERIUS ✦ PATIUN ✦ QUOD EST SUPERIUS EST SICUT QUOD INFERIUS ✦ PATIUN ✦ ♈︎♉︎♊︎♋︎♌︎♍︎♎︎♏︎♐︎♑︎♒︎♓︎☉☽☿♀♂♃♄ + \ No newline at end of file diff --git a/packages/ui/src/assets/ornaments/card-ticks.svg b/packages/ui/src/assets/ornaments/card-ticks.svg new file mode 100644 index 0000000..f4cfb7e --- /dev/null +++ b/packages/ui/src/assets/ornaments/card-ticks.svg @@ -0,0 +1 @@ + diff --git a/packages/ui/src/components/Button.astro b/packages/ui/src/components/Button.astro new file mode 100644 index 0000000..7b146e0 --- /dev/null +++ b/packages/ui/src/components/Button.astro @@ -0,0 +1,37 @@ +--- +/** + * Rule I. One primary per section; a second action is secondary or a text + * link. A link when given `href`, otherwise a button. + */ +import type { HTMLAttributes } from "astro/types"; + +type Props = { + variant?: "primary" | "secondary" | "text"; + size?: "default" | "compact"; + href?: string; + type?: "button" | "submit" | "reset"; + disabled?: boolean; + /** Show the arrow used on "details" and "read more" actions. */ + arrow?: boolean; + class?: string; +} & Omit, "type">; + +const { variant = "primary", size = "default", href, type = "button", disabled, arrow, class: className, ...rest } = Astro.props; +const classes = [ + variant === "text" ? "gk-text" : ["gk-btn", variant === "primary" ? "gk-pri" : "gk-sec", size === "compact" && "gk-sm"], + className, +]; +--- +{ + href && !disabled ? ( + + + {arrow && } + + ) : ( + + ) +} diff --git a/packages/ui/src/components/Card.astro b/packages/ui/src/components/Card.astro new file mode 100644 index 0000000..aafcfa9 --- /dev/null +++ b/packages/ui/src/components/Card.astro @@ -0,0 +1,29 @@ +--- +/** + * Rule VI. A card. Linked cards turn their border gold on hover and show a + * tick scale along the top edge; the featured card keeps a gold border and + * gilt corners, one per page. + */ +import GiltCorners from "./GiltCorners.astro"; + +interface Props { + href?: string; + featured?: boolean; + as?: "div" | "article" | "li" | "section"; + class?: string; +} +const { href, featured, as: Tag = "article", class: className } = Astro.props; +--- +{ + href ? ( + + {featured && } + + + ) : ( + + {featured && } + + + ) +} diff --git a/packages/ui/src/components/Chip.astro b/packages/ui/src/components/Chip.astro new file mode 100644 index 0000000..4809988 --- /dev/null +++ b/packages/ui/src/components/Chip.astro @@ -0,0 +1,27 @@ +--- +/** + * Rule II. A square 6px chip is a label. With `href` it is a page link and + * leads with a violet star. Chips are never clickable unless they link. + */ +import Star from "./Star.astro"; + +interface Props { + href?: string; + /** A core skill or tag, given a teal edge. */ + core?: boolean; + class?: string; +} +const { href, core, class: className } = Astro.props; +--- +{ + href ? ( + + + + + ) : ( + + + + ) +} diff --git a/packages/ui/src/components/ConstellationDust.astro b/packages/ui/src/components/ConstellationDust.astro new file mode 100644 index 0000000..cc87226 --- /dev/null +++ b/packages/ui/src/components/ConstellationDust.astro @@ -0,0 +1,19 @@ +--- +/** Sparse, static specks behind a page; about one in five gold. */ +import { dust, seedOf } from "../lib/ornaments.ts"; + +interface Props { + count?: number; + seed?: string; +} +const { count = 48, seed = Astro.url.pathname } = Astro.props; +--- +