diff --git a/.github/workflows/build_artifacts.yml b/.github/workflows/build_artifacts.yml index 6365a50..ee8577c 100644 --- a/.github/workflows/build_artifacts.yml +++ b/.github/workflows/build_artifacts.yml @@ -24,16 +24,28 @@ jobs: with: cache-read-only: false + - name: Store short commit hash + run: echo "short_commit_hash=$(git rev-parse --short "$GITHUB_SHA")" >> "$GITHUB_ENV" + - name: Generate resources with Gradle run: ./gradlew runDatagenClient env: + CUSTOM_VERSION: ${{ env.short_commit_hash }} DISABLE_PROPERTIES_UPDATE: true - - name: Build with Gradle run: ./gradlew build env: + CUSTOM_VERSION: ${{ env.short_commit_hash }} + DISABLE_PROPERTIES_UPDATE: true + + - name: Publish to Maven + run: ./gradlew publishMavenPublicationToOffsetMonkey538Repository + env: + CUSTOM_VERSION: ${{ env.short_commit_hash }} DISABLE_PROPERTIES_UPDATE: true + MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }} + MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }} - name: Upload build artifacts uses: actions/upload-artifact@v4 diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..c1778bf --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,58 @@ +name: Publish docs + +on: + push: + branches: + - "*" + release: + types: [published] + + +jobs: + publish-docs: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set url prefix + run: | + if [[ "${{ github.event_name }}" == "push" || "${{ github.event.release.prerelease }}" == "true" ]]; then + echo "url_prefix=staging-" >> "$GITHUB_ENV" + else + echo "url_prefix=" >> "$GITHUB_ENV" + fi + + - name: Store short commit hash + run: echo "short_commit_hash=$(git rev-parse --short "$GITHUB_SHA")" >> "$GITHUB_ENV" + + - uses: danielr1996/envsubst-action@1.1.0 + env: + URL_PREFIX: ${{ env.url_prefix }} + SHORT_COMMIT_HASH: ${{ env.short_commit_hash }} + with: + input: docker-compose-template.yml + output: docker-compose.yml + + - name: Login to container registry + uses: docker/login-action@v3 + with: + registry: registry.oracle.offsetmonkey538.top + username: ${{ secrets.REGISTRY_USER }} + password: ${{ secrets.REGISTRY_PASSWORD }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build and push + uses: docker/build-push-action@v6 + with: + context: . + push: true + tags: registry.oracle.offsetmonkey538.top/loot-table-modifier/docs:${{ env.short_commit_hash }} + + - run: | + tar -czf archive.tar.gz docker-compose.yml && curl -i -X POST -H "Authorization: Bearer $SEELF_TOKEN" -F environment=$SEELF_ENVIRONMENT -F archive=@archive.tar.gz $SEELF_APPLICATION_URL + env: + SEELF_TOKEN: ${{ secrets.SEELF_TOKEN }} + SEELF_ENVIRONMENT: staging + SEELF_APPLICATION_URL: https://seelf.oracle.offsetmonkey538.top/api/v1/apps/300qBnG3t1dOFrUGfw1EeWYwKYA/deployments diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 2e57fd4..67250bb 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -26,20 +26,26 @@ jobs: with: cache-read-only: false + - name: Store short commit hash + run: echo "short_commit_hash=$(git rev-parse --short "$GITHUB_SHA")" >> "$GITHUB_ENV" + - name: Generate resources with Gradle run: ./gradlew runDatagenClient env: + CUSTOM_VERSION: ${{ env.short_commit_hash }} DISABLE_PROPERTIES_UPDATE: true - name: Build with Gradle run: ./gradlew build env: + CUSTOM_VERSION: ${{ env.short_commit_hash }} DISABLE_PROPERTIES_UPDATE: true - name: Upload to Modrinth run: ./gradlew modrinth env: + CUSTOM_VERSION: ${{ env.short_commit_hash }} MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }} VERSION_NAME: ${{ github.event.release.name }} VERSION_IS_PRERELEASE: ${{ github.event.release.prerelease }} @@ -50,10 +56,9 @@ jobs: with: files: build/libs/*.jar - - name: Publish to Maven run: ./gradlew publishMavenPublicationToOffsetMonkey538Repository env: + CUSTOM_VERSION: ${{ env.short_commit_hash }} MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }} MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }} - diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..93abdf3 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,40 @@ +#FROM nginx:alpine AS runtime +#COPY ./nginx.conf /etc/nginx/nginx.conf +#COPY ./dist /usr/share/nginx/html +#CMD ["nginx", "-g", "daemon off;"] + +#FROM httpd:latest AS runtime +#COPY ./dist /usr/local/apache2/htdocs/ + +FROM node:lts-slim AS base +ENV PNPM_HOME="/pnpm" +ENV PATH="$PATH:$PNPM_HOME" +RUN corepack enable +COPY /docs /docs +WORKDIR /docs + +FROM base AS build +RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile +RUN pnpm run build + +FROM httpd:alpine +COPY --from=build /docs/dist /usr/local/apache2/htdocs/ +EXPOSE 80 + + +#FROM base +#COPY --from=prod-deps +#COPY package.json pnpm-lock.yaml +#RUN npm install -g pnpm + + + +#FROM node:lts AS setup +#WORKDIR /docs +#COPY ./docs . +#RUN npm install -g pnpm +#RUN pnpm i +#RUN pnpm run build + +#FROM httpd:2.4 AS runtime +#COPY --from=build /docs/dist /usr/local/apache2/htdocs/ diff --git a/README.md b/README.md index 1411d1b..ef44504 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,25 @@ [![modrinth](https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/cozy/available/modrinth_vector.svg)](https://modrinth.com/mod/loot-table-modifier) [![Requires Fabric API](https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/cozy/requires/fabric-api_vector.svg)](https://modrinth.com/mod/fabric-api) +todo: good readme for alpha version + +Allows datapacks to modify loot tables, instead of just overwriting them. +Version 2 of the mod is currently in alpha. v2 adds more ways to modify loot tables than just adding to them +v2 will be backwards-compatible with v1 modifiers, so no need to worry about them breaking. + +If you want to use v1, then see the original description below. +If you do decide to try out the alpha version of v2 (please do), then please go ahead and read the documentation [here](https://loot-table-modifier.docs.offsetmonkey538.top/) and if you encounter any problems, have suggestions for new actions/predicates or just want to say literally anything about the mod, please please please join my discord and tell me about it. I want to make this as good as I can and any sort of feedback really helps. + + + +## Original Description + Allows datapacks (and thus mods as well) to add to loot tables, instead of just overwriting them. +This mod shouldn't impact performance while playing the game, but only when datapacks are reloading (joining a world, starting a server, `/reload` command, whatever else). +Performance impact during pack reloading varies depending on the datapacks. +The mod writes how long applying modifiers took in the console. + Also provides a datagen provider for creating loot table modifiers in mods. A modifier json file includes two components: @@ -65,21 +82,3 @@ dependencies { modImplementation "top.offsetmonkey538.loottablemodifier:loot-table-modifier:1.0.1+1.21.1" } ``` - -## Badges -###### No need to use them if you don't want to -Badges are meant for developers to put on their project pages, so users can see that Loot Table Modifier is used or requires. -They're based on [Devin's Badges](https://github.com/intergrav/devins-badges). - -The `uses` badge is meant for mods or modpacks which want to display that Loot Table Modifier is included. -It can be used in Markdown like this: `[![This project includes Loot Table Modifier](https://raw.githubusercontent.com/OffsetMods538/Loot-Table-Modifier/master/images/uses_badge.svg)](https://modrinth.com/mod/loot-table-modifier)` -And will look like this: -[![This project includes Loot Table Modifier](https://raw.githubusercontent.com/OffsetMods538/Loot-Table-Modifier/master/images/uses_badge.svg)](https://modrinth.com/mod/loot-table-modifier) -Clicking it will bring the user to the Modrinth page for Loot Table Modifier - - -The `requires` badge is meant for datapacks which want to display that Loot Table Modifier is required for the pack to function. -It can be used in Markdown like this: `[![This project requires Loot Table Modifier to be installed](https://raw.githubusercontent.com/OffsetMods538/Loot-Table-Modifier/master/images/requires_badge.svg)](https://modrinth.com/mod/loot-table-modifier)` -And will look like this: -[![This project requires Loot Table Modifier to be installed](https://raw.githubusercontent.com/OffsetMods538/Loot-Table-Modifier/master/images/requires_badge.svg)](https://modrinth.com/mod/loot-table-modifier) -Clicking it will bring the user to the Modrinth page for Loot Table Modifier \ No newline at end of file diff --git a/build.gradle b/build.gradle index 8b86854..bba62c3 100644 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,7 @@ import dex.plugins.outlet.v2.util.ReleaseType plugins { - id 'fabric-loom' version '1.10-SNAPSHOT' + id 'fabric-loom' version '1.11-SNAPSHOT' id 'io.github.dexman545.outlet' version '1.6.1' id 'com.modrinth.minotaur' version '2.+' id 'maven-publish' @@ -12,8 +12,21 @@ targetCompatibility = JavaVersion.VERSION_17 archivesBaseName = "loot-table-modifier" group = "top.offsetmonkey538.loottablemodifier" -version = "${project.mod_version}+${project.minecraft_version}" -if (System.getenv("IS_DEBUG") == "true") version = "${version}-${System.currentTimeMillis()}" + + +version = project.mod_version + +if ("true".equalsIgnoreCase(System.getenv("IS_DEBUG"))) { + version = "${version}-${System.currentTimeMillis()}" +} + +final String customVersion = System.getenv("CUSTOM_VERSION") +if (customVersion != null && !customVersion.isEmpty()) { + version = "${version}-${customVersion}" +} + +version = "${project.version}+${project.minecraft_version}" +println "Version: ${version}" outlet { maintainPropertiesFile = System.getenv("DISABLE_PROPERTIES_UPDATE") == null @@ -146,13 +159,12 @@ java { tasks.named("javadoc", Javadoc) { options.addFileOption('-add-stylesheet', project.file("javadoc-stylesheet.css")) - // Temporarily - //options { - // links( - // "https://maven.fabricmc.net/docs/fabric-api-${project.fapi_version}/", - // "https://maven.fabricmc.net/docs/yarn-${project.yarn_version}/" - // ) - //} + options { + links( + "https://maven.fabricmc.net/docs/fabric-api-${project.fapi_version}/", + "https://maven.fabricmc.net/docs/yarn-${project.yarn_version}/" + ) + } } jar { @@ -162,28 +174,43 @@ jar { } modrinth { + // Main properties token = System.getenv("MODRINTH_TOKEN") projectId = "loot-table-modifier" + gameVersions = outlet.mcVersions() + + // Version stuff def customVersionName = System.getenv("VERSION_NAME") if (customVersionName != null) versionName = customVersionName + versionNumber = "${project.version}" - versionType = "alpha" + def isPreRelease = System.getenv("VERSION_IS_PRERELEASE") - versionType = !"false".equals(isPreRelease) ? "beta" : "release" + versionType = "true".equalsIgnoreCase(isPreRelease) ? "beta" : "release" + + if (project.mod_version.contains("beta")) versionType = "beta" + else if (project.mod_version.contains("alpha")) versionType = "alpha" + + + // Files uploadFile = remapJar.archiveFile //additionalFiles = [sourcesJar.archiveFile, javadocJar.archiveFile] additionalFiles = [sourcesJar.archiveFile] - gameVersions = outlet.mcVersions() + + syncBodyFrom = rootProject.file("README.md").text def changelogEnv = System.getenv("VERSION_CHANGELOG") if (changelogEnv != null) changelog = changelogEnv + dependencies { required.project "fabric-api" } } tasks.modrinth.dependsOn(tasks.modrinthSyncBody) + + publishing { repositories { maven { @@ -205,10 +232,4 @@ publishing { from(components["java"]) } } - tasks.publishMavenPublicationToMavenLocal.doLast { - if (System.getenv("IS_DEBUG") == "true") System.out.println("Version: " + version) - } - tasks.publishMavenPublicationToOffsetMonkey538Repository.doLast { - if (System.getenv("IS_DEBUG") == "true") System.out.println("Version: " + version) - } -} \ No newline at end of file +} diff --git a/docker-compose-template.yml b/docker-compose-template.yml new file mode 100644 index 0000000..96c558a --- /dev/null +++ b/docker-compose-template.yml @@ -0,0 +1,17 @@ +# Hosts a static page +services: + ${URL_PREFIX}loot-table-modifier: + image: registry.oracle.offsetmonkey538.top/loot-table-modifier/docs:${SHORT_COMMIT_HASH} + restart: unless-stopped + networks: + - traefik-proxy + labels: + - "traefik.enable=true" + + - "traefik.http.routers.${URL_PREFIX}loot-table-modifier.rule=Host(`${URL_PREFIX}loot-table-modifier.docs.offsetmonkey538.top`)" + - "traefik.http.routers.${URL_PREFIX}loot-table-modifier.service=${URL_PREFIX}loot-table-modifier" + - "traefik.http.services.${URL_PREFIX}loot-table-modifier.loadbalancer.server.port=80" + +networks: + traefik-proxy: + external: true \ No newline at end of file diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 0000000..6240da8 --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,21 @@ +# build output +dist/ +# generated types +.astro/ + +# dependencies +node_modules/ + +# logs +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* + + +# environment variables +.env +.env.production + +# macOS-specific files +.DS_Store diff --git a/docs/.vscode/extensions.json b/docs/.vscode/extensions.json new file mode 100644 index 0000000..22a1505 --- /dev/null +++ b/docs/.vscode/extensions.json @@ -0,0 +1,4 @@ +{ + "recommendations": ["astro-build.astro-vscode"], + "unwantedRecommendations": [] +} diff --git a/docs/.vscode/launch.json b/docs/.vscode/launch.json new file mode 100644 index 0000000..d642209 --- /dev/null +++ b/docs/.vscode/launch.json @@ -0,0 +1,11 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "command": "./node_modules/.bin/astro dev", + "name": "Development server", + "request": "launch", + "type": "node-terminal" + } + ] +} diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..70f4d8b --- /dev/null +++ b/docs/README.md @@ -0,0 +1,54 @@ +# Starlight Starter Kit: Basics + +[![Built with Starlight](https://astro.badg.es/v2/built-with-starlight/tiny.svg)](https://starlight.astro.build) + +``` +pnpm create astro@latest -- --template starlight +``` + +[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/withastro/starlight/tree/main/examples/basics) +[![Open with CodeSandbox](https://assets.codesandbox.io/github/button-edit-lime.svg)](https://codesandbox.io/p/sandbox/github/withastro/starlight/tree/main/examples/basics) +[![Deploy to Netlify](https://www.netlify.com/img/deploy/button.svg)](https://app.netlify.com/start/deploy?repository=https://github.com/withastro/starlight&create_from_path=examples/basics) +[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fwithastro%2Fstarlight%2Ftree%2Fmain%2Fexamples%2Fbasics&project-name=my-starlight-docs&repository-name=my-starlight-docs) + +> πŸ§‘β€πŸš€ **Seasoned astronaut?** Delete this file. Have fun! + +## πŸš€ Project Structure + +Inside of your Astro + Starlight project, you'll see the following folders and files: + +``` +. +β”œβ”€β”€ public/ +β”œβ”€β”€ src/ +β”‚ β”œβ”€β”€ assets/ +β”‚ β”œβ”€β”€ content/ +β”‚ β”‚ β”œβ”€β”€ docs/ +β”‚ └── content.config.ts +β”œβ”€β”€ astro.config.mjs +β”œβ”€β”€ package.json +└── tsconfig.json +``` + +Starlight looks for `.md` or `.mdx` files in the `src/content/docs/` directory. Each file is exposed as a route based on its file name. + +Images can be added to `src/assets/` and embedded in Markdown with a relative link. + +Static assets, like favicons, can be placed in the `public/` directory. + +## 🧞 Commands + +All commands are run from the root of the project, from a terminal: + +| Command | Action | +| :------------------------ | :----------------------------------------------- | +| `pnpm install` | Installs dependencies | +| `pnpm dev` | Starts local dev server at `localhost:4321` | +| `pnpm build` | Build your production site to `./dist/` | +| `pnpm preview` | Preview your build locally, before deploying | +| `pnpm astro ...` | Run CLI commands like `astro add`, `astro check` | +| `pnpm astro -- --help` | Get help using the Astro CLI | + +## πŸ‘€ Want to learn more? + +Check out [Starlight’s docs](https://starlight.astro.build/), read [the Astro documentation](https://docs.astro.build), or jump into the [Astro Discord server](https://astro.build/chat). diff --git a/docs/astro.config.mjs b/docs/astro.config.mjs new file mode 100644 index 0000000..fea992e --- /dev/null +++ b/docs/astro.config.mjs @@ -0,0 +1,53 @@ +// @ts-check +import { defineConfig } from 'astro/config'; +import starlightSocialIcons from './src/utils/socialIcons'; +import starlight from '@astrojs/starlight'; + +// https://astro.build/config +export default defineConfig({ + integrations: [ + starlightSocialIcons({ + modrinth: "https://modrinth.com/mod/loot-table-modifier" + }), + starlight({ + title: 'Loot Table Modifier', + credits: true, + logo: { + src: './src/assets/face.svg' + }, + customCss: [ + './src/styles/custom.css' + ], + components: { + SocialIcons: './src/utils/SocialIcons.astro', + PageFrame: './src/utils/PageFrame.astro' + }, + social: [ + {icon: 'github', label: 'GitHub', href: 'https://github.com/OffsetMods538/Loot-Table-Modifier'}, + {icon: 'discord', label: 'Discord', href: 'https://discord.offsetmonkey538.top'} + ], + sidebar: [ + {label: 'Getting Started', slug: 'getting_started'}, + { + label: 'Examples', + items: [ + {label: 'Replace any ingot with a diamond', slug: 'examples/replace_ingot_w_diamond'}, + {label: 'Make Creepers and Zombies drop tnt', slug: 'examples/creepers_and_zombies_drop_tnt'}, + {label: 'Remove sticks', slug: 'examples/remove_sticks'}, + {label: 'Remove glowstone and gunpowder from witches', slug: 'examples/remove_glowstone_and_gunpowder_witches'}, + ], + }, + { + label: 'Reference', + items: [ + {label: 'Loot Modifier', slug: 'reference/loot_modifier'}, + {label: 'Actions', slug: 'reference/actions'}, + {label: 'Regex Identifier', slug: 'reference/regex_identifier'}, + {label: 'Predicates', slug: 'reference/predicates'}, + {label: 'Badges', slug: 'reference/badges'}, + ], + }, + ], + }), + ] +}); diff --git a/docs/package.json b/docs/package.json new file mode 100644 index 0000000..f50ef1e --- /dev/null +++ b/docs/package.json @@ -0,0 +1,18 @@ +{ + "name": "docs", + "type": "module", + "version": "0.0.1", + "scripts": { + "dev": "astro dev", + "start": "astro dev", + "build": "astro build", + "preview": "astro preview", + "astro": "astro" + }, + "dependencies": { + "@astrojs/starlight": "^0.35.1", + "astro": "^5.12.0", + "pathe": "^2.0.3", + "sharp": "^0.34.2" + } +} \ No newline at end of file diff --git a/docs/pnpm-lock.yaml b/docs/pnpm-lock.yaml new file mode 100644 index 0000000..b25848d --- /dev/null +++ b/docs/pnpm-lock.yaml @@ -0,0 +1,4306 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@astrojs/starlight': + specifier: ^0.35.1 + version: 0.35.1(astro@5.12.0(@types/node@24.0.10)(rollup@4.44.2)(typescript@5.8.3)) + astro: + specifier: ^5.12.0 + version: 5.12.0(@types/node@24.0.10)(rollup@4.44.2)(typescript@5.8.3) + pathe: + specifier: ^2.0.3 + version: 2.0.3 + sharp: + specifier: ^0.34.2 + version: 0.34.2 + +packages: + + '@astrojs/compiler@2.12.2': + resolution: {integrity: sha512-w2zfvhjNCkNMmMMOn5b0J8+OmUaBL1o40ipMvqcG6NRpdC+lKxmTi48DT8Xw0SzJ3AfmeFLB45zXZXtmbsjcgw==} + + '@astrojs/internal-helpers@0.6.1': + resolution: {integrity: sha512-l5Pqf6uZu31aG+3Lv8nl/3s4DbUzdlxTWDof4pEpto6GUJNhhCbelVi9dEyurOVyqaelwmS9oSyOWOENSfgo9A==} + + '@astrojs/markdown-remark@6.3.2': + resolution: {integrity: sha512-bO35JbWpVvyKRl7cmSJD822e8YA8ThR/YbUsciWNA7yTcqpIAL2hJDToWP5KcZBWxGT6IOdOkHSXARSNZc4l/Q==} + + '@astrojs/markdown-remark@6.3.3': + resolution: {integrity: sha512-DDRtD1sPvAuA7ms2btc9A7/7DApKqgLMNrE6kh5tmkfy8utD0Z738gqd3p5aViYYdUtHIyEJ1X4mCMxfCfu15w==} + + '@astrojs/mdx@4.3.0': + resolution: {integrity: sha512-OGX2KvPeBzjSSKhkCqrUoDMyzFcjKt5nTE5SFw3RdoLf0nrhyCXBQcCyclzWy1+P+XpOamn+p+hm1EhpCRyPxw==} + engines: {node: 18.20.8 || ^20.3.0 || >=22.0.0} + peerDependencies: + astro: ^5.0.0 + + '@astrojs/prism@3.3.0': + resolution: {integrity: sha512-q8VwfU/fDZNoDOf+r7jUnMC2//H2l0TuQ6FkGJL8vD8nw/q5KiL3DS1KKBI3QhI9UQhpJ5dc7AtqfbXWuOgLCQ==} + engines: {node: 18.20.8 || ^20.3.0 || >=22.0.0} + + '@astrojs/sitemap@3.4.1': + resolution: {integrity: sha512-VjZvr1e4FH6NHyyHXOiQgLiw94LnCVY4v06wN/D0gZKchTMkg71GrAHJz81/huafcmavtLkIv26HnpfDq6/h/Q==} + + '@astrojs/starlight@0.35.1': + resolution: {integrity: sha512-/hshlAayMd3B+E+h8wY6JWT1lNmX/K1+ugiZPirW5XFo5QUcNMk/Bsa4oHgg+TFoU6kbxPtijo0VppATfD9XuA==} + peerDependencies: + astro: ^5.5.0 + + '@astrojs/telemetry@3.3.0': + resolution: {integrity: sha512-UFBgfeldP06qu6khs/yY+q1cDAaArM2/7AEIqQ9Cuvf7B1hNLq0xDrZkct+QoIGyjq56y8IaE2I3CTvG99mlhQ==} + engines: {node: 18.20.8 || ^20.3.0 || >=22.0.0} + + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.27.1': + resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.28.0': + resolution: {integrity: sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/runtime@7.27.6': + resolution: {integrity: sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.28.0': + resolution: {integrity: sha512-jYnje+JyZG5YThjHiF28oT4SIZLnYOcSBb6+SDaFIyzDVSkXQmQQYclJ2R+YxcdmK0AX6x1E5OQNtuh3jHDrUg==} + engines: {node: '>=6.9.0'} + + '@capsizecss/unpack@2.4.0': + resolution: {integrity: sha512-GrSU71meACqcmIUxPYOJvGKF0yryjN/L1aCuE9DViCTJI7bfkjgYDPD1zbNDcINJwSSP6UaBZY9GAbYDO7re0Q==} + + '@ctrl/tinycolor@4.1.0': + resolution: {integrity: sha512-WyOx8cJQ+FQus4Mm4uPIZA64gbk3Wxh0so5Lcii0aJifqwoVOlfFtorjLE0Hen4OYyHZMXDWqMmaQemBhgxFRQ==} + engines: {node: '>=14'} + + '@emnapi/runtime@1.4.3': + resolution: {integrity: sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==} + + '@esbuild/aix-ppc64@0.25.5': + resolution: {integrity: sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.25.5': + resolution: {integrity: sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.25.5': + resolution: {integrity: sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.25.5': + resolution: {integrity: sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.25.5': + resolution: {integrity: sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.25.5': + resolution: {integrity: sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.25.5': + resolution: {integrity: sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.25.5': + resolution: {integrity: sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.25.5': + resolution: {integrity: sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.25.5': + resolution: {integrity: sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.25.5': + resolution: {integrity: sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.25.5': + resolution: {integrity: sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.25.5': + resolution: {integrity: sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.25.5': + resolution: {integrity: sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.25.5': + resolution: {integrity: sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.25.5': + resolution: {integrity: sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.25.5': + resolution: {integrity: sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.25.5': + resolution: {integrity: sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.5': + resolution: {integrity: sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.25.5': + resolution: {integrity: sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.25.5': + resolution: {integrity: sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/sunos-x64@0.25.5': + resolution: {integrity: sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.25.5': + resolution: {integrity: sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.25.5': + resolution: {integrity: sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.25.5': + resolution: {integrity: sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@expressive-code/core@0.41.2': + resolution: {integrity: sha512-AJW5Tp9czbLqKMzwudL9Rv4js9afXBxkSGLmCNPq1iRgAYcx9NkTPJiSNCesjKRWoVC328AdSu6fqrD22zDgDg==} + + '@expressive-code/plugin-frames@0.41.2': + resolution: {integrity: sha512-pfy0hkJI4nbaONjmksFDcuHmIuyPTFmi1JpABe4q2ajskiJtfBf+WDAL2pg595R9JNoPrrH5+aT9lbkx2noicw==} + + '@expressive-code/plugin-shiki@0.41.2': + resolution: {integrity: sha512-xD4zwqAkDccXqye+235BH5bN038jYiSMLfUrCOmMlzxPDGWdxJDk5z4uUB/aLfivEF2tXyO2zyaarL3Oqht0fQ==} + + '@expressive-code/plugin-text-markers@0.41.2': + resolution: {integrity: sha512-JFWBz2qYxxJOJkkWf96LpeolbnOqJY95TvwYc0hXIHf9oSWV0h0SY268w/5N3EtQaD9KktzDE+VIVwb9jdb3nw==} + + '@img/sharp-darwin-arm64@0.33.5': + resolution: {integrity: sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [darwin] + + '@img/sharp-darwin-arm64@0.34.2': + resolution: {integrity: sha512-OfXHZPppddivUJnqyKoi5YVeHRkkNE2zUFT2gbpKxp/JZCFYEYubnMg+gOp6lWfasPrTS+KPosKqdI+ELYVDtg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [darwin] + + '@img/sharp-darwin-x64@0.33.5': + resolution: {integrity: sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [darwin] + + '@img/sharp-darwin-x64@0.34.2': + resolution: {integrity: sha512-dYvWqmjU9VxqXmjEtjmvHnGqF8GrVjM2Epj9rJ6BUIXvk8slvNDJbhGFvIoXzkDhrJC2jUxNLz/GUjjvSzfw+g==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-darwin-arm64@1.0.4': + resolution: {integrity: sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==} + cpu: [arm64] + os: [darwin] + + '@img/sharp-libvips-darwin-arm64@1.1.0': + resolution: {integrity: sha512-HZ/JUmPwrJSoM4DIQPv/BfNh9yrOA8tlBbqbLz4JZ5uew2+o22Ik+tHQJcih7QJuSa0zo5coHTfD5J8inqj9DA==} + cpu: [arm64] + os: [darwin] + + '@img/sharp-libvips-darwin-x64@1.0.4': + resolution: {integrity: sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-darwin-x64@1.1.0': + resolution: {integrity: sha512-Xzc2ToEmHN+hfvsl9wja0RlnXEgpKNmftriQp6XzY/RaSfwD9th+MSh0WQKzUreLKKINb3afirxW7A0fz2YWuQ==} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-linux-arm64@1.0.4': + resolution: {integrity: sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==} + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linux-arm64@1.1.0': + resolution: {integrity: sha512-IVfGJa7gjChDET1dK9SekxFFdflarnUB8PwW8aGwEoF3oAsSDuNUTYS+SKDOyOJxQyDC1aPFMuRYLoDInyV9Ew==} + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linux-arm@1.0.5': + resolution: {integrity: sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==} + cpu: [arm] + os: [linux] + + '@img/sharp-libvips-linux-arm@1.1.0': + resolution: {integrity: sha512-s8BAd0lwUIvYCJyRdFqvsj+BJIpDBSxs6ivrOPm/R7piTs5UIwY5OjXrP2bqXC9/moGsyRa37eYWYCOGVXxVrA==} + cpu: [arm] + os: [linux] + + '@img/sharp-libvips-linux-ppc64@1.1.0': + resolution: {integrity: sha512-tiXxFZFbhnkWE2LA8oQj7KYR+bWBkiV2nilRldT7bqoEZ4HiDOcePr9wVDAZPi/Id5fT1oY9iGnDq20cwUz8lQ==} + cpu: [ppc64] + os: [linux] + + '@img/sharp-libvips-linux-s390x@1.0.4': + resolution: {integrity: sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==} + cpu: [s390x] + os: [linux] + + '@img/sharp-libvips-linux-s390x@1.1.0': + resolution: {integrity: sha512-xukSwvhguw7COyzvmjydRb3x/09+21HykyapcZchiCUkTThEQEOMtBj9UhkaBRLuBrgLFzQ2wbxdeCCJW/jgJA==} + cpu: [s390x] + os: [linux] + + '@img/sharp-libvips-linux-x64@1.0.4': + resolution: {integrity: sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==} + cpu: [x64] + os: [linux] + + '@img/sharp-libvips-linux-x64@1.1.0': + resolution: {integrity: sha512-yRj2+reB8iMg9W5sULM3S74jVS7zqSzHG3Ol/twnAAkAhnGQnpjj6e4ayUz7V+FpKypwgs82xbRdYtchTTUB+Q==} + cpu: [x64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-arm64@1.0.4': + resolution: {integrity: sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==} + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-arm64@1.1.0': + resolution: {integrity: sha512-jYZdG+whg0MDK+q2COKbYidaqW/WTz0cc1E+tMAusiDygrM4ypmSCjOJPmFTvHHJ8j/6cAGyeDWZOsK06tP33w==} + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-x64@1.0.4': + resolution: {integrity: sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==} + cpu: [x64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-x64@1.1.0': + resolution: {integrity: sha512-wK7SBdwrAiycjXdkPnGCPLjYb9lD4l6Ze2gSdAGVZrEL05AOUJESWU2lhlC+Ffn5/G+VKuSm6zzbQSzFX/P65A==} + cpu: [x64] + os: [linux] + + '@img/sharp-linux-arm64@0.33.5': + resolution: {integrity: sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + + '@img/sharp-linux-arm64@0.34.2': + resolution: {integrity: sha512-D8n8wgWmPDakc83LORcfJepdOSN6MvWNzzz2ux0MnIbOqdieRZwVYY32zxVx+IFUT8er5KPcyU3XXsn+GzG/0Q==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + + '@img/sharp-linux-arm@0.33.5': + resolution: {integrity: sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm] + os: [linux] + + '@img/sharp-linux-arm@0.34.2': + resolution: {integrity: sha512-0DZzkvuEOqQUP9mo2kjjKNok5AmnOr1jB2XYjkaoNRwpAYMDzRmAqUIa1nRi58S2WswqSfPOWLNOr0FDT3H5RQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm] + os: [linux] + + '@img/sharp-linux-s390x@0.33.5': + resolution: {integrity: sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [s390x] + os: [linux] + + '@img/sharp-linux-s390x@0.34.2': + resolution: {integrity: sha512-EGZ1xwhBI7dNISwxjChqBGELCWMGDvmxZXKjQRuqMrakhO8QoMgqCrdjnAqJq/CScxfRn+Bb7suXBElKQpPDiw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [s390x] + os: [linux] + + '@img/sharp-linux-x64@0.33.5': + resolution: {integrity: sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + + '@img/sharp-linux-x64@0.34.2': + resolution: {integrity: sha512-sD7J+h5nFLMMmOXYH4DD9UtSNBD05tWSSdWAcEyzqW8Cn5UxXvsHAxmxSesYUsTOBmUnjtxghKDl15EvfqLFbQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + + '@img/sharp-linuxmusl-arm64@0.33.5': + resolution: {integrity: sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + + '@img/sharp-linuxmusl-arm64@0.34.2': + resolution: {integrity: sha512-NEE2vQ6wcxYav1/A22OOxoSOGiKnNmDzCYFOZ949xFmrWZOVII1Bp3NqVVpvj+3UeHMFyN5eP/V5hzViQ5CZNA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + + '@img/sharp-linuxmusl-x64@0.33.5': + resolution: {integrity: sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + + '@img/sharp-linuxmusl-x64@0.34.2': + resolution: {integrity: sha512-DOYMrDm5E6/8bm/yQLCWyuDJwUnlevR8xtF8bs+gjZ7cyUNYXiSf/E8Kp0Ss5xasIaXSHzb888V1BE4i1hFhAA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + + '@img/sharp-wasm32@0.33.5': + resolution: {integrity: sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [wasm32] + + '@img/sharp-wasm32@0.34.2': + resolution: {integrity: sha512-/VI4mdlJ9zkaq53MbIG6rZY+QRN3MLbR6usYlgITEzi4Rpx5S6LFKsycOQjkOGmqTNmkIdLjEvooFKwww6OpdQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [wasm32] + + '@img/sharp-win32-arm64@0.34.2': + resolution: {integrity: sha512-cfP/r9FdS63VA5k0xiqaNaEoGxBg9k7uE+RQGzuK9fHt7jib4zAVVseR9LsE4gJcNWgT6APKMNnCcnyOtmSEUQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [win32] + + '@img/sharp-win32-ia32@0.33.5': + resolution: {integrity: sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ia32] + os: [win32] + + '@img/sharp-win32-ia32@0.34.2': + resolution: {integrity: sha512-QLjGGvAbj0X/FXl8n1WbtQ6iVBpWU7JO94u/P2M4a8CFYsvQi4GW2mRy/JqkRx0qpBzaOdKJKw8uc930EX2AHw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ia32] + os: [win32] + + '@img/sharp-win32-x64@0.33.5': + resolution: {integrity: sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [win32] + + '@img/sharp-win32-x64@0.34.2': + resolution: {integrity: sha512-aUdT6zEYtDKCaxkofmmJDJYGCf0+pJg3eU9/oBuqvEeoB9dKI6ZLc/1iLJCTuJQDO4ptntAlkUmHgGjyuobZbw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [win32] + + '@jridgewell/sourcemap-codec@1.5.4': + resolution: {integrity: sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==} + + '@mdx-js/mdx@3.1.0': + resolution: {integrity: sha512-/QxEhPAvGwbQmy1Px8F899L5Uc2KZ6JtXwlCgJmjSTBedwOZkByYcBG4GceIGPXRDsmfxhHazuS+hlOShRLeDw==} + + '@oslojs/encoding@1.1.0': + resolution: {integrity: sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==} + + '@pagefind/darwin-arm64@1.3.0': + resolution: {integrity: sha512-365BEGl6ChOsauRjyVpBjXybflXAOvoMROw3TucAROHIcdBvXk9/2AmEvGFU0r75+vdQI4LJdJdpH4Y6Yqaj4A==} + cpu: [arm64] + os: [darwin] + + '@pagefind/darwin-x64@1.3.0': + resolution: {integrity: sha512-zlGHA23uuXmS8z3XxEGmbHpWDxXfPZ47QS06tGUq0HDcZjXjXHeLG+cboOy828QIV5FXsm9MjfkP5e4ZNbOkow==} + cpu: [x64] + os: [darwin] + + '@pagefind/default-ui@1.3.0': + resolution: {integrity: sha512-CGKT9ccd3+oRK6STXGgfH+m0DbOKayX6QGlq38TfE1ZfUcPc5+ulTuzDbZUnMo+bubsEOIypm4Pl2iEyzZ1cNg==} + + '@pagefind/linux-arm64@1.3.0': + resolution: {integrity: sha512-8lsxNAiBRUk72JvetSBXs4WRpYrQrVJXjlRRnOL6UCdBN9Nlsz0t7hWstRk36+JqHpGWOKYiuHLzGYqYAqoOnQ==} + cpu: [arm64] + os: [linux] + + '@pagefind/linux-x64@1.3.0': + resolution: {integrity: sha512-hAvqdPJv7A20Ucb6FQGE6jhjqy+vZ6pf+s2tFMNtMBG+fzcdc91uTw7aP/1Vo5plD0dAOHwdxfkyw0ugal4kcQ==} + cpu: [x64] + os: [linux] + + '@pagefind/windows-x64@1.3.0': + resolution: {integrity: sha512-BR1bIRWOMqkf8IoU576YDhij1Wd/Zf2kX/kCI0b2qzCKC8wcc2GQJaaRMCpzvCCrmliO4vtJ6RITp/AnoYUUmQ==} + cpu: [x64] + os: [win32] + + '@rollup/pluginutils@5.2.0': + resolution: {integrity: sha512-qWJ2ZTbmumwiLFomfzTyt5Kng4hwPi9rwCYN4SHb6eaRU1KNO4ccxINHr/VhH4GgPlt1XfSTLX2LBTme8ne4Zw==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/rollup-android-arm-eabi@4.44.2': + resolution: {integrity: sha512-g0dF8P1e2QYPOj1gu7s/3LVP6kze9A7m6x0BZ9iTdXK8N5c2V7cpBKHV3/9A4Zd8xxavdhK0t4PnqjkqVmUc9Q==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.44.2': + resolution: {integrity: sha512-Yt5MKrOosSbSaAK5Y4J+vSiID57sOvpBNBR6K7xAaQvk3MkcNVV0f9fE20T+41WYN8hDn6SGFlFrKudtx4EoxA==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.44.2': + resolution: {integrity: sha512-EsnFot9ZieM35YNA26nhbLTJBHD0jTwWpPwmRVDzjylQT6gkar+zenfb8mHxWpRrbn+WytRRjE0WKsfaxBkVUA==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.44.2': + resolution: {integrity: sha512-dv/t1t1RkCvJdWWxQ2lWOO+b7cMsVw5YFaS04oHpZRWehI1h0fV1gF4wgGCTyQHHjJDfbNpwOi6PXEafRBBezw==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.44.2': + resolution: {integrity: sha512-W4tt4BLorKND4qeHElxDoim0+BsprFTwb+vriVQnFFtT/P6v/xO5I99xvYnVzKWrK6j7Hb0yp3x7V5LUbaeOMg==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.44.2': + resolution: {integrity: sha512-tdT1PHopokkuBVyHjvYehnIe20fxibxFCEhQP/96MDSOcyjM/shlTkZZLOufV3qO6/FQOSiJTBebhVc12JyPTA==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.44.2': + resolution: {integrity: sha512-+xmiDGGaSfIIOXMzkhJ++Oa0Gwvl9oXUeIiwarsdRXSe27HUIvjbSIpPxvnNsRebsNdUo7uAiQVgBD1hVriwSQ==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.44.2': + resolution: {integrity: sha512-bDHvhzOfORk3wt8yxIra8N4k/N0MnKInCW5OGZaeDYa/hMrdPaJzo7CSkjKZqX4JFUWjUGm88lI6QJLCM7lDrA==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.44.2': + resolution: {integrity: sha512-NMsDEsDiYghTbeZWEGnNi4F0hSbGnsuOG+VnNvxkKg0IGDvFh7UVpM/14mnMwxRxUf9AdAVJgHPvKXf6FpMB7A==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.44.2': + resolution: {integrity: sha512-lb5bxXnxXglVq+7imxykIp5xMq+idehfl+wOgiiix0191av84OqbjUED+PRC5OA8eFJYj5xAGcpAZ0pF2MnW+A==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loongarch64-gnu@4.44.2': + resolution: {integrity: sha512-Yl5Rdpf9pIc4GW1PmkUGHdMtbx0fBLE1//SxDmuf3X0dUC57+zMepow2LK0V21661cjXdTn8hO2tXDdAWAqE5g==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-powerpc64le-gnu@4.44.2': + resolution: {integrity: sha512-03vUDH+w55s680YYryyr78jsO1RWU9ocRMaeV2vMniJJW/6HhoTBwyyiiTPVHNWLnhsnwcQ0oH3S9JSBEKuyqw==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.44.2': + resolution: {integrity: sha512-iYtAqBg5eEMG4dEfVlkqo05xMOk6y/JXIToRca2bAWuqjrJYJlx/I7+Z+4hSrsWU8GdJDFPL4ktV3dy4yBSrzg==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.44.2': + resolution: {integrity: sha512-e6vEbgaaqz2yEHqtkPXa28fFuBGmUJ0N2dOJK8YUfijejInt9gfCSA7YDdJ4nYlv67JfP3+PSWFX4IVw/xRIPg==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.44.2': + resolution: {integrity: sha512-evFOtkmVdY3udE+0QKrV5wBx7bKI0iHz5yEVx5WqDJkxp9YQefy4Mpx3RajIVcM6o7jxTvVd/qpC1IXUhGc1Mw==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.44.2': + resolution: {integrity: sha512-/bXb0bEsWMyEkIsUL2Yt5nFB5naLAwyOWMEviQfQY1x3l5WsLKgvZf66TM7UTfED6erckUVUJQ/jJ1FSpm3pRQ==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.44.2': + resolution: {integrity: sha512-3D3OB1vSSBXmkGEZR27uiMRNiwN08/RVAcBKwhUYPaiZ8bcvdeEwWPvbnXvvXHY+A/7xluzcN+kaiOFNiOZwWg==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-win32-arm64-msvc@4.44.2': + resolution: {integrity: sha512-VfU0fsMK+rwdK8mwODqYeM2hDrF2WiHaSmCBrS7gColkQft95/8tphyzv2EupVxn3iE0FI78wzffoULH1G+dkw==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.44.2': + resolution: {integrity: sha512-+qMUrkbUurpE6DVRjiJCNGZBGo9xM4Y0FXU5cjgudWqIBWbcLkjE3XprJUsOFgC6xjBClwVa9k6O3A7K3vxb5Q==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.44.2': + resolution: {integrity: sha512-3+QZROYfJ25PDcxFF66UEk8jGWigHJeecZILvkPkyQN7oc5BvFo4YEXFkOs154j3FTMp9mn9Ky8RCOwastduEA==} + cpu: [x64] + os: [win32] + + '@shikijs/core@3.7.0': + resolution: {integrity: sha512-yilc0S9HvTPyahHpcum8eonYrQtmGTU0lbtwxhA6jHv4Bm1cAdlPFRCJX4AHebkCm75aKTjjRAW+DezqD1b/cg==} + + '@shikijs/engine-javascript@3.7.0': + resolution: {integrity: sha512-0t17s03Cbv+ZcUvv+y33GtX75WBLQELgNdVghnsdhTgU3hVcWcMsoP6Lb0nDTl95ZJfbP1mVMO0p3byVh3uuzA==} + + '@shikijs/engine-oniguruma@3.7.0': + resolution: {integrity: sha512-5BxcD6LjVWsGu4xyaBC5bu8LdNgPCVBnAkWTtOCs/CZxcB22L8rcoWfv7Hh/3WooVjBZmFtyxhgvkQFedPGnFw==} + + '@shikijs/langs@3.7.0': + resolution: {integrity: sha512-1zYtdfXLr9xDKLTGy5kb7O0zDQsxXiIsw1iIBcNOO8Yi5/Y1qDbJ+0VsFoqTlzdmneO8Ij35g7QKF8kcLyznCQ==} + + '@shikijs/themes@3.7.0': + resolution: {integrity: sha512-VJx8497iZPy5zLiiCTSIaOChIcKQwR0FebwE9S3rcN0+J/GTWwQ1v/bqhTbpbY3zybPKeO8wdammqkpXc4NVjQ==} + + '@shikijs/types@3.7.0': + resolution: {integrity: sha512-MGaLeaRlSWpnP0XSAum3kP3a8vtcTsITqoEPYdt3lQG3YCdQH4DnEhodkYcNMcU0uW0RffhoD1O3e0vG5eSBBg==} + + '@shikijs/vscode-textmate@10.0.2': + resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} + + '@swc/helpers@0.5.17': + resolution: {integrity: sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==} + + '@types/debug@4.1.12': + resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} + + '@types/estree-jsx@1.0.5': + resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/fontkit@2.0.8': + resolution: {integrity: sha512-wN+8bYxIpJf+5oZdrdtaX04qUuWHcKxcDEgRS9Qm9ZClSHjzEn13SxUC+5eRM+4yXIeTYk8mTzLAWGF64847ew==} + + '@types/hast@3.0.4': + resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} + + '@types/js-yaml@4.0.9': + resolution: {integrity: sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==} + + '@types/mdast@4.0.4': + resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} + + '@types/mdx@2.0.13': + resolution: {integrity: sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw==} + + '@types/ms@2.1.0': + resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} + + '@types/nlcst@2.0.3': + resolution: {integrity: sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA==} + + '@types/node@17.0.45': + resolution: {integrity: sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==} + + '@types/node@24.0.10': + resolution: {integrity: sha512-ENHwaH+JIRTDIEEbDK6QSQntAYGtbvdDXnMXnZaZ6k13Du1dPMmprkEHIL7ok2Wl2aZevetwTAb5S+7yIF+enA==} + + '@types/sax@1.2.7': + resolution: {integrity: sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==} + + '@types/unist@2.0.11': + resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} + + '@types/unist@3.0.3': + resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + + '@ungap/structured-clone@1.3.0': + resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.15.0: + resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} + engines: {node: '>=0.4.0'} + hasBin: true + + ansi-align@3.0.1: + resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-regex@6.1.0: + resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==} + engines: {node: '>=12'} + + ansi-styles@6.2.1: + resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + engines: {node: '>=12'} + + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + + arg@5.0.2: + resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + aria-query@5.3.2: + resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} + engines: {node: '>= 0.4'} + + array-iterate@2.0.1: + resolution: {integrity: sha512-I1jXZMjAgCMmxT4qxXfPXa6SthSoE8h6gkSI9BGGNv8mP8G/v0blc+qFnZu6K42vTOiuME596QaLO0TP3Lk0xg==} + + astring@1.9.0: + resolution: {integrity: sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==} + hasBin: true + + astro-expressive-code@0.41.2: + resolution: {integrity: sha512-HN0jWTnhr7mIV/2e6uu4PPRNNo/k4UEgTLZqbp3MrHU+caCARveG2yZxaZVBmxyiVdYqW5Pd3u3n2zjnshixbw==} + peerDependencies: + astro: ^4.0.0-beta || ^5.0.0-beta || ^3.3.0 + + astro@5.12.0: + resolution: {integrity: sha512-Oov5JsMFHuUmuO+Nx6plfv3nQNK1Xl/8CgLvR8lBhZTjYnraxhuPX5COVAzbom+YLgwaDfK7KBd8zOEopRf9mg==} + engines: {node: 18.20.8 || ^20.3.0 || >=22.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0'} + hasBin: true + + axobject-query@4.1.0: + resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} + engines: {node: '>= 0.4'} + + bail@2.0.2: + resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} + + base-64@1.0.0: + resolution: {integrity: sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==} + + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + + bcp-47-match@2.0.3: + resolution: {integrity: sha512-JtTezzbAibu8G0R9op9zb3vcWZd9JF6M0xOYGPn0fNCd7wOpRB1mU2mH9T8gaBGbAAyIIVgB2G7xG0GP98zMAQ==} + + bcp-47@2.1.0: + resolution: {integrity: sha512-9IIS3UPrvIa1Ej+lVDdDwO7zLehjqsaByECw0bu2RRGP73jALm6FYbzI5gWbgHLvNdkvfXB5YrSbocZdOS0c0w==} + + blob-to-buffer@1.2.9: + resolution: {integrity: sha512-BF033y5fN6OCofD3vgHmNtwZWRcq9NLyyxyILx9hfMy1sXYy4ojFl765hJ2lP0YaN2fuxPaLO2Vzzoxy0FLFFA==} + + boolbase@1.0.0: + resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + + boxen@8.0.1: + resolution: {integrity: sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw==} + engines: {node: '>=18'} + + brotli@1.3.3: + resolution: {integrity: sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==} + + camelcase@8.0.0: + resolution: {integrity: sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==} + engines: {node: '>=16'} + + ccount@2.0.1: + resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + + chalk@5.4.1: + resolution: {integrity: sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + + character-entities-html4@2.1.0: + resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} + + character-entities-legacy@3.0.0: + resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} + + character-entities@2.0.2: + resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} + + character-reference-invalid@2.0.1: + resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==} + + chokidar@4.0.3: + resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} + engines: {node: '>= 14.16.0'} + + ci-info@4.2.0: + resolution: {integrity: sha512-cYY9mypksY8NRqgDB1XD1RiJL338v/551niynFTGkZOO2LHuB2OmOYxDIe/ttN9AHwrqdum1360G3ald0W9kCg==} + engines: {node: '>=8'} + + cli-boxes@3.0.0: + resolution: {integrity: sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==} + engines: {node: '>=10'} + + clone@2.1.2: + resolution: {integrity: sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==} + engines: {node: '>=0.8'} + + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + + collapse-white-space@2.1.0: + resolution: {integrity: sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + color-string@1.9.1: + resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} + + color@4.2.3: + resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==} + engines: {node: '>=12.5.0'} + + comma-separated-tokens@2.0.3: + resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + + common-ancestor-path@1.0.1: + resolution: {integrity: sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w==} + + cookie-es@1.2.2: + resolution: {integrity: sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg==} + + cookie@1.0.2: + resolution: {integrity: sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==} + engines: {node: '>=18'} + + cross-fetch@3.2.0: + resolution: {integrity: sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==} + + crossws@0.3.5: + resolution: {integrity: sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA==} + + css-selector-parser@3.1.3: + resolution: {integrity: sha512-gJMigczVZqYAk0hPVzx/M4Hm1D9QOtqkdQk9005TNzDIUGzo5cnHEDiKUT7jGPximL/oYb+LIitcHFQ4aKupxg==} + + css-tree@3.1.0: + resolution: {integrity: sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + + cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + + debug@4.4.1: + resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decode-named-character-reference@1.2.0: + resolution: {integrity: sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==} + + defu@6.1.4: + resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} + + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + + destr@2.0.5: + resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==} + + detect-libc@2.0.4: + resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==} + engines: {node: '>=8'} + + deterministic-object-hash@2.0.2: + resolution: {integrity: sha512-KxektNH63SrbfUyDiwXqRb1rLwKt33AmMv+5Nhsw1kqZ13SJBRTgZHtGbE+hH3a1mVW1cz+4pqSWVPAtLVXTzQ==} + engines: {node: '>=18'} + + devalue@5.1.1: + resolution: {integrity: sha512-maua5KUiapvEwiEAe+XnlZ3Rh0GD+qI1J/nb9vrJc3muPXvcF/8gXYTWF76+5DAqHyDUtOIImEuo0YKE9mshVw==} + + devlop@1.1.0: + resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + + dfa@1.2.0: + resolution: {integrity: sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q==} + + diff@5.2.0: + resolution: {integrity: sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==} + engines: {node: '>=0.3.1'} + + direction@2.0.1: + resolution: {integrity: sha512-9S6m9Sukh1cZNknO1CWAr2QAWsbKLafQiyM5gZ7VgXHeuaoUwffKN4q6NC4A/Mf9iiPlOXQEKW/Mv/mh9/3YFA==} + hasBin: true + + dlv@1.1.3: + resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} + + dset@3.1.4: + resolution: {integrity: sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==} + engines: {node: '>=4'} + + emoji-regex@10.4.0: + resolution: {integrity: sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + entities@6.0.1: + resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} + engines: {node: '>=0.12'} + + es-module-lexer@1.7.0: + resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + + esast-util-from-estree@2.0.0: + resolution: {integrity: sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ==} + + esast-util-from-js@2.0.1: + resolution: {integrity: sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw==} + + esbuild@0.25.5: + resolution: {integrity: sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==} + engines: {node: '>=18'} + hasBin: true + + escape-string-regexp@5.0.0: + resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} + engines: {node: '>=12'} + + estree-util-attach-comments@3.0.0: + resolution: {integrity: sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw==} + + estree-util-build-jsx@3.0.1: + resolution: {integrity: sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ==} + + estree-util-is-identifier-name@3.0.0: + resolution: {integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==} + + estree-util-scope@1.0.0: + resolution: {integrity: sha512-2CAASclonf+JFWBNJPndcOpA8EMJwa0Q8LUFJEKqXLW6+qBvbFZuF5gItbQOs/umBUkjviCSDCbBwU2cXbmrhQ==} + + estree-util-to-js@2.0.0: + resolution: {integrity: sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg==} + + estree-util-visit@2.0.0: + resolution: {integrity: sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==} + + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + + eventemitter3@5.0.1: + resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + + expressive-code@0.41.2: + resolution: {integrity: sha512-aLZiZaqorRtNExtGpUjK9zFH9aTpWeoTXMyLo4b4IcuXfPqtLPPxhRm/QlPb8QqIcMMXnSiGRHSFpQfX0m7HJw==} + + extend@3.0.2: + resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fdir@6.4.6: + resolution: {integrity: sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + flattie@1.1.1: + resolution: {integrity: sha512-9UbaD6XdAL97+k/n+N7JwX46K/M6Zc6KcFYskrYL8wbBV/Uyk0CTAMY0VT+qiK5PM7AIc9aTWYtq65U7T+aCNQ==} + engines: {node: '>=8'} + + fontace@0.3.0: + resolution: {integrity: sha512-czoqATrcnxgWb/nAkfyIrRp6Q8biYj7nGnL6zfhTcX+JKKpWHFBnb8uNMw/kZr7u++3Y3wYSYoZgHkCcsuBpBg==} + + fontkit@2.0.4: + resolution: {integrity: sha512-syetQadaUEDNdxdugga9CpEYVaQIxOwk7GlwZWWZ19//qW4zE5bknOKeMBDYAASwnpaSHKJITRLMF9m1fp3s6g==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + get-east-asian-width@1.3.0: + resolution: {integrity: sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==} + engines: {node: '>=18'} + + github-slugger@2.0.0: + resolution: {integrity: sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==} + + h3@1.15.3: + resolution: {integrity: sha512-z6GknHqyX0h9aQaTx22VZDf6QyZn+0Nh+Ym8O/u0SGSkyF5cuTJYKlc8MkzW3Nzf9LE1ivcpmYC3FUGpywhuUQ==} + + hast-util-embedded@3.0.0: + resolution: {integrity: sha512-naH8sld4Pe2ep03qqULEtvYr7EjrLK2QHY8KJR6RJkTUjPGObe1vnx585uzem2hGra+s1q08DZZpfgDVYRbaXA==} + + hast-util-format@1.1.0: + resolution: {integrity: sha512-yY1UDz6bC9rDvCWHpx12aIBGRG7krurX0p0Fm6pT547LwDIZZiNr8a+IHDogorAdreULSEzP82Nlv5SZkHZcjA==} + + hast-util-from-html@2.0.3: + resolution: {integrity: sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==} + + hast-util-from-parse5@8.0.3: + resolution: {integrity: sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==} + + hast-util-has-property@3.0.0: + resolution: {integrity: sha512-MNilsvEKLFpV604hwfhVStK0usFY/QmM5zX16bo7EjnAEGofr5YyI37kzopBlZJkHD4t887i+q/C8/tr5Q94cA==} + + hast-util-is-body-ok-link@3.0.1: + resolution: {integrity: sha512-0qpnzOBLztXHbHQenVB8uNuxTnm/QBFUOmdOSsEn7GnBtyY07+ENTWVFBAnXd/zEgd9/SUG3lRY7hSIBWRgGpQ==} + + hast-util-is-element@3.0.0: + resolution: {integrity: sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==} + + hast-util-minify-whitespace@1.0.1: + resolution: {integrity: sha512-L96fPOVpnclQE0xzdWb/D12VT5FabA7SnZOUMtL1DbXmYiHJMXZvFkIZfiMmTCNJHUeO2K9UYNXoVyfz+QHuOw==} + + hast-util-parse-selector@4.0.0: + resolution: {integrity: sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==} + + hast-util-phrasing@3.0.1: + resolution: {integrity: sha512-6h60VfI3uBQUxHqTyMymMZnEbNl1XmEGtOxxKYL7stY2o601COo62AWAYBQR9lZbYXYSBoxag8UpPRXK+9fqSQ==} + + hast-util-raw@9.1.0: + resolution: {integrity: sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==} + + hast-util-select@6.0.4: + resolution: {integrity: sha512-RqGS1ZgI0MwxLaKLDxjprynNzINEkRHY2i8ln4DDjgv9ZhcYVIHN9rlpiYsqtFwrgpYU361SyWDQcGNIBVu3lw==} + + hast-util-to-estree@3.1.3: + resolution: {integrity: sha512-48+B/rJWAp0jamNbAAf9M7Uf//UVqAoMmgXhBdxTDJLGKY+LRnZ99qcG+Qjl5HfMpYNzS5v4EAwVEF34LeAj7w==} + + hast-util-to-html@9.0.5: + resolution: {integrity: sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==} + + hast-util-to-jsx-runtime@2.3.6: + resolution: {integrity: sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==} + + hast-util-to-parse5@8.0.0: + resolution: {integrity: sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw==} + + hast-util-to-string@3.0.1: + resolution: {integrity: sha512-XelQVTDWvqcl3axRfI0xSeoVKzyIFPwsAGSLIsKdJKQMXDYJS4WYrBNF/8J7RdhIcFI2BOHgAifggsvsxp/3+A==} + + hast-util-to-text@4.0.2: + resolution: {integrity: sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A==} + + hast-util-whitespace@3.0.0: + resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} + + hastscript@9.0.1: + resolution: {integrity: sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==} + + html-escaper@3.0.3: + resolution: {integrity: sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==} + + html-void-elements@3.0.0: + resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} + + html-whitespace-sensitive-tag-names@3.0.1: + resolution: {integrity: sha512-q+310vW8zmymYHALr1da4HyXUQ0zgiIwIicEfotYPWGN0OJVEN/58IJ3A4GBYcEq3LGAZqKb+ugvP0GNB9CEAA==} + + http-cache-semantics@4.2.0: + resolution: {integrity: sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==} + + i18next@23.16.8: + resolution: {integrity: sha512-06r/TitrM88Mg5FdUXAKL96dJMzgqLE5dv3ryBAra4KCwD9mJ4ndOTS95ZuymIGoE+2hzfdaMak2X11/es7ZWg==} + + import-meta-resolve@4.1.0: + resolution: {integrity: sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==} + + inline-style-parser@0.2.4: + resolution: {integrity: sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==} + + iron-webcrypto@1.2.1: + resolution: {integrity: sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==} + + is-alphabetical@2.0.1: + resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==} + + is-alphanumerical@2.0.1: + resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==} + + is-arrayish@0.3.2: + resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} + + is-decimal@2.0.1: + resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==} + + is-docker@3.0.0: + resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + hasBin: true + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-hexadecimal@2.0.1: + resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==} + + is-inside-container@1.0.0: + resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} + engines: {node: '>=14.16'} + hasBin: true + + is-plain-obj@4.1.0: + resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} + engines: {node: '>=12'} + + is-wsl@3.1.0: + resolution: {integrity: sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==} + engines: {node: '>=16'} + + js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + + kleur@3.0.3: + resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} + engines: {node: '>=6'} + + kleur@4.1.5: + resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} + engines: {node: '>=6'} + + klona@2.0.6: + resolution: {integrity: sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==} + engines: {node: '>= 8'} + + longest-streak@3.1.0: + resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} + + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + + magic-string@0.30.17: + resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} + + magicast@0.3.5: + resolution: {integrity: sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==} + + markdown-extensions@2.0.0: + resolution: {integrity: sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==} + engines: {node: '>=16'} + + markdown-table@3.0.4: + resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} + + mdast-util-definitions@6.0.0: + resolution: {integrity: sha512-scTllyX6pnYNZH/AIp/0ePz6s4cZtARxImwoPJ7kS42n+MnVsI4XbnG6d4ibehRIldYMWM2LD7ImQblVhUejVQ==} + + mdast-util-directive@3.1.0: + resolution: {integrity: sha512-I3fNFt+DHmpWCYAT7quoM6lHf9wuqtI+oCOfvILnoicNIqjh5E3dEJWiXuYME2gNe8vl1iMQwyUHa7bgFmak6Q==} + + mdast-util-find-and-replace@3.0.2: + resolution: {integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==} + + mdast-util-from-markdown@2.0.2: + resolution: {integrity: sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==} + + mdast-util-gfm-autolink-literal@2.0.1: + resolution: {integrity: sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==} + + mdast-util-gfm-footnote@2.1.0: + resolution: {integrity: sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==} + + mdast-util-gfm-strikethrough@2.0.0: + resolution: {integrity: sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==} + + mdast-util-gfm-table@2.0.0: + resolution: {integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==} + + mdast-util-gfm-task-list-item@2.0.0: + resolution: {integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==} + + mdast-util-gfm@3.1.0: + resolution: {integrity: sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==} + + mdast-util-mdx-expression@2.0.1: + resolution: {integrity: sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==} + + mdast-util-mdx-jsx@3.2.0: + resolution: {integrity: sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==} + + mdast-util-mdx@3.0.0: + resolution: {integrity: sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w==} + + mdast-util-mdxjs-esm@2.0.1: + resolution: {integrity: sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==} + + mdast-util-phrasing@4.1.0: + resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==} + + mdast-util-to-hast@13.2.0: + resolution: {integrity: sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==} + + mdast-util-to-markdown@2.1.2: + resolution: {integrity: sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==} + + mdast-util-to-string@4.0.0: + resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} + + mdn-data@2.12.2: + resolution: {integrity: sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==} + + micromark-core-commonmark@2.0.3: + resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} + + micromark-extension-directive@3.0.2: + resolution: {integrity: sha512-wjcXHgk+PPdmvR58Le9d7zQYWy+vKEU9Se44p2CrCDPiLr2FMyiT4Fyb5UFKFC66wGB3kPlgD7q3TnoqPS7SZA==} + + micromark-extension-gfm-autolink-literal@2.1.0: + resolution: {integrity: sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==} + + micromark-extension-gfm-footnote@2.1.0: + resolution: {integrity: sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==} + + micromark-extension-gfm-strikethrough@2.1.0: + resolution: {integrity: sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==} + + micromark-extension-gfm-table@2.1.1: + resolution: {integrity: sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==} + + micromark-extension-gfm-tagfilter@2.0.0: + resolution: {integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==} + + micromark-extension-gfm-task-list-item@2.1.0: + resolution: {integrity: sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==} + + micromark-extension-gfm@3.0.0: + resolution: {integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==} + + micromark-extension-mdx-expression@3.0.1: + resolution: {integrity: sha512-dD/ADLJ1AeMvSAKBwO22zG22N4ybhe7kFIZ3LsDI0GlsNr2A3KYxb0LdC1u5rj4Nw+CHKY0RVdnHX8vj8ejm4Q==} + + micromark-extension-mdx-jsx@3.0.2: + resolution: {integrity: sha512-e5+q1DjMh62LZAJOnDraSSbDMvGJ8x3cbjygy2qFEi7HCeUT4BDKCvMozPozcD6WmOt6sVvYDNBKhFSz3kjOVQ==} + + micromark-extension-mdx-md@2.0.0: + resolution: {integrity: sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ==} + + micromark-extension-mdxjs-esm@3.0.0: + resolution: {integrity: sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A==} + + micromark-extension-mdxjs@3.0.0: + resolution: {integrity: sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ==} + + micromark-factory-destination@2.0.1: + resolution: {integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==} + + micromark-factory-label@2.0.1: + resolution: {integrity: sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==} + + micromark-factory-mdx-expression@2.0.3: + resolution: {integrity: sha512-kQnEtA3vzucU2BkrIa8/VaSAsP+EJ3CKOvhMuJgOEGg9KDC6OAY6nSnNDVRiVNRqj7Y4SlSzcStaH/5jge8JdQ==} + + micromark-factory-space@2.0.1: + resolution: {integrity: sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==} + + micromark-factory-title@2.0.1: + resolution: {integrity: sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==} + + micromark-factory-whitespace@2.0.1: + resolution: {integrity: sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==} + + micromark-util-character@2.1.1: + resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} + + micromark-util-chunked@2.0.1: + resolution: {integrity: sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==} + + micromark-util-classify-character@2.0.1: + resolution: {integrity: sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==} + + micromark-util-combine-extensions@2.0.1: + resolution: {integrity: sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==} + + micromark-util-decode-numeric-character-reference@2.0.2: + resolution: {integrity: sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==} + + micromark-util-decode-string@2.0.1: + resolution: {integrity: sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==} + + micromark-util-encode@2.0.1: + resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} + + micromark-util-events-to-acorn@2.0.3: + resolution: {integrity: sha512-jmsiEIiZ1n7X1Rr5k8wVExBQCg5jy4UXVADItHmNk1zkwEVhBuIUKRu3fqv+hs4nxLISi2DQGlqIOGiFxgbfHg==} + + micromark-util-html-tag-name@2.0.1: + resolution: {integrity: sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==} + + micromark-util-normalize-identifier@2.0.1: + resolution: {integrity: sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==} + + micromark-util-resolve-all@2.0.1: + resolution: {integrity: sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==} + + micromark-util-sanitize-uri@2.0.1: + resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} + + micromark-util-subtokenize@2.1.0: + resolution: {integrity: sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==} + + micromark-util-symbol@2.0.1: + resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} + + micromark-util-types@2.0.2: + resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==} + + micromark@4.0.2: + resolution: {integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==} + + mrmime@2.0.1: + resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} + engines: {node: '>=10'} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + neotraverse@0.6.18: + resolution: {integrity: sha512-Z4SmBUweYa09+o6pG+eASabEpP6QkQ70yHj351pQoEXIs8uHbaU2DWVmzBANKgflPa47A50PtB2+NgRpQvr7vA==} + engines: {node: '>= 10'} + + nlcst-to-string@4.0.0: + resolution: {integrity: sha512-YKLBCcUYKAg0FNlOBT6aI91qFmSiFKiluk655WzPF+DDMA02qIyy8uiRqI8QXtcFpEvll12LpL5MXqEmAZ+dcA==} + + node-fetch-native@1.6.6: + resolution: {integrity: sha512-8Mc2HhqPdlIfedsuZoc3yioPuzp6b+L5jRCRY1QzuWZh2EGJVQrGppC6V6cF0bLdbW0+O2YpqCA25aF/1lvipQ==} + + node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + + node-mock-http@1.0.1: + resolution: {integrity: sha512-0gJJgENizp4ghds/Ywu2FCmcRsgBTmRQzYPZm61wy+Em2sBarSka0OhQS5huLBg6od1zkNpnWMCZloQDFVvOMQ==} + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + nth-check@2.1.1: + resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + + ofetch@1.4.1: + resolution: {integrity: sha512-QZj2DfGplQAr2oj9KzceK9Hwz6Whxazmn85yYeVuS3u9XTMOGMRx0kO95MQ+vLsj/S/NwBDMMLU5hpxvI6Tklw==} + + ohash@2.0.11: + resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==} + + oniguruma-parser@0.12.1: + resolution: {integrity: sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w==} + + oniguruma-to-es@4.3.3: + resolution: {integrity: sha512-rPiZhzC3wXwE59YQMRDodUwwT9FZ9nNBwQQfsd1wfdtlKEyCdRV0avrTcSZ5xlIvGRVPd/cx6ZN45ECmS39xvg==} + + p-limit@6.2.0: + resolution: {integrity: sha512-kuUqqHNUqoIWp/c467RI4X6mmyuojY5jGutNU0wVTmEOOfcuwLqyMVoAi9MKi2Ak+5i9+nhmrK4ufZE8069kHA==} + engines: {node: '>=18'} + + p-queue@8.1.0: + resolution: {integrity: sha512-mxLDbbGIBEXTJL0zEx8JIylaj3xQ7Z/7eEVjcF9fJX4DBiH9oqe+oahYnlKKxm0Ci9TlWTyhSHgygxMxjIB2jw==} + engines: {node: '>=18'} + + p-timeout@6.1.4: + resolution: {integrity: sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg==} + engines: {node: '>=14.16'} + + package-manager-detector@1.3.0: + resolution: {integrity: sha512-ZsEbbZORsyHuO00lY1kV3/t72yp6Ysay6Pd17ZAlNGuGwmWDLCJxFpRs0IzfXfj1o4icJOkUEioexFHzyPurSQ==} + + pagefind@1.3.0: + resolution: {integrity: sha512-8KPLGT5g9s+olKMRTU9LFekLizkVIu9tes90O1/aigJ0T5LmyPqTzGJrETnSw3meSYg58YH7JTzhTTW/3z6VAw==} + hasBin: true + + pako@0.2.9: + resolution: {integrity: sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==} + + parse-entities@4.0.2: + resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==} + + parse-latin@7.0.0: + resolution: {integrity: sha512-mhHgobPPua5kZ98EF4HWiH167JWBfl4pvAIXXdbaVohtK7a6YBOy56kvhCqduqyo/f3yrHFWmqmiMg/BkBkYYQ==} + + parse5@7.3.0: + resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} + + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + picomatch@4.0.2: + resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} + engines: {node: '>=12'} + + postcss-nested@6.2.0: + resolution: {integrity: sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.2.14 + + postcss-selector-parser@6.1.2: + resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} + engines: {node: '>=4'} + + postcss@8.5.6: + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + engines: {node: ^10 || ^12 || >=14} + + prismjs@1.30.0: + resolution: {integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==} + engines: {node: '>=6'} + + prompts@2.4.2: + resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} + engines: {node: '>= 6'} + + property-information@6.5.0: + resolution: {integrity: sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==} + + property-information@7.1.0: + resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} + + radix3@1.1.2: + resolution: {integrity: sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==} + + readdirp@4.1.2: + resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} + engines: {node: '>= 14.18.0'} + + recma-build-jsx@1.0.0: + resolution: {integrity: sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew==} + + recma-jsx@1.0.0: + resolution: {integrity: sha512-5vwkv65qWwYxg+Atz95acp8DMu1JDSqdGkA2Of1j6rCreyFUE/gp15fC8MnGEuG1W68UKjM6x6+YTWIh7hZM/Q==} + + recma-parse@1.0.0: + resolution: {integrity: sha512-OYLsIGBB5Y5wjnSnQW6t3Xg7q3fQ7FWbw/vcXtORTnyaSFscOtABg+7Pnz6YZ6c27fG1/aN8CjfwoUEUIdwqWQ==} + + recma-stringify@1.0.0: + resolution: {integrity: sha512-cjwII1MdIIVloKvC9ErQ+OgAtwHBmcZ0Bg4ciz78FtbT8In39aAYbaA7zvxQ61xVMSPE8WxhLwLbhif4Js2C+g==} + + regex-recursion@6.0.2: + resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==} + + regex-utilities@2.3.0: + resolution: {integrity: sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==} + + regex@6.0.1: + resolution: {integrity: sha512-uorlqlzAKjKQZ5P+kTJr3eeJGSVroLKoHmquUj4zHWuR+hEyNqlXsSKlYYF5F4NI6nl7tWCs0apKJ0lmfsXAPA==} + + rehype-expressive-code@0.41.2: + resolution: {integrity: sha512-vHYfWO9WxAw6kHHctddOt+P4266BtyT1mrOIuxJD+1ELuvuJAa5uBIhYt0OVMyOhlvf57hzWOXJkHnMhpaHyxw==} + + rehype-format@5.0.1: + resolution: {integrity: sha512-zvmVru9uB0josBVpr946OR8ui7nJEdzZobwLOOqHb/OOD88W0Vk2SqLwoVOj0fM6IPCCO6TaV9CvQvJMWwukFQ==} + + rehype-parse@9.0.1: + resolution: {integrity: sha512-ksCzCD0Fgfh7trPDxr2rSylbwq9iYDkSn8TCDmEJ49ljEUBxDVCzCHv7QNzZOfODanX4+bWQ4WZqLCRWYLfhag==} + + rehype-raw@7.0.0: + resolution: {integrity: sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==} + + rehype-recma@1.0.0: + resolution: {integrity: sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw==} + + rehype-stringify@10.0.1: + resolution: {integrity: sha512-k9ecfXHmIPuFVI61B9DeLPN0qFHfawM6RsuX48hoqlaKSF61RskNjSm1lI8PhBEM0MRdLxVVm4WmTqJQccH9mA==} + + rehype@13.0.2: + resolution: {integrity: sha512-j31mdaRFrwFRUIlxGeuPXXKWQxet52RBQRvCmzl5eCefn/KGbomK5GMHNMsOJf55fgo3qw5tST5neDuarDYR2A==} + + remark-directive@3.0.1: + resolution: {integrity: sha512-gwglrEQEZcZYgVyG1tQuA+h58EZfq5CSULw7J90AFuCTyib1thgHPoqQ+h9iFvU6R+vnZ5oNFQR5QKgGpk741A==} + + remark-gfm@4.0.1: + resolution: {integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==} + + remark-mdx@3.1.0: + resolution: {integrity: sha512-Ngl/H3YXyBV9RcRNdlYsZujAmhsxwzxpDzpDEhFBVAGthS4GDgnctpDjgFl/ULx5UEDzqtW1cyBSNKqYYrqLBA==} + + remark-parse@11.0.0: + resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==} + + remark-rehype@11.1.2: + resolution: {integrity: sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==} + + remark-smartypants@3.0.2: + resolution: {integrity: sha512-ILTWeOriIluwEvPjv67v7Blgrcx+LZOkAUVtKI3putuhlZm84FnqDORNXPPm+HY3NdZOMhyDwZ1E+eZB/Df5dA==} + engines: {node: '>=16.0.0'} + + remark-stringify@11.0.0: + resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==} + + restructure@3.0.2: + resolution: {integrity: sha512-gSfoiOEA0VPE6Tukkrr7I0RBdE0s7H1eFCDBk05l1KIQT1UIKNc5JZy6jdyW6eYH3aR3g5b3PuL77rq0hvwtAw==} + + retext-latin@4.0.0: + resolution: {integrity: sha512-hv9woG7Fy0M9IlRQloq/N6atV82NxLGveq+3H2WOi79dtIYWN8OaxogDm77f8YnVXJL2VD3bbqowu5E3EMhBYA==} + + retext-smartypants@6.2.0: + resolution: {integrity: sha512-kk0jOU7+zGv//kfjXEBjdIryL1Acl4i9XNkHxtM7Tm5lFiCog576fjNC9hjoR7LTKQ0DsPWy09JummSsH1uqfQ==} + + retext-stringify@4.0.0: + resolution: {integrity: sha512-rtfN/0o8kL1e+78+uxPTqu1Klt0yPzKuQ2BfWwwfgIUSayyzxpM1PJzkKt4V8803uB9qSy32MvI7Xep9khTpiA==} + + retext@9.0.0: + resolution: {integrity: sha512-sbMDcpHCNjvlheSgMfEcVrZko3cDzdbe1x/e7G66dFp0Ff7Mldvi2uv6JkJQzdRcvLYE8CA8Oe8siQx8ZOgTcA==} + + rollup@4.44.2: + resolution: {integrity: sha512-PVoapzTwSEcelaWGth3uR66u7ZRo6qhPHc0f2uRO9fX6XDVNrIiGYS0Pj9+R8yIIYSD/mCx2b16Ws9itljKSPg==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + sax@1.4.1: + resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==} + + semver@7.7.2: + resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} + engines: {node: '>=10'} + hasBin: true + + sharp@0.33.5: + resolution: {integrity: sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + + sharp@0.34.2: + resolution: {integrity: sha512-lszvBmB9QURERtyKT2bNmsgxXK0ShJrL/fvqlonCo7e6xBF8nT8xU6pW+PMIbLsz0RxQk3rgH9kd8UmvOzlMJg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + + shiki@3.7.0: + resolution: {integrity: sha512-ZcI4UT9n6N2pDuM2n3Jbk0sR4Swzq43nLPgS/4h0E3B/NrFn2HKElrDtceSf8Zx/OWYOo7G1SAtBLypCp+YXqg==} + + simple-swizzle@0.2.2: + resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} + + sisteransi@1.0.5: + resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + + sitemap@8.0.0: + resolution: {integrity: sha512-+AbdxhM9kJsHtruUF39bwS/B0Fytw6Fr1o4ZAIAEqA6cke2xcoO2GleBw9Zw7nRzILVEgz7zBM5GiTJjie1G9A==} + engines: {node: '>=14.0.0', npm: '>=6.0.0'} + hasBin: true + + smol-toml@1.4.1: + resolution: {integrity: sha512-CxdwHXyYTONGHThDbq5XdwbFsuY4wlClRGejfE2NtwUtiHYsP1QtNsHb/hnj31jKYSchztJsaA8pSQoVzkfCFg==} + engines: {node: '>= 18'} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + source-map@0.7.4: + resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==} + engines: {node: '>= 8'} + + space-separated-tokens@2.0.2: + resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} + + stream-replace-string@2.0.0: + resolution: {integrity: sha512-TlnjJ1C0QrmxRNrON00JvaFFlNh5TTG00APw23j74ET7gkQpTASi6/L2fuiav8pzK715HXtUeClpBTw2NPSn6w==} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@7.2.0: + resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} + engines: {node: '>=18'} + + stringify-entities@4.0.4: + resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + engines: {node: '>=12'} + + style-to-js@1.1.17: + resolution: {integrity: sha512-xQcBGDxJb6jjFCTzvQtfiPn6YvvP2O8U1MDIPNfJQlWMYfktPy+iGsHE7cssjs7y84d9fQaK4UF3RIJaAHSoYA==} + + style-to-object@1.0.9: + resolution: {integrity: sha512-G4qppLgKu/k6FwRpHiGiKPaPTFcG3g4wNVX/Qsfu+RqQM30E7Tyu/TEgxcL9PNLF5pdRLwQdE3YKKf+KF2Dzlw==} + + tiny-inflate@1.0.3: + resolution: {integrity: sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==} + + tinyexec@0.3.2: + resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + + tinyglobby@0.2.14: + resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==} + engines: {node: '>=12.0.0'} + + tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + + trim-lines@3.0.1: + resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} + + trough@2.2.0: + resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} + + tsconfck@3.1.6: + resolution: {integrity: sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==} + engines: {node: ^18 || >=20} + hasBin: true + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + type-fest@4.41.0: + resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} + engines: {node: '>=16'} + + typescript@5.8.3: + resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} + engines: {node: '>=14.17'} + hasBin: true + + ufo@1.6.1: + resolution: {integrity: sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==} + + ultrahtml@1.6.0: + resolution: {integrity: sha512-R9fBn90VTJrqqLDwyMph+HGne8eqY1iPfYhPzZrvKpIfwkWZbcYlfpsb8B9dTvBfpy1/hqAD7Wi8EKfP9e8zdw==} + + uncrypto@0.1.3: + resolution: {integrity: sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==} + + undici-types@7.8.0: + resolution: {integrity: sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==} + + unicode-properties@1.4.1: + resolution: {integrity: sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg==} + + unicode-trie@2.0.0: + resolution: {integrity: sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==} + + unified@11.0.5: + resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} + + unifont@0.5.2: + resolution: {integrity: sha512-LzR4WUqzH9ILFvjLAUU7dK3Lnou/qd5kD+IakBtBK4S15/+x2y9VX+DcWQv6s551R6W+vzwgVS6tFg3XggGBgg==} + + unist-util-find-after@5.0.0: + resolution: {integrity: sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==} + + unist-util-is@6.0.0: + resolution: {integrity: sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==} + + unist-util-modify-children@4.0.0: + resolution: {integrity: sha512-+tdN5fGNddvsQdIzUF3Xx82CU9sMM+fA0dLgR9vOmT0oPT2jH+P1nd5lSqfCfXAw+93NhcXNY2qqvTUtE4cQkw==} + + unist-util-position-from-estree@2.0.0: + resolution: {integrity: sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ==} + + unist-util-position@5.0.0: + resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} + + unist-util-remove-position@5.0.0: + resolution: {integrity: sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==} + + unist-util-stringify-position@4.0.0: + resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} + + unist-util-visit-children@3.0.0: + resolution: {integrity: sha512-RgmdTfSBOg04sdPcpTSD1jzoNBjt9a80/ZCzp5cI9n1qPzLZWF9YdvWGN2zmTumP1HWhXKdUWexjy/Wy/lJ7tA==} + + unist-util-visit-parents@6.0.1: + resolution: {integrity: sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==} + + unist-util-visit@5.0.0: + resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==} + + unstorage@1.16.0: + resolution: {integrity: sha512-WQ37/H5A7LcRPWfYOrDa1Ys02xAbpPJq6q5GkO88FBXVSQzHd7+BjEwfRqyaSWCv9MbsJy058GWjjPjcJ16GGA==} + 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.0.3 || ^7.0.0 + '@deno/kv': '>=0.9.0' + '@netlify/blobs': ^6.5.0 || ^7.0.0 || ^8.1.0 + '@planetscale/database': ^1.19.0 + '@upstash/redis': ^1.34.3 + '@vercel/blob': '>=0.27.1' + '@vercel/kv': ^1.0.1 + 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/kv': + optional: true + aws4fetch: + optional: true + db0: + optional: true + idb-keyval: + optional: true + ioredis: + optional: true + uploadthing: + optional: true + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + vfile-location@5.0.3: + resolution: {integrity: sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==} + + vfile-message@4.0.2: + resolution: {integrity: sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==} + + vfile@6.0.3: + resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} + + vite@6.3.5: + resolution: {integrity: sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + jiti: '>=1.21.0' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vitefu@1.1.1: + resolution: {integrity: sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ==} + peerDependencies: + vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0 + peerDependenciesMeta: + vite: + optional: true + + web-namespaces@2.0.1: + resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==} + + webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + + whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + + which-pm-runs@1.1.0: + resolution: {integrity: sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA==} + engines: {node: '>=4'} + + widest-line@5.0.0: + resolution: {integrity: sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA==} + engines: {node: '>=18'} + + wrap-ansi@9.0.0: + resolution: {integrity: sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==} + engines: {node: '>=18'} + + xxhash-wasm@1.1.0: + resolution: {integrity: sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA==} + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yocto-queue@1.2.1: + resolution: {integrity: sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg==} + engines: {node: '>=12.20'} + + yocto-spinner@0.2.3: + resolution: {integrity: sha512-sqBChb33loEnkoXte1bLg45bEBsOP9N1kzQh5JZNKj/0rik4zAPTNSAVPj3uQAdc6slYJ0Ksc403G2XgxsJQFQ==} + engines: {node: '>=18.19'} + + yoctocolors@2.1.1: + resolution: {integrity: sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ==} + engines: {node: '>=18'} + + zod-to-json-schema@3.24.6: + resolution: {integrity: sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg==} + peerDependencies: + zod: ^3.24.1 + + zod-to-ts@1.2.0: + resolution: {integrity: sha512-x30XE43V+InwGpvTySRNz9kB7qFU8DlyEy7BsSTCHPH1R0QasMmHWZDCzYm6bVXtj/9NNJAZF3jW8rzFvH5OFA==} + peerDependencies: + typescript: ^4.9.4 || ^5.0.2 + zod: ^3 + + zod@3.25.74: + resolution: {integrity: sha512-J8poo92VuhKjNknViHRAIuuN6li/EwFbAC8OedzI8uxpEPGiXHGQu9wemIAioIpqgfB4SySaJhdk0mH5Y4ICBg==} + + zwitch@2.0.4: + resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} + +snapshots: + + '@astrojs/compiler@2.12.2': {} + + '@astrojs/internal-helpers@0.6.1': {} + + '@astrojs/markdown-remark@6.3.2': + dependencies: + '@astrojs/internal-helpers': 0.6.1 + '@astrojs/prism': 3.3.0 + github-slugger: 2.0.0 + hast-util-from-html: 2.0.3 + hast-util-to-text: 4.0.2 + import-meta-resolve: 4.1.0 + js-yaml: 4.1.0 + mdast-util-definitions: 6.0.0 + rehype-raw: 7.0.0 + rehype-stringify: 10.0.1 + remark-gfm: 4.0.1 + remark-parse: 11.0.0 + remark-rehype: 11.1.2 + remark-smartypants: 3.0.2 + shiki: 3.7.0 + smol-toml: 1.4.1 + unified: 11.0.5 + unist-util-remove-position: 5.0.0 + unist-util-visit: 5.0.0 + unist-util-visit-parents: 6.0.1 + vfile: 6.0.3 + transitivePeerDependencies: + - supports-color + + '@astrojs/markdown-remark@6.3.3': + dependencies: + '@astrojs/internal-helpers': 0.6.1 + '@astrojs/prism': 3.3.0 + github-slugger: 2.0.0 + hast-util-from-html: 2.0.3 + hast-util-to-text: 4.0.2 + import-meta-resolve: 4.1.0 + js-yaml: 4.1.0 + mdast-util-definitions: 6.0.0 + rehype-raw: 7.0.0 + rehype-stringify: 10.0.1 + remark-gfm: 4.0.1 + remark-parse: 11.0.0 + remark-rehype: 11.1.2 + remark-smartypants: 3.0.2 + shiki: 3.7.0 + smol-toml: 1.4.1 + unified: 11.0.5 + unist-util-remove-position: 5.0.0 + unist-util-visit: 5.0.0 + unist-util-visit-parents: 6.0.1 + vfile: 6.0.3 + transitivePeerDependencies: + - supports-color + + '@astrojs/mdx@4.3.0(astro@5.12.0(@types/node@24.0.10)(rollup@4.44.2)(typescript@5.8.3))': + dependencies: + '@astrojs/markdown-remark': 6.3.2 + '@mdx-js/mdx': 3.1.0(acorn@8.15.0) + acorn: 8.15.0 + astro: 5.12.0(@types/node@24.0.10)(rollup@4.44.2)(typescript@5.8.3) + es-module-lexer: 1.7.0 + estree-util-visit: 2.0.0 + hast-util-to-html: 9.0.5 + kleur: 4.1.5 + rehype-raw: 7.0.0 + remark-gfm: 4.0.1 + remark-smartypants: 3.0.2 + source-map: 0.7.4 + unist-util-visit: 5.0.0 + vfile: 6.0.3 + transitivePeerDependencies: + - supports-color + + '@astrojs/prism@3.3.0': + dependencies: + prismjs: 1.30.0 + + '@astrojs/sitemap@3.4.1': + dependencies: + sitemap: 8.0.0 + stream-replace-string: 2.0.0 + zod: 3.25.74 + + '@astrojs/starlight@0.35.1(astro@5.12.0(@types/node@24.0.10)(rollup@4.44.2)(typescript@5.8.3))': + dependencies: + '@astrojs/markdown-remark': 6.3.2 + '@astrojs/mdx': 4.3.0(astro@5.12.0(@types/node@24.0.10)(rollup@4.44.2)(typescript@5.8.3)) + '@astrojs/sitemap': 3.4.1 + '@pagefind/default-ui': 1.3.0 + '@types/hast': 3.0.4 + '@types/js-yaml': 4.0.9 + '@types/mdast': 4.0.4 + astro: 5.12.0(@types/node@24.0.10)(rollup@4.44.2)(typescript@5.8.3) + astro-expressive-code: 0.41.2(astro@5.12.0(@types/node@24.0.10)(rollup@4.44.2)(typescript@5.8.3)) + bcp-47: 2.1.0 + hast-util-from-html: 2.0.3 + hast-util-select: 6.0.4 + hast-util-to-string: 3.0.1 + hastscript: 9.0.1 + i18next: 23.16.8 + js-yaml: 4.1.0 + klona: 2.0.6 + mdast-util-directive: 3.1.0 + mdast-util-to-markdown: 2.1.2 + mdast-util-to-string: 4.0.0 + pagefind: 1.3.0 + rehype: 13.0.2 + rehype-format: 5.0.1 + remark-directive: 3.0.1 + ultrahtml: 1.6.0 + unified: 11.0.5 + unist-util-visit: 5.0.0 + vfile: 6.0.3 + transitivePeerDependencies: + - supports-color + + '@astrojs/telemetry@3.3.0': + dependencies: + ci-info: 4.2.0 + debug: 4.4.1 + dlv: 1.1.3 + dset: 3.1.4 + is-docker: 3.0.0 + is-wsl: 3.1.0 + which-pm-runs: 1.1.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.27.1': {} + + '@babel/parser@7.28.0': + dependencies: + '@babel/types': 7.28.0 + + '@babel/runtime@7.27.6': {} + + '@babel/types@7.28.0': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + + '@capsizecss/unpack@2.4.0': + dependencies: + blob-to-buffer: 1.2.9 + cross-fetch: 3.2.0 + fontkit: 2.0.4 + transitivePeerDependencies: + - encoding + + '@ctrl/tinycolor@4.1.0': {} + + '@emnapi/runtime@1.4.3': + dependencies: + tslib: 2.8.1 + optional: true + + '@esbuild/aix-ppc64@0.25.5': + optional: true + + '@esbuild/android-arm64@0.25.5': + optional: true + + '@esbuild/android-arm@0.25.5': + optional: true + + '@esbuild/android-x64@0.25.5': + optional: true + + '@esbuild/darwin-arm64@0.25.5': + optional: true + + '@esbuild/darwin-x64@0.25.5': + optional: true + + '@esbuild/freebsd-arm64@0.25.5': + optional: true + + '@esbuild/freebsd-x64@0.25.5': + optional: true + + '@esbuild/linux-arm64@0.25.5': + optional: true + + '@esbuild/linux-arm@0.25.5': + optional: true + + '@esbuild/linux-ia32@0.25.5': + optional: true + + '@esbuild/linux-loong64@0.25.5': + optional: true + + '@esbuild/linux-mips64el@0.25.5': + optional: true + + '@esbuild/linux-ppc64@0.25.5': + optional: true + + '@esbuild/linux-riscv64@0.25.5': + optional: true + + '@esbuild/linux-s390x@0.25.5': + optional: true + + '@esbuild/linux-x64@0.25.5': + optional: true + + '@esbuild/netbsd-arm64@0.25.5': + optional: true + + '@esbuild/netbsd-x64@0.25.5': + optional: true + + '@esbuild/openbsd-arm64@0.25.5': + optional: true + + '@esbuild/openbsd-x64@0.25.5': + optional: true + + '@esbuild/sunos-x64@0.25.5': + optional: true + + '@esbuild/win32-arm64@0.25.5': + optional: true + + '@esbuild/win32-ia32@0.25.5': + optional: true + + '@esbuild/win32-x64@0.25.5': + optional: true + + '@expressive-code/core@0.41.2': + dependencies: + '@ctrl/tinycolor': 4.1.0 + hast-util-select: 6.0.4 + hast-util-to-html: 9.0.5 + hast-util-to-text: 4.0.2 + hastscript: 9.0.1 + postcss: 8.5.6 + postcss-nested: 6.2.0(postcss@8.5.6) + unist-util-visit: 5.0.0 + unist-util-visit-parents: 6.0.1 + + '@expressive-code/plugin-frames@0.41.2': + dependencies: + '@expressive-code/core': 0.41.2 + + '@expressive-code/plugin-shiki@0.41.2': + dependencies: + '@expressive-code/core': 0.41.2 + shiki: 3.7.0 + + '@expressive-code/plugin-text-markers@0.41.2': + dependencies: + '@expressive-code/core': 0.41.2 + + '@img/sharp-darwin-arm64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.0.4 + optional: true + + '@img/sharp-darwin-arm64@0.34.2': + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.1.0 + optional: true + + '@img/sharp-darwin-x64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.0.4 + optional: true + + '@img/sharp-darwin-x64@0.34.2': + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.1.0 + optional: true + + '@img/sharp-libvips-darwin-arm64@1.0.4': + optional: true + + '@img/sharp-libvips-darwin-arm64@1.1.0': + optional: true + + '@img/sharp-libvips-darwin-x64@1.0.4': + optional: true + + '@img/sharp-libvips-darwin-x64@1.1.0': + optional: true + + '@img/sharp-libvips-linux-arm64@1.0.4': + optional: true + + '@img/sharp-libvips-linux-arm64@1.1.0': + optional: true + + '@img/sharp-libvips-linux-arm@1.0.5': + optional: true + + '@img/sharp-libvips-linux-arm@1.1.0': + optional: true + + '@img/sharp-libvips-linux-ppc64@1.1.0': + optional: true + + '@img/sharp-libvips-linux-s390x@1.0.4': + optional: true + + '@img/sharp-libvips-linux-s390x@1.1.0': + optional: true + + '@img/sharp-libvips-linux-x64@1.0.4': + optional: true + + '@img/sharp-libvips-linux-x64@1.1.0': + optional: true + + '@img/sharp-libvips-linuxmusl-arm64@1.0.4': + optional: true + + '@img/sharp-libvips-linuxmusl-arm64@1.1.0': + optional: true + + '@img/sharp-libvips-linuxmusl-x64@1.0.4': + optional: true + + '@img/sharp-libvips-linuxmusl-x64@1.1.0': + optional: true + + '@img/sharp-linux-arm64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.0.4 + optional: true + + '@img/sharp-linux-arm64@0.34.2': + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.1.0 + optional: true + + '@img/sharp-linux-arm@0.33.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.0.5 + optional: true + + '@img/sharp-linux-arm@0.34.2': + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.1.0 + optional: true + + '@img/sharp-linux-s390x@0.33.5': + optionalDependencies: + '@img/sharp-libvips-linux-s390x': 1.0.4 + optional: true + + '@img/sharp-linux-s390x@0.34.2': + optionalDependencies: + '@img/sharp-libvips-linux-s390x': 1.1.0 + optional: true + + '@img/sharp-linux-x64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.0.4 + optional: true + + '@img/sharp-linux-x64@0.34.2': + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.1.0 + optional: true + + '@img/sharp-linuxmusl-arm64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.0.4 + optional: true + + '@img/sharp-linuxmusl-arm64@0.34.2': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.1.0 + optional: true + + '@img/sharp-linuxmusl-x64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.0.4 + optional: true + + '@img/sharp-linuxmusl-x64@0.34.2': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.1.0 + optional: true + + '@img/sharp-wasm32@0.33.5': + dependencies: + '@emnapi/runtime': 1.4.3 + optional: true + + '@img/sharp-wasm32@0.34.2': + dependencies: + '@emnapi/runtime': 1.4.3 + optional: true + + '@img/sharp-win32-arm64@0.34.2': + optional: true + + '@img/sharp-win32-ia32@0.33.5': + optional: true + + '@img/sharp-win32-ia32@0.34.2': + optional: true + + '@img/sharp-win32-x64@0.33.5': + optional: true + + '@img/sharp-win32-x64@0.34.2': + optional: true + + '@jridgewell/sourcemap-codec@1.5.4': {} + + '@mdx-js/mdx@3.1.0(acorn@8.15.0)': + dependencies: + '@types/estree': 1.0.8 + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdx': 2.0.13 + collapse-white-space: 2.1.0 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + estree-util-scope: 1.0.0 + estree-walker: 3.0.3 + hast-util-to-jsx-runtime: 2.3.6 + markdown-extensions: 2.0.0 + recma-build-jsx: 1.0.0 + recma-jsx: 1.0.0(acorn@8.15.0) + recma-stringify: 1.0.0 + rehype-recma: 1.0.0 + remark-mdx: 3.1.0 + remark-parse: 11.0.0 + remark-rehype: 11.1.2 + source-map: 0.7.4 + unified: 11.0.5 + unist-util-position-from-estree: 2.0.0 + unist-util-stringify-position: 4.0.0 + unist-util-visit: 5.0.0 + vfile: 6.0.3 + transitivePeerDependencies: + - acorn + - supports-color + + '@oslojs/encoding@1.1.0': {} + + '@pagefind/darwin-arm64@1.3.0': + optional: true + + '@pagefind/darwin-x64@1.3.0': + optional: true + + '@pagefind/default-ui@1.3.0': {} + + '@pagefind/linux-arm64@1.3.0': + optional: true + + '@pagefind/linux-x64@1.3.0': + optional: true + + '@pagefind/windows-x64@1.3.0': + optional: true + + '@rollup/pluginutils@5.2.0(rollup@4.44.2)': + dependencies: + '@types/estree': 1.0.8 + estree-walker: 2.0.2 + picomatch: 4.0.2 + optionalDependencies: + rollup: 4.44.2 + + '@rollup/rollup-android-arm-eabi@4.44.2': + optional: true + + '@rollup/rollup-android-arm64@4.44.2': + optional: true + + '@rollup/rollup-darwin-arm64@4.44.2': + optional: true + + '@rollup/rollup-darwin-x64@4.44.2': + optional: true + + '@rollup/rollup-freebsd-arm64@4.44.2': + optional: true + + '@rollup/rollup-freebsd-x64@4.44.2': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.44.2': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.44.2': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.44.2': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.44.2': + optional: true + + '@rollup/rollup-linux-loongarch64-gnu@4.44.2': + optional: true + + '@rollup/rollup-linux-powerpc64le-gnu@4.44.2': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.44.2': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.44.2': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.44.2': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.44.2': + optional: true + + '@rollup/rollup-linux-x64-musl@4.44.2': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.44.2': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.44.2': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.44.2': + optional: true + + '@shikijs/core@3.7.0': + dependencies: + '@shikijs/types': 3.7.0 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + hast-util-to-html: 9.0.5 + + '@shikijs/engine-javascript@3.7.0': + dependencies: + '@shikijs/types': 3.7.0 + '@shikijs/vscode-textmate': 10.0.2 + oniguruma-to-es: 4.3.3 + + '@shikijs/engine-oniguruma@3.7.0': + dependencies: + '@shikijs/types': 3.7.0 + '@shikijs/vscode-textmate': 10.0.2 + + '@shikijs/langs@3.7.0': + dependencies: + '@shikijs/types': 3.7.0 + + '@shikijs/themes@3.7.0': + dependencies: + '@shikijs/types': 3.7.0 + + '@shikijs/types@3.7.0': + dependencies: + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + + '@shikijs/vscode-textmate@10.0.2': {} + + '@swc/helpers@0.5.17': + dependencies: + tslib: 2.8.1 + + '@types/debug@4.1.12': + dependencies: + '@types/ms': 2.1.0 + + '@types/estree-jsx@1.0.5': + dependencies: + '@types/estree': 1.0.8 + + '@types/estree@1.0.8': {} + + '@types/fontkit@2.0.8': + dependencies: + '@types/node': 24.0.10 + + '@types/hast@3.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/js-yaml@4.0.9': {} + + '@types/mdast@4.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/mdx@2.0.13': {} + + '@types/ms@2.1.0': {} + + '@types/nlcst@2.0.3': + dependencies: + '@types/unist': 3.0.3 + + '@types/node@17.0.45': {} + + '@types/node@24.0.10': + dependencies: + undici-types: 7.8.0 + + '@types/sax@1.2.7': + dependencies: + '@types/node': 24.0.10 + + '@types/unist@2.0.11': {} + + '@types/unist@3.0.3': {} + + '@ungap/structured-clone@1.3.0': {} + + acorn-jsx@5.3.2(acorn@8.15.0): + dependencies: + acorn: 8.15.0 + + acorn@8.15.0: {} + + ansi-align@3.0.1: + dependencies: + string-width: 4.2.3 + + ansi-regex@5.0.1: {} + + ansi-regex@6.1.0: {} + + ansi-styles@6.2.1: {} + + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + + arg@5.0.2: {} + + argparse@2.0.1: {} + + aria-query@5.3.2: {} + + array-iterate@2.0.1: {} + + astring@1.9.0: {} + + astro-expressive-code@0.41.2(astro@5.12.0(@types/node@24.0.10)(rollup@4.44.2)(typescript@5.8.3)): + dependencies: + astro: 5.12.0(@types/node@24.0.10)(rollup@4.44.2)(typescript@5.8.3) + rehype-expressive-code: 0.41.2 + + astro@5.12.0(@types/node@24.0.10)(rollup@4.44.2)(typescript@5.8.3): + dependencies: + '@astrojs/compiler': 2.12.2 + '@astrojs/internal-helpers': 0.6.1 + '@astrojs/markdown-remark': 6.3.3 + '@astrojs/telemetry': 3.3.0 + '@capsizecss/unpack': 2.4.0 + '@oslojs/encoding': 1.1.0 + '@rollup/pluginutils': 5.2.0(rollup@4.44.2) + acorn: 8.15.0 + aria-query: 5.3.2 + axobject-query: 4.1.0 + boxen: 8.0.1 + ci-info: 4.2.0 + clsx: 2.1.1 + common-ancestor-path: 1.0.1 + cookie: 1.0.2 + cssesc: 3.0.0 + debug: 4.4.1 + deterministic-object-hash: 2.0.2 + devalue: 5.1.1 + diff: 5.2.0 + dlv: 1.1.3 + dset: 3.1.4 + es-module-lexer: 1.7.0 + esbuild: 0.25.5 + estree-walker: 3.0.3 + flattie: 1.1.1 + fontace: 0.3.0 + github-slugger: 2.0.0 + html-escaper: 3.0.3 + http-cache-semantics: 4.2.0 + import-meta-resolve: 4.1.0 + js-yaml: 4.1.0 + kleur: 4.1.5 + magic-string: 0.30.17 + magicast: 0.3.5 + mrmime: 2.0.1 + neotraverse: 0.6.18 + p-limit: 6.2.0 + p-queue: 8.1.0 + package-manager-detector: 1.3.0 + picomatch: 4.0.2 + prompts: 2.4.2 + rehype: 13.0.2 + semver: 7.7.2 + shiki: 3.7.0 + smol-toml: 1.4.1 + tinyexec: 0.3.2 + tinyglobby: 0.2.14 + tsconfck: 3.1.6(typescript@5.8.3) + ultrahtml: 1.6.0 + unifont: 0.5.2 + unist-util-visit: 5.0.0 + unstorage: 1.16.0 + vfile: 6.0.3 + vite: 6.3.5(@types/node@24.0.10) + vitefu: 1.1.1(vite@6.3.5(@types/node@24.0.10)) + xxhash-wasm: 1.1.0 + yargs-parser: 21.1.1 + yocto-spinner: 0.2.3 + zod: 3.25.74 + zod-to-json-schema: 3.24.6(zod@3.25.74) + zod-to-ts: 1.2.0(typescript@5.8.3)(zod@3.25.74) + optionalDependencies: + sharp: 0.33.5 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@types/node' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/kv' + - aws4fetch + - db0 + - encoding + - idb-keyval + - ioredis + - jiti + - less + - lightningcss + - rollup + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - typescript + - uploadthing + - yaml + + axobject-query@4.1.0: {} + + bail@2.0.2: {} + + base-64@1.0.0: {} + + base64-js@1.5.1: {} + + bcp-47-match@2.0.3: {} + + bcp-47@2.1.0: + dependencies: + is-alphabetical: 2.0.1 + is-alphanumerical: 2.0.1 + is-decimal: 2.0.1 + + blob-to-buffer@1.2.9: {} + + boolbase@1.0.0: {} + + boxen@8.0.1: + dependencies: + ansi-align: 3.0.1 + camelcase: 8.0.0 + chalk: 5.4.1 + cli-boxes: 3.0.0 + string-width: 7.2.0 + type-fest: 4.41.0 + widest-line: 5.0.0 + wrap-ansi: 9.0.0 + + brotli@1.3.3: + dependencies: + base64-js: 1.5.1 + + camelcase@8.0.0: {} + + ccount@2.0.1: {} + + chalk@5.4.1: {} + + character-entities-html4@2.1.0: {} + + character-entities-legacy@3.0.0: {} + + character-entities@2.0.2: {} + + character-reference-invalid@2.0.1: {} + + chokidar@4.0.3: + dependencies: + readdirp: 4.1.2 + + ci-info@4.2.0: {} + + cli-boxes@3.0.0: {} + + clone@2.1.2: {} + + clsx@2.1.1: {} + + collapse-white-space@2.1.0: {} + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + color-string@1.9.1: + dependencies: + color-name: 1.1.4 + simple-swizzle: 0.2.2 + + color@4.2.3: + dependencies: + color-convert: 2.0.1 + color-string: 1.9.1 + + comma-separated-tokens@2.0.3: {} + + common-ancestor-path@1.0.1: {} + + cookie-es@1.2.2: {} + + cookie@1.0.2: {} + + cross-fetch@3.2.0: + dependencies: + node-fetch: 2.7.0 + transitivePeerDependencies: + - encoding + + crossws@0.3.5: + dependencies: + uncrypto: 0.1.3 + + css-selector-parser@3.1.3: {} + + css-tree@3.1.0: + dependencies: + mdn-data: 2.12.2 + source-map-js: 1.2.1 + + cssesc@3.0.0: {} + + debug@4.4.1: + dependencies: + ms: 2.1.3 + + decode-named-character-reference@1.2.0: + dependencies: + character-entities: 2.0.2 + + defu@6.1.4: {} + + dequal@2.0.3: {} + + destr@2.0.5: {} + + detect-libc@2.0.4: {} + + deterministic-object-hash@2.0.2: + dependencies: + base-64: 1.0.0 + + devalue@5.1.1: {} + + devlop@1.1.0: + dependencies: + dequal: 2.0.3 + + dfa@1.2.0: {} + + diff@5.2.0: {} + + direction@2.0.1: {} + + dlv@1.1.3: {} + + dset@3.1.4: {} + + emoji-regex@10.4.0: {} + + emoji-regex@8.0.0: {} + + entities@6.0.1: {} + + es-module-lexer@1.7.0: {} + + esast-util-from-estree@2.0.0: + dependencies: + '@types/estree-jsx': 1.0.5 + devlop: 1.1.0 + estree-util-visit: 2.0.0 + unist-util-position-from-estree: 2.0.0 + + esast-util-from-js@2.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + acorn: 8.15.0 + esast-util-from-estree: 2.0.0 + vfile-message: 4.0.2 + + esbuild@0.25.5: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.5 + '@esbuild/android-arm': 0.25.5 + '@esbuild/android-arm64': 0.25.5 + '@esbuild/android-x64': 0.25.5 + '@esbuild/darwin-arm64': 0.25.5 + '@esbuild/darwin-x64': 0.25.5 + '@esbuild/freebsd-arm64': 0.25.5 + '@esbuild/freebsd-x64': 0.25.5 + '@esbuild/linux-arm': 0.25.5 + '@esbuild/linux-arm64': 0.25.5 + '@esbuild/linux-ia32': 0.25.5 + '@esbuild/linux-loong64': 0.25.5 + '@esbuild/linux-mips64el': 0.25.5 + '@esbuild/linux-ppc64': 0.25.5 + '@esbuild/linux-riscv64': 0.25.5 + '@esbuild/linux-s390x': 0.25.5 + '@esbuild/linux-x64': 0.25.5 + '@esbuild/netbsd-arm64': 0.25.5 + '@esbuild/netbsd-x64': 0.25.5 + '@esbuild/openbsd-arm64': 0.25.5 + '@esbuild/openbsd-x64': 0.25.5 + '@esbuild/sunos-x64': 0.25.5 + '@esbuild/win32-arm64': 0.25.5 + '@esbuild/win32-ia32': 0.25.5 + '@esbuild/win32-x64': 0.25.5 + + escape-string-regexp@5.0.0: {} + + estree-util-attach-comments@3.0.0: + dependencies: + '@types/estree': 1.0.8 + + estree-util-build-jsx@3.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + estree-walker: 3.0.3 + + estree-util-is-identifier-name@3.0.0: {} + + estree-util-scope@1.0.0: + dependencies: + '@types/estree': 1.0.8 + devlop: 1.1.0 + + estree-util-to-js@2.0.0: + dependencies: + '@types/estree-jsx': 1.0.5 + astring: 1.9.0 + source-map: 0.7.4 + + estree-util-visit@2.0.0: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/unist': 3.0.3 + + estree-walker@2.0.2: {} + + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.8 + + eventemitter3@5.0.1: {} + + expressive-code@0.41.2: + dependencies: + '@expressive-code/core': 0.41.2 + '@expressive-code/plugin-frames': 0.41.2 + '@expressive-code/plugin-shiki': 0.41.2 + '@expressive-code/plugin-text-markers': 0.41.2 + + extend@3.0.2: {} + + fast-deep-equal@3.1.3: {} + + fdir@6.4.6(picomatch@4.0.2): + optionalDependencies: + picomatch: 4.0.2 + + flattie@1.1.1: {} + + fontace@0.3.0: + dependencies: + '@types/fontkit': 2.0.8 + fontkit: 2.0.4 + + fontkit@2.0.4: + dependencies: + '@swc/helpers': 0.5.17 + brotli: 1.3.3 + clone: 2.1.2 + dfa: 1.2.0 + fast-deep-equal: 3.1.3 + restructure: 3.0.2 + tiny-inflate: 1.0.3 + unicode-properties: 1.4.1 + unicode-trie: 2.0.0 + + fsevents@2.3.3: + optional: true + + get-east-asian-width@1.3.0: {} + + github-slugger@2.0.0: {} + + h3@1.15.3: + dependencies: + cookie-es: 1.2.2 + crossws: 0.3.5 + defu: 6.1.4 + destr: 2.0.5 + iron-webcrypto: 1.2.1 + node-mock-http: 1.0.1 + radix3: 1.1.2 + ufo: 1.6.1 + uncrypto: 0.1.3 + + hast-util-embedded@3.0.0: + dependencies: + '@types/hast': 3.0.4 + hast-util-is-element: 3.0.0 + + hast-util-format@1.1.0: + dependencies: + '@types/hast': 3.0.4 + hast-util-embedded: 3.0.0 + hast-util-minify-whitespace: 1.0.1 + hast-util-phrasing: 3.0.1 + hast-util-whitespace: 3.0.0 + html-whitespace-sensitive-tag-names: 3.0.1 + unist-util-visit-parents: 6.0.1 + + hast-util-from-html@2.0.3: + dependencies: + '@types/hast': 3.0.4 + devlop: 1.1.0 + hast-util-from-parse5: 8.0.3 + parse5: 7.3.0 + vfile: 6.0.3 + vfile-message: 4.0.2 + + hast-util-from-parse5@8.0.3: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + devlop: 1.1.0 + hastscript: 9.0.1 + property-information: 7.1.0 + vfile: 6.0.3 + vfile-location: 5.0.3 + web-namespaces: 2.0.1 + + hast-util-has-property@3.0.0: + dependencies: + '@types/hast': 3.0.4 + + hast-util-is-body-ok-link@3.0.1: + dependencies: + '@types/hast': 3.0.4 + + hast-util-is-element@3.0.0: + dependencies: + '@types/hast': 3.0.4 + + hast-util-minify-whitespace@1.0.1: + dependencies: + '@types/hast': 3.0.4 + hast-util-embedded: 3.0.0 + hast-util-is-element: 3.0.0 + hast-util-whitespace: 3.0.0 + unist-util-is: 6.0.0 + + hast-util-parse-selector@4.0.0: + dependencies: + '@types/hast': 3.0.4 + + hast-util-phrasing@3.0.1: + dependencies: + '@types/hast': 3.0.4 + hast-util-embedded: 3.0.0 + hast-util-has-property: 3.0.0 + hast-util-is-body-ok-link: 3.0.1 + hast-util-is-element: 3.0.0 + + hast-util-raw@9.1.0: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + '@ungap/structured-clone': 1.3.0 + hast-util-from-parse5: 8.0.3 + hast-util-to-parse5: 8.0.0 + html-void-elements: 3.0.0 + mdast-util-to-hast: 13.2.0 + parse5: 7.3.0 + unist-util-position: 5.0.0 + unist-util-visit: 5.0.0 + vfile: 6.0.3 + web-namespaces: 2.0.1 + zwitch: 2.0.4 + + hast-util-select@6.0.4: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + bcp-47-match: 2.0.3 + comma-separated-tokens: 2.0.3 + css-selector-parser: 3.1.3 + devlop: 1.1.0 + direction: 2.0.1 + hast-util-has-property: 3.0.0 + hast-util-to-string: 3.0.1 + hast-util-whitespace: 3.0.0 + nth-check: 2.1.1 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + unist-util-visit: 5.0.0 + zwitch: 2.0.4 + + hast-util-to-estree@3.1.3: + dependencies: + '@types/estree': 1.0.8 + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + comma-separated-tokens: 2.0.3 + devlop: 1.1.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.1 + mdast-util-mdx-jsx: 3.2.0 + mdast-util-mdxjs-esm: 2.0.1 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + style-to-js: 1.1.17 + unist-util-position: 5.0.0 + zwitch: 2.0.4 + transitivePeerDependencies: + - supports-color + + hast-util-to-html@9.0.5: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + ccount: 2.0.1 + comma-separated-tokens: 2.0.3 + hast-util-whitespace: 3.0.0 + html-void-elements: 3.0.0 + mdast-util-to-hast: 13.2.0 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + stringify-entities: 4.0.4 + zwitch: 2.0.4 + + hast-util-to-jsx-runtime@2.3.6: + dependencies: + '@types/estree': 1.0.8 + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + comma-separated-tokens: 2.0.3 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + hast-util-whitespace: 3.0.0 + mdast-util-mdx-expression: 2.0.1 + mdast-util-mdx-jsx: 3.2.0 + mdast-util-mdxjs-esm: 2.0.1 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + style-to-js: 1.1.17 + unist-util-position: 5.0.0 + vfile-message: 4.0.2 + transitivePeerDependencies: + - supports-color + + hast-util-to-parse5@8.0.0: + dependencies: + '@types/hast': 3.0.4 + comma-separated-tokens: 2.0.3 + devlop: 1.1.0 + property-information: 6.5.0 + space-separated-tokens: 2.0.2 + web-namespaces: 2.0.1 + zwitch: 2.0.4 + + hast-util-to-string@3.0.1: + dependencies: + '@types/hast': 3.0.4 + + hast-util-to-text@4.0.2: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + hast-util-is-element: 3.0.0 + unist-util-find-after: 5.0.0 + + hast-util-whitespace@3.0.0: + dependencies: + '@types/hast': 3.0.4 + + hastscript@9.0.1: + dependencies: + '@types/hast': 3.0.4 + comma-separated-tokens: 2.0.3 + hast-util-parse-selector: 4.0.0 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + + html-escaper@3.0.3: {} + + html-void-elements@3.0.0: {} + + html-whitespace-sensitive-tag-names@3.0.1: {} + + http-cache-semantics@4.2.0: {} + + i18next@23.16.8: + dependencies: + '@babel/runtime': 7.27.6 + + import-meta-resolve@4.1.0: {} + + inline-style-parser@0.2.4: {} + + iron-webcrypto@1.2.1: {} + + is-alphabetical@2.0.1: {} + + is-alphanumerical@2.0.1: + dependencies: + is-alphabetical: 2.0.1 + is-decimal: 2.0.1 + + is-arrayish@0.3.2: {} + + is-decimal@2.0.1: {} + + is-docker@3.0.0: {} + + is-fullwidth-code-point@3.0.0: {} + + is-hexadecimal@2.0.1: {} + + is-inside-container@1.0.0: + dependencies: + is-docker: 3.0.0 + + is-plain-obj@4.1.0: {} + + is-wsl@3.1.0: + dependencies: + is-inside-container: 1.0.0 + + js-yaml@4.1.0: + dependencies: + argparse: 2.0.1 + + kleur@3.0.3: {} + + kleur@4.1.5: {} + + klona@2.0.6: {} + + longest-streak@3.1.0: {} + + lru-cache@10.4.3: {} + + magic-string@0.30.17: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.4 + + magicast@0.3.5: + dependencies: + '@babel/parser': 7.28.0 + '@babel/types': 7.28.0 + source-map-js: 1.2.1 + + markdown-extensions@2.0.0: {} + + markdown-table@3.0.4: {} + + mdast-util-definitions@6.0.0: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + unist-util-visit: 5.0.0 + + mdast-util-directive@3.1.0: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + parse-entities: 4.0.2 + stringify-entities: 4.0.4 + unist-util-visit-parents: 6.0.1 + transitivePeerDependencies: + - supports-color + + mdast-util-find-and-replace@3.0.2: + dependencies: + '@types/mdast': 4.0.4 + escape-string-regexp: 5.0.0 + unist-util-is: 6.0.0 + unist-util-visit-parents: 6.0.1 + + mdast-util-from-markdown@2.0.2: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + decode-named-character-reference: 1.2.0 + devlop: 1.1.0 + mdast-util-to-string: 4.0.0 + micromark: 4.0.2 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-decode-string: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + unist-util-stringify-position: 4.0.0 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-autolink-literal@2.0.1: + dependencies: + '@types/mdast': 4.0.4 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-find-and-replace: 3.0.2 + micromark-util-character: 2.1.1 + + mdast-util-gfm-footnote@2.1.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + micromark-util-normalize-identifier: 2.0.1 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-strikethrough@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-table@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + markdown-table: 3.0.4 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-task-list-item@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm@3.1.0: + dependencies: + mdast-util-from-markdown: 2.0.2 + mdast-util-gfm-autolink-literal: 2.0.1 + mdast-util-gfm-footnote: 2.1.0 + mdast-util-gfm-strikethrough: 2.0.0 + mdast-util-gfm-table: 2.0.0 + mdast-util-gfm-task-list-item: 2.0.0 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx-expression@2.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx-jsx@3.2.0: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + parse-entities: 4.0.2 + stringify-entities: 4.0.4 + unist-util-stringify-position: 4.0.0 + vfile-message: 4.0.2 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx@3.0.0: + dependencies: + mdast-util-from-markdown: 2.0.2 + mdast-util-mdx-expression: 2.0.1 + mdast-util-mdx-jsx: 3.2.0 + mdast-util-mdxjs-esm: 2.0.1 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-mdxjs-esm@2.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-phrasing@4.1.0: + dependencies: + '@types/mdast': 4.0.4 + unist-util-is: 6.0.0 + + mdast-util-to-hast@13.2.0: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@ungap/structured-clone': 1.3.0 + devlop: 1.1.0 + micromark-util-sanitize-uri: 2.0.1 + trim-lines: 3.0.1 + unist-util-position: 5.0.0 + unist-util-visit: 5.0.0 + vfile: 6.0.3 + + mdast-util-to-markdown@2.1.2: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + longest-streak: 3.1.0 + mdast-util-phrasing: 4.1.0 + mdast-util-to-string: 4.0.0 + micromark-util-classify-character: 2.0.1 + micromark-util-decode-string: 2.0.1 + unist-util-visit: 5.0.0 + zwitch: 2.0.4 + + mdast-util-to-string@4.0.0: + dependencies: + '@types/mdast': 4.0.4 + + mdn-data@2.12.2: {} + + micromark-core-commonmark@2.0.3: + dependencies: + decode-named-character-reference: 1.2.0 + devlop: 1.1.0 + micromark-factory-destination: 2.0.1 + micromark-factory-label: 2.0.1 + micromark-factory-space: 2.0.1 + micromark-factory-title: 2.0.1 + micromark-factory-whitespace: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-html-tag-name: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-subtokenize: 2.1.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-directive@3.0.2: + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-factory-whitespace: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + parse-entities: 4.0.2 + + micromark-extension-gfm-autolink-literal@2.1.0: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-footnote@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-strikethrough@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-table@2.1.1: + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-tagfilter@2.0.0: + dependencies: + micromark-util-types: 2.0.2 + + micromark-extension-gfm-task-list-item@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm@3.0.0: + dependencies: + micromark-extension-gfm-autolink-literal: 2.1.0 + micromark-extension-gfm-footnote: 2.1.0 + micromark-extension-gfm-strikethrough: 2.1.0 + micromark-extension-gfm-table: 2.1.1 + micromark-extension-gfm-tagfilter: 2.0.0 + micromark-extension-gfm-task-list-item: 2.1.0 + micromark-util-combine-extensions: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-mdx-expression@3.0.1: + dependencies: + '@types/estree': 1.0.8 + devlop: 1.1.0 + micromark-factory-mdx-expression: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-events-to-acorn: 2.0.3 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-mdx-jsx@3.0.2: + dependencies: + '@types/estree': 1.0.8 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + micromark-factory-mdx-expression: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-events-to-acorn: 2.0.3 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + vfile-message: 4.0.2 + + micromark-extension-mdx-md@2.0.0: + dependencies: + micromark-util-types: 2.0.2 + + micromark-extension-mdxjs-esm@3.0.0: + dependencies: + '@types/estree': 1.0.8 + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-util-character: 2.1.1 + micromark-util-events-to-acorn: 2.0.3 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + unist-util-position-from-estree: 2.0.0 + vfile-message: 4.0.2 + + micromark-extension-mdxjs@3.0.0: + dependencies: + acorn: 8.15.0 + acorn-jsx: 5.3.2(acorn@8.15.0) + micromark-extension-mdx-expression: 3.0.1 + micromark-extension-mdx-jsx: 3.0.2 + micromark-extension-mdx-md: 2.0.0 + micromark-extension-mdxjs-esm: 3.0.0 + micromark-util-combine-extensions: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-destination@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-label@2.0.1: + dependencies: + devlop: 1.1.0 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-mdx-expression@2.0.3: + dependencies: + '@types/estree': 1.0.8 + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-events-to-acorn: 2.0.3 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + unist-util-position-from-estree: 2.0.0 + vfile-message: 4.0.2 + + micromark-factory-space@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-types: 2.0.2 + + micromark-factory-title@2.0.1: + dependencies: + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-whitespace@2.0.1: + dependencies: + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-character@2.1.1: + dependencies: + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-chunked@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-classify-character@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-combine-extensions@2.0.1: + dependencies: + micromark-util-chunked: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-decode-numeric-character-reference@2.0.2: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-decode-string@2.0.1: + dependencies: + decode-named-character-reference: 1.2.0 + micromark-util-character: 2.1.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-symbol: 2.0.1 + + micromark-util-encode@2.0.1: {} + + micromark-util-events-to-acorn@2.0.3: + dependencies: + '@types/estree': 1.0.8 + '@types/unist': 3.0.3 + devlop: 1.1.0 + estree-util-visit: 2.0.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + vfile-message: 4.0.2 + + micromark-util-html-tag-name@2.0.1: {} + + micromark-util-normalize-identifier@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-resolve-all@2.0.1: + dependencies: + micromark-util-types: 2.0.2 + + micromark-util-sanitize-uri@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-encode: 2.0.1 + micromark-util-symbol: 2.0.1 + + micromark-util-subtokenize@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-symbol@2.0.1: {} + + micromark-util-types@2.0.2: {} + + micromark@4.0.2: + dependencies: + '@types/debug': 4.1.12 + debug: 4.4.1 + decode-named-character-reference: 1.2.0 + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-combine-extensions: 2.0.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-encode: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-subtokenize: 2.1.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + transitivePeerDependencies: + - supports-color + + mrmime@2.0.1: {} + + ms@2.1.3: {} + + nanoid@3.3.11: {} + + neotraverse@0.6.18: {} + + nlcst-to-string@4.0.0: + dependencies: + '@types/nlcst': 2.0.3 + + node-fetch-native@1.6.6: {} + + node-fetch@2.7.0: + dependencies: + whatwg-url: 5.0.0 + + node-mock-http@1.0.1: {} + + normalize-path@3.0.0: {} + + nth-check@2.1.1: + dependencies: + boolbase: 1.0.0 + + ofetch@1.4.1: + dependencies: + destr: 2.0.5 + node-fetch-native: 1.6.6 + ufo: 1.6.1 + + ohash@2.0.11: {} + + oniguruma-parser@0.12.1: {} + + oniguruma-to-es@4.3.3: + dependencies: + oniguruma-parser: 0.12.1 + regex: 6.0.1 + regex-recursion: 6.0.2 + + p-limit@6.2.0: + dependencies: + yocto-queue: 1.2.1 + + p-queue@8.1.0: + dependencies: + eventemitter3: 5.0.1 + p-timeout: 6.1.4 + + p-timeout@6.1.4: {} + + package-manager-detector@1.3.0: {} + + pagefind@1.3.0: + optionalDependencies: + '@pagefind/darwin-arm64': 1.3.0 + '@pagefind/darwin-x64': 1.3.0 + '@pagefind/linux-arm64': 1.3.0 + '@pagefind/linux-x64': 1.3.0 + '@pagefind/windows-x64': 1.3.0 + + pako@0.2.9: {} + + parse-entities@4.0.2: + dependencies: + '@types/unist': 2.0.11 + character-entities-legacy: 3.0.0 + character-reference-invalid: 2.0.1 + decode-named-character-reference: 1.2.0 + is-alphanumerical: 2.0.1 + is-decimal: 2.0.1 + is-hexadecimal: 2.0.1 + + parse-latin@7.0.0: + dependencies: + '@types/nlcst': 2.0.3 + '@types/unist': 3.0.3 + nlcst-to-string: 4.0.0 + unist-util-modify-children: 4.0.0 + unist-util-visit-children: 3.0.0 + vfile: 6.0.3 + + parse5@7.3.0: + dependencies: + entities: 6.0.1 + + pathe@2.0.3: {} + + picocolors@1.1.1: {} + + picomatch@2.3.1: {} + + picomatch@4.0.2: {} + + postcss-nested@6.2.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-selector-parser: 6.1.2 + + postcss-selector-parser@6.1.2: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss@8.5.6: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + prismjs@1.30.0: {} + + prompts@2.4.2: + dependencies: + kleur: 3.0.3 + sisteransi: 1.0.5 + + property-information@6.5.0: {} + + property-information@7.1.0: {} + + radix3@1.1.2: {} + + readdirp@4.1.2: {} + + recma-build-jsx@1.0.0: + dependencies: + '@types/estree': 1.0.8 + estree-util-build-jsx: 3.0.1 + vfile: 6.0.3 + + recma-jsx@1.0.0(acorn@8.15.0): + dependencies: + acorn-jsx: 5.3.2(acorn@8.15.0) + estree-util-to-js: 2.0.0 + recma-parse: 1.0.0 + recma-stringify: 1.0.0 + unified: 11.0.5 + transitivePeerDependencies: + - acorn + + recma-parse@1.0.0: + dependencies: + '@types/estree': 1.0.8 + esast-util-from-js: 2.0.1 + unified: 11.0.5 + vfile: 6.0.3 + + recma-stringify@1.0.0: + dependencies: + '@types/estree': 1.0.8 + estree-util-to-js: 2.0.0 + unified: 11.0.5 + vfile: 6.0.3 + + regex-recursion@6.0.2: + dependencies: + regex-utilities: 2.3.0 + + regex-utilities@2.3.0: {} + + regex@6.0.1: + dependencies: + regex-utilities: 2.3.0 + + rehype-expressive-code@0.41.2: + dependencies: + expressive-code: 0.41.2 + + rehype-format@5.0.1: + dependencies: + '@types/hast': 3.0.4 + hast-util-format: 1.1.0 + + rehype-parse@9.0.1: + dependencies: + '@types/hast': 3.0.4 + hast-util-from-html: 2.0.3 + unified: 11.0.5 + + rehype-raw@7.0.0: + dependencies: + '@types/hast': 3.0.4 + hast-util-raw: 9.1.0 + vfile: 6.0.3 + + rehype-recma@1.0.0: + dependencies: + '@types/estree': 1.0.8 + '@types/hast': 3.0.4 + hast-util-to-estree: 3.1.3 + transitivePeerDependencies: + - supports-color + + rehype-stringify@10.0.1: + dependencies: + '@types/hast': 3.0.4 + hast-util-to-html: 9.0.5 + unified: 11.0.5 + + rehype@13.0.2: + dependencies: + '@types/hast': 3.0.4 + rehype-parse: 9.0.1 + rehype-stringify: 10.0.1 + unified: 11.0.5 + + remark-directive@3.0.1: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-directive: 3.1.0 + micromark-extension-directive: 3.0.2 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-gfm@4.0.1: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-gfm: 3.1.0 + micromark-extension-gfm: 3.0.0 + remark-parse: 11.0.0 + remark-stringify: 11.0.0 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-mdx@3.1.0: + dependencies: + mdast-util-mdx: 3.0.0 + micromark-extension-mdxjs: 3.0.0 + transitivePeerDependencies: + - supports-color + + remark-parse@11.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-from-markdown: 2.0.2 + micromark-util-types: 2.0.2 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-rehype@11.1.2: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + mdast-util-to-hast: 13.2.0 + unified: 11.0.5 + vfile: 6.0.3 + + remark-smartypants@3.0.2: + dependencies: + retext: 9.0.0 + retext-smartypants: 6.2.0 + unified: 11.0.5 + unist-util-visit: 5.0.0 + + remark-stringify@11.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-to-markdown: 2.1.2 + unified: 11.0.5 + + restructure@3.0.2: {} + + retext-latin@4.0.0: + dependencies: + '@types/nlcst': 2.0.3 + parse-latin: 7.0.0 + unified: 11.0.5 + + retext-smartypants@6.2.0: + dependencies: + '@types/nlcst': 2.0.3 + nlcst-to-string: 4.0.0 + unist-util-visit: 5.0.0 + + retext-stringify@4.0.0: + dependencies: + '@types/nlcst': 2.0.3 + nlcst-to-string: 4.0.0 + unified: 11.0.5 + + retext@9.0.0: + dependencies: + '@types/nlcst': 2.0.3 + retext-latin: 4.0.0 + retext-stringify: 4.0.0 + unified: 11.0.5 + + rollup@4.44.2: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.44.2 + '@rollup/rollup-android-arm64': 4.44.2 + '@rollup/rollup-darwin-arm64': 4.44.2 + '@rollup/rollup-darwin-x64': 4.44.2 + '@rollup/rollup-freebsd-arm64': 4.44.2 + '@rollup/rollup-freebsd-x64': 4.44.2 + '@rollup/rollup-linux-arm-gnueabihf': 4.44.2 + '@rollup/rollup-linux-arm-musleabihf': 4.44.2 + '@rollup/rollup-linux-arm64-gnu': 4.44.2 + '@rollup/rollup-linux-arm64-musl': 4.44.2 + '@rollup/rollup-linux-loongarch64-gnu': 4.44.2 + '@rollup/rollup-linux-powerpc64le-gnu': 4.44.2 + '@rollup/rollup-linux-riscv64-gnu': 4.44.2 + '@rollup/rollup-linux-riscv64-musl': 4.44.2 + '@rollup/rollup-linux-s390x-gnu': 4.44.2 + '@rollup/rollup-linux-x64-gnu': 4.44.2 + '@rollup/rollup-linux-x64-musl': 4.44.2 + '@rollup/rollup-win32-arm64-msvc': 4.44.2 + '@rollup/rollup-win32-ia32-msvc': 4.44.2 + '@rollup/rollup-win32-x64-msvc': 4.44.2 + fsevents: 2.3.3 + + sax@1.4.1: {} + + semver@7.7.2: {} + + sharp@0.33.5: + dependencies: + color: 4.2.3 + detect-libc: 2.0.4 + semver: 7.7.2 + optionalDependencies: + '@img/sharp-darwin-arm64': 0.33.5 + '@img/sharp-darwin-x64': 0.33.5 + '@img/sharp-libvips-darwin-arm64': 1.0.4 + '@img/sharp-libvips-darwin-x64': 1.0.4 + '@img/sharp-libvips-linux-arm': 1.0.5 + '@img/sharp-libvips-linux-arm64': 1.0.4 + '@img/sharp-libvips-linux-s390x': 1.0.4 + '@img/sharp-libvips-linux-x64': 1.0.4 + '@img/sharp-libvips-linuxmusl-arm64': 1.0.4 + '@img/sharp-libvips-linuxmusl-x64': 1.0.4 + '@img/sharp-linux-arm': 0.33.5 + '@img/sharp-linux-arm64': 0.33.5 + '@img/sharp-linux-s390x': 0.33.5 + '@img/sharp-linux-x64': 0.33.5 + '@img/sharp-linuxmusl-arm64': 0.33.5 + '@img/sharp-linuxmusl-x64': 0.33.5 + '@img/sharp-wasm32': 0.33.5 + '@img/sharp-win32-ia32': 0.33.5 + '@img/sharp-win32-x64': 0.33.5 + optional: true + + sharp@0.34.2: + dependencies: + color: 4.2.3 + detect-libc: 2.0.4 + semver: 7.7.2 + optionalDependencies: + '@img/sharp-darwin-arm64': 0.34.2 + '@img/sharp-darwin-x64': 0.34.2 + '@img/sharp-libvips-darwin-arm64': 1.1.0 + '@img/sharp-libvips-darwin-x64': 1.1.0 + '@img/sharp-libvips-linux-arm': 1.1.0 + '@img/sharp-libvips-linux-arm64': 1.1.0 + '@img/sharp-libvips-linux-ppc64': 1.1.0 + '@img/sharp-libvips-linux-s390x': 1.1.0 + '@img/sharp-libvips-linux-x64': 1.1.0 + '@img/sharp-libvips-linuxmusl-arm64': 1.1.0 + '@img/sharp-libvips-linuxmusl-x64': 1.1.0 + '@img/sharp-linux-arm': 0.34.2 + '@img/sharp-linux-arm64': 0.34.2 + '@img/sharp-linux-s390x': 0.34.2 + '@img/sharp-linux-x64': 0.34.2 + '@img/sharp-linuxmusl-arm64': 0.34.2 + '@img/sharp-linuxmusl-x64': 0.34.2 + '@img/sharp-wasm32': 0.34.2 + '@img/sharp-win32-arm64': 0.34.2 + '@img/sharp-win32-ia32': 0.34.2 + '@img/sharp-win32-x64': 0.34.2 + + shiki@3.7.0: + dependencies: + '@shikijs/core': 3.7.0 + '@shikijs/engine-javascript': 3.7.0 + '@shikijs/engine-oniguruma': 3.7.0 + '@shikijs/langs': 3.7.0 + '@shikijs/themes': 3.7.0 + '@shikijs/types': 3.7.0 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + + simple-swizzle@0.2.2: + dependencies: + is-arrayish: 0.3.2 + + sisteransi@1.0.5: {} + + sitemap@8.0.0: + dependencies: + '@types/node': 17.0.45 + '@types/sax': 1.2.7 + arg: 5.0.2 + sax: 1.4.1 + + smol-toml@1.4.1: {} + + source-map-js@1.2.1: {} + + source-map@0.7.4: {} + + space-separated-tokens@2.0.2: {} + + stream-replace-string@2.0.0: {} + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string-width@7.2.0: + dependencies: + emoji-regex: 10.4.0 + get-east-asian-width: 1.3.0 + strip-ansi: 7.1.0 + + stringify-entities@4.0.4: + dependencies: + character-entities-html4: 2.1.0 + character-entities-legacy: 3.0.0 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-ansi@7.1.0: + dependencies: + ansi-regex: 6.1.0 + + style-to-js@1.1.17: + dependencies: + style-to-object: 1.0.9 + + style-to-object@1.0.9: + dependencies: + inline-style-parser: 0.2.4 + + tiny-inflate@1.0.3: {} + + tinyexec@0.3.2: {} + + tinyglobby@0.2.14: + dependencies: + fdir: 6.4.6(picomatch@4.0.2) + picomatch: 4.0.2 + + tr46@0.0.3: {} + + trim-lines@3.0.1: {} + + trough@2.2.0: {} + + tsconfck@3.1.6(typescript@5.8.3): + optionalDependencies: + typescript: 5.8.3 + + tslib@2.8.1: {} + + type-fest@4.41.0: {} + + typescript@5.8.3: {} + + ufo@1.6.1: {} + + ultrahtml@1.6.0: {} + + uncrypto@0.1.3: {} + + undici-types@7.8.0: {} + + unicode-properties@1.4.1: + dependencies: + base64-js: 1.5.1 + unicode-trie: 2.0.0 + + unicode-trie@2.0.0: + dependencies: + pako: 0.2.9 + tiny-inflate: 1.0.3 + + unified@11.0.5: + dependencies: + '@types/unist': 3.0.3 + bail: 2.0.2 + devlop: 1.1.0 + extend: 3.0.2 + is-plain-obj: 4.1.0 + trough: 2.2.0 + vfile: 6.0.3 + + unifont@0.5.2: + dependencies: + css-tree: 3.1.0 + ofetch: 1.4.1 + ohash: 2.0.11 + + unist-util-find-after@5.0.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.0 + + unist-util-is@6.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-modify-children@4.0.0: + dependencies: + '@types/unist': 3.0.3 + array-iterate: 2.0.1 + + unist-util-position-from-estree@2.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-position@5.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-remove-position@5.0.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-visit: 5.0.0 + + unist-util-stringify-position@4.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-visit-children@3.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-visit-parents@6.0.1: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.0 + + unist-util-visit@5.0.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.0 + unist-util-visit-parents: 6.0.1 + + unstorage@1.16.0: + dependencies: + anymatch: 3.1.3 + chokidar: 4.0.3 + destr: 2.0.5 + h3: 1.15.3 + lru-cache: 10.4.3 + node-fetch-native: 1.6.6 + ofetch: 1.4.1 + ufo: 1.6.1 + + util-deprecate@1.0.2: {} + + vfile-location@5.0.3: + dependencies: + '@types/unist': 3.0.3 + vfile: 6.0.3 + + vfile-message@4.0.2: + dependencies: + '@types/unist': 3.0.3 + unist-util-stringify-position: 4.0.0 + + vfile@6.0.3: + dependencies: + '@types/unist': 3.0.3 + vfile-message: 4.0.2 + + vite@6.3.5(@types/node@24.0.10): + dependencies: + esbuild: 0.25.5 + fdir: 6.4.6(picomatch@4.0.2) + picomatch: 4.0.2 + postcss: 8.5.6 + rollup: 4.44.2 + tinyglobby: 0.2.14 + optionalDependencies: + '@types/node': 24.0.10 + fsevents: 2.3.3 + + vitefu@1.1.1(vite@6.3.5(@types/node@24.0.10)): + optionalDependencies: + vite: 6.3.5(@types/node@24.0.10) + + web-namespaces@2.0.1: {} + + webidl-conversions@3.0.1: {} + + whatwg-url@5.0.0: + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + + which-pm-runs@1.1.0: {} + + widest-line@5.0.0: + dependencies: + string-width: 7.2.0 + + wrap-ansi@9.0.0: + dependencies: + ansi-styles: 6.2.1 + string-width: 7.2.0 + strip-ansi: 7.1.0 + + xxhash-wasm@1.1.0: {} + + yargs-parser@21.1.1: {} + + yocto-queue@1.2.1: {} + + yocto-spinner@0.2.3: + dependencies: + yoctocolors: 2.1.1 + + yoctocolors@2.1.1: {} + + zod-to-json-schema@3.24.6(zod@3.25.74): + dependencies: + zod: 3.25.74 + + zod-to-ts@1.2.0(typescript@5.8.3)(zod@3.25.74): + dependencies: + typescript: 5.8.3 + zod: 3.25.74 + + zod@3.25.74: {} + + zwitch@2.0.4: {} diff --git a/docs/pnpm-workspace.yaml b/docs/pnpm-workspace.yaml new file mode 100644 index 0000000..d0b7dbe --- /dev/null +++ b/docs/pnpm-workspace.yaml @@ -0,0 +1,3 @@ +onlyBuiltDependencies: + - esbuild + - sharp diff --git a/docs/public/favicon.svg b/docs/public/favicon.svg new file mode 100644 index 0000000..a24ca8b --- /dev/null +++ b/docs/public/favicon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/src/assets/face.svg b/docs/src/assets/face.svg new file mode 100644 index 0000000..a24ca8b --- /dev/null +++ b/docs/src/assets/face.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/src/assets/icon.svg b/docs/src/assets/icon.svg new file mode 100644 index 0000000..77253df --- /dev/null +++ b/docs/src/assets/icon.svg @@ -0,0 +1 @@ + diff --git a/docs/src/content.config.ts b/docs/src/content.config.ts new file mode 100644 index 0000000..d9ee8c9 --- /dev/null +++ b/docs/src/content.config.ts @@ -0,0 +1,7 @@ +import { defineCollection } from 'astro:content'; +import { docsLoader } from '@astrojs/starlight/loaders'; +import { docsSchema } from '@astrojs/starlight/schema'; + +export const collections = { + docs: defineCollection({ loader: docsLoader(), schema: docsSchema() }), +}; diff --git a/docs/src/content/docs/examples/creepers_and_zombies_drop_tnt.md b/docs/src/content/docs/examples/creepers_and_zombies_drop_tnt.md new file mode 100644 index 0000000..61bea66 --- /dev/null +++ b/docs/src/content/docs/examples/creepers_and_zombies_drop_tnt.md @@ -0,0 +1,121 @@ +--- +title: Make Creepers and Zombies drop tnt +--- + +There are two ways to go about doing this, depending on how you want the tnt to drop. +You can either add them in a pool that's separate from the existing drops or do it in the same pool. + +But what's the difference? +In a loot table, each pool generates drops by choosing one or multiple entries from itself. +Adding the tnt in a separate pool means that the tnt will *always* drop no matter what other drops are in the table. +Adding the tnt to an existing pool means that the tnt is one of the options that the game may drop, among the normal drops. + +## In a separate pool +```json +{ + "actions": [ + { + "type": "loot-table-modifier:pool_add", + "pools": [ + { + "rolls": 1, + "entries": [ + { + "type": "minecraft:item", + "name": "minecraft:tnt" + } + ] + } + ] + } + ], + "predicate": { + "type": "loot-table-modifier:table", + "identifiers": [ + "minecraft:entities/creeper", + "minecraft:entities/zombie" + ] + } +} +``` + +### Explanation + +The action [`pool_add`](/reference/actions#add-pool) adds the pool to the matched tables. + +The predicate [`table`](/reference/predicates#loot-table) matches either the creeper or zombie loot tables. + +## In an existing pool +```json +{ + "actions": [ + { + "type": "loot-table-modifier:entry_add", + "entries": [ + { + "type": "minecraft:item", + "name": "minecraft:tnt" + } + ] + } + ], + "predicate": { + "type": "loot-table-modifier:table", + "identifiers": [ + "minecraft:entities/creeper", + "minecraft:entities/zombie" + ] + } +} +``` + +### Explanation + +The action [`entry_add`](/reference/actions#add-entry) adds the entry to a pool. + +The predicate [`table`](/reference/predicates#loot-table) matches either the creeper or zombie loot tables. + +The action will only be applied on the first pool it finds in the target table, so you may want to use a predicate like below to specify which pool it should be added to. +(Also, if adding some configuration to how many pools it would try matching or whatever would be useful for your use case, hit me up on [discord](https://discord.offsetmonkey538.top)) +```json {"Creeper predicate, matches the pool dropping gunpowder":5-20} {"Zombie predicate, matches the pool dropping rotten flesh":22-37} +{ + "predicate": { + "type": "loot-table-modifier:any_of", + "terms": [ + + { + "type": "loot-table-modifier:all_of", + "terms": [ + { + "type": "loot-table-modifier:table", + "identifiers": [ + "minecraft:entities/creeper" + ] + }, + { + "type": "loot-table-modifier:entry_item", + "name": "minecraft:gunpowder" + } + ] + }, + + + { + "type": "loot-table-modifier:all_of", + "terms": [ + { + "type": "loot-table-modifier:table", + "identifiers": [ + "minecraft:entities/zombie" + ] + }, + { + "type": "loot-table-modifier:entry_item", + "name": "minecraft:rotten_flesh" + } + ] + } + ] + } +} +``` diff --git a/docs/src/content/docs/examples/remove_glowstone_and_gunpowder_witches.md b/docs/src/content/docs/examples/remove_glowstone_and_gunpowder_witches.md new file mode 100644 index 0000000..7e724d7 --- /dev/null +++ b/docs/src/content/docs/examples/remove_glowstone_and_gunpowder_witches.md @@ -0,0 +1,43 @@ +--- +title: Remove glowstone and gunpowder from witches +--- + +For removing an item from a loot table, the [`entry_remove`](/reference/actions#remove-entry) action should be used in most cases. +Using [`pool_remove`](/reference/actions#remove-pool) would remove the whole pool, including all the other entries in there. + +```json {"First part matches the witch table":10-16} {"Second part matches either glowstone dust or gunpowder":18-31} +{ + "actions": [ + { + "type": "loot-table-modifier:entry_remove" + } + ], + "predicate": { + "type": "loot-table-modifier:all_of", + "terms": [ + + { + "type": "loot-table-modifier:table", + "identifiers": [ + "minecraft:entities/witch" + ] + }, + + + { + "type": "loot-table-modifier:any_of", + "terms": [ + { + "type": "loot-table-modifier:entry_item", + "name": "minecraft:glowstone_dust" + }, + { + "type": "loot-table-modifier:entry_item", + "name": "minecraft:gunpowder" + } + ] + } + ] + } +} +``` diff --git a/docs/src/content/docs/examples/remove_sticks.md b/docs/src/content/docs/examples/remove_sticks.md new file mode 100644 index 0000000..c6fc8ab --- /dev/null +++ b/docs/src/content/docs/examples/remove_sticks.md @@ -0,0 +1,26 @@ +--- +title: Remove sticks +--- + +For removing an item from a loot table, the [`entry_remove`](/reference/actions#remove-entry) action should be used in most cases. +Using [`pool_remove`](/reference/actions#remove-pool) would remove the whole pool, including all the other entries in there. + +```json +{ + "actions": [ + { + "type": "loot-table-modifier:entry_remove" + } + ], + "predicate": { + "type": "loot-table-modifier:entry_item", + "name": "minecraft:stick" + } +} +``` + +### Explanation + +The action [`entry_remove`](../../reference/loot_modifier#add-pool) removes the matched entries from their pools. + +The predicate [`entry_item`](../../reference/loot_modifier#loot-table) matches entries containing a stick. diff --git a/docs/src/content/docs/examples/replace_ingot_w_diamond.md b/docs/src/content/docs/examples/replace_ingot_w_diamond.md new file mode 100644 index 0000000..6dc9b0d --- /dev/null +++ b/docs/src/content/docs/examples/replace_ingot_w_diamond.md @@ -0,0 +1,29 @@ +--- +title: Replace any Minecraft ingot with a diamond +--- + +The following loot modifier will replace any Minecraft ingot item with a diamond: +```json +{ + "actions": + [ + { + "type": "loot-table-modifier:entry_item_set", + "name": "minecraft:diamond" + } + ], + "predicate": { + "type": "loot-table-modifier:entry_item", + "name": { + "regexPattern": "minecraft:.*_ingot" + } + } +} +``` + +### Explanation + +The action [`entry_item_set`](/reference/actions#set-item-in-item-entry) replaces the item in an existing matched item entry. + +The predicate [`entry_item`](/reference/predicates#item-entry) matches specific item entries based on their IDs. It can match using a [regex identifier](/reference/regex_identifier), which allows using regex patterns. +The regex pattern `minecraft:.*_ingot` will match every item that has an identifier that begins with `minecraft:` and ends with `_ingot`. For example `minecraft:iron_ingot` and `minecraft:gold_ingot` will both be matched. diff --git a/docs/src/content/docs/getting_started.md b/docs/src/content/docs/getting_started.md new file mode 100644 index 0000000..7b3924f --- /dev/null +++ b/docs/src/content/docs/getting_started.md @@ -0,0 +1,62 @@ +--- +title: Getting Started +--- + +## Installing +:::note +Currently, as of v2 alpha 1, only **fabric** is supported. Running though [Sinytra Connector](https://modrinth.com/mod/connector) may or may not be possible (please do let me know on [discord](https://discord.offsetmonkey538.top) if you test it :D), but a NeoForge version is planned for the future. +::: + +To use Loot Table Modifier, you first gotta have it installed (I am so damn good at writing useful documentation, no need to thank me πŸ‘) + +When developing a plain datapack, all you need to do is download it from [modrinth](https://modrinth.com/mod/loot-table-modifier) and put it in your mods folder. + +You may also want to show the `Requires` badge in your readme, see below for an example and [here](#badges) for more info. +[![This project requires Loot Table Modifier to be installed](https://raw.githubusercontent.com/OffsetMods538/Loot-Table-Modifier/master/images/requires_badge.svg)](https://modrinth.com/mod/loot-table-modifier) + +### Mod developers +Mods can depend on Loot Table Modifier like this: +```groovy +repositories { + // ...rest of repositories block + maven { + name = "OffsetMods538" + url = "https://maven.offsetmonkey538.top/releases" + content { + includeGroup "top.offsetmonkey538.loottablemodifer" + } + } +} + +dependencies { + // ...rest of dependencies block + + implementation "top.offsetmonkey538.loottablemodifier:loot-table-modifier:VERSION_HERE" +} +``` +Make sure to replace `VERSION_HERE` with the actual version you want to use! +See my maven page [here](https://maven.offsetmonkey538.top/#/releases/top/offsetmonkey538/loottablemodifier/loot-table-modifier) for all available versions and their javadocs. + +Also add this to your `fabric.mod.json` file: +```json {" This matches the major version of 2":3-4} +{ + "depends": { + + "loot-table-modifier": ">=2.0.0 <3.0.0" + } +} +``` + +## Development Mode + +Development mode enables additional logging and the ability to export modified loot tables. + +Dev mode will automatically be enabled when Minecraft is launched from an IDE or can be enabled by setting the JVM property `lootTableModifierDev` to `true`. +That can be done with Prism Launcher by going to `Settings`, selecting `Java` and then adding `-DlootTableModifierDev=true` into `JVM arguments`. + +Mod developers who, for some weird reason, *don't* want their logs to be spammed with random unnecessary stuff can set the property to `false` to override the IDE check. + +Modified loot tables can then be exported using the command `/loot-table-modifier debug export`. + +## Next Up +Take a look at how loot modifiers work [here](/reference/loot_modifier) and then take a look at the examples. diff --git a/docs/src/content/docs/index.md b/docs/src/content/docs/index.md new file mode 100644 index 0000000..45c7e75 --- /dev/null +++ b/docs/src/content/docs/index.md @@ -0,0 +1,36 @@ +--- +title: Loot Table Modifier +template: splash +hero: + tagline: Makes it possible for datapacks to modify loot tables, instead of just replacing them. + image: + file: ../../assets/icon.svg + actions: + - text: Get Started + link: /getting_started/ + icon: right-arrow + - text: View on Modrinth + link: https://modrinth.com/mod/loot-table-modifier + icon: external + variant: minimal + - text: Chat with me on Dicord + link: https://discord.offsetmonkey538.top + icon: discord + variant: minimal +--- + +# About + +:::caution +This documentation is written for Loot Table Modifier v2 alpha 1. +Both the mod and this wiki are in development. +If you encounter any issues or have suggestions, please *please* **please** come join my [discord](https://discord.offsetmonkey538.top) and yell at me about them. +::: + + +Vanilla datapacks can only replace existing loot tables, which is not good for being compatible with other datapacks that may also try to replace the same loot tables. +This mod fixes that by implementing a system of *loot modifiers*, which do just that, modify loot (tables)! + +Loot modifiers are in datapacks, meaning mods and modpacks are able to use them too! + +Unlike some mods which replace stuff at runtime, Loot Table Modifier does all of its processing during data loading. This means that the mod will have no performance impact during gameplay! (Except of course if you make a zombie drop a bajillion stacks of diamonds, though that's not the mod's fault...) diff --git a/docs/src/content/docs/reference/actions.md b/docs/src/content/docs/reference/actions.md new file mode 100644 index 0000000..d4a3826 --- /dev/null +++ b/docs/src/content/docs/reference/actions.md @@ -0,0 +1,89 @@ +--- +title: Actions +--- + +:::note +Fields marked as `Optional` have their value set to the default ones. +::: + +Actions tell the mod how a matched loot table, pool or entry should be modified. + +Actions are json objects that consist of their identifier `type` and then any other fields specific to each action. +Below is a list of all currently supported actions: + +| | | +|-----------------------------------------------------------------|--------------------------------------------| +| [`loot-table-modifier:pool_add`](#add-pool) | Adds the provided pools to matched tables | +| [`loot-table-modifier:pool_remove`](#remove-pool) | Removes matched pools | +| [`loot-table-modifier:entry_add`](#add-entry) | Adds the provided entries to matched pools | +| [`loot-table-modifier:entry_remove`](#remove-entry) | Removes matched entries | +| [`loot-table-modifier:entry_item_set`](#set-item-in-item-entry) | Sets the item in matched item entries | + +### Add pool +```json +{ + "type": "loot-table-modifier:pool_add", + "pools": [ + { + /* Loot pool */ + }, + { + /* Loot pool */ + } + ] +} +``` +This action adds the provided loot pools to matched tables. + +See: [Make Creepers and Zombies drop tnt](/examples/creepers_and_zombies_drop_tnt) + +### Remove pool +```json +{ + "type": "loot-table-modifier:pool_remove" +} +``` +This action removes all matched pools. + +### Add entry +```json +{ + "type": "loot-table-modifier:entry_add", + "entries": [ + { + /* Loot entry */ + }, + { + /* Loot entry */ + } + ] +} +``` +This action adds the provided loot entries to matched pools. + +See: [Make Creepers and Zombies drop tnt](/examples/creepers_and_zombies_drop_tnt) + +### Remove entry +```json +{ + "type": "loot-table-modifier:entry_remove" +} +``` +This action removes all matched entries. + +See: [Remove sticks](/examples/remove_sticks), [Remove glowstone and gunpowder from witches](/examples/remove_glowstone_and_gunpowder_witches) + +### Set item in item entry +```json {" Optional":4-5} +{ + "type": "loot-table-modifier:entry_item_set", + "name": /* Identifier of an item */, + + "canReplaceEntry": false +} +``` +This action will replace the item in a matched item entry with the provided item. +If `canReplaceEntry` is enabled, any other matched entry will be replaced with an item entry containing the provided item. +By default, it will not replace other types of entries, but that can be enabled by setting `canReplaceEntry` to true. + +See: [Replace any Minecraft ingot with a diamond](/examples/replace_ingot_w_diamond) diff --git a/docs/src/content/docs/reference/badges.md b/docs/src/content/docs/reference/badges.md new file mode 100644 index 0000000..3c6ff4d --- /dev/null +++ b/docs/src/content/docs/reference/badges.md @@ -0,0 +1,21 @@ +--- +title: Badges +--- +Badges are meant for developers to put on their project pages, so users can see that Loot Table Modifier is used or required. +They're based on [Devin's Badges](https://github.com/intergrav/devins-badges). + +The `requires` badge is meant for datapacks or mods which want to display that Loot Table Modifier is required for them to function. +It can be used in Markdown like this: `[![This project requires Loot Table Modifier to be installed](https://raw.githubusercontent.com/OffsetMods538/Loot-Table-Modifier/master/images/requires_badge.svg)](https://modrinth.com/mod/loot-table-modifier)` +And will look like this: +[![This project requires Loot Table Modifier to be installed](https://raw.githubusercontent.com/OffsetMods538/Loot-Table-Modifier/master/images/requires_badge.svg)](https://modrinth.com/mod/loot-table-modifier) + +Clicking it will bring the user to the Modrinth page for Loot Table Modifier + +
+ +The `uses` badge is meant for mods or modpacks which want to display that Loot Table Modifier is included. (Honestly not sure why I made this 'cause I can't imagine anyone would do that but uhh sure πŸ˜…) +It can be used in Markdown like this: `[![This project includes Loot Table Modifier](https://raw.githubusercontent.com/OffsetMods538/Loot-Table-Modifier/master/images/uses_badge.svg)](https://modrinth.com/mod/loot-table-modifier)` +And will look like this: +[![This project includes Loot Table Modifier](https://raw.githubusercontent.com/OffsetMods538/Loot-Table-Modifier/master/images/uses_badge.svg)](https://modrinth.com/mod/loot-table-modifier) + +Clicking it will bring the user to the Modrinth page for Loot Table Modifier diff --git a/docs/src/content/docs/reference/loot_modifier.md b/docs/src/content/docs/reference/loot_modifier.md new file mode 100644 index 0000000..a8799ef --- /dev/null +++ b/docs/src/content/docs/reference/loot_modifier.md @@ -0,0 +1,20 @@ +--- +title: Loot Modifier +--- +The mod uses loot modifiers to figure out how and what it should modify. +Loot modifiers are JSON files located in datapacks at `data/namespace/loot-table-modifier/loot_modifier/modifier_name.json`. + +Loot modifiers consist of two parts: the [actions](/reference/actions) and a [predicate](/reference/predicates). + +During data loading, the mod looks through every existing loot table and its pools and entries. +For every entry, pool and table, it will try matching the predicates of loot modifiers. +When a loot modifier's predicate matches, its actions will be executed on the matched entry, pool or table. + +The mod counts less specific predicates as matching everything underneath. +This means that using the [`add_entry`](/reference/actions#add-entry) action with the [`table`](/reference/predicates#loot-table) predicate will add the entries to all pools in the matched table. +The same applies in reverse; a more specific predicate also counts as matching everything above it, meaning that using the [`add_pool`](/reference/actions#add-pool) action with the [`entry_item`](/reference/predicates#item-entry) predicate will add the pools to all tables that contain a matching item entry. + +## Generation +Loot modifiers can be generated using the misode generator available [here](https://misode-itd7xiyf1-misodes-projects.vercel.app/) under `Modded Generators` + +Mod developers can use the `LootModifierProvider` datagen provider. Javadoc can be seen in-IDE or [here](https://maven.offsetmonkey538.top/#/releases/top/offsetmonkey538/loottablemodifier/loot-table-modifier). diff --git a/docs/src/content/docs/reference/predicates.md b/docs/src/content/docs/reference/predicates.md new file mode 100644 index 0000000..c2ab931 --- /dev/null +++ b/docs/src/content/docs/reference/predicates.md @@ -0,0 +1,84 @@ +--- +title: Predicates +--- + +:::note +Predicates may use this thing I call a `RegexIdentifier`, which you can read about [here](/reference/regex_identifier). +::: + +Predicates tell the mod which loot tables, pools or entries should be modified. +Different predicates can be combined using `invert`, `any_of` and `all_of`. + +Below is a list of all supported predicates: + +| | | +|-------------------------------------------------|---------------------------------------------------| +| [`loot-table-modifier:inverted`](#invert) | Inverts the result of the provided predicate | +| [`loot-table-modifier:any_of`](#any-of) | Matches when any of the provided predicates match | +| [`loot-table-modifier:all_of`](#all-of) | Matches when all of the provided predicates match | +| [`loot-table-modifier:entry_item`](#item-entry) | Matches an item entry based on its identifier | +| [`loot-table-modifier:table`](#loot-table) | Matches a table based on its identifier or type | + +### Invert +```json +{ + "type": "loot-table-modifier:inverted", + "term": { + /* Predicate */ + } +} +``` +This predicate will match when the provided predicate doesn't. A logical `NOT` operation. + +### Any of +```json +{ + "type": "loot-table-modifier:any_of", + "terms": [ + { + /* Predicate */ + }, + { + /* Predicate */ + } + ] +} +``` +This predicate will match when any of the provided predicates match. A logical `OR` operation. + +### All of +```json +{ + "type": "loot-table-modifier:all_of", + "terms": [ + { + /* Predicate */ + }, + { + /* Predicate */ + } + ] +} +``` +This predicate will match when all of the provided predicates match. A logical `AND` operation. + +### Item Entry +```json +{ + "type": "loot-table-modifier:entry_item", + "name": /* RegexIdentifier */ +} +``` +This predicate will match item entries based on their identifiers. + +### Loot Table +```json {" Optional":3-4} {" Optional":5-6} +{ + "type": "loot-table-modifier:table", + + "identifiers": /* RegexIdentifier */, + + "types": /* RegexIdentifier */ +} +``` +This predicate will match loot tables based on their identifiers or types. diff --git a/docs/src/content/docs/reference/regex_identifier.md b/docs/src/content/docs/reference/regex_identifier.md new file mode 100644 index 0000000..3aba7f8 --- /dev/null +++ b/docs/src/content/docs/reference/regex_identifier.md @@ -0,0 +1,39 @@ +--- +title: Regex Identifier +--- + +Identifiers (officially resource locations) are used all over Minecraft for identifying stuff like items, blocks, entities, loot tables and so on. +They consist of a namespace and a path, separated by a colon. Full documentation on identifiers can be found [here](https://minecraft.wiki/w/Resource_location). + +Loot modifier predicates often use regex identifiers for matching these identifiers. +A regex identifier allows matching either a literal identifier or a regex pattern. + +Instead of matching an exact piece of text, regex allows describing a pattern to match many possible variations. +For that, regex uses *metacharacters*. The most useful combination of which for matching identifiers is `.*`, which matches any character from zero to infinite times. +This is very useful for loot modifier predicates because you can build patterns like `minecraft:.*_ingot` for matching any ingot from minecraft. + +Full documentation on supported regex syntax can be found [here](https://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html), and you can use a site like [regex101](https://regex101.com/) to write and test you regular expressions (just make sure to choose the `Java 8` flavor) + +A `RegexIdentifier` in json can either be an inlined string or an object with the `regexPattern` field. +Examples: +```json {"Matches only the exact identifier of minecraft:diamond_sword":2-3} {"Matches all minecraft sword types (minecraft:stone_sword, minecraft:iron_sword, etc)":5-8} {"Matches anything from minecraft":10-13} {"Matches any minecraft oak block/item, except for the stripped variants":15-18} +{ + + "value1": "minecraft:diamond_sword", + + + "value2": { + "regexPattern": "minecraft:.*_sword" + }, + + + "value3": { + "regexPattern": "minecraft:.*" + }, + + + "value4": { + "regexPattern": "minecraft:oak_.*" + } +} +``` diff --git a/docs/src/styles/custom.css b/docs/src/styles/custom.css new file mode 100644 index 0000000..7cec2c0 --- /dev/null +++ b/docs/src/styles/custom.css @@ -0,0 +1,37 @@ +/* Dark mode colors. */ +:root { + --sl-color-accent-low: #420d1f; + --sl-color-accent: #c1005c; + --sl-color-accent-high: #f7b3c2; + --sl-color-white: #ffffff; + --sl-color-gray-1: #eceef2; + --sl-color-gray-2: #c6c8ce; + --sl-color-gray-3: #a0a4ae; + --sl-color-gray-4: #545861; + --sl-color-gray-5: #353841; + --sl-color-gray-6: #24272f; + --sl-color-black: #17181c; +} +/* Light mode colors. */ +:root[data-theme='light'] { + --sl-color-accent-low: #fbc7d2; + --sl-color-accent: #a2004c; + --sl-color-accent-high: #60052b; + --sl-color-white: #17181c; + --sl-color-gray-1: #24272f; + --sl-color-gray-2: #353841; + --sl-color-gray-3: #545861; + --sl-color-gray-4: #888b96; + --sl-color-gray-5: #c0c2c7; + --sl-color-gray-6: #eceef2; + --sl-color-gray-7: #f5f6f8; + --sl-color-black: #ffffff; +} + +.site-title > img { + border: 1px solid #420d1f; +} + +.site-title > span { + color: #FFFFFF; +} diff --git a/docs/src/utils/PageFrame.astro b/docs/src/utils/PageFrame.astro new file mode 100644 index 0000000..7f7ab1d --- /dev/null +++ b/docs/src/utils/PageFrame.astro @@ -0,0 +1,98 @@ +--- +import MobileMenuToggle from 'virtual:starlight/components/MobileMenuToggle'; + +const { hasSidebar } = Astro.locals.starlightRoute; +--- + +
+
+ { + hasSidebar && ( + + ) + } +
+
+ + \ No newline at end of file diff --git a/docs/src/utils/SocialIcons.astro b/docs/src/utils/SocialIcons.astro new file mode 100644 index 0000000..b073bdc --- /dev/null +++ b/docs/src/utils/SocialIcons.astro @@ -0,0 +1,57 @@ +--- +import config from "virtual:starlight/user-config"; +// @ts-expect-error - This is a custom Virtual Module +import augment from "virtual:starlight-social-icon-augment"; +import { Icon } from "@astrojs/starlight/components"; +import type { ComponentProps } from "astro/types"; + +type IconProps = ComponentProps; + +const customIcons = { + modrinth: + '', +} as const; + +const combinedLinks: { + main: { label: string; href: string; icon: IconProps["name"] }[]; + custom: { label: string; href: string; icon: keyof typeof customIcons }[]; +} = { main: config.social || [], custom: augment || [] }; +--- + +{ + combinedLinks.main.length > 0 && ( + <> + {combinedLinks.main.map(({ label, href, icon }) => ( + + {label} + + + ))} + + ) +} +{ + combinedLinks.custom.length > 0 && ( + <> + {combinedLinks.custom.map(({ label, href, icon }) => ( + + {label} + + + ))} + + ) +} + + diff --git a/docs/src/utils/socialIcons.ts b/docs/src/utils/socialIcons.ts new file mode 100644 index 0000000..6bef9aa --- /dev/null +++ b/docs/src/utils/socialIcons.ts @@ -0,0 +1,39 @@ +import type { AstroIntegration } from 'astro'; + +export function starlightSocialIcons(socials: { + modrinth: string; +}): AstroIntegration { + const labelMap = { + modrinth: 'Modrinth' + } + + return { + name: 'starlight-social-icon-augment', + hooks: { + 'astro:config:setup': ({ updateConfig }) => { + updateConfig({ + vite: { + plugins: [ + { + name: 'virtual-config-plugin', + resolveId(id) { + if (id === 'virtual:starlight-social-icon-augment') { + return '\0virtual:starlight-social-icon-augment'; // Prefix with \0 to mark as virtual + } + }, + load(id) { + if (id === '\0virtual:starlight-social-icon-augment') { + const config = socials; + return `export default ${JSON.stringify(Object.entries(config).flatMap(([key , value]) => ({ label: labelMap[key as keyof typeof labelMap], href: value, icon: key })))};`; + } + }, + } + ] + } + }) + }, + }, + }; +} + +export default starlightSocialIcons; \ No newline at end of file diff --git a/docs/tsconfig.json b/docs/tsconfig.json new file mode 100644 index 0000000..8bf91d3 --- /dev/null +++ b/docs/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "astro/tsconfigs/strict", + "include": [".astro/types.d.ts", "**/*"], + "exclude": ["dist"] +} diff --git a/gradle.properties b/gradle.properties index 468d4bf..9e35644 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,12 +4,12 @@ org.gradle.parallel = true # Fabric # Check at https://fabricmc.net/develop -minecraft_version = 1.21.5 +minecraft_version = 1.21.8 # These should be automatically updated, unless the environment # variable "DISABLE_PROPERTIES_UPDATE" is set. -fapi_version = 0.125.3+1.21.5 -yarn_version = 1.21.5+build.1 +fapi_version = 0.129.0+1.21.8 +yarn_version = 1.21.8+build.1 loader_version = 0.16.14 # Dependencies @@ -17,5 +17,5 @@ loader_version = 0.16.14 devauth_version = 1.2.1 # Mod Properties -mod_version = 1.1.2 -supported_minecraft_versions = >=1.20.5 <=1.21.5 +mod_version = 2.0.0-alpha.1 +supported_minecraft_versions = >=1.21.5 <=1.21.8 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 9128c7d..78cb6e1 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=845952a9d6afa783db70bb3b0effaae45ae5542ca2bb7929619e8af49cb634cf -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.1-bin.zip +distributionSha256Sum=bd71102213493060956ec229d946beee57158dbd89d0e62b91bca0fa2c5f3531 +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java b/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java index 7bb3801..e0c461a 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/LootTableModifier.java @@ -1,60 +1,150 @@ package top.offsetmonkey538.loottablemodifier; -import com.google.common.collect.ImmutableList; +import com.google.common.base.Stopwatch; import com.google.gson.JsonElement; import com.google.gson.JsonParser; +import com.google.gson.stream.JsonWriter; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.serialization.DataResult; +import com.mojang.serialization.DynamicOps; +import com.mojang.serialization.JsonOps; import net.fabricmc.api.ModInitializer; +import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; import net.fabricmc.fabric.api.resource.ResourceManagerHelper; import net.fabricmc.fabric.api.resource.ResourcePackActivationType; import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.data.DataProvider; import net.minecraft.loot.LootPool; import net.minecraft.loot.LootTable; -import net.minecraft.registry.Registry; -import net.minecraft.registry.RegistryKey; -import net.minecraft.registry.RegistryOps; -import net.minecraft.registry.RegistryWrapper; +import net.minecraft.loot.entry.LootPoolEntry; +import net.minecraft.registry.*; import net.minecraft.registry.entry.RegistryEntry; import net.minecraft.resource.Resource; import net.minecraft.resource.ResourceManager; -import net.minecraft.text.Text; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.command.ServerCommandSource; +import net.minecraft.text.*; +import net.minecraft.util.Formatting; import net.minecraft.util.Identifier; +import net.minecraft.util.JsonHelper; +import org.apache.commons.io.file.PathUtils; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import top.offsetmonkey538.loottablemodifier.mixin.LootTableAccessor; -import top.offsetmonkey538.loottablemodifier.resource.LootModifier; +import top.offsetmonkey538.loottablemodifier.api.resource.action.LootModifierActionTypes; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicateTypes; +import top.offsetmonkey538.loottablemodifier.api.resource.LootModifier; +import top.offsetmonkey538.loottablemodifier.api.resource.util.LootModifierContext; import java.io.IOException; import java.lang.reflect.InvocationTargetException; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; + +import static net.minecraft.server.command.CommandManager.literal; +import static top.offsetmonkey538.loottablemodifier.api.resource.action.LootModifierAction.*; public class LootTableModifier implements ModInitializer { public static final String MOD_ID = "loot-table-modifier"; public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID); + public static final boolean IS_DEV; + static { + final String isDev = System.getProperty("lootTableModifierDev", ""); + if (isDev.equalsIgnoreCase("true")) IS_DEV = true; + else if (isDev.equalsIgnoreCase("false")) IS_DEV = false; // This way it can be disabled in devenv too. + else IS_DEV = FabricLoader.getInstance().isDevelopmentEnvironment(); + } + + // Only used when IS_DEV is true + private static final List MODIFIED_TABLE_IDs; + static { + if (IS_DEV) MODIFIED_TABLE_IDs = Collections.synchronizedList(new ArrayList<>(0)); + else MODIFIED_TABLE_IDs = null; + } + @Override public void onInitialize() { - if(FabricLoader.getInstance().isDevelopmentEnvironment()) ResourceManagerHelper.registerBuiltinResourcePack(id("example_pack"), FabricLoader.getInstance().getModContainer(MOD_ID).orElseThrow(), Text.of("Example Pack"), ResourcePackActivationType.NORMAL); + LootModifierActionTypes.register(); + LootModifierPredicateTypes.register(); + + if (IS_DEV) enableDebug(); } public static void runModification(ResourceManager resourceManager, Registry lootRegistry, RegistryOps registryOps) { final Map modifiers = loadModifiers(resourceManager, registryOps); - int amountModified = 0; - LOGGER.info("Applying loot table modifiers..."); - for (RegistryEntry.Reference tableReference : getRegistryAsWrapper(lootRegistry).streamEntries().toList()) { - final RegistryKey tableKey = tableReference.registryKey(); + final List modifiedTableIds = new ArrayList<>(); // Used for exporting modified ones + int poolsModified = 0, entriesModified = 0; + boolean tableModified, poolModified; + + LOGGER.info("Modifying loot tables..."); + final Stopwatch stopwatch = Stopwatch.createStarted(); + + for (Iterator> it = getRegistryAsWrapper(lootRegistry).streamEntries().iterator(); it.hasNext(); ) { + final RegistryEntry.Reference registryEntry = it.next(); + final RegistryKey key = registryEntry.registryKey(); + + final LootTable table = lootRegistry.get(key); + final Identifier tableId = key.getValue(); + if (table == null) throw new IllegalStateException("Loot table with id '%s' is null!".formatted(key)); + + tableModified = false; + + final List poolsCopy = new LinkedList<>(table.pools); + int poolsSize = Math.max(1, poolsCopy.size()); + for (int i = 0; i < poolsSize; i++) { + final @Nullable LootPool pool = poolsCopy.isEmpty() ? null : poolsCopy.get(i); + poolModified = false; + + final List entriesCopy = pool == null ? List.of() : new LinkedList<>(pool.entries); + int entriesSize = Math.max(1, entriesCopy.size()); + for (int j = 0; j < entriesSize; j++) { + final @Nullable LootPoolEntry entry = entriesCopy.isEmpty() ? null : entriesCopy.get(j); - amountModified += applyModifiers(lootRegistry.get(tableKey), tableKey, modifiers); + for (Map.Entry modifierEntry : modifiers.entrySet()) { + // Everything is so fast anyway that there's probably no point in doing what the to-do here said + final LootModifierContext context = new LootModifierContext(table, tableId, pool, entry, tableModified, poolModified); + + final LootModifier modifier = modifierEntry.getValue(); + if (!modifier.test(context)) continue; + + if (IS_DEV) LOGGER.warn("Modifier {} can modify table {}", modifierEntry.getKey(), tableId); + + + int result = modifier.apply(context); + + if (IS_DEV && result != MODIFIED_NONE) LOGGER.warn("Modifier {} modified table {} with modified mask {}", modifierEntry.getKey(), tableId, Integer.toUnsignedString(result, 2)); + + if ((result & MODIFIED_TABLE) == MODIFIED_TABLE) tableModified = true; + if ((result & MODIFIED_POOL) == MODIFIED_POOL) poolModified = true; + if ((result & MODIFIED_ENTRY) == MODIFIED_ENTRY) entriesModified++; + } + } + + poolsModified += poolModified ? 1 : 0; + } + if (tableModified) modifiedTableIds.add(tableId); } - modifiersApplied(amountModified, modifiers); - } + + LOGGER.info("Applied {} modifiers and modified {} entries, {} pools and {} loot tables in {}!", modifiers.size(), entriesModified, poolsModified, modifiedTableIds.size(), stopwatch.stop()); + + if (!IS_DEV) return; + + LOGGER.warn("Dev mode enabled, modified loot tables can be exported using the '/loot-table-modifier debug export' command"); + synchronized (MODIFIED_TABLE_IDs) { + MODIFIED_TABLE_IDs.clear(); + MODIFIED_TABLE_IDs.addAll(modifiedTableIds); + } + } private static Map loadModifiers(ResourceManager resourceManager, RegistryOps registryOps) { LOGGER.info("Loading loot table modifiers..."); + final Stopwatch stopwatch = Stopwatch.createStarted(); final Map result = new HashMap<>(); @@ -62,89 +152,101 @@ private static Map loadModifiers(ResourceManager resou final Identifier id = entry.getKey(); try { + LOGGER.debug("Loading load loot table modifier from '{}'", id); result.put( id, LootModifier.CODEC.decode(registryOps, JsonParser.parseReader(entry.getValue().getReader())).getOrThrow().getFirst() ); - } catch (IOException e) { + } catch (Exception e) { //noinspection StringConcatenationArgumentToLogCall - LOGGER.error("Failed to load loot table modifier from '" + id + "'!", e); + LOGGER.error("Failed to load loot table modifier from '%s'!".formatted(id), e); } } - LOGGER.info("Loaded {} loot modifiers", result.size()); + LOGGER.info("Loaded {} loot modifiers in {}!", result.size(), stopwatch.stop()); return result; } - // Returns: amount of loot tables modified - private static int applyModifiers(LootTable table, RegistryKey tableKey, Map modifiers) { - final Identifier currentId = tableKey.getValue(); - final List usable = modifiers.keySet().stream().filter(entry -> modifiers.get(entry).modifies().contains(currentId)).toList(); - - if (usable.isEmpty()) return 0; - - final List newPools = ImmutableList.builder() - .addAll(table.pools) - .addAll( - usable.stream() - .map(modifiers::get) - .map(LootModifier::pools) - .flatMap(List::stream) - .toList() - ) - .build(); - - ((LootTableAccessor) table).setPools(newPools); + private static void enableDebug() { + ResourceManagerHelper.registerBuiltinResourcePack(id("example_pack"), FabricLoader.getInstance().getModContainer(MOD_ID).orElseThrow(), Text.of("Example Pack"), ResourcePackActivationType.NORMAL); + CommandRegistrationCallback.EVENT.register((dispatcher, commandRegistryAccess, registrationEnvironment) -> dispatcher.register( + literal(MOD_ID) + .then( + literal("debug") + .then( + literal("export") + .executes( + LootTableModifier::executeExportCommand + ) + ) + ) + )); + } - for (Identifier modifierId : usable) { - final LootModifier modifier = modifiers.get(modifierId); + private static int executeExportCommand(CommandContext context) { + synchronized (MODIFIED_TABLE_IDs) { + final ServerCommandSource source = context.getSource(); + final MinecraftServer server = source.getServer(); - modifier.modifies().remove(currentId); + final DynamicOps ops = RegistryOps.of(JsonOps.INSTANCE, server.getRegistryManager()); + try { + final Path exportDir = FabricLoader.getInstance().getGameDir().resolve(".loot-table-modifier").resolve("export"); + if (Files.exists(exportDir)) PathUtils.deleteDirectory(exportDir); + + source.sendFeedback(() -> Text.literal("Exporting modified tables to ").append(Text.literal(exportDir.toString()).setStyle(Style.EMPTY.withUnderline(true).withColor(Formatting.WHITE).withHoverEvent(new HoverEvent.ShowText(Text.literal("Click to copy"))).withClickEvent(new ClickEvent.CopyToClipboard(exportDir.toAbsolutePath().toString())))), true); + final Stopwatch stopwatch = Stopwatch.createStarted(); + + for (Identifier id : MODIFIED_TABLE_IDs) { + final LootTable table = server.getReloadableRegistries().getLootTable(RegistryKey.of(RegistryKeys.LOOT_TABLE, id)); + final Path file = exportDir.resolve(id.getNamespace()).resolve(id.getPath() + ".json"); + Files.createDirectories(file.getParent()); + + LOGGER.warn("Exporting loot table to {}", file); + DataResult dataResult = LootTable.CODEC.encodeStart(ops, table); + final Optional optionalResult = dataResult.resultOrPartial(LOGGER::error); + final JsonElement result = optionalResult.orElseThrow(); + + LOGGER.warn("Writing loot table to {}", file); + + try (JsonWriter jsonWriter = new JsonWriter(Files.newBufferedWriter(file, StandardCharsets.UTF_8))) { + jsonWriter.setSerializeNulls(false); + jsonWriter.setIndent(" "); + JsonHelper.writeSorted(jsonWriter, result, DataProvider.JSON_KEY_SORTING_COMPARATOR); + } + } + source.sendFeedback(() -> Text.literal("Exported %s modified tables in %s".formatted(MODIFIED_TABLE_IDs.size(), stopwatch.stop())), true); + } catch (IOException e) { + throw new RuntimeException("Failed to export modified tables!", e); + } - if (modifier.modifies().isEmpty()) modifiers.remove(modifierId); + return 1; } - - return usable.size(); } - private static void modifiersApplied(int amountModified, Map modifiers) { - LOGGER.info("Modified {} loot tables!", amountModified); - - if (modifiers.isEmpty()) return; - - LOGGER.warn("There were unused modifiers:"); - for (Map.Entry entry : modifiers.entrySet()) { - LOGGER.warn("\tModifier '{}' failed to modify loot table for entities: ", entry.getKey()); - for (Identifier id : entry.getValue().modifies()) { - LOGGER.warn("\t\t- {}", id); - } - } + public static Identifier id(String path) { + return Identifier.of(MOD_ID, path); } /* - In 1.21.4, the 'Registry' class extends 'RegistryWrapper' and inherits the 'streamEntries' method from *it*. - In 1.20.5, the 'Registry' class *doesn't* extend the 'RegistryWrapper' and implements its own 'streamEntries' method. - Compiling on both versions works, because the names of the methods are the same, but they compile to different intermediary names, thus a jar compiled for 1.20.5 doesn't work on 1.21.4 and vice versa. - Solution: Turn the 'Registry' into a 'RegistryWrapper' as its 'streamEntries' retains the same intermediary on both versions. - If 'Registry' implements 'RegistryWrapper': cast it - Else: call 'getReadOnlyWrapper' on the registry (doesn't exist on 1.21.4, otherwise would've used 'registry.getReadOnlyWrapper().streamEntries()') + In 1.21.4, the 'Registry' class extends 'RegistryWrapper' and inherits the 'streamEntries' method from *it*. + In 1.20.5, the 'Registry' class *doesn't* extend the 'RegistryWrapper' and implements its own 'streamEntries' method. + Compiling on both versions works, because the names of the methods are the same, but they compile to different intermediary names, thus a jar compiled for 1.20.5 doesn't work on 1.21.4 and vice versa. + Solution: Turn the 'Registry' into a 'RegistryWrapper' as its 'streamEntries' retains the same intermediary on both versions. + If 'Registry' implements 'RegistryWrapper': cast it + Else: call 'getReadOnlyWrapper' on the registry (doesn't exist on 1.21.4, otherwise would've used 'registry.getReadOnlyWrapper().streamEntries()') */ - private static RegistryWrapper getRegistryAsWrapper(@NotNull Registry registry) { - //noinspection ConstantValue,RedundantSuppression: On lower versions, Registry doesn't extend RegistryWrapper and thus the 'isAssignableFrom' check can be false. The redundant supression is for the unchecked cast below. - if (RegistryWrapper.class.isAssignableFrom(registry.getClass())) + private static RegistryWrapper getRegistryAsWrapper(@NotNull Registry registry) { + //noinspection ConstantValue,RedundantSuppression: On lower versions, Registry doesn't extend RegistryWrapper and thus the 'isAssignableFrom' check can be false. The redundant supression is for the unchecked cast below. + if (RegistryWrapper.class.isAssignableFrom(registry.getClass())) //noinspection unchecked,RedundantCast: I swear it casts 🀞 - return (RegistryWrapper) registry; - - try { - //noinspection unchecked: Seriously I swear 🀞🀞 - return (RegistryWrapper) registry.getClass().getDeclaredMethod(FabricLoader.getInstance().getMappingResolver().mapMethodName("intermediary", "net.minecraft.class_2378", "method_46771", "()Lnet/minecraft/class_7225$class_7226;")).invoke(registry); - } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { - throw new RuntimeException(e); - } - } + return (RegistryWrapper) registry; - public static Identifier id(String path) { - return Identifier.of(MOD_ID, path); + try { + //noinspection unchecked: Seriously I swear 🀞🀞 + return (RegistryWrapper) registry.getClass().getDeclaredMethod(FabricLoader.getInstance().getMappingResolver().mapMethodName("intermediary", "net.minecraft.class_2378", "method_46771", "()Lnet/minecraft/class_7225$class_7226;")).invoke(registry); + } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + throw new RuntimeException(e); + } } } diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/datagen/LootModifierProvider.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/datagen/LootModifierProvider.java index 789f549..8e52fad 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/api/datagen/LootModifierProvider.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/datagen/LootModifierProvider.java @@ -2,47 +2,44 @@ import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput; import net.fabricmc.fabric.api.datagen.v1.provider.FabricCodecDataProvider; -import net.fabricmc.loader.api.FabricLoader; -import net.fabricmc.loader.api.MappingResolver; import net.minecraft.data.DataOutput; -import net.minecraft.entity.EntityType; -import net.minecraft.loot.LootPool; -import net.minecraft.loot.LootTable; -import net.minecraft.registry.RegistryKey; import net.minecraft.registry.RegistryWrapper; import net.minecraft.util.Identifier; -import top.offsetmonkey538.loottablemodifier.resource.LootModifier; +import org.jetbrains.annotations.NotNull; +import top.offsetmonkey538.loottablemodifier.api.resource.LootModifier; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.BiConsumer; -import java.util.function.Function; -import java.util.stream.Stream; import static top.offsetmonkey538.loottablemodifier.LootTableModifier.MOD_ID; /** * A datagen provider for creating loot modifiers. *
- * Override {@link #generate(RegistryWrapper.WrapperLookup) generate()} and use the {@code addModifier(...)} methods to add modifiers. + * Override {@link #generate(RegistryWrapper.WrapperLookup) generate()} and use the {@link #addModifier(Identifier, LootModifier.Builder)} method to add modifiers. *
*
{@code
  * @Override
  * protected void generate(RegistryWrapper.WrapperLookup lookup) {
- *     addModifier(
- *             Identifier.of("testmod", "drop_tnt"),
- *             LootPool.builder()
- *                     .rolls(ConstantLootNumberProvider.create(1))
- *                     .with(
- *                             ItemEntry.builder(Items.TNT)
- *                                     .apply(SetCountLootFunction.builder(UniformLootNumberProvider.create(1, 1)))
- *                     ),
- *             EntityType.CREEPER,
- *             EntityType.ZOMBIE
+ *      addModifier(
+ *          Identifier.of("testmod", "mobs_drop_tnt"),
+ *              LootModifier.builder()
+ *                  .conditionally(
+ *                      TablePredicate.builder()
+ *                          .name(EntityType.CREEPER)
+ *                          .name(EntityType.ZOMBIE)
+ *                  )
+ *                  .action(
+ *                      PoolAddAction.builder()
+ *                          .pool(
+ *                              LootPool.builder()
+ *                                  .rolls(ConstantLootNumberProvider.create(1))
+ *                                  .with(
+ *                                      ItemEntry.builder(Items.TNT)
+ *                                          .apply(SetCountLootFunction.builder(UniformLootNumberProvider.create(1, 1)))
+ *                                  )
+ *                          )
+ *                  )
  *     );
  * }
  * }
@@ -66,183 +63,19 @@ public String getName() { } /** - * Override and use {@code addModifier()} methods to add modifiers. + * Override and use {@link #addModifier(Identifier, LootModifier.Builder)} method to add modifiers. * * @param lookup A lookup for registries. */ protected abstract void generate(RegistryWrapper.WrapperLookup lookup); /** - * Adds a new loot table modifier for the given {@link EntityType}s. + * Adds a loot modifier using the provided name and builder. * - * @param name Name of this modifier - * @param builder The loot pool to add - * @param modifies The {@link EntityType} to add the modifier to - * @param modifiesAdditional Additional {@link EntityType}s to add the modifier to + * @param name the identifier of the modifier + * @param builder the builder to generate the loot modifier from */ - protected void addModifier(Identifier name, LootPool.Builder builder, EntityType modifies, EntityType... modifiesAdditional) { - addModifier( - name, - List.of(builder), - modifies, - modifiesAdditional - ); + protected void addModifier(@NotNull Identifier name, @NotNull LootModifier.Builder builder) { + provider.accept(name, builder.build()); } - - /** - * Adds a new loot table modifier for the given {@link EntityType}s. - * - * @param name Name of this modifier - * @param builders The loot pools to add - * @param modifies The {@link EntityType} to add the modifier to - * @param modifiesAdditional Additional {@link EntityType}s to add the modifier to - */ - protected void addModifier(Identifier name, List builders, EntityType modifies, EntityType... modifiesAdditional) { - addModifier( - name, - builders, - Stream.concat(Stream.of(modifies), Stream.of(modifiesAdditional)).map(EntityLootTableIdGetter.get).toList() - ); - } - - /** - * Adds a new loot table modifier for the given {@link RegistryKey}s. - * - * @param name Name of this modifier - * @param builder The loot pool to add - * @param modifies The {@link RegistryKey} to add the modifier to - * @param modifiesAdditional Additional {@link RegistryKey}s to add the modifier to - */ - protected void addModifier(Identifier name, LootPool.Builder builder, RegistryKey modifies, RegistryKey... modifiesAdditional) { - addModifier( - name, - List.of(builder), - modifies, - modifiesAdditional - ); - } - - /** - * Adds a new loot table modifier for the given {@link RegistryKey}s. - * - * @param name Name of this modifier - * @param builders The loot pools to add - * @param modifies The {@link RegistryKey} to add the modifier to - * @param modifiesAdditional Additional {@link RegistryKey}s to add the modifier to - */ - protected void addModifier(Identifier name, List builders, RegistryKey modifies, RegistryKey... modifiesAdditional) { - addModifier( - name, - builders, - Stream.concat(Stream.of(modifies), Stream.of(modifiesAdditional)).map(RegistryKey::getValue).toList() - ); - } - - /** - * Adds a new loot table modifier for the given {@link Identifier}s. - * - * @param name Name of this modifier - * @param builder The loot pool to add - * @param modifies The {@link Identifier} to add the modifier to - * @param modifiesAdditional Additional {@link Identifier}s to add the modifier to - */ - protected void addModifier(Identifier name, LootPool.Builder builder, Identifier modifies, Identifier... modifiesAdditional) { - addModifier( - name, - List.of(builder), - modifies, - modifiesAdditional - ); - } - - /** - * Adds a new loot table modifier for the given {@link Identifier}s. - * - * @param name Name of this modifier - * @param builders The loot pools to add - * @param modifies The {@link Identifier} to add the modifier to - * @param modifiesAdditional Additional {@link Identifier}s to add the modifier to - */ - protected void addModifier(Identifier name, List builders, Identifier modifies, Identifier... modifiesAdditional) { - addModifier( - name, - builders, - Stream.concat(Stream.of(modifies), Stream.of(modifiesAdditional)).toList() - ); - } - - private void addModifier(Identifier name, List builders, List modifies) { - provider.accept(name, new LootModifier( - new ArrayList<>(modifies), - builders.stream().map(LootPool.Builder::build).toList() - )); - } - - - - private static class EntityLootTableIdGetter { - // Resolver returns the provided name (like 'method_16351') when it fails to map it - private static final MappingResolver RESOLVER = FabricLoader.getInstance().getMappingResolver(); - - private static final String V1d21d2 = RESOLVER.mapMethodName("intermediary", "net.minecraft.class_1299", "method_16351", "()Ljava/util/Optional;"); - private static final String V1d20d5 = RESOLVER.mapMethodName("intermediary", "net.minecraft.class_1299", "method_16351", "()Lnet/minecraft/class_5321;"); - - // mod only supports down to 1.20.5 soo: private static final String V1d14d0 = RESOLVER.mapMethodName("intermediary", "net.minecraft.class_1299", "method_16351", "()Lnet/minecraft/util/Identifier;"); - - public static final Function, Identifier> get; - - // Should be executed when class is first loaded/accessed - static { - try { - //final Class entityType = EntityType.class; - final Class entityType = Class.forName(RESOLVER.mapClassName("intermediary", "net.minecraft.class_1299")); - final Method method; - - // 1.21.2 to future:tm: - if (isMethod(entityType, V1d21d2)) { - method = entityType.getDeclaredMethod(V1d21d2); - method.setAccessible(true); - get = entity -> { - try { - @SuppressWarnings("unchecked") - final Optional> optional = (Optional>) method.invoke(entity); - if (optional.isPresent()) return optional.get().getValue(); - throw new IllegalStateException("Entity '" + entity + "' has no loot table! (It is created with 'builder.dropsNothing()')"); - } catch (IllegalAccessException | InvocationTargetException e) { - throw new RuntimeException(e); - } - }; - } - - // 1.20.5 to 1.21.1 - else if (isMethod(entityType, V1d20d5)) { - method = entityType.getDeclaredMethod(V1d20d5); - method.setAccessible(true); - get = entity -> { - try { - return ((RegistryKey) method.invoke(entity)).getValue(); - } catch (IllegalAccessException | InvocationTargetException e) { - throw new RuntimeException(e); - } - }; - } - - else { - throw new IllegalStateException("No valid way to get entity loot table id found!"); - } - } catch (NoSuchMethodException | ClassNotFoundException e) { - throw new RuntimeException(e); - } - } - - private static boolean isMethod(Class clazz, String method) { - try { - clazz.getDeclaredMethod(method); - return true; - } catch (NoSuchMethodException e) { - LOGGER.warn("", e); - return false; - } - } - } -} \ No newline at end of file +} diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/LootModifier.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/LootModifier.java new file mode 100644 index 0000000..d3a3647 --- /dev/null +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/LootModifier.java @@ -0,0 +1,156 @@ +package top.offsetmonkey538.loottablemodifier.api.resource; + +import com.google.common.collect.ImmutableList; +import com.mojang.datafixers.util.Either; +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.loot.LootPool; +import net.minecraft.util.Identifier; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.UnmodifiableView; +import top.offsetmonkey538.loottablemodifier.api.datagen.LootModifierProvider; +import top.offsetmonkey538.loottablemodifier.api.resource.action.pool.PoolAddAction; +import top.offsetmonkey538.loottablemodifier.api.resource.action.LootModifierAction; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicate; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.table.TablePredicate; +import top.offsetmonkey538.loottablemodifier.api.resource.util.LootModifierContext; + +import java.util.*; +import java.util.function.Predicate; + +import static top.offsetmonkey538.loottablemodifier.LootTableModifier.LOGGER; +import static top.offsetmonkey538.loottablemodifier.api.resource.action.LootModifierAction.MODIFIED_NONE; + +/** + * A loot modifier. Use the {@link LootModifier.Builder} for building it and the {@link LootModifierProvider} for generating the files. + * + * @param actions a list of actions to apply + * @param predicate the predicate + */ +public record LootModifier(@NotNull @UnmodifiableView List actions, @NotNull LootModifierPredicate predicate) implements Predicate { + private static final Codec LEGACY_CODEC = RecordCodecBuilder.create(instance -> instance.group( + Codec.either(Identifier.CODEC, Identifier.CODEC.listOf()).fieldOf("modifies").forGetter(modifier -> { + throw new IllegalStateException("Tried using legacy loot table modifier codec for serialization for some reason!"); + }), + LootPool.CODEC.listOf().optionalFieldOf("pools").forGetter(lootModifier -> Optional.empty()), + LootPool.CODEC.listOf().optionalFieldOf("loot_pools").forGetter(lootModifier -> Optional.empty()) + ).apply(instance, (modifiesEither, pools, lootPools) -> new LootModifier(getActionsFromLegacyCodec(pools, lootPools), getPredicateFromLegacyCodec(modifiesEither)))); + + private static final Codec CURRENT_CODEC = RecordCodecBuilder.create(instance -> instance.group( + LootModifierAction.CODEC.listOf().fieldOf("actions").forGetter(LootModifier::actions), + LootModifierPredicate.CODEC.fieldOf("predicate").forGetter(LootModifier::predicate) + ).apply(instance, LootModifier::new)); + + public static final Codec CODEC = Codec.either( + CURRENT_CODEC, + LEGACY_CODEC + ).xmap(either -> either.map(it -> it, it -> it), Either::left); // Always encode as current codec, which is on the left. + + @SuppressWarnings("OptionalUsedAsFieldOrParameterType") // from the codec + private static @NotNull List getActionsFromLegacyCodec(@NotNull Optional> pools, @NotNull Optional> lootPools) { + List actions = null; + + if (pools.isPresent() && lootPools.isPresent()) throw new IllegalStateException("Both \"pools\" and \"loot_pools\" present in legacy loot modifier!"); + + if (pools.isPresent()) actions = List.of(new PoolAddAction(pools.get())); + if (lootPools.isPresent()) actions = List.of(new PoolAddAction(lootPools.get())); + + if (actions == null) throw new IllegalStateException("Neither \"pools\" nor \"loot_pools\" present in legacy loot modifier!"); + return actions; + } + + private static @NotNull LootModifierPredicate getPredicateFromLegacyCodec(@NotNull Either> modifiesEither) { + final TablePredicate.Builder predicateBuilder = TablePredicate.builder(); + for (final Identifier currentId : modifiesEither.map(List::of, it -> it)) { + predicateBuilder.name(currentId); + } + return predicateBuilder.build(); + } + + /** + * Applied all the actions of this modifier using the provided context. + * + * @param context context to modify + * @return highest modification level from applied actions. {@link LootModifierAction#MODIFIED_NONE} when no action was applied. + * @see LootModifierAction#MODIFIED_NONE + * @see LootModifierAction#MODIFIED_TABLE + * @see LootModifierAction#MODIFIED_POOL + * @see LootModifierAction#MODIFIED_ENTRY + */ + public int apply(final @NotNull LootModifierContext context) { + int result = MODIFIED_NONE; + for (LootModifierAction action : actions) { + result = result | action.apply(context); + } + return result; + } + + /** + * Tests the predicate of this loot modifier against the provided context. + * + * @param context the context to match against + * @return if the predicate of this loot modifier matched the provided context + */ + public boolean test(final @NotNull LootModifierContext context) { + return predicate.test(context); + } + + /** + * Creates a builder for {@link LootModifier} + * + * @return a new {@link LootModifier.Builder} + */ + @Contract("->new") + public static LootModifier.Builder builder() { + return new LootModifier.Builder(); + } + + /** + * Builder for {@link LootModifier} + */ + public static class Builder { + private Builder() { + + } + + private final ImmutableList.Builder actions = ImmutableList.builder(); + private LootModifierPredicate predicate; + + /** + * Adds an action + * + * @param action the action to add + * @return this + */ + @Contract("_->this") + public LootModifier.Builder action(@NotNull LootModifierAction.Builder action) { + this.actions.add(action.build()); + return this; + } + + /** + * Sets the predicate + *
+ * Loot modifier may only have one predicate and this may only be called once! + * + * @param predicate the predicate to use + * @return this + */ + @Contract("_->this") + public LootModifier.Builder conditionally(@NotNull LootModifierPredicate.Builder predicate) { + if (this.predicate != null) LOGGER.error("Predicate has already been set for this builder! The previously set predicate will be overwritten! Please use the 'LootModifierPredicate.Builder#and()' and 'LootModifierPredicate.Builder#or()' methods for adding multiple conditions!"); + this.predicate = predicate.build(); + return this; + } + + /** + * Builds the {@link LootModifier} + * + * @return a built {@link LootModifier} + */ + public LootModifier build() { + return new LootModifier(this.actions.build(), this.predicate); + } + } +} diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/LootModifierAction.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/LootModifierAction.java new file mode 100644 index 0000000..bdf74ab --- /dev/null +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/LootModifierAction.java @@ -0,0 +1,63 @@ +package top.offsetmonkey538.loottablemodifier.api.resource.action; + +import com.mojang.serialization.Codec; +import org.jetbrains.annotations.NotNull; +import top.offsetmonkey538.loottablemodifier.api.resource.util.LootModifierContext; + +/** + * A loot modifier action + */ +public interface LootModifierAction { + /** + * Codec containing the type id + */ + Codec CODEC = LootModifierActionType.REGISTRY.getCodec().dispatch(LootModifierAction::getType, LootModifierActionType::codec); + + /** + * Bitmask specifying that the action modified nothing + */ + int MODIFIED_NONE = 0b000; + /** + * Bitmask specifying that the action modified a table + */ + int MODIFIED_TABLE = 0b001; + /** + * Bitmask specifying that the action modified a pool + */ + int MODIFIED_POOL = 0b011; + /** + * Bitmask specifying that the action modified an entry + */ + int MODIFIED_ENTRY = 0b111; + + /** + * Returns the type of this action. + * + * @return the {@link LootModifierActionType type} of this action. + */ + LootModifierActionType getType(); + + /** + * Applies this action to the provided context + * @param context the context to apply to + * @return the applied modification level + * @see #MODIFIED_NONE + * @see #MODIFIED_TABLE + * @see #MODIFIED_POOL + * @see #MODIFIED_ENTRY + */ + int apply(final @NotNull LootModifierContext context); + + /** + * A builder for loot modifier actions. + */ + @FunctionalInterface + interface Builder { + /** + * Builds the action + * + * @return a built {@link LootModifierAction} + */ + LootModifierAction build(); + } +} diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/LootModifierActionType.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/LootModifierActionType.java new file mode 100644 index 0000000..780da7a --- /dev/null +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/LootModifierActionType.java @@ -0,0 +1,23 @@ +package top.offsetmonkey538.loottablemodifier.api.resource.action; + +import com.mojang.serialization.Lifecycle; +import com.mojang.serialization.MapCodec; +import net.minecraft.registry.Registry; +import net.minecraft.registry.RegistryKey; +import net.minecraft.registry.SimpleRegistry; +import org.jetbrains.annotations.NotNull; +import top.offsetmonkey538.loottablemodifier.LootTableModifier; + +/** + * The type of a {@link LootModifierAction}, holds the codec. + * + * @param codec the codec for this action. + */ +public record LootModifierActionType(@NotNull MapCodec codec) { + /** + * Registry of {@link LootModifierActionType}s + */ + public static final Registry REGISTRY = new SimpleRegistry<>( + RegistryKey.ofRegistry(LootTableModifier.id("loot_modifier_action_types")), Lifecycle.stable() + ); +} diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/LootModifierActionTypes.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/LootModifierActionTypes.java new file mode 100644 index 0000000..5d263f3 --- /dev/null +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/LootModifierActionTypes.java @@ -0,0 +1,61 @@ +package top.offsetmonkey538.loottablemodifier.api.resource.action; + +import com.mojang.serialization.MapCodec; +import net.minecraft.registry.Registry; +import net.minecraft.util.Identifier; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import top.offsetmonkey538.loottablemodifier.api.resource.action.entry.EntryAddAction; +import top.offsetmonkey538.loottablemodifier.api.resource.action.entry.EntryRemoveAction; +import top.offsetmonkey538.loottablemodifier.api.resource.action.pool.PoolAddAction; +import top.offsetmonkey538.loottablemodifier.api.resource.action.entry.EntryItemSetAction; +import top.offsetmonkey538.loottablemodifier.api.resource.action.pool.PoolRemoveAction; + +import static top.offsetmonkey538.loottablemodifier.LootTableModifier.id; + +/** + * Contains all {@link LootModifierAction} types available in Loot Table Modifier. + *
+ * Use their builders to create them. + */ +public final class LootModifierActionTypes { + private LootModifierActionTypes() { + + } + + /** + * Type of {@link PoolAddAction} + */ + public static final LootModifierActionType POOL_ADD = register(id("pool_add"), PoolAddAction.CODEC); + /** + * Type of {@link PoolRemoveAction} + */ + public static final LootModifierActionType POOL_REMOVE = register(id("pool_remove"), PoolRemoveAction.CODEC); + + /** + * Type of {@link EntryAddAction} + */ + public static final LootModifierActionType ENTRY_ADD = register(id("entry_add"), EntryAddAction.CODEC); + /** + * Type of {@link EntryRemoveAction} + */ + public static final LootModifierActionType ENTRY_REMOVE = register(id("entry_remove"), EntryRemoveAction.CODEC); + /** + * Type of {@link EntryItemSetAction} + */ + public static final LootModifierActionType ENTRY_ITEM_SET = register(id("entry_item_set"), EntryItemSetAction.CODEC); + + private static LootModifierActionType register(final @NotNull Identifier id, final @NotNull MapCodec codec) { + return Registry.register(LootModifierActionType.REGISTRY, id, new LootModifierActionType(codec)); + } + + /** + * Registers action types by loading the class. + *
+ * Only for the loot table modifier initializer to call, NO TOUCHY >:( + */ + @ApiStatus.Internal + public static void register() { + // Registers action types by loading the class + } +} diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/entry/EntryAddAction.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/entry/EntryAddAction.java new file mode 100644 index 0000000..0a9ceab --- /dev/null +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/entry/EntryAddAction.java @@ -0,0 +1,88 @@ +package top.offsetmonkey538.loottablemodifier.api.resource.action.entry; + +import com.google.common.collect.ImmutableList; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.loot.LootPool; +import net.minecraft.loot.entry.LootPoolEntry; +import net.minecraft.loot.entry.LootPoolEntryTypes; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import top.offsetmonkey538.loottablemodifier.api.resource.action.LootModifierAction; +import top.offsetmonkey538.loottablemodifier.api.resource.action.LootModifierActionType; +import top.offsetmonkey538.loottablemodifier.api.resource.action.LootModifierActionTypes; +import top.offsetmonkey538.loottablemodifier.api.resource.util.LootModifierContext; +import top.offsetmonkey538.loottablemodifier.mixin.LootPoolAccessor; + +import java.util.List; + +/** + * Adds the provided entries to matched pools + * + * @param entries the entries to add + */ +public record EntryAddAction(List entries) implements LootModifierAction { + public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( + LootPoolEntryTypes.CODEC.listOf().fieldOf("entries").forGetter(EntryAddAction::entries) + ).apply(instance, EntryAddAction::new)); + + @Override + public LootModifierActionType getType() { + return LootModifierActionTypes.ENTRY_ADD; + } + + @Override + public int apply(@NotNull LootModifierContext context) { + if (context.poolAlreadyModified()) return MODIFIED_NONE; + + final LootPool pool = context.pool(); + if (pool == null) return MODIFIED_NONE; + + final List newEntries = ImmutableList.builder() + .addAll(pool.entries) + .addAll(this.entries) + .build(); + + ((LootPoolAccessor) pool).setEntries(newEntries); + + return MODIFIED_ENTRY; + } + + /** + * Creates a builder for {@link EntryAddAction} + * + * @return a new {@link EntryAddAction.Builder} + */ + @Contract("->new") + public static EntryAddAction.Builder builder() { + return new EntryAddAction.Builder(); + } + + /** + * Builder for {@link EntryAddAction} + */ + public static class Builder implements LootModifierAction.Builder { + private Builder() { + + } + + private final ImmutableList.Builder entries = ImmutableList.builder(); + + /** + * Adds an entry + * + * @param entryBuilder The entry to add + * @return this + */ + @Contract("_->this") + public EntryAddAction.Builder entry(LootPoolEntry.Builder entryBuilder) { + this.entries.add(entryBuilder.build()); + return this; + } + + @Override + public EntryAddAction build() { + return new EntryAddAction(entries.build()); + } + } +} diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/entry/EntryItemSetAction.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/entry/EntryItemSetAction.java new file mode 100644 index 0000000..ebdd089 --- /dev/null +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/entry/EntryItemSetAction.java @@ -0,0 +1,105 @@ +package top.offsetmonkey538.loottablemodifier.api.resource.action.entry; + +import com.google.common.collect.ImmutableList; +import com.mojang.serialization.Codec; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.item.Item; +import net.minecraft.item.ItemConvertible; +import net.minecraft.loot.LootPool; +import net.minecraft.loot.entry.ItemEntry; +import net.minecraft.loot.entry.LootPoolEntry; +import net.minecraft.registry.Registries; +import net.minecraft.registry.entry.RegistryEntry; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import top.offsetmonkey538.loottablemodifier.api.resource.action.LootModifierActionTypes; +import top.offsetmonkey538.loottablemodifier.mixin.ItemEntryAccessor; +import top.offsetmonkey538.loottablemodifier.mixin.LootPoolAccessor; +import top.offsetmonkey538.loottablemodifier.api.resource.util.LootModifierContext; +import top.offsetmonkey538.loottablemodifier.api.resource.action.LootModifierAction; +import top.offsetmonkey538.loottablemodifier.api.resource.action.LootModifierActionType; + +/** + * Sets the item in matched item entries + * + * @param item the new item to replace the existing one with + * @param canReplaceEntry if other types of entries can be replaced with a basic item entry containing the target item + */ +public record EntryItemSetAction(RegistryEntry item, boolean canReplaceEntry) implements LootModifierAction { + public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( + Item.ENTRY_CODEC.fieldOf("name").forGetter(EntryItemSetAction::item), + Codec.BOOL.optionalFieldOf("canReplaceEntry", false).forGetter(EntryItemSetAction::canReplaceEntry) + ).apply(instance, EntryItemSetAction::new)); + + @Override + public LootModifierActionType getType() { + return LootModifierActionTypes.ENTRY_ITEM_SET; + } + + @Override + public int apply(@NotNull LootModifierContext context) { + final LootPoolEntry entry = context.entry(); + if (entry == null) return MODIFIED_NONE; + + if (entry instanceof ItemEntry itemEntry) { + ((ItemEntryAccessor) itemEntry).setItem(item); + return MODIFIED_ENTRY; + } + // Matched entry is not an ItemEntry, check if entry replacing is on + if (!canReplaceEntry) return MODIFIED_NONE; + + final LootPool pool = context.pool(); + if (pool == null) return MODIFIED_NONE; + + final ImmutableList.Builder newEntriesBuilder = ImmutableList.builder(); + + for (LootPoolEntry originalEntry : pool.entries) { + if (originalEntry == entry) continue; // I think we do want '==' here as the references should be the same? + newEntriesBuilder.add(originalEntry); + } + ((LootPoolAccessor) pool).setEntries(newEntriesBuilder.build()); + + return MODIFIED_ENTRY; + } + + /** + * Creates a builder for {@link EntryItemSetAction} + * + * @param item the new item to replace the existing one with + * @return a new {@link EntryItemSetAction.Builder} + */ + @Contract("_->new") + public static EntryItemSetAction.Builder builder(@NotNull ItemConvertible item) { + return new EntryItemSetAction.Builder(item); + } + + /** + * Builder for {@link EntryItemSetAction} + */ + public static class Builder implements LootModifierAction.Builder { + private final RegistryEntry item; + private boolean canReplaceEntry; + + private Builder(@NotNull ItemConvertible item) { + this.item = Registries.ITEM.getEntry(item.asItem()); + } + + /** + * Sets if other types of entries can be replaced with a basic item entry containing the target item + * + * @param canReplaceEntry if other types of entries can be replaced with a basic item entry containing the target item + * @return this + */ + @Contract("_->this") + public EntryItemSetAction.Builder setCanReplaceEntry(boolean canReplaceEntry) { + this.canReplaceEntry = canReplaceEntry; + return this; + } + + @Override + public EntryItemSetAction build() { + return new EntryItemSetAction(item, canReplaceEntry); + } + } +} diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/entry/EntryRemoveAction.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/entry/EntryRemoveAction.java new file mode 100644 index 0000000..d07f05e --- /dev/null +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/entry/EntryRemoveAction.java @@ -0,0 +1,56 @@ +package top.offsetmonkey538.loottablemodifier.api.resource.action.entry; + +import com.google.common.collect.ImmutableList; +import com.mojang.serialization.Codec; +import com.mojang.serialization.Decoder; +import com.mojang.serialization.Encoder; +import com.mojang.serialization.MapCodec; +import net.minecraft.loot.LootPool; +import net.minecraft.loot.entry.LootPoolEntry; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import top.offsetmonkey538.loottablemodifier.api.resource.action.LootModifierAction; +import top.offsetmonkey538.loottablemodifier.api.resource.action.LootModifierActionType; +import top.offsetmonkey538.loottablemodifier.api.resource.action.LootModifierActionTypes; +import top.offsetmonkey538.loottablemodifier.api.resource.util.LootModifierContext; +import top.offsetmonkey538.loottablemodifier.mixin.LootPoolAccessor; + +/** + * Removes the matched entries from their pools + */ +public record EntryRemoveAction() implements LootModifierAction { + public static final MapCodec CODEC = Codec.of(Encoder.empty(), Decoder.unit(EntryRemoveAction::new)); + + @Override + public LootModifierActionType getType() { + return LootModifierActionTypes.ENTRY_REMOVE; + } + + @Override + public int apply(@NotNull LootModifierContext context) { + final LootPool pool = context.pool(); + final LootPoolEntry entry = context.entry(); + if (pool == null || entry == null) return MODIFIED_NONE; + + final ImmutableList.Builder newEntriesBuilder = ImmutableList.builder(); + + for (final LootPoolEntry originalEntry : pool.entries) { + if (originalEntry == entry) continue; + newEntriesBuilder.add(originalEntry); + } + + ((LootPoolAccessor) pool).setEntries(newEntriesBuilder.build()); + + return MODIFIED_ENTRY; + } + + /** + * Creates a builder for {@link EntryRemoveAction} + * + * @return a new {@link EntryRemoveAction.Builder} + */ + @Contract("->new") + public static EntryRemoveAction.Builder builder() { + return EntryRemoveAction::new; + } +} diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/pool/PoolAddAction.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/pool/PoolAddAction.java new file mode 100644 index 0000000..f750d17 --- /dev/null +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/pool/PoolAddAction.java @@ -0,0 +1,83 @@ +package top.offsetmonkey538.loottablemodifier.api.resource.action.pool; + +import com.google.common.collect.ImmutableList; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.loot.LootPool; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import top.offsetmonkey538.loottablemodifier.api.resource.action.LootModifierActionTypes; +import top.offsetmonkey538.loottablemodifier.mixin.LootTableAccessor; +import top.offsetmonkey538.loottablemodifier.api.resource.util.LootModifierContext; +import top.offsetmonkey538.loottablemodifier.api.resource.action.LootModifierAction; +import top.offsetmonkey538.loottablemodifier.api.resource.action.LootModifierActionType; + +import java.util.List; + +/** + * Adds the provided pools to matched tables + * + * @param pools the pools to add + */ +public record PoolAddAction(List pools) implements LootModifierAction { + public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( + LootPool.CODEC.listOf().fieldOf("pools").forGetter(PoolAddAction::pools) + ).apply(instance, PoolAddAction::new)); + + @Override + public LootModifierActionType getType() { + return LootModifierActionTypes.POOL_ADD; + } + + @Override + public int apply(@NotNull LootModifierContext context) { + if (context.tableAlreadyModified()) return MODIFIED_NONE; + + final List newPools = ImmutableList.builder() + .addAll(context.table().pools) + .addAll(this.pools) + .build(); + + ((LootTableAccessor) context.table()).setPools(newPools); + + return MODIFIED_POOL; + } + + /** + * Creates a builder for {@link PoolAddAction} + * + * @return a new {@link PoolAddAction.Builder} + */ + @Contract("->new") + public static PoolAddAction.Builder builder() { + return new PoolAddAction.Builder(); + } + + /** + * Builder for {@link PoolAddAction} + */ + public static class Builder implements LootModifierAction.Builder { + private Builder() { + + } + + private final ImmutableList.Builder pools = ImmutableList.builder(); + + /** + * Adds a pool + * + * @param poolBuilder The pool to add + * @return this + */ + @Contract("_->this") + public PoolAddAction.Builder pool(LootPool.Builder poolBuilder) { + this.pools.add(poolBuilder.build()); + return this; + } + + @Override + public PoolAddAction build() { + return new PoolAddAction(pools.build()); + } + } +} diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/pool/PoolRemoveAction.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/pool/PoolRemoveAction.java new file mode 100644 index 0000000..e1debda --- /dev/null +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/action/pool/PoolRemoveAction.java @@ -0,0 +1,56 @@ +package top.offsetmonkey538.loottablemodifier.api.resource.action.pool; + +import com.google.common.collect.ImmutableList; +import com.mojang.serialization.Codec; +import com.mojang.serialization.Decoder; +import com.mojang.serialization.Encoder; +import com.mojang.serialization.MapCodec; +import net.minecraft.loot.LootPool; +import net.minecraft.loot.LootTable; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import top.offsetmonkey538.loottablemodifier.api.resource.action.LootModifierActionTypes; +import top.offsetmonkey538.loottablemodifier.mixin.LootTableAccessor; +import top.offsetmonkey538.loottablemodifier.api.resource.util.LootModifierContext; +import top.offsetmonkey538.loottablemodifier.api.resource.action.LootModifierAction; +import top.offsetmonkey538.loottablemodifier.api.resource.action.LootModifierActionType; + +/** + * Removes the matched pools from their tables + */ +public record PoolRemoveAction() implements LootModifierAction { + public static final MapCodec CODEC = Codec.of(Encoder.empty(), Decoder.unit(PoolRemoveAction::new)); + + @Override + public LootModifierActionType getType() { + return LootModifierActionTypes.POOL_REMOVE; + } + + @Override + public int apply(@NotNull LootModifierContext context) { + final LootTable table = context.table(); + final LootPool pool = context.pool(); + if (pool == null) return MODIFIED_NONE; + + final ImmutableList.Builder newPoolsBuilder = ImmutableList.builder(); + + for (LootPool originalPool : table.pools) { + if (originalPool == pool) continue; // I think we do want '==' here as the references should be the same? + newPoolsBuilder.add(originalPool); + } + + ((LootTableAccessor) table).setPools(newPoolsBuilder.build()); + + return MODIFIED_POOL; + } + + /** + * Creates a builder for {@link PoolRemoveAction} + * + * @return a new {@link PoolRemoveAction.Builder} + */ + @Contract("->new") + public static PoolRemoveAction.Builder builder() { + return PoolRemoveAction::new; + } +} diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/LootModifierPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/LootModifierPredicate.java new file mode 100644 index 0000000..a8eb983 --- /dev/null +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/LootModifierPredicate.java @@ -0,0 +1,87 @@ +package top.offsetmonkey538.loottablemodifier.api.resource.predicate; + +import com.mojang.serialization.Codec; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import top.offsetmonkey538.loottablemodifier.api.resource.util.LootModifierContext; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.op.AllOfPredicate; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.op.AnyOfPredicate; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.op.InvertedPredicate; + +import java.util.function.Predicate; + +/** + * A loot modifier predicate + */ +public interface LootModifierPredicate extends Predicate { + /** + * Codec containing the type id + */ + Codec CODEC = LootModifierPredicateType.REGISTRY.getCodec().dispatch(LootModifierPredicate::getType, LootModifierPredicateType::codec); + + /** + * Returns the type of this action. + * + * @return the {@link LootModifierPredicateType type} of this action. + */ + LootModifierPredicateType getType(); + + /** + * Tests this predicate against the provided context. + * + * @param context the context to match against + * @return if this predicate matched the provided context + */ + boolean test(final @NotNull LootModifierContext context); + + /** + * A builder for loot modifier predicates. + */ + @FunctionalInterface + interface Builder { + /** + * Builds the predicate + * + * @return a built {@link LootModifierPredicate} + */ + LootModifierPredicate build(); + + /** + * Inverts this builder. + *
+ * Wraps this builder in an {@link InvertedPredicate} + * + * @return An inverted version of this builder. + */ + @Contract("->new") + default Builder invert() { + return InvertedPredicate.builder(this); + } + + /** + * Adds another predicate builder in an {@code OR} relationship. + *
+ * Wraps this and the provided builder in an {@link AnyOfPredicate} + * + * @param otherPredicate The other predicate + * @return A builder matching when this builder or the provided other builder match. + */ + @Contract("_->new") + default LootModifierPredicate.Builder or(@NotNull LootModifierPredicate.Builder otherPredicate) { + return AnyOfPredicate.builder().or(this).or(otherPredicate); + } + + /** + * Adds another predicate builder in an {@code AND} relationship. + *
+ * Wraps this and the provided builder in an {@link AllOfPredicate} + * + * @param otherPredicate The other predicate + * @return A builder matching when this builder and the provided other builder match. + */ + @Contract("_->new") + default LootModifierPredicate.Builder and(@NotNull LootModifierPredicate.Builder otherPredicate) { + return AllOfPredicate.builder().and(this).and(otherPredicate); + } + } +} diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/LootModifierPredicateType.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/LootModifierPredicateType.java new file mode 100644 index 0000000..709f0aa --- /dev/null +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/LootModifierPredicateType.java @@ -0,0 +1,23 @@ +package top.offsetmonkey538.loottablemodifier.api.resource.predicate; + +import com.mojang.serialization.Lifecycle; +import com.mojang.serialization.MapCodec; +import net.minecraft.registry.Registry; +import net.minecraft.registry.RegistryKey; +import net.minecraft.registry.SimpleRegistry; +import org.jetbrains.annotations.NotNull; +import top.offsetmonkey538.loottablemodifier.LootTableModifier; + +/** + * They type of a {@link LootModifierPredicate}, holds the codec. + * + * @param codec the codec for this predicate + */ +public record LootModifierPredicateType(@NotNull MapCodec codec) { + /** + * Registry of {@link LootModifierPredicateType}s + */ + public static final Registry REGISTRY = new SimpleRegistry<>( + RegistryKey.ofRegistry(LootTableModifier.id("loot_modifier_predicate_types")), Lifecycle.stable() + ); +} diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/LootModifierPredicateTypes.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/LootModifierPredicateTypes.java new file mode 100644 index 0000000..8981466 --- /dev/null +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/LootModifierPredicateTypes.java @@ -0,0 +1,62 @@ +package top.offsetmonkey538.loottablemodifier.api.resource.predicate; + +import com.mojang.serialization.MapCodec; +import net.minecraft.registry.Registry; +import net.minecraft.util.Identifier; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.entry.EntryItemPredicate; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.op.AllOfPredicate; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.op.AnyOfPredicate; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.op.InvertedPredicate; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.table.TablePredicate; + +import static top.offsetmonkey538.loottablemodifier.LootTableModifier.id; + +/** + * Contains all {@link LootModifierPredicate} types available in Loot Table Modifier + *
+ * Use their builders to create them. + */ +public final class LootModifierPredicateTypes { + private LootModifierPredicateTypes() { + + } + + /** + * Type of {@link InvertedPredicate} + */ + public static final LootModifierPredicateType INVERTED = register(id("inverted"), InvertedPredicate.CODEC); + /** + * Type of {@link AnyOfPredicate} + */ + public static final LootModifierPredicateType ANY_OF = register(id("any_of"), AnyOfPredicate.CODEC); + /** + * Type of {@link AllOfPredicate} + */ + public static final LootModifierPredicateType ALL_OF = register(id("all_of"), AllOfPredicate.CODEC); + + /** + * Type of {@link EntryItemPredicate} + */ + public static final LootModifierPredicateType ENTRY_ITEM = register(id("entry_item"), EntryItemPredicate.CODEC); + + /** + * Type of {@link TablePredicate} + */ + public static final LootModifierPredicateType TABLE = register(id("table"), TablePredicate.CODEC); + + private static LootModifierPredicateType register(final @NotNull Identifier id, final @NotNull MapCodec codec) { + return Registry.register(LootModifierPredicateType.REGISTRY, id, new LootModifierPredicateType(codec)); + } + + /** + * Registers predicate types by loading the class. + *
+ * Only for the loot table modifier initializer to call, NO TOUCHY >:( + */ + @ApiStatus.Internal + public static void register() { + // Registers predicate types by loading the class + } +} diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/entry/EntryItemPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/entry/EntryItemPredicate.java new file mode 100644 index 0000000..72d4710 --- /dev/null +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/entry/EntryItemPredicate.java @@ -0,0 +1,70 @@ +package top.offsetmonkey538.loottablemodifier.api.resource.predicate.entry; + +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.item.ItemConvertible; +import net.minecraft.loot.entry.ItemEntry; +import net.minecraft.registry.Registries; +import net.minecraft.util.Identifier; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicateTypes; +import top.offsetmonkey538.loottablemodifier.api.resource.util.LootModifierContext; +import top.offsetmonkey538.loottablemodifier.api.resource.util.RegexPattern; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicate; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicateType; + +/** + * Matches an item entry based on its item + * + * @param name the {@link RegexPattern} matching the item identifier + */ +public record EntryItemPredicate(RegexPattern name) implements LootModifierPredicate { + public static final MapCodec CODEC = RecordCodecBuilder.mapCodec( + instance -> instance.group(RegexPattern.CODEC.fieldOf("name").forGetter(EntryItemPredicate::name)).apply(instance, EntryItemPredicate::new) + ); + + @Override + public LootModifierPredicateType getType() { + return LootModifierPredicateTypes.ENTRY_ITEM; + } + + @Override + public boolean test(@NotNull LootModifierContext context) { + // No need for a separate null check of entry as null isn't an instance of ItemEntry + if (!(context.entry() instanceof ItemEntry itemEntry)) return false; + + return name.matches(itemEntry.item.getIdAsString()); + } + + /** + * Creates a builder for {@link EntryItemPredicate} matching the provided item + * + * @param name the item to match + * @return a new {@link EntryItemPredicate.Builder} + */ + @Contract("_->new") + public static EntryItemPredicate.Builder builder(ItemConvertible name) { + return builder(Registries.ITEM.getId(name.asItem())); + } + /** + * Creates a builder for {@link EntryItemPredicate} matching the item based on the provided identifier + * + * @param name the item id to match + * @return a new {@link EntryItemPredicate.Builder} + */ + @Contract("_->new") + public static EntryItemPredicate.Builder builder(Identifier name) { + return builder(RegexPattern.literal(name)); + } + /** + * Creates a builder for {@link EntryItemPredicate} matching the provided item + * + * @param name the {@link RegexPattern} to match the item id with + * @return a new {@link EntryItemPredicate.Builder} + */ + @Contract("_->new") + public static EntryItemPredicate.Builder builder(RegexPattern name) { + return () -> new EntryItemPredicate(name); + } +} diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/AllOfPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/AllOfPredicate.java new file mode 100644 index 0000000..7ae03ab --- /dev/null +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/AllOfPredicate.java @@ -0,0 +1,60 @@ +package top.offsetmonkey538.loottablemodifier.api.resource.predicate.op; + +import com.mojang.serialization.MapCodec; +import net.minecraft.util.Util; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicateTypes; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicate; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicateType; + +import java.util.List; + +/** + * Matches when all the provided predicates match + */ +public class AllOfPredicate extends TermsPredicate { + /** + * The codec + */ + public static final MapCodec CODEC = createCodec(AllOfPredicate::new); + + private AllOfPredicate(final List terms) { + super(terms, Util.allOf(terms)); + } + + @Override + public LootModifierPredicateType getType() { + return LootModifierPredicateTypes.ALL_OF; + } + + /** + * Creates a builder for {@link AllOfPredicate} + * + * @return a new {@link AllOfPredicate.Builder} + */ + public static AllOfPredicate.Builder builder() { + return new AllOfPredicate.Builder(); + } + + /** + * Builder for {@link AllOfPredicate} + */ + public static class Builder extends TermsPredicate.Builder { + private Builder() { + + } + + @Override + @Contract("_->this") + public AllOfPredicate.Builder and(LootModifierPredicate.@NotNull Builder builder) { + this.add(builder); + return this; + } + + @Override + protected AllOfPredicate build(List terms) { + return new AllOfPredicate(terms); + } + } +} diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/AnyOfPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/AnyOfPredicate.java new file mode 100644 index 0000000..a754d55 --- /dev/null +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/AnyOfPredicate.java @@ -0,0 +1,61 @@ +package top.offsetmonkey538.loottablemodifier.api.resource.predicate.op; + +import com.mojang.serialization.MapCodec; +import net.minecraft.util.Util; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicateTypes; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicate; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicateType; + +import java.util.List; + +/** + * Matches when any of the provided predicates match + */ +public class AnyOfPredicate extends TermsPredicate { + /** + * The codec + */ + public static final MapCodec CODEC = createCodec(AnyOfPredicate::new); + + private AnyOfPredicate(final List terms) { + super(terms, Util.anyOf(terms)); + } + + @Override + public LootModifierPredicateType getType() { + return LootModifierPredicateTypes.ANY_OF; + } + + /** + * Creates a builder for {@link AnyOfPredicate} + * + * @return a new {@link AnyOfPredicate.Builder} + */ + @Contract("->new") + public static AnyOfPredicate.Builder builder() { + return new AnyOfPredicate.Builder(); + } + + /** + * Builder for {@link AnyOfPredicate} + */ + public static class Builder extends TermsPredicate.Builder { + private Builder() { + + } + + @Override + @Contract("_->this") + public AnyOfPredicate.Builder or(@NotNull LootModifierPredicate.Builder builder) { + this.add(builder); + return this; + } + + @Override + protected AnyOfPredicate build(List terms) { + return new AnyOfPredicate(terms); + } + } +} diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/InvertedPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/InvertedPredicate.java new file mode 100644 index 0000000..727e12a --- /dev/null +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/InvertedPredicate.java @@ -0,0 +1,40 @@ +package top.offsetmonkey538.loottablemodifier.api.resource.predicate.op; + +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import org.jetbrains.annotations.NotNull; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicateTypes; +import top.offsetmonkey538.loottablemodifier.api.resource.util.LootModifierContext; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicate; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicateType; + +/** + * Matches when the provided predicate doesn't + * + * @param term the predicate to invert + */ +public record InvertedPredicate(LootModifierPredicate term) implements LootModifierPredicate { + public static final MapCodec CODEC = RecordCodecBuilder.mapCodec( + instance -> instance.group(LootModifierPredicate.CODEC.fieldOf("term").forGetter(InvertedPredicate::term)).apply(instance, InvertedPredicate::new) + ); + + @Override + public LootModifierPredicateType getType() { + return LootModifierPredicateTypes.INVERTED; + } + + @Override + public boolean test(@NotNull LootModifierContext context) { + return !term.test(context); + } + + /** + * Creates a builder for {@link InvertedPredicate} + * + * @param term the predicate to invert + * @return a new {@link InvertedPredicate.Builder} containing the provided predicate + */ + public static InvertedPredicate.Builder builder(LootModifierPredicate.Builder term) { + return () -> new InvertedPredicate(term.build()); + } +} diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/TermsPredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/TermsPredicate.java new file mode 100644 index 0000000..a0c5c20 --- /dev/null +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/op/TermsPredicate.java @@ -0,0 +1,74 @@ +package top.offsetmonkey538.loottablemodifier.api.resource.predicate.op; + +import com.google.common.collect.ImmutableList; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import org.jetbrains.annotations.NotNull; +import top.offsetmonkey538.loottablemodifier.api.resource.util.LootModifierContext; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicate; + +import java.util.List; +import java.util.function.Function; +import java.util.function.Predicate; + +/** + * An abstract predicate for predicates that take other predicates as terms. + */ +abstract class TermsPredicate implements LootModifierPredicate { + /** + * The terms of this predicate + */ + protected final List terms; + private final Predicate builtPredicate; + + protected TermsPredicate(final List terms, final Predicate builtPredicate) { + this.terms = terms; + this.builtPredicate = builtPredicate; + } + + /** + * Creates a codec for the terms predicate using the provided constructor. + * + * @param constructor the constructor of the predicate. + * @return a codec for the terms predicate using the provided constructor + * @param the terms predicate + */ + protected static MapCodec createCodec(final Function, T> constructor) { + return RecordCodecBuilder.mapCodec( + instance -> instance.group(LootModifierPredicate.CODEC.listOf().fieldOf("terms").forGetter(TermsPredicate::getTerms)).apply(instance, constructor) + ); + } + + private List getTerms() { + return terms; + } + + @Override + public boolean test(final @NotNull LootModifierContext context) { + return builtPredicate.test(context); + } + + /** + * Abstract builder for {@link TermsPredicate} + */ + public abstract static class Builder implements LootModifierPredicate.Builder { + private final ImmutableList.Builder terms = ImmutableList.builder(); + + protected void add(LootModifierPredicate.Builder builder) { + this.terms.add(builder.build()); + } + + @Override + public LootModifierPredicate build() { + return this.build(this.terms.build()); + } + + /** + * Builds the predicate using the provided terms. + * + * @param terms the terms to build the predicate with + * @return a built predicate + */ + protected abstract LootModifierPredicate build(List terms); + } +} diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/table/TablePredicate.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/table/TablePredicate.java new file mode 100644 index 0000000..0cdc8f8 --- /dev/null +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/predicate/table/TablePredicate.java @@ -0,0 +1,199 @@ +package top.offsetmonkey538.loottablemodifier.api.resource.predicate.table; + +import com.google.common.collect.ImmutableList; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.block.Block; +import net.minecraft.entity.EntityType; +import net.minecraft.loot.LootTable; +import net.minecraft.loot.context.LootContextTypes; +import net.minecraft.registry.RegistryKey; +import net.minecraft.util.Identifier; +import net.minecraft.util.context.ContextType; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicateTypes; +import top.offsetmonkey538.loottablemodifier.api.resource.util.LootModifierContext; +import top.offsetmonkey538.loottablemodifier.api.resource.util.LootTableIdGetter; +import top.offsetmonkey538.loottablemodifier.api.resource.util.RegexPattern; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicate; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.LootModifierPredicateType; + +import java.util.List; +import java.util.Optional; + +/** + * Matches loot tables based on the identifier and type patterns + * + * @param identifiers the identifiers to match. List entries are in an {@code OR} relationship + * @param types the types to match. List entries are in an {@code OR} relationship + */ +public record TablePredicate(@Nullable List identifiers, @Nullable List types) implements LootModifierPredicate { + public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( + RegexPattern.CODEC.listOf().optionalFieldOf("identifiers").forGetter(TablePredicate::optionalIdentifier), + RegexPattern.CODEC.listOf().optionalFieldOf("types").forGetter(TablePredicate::optionalType) + ).apply(instance, TablePredicate::new)); + + @SuppressWarnings("OptionalUsedAsFieldOrParameterType") // Codec gib it to me + private TablePredicate(@NotNull Optional> optionalIdentifier, @NotNull Optional> optionalType) { + this( + optionalIdentifier.orElse(null), + optionalType.orElse(null) + ); + } + + private Optional> optionalIdentifier() { + if (identifiers == null || identifiers.isEmpty()) return Optional.empty(); + + return Optional.of(identifiers); + } + private Optional> optionalType() { + if (types == null || types.isEmpty()) return Optional.empty(); + + return Optional.of(types); + } + + @Override + public LootModifierPredicateType getType() { + return LootModifierPredicateTypes.TABLE; + } + + @Override + public boolean test(@NotNull LootModifierContext context) { + boolean result = true; + + if (identifiers != null && !identifiers.isEmpty()) { + boolean idResult = false; + final String tableIdString = context.tableId().toString(); + for (RegexPattern pattern : identifiers) idResult = idResult || pattern.matches(tableIdString); + result = idResult; + } + + if (types != null && !types.isEmpty()) { + boolean typeResult = false; + final String tableTypeString = LootContextTypes.MAP.inverse().get(context.table().getType()).toString(); + for (RegexPattern pattern : types) typeResult = typeResult || pattern.matches(tableTypeString); + result = result && typeResult; + } + + return result; + } + + /** + * Creates a builder for {@link TablePredicate} + * + * @return a new {@link TablePredicate.Builder} + */ + @Contract("->new") + public static TablePredicate.Builder builder() { + return new TablePredicate.Builder(); + } + + /** + * Builder for {@link TablePredicate} + */ + public static class Builder implements LootModifierPredicate.Builder { + private Builder() { + + } + + private final ImmutableList.Builder names = ImmutableList.builder(); + private final ImmutableList.Builder types = ImmutableList.builder(); + + /** + * Adds an entity type to match + * + * @param name the {@link EntityType} to match + * @return this + */ + @Contract("_->this") + public TablePredicate.Builder name(@NotNull EntityType name) { + name(LootTableIdGetter.INSTANCE.get(name)); + return this; + } + /** + * Adds a block to match + * + * @param name the {@link Block} to match + * @return this + */ + @Contract("_->this") + public TablePredicate.Builder name(@NotNull Block name) { + name(LootTableIdGetter.INSTANCE.get(name)); + return this; + } + /** + * Adds a loot table to match + * + * @param name the {@link RegistryKey} of the loot table to match + * @return this + */ + @Contract("_->this") + public TablePredicate.Builder name(@NotNull RegistryKey name) { + name(name.getValue()); + return this; + } + /** + * Adds a loot table to match + * + * @param name the {@link Identifier} of the loot table to match + * @return this + */ + @Contract("_->this") + public TablePredicate.Builder name(@NotNull Identifier name) { + name(RegexPattern.literal(name)); + return this; + } + /** + * Adds a {@link RegexPattern} to match the loot table id with. + * + * @param name the {@link RegexPattern} to match + * @return this + */ + @Contract("_->this") + public TablePredicate.Builder name(@NotNull RegexPattern name) { + this.names.add(name); + return this; + } + + /** + * Adds a {@link ContextType type} to match. + * + * @param type the {@link ContextType type} to match + * @return this + */ + @Contract("_->this") + public TablePredicate.Builder type(@NotNull ContextType type) { + type(LootContextTypes.MAP.inverse().get(type)); + return this; + } + /** + * Adds a type to match. + * + * @param type the {@link Identifier} of the type to match + * @return this + */ + @Contract("_->this") + public TablePredicate.Builder type(@NotNull Identifier type) { + type(RegexPattern.literal(type)); + return this; + } + /** + * Adds a {@link RegexPattern} to match the loot table type with. + * + * @param type the {@link RegexPattern} of the type to match + * @return this + */ + @Contract("_->this") + public TablePredicate.Builder type(@NotNull RegexPattern type) { + this.types.add(type); + return this; + } + + @Override + public TablePredicate build() { + return new TablePredicate(names.build(), types.build()); + } + } +} diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/util/LootModifierContext.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/util/LootModifierContext.java new file mode 100644 index 0000000..aa764ae --- /dev/null +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/util/LootModifierContext.java @@ -0,0 +1,22 @@ +package top.offsetmonkey538.loottablemodifier.api.resource.util; + +import net.minecraft.loot.LootPool; +import net.minecraft.loot.LootTable; +import net.minecraft.loot.entry.LootPoolEntry; +import net.minecraft.util.Identifier; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * A context for predicates to match against and actions to modify. + * + * @param table the table + * @param tableId the id of the table + * @param pool the pool + * @param entry the entry + * @param tableAlreadyModified if the table has already been modified by the current action + * @param poolAlreadyModified if the pool has already been modified by the current action + */ +public record LootModifierContext(@NotNull LootTable table, @NotNull Identifier tableId, @Nullable LootPool pool, @Nullable LootPoolEntry entry, boolean tableAlreadyModified, boolean poolAlreadyModified) { + +} diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/util/LootTableIdGetter.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/util/LootTableIdGetter.java new file mode 100644 index 0000000..a6763f8 --- /dev/null +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/util/LootTableIdGetter.java @@ -0,0 +1,36 @@ +package top.offsetmonkey538.loottablemodifier.api.resource.util; + +import net.minecraft.block.Block; +import net.minecraft.entity.EntityType; +import net.minecraft.util.Identifier; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import top.offsetmonkey538.loottablemodifier.impl.LootTableIdGetterImpl; + +/** + * Provides methods for getting the identifier of loot tables on different versions. + *
+ * The implemented methods may compile fine but throw runtime exceptions on newer versions because there's reflection magic going on, but it should be fine as these methods should only be called during data generation afaik. + */ +@ApiStatus.NonExtendable +public interface LootTableIdGetter { + /** + * The instance + */ + LootTableIdGetter INSTANCE = new LootTableIdGetterImpl(); + + /** + * Returns the identifier of an entity's loot table + * + * @param entityType the entity to get the loot table id for + * @return the identifier of the provided entity's loot table + */ + Identifier get(@NotNull EntityType entityType); + /** + * Returns the identifier of a block's loot table + * + * @param block the block to get the loot table id for + * @return the identifier of the provided block's loot table + */ + Identifier get(@NotNull Block block); +} diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/util/RegexPattern.java b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/util/RegexPattern.java new file mode 100644 index 0000000..f0380e4 --- /dev/null +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/api/resource/util/RegexPattern.java @@ -0,0 +1,74 @@ +package top.offsetmonkey538.loottablemodifier.api.resource.util; + +import com.mojang.datafixers.util.Either; +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.util.Identifier; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.regex.Pattern; + +/** + * {@link RegexPattern}s allow either matching {@link Identifier}s directly or using regex to do so. + *
+ * Use {@link #literal(Identifier)} for creating literal patterns and {@link #compile(String)} for creating regex patterns. + * + * @param isRegex if this {@link RegexPattern} is using regex or not + * @param patternString the pattern as a plain string + * @param pattern the compiled pattern + */ +public record RegexPattern(boolean isRegex, @NotNull String patternString, @NotNull Pattern pattern) { + private static final Codec INLINE_CODEC = Identifier.CODEC.xmap(RegexPattern::literal, instance -> Identifier.of(instance.patternString())); + private static final Codec FULL_CODEC = RecordCodecBuilder.create(instance -> instance.group( + Codec.STRING.fieldOf("regexPattern").forGetter(RegexPattern::patternString) + ).apply(instance, RegexPattern::compile)); + public static final Codec CODEC = Codec.either( + INLINE_CODEC, + FULL_CODEC + ).xmap( + either -> either.map(it -> it, it -> it), + pattern -> pattern.isRegex ? Either.right(pattern) : Either.left(pattern) + ); + + /** + * Creates a literal {@link RegexPattern} from the provided {@link Identifier}. + * + * @param identifier the {@link Identifier} to match + * @return a new {@link RegexPattern} matching the provided {@link Identifier} + */ + @Contract("_->new") + public static RegexPattern literal(final Identifier identifier) { + return new RegexPattern( + false, + identifier.toString(), + Pattern.compile(Pattern.quote(identifier.toString())) + ); + } + + /** + * Compiles a {@link RegexPattern} from the provided regex pattern. + * + * @param regexPattern the pattern to use + * @return a new {@link RegexPattern} compiled from the provided regex pattern + * @see Pattern#compile(String) + */ + public static RegexPattern compile(final String regexPattern) { + return new RegexPattern( + true, + regexPattern, + Pattern.compile(regexPattern) + ); + } + + /** + * Checks if the provided character sequence matches this. + * + * @param input the character sequence to check + * @return if the provided character sequence matches this + */ + public boolean matches(CharSequence input) { + if (isRegex) return pattern.matcher(input).matches(); + return patternString.contentEquals(input); + } +} diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/datagen/LootTableModifierDatagen.java b/src/main/java/top/offsetmonkey538/loottablemodifier/datagen/LootTableModifierDatagen.java index d9e1613..3bc41dc 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/datagen/LootTableModifierDatagen.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/datagen/LootTableModifierDatagen.java @@ -3,22 +3,39 @@ import net.fabricmc.fabric.api.datagen.v1.DataGeneratorEntrypoint; import net.fabricmc.fabric.api.datagen.v1.FabricDataGenerator; import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput; +import net.fabricmc.fabric.api.datagen.v1.provider.SimpleFabricLootTableProvider; +import net.minecraft.block.Blocks; import net.minecraft.entity.EntityType; import net.minecraft.item.Items; import net.minecraft.loot.LootPool; -import net.minecraft.loot.LootTables; +import net.minecraft.loot.LootTable; +import net.minecraft.loot.context.LootContextTypes; import net.minecraft.loot.entry.ItemEntry; -import net.minecraft.loot.function.EnchantWithLevelsLootFunction; import net.minecraft.loot.function.SetCountLootFunction; import net.minecraft.loot.provider.number.ConstantLootNumberProvider; import net.minecraft.loot.provider.number.UniformLootNumberProvider; +import net.minecraft.registry.RegistryKey; +import net.minecraft.registry.RegistryKeys; import net.minecraft.registry.RegistryWrapper; import top.offsetmonkey538.loottablemodifier.api.datagen.LootModifierProvider; +import top.offsetmonkey538.loottablemodifier.api.resource.LootModifier; +import top.offsetmonkey538.loottablemodifier.api.resource.action.entry.EntryAddAction; +import top.offsetmonkey538.loottablemodifier.api.resource.action.entry.EntryRemoveAction; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.table.TablePredicate; +import top.offsetmonkey538.loottablemodifier.api.resource.util.RegexPattern; +import top.offsetmonkey538.loottablemodifier.api.resource.action.pool.PoolAddAction; +import top.offsetmonkey538.loottablemodifier.api.resource.action.entry.EntryItemSetAction; +import top.offsetmonkey538.loottablemodifier.api.resource.action.pool.PoolRemoveAction; +import top.offsetmonkey538.loottablemodifier.api.resource.predicate.entry.EntryItemPredicate; import java.util.concurrent.CompletableFuture; +import java.util.function.BiConsumer; import static top.offsetmonkey538.loottablemodifier.LootTableModifier.id; +/** + * Datagen for loot modifiers used for testing. + */ public class LootTableModifierDatagen implements DataGeneratorEntrypoint { @Override @@ -26,6 +43,7 @@ public void onInitializeDataGenerator(FabricDataGenerator fabricDataGenerator) { final FabricDataGenerator.Pack pack = fabricDataGenerator.createBuiltinResourcePack(id("example_pack")); pack.addProvider(ModLootModifierProvider::new); + pack.addProvider(LootProvider::new); } private static class ModLootModifierProvider extends LootModifierProvider { @@ -36,35 +54,139 @@ public ModLootModifierProvider(FabricDataOutput dataOutput, CompletableFuture registryLookup) { + super(output, registryLookup, LootContextTypes.CHEST); + } - LootTables.ABANDONED_MINESHAFT_CHEST - ); + @Override + public void accept(BiConsumer, LootTable.Builder> lootTableBiConsumer) { + lootTableBiConsumer.accept(RegistryKey.of(RegistryKeys.LOOT_TABLE, id("test_empty_table")), LootTable.builder()); } } } diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/impl/LootTableIdGetterImpl.java b/src/main/java/top/offsetmonkey538/loottablemodifier/impl/LootTableIdGetterImpl.java new file mode 100644 index 0000000..05722e4 --- /dev/null +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/impl/LootTableIdGetterImpl.java @@ -0,0 +1,138 @@ +package top.offsetmonkey538.loottablemodifier.impl; + +import net.fabricmc.loader.api.FabricLoader; +import net.fabricmc.loader.api.MappingResolver; +import net.minecraft.block.AbstractBlock; +import net.minecraft.block.Block; +import net.minecraft.entity.EntityType; +import net.minecraft.loot.LootTable; +import net.minecraft.registry.RegistryKey; +import net.minecraft.util.Identifier; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import top.offsetmonkey538.loottablemodifier.api.resource.util.LootTableIdGetter; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Optional; +import java.util.function.Function; + +import static top.offsetmonkey538.loottablemodifier.LootTableModifier.LOGGER; + +@ApiStatus.Internal +public class LootTableIdGetterImpl implements LootTableIdGetter { + + @Override + public Identifier get(@NotNull EntityType entityType) { + return VersionSpecific.Entity.get.apply(entityType); + } + + @Override + public Identifier get(@NotNull Block block) { + return VersionSpecific.Block.get.apply(block); + } + + + // Should be executed when class is first accessed, which I think should only happen when using action or predicate Builders which are probably only used with datagen so this should only make datagen stuff crash but still work when used by a player? + private static class VersionSpecific { + private static final MappingResolver RESOLVER = FabricLoader.getInstance().getMappingResolver(); + + private static class Entity { + private static final String V1d21d2 = RESOLVER.mapMethodName("intermediary", "net.minecraft.class_1299", "method_16351", "()Ljava/util/Optional;"); + private static final String V1d20d5 = RESOLVER.mapMethodName("intermediary", "net.minecraft.class_1299", "method_16351", "()Lnet/minecraft/class_5321;"); + + // mod only supports down to 1.20.5 soo: private static final String V1d14d0 = RESOLVER.mapMethodName("intermediary", "net.minecraft.class_1299", "method_16351", "()Lnet/minecraft/util/Identifier;"); + + private static final Function, Identifier> get; + + static { + // TODO: I had the below statement commented out, maybe it doesnt work? use this then: final Class entityType = Class.forName(RESOLVER.mapClassName("intermediary", "net.minecraft.class_1299")); + final Class entityType = EntityType.class; + final Method finalMethod; + Method method; + + // 1.21.2 to future:tm: + if ((method = getMethod(entityType, V1d21d2)) != null) { + method.setAccessible(true); + finalMethod = method; + get = entity -> { + try { + @SuppressWarnings("unchecked") final Optional> optional = (Optional>) finalMethod.invoke(entity); + if (optional.isPresent()) return optional.get().getValue(); + throw new IllegalStateException("Entity '" + entity + "' has no loot table! (It is created with 'builder.dropsNothing()')"); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + }; + } + + // 1.20.5 to 1.21.1 + else if ((method = getMethod(entityType, V1d20d5)) != null) { + method.setAccessible(true); + finalMethod = method; + get = entity -> { + try { + return ((RegistryKey) finalMethod.invoke(entity)).getValue(); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + }; + } else { + throw new IllegalStateException("No valid way to get entity loot table id found!"); + } + } + } + private static class Block { + private static final String V1d21d2 = RESOLVER.mapMethodName("intermediary", "net.minecraft.class_4970", "method_26162", "()Ljava/util/Optional;"); + private static final String V1d20d5 = RESOLVER.mapMethodName("intermediary", "net.minecraft.class_4970", "method_26162", "()Lnet/minecraft/class_5321;"); + + private static final Function get; + + static { + final Class abstractBlock = AbstractBlock.class; + final Method finalMethod; + Method method; + + // 1.21.2 to future:tm: + if ((method = getMethod(abstractBlock, V1d21d2)) != null) { + method.setAccessible(true); + finalMethod = method; + get = entity -> { + try { + @SuppressWarnings("unchecked") final Optional> optional = (Optional>) finalMethod.invoke(entity); + if (optional.isPresent()) return optional.get().getValue(); + throw new IllegalStateException("Entity '" + entity + "' has no loot table! (It is created with 'builder.dropsNothing()')"); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + }; + } + + // 1.20.5 to 1.21.1 + else if ((method = getMethod(abstractBlock, V1d20d5)) != null) { + method.setAccessible(true); + finalMethod = method; + get = entity -> { + try { + return ((RegistryKey) finalMethod.invoke(entity)).getValue(); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + }; + } else { + throw new IllegalStateException("No valid way to get entity loot table id found!"); + } + } + } + + private static @Nullable Method getMethod(Class clazz, String method) { + try { + return clazz.getDeclaredMethod(method); + } catch (NoSuchMethodException e) { + LOGGER.warn("Method '%s' not valid for getting loot table id on this version, trying next method...", e); + return null; + } + } + } +} diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/mixin/ItemEntryAccessor.java b/src/main/java/top/offsetmonkey538/loottablemodifier/mixin/ItemEntryAccessor.java new file mode 100644 index 0000000..f96296f --- /dev/null +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/mixin/ItemEntryAccessor.java @@ -0,0 +1,17 @@ +package top.offsetmonkey538.loottablemodifier.mixin; + +import net.minecraft.item.Item; +import net.minecraft.loot.entry.ItemEntry; +import net.minecraft.registry.entry.RegistryEntry; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Mutable; +import org.spongepowered.asm.mixin.gen.Accessor; + +@SuppressWarnings("MissingJavadoc") +@Mixin(ItemEntry.class) +public interface ItemEntryAccessor { + + @Mutable + @Accessor + void setItem(RegistryEntry item); +} diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/mixin/LootPoolAccessor.java b/src/main/java/top/offsetmonkey538/loottablemodifier/mixin/LootPoolAccessor.java new file mode 100644 index 0000000..7a8a0ce --- /dev/null +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/mixin/LootPoolAccessor.java @@ -0,0 +1,17 @@ +package top.offsetmonkey538.loottablemodifier.mixin; + +import net.minecraft.loot.LootPool; +import net.minecraft.loot.entry.LootPoolEntry; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Mutable; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.List; + +@Mixin(LootPool.class) +public interface LootPoolAccessor { + + @Mutable + @Accessor + void setEntries(List entries); +} diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/mixin/ReloadableRegistriesMixin.java b/src/main/java/top/offsetmonkey538/loottablemodifier/mixin/ReloadableRegistriesMixin.java index 62d2659..cb65664 100644 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/mixin/ReloadableRegistriesMixin.java +++ b/src/main/java/top/offsetmonkey538/loottablemodifier/mixin/ReloadableRegistriesMixin.java @@ -40,7 +40,6 @@ public abstract class ReloadableRegistriesMixin { ) private static void loottablemodifier$modifyLootTables(LootDataType lootDataType, ResourceManager resourceManager, RegistryOps registryOps, CallbackInfoReturnable> cir) { if (lootDataType != LootDataType.LOOT_TABLES) return; - //noinspection unchecked LootTableModifier.runModification(resourceManager, (Registry) cir.getReturnValue(), registryOps); } diff --git a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/LootModifier.java b/src/main/java/top/offsetmonkey538/loottablemodifier/resource/LootModifier.java deleted file mode 100644 index 7925d93..0000000 --- a/src/main/java/top/offsetmonkey538/loottablemodifier/resource/LootModifier.java +++ /dev/null @@ -1,34 +0,0 @@ -package top.offsetmonkey538.loottablemodifier.resource; - -import com.mojang.datafixers.util.Either; -import com.mojang.serialization.Codec; -import com.mojang.serialization.codecs.RecordCodecBuilder; -import net.minecraft.loot.LootPool; -import net.minecraft.util.Identifier; - -import java.util.ArrayList; -import java.util.List; - -public record LootModifier(ArrayList modifies, List pools) { - public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( - Codec.either(Identifier.CODEC, Identifier.CODEC.listOf()).fieldOf("modifies").forGetter(LootModifier::modifiesEither), - Codec.mapEither(LootPool.CODEC.listOf().fieldOf("pools"), LootPool.CODEC.listOf().fieldOf("loot_pools")).forGetter(LootModifier::poolsEither) - ).apply(instance, LootModifier::new)); - - private LootModifier(Either> modifiesEither, Either, List> poolsEither) { - this( - new ArrayList<>(modifiesEither.right().orElseGet(() -> List.of(modifiesEither.left().orElseThrow()))), - new ArrayList<>(poolsEither.left().orElseGet(() -> poolsEither.right().orElseThrow())) - ); - } - - private Either> modifiesEither() { - if (modifies.size() == 1) return Either.left(modifies.get(0)); - return Either.right(modifies); - } - - // Left is "pools", right is "loot_pools". Want datagen to use "pools" so left it is. - private Either, List> poolsEither() { - return Either.left(pools); - } -} diff --git a/src/main/resources/loot-table-modifier.accesswidener b/src/main/resources/loot-table-modifier.accesswidener index 46a644a..9bcda25 100644 --- a/src/main/resources/loot-table-modifier.accesswidener +++ b/src/main/resources/loot-table-modifier.accesswidener @@ -1,3 +1,10 @@ accessWidener v1 named -accessible field net/minecraft/loot/LootTable pools Ljava/util/List; \ No newline at end of file +accessible field net/minecraft/loot/LootTable pools Ljava/util/List; +accessible field net/minecraft/loot/LootTable functions Ljava/util/List; + +accessible field net/minecraft/loot/context/LootContextTypes MAP Lcom/google/common/collect/BiMap; + +accessible field net/minecraft/loot/entry/LootPoolEntry conditions Ljava/util/List; + +accessible field net/minecraft/loot/entry/ItemEntry item Lnet/minecraft/registry/entry/RegistryEntry; \ No newline at end of file diff --git a/src/main/resources/loot-table-modifier.mixins.json b/src/main/resources/loot-table-modifier.mixins.json index e306650..2352da9 100644 --- a/src/main/resources/loot-table-modifier.mixins.json +++ b/src/main/resources/loot-table-modifier.mixins.json @@ -1,12 +1,14 @@ { - "required": true, - "package": "top.offsetmonkey538.loottablemodifier.mixin", - "compatibilityLevel": "JAVA_17", - "mixins": [ - "LootTableAccessor", - "ReloadableRegistriesMixin" - ], - "injectors": { - "defaultRequire": 1 + "required": true, + "package": "top.offsetmonkey538.loottablemodifier.mixin", + "compatibilityLevel": "JAVA_17", + "mixins": [ + "ItemEntryAccessor", + "LootPoolAccessor", + "LootTableAccessor", + "ReloadableRegistriesMixin" + ], + "injectors": { + "defaultRequire": 1 } }