diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..d57bf98 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,51 @@ +name: Deploy docs to GitHub Pages + +on: + push: + branches: [master] + paths: &doc-paths + - "docs/**" + - ".github/workflows/docs.yml" + pull_request: + paths: *doc-paths + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: pages-${{ github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + + - uses: jdx/mise-action@dba19683ed58901619b14f395a24841710cb4925 # v4.1.0 + + - run: pnpm install --frozen-lockfile --filter omnibot-docs + + - run: pnpm --filter omnibot-docs build + env: + DOCS_BASE: /OmniBot/ + + - if: &deploy-condition github.event_name == 'push' && github.ref == 'refs/heads/master' + uses: actions/upload-pages-artifact@v5 + with: + path: docs/site/.vitepress/dist + + deploy: + needs: build + if: *deploy-condition + runs-on: ubuntu-latest + permissions: + pages: write + id-token: write + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - uses: actions/deploy-pages@v5 + id: deployment diff --git a/AGENTS.md b/AGENTS.md index 7b8592a..9d4d740 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -14,6 +14,10 @@ pnpm prisma:consolidate # merge *.prisma module files → src/prisma/schema.pr pnpm prisma:generate # generate Prisma client (required before build) pnpm prisma:migrate # create and apply a migration pnpm prisma:studio # open Prisma Studio + +# Documentation (in docs/) +pnpm --filter omnibot-docs dev # start VitePress dev server +pnpm --filter omnibot-docs build # build static site ``` Run the whole stack in one command with [pitchfork](https://github.com/jdx/pitchfork) (pinned in `.mise.toml`, so `mise install` provides it): @@ -30,17 +34,19 @@ Daemons are defined in `pitchfork.toml` (`bot` depends on `db`). Requires Docker OmniBot is a modular Discord bot. The core loads modules dynamically — **no registration needed outside the module directory**, modules are auto-discovered at startup. Each module is a self-contained plugin installed/uninstalled per guild. -See [`docs/`](docs/README.md) for the full developer guide: - -| Topic | Doc | -| -------------------------- | -------------------------------------------- | -| Creating a module | [docs/modules.md](docs/modules.md) | -| Slash commands | [docs/commands.md](docs/commands.md) | -| Event listeners | [docs/listeners.md](docs/listeners.md) | -| Buttons / modals / selects | [docs/interactions.md](docs/interactions.md) | -| Services | [docs/services.md](docs/services.md) | -| Prisma multi-file schema | [docs/prisma.md](docs/prisma.md) | -| Functional behavior | [docs/functional.md](docs/functional.md) | +See [`docs/site/`](docs/site/README.md) for the full developer guide: + +| Topic | Doc | +| -------------------------- | ---------------------------------------------------------------------------------- | +| Creating a module | [docs/site/en/guide/creating-a-module.md](docs/site/en/guide/creating-a-module.md) | +| Slash commands | [docs/site/en/guide/commands.md](docs/site/en/guide/commands.md) | +| Event listeners | [docs/site/en/guide/listeners.md](docs/site/en/guide/listeners.md) | +| Buttons / modals / selects | [docs/site/en/guide/interactions.md](docs/site/en/guide/interactions.md) | +| Services | [docs/site/en/guide/services.md](docs/site/en/guide/services.md) | +| Prisma / database | [docs/site/en/guide/database.md](docs/site/en/guide/database.md) | +| Functional behavior | [docs/functional.md](docs/functional.md) | + +The docs are also published as a VitePress site (sources in `docs/site/`) — run `pnpm --filter omnibot-docs dev` to preview. ## Key rules diff --git a/AUDIT.md b/AUDIT.md index 41ae4fd..e6827ba 100644 --- a/AUDIT.md +++ b/AUDIT.md @@ -1,4 +1,4 @@ -🔍 Audit OmniBot +# 🔍 Audit OmniBot > Les items portent un ID stable `**#N**` (référencé par les commits, ex. > « fixed in #26 »). Ils sont en **puces** à dessein : oxfmt renumérote les listes @@ -79,9 +79,15 @@ - **#26** — Permission des interactions — défaut _fail-open_ (dette de conception, faible). Le flag `requiresAdmin?: boolean` sur `InteractionHandler` (enforcé par le dispatcher) est **optionnel** : un handler sans flag est public. Sûr aujourd'hui (tous les handlers sont admin et explicitement marqués), mais repose sur l'humain pour ne pas oublier `requiresAdmin: true` sur un futur handler sensible. - **Évolution possible (option C, safe-by-construction)** : remplacer le flag optionnel par un champ **requis** type `access: "admin" | "everyone"` (aucun défaut) → le typage force chaque handler à déclarer son niveau d'accès, impossible d'oublier. - **Déclencheur** : à faire quand le nombre de handlers grandit, ou dès l'apparition du premier handler volontairement non-admin (le risque d'oubli devient alors réel). -- **#28** — CI : remplacer le grep de version Node par `jdx/mise-action`. `ci.yml` fait `pnpm/action-setup` + `grep '^node = ' .mise.toml` + `setup-node` ; maintenant que `.mise.toml` est la source de vérité (node + pnpm + pitchfork), `mise install` via l'action officielle ferait tout en une étape, sans grep fragile. (Caveat : installerait aussi pitchfork en CI — bénin.) +- **#28** — ~~CI : remplacer le grep de version Node par `jdx/mise-action`~~ 🚫 _non retenu (décidé)_. L'idée : `mise install` en une étape à la place de `pnpm/action-setup` + `grep '^node = ' .mise.toml` + `setup-node`. **Raisons du refus** : (1) perte du cache pnpm automatique fourni par `setup-node` (`cache: pnpm`) — il faudrait le re-câbler à la main (cf. #32) ; (2) `mise install` installerait aussi des outils inutiles en CI (pitchfork…) ; (3) le grep actuel, bien que peu élégant, est explicite et fonctionne. Le ratio bénéfice/inconvénient n'est pas favorable. (`docs.yml` garde `mise-action` car le build docs est peu fréquent et non sensible à ces points.) - **#29** — Branch protection `master` avec `lint` + `build` en _required status checks_ (réglage GitHub, hors repo). Prérequis pour que l'auto-merge Renovate (`platformAutomerge`) attende réellement la CI ; sans ça il pourrait fusionner sans gate. - **#30** — `pnpm dev` ne fait pas de hot-reload alors que `CLAUDE.md` annonce « tsx watch » : le script est `node --import tsx src/index.ts` (sans `watch`). À réconcilier (passer le script en `tsx watch`, ou corriger la doc). +- **#31** — Docs VitePress : logo manquant. Le hero de `docs/site/index.md` référençait `/logo.svg`, absent de `docs/site/public/` (image 404 sur le site publié). La référence `image:` a été retirée temporairement. À rétablir une fois qu'un logo existe : ajouter `docs/site/public/logo.svg` puis remettre le bloc `image: { src: /logo.svg, alt: OmniBot }` dans le frontmatter du hero. +- **#32** — CI docs : pas de cache du store pnpm. `docs.yml` utilise `jdx/mise-action` (qui ne cache que les outils, pas le store pnpm), contrairement à `ci.yml` qui bénéficie de `cache: pnpm` via `setup-node`. Le workflow ne tournant que sur changements de `docs/`, le ROI est faible — délayé. À traiter si le build docs devient lent : ajouter un `actions/cache` sur `pnpm store path` (clé sur `hashFiles('pnpm-lock.yaml')`), ou activer le cache pnpm de `mise-action`. +- **#33** — Docs VitePress : la home racine `docs/site/index.md` est entièrement en français (hero + features). **Décidé** : chaque locale est autonome — la nav `Accueil`/`Home` pointe désormais vers `/fr/` et `/en/` (et plus vers `/`), donc la racine `/` n'est plus qu'un point d'entrée rarement visité. Priorité **rétrogradée** : la bilinguiser/neutraliser devient optionnel ; alternative possible : la réduire à une simple redirection vers la locale par défaut. +- **#34** — CI : `permissions: contents: read` est répété à l'identique dans les jobs `lint` et `build` de `ci.yml`. Pourrait remonter au niveau workflow (top-level) pour éviter la duplication. Cosmétique / moindre privilège. +- **#35** — CI (_incertain_) : le bloc de setup (checkout + `pnpm/action-setup` + lecture version Node + `setup-node`) est dupliqué à l'identique entre `lint` et `build`. Factorisation possible **via ancres YAML** (privilégié), mais les ancres ne savent pas concaténer une séquence de steps + des steps supplémentaires ; l'alternative propre est une **composite action** `.github/actions/setup` — dont la complexité induite reste à valider pour seulement 2 jobs. +- **#36** — CI (_incertain_) : incohérence d'install entre `lint` (`pnpm ci`) et `build` (`pnpm install --frozen-lockfile`). Uniformiser, mais **vérifié** : sans filtre, `pnpm ci` comme `pnpm install` font un (clean-)install de **tout le workspace**, vitepress (dép. d'`omnibot-docs`) compris — inutile pour linter/builder/tester le bot. Le vrai gain serait de **scoper l'install CI au paquet du bot** (filtre excluant `docs/site`) plutôt que de choisir `ci` vs `install`. `pnpm ci` n'aide pas sur ce point (il ignore `--filter`, cf. essais docs). --- @@ -105,6 +111,11 @@ Récapitulatif des actions restantes | 🔵 | Extraire client/modules dans un context.ts (#14) — délayé | | 🟣 | Accès interactions : champ requis (option C, #26) — évol. | | 🟠 | Enum >25 options : warn + doc (#27) | -| 🟣 | CI : `jdx/mise-action` au lieu du grep (#28) | | 🔵 | Branch protection : `lint`+`build` required (#29) — GitHub | | 🟣 | `pnpm dev` : hot-reload vs doc (#30) | +| 🟢 | Docs : ajouter un logo + rétablir le hero image (#31) | +| 🟢 | CI docs : cache du store pnpm (#32) — délayé | +| 🟢 | Docs : home racine FR-only (#33) — optionnel | +| 🟢 | CI : `permissions` au niveau workflow (#34) | +| 🟢 | CI : factoriser le setup dupliqué (#35) — incertain | +| 🟢 | CI : uniformiser/scoper l'install (#36) — incertain | diff --git a/docs/README.md b/docs/README.md deleted file mode 100644 index a42020d..0000000 --- a/docs/README.md +++ /dev/null @@ -1,49 +0,0 @@ -# Guide de Développement AsyncMod - -Bienvenue dans le guide de développement d'AsyncMod ! Cette documentation vous expliquera comment créer des modules, -commandes et listeners pour étendre les fonctionnalités du bot Discord. - -## Structure de la Documentation - -- [**Modules**](modules.md) - Comment créer et structurer un module -- [**Commandes**](commands.md) - Comment créer des commandes slash -- [**Listeners**](listeners.md) - Comment écouter et réagir aux événements Discord -- [**Interactions**](interactions.md) - Comment gérer les boutons, menus et modales -- [**Services**](services.md) - Comment créer des services pour la logique métier -- [**Prisma**](prisma.md) - Guide sur l'utilisation de Prisma pour la base de données -- [**Fonctionnel**](functional.md) - Comportement visible par les utilisateurs Discord - -## Architecture du Projet - -AsyncMod utilise un système de modules modulaire où chaque fonctionnalité est encapsulée dans son propre module. Chaque -module peut contenir des commandes, des listeners et sa propre logique métier. - -## Conventions de Nommage - -Le projet utilise un système de nommage inspiré d'Angular avec des suffixes pour identifier le type de fichier : - -- `*.module.ts` - Fichiers de modules (ex: `example.module.ts`) -- `*.command.ts` - Fichiers de commandes Discord (ex: `test.command.ts`) -- `*.listener.ts` - Fichiers d'événements Discord (ex: `message.listener.ts`) -- `*.button.ts` / `*.modal.ts` / `*.select.ts` - Handlers d'interactions -- `*.service.ts` - Fichiers de services (ex: `user.service.ts`) -- `*.prisma` - Modèles de base de données Prisma (ex: `users.prisma`) - -Cette convention permet d'identifier rapidement le rôle de chaque fichier dans le projet. - -## Structure du Projet - -``` -src/ -├── modules/ # Dossier contenant tous les modules du bot -│ ├── mon-module-ici/ # Module -│ │ ├── commands/ # Commandes du module -│ │ ├── listeners/ # Listeners du module -│ │ ├── models/ # Modèles de données pour la base de données -│ │ ├── services/ # Services du module -│ │ └── mon-module.module.ts # Fichier principal du module -├── lib/ # Bibliothèques utile pour les modules -└── core/ # Système central, services et gestion des modules - ├── loaders/ # Chargeurs de modules - └── services/ # Services principaux -``` diff --git a/docs/commands.md b/docs/commands.md deleted file mode 100644 index 77b0fd5..0000000 --- a/docs/commands.md +++ /dev/null @@ -1,80 +0,0 @@ -# Commandes Discord - -Ce guide explique comment créer des commandes Discord dans vos modules. -Ces commandes sont des interactions slash qui permettent aux utilisateurs d'interagir avec votre bot. Elles ne seront -disponibles que si le module est activé, et sur le serveur. - -## Structure des Commandes - -Les commandes Discord sont des interactions slash ("/") que les utilisateurs peuvent exécuter. Chaque commande doit être -définie dans un fichier séparé dans le dossier `commands/` de votre module. - -### Structure de fichier - -``` -src/modules/mon-module/ -├── module.ts -└── commands/ - └── ma-commande.command.ts -``` - -## Création d'une Commande - -```typescript -// src/modules/mon-module/commands/test.command.ts -import { SlashCommandBuilder } from "discord.js"; -import { declareCommand } from "../../../lib/command.js"; - -export default declareCommand({ - data: new SlashCommandBuilder() - .setName("test") - .setDescription("Commande de test"), - - async execute(interaction) { - await interaction.reply("Test !"); - }, -}); -``` - -## Interface Command - -La fonction `declareCommand` accepte un objet qui implémente l'interface `Command` : - -- `data` : Configuration de la commande (SlashCommandBuilder) -- `execute` : Fonction d'exécution (obligatoire) -- `complete` : Fonction d'autocomplétion (optionnelle) - -## Enregistrement dans le Module - -```typescript -// src/modules/mon-module/mon-module.module.ts -import messageListener from "./commands/test.command.js"; - -export default defineModule({ - onLoad(client, registry) { - // Le système gère automatiquement l'enregistrement et la désactivation des commandes - registry.register(testCommand); - }, -}); -``` - -## Enregistrement & propagation - -L'enregistrement des commandes auprès de Discord dépend du type de commande et -du mode d'exécution : - -| Commandes | Production | Développement (`NODE_ENV=development`) | -| -------------------------------- | ---------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- | -| **core** (`/config`, `/modules`) | globales (propagation jusqu'à ~1 h) | enregistrées sur `DEV_GUILD_ID` → **instantané** | -| **module** | par serveur, (ré)installées au changement de `version` du module | re-synchronisées **à chaque démarrage** sur `DEV_GUILD_ID` pour les modules activés, sans bump de version | - -En production, le gate par version évite de re-pousser les commandes d'un module -sur toutes les guildes à chaque démarrage. En développement ce gate est une -friction (une seule guilde, itérations rapides) : `loadDevGuildCommands` -enregistre, en **un seul PUT** sur la dev guild, le core + les commandes de tous -les modules activés. Le PUT unique est nécessaire car il écrase l'ensemble des -commandes de la guilde — mélanger un PUT (core) et des POST (modules) sur le même -scope ferait disparaître les commandes POST. - -> `DEV_GUILD_ID` est **requis** en mode dev (validé au démarrage). Voir -> `.env.example`. diff --git a/docs/functional.md b/docs/functional.md index 78c3e01..be1f087 100644 --- a/docs/functional.md +++ b/docs/functional.md @@ -28,11 +28,12 @@ Dès qu'un message est posté dans le salon configuré : 2. Si un message de bienvenue est configuré, le bot le poste dans le fil **Variables disponibles dans le template de nom :** -| Variable | Valeur | -|---|---| -| `{messageAuthor}` | Nom d'affichage ou pseudo de l'auteur | -| `{messageContent}` | 50 premiers caractères du message | -| `{timestamp}` | Heure au format `JJ/MM HH:MM` (locale française) | + +| Variable | Valeur | +| ------------------ | ------------------------------------------------ | +| `{messageAuthor}` | Nom d'affichage ou pseudo de l'auteur | +| `{messageContent}` | 50 premiers caractères du message | +| `{timestamp}` | Heure au format `JJ/MM HH:MM` (locale française) | **Template par défaut :** `Discussion - {messageAuthor}` **Message de bienvenue par défaut :** `💬 Utilisez ce fil pour discuter de ce sujet !` diff --git a/docs/listeners.md b/docs/listeners.md deleted file mode 100644 index 2cbdcaf..0000000 --- a/docs/listeners.md +++ /dev/null @@ -1,53 +0,0 @@ -# Guide des Listeners - -Les listeners (écouteurs d'événements) dans AsyncMod permettent à votre module de réagir aux événements Discord. Le -système de modules gère automatiquement l'activation/désactivation des listeners selon l'état du module. - -> ⚠️ **Important**: Pour les événements pouvant se produire en MP (messages privés), vous devez gérer manuellement la -> vérification d'activation du module car le système ne peut pas détecter automatiquement si le module est actif en -> dehors d'un serveur. - -> 📋 **Intents Discord**: Assurez-vous d'ajouter les bons intents dans votre configuration Discord pour recevoir les -> événements souhaités. - -## API des Listeners - -### Déclaration d'un Listener - -```typescript -import { declareEventListener } from "../../../lib/listener.js"; - -export default declareEventListener({ - eventType: "messageCreate", // Type d'événement Discord - - async execute(...args) { - // Votre logique ici - // Les arguments dépendent du type d'événement - }, -}); -``` - -### Enregistrement dans le Module - -```typescript -// src/modules/mon-module/mon-module.module.ts -import messageListener from "./listeners/message-create.listener.js"; - -export default defineModule({ - onLoad(client, registry) { - // Le système gère automatiquement l'activation/désactivation - registry.register(messageListener); - }, -}); -``` - -## Configuration des Intents Discord - -Les intents sont requis pour recevoir certains événements. -Pour plus d'informations sur quels intents sont nécessaires pour chaque événement, consultez -la [documentation Discord](https://discord.com/developers/docs/events/gateway#list-of-intents). - -## Prochaines Étapes - -- [Configuration des Modules](./modules.md) pour gérer les paramètres -- [Gestion de la BDD](./prisma.md) pour les données persistantes diff --git a/docs/modules.md b/docs/modules.md deleted file mode 100644 index 2f8b596..0000000 --- a/docs/modules.md +++ /dev/null @@ -1,140 +0,0 @@ -# Guide des Modules - -Un module dans AsyncMod est une unité fonctionnelle autonome qui peut être installée et désinstallée par serveur -Discord. Chaque module peut contenir des commandes, des listeners d'événements et ses propres données. - -## Structure d'un Module - -``` -src/modules/mon-module/ -├── mon-module.module.ts # Définition du module principal -├── commands/ # Commandes du module -│ └── ma-commande.command.ts -├── listeners/ # Listeners d'événements -│ └── mon-listener.listener.ts -└── models/ # Modèles de données (optionnel) - └── mon-modele.prisma -``` - -## Création d'un Module - -### 1. Définition du Module Principal - -Créez le fichier principal de votre module : - -```typescript -// src/modules/mon-module/mon-module.module.ts - -export default defineModule({ - id: "mon-module", - name: "Mon Module", - description: "Description de mon module personnalisé.", - version: "1.0.0", - author: "Votre Nom", // Optionnel - - // Intents Discord requis par le module - intents: [ - GatewayIntentBits.Guilds, - GatewayIntentBits.GuildMessages, - GatewayIntentBits.MessageContent, - ], - - // Appelé au démarrage du bot - onLoad(client, registry) { - // Enregistrer les commandes - registry.register(maCommande); - - // Enregistrer les listeners - registry.register(monListener); - - // Enregistrer les interactions - registry.register(monInteraction); - - logger.info("Mon module chargé avec succès"); - }, - - // Appelé quand le module est installé sur un serveur - onInstall(client, guild, registry) { - logger.info(`Mon module installé sur ${guild.name}`); - - // Exemples de logique d'installation : - // - Créer des rôles - // - Configurer des channels - // - Initialiser des données - }, - - // Appelé quand le module est désinstallé d'un serveur - onUninstall(client, guild, registry) { - logger.info(`Mon module désinstallé de ${guild.name}`); - - // Exemple de logique de nettoyage : - // - Supprimer des données - // - Nettoyer des configurations - }, -}); -``` - -### 2. Interface ModuleDeclaration - -Voici les propriétés disponibles pour définir un module : - -```typescript -interface ModuleDeclaration { - id: string; // Identifiant unique (requis) - name: string; // Nom d'affichage (requis) - description: string; // Description (requis) - version: string; // Version semver (requis) - author?: string; // Auteur (optionnel) - intents?: GatewayIntentBits[]; // Intents Discord (optionnel) - - // Hooks du cycle de vie - onLoad: (client, registry) => void; - onInstall: (client, guild, registry) => void; - onUninstall: (client, guild, registry) => void; -} -``` - -## Bonnes Pratiques - -### Naming Convention - -- **ID du module** : kebab-case (`mon-super-module`) -- **Nom du fichier** : `{id}.module.ts` -- **Dossier** : même nom que l'ID du module - -### Gestion des Intents - -Ne déclarez uniquement les intents nécessaires à votre module. - -### Gestion des Erreurs - -Toujours gérer les erreurs dans les hooks : - -```typescript -onInstall(client, guild, registry); -{ - try { - // Logique d'installation - } catch (error) { - logger.error(`Erreur lors de l'installation du module : ${error.message}`); - } -} -``` - -### Organisation des Fichiers - -- Gardez le fichier principal du module léger -- Séparez la logique complexe dans des services -- Utilisez des sous-dossiers pour organiser commandes et listeners - -## Découverte automatique - -Les modules sont chargés automatiquement depuis `src/modules/` au démarrage — aucun enregistrement manuel n'est nécessaire en dehors du dossier du module. - -## Prochaines Étapes - -Une fois votre module créé, consultez : - -- [Guide des Commandes](./commands.md) pour ajouter des commandes -- [Guide des Listeners](./listeners.md) pour écouter des événements -- [Guide des Interactions](./interactions.md) pour définir des interactions (boutons, modal, etc.) diff --git a/docs/prisma.md b/docs/prisma.md deleted file mode 100644 index 0bf773c..0000000 --- a/docs/prisma.md +++ /dev/null @@ -1,181 +0,0 @@ -# Configuration Prisma - AsyncMod - -## Vue d'ensemble - -Ce projet utilise Prisma comme ORM avec une architecture modulaire personnalisée qui permet de distribuer les modèles de -base de données à travers différents modules tout en maintenant un schéma consolidé. - -## Architecture - -### Structure des fichiers - -``` -src/ -├── prisma/ -│ ├── header.prisma # Configuration du générateur et datasource -│ └── schema.prisma # Schéma consolidé (généré automatiquement) -├── core/ -│ └── models/ -│ └── modules.prisma # Modèles du système de modules -└── modules/ - └── [module-name]/ - └── models/ - └── *.prisma # Modèles spécifiques au module -``` - -### Configuration - -#### dbinfo.prisma - -Le fichier `dbinfo.prisma` contient la configuration de base qui sera utilisée pour tous les schémas consolidés : - -```prisma -generator client { - provider = "prisma-client-js" - output = "../src/generated/prisma" -} - -datasource db { - provider = "postgresql" - url = env("DATABASE_URL") -} -``` - -**Avantages de cette approche :** - -- Configuration centralisée et modifiable -- Possibilité de changer facilement la base de données ou les options du générateur -- Séparation claire entre configuration et modèles - -## Système de consolidation des modèles - -### Principe - -Au lieu d'avoir tous les modèles dans un seul fichier `schema.prisma`, ce projet permet de distribuer les modèles dans -différents fichiers `.prisma` à travers les modules. Un script de consolidation (`scripts/consolidate-schema.ts`) -rassemble automatiquement tous ces modèles avec le header dans le schéma principal. - -### Script de consolidation - -Le script `consolidate-schema.ts` : - -1. **Recherche récursive** : Parcourt tous les dossiers dans `src/` pour trouver les fichiers `.prisma` (sauf - dans le dossier `src/prisma/`) -2. **Consolidation** : Combine le header et tous les modèles trouvés dans un seul `schema.prisma` -3. **Organisation** : Ajoute des commentaires pour identifier la source de chaque modèle - -## Scripts NPM - -Le projet fournit plusieurs scripts pour faciliter le travail avec Prisma : - -```json -{ - "prisma:consolidate": "tsx scripts/consolidate-schema.ts", - "prisma:generate": "pnpm prisma:consolidate && prisma generate", - "prisma:migrate": "pnpm prisma:consolidate && prisma migrate dev", - "prisma:studio": "pnpm prisma:consolidate && prisma studio" -} -``` - -### Description des scripts - -- **`prisma:consolidate`** : Exécute uniquement la consolidation des modèles -- **`prisma:generate`** : Consolide puis génère le client Prisma -- **`prisma:migrate`** : Consolide puis crée/applique les migrations -- **`prisma:studio`** : Consolide puis lance Prisma Studio - -## Workflow de développement - -### 1. Ajouter un nouveau modèle - -1. Créer un fichier `.prisma` dans le dossier `models/` de votre module : - - ``` - src/modules/mon-module/models/mon-modele.prisma - ``` - -2. Définir votre modèle : - ```prisma - model MonModele { - id String @id @default(cuid()) - nom String - // ... autres champs - } - ``` - -### 2. Générer le client - -```bash -pnpm prisma:generate -``` - -Cette commande : - -1. Consolide tous les modèles -2. Génère le client TypeScript typé - -### 3. Créer une migration - -```bash -pnpm prisma:migrate -``` - -Cette commande : - -1. Consolide tous les modèles -2. Crée une nouvelle migration -3. L'applique à la base de données - -## Bonnes pratiques - -### Organisation des modèles - -- **Modèles du core** : `src/core/models/` -- **Modèles spécifiques aux modules** : `src/modules/[module-name]/models/` -- **Réduire le nombre de modèles par fichier** : Facilite la maintenance et la lecture - -### Nommage - -- **Fichiers** : `nom-descriptif.prisma` -- **Modèles** : PascalCase (ex: `ModuleActivation`) -- **Champs** : camelCase (ex: `moduleId`) - -## Dépannage - -### Erreur de consolidation - -Si le script de consolidation échoue : - -1. Vérifiez la syntaxe Prisma de tous vos fichiers `.prisma` -2. Assurez-vous qu'il n'y a pas de conflits de noms de modèles -3. Vérifiez les permissions de fichiers - -### Erreur de génération - -Si `prisma generate` échoue après consolidation : - -1. Vérifiez le fichier `schema.prisma` généré -2. Recherchez les erreurs de syntaxe ou de relations -3. Assurez-vous que toutes les relations sont valides - -### Problèmes de migration - -En cas de problème avec les migrations : - -1. Vérifiez que votre base de données est accessible -2. Assurez-vous que `DATABASE_URL` est correctement configurée -3. Consultez les logs de migration pour plus de détails - -## Variables d'environnement - -Assurez-vous d'avoir configuré dans votre fichier `.env` : - -```env -DATABASE_URL="postgresql://username:password@localhost:5432/database_name" -``` - -## Ressources utiles - -- [Documentation Prisma](https://www.prisma.io/docs/) -- [Prisma Schema Reference](https://www.prisma.io/docs/reference/api-reference/prisma-schema-reference) -- [Prisma Client API](https://www.prisma.io/docs/reference/api-reference/prisma-client-reference) diff --git a/docs/review/config-completion-findings.md b/docs/review/config-completion-findings.md index 5f9f45a..f5227b1 100644 --- a/docs/review/config-completion-findings.md +++ b/docs/review/config-completion-findings.md @@ -26,6 +26,7 @@ Légende statut : [ ] à faire · [x] fait - un champ **avec** `defaultValue` est toujours présent → **pas** de `| undefined`. - **Fix proposé :** - Typage conditionnel sur la présence de `defaultValue` : + ```ts type ConfigEntryValue> = E extends { defaultValue: unknown; @@ -33,7 +34,9 @@ Légende statut : [ ] à faire · [x] fait ? ResolveType : ResolveType | undefined; ``` + utilisé par `ConfigData` et `ConfigProvider.get`. + - Runtime : `createDefaultConfigForModule` ne pose une valeur que si `defaultValue` est déclaré (sinon clé absente → `undefined`), au lieu de fabriquer `""`/`0`/`false`/`null`. diff --git a/docs/services.md b/docs/services.md deleted file mode 100644 index fb839f4..0000000 --- a/docs/services.md +++ /dev/null @@ -1,69 +0,0 @@ -# Services - -Ce guide explique comment créer des services dans vos modules pour organiser la logique métier. - -## Structure des Services - -Les services permettent d'encapsuler la logique métier et de la réutiliser à travers votre module. Chaque service doit -être défini dans un fichier séparé dans le dossier `services/` de votre module. - -### Structure de fichier - -``` -src/modules/mon-module/ -├── mon-module.module.ts -└── services/ - └── mon-service.service.ts -``` - -## Création d'un Service - -```typescript -// src/modules/mon-module/services/user.service.ts -import { declareService, type Service } from "../../../lib/service.js"; -import prisma from "../../../lib/database.js"; - -class UserService implements Service { - async createUser(userId: string, username: string) { - return await prisma.user.create({ - data: { userId, username }, - }); - } - - async getUser(userId: string) { - return await prisma.user.findUnique({ - where: { userId }, - }); - } -} - -export default declareService(new UserService()); -``` - -## Interface Service - -La fonction `declareService` accepte une instance de classe qui implémente l'interface `Service`. L'interface est -actuellement vide, permettant une grande flexibilité dans la structure de vos services. - -## Utilisation - -Les services peuvent être importés et utilisés dans vos commandes, listeners ou autres services : - -```typescript -// Dans une commande -import userService from "../services/user.service.js"; - -async; -execute(interaction); -{ - const user = await userService.getUser(interaction.user.id); - // ... -} -``` - -## Bonnes Pratiques - -- Utilisez les services pour encapsuler la logique métier -- Gardez les services focalisés sur une responsabilité spécifique -- Utilisez les services pour les interactions avec la base de données -- Exportez toujours une instance déclarée avec `declareService` diff --git a/docs/site/.gitignore b/docs/site/.gitignore new file mode 100644 index 0000000..203f4b2 --- /dev/null +++ b/docs/site/.gitignore @@ -0,0 +1,4 @@ +node_modules/ +dist +.vitepress/dist +.vitepress/cache diff --git a/docs/site/.vitepress/config.ts b/docs/site/.vitepress/config.ts new file mode 100644 index 0000000..d7be5d8 --- /dev/null +++ b/docs/site/.vitepress/config.ts @@ -0,0 +1,117 @@ +import { defineConfig } from "vitepress"; + +// VitePress requires `base` to start and end with a slash; normalize whatever +// DOCS_BASE provides so an override like `/OmniBot` doesn't break routing/assets. +const rawBase = process.env["DOCS_BASE"] ?? "/OmniBot/"; +const trimmed = rawBase.replace(/^\/+|\/+$/g, ""); +const base = trimmed ? `/${trimmed}/` : "/"; + +export default defineConfig({ + base, + + title: "OmniBot", + description: "Documentation du bot Discord modulaire OmniBot", + + srcExclude: ["README.md"], + + cleanUrls: true, + + themeConfig: { + search: { + provider: "local", + }, + }, + + locales: { + fr: { + label: "Français", + lang: "fr", + link: "/fr/", + title: "OmniBot", + description: "Documentation du bot Discord modulaire OmniBot", + themeConfig: { + nav: [ + { text: "Accueil", link: "/fr/" }, + { text: "Guide", link: "/fr/guide/getting-started" }, + ], + sidebar: { + "/fr/guide/": [ + { + text: "Guide", + items: [ + { text: "Pour commencer", link: "/fr/guide/getting-started" }, + { + text: "Créer un module", + link: "/fr/guide/creating-a-module", + }, + { text: "Commandes", link: "/fr/guide/commands" }, + { text: "Écouteurs", link: "/fr/guide/listeners" }, + { text: "Interactions", link: "/fr/guide/interactions" }, + { text: "Configuration", link: "/fr/guide/configuration" }, + { text: "Services", link: "/fr/guide/services" }, + { text: "Base de données", link: "/fr/guide/database" }, + ], + }, + ], + }, + outline: { + label: "Sur cette page", + }, + returnToTopLabel: "Retour en haut", + sidebarMenuLabel: "Menu", + darkModeSwitchLabel: "Apparence", + lightModeSwitchTitle: "Passer en mode clair", + darkModeSwitchTitle: "Passer en mode sombre", + docFooter: { + prev: "Page précédente", + next: "Page suivante", + }, + }, + }, + en: { + label: "English", + lang: "en", + link: "/en/", + title: "OmniBot", + description: "OmniBot modular Discord bot documentation", + themeConfig: { + nav: [ + { text: "Guide", link: "/en/guide/getting-started" }, + { text: "Home", link: "/en/" }, + ], + sidebar: { + "/en/guide/": [ + { + text: "Guide", + items: [ + { text: "Getting Started", link: "/en/guide/getting-started" }, + { + text: "Creating a Module", + link: "/en/guide/creating-a-module", + }, + { text: "Commands", link: "/en/guide/commands" }, + { text: "Listeners", link: "/en/guide/listeners" }, + { text: "Interactions", link: "/en/guide/interactions" }, + { text: "Configuration", link: "/en/guide/configuration" }, + { text: "Services", link: "/en/guide/services" }, + { text: "Database", link: "/en/guide/database" }, + ], + }, + ], + }, + outline: { + label: "On this page", + }, + returnToTopLabel: "Return to top", + sidebarMenuLabel: "Menu", + darkModeSwitchLabel: "Appearance", + lightModeSwitchTitle: "Switch to light mode", + darkModeSwitchTitle: "Switch to dark mode", + docFooter: { + prev: "Previous page", + next: "Next page", + }, + }, + }, + }, +}); diff --git a/docs/site/README.md b/docs/site/README.md new file mode 100644 index 0000000..1d9075d --- /dev/null +++ b/docs/site/README.md @@ -0,0 +1,24 @@ +# Documentation OmniBot + +La documentation a été migrée vers [VitePress](https://vitepress.dev/). + +Pour lancer le serveur de documentation en local : + +```bash +pnpm --filter omnibot-docs dev +``` + +Pour construire la version statique : + +```bash +pnpm --filter omnibot-docs build +``` + +Les sources du site sont organisées dans `docs/site/` : + +- `en/` — Documentation en anglais +- `fr/` — Documentation en français + +> Les anciennes notes internes (spécifications, revues, comportement +> fonctionnel) restent en markdown simple dans `docs/` (`functional.md`, +> `specs/`, `review/`). diff --git a/docs/site/en/guide/commands.md b/docs/site/en/guide/commands.md new file mode 100644 index 0000000..56e920b --- /dev/null +++ b/docs/site/en/guide/commands.md @@ -0,0 +1,66 @@ +# Commands + +This guide explains how to create Discord slash commands in your modules. Commands are only available when the module is enabled on the guild. + +## Creating a command + +```typescript +// src/modules/my-module/commands/test.command.ts + +import { SlashCommandBuilder } from "discord.js"; +import { declareCommand } from "#lib/command.js"; + +export default declareCommand({ + data: new SlashCommandBuilder() + .setName("test") + .setDescription("A test command"), + + async execute(interaction) { + await interaction.reply("Test!"); + }, +}); +``` + +## Command interface + +```typescript +interface Command { + data: SlashCommandBuilder; // Command configuration + execute: ( + // Execution function (required) + interaction, + config + ) => Promise; + complete?: ( + // Autocomplete (optional) + interaction, + config + ) => Promise; +} +``` + +The `config` parameter is a `ConfigProvider` giving access to the module's configuration (see [Configuration](./configuration)). + +## Registering in the module + +```typescript +// src/modules/my-module/my-module.module.ts +import testCommand from "./commands/test.command.js"; + +export default defineModule({ + onLoad(_client, registry) { + registry.register(testCommand); + }, +}); +``` + +## Registration & propagation + +| Commands | Production | Development (`NODE_ENV=development`) | +| -------------------------------- | ------------------------------------------------- | -------------------------------------------------------------------- | +| **core** (`/config`, `/modules`) | Global (~1 h propagation) | Registered on `DEV_GUILD_ID` — **instant** | +| **module** | Per guild, (re)installed on module `version` bump | Re-synced **at every startup** on `DEV_GUILD_ID` for enabled modules | + +In production, version gating prevents re-pushing module commands to all guilds on every startup. In development, all commands are registered in a single PUT on the dev guild for instant updates. + +> `DEV_GUILD_ID` is **required** in development mode. See `.env.example`. diff --git a/docs/site/en/guide/configuration.md b/docs/site/en/guide/configuration.md new file mode 100644 index 0000000..f535961 --- /dev/null +++ b/docs/site/en/guide/configuration.md @@ -0,0 +1,153 @@ +# Configuration + +OmniBot provides a typed configuration system for modules. Each module can declare a configuration schema that is automatically exposed through an interactive Discord interface accessible to administrators. + +## Declaring a configuration schema + +A module declares its schema in `defineModule()` via the `config` property: + +```typescript +// src/modules/my-module/my-module.module.ts + +import { ConfigType } from "#lib/config.js"; +import { defineModule } from "#lib/module.js"; + +export default defineModule({ + id: "my-module", + name: "My Module", + description: "Description of my module.", + version: "1.0.0", + + config: { + logChannel: { + name: "Log channel", + description: "Channel where to post logs", + type: ConfigType.CHANNEL, + }, + maxWarnings: { + name: "Max warnings", + description: "Number of warnings before action", + type: ConfigType.NUMBER, + defaultValue: 3, + }, + logLevel: { + name: "Log level", + description: "Log verbosity", + type: ConfigType.ENUM, + options: ["debug", "info", "warn", "error"] as const, + defaultValue: "info", + }, + }, + + onLoad(_client, registry) { + // ... + }, +}); +``` + +## Supported field types + +| Type | Input | Storage (JSON) | Read | +| ---------- | --------------------- | ----------------- | ----------------- | +| `STRING` | Modal (text) | `string` | `string` | +| `NUMBER` | Modal (text → number) | `number` | `number` | +| `BOOLEAN` | Toggle button | `boolean` | `boolean` | +| `USER` | User select menu | `string` (id) | `User` | +| `ROLE` | Role select menu | `string` (id) | `Role` | +| `CHANNEL` | Channel select menu | `string` (id) | `Channel` | +| `CATEGORY` | Category select menu | `string` (id) | `CategoryChannel` | +| `ENUM` | Select menu (choices) | `string` (choice) | Literal union | + +## Lists + +Any type can be declared as a **list** using `type: [ConfigType.X]`: + +```typescript +config: { + channels: { + name: "Monitored channels", + description: "Channels to monitor (one or more)", + type: [ConfigType.CHANNEL], // Channel list + }, + warnings: { + name: "Warnings", + description: "List of warnings", + type: [ConfigType.NUMBER], // Number list + }, +} +``` + +The returned type of `config.get("channels")` is `Channel[]`. + +## ENUM type (fixed choices) + +The `ENUM` type restricts a field to a fixed set of values: + +```typescript +config: { + theme: { + name: "Theme", + description: "Display theme", + type: ConfigType.ENUM, + options: ["light", "dark", "auto"] as const, + defaultValue: "auto", + }, + features: { + name: "Features", + description: "Enabled features", + type: [ConfigType.ENUM], // Multi-select list + options: ["welcome", "logs", "automod"] as const, + }, +} +``` + +- `options` is **required** on an `ENUM` entry +- Using `as const` gives precise typing: `config.get("theme")` returns `"light" | "dark" | "auto"` +- Without `as const`, the returned type is `string` + +## Default values + +- A field **with** `defaultValue` is always present (`T`) +- A field **without** `defaultValue` may be `undefined` (`T | undefined`) +- Discord entities are automatically deserialized (stored as id, read as Discord objects) + +## Accessing configuration + +Configuration is accessible via the `config` parameter in commands, listeners, and interactions: + +```typescript +// In a command +async execute(interaction, config) { + const channel = config.get("logChannel"); // Channel | undefined + const max = config.get("maxWarnings"); // number (has defaultValue) + const level = config.get("logLevel"); // "debug" | "info" | "warn" | "error" +} +``` + +## Admin interface + +The `/config ` command (admin-only) displays an interactive configuration panel featuring: + +- All configurable fields with their type and current value +- Edit buttons adapted to each type (modal, toggle, select menu) +- Automatic pagination if the module has more than 10 fields +- Navigation bar when multiple pages + +Changes are persisted and take effect immediately. + +## Best practices + +### Field naming + +- **Key**: camelCase (`logChannel`, `maxWarnings`) +- **name**: human-readable, shown in the UI (`Log channel`) +- **description**: explains the field's purpose + +### Validation + +Validation is automatic based on the declared type. For `ENUM`, only values listed in `options` are accepted. + +## Next steps + +- [Services](./services) for organizing business logic +- [Database](./database) for advanced persistent data diff --git a/docs/site/en/guide/creating-a-module.md b/docs/site/en/guide/creating-a-module.md new file mode 100644 index 0000000..3ae7543 --- /dev/null +++ b/docs/site/en/guide/creating-a-module.md @@ -0,0 +1,146 @@ +# Creating a Module + +A module in OmniBot is a self-contained functional unit that can be installed and uninstalled per Discord guild. Each module can contain commands, event listeners, interactions, and its own data. + +## Module structure + +``` +src/modules/my-module/ +├── my-module.module.ts # Main module definition +├── commands/ # Module commands +│ └── my-command.command.ts +├── listeners/ # Event listeners +│ └── my-listener.listener.ts +├── interactions/ # Interaction handlers (buttons, etc.) +│ └── my-button.button.ts +├── services/ # Business services +│ └── my-service.service.ts +└── models/ # Prisma models (optional) + └── my-model.prisma +``` + +## Creating a module + +### 1. Define the main module file + +```typescript +// src/modules/my-module/my-module.module.ts + +import { GatewayIntentBits } from "discord.js"; +import { defineModule } from "#lib/module.js"; +import logger from "#lib/logger.js"; + +export default defineModule({ + id: "my-module", + name: "My Module", + description: "Description of my custom module.", + version: "1.0.0", + author: "Your Name", // Optional + + // Discord intents required by the module + intents: [ + GatewayIntentBits.Guilds, + GatewayIntentBits.GuildMessages, + GatewayIntentBits.MessageContent, + ], + + // Called at bot startup + onLoad(_client, registry) { + // Register commands, listeners, and interactions + // registry.register(myCommand); + // registry.register(myListener); + // registry.register(myInteraction); + + logger.info("My module loaded successfully"); + }, + + // Called when the module is installed on a guild + onInstall(_client, guild) { + logger.info(`My module installed on ${guild.name}`); + }, + + // Called when the module is uninstalled from a guild + onUninstall(_client, guild) { + logger.info(`My module uninstalled from ${guild.name}`); + }, +}); +``` + +### 2. Lifecycle hooks + +A module exposes three hooks: + +| Hook | When | Usage | +| ------------- | -------------------- | ------------------------------------------ | +| `onLoad` | Bot startup | Register commands, listeners, interactions | +| `onInstall` | Guild installation | Create roles, initialize data | +| `onUninstall` | Guild uninstallation | Clean up data, remove configurations | + +### 3. ModuleDeclaration interface + +```typescript +interface ModuleDeclaration { + id: string; // Unique identifier (kebab-case) + name: string; // Display name + description: string; // Description + version: string; // Semver version (e.g. "1.0.0") + author?: string; // Author (optional) + intents?: GatewayIntentBits[]; // Required Discord intents + devOnly?: boolean; // Only loaded in dev mode + config?: ConfigSchema; // Configuration schema (see Configuration) + + onLoad: (client, registry) => void; + onInstall: (client, guild, registry) => void; + onUninstall: (client, guild, registry) => void; +} +``` + +## Best practices + +### Naming conventions + +- **Module ID**: kebab-case (`my-super-module`) +- **File name**: `{id}.module.ts` +- **Directory**: same as module ID + +### Intent management + +Only declare the intents your module actually needs. See the [Discord documentation](https://discord.com/developers/docs/events/gateway#list-of-intents) for which intents are required. + +### Error handling + +```typescript +onInstall(_client, guild) { + try { + // Installation logic + } catch (error) { + logger.error(`Installation error: ${error.message}`); + } +} +``` + +### File organization + +- Keep the main module file lightweight +- Move complex logic to services +- Use subdirectories to organize commands, listeners, and interactions + +## Auto-discovery + +Modules are automatically loaded from `src/modules/` at startup. The loader (`module-loader.ts`): + +1. Iterates over each subdirectory in `src/modules/` +2. Looks for a `*.module.ts` (or `*.module.js`) file +3. Imports the module and validates it exposes a `Module`-typed default export +4. Skips modules marked `devOnly: true` in production + +No manual registration is needed. + +## Next steps + +- [Commands](./commands) — Add slash commands to your module +- [Listeners](./listeners) — React to Discord events +- [Interactions](./interactions) — Buttons, modals, and select menus +- [Configuration](./configuration) — Declare a configuration schema +- [Services](./services) — Organize business logic +- [Database](./database) — Use Prisma with your module diff --git a/docs/site/en/guide/database.md b/docs/site/en/guide/database.md new file mode 100644 index 0000000..4c5c229 --- /dev/null +++ b/docs/site/en/guide/database.md @@ -0,0 +1,118 @@ +# Database + +OmniBot uses Prisma as its ORM with a multi-file architecture that distributes database models across different modules. + +## Architecture + +### Prisma file structure + +``` +src/ +├── prisma/ +│ ├── dbinfo.prisma # Generator and datasource configuration +│ └── schema.prisma # Consolidated schema (generated) +├── core/ +│ └── models/ +│ └── modules.prisma # Core module system models +└── modules/ + └── [module-name]/ + └── models/ + └── *.prisma # Module-specific models +``` + +### Configuration + +The `dbinfo.prisma` file contains the base configuration: + +```prisma +generator client { + provider = "prisma-client-js" + output = "../src/generated/prisma" +} + +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} +``` + +## Adding a model to your module + +1. Create a `.prisma` file in your module's `models/` directory: + +``` +src/modules/my-module/models/my-model.prisma +``` + +2. Define your model: + +```prisma +model MyModel { + id String @id @default(cuid()) + name String + createdAt DateTime @default(now()) +} +``` + +3. Generate the Prisma client: + +```bash +pnpm prisma:generate +``` + +4. Create a migration: + +```bash +pnpm prisma:migrate +``` + +## Consolidation system + +Instead of keeping all models in a single `schema.prisma` file, the project distributes models across `.prisma` files in different modules. A consolidation script (`scripts/consolidate-schema.ts`) automatically merges all these files with the `dbinfo.prisma` header into the main schema. + +The script: + +1. Recursively scans all directories in `src/` for `.prisma` files (except `src/prisma/`) +2. Combines the header and all models into a single `schema.prisma` +3. Adds comments identifying each model's source + +### npm scripts + +```bash +pnpm prisma:consolidate # Consolidate models only +pnpm prisma:generate # Consolidate + generate Prisma client +pnpm prisma:migrate # Consolidate + create/apply migration +pnpm prisma:studio # Consolidate + open Prisma Studio +``` + +## Usage in code + +```typescript +import prisma from "#lib/database.js"; + +// In a service or command +const users = await prisma.user.findMany(); +const newUser = await prisma.user.create({ + data: { userId: "123", username: "test" }, +}); +``` + +## Best practices + +- **Core models**: `src/core/models/` +- **Module-specific models**: `src/modules/[module-name]/models/` +- **File naming**: `descriptive-name.prisma` +- **Model naming**: PascalCase (`ModuleActivation`) +- **Field naming**: camelCase (`moduleId`) +- Keep few models per file for easier maintenance + +## Environment variables + +```ini +DATABASE_URL="postgresql://username:password@localhost:5432/omnibot" +``` + +## Useful resources + +- [Prisma Documentation](https://www.prisma.io/docs/) +- [Prisma Schema Reference](https://www.prisma.io/docs/reference/api-reference/prisma-schema-reference) diff --git a/docs/site/en/guide/getting-started.md b/docs/site/en/guide/getting-started.md new file mode 100644 index 0000000..5fc3e28 --- /dev/null +++ b/docs/site/en/guide/getting-started.md @@ -0,0 +1,73 @@ +# Getting Started + +Welcome to the OmniBot development guide! This documentation explains how to create modules, commands, and listeners to extend the Discord bot's functionality. + +OmniBot is a modular Discord bot where each feature is encapsulated in its own module. Modules are auto-discovered at startup — **no manual registration is needed** outside the module directory. + +## Project structure + +``` +src/ +├── modules/ # All bot modules +│ ├── my-module/ # Module +│ │ ├── commands/ # Module commands +│ │ ├── listeners/ # Module listeners +│ │ ├── models/ # Prisma models (optional) +│ │ ├── services/ # Module services +│ │ ├── interactions/ # Interaction handlers (buttons, etc.) +│ │ └── my-module.module.ts # Main module file +├── lib/ # Library exposed to modules +│ ├── module.ts # defineModule() +│ ├── command.ts # declareCommand() +│ ├── listener.ts # declareEventListener() +│ ├── interaction.ts # declareInteractionHandler() +│ ├── service.ts # declareService() +│ ├── config.ts # ConfigType, ConfigProvider +│ ├── database.ts # Prisma client +│ └── logger.ts # Structured logger +├── core/ # Bot core system +│ ├── commands/ # System commands (/modules, /config) +│ ├── loaders/ # Module/command loaders +│ └── services/ # Internal services +└── prisma/ # Prisma configuration + ├── dbinfo.prisma # Generator + datasource config + └── schema.prisma # Consolidated schema (auto-generated) +``` + +## Naming conventions + +The project uses suffixes to identify each file's role: + +- `*.module.ts` — Module definition (e.g. `my-module.module.ts`) +- `*.command.ts` — Slash command (e.g. `test.command.ts`) +- `*.listener.ts` — Event listener (e.g. `message-create.listener.ts`) +- `*.button.ts` / `*.modal.ts` / `*.select.ts` — Interaction handlers +- `*.service.ts` — Business service (e.g. `user.service.ts`) +- `*.prisma` — Database model (e.g. `users.prisma`) + +## Imports + +Cross-directory imports use **subpath imports** with `#lib/...`: + +```typescript +import { defineModule } from "#lib/module.js"; +import { declareCommand } from "#lib/command.js"; +import { declareEventListener } from "#lib/listener.js"; +import { declareInteractionHandler } from "#lib/interaction.js"; +import { declareService } from "#lib/service.js"; +import { ConfigType } from "#lib/config.js"; +import logger from "#lib/logger.js"; +import prisma from "#lib/database.js"; +``` + +Same-directory imports stay relative (`./file.js`). The `.js` extension is always required (NodeNext). + +## Next steps + +- [Creating a Module](./creating-a-module) — Module structure and lifecycle +- [Commands](./commands) — Creating slash commands +- [Listeners](./listeners) — Reacting to Discord events +- [Interactions](./interactions) — Buttons, select menus, and modals +- [Configuration](./configuration) — Typed configuration schema +- [Services](./services) — Business logic +- [Database](./database) — Prisma ORM diff --git a/docs/site/en/guide/interactions.md b/docs/site/en/guide/interactions.md new file mode 100644 index 0000000..3b824e3 --- /dev/null +++ b/docs/site/en/guide/interactions.md @@ -0,0 +1,211 @@ +# Interactions + +Discord interactions allow your module to react to user actions on UI components: buttons, select menus, and modals. + +## Supported interaction types + +OmniBot supports two types of interactions: + +- **Message Component Interactions**: buttons, select menus +- **Modal Submit Interactions**: form submissions + +## File structure + +``` +src/modules/my-module/ +├── my-module.module.ts +├── commands/ +│ └── my-command.command.ts +└── interactions/ + ├── my-button.button.ts + ├── my-menu.select.ts + └── my-modal.modal.ts +``` + +## API + +```typescript +interface InteractionHandler { + customId: string; // Unique identifier + requiresAdmin?: boolean; // Admin-only + check: ( + interaction, + config // Type guard + ) => interaction is Interaction; + execute: ( + interaction, + args: string[], + config // Execution function + ) => Promise; +} +``` + +The `config` parameter is a `ConfigProvider` giving access to the module's configuration (see [Configuration](./configuration)). + +The `requiresAdmin` flag restricts the interaction to guild administrators. The check is enforced centrally by the system — no additional code needed. + +## Creating an interaction + +### Button + +```typescript +// src/modules/my-module/interactions/confirm.button.ts + +import { MessageFlags } from "discord.js"; +import { declareInteractionHandler } from "#lib/interaction.js"; + +export default declareInteractionHandler({ + customId: "confirm-action", + check: (interaction) => interaction.isButton(), + + async execute(interaction, [actionId, userId]) { + await interaction.reply({ + content: `Action ${actionId} confirmed for user ${userId}`, + flags: MessageFlags.Ephemeral, + }); + }, +}); +``` + +### Select menu + +```typescript +// src/modules/my-module/interactions/role-select.select.ts + +import { declareInteractionHandler } from "#lib/interaction.js"; + +export default declareInteractionHandler({ + customId: "role-select", + check: (interaction) => interaction.isStringSelectMenu(), + + async execute(interaction, [targetUserId]) { + const selectedRoles = interaction.values; + + await interaction.reply({ + content: `Roles ${selectedRoles.join(", ")} assigned to <@${targetUserId}>`, + ephemeral: true, + }); + }, +}); +``` + +### Modal + +```typescript +// src/modules/my-module/interactions/feedback.modal.ts + +import { declareInteractionHandler } from "#lib/interaction.js"; + +export default declareInteractionHandler({ + customId: "feedback-modal", + check: (interaction) => interaction.isModalSubmit(), + + async execute(interaction, [category]) { + const feedback = interaction.fields.getTextInputValue("feedback-input"); + + await interaction.reply({ + content: "Thank you for your feedback!", + ephemeral: true, + }); + }, +}); +``` + +## Custom ID with arguments + +The system supports passing arguments through the `customId` using `:` as separator: + +``` +customId:arg1:arg2:arg3 +``` + +### Usage in a command + +```typescript +import { ButtonBuilder, ButtonStyle } from "discord.js"; + +const button = new ButtonBuilder() + .setCustomId(`confirm-action:delete:${userId}`) + .setLabel("Confirm") + .setStyle(ButtonStyle.Danger); +``` + +Arguments are automatically extracted and passed to the handler as an array (`args`). + +## Registering in the module + +```typescript +// src/modules/my-module/my-module.module.ts +import confirmButton from "./interactions/confirm.button.js"; +import roleSelect from "./interactions/role-select.select.js"; + +export default defineModule({ + onLoad(_client, registry) { + registry.register(confirmButton); + registry.register(roleSelect); + }, +}); +``` + +## Automatic activation management + +The system handles interaction activation/deactivation automatically: + +- Interactions only work when the module is enabled on the guild +- No extra code needed to check module state +- Automatic error messages if the module is disabled +- Interactions marked `requiresAdmin: true` are auto-protected + +## Best practices + +### Argument validation + +```typescript +async execute(interaction, [userId, action]) { + if (!userId || !action) { + await interaction.reply({ + content: "Missing data. Please try again.", + ephemeral: true, + }); + return; + } +} +``` + +### Error handling + +```typescript +async execute(interaction, [targetId]) { + try { + await performRiskyOperation(targetId); + await interaction.reply({ content: "Success!", ephemeral: true }); + } catch (error) { + logger.error("Error:", error); + await interaction.reply({ + content: "An error occurred. Please try again.", + ephemeral: true, + }); + } +} +``` + +### Response types + +```typescript +// Instant action +await interaction.reply({ content: "Done!", ephemeral: true }); + +// Long running action +await interaction.deferReply({ ephemeral: true }); +await longOperation(); +await interaction.editReply({ content: "Finished!" }); + +// Update message (buttons) +await interaction.update({ content: "Updated", components: [] }); +``` + +## Next steps + +- [Configuration](./configuration) for managing module settings +- [Commands](./commands) for creating slash commands +- [Listeners](./listeners) for reacting to Discord events diff --git a/docs/site/en/guide/listeners.md b/docs/site/en/guide/listeners.md new file mode 100644 index 0000000..7aabe7f --- /dev/null +++ b/docs/site/en/guide/listeners.md @@ -0,0 +1,44 @@ +# Listeners + +Event listeners allow your module to react to Discord events. The module system automatically activates/deactivates listeners based on the module's installation state. + +> **Important**: For events that can occur in DMs (direct messages), you must manually check if the module is active, since the system cannot automatically detect module state outside of a guild. + +> **Discord Intents**: Make sure to add the required intents in `defineModule()` and in your Discord Developer Portal configuration. + +## Declaring a listener + +```typescript +// src/modules/my-module/listeners/message-create.listener.ts + +import { declareEventListener } from "#lib/listener.js"; + +export default declareEventListener({ + eventType: "messageCreate", + + async execute(message, config) { + // config is a ConfigProvider | undefined + // Your logic here + }, +}); +``` + +## Registering in the module + +```typescript +// src/modules/my-module/my-module.module.ts +import messageListener from "./listeners/message-create.listener.js"; + +export default defineModule({ + onLoad(_client, registry) { + registry.register(messageListener); + }, +}); +``` + +The system automatically enables or disables the listener based on whether the module is installed on the relevant guild. + +## Next steps + +- [Configuration](./configuration) for managing module settings +- [Database](./database) for persistent data diff --git a/docs/site/en/guide/services.md b/docs/site/en/guide/services.md new file mode 100644 index 0000000..3698cf8 --- /dev/null +++ b/docs/site/en/guide/services.md @@ -0,0 +1,53 @@ +# Services + +Services allow you to encapsulate business logic and reuse it across your module. + +## Creating a service + +```typescript +// src/modules/my-module/services/user.service.ts + +import { declareService, type Service } from "#lib/service.js"; +import prisma from "#lib/database.js"; + +class UserService implements Service { + async createUser(userId: string, username: string) { + return await prisma.user.create({ + data: { userId, username }, + }); + } + + async getUser(userId: string) { + return await prisma.user.findUnique({ + where: { userId }, + }); + } +} + +export default declareService(new UserService()); +``` + +## Service interface + +`declareService` accepts any class instance. The `Service` interface is currently empty, allowing great flexibility in service structure. + +## Usage + +Services can be imported and used in commands, listeners, or other services: + +```typescript +// In a command +import userService from "../services/user.service.js"; + +async execute(interaction) { + const user = await userService.getUser(interaction.user.id); + // ... +} +``` + +## Best practices + +- Use services to encapsulate complex business logic +- Keep services focused on a single responsibility +- Use services for database interactions +- Always export an instance declared with `declareService` diff --git a/docs/site/en/index.md b/docs/site/en/index.md new file mode 100644 index 0000000..bad17a1 --- /dev/null +++ b/docs/site/en/index.md @@ -0,0 +1,29 @@ +--- +layout: home + +hero: + name: OmniBot + text: Modular Discord Bot + tagline: Documentation for creating and extending the bot's functionality. + actions: + - theme: brand + text: Get Started + link: /en/guide/getting-started + - theme: alt + text: Create a Module + link: /en/guide/creating-a-module + +features: + - icon: 🧩 + title: Self-contained modules + details: Each feature is an independent module, installable per guild. + - icon: ⚡ + title: Slash commands + details: Create typed slash commands with autocomplete support. + - icon: 🔔 + title: Event listeners + details: React to Discord events with per-module auto-activation. + - icon: 🎛️ + title: Typed configuration + details: Declare a config schema and get an interactive editing UI. +--- diff --git a/docs/site/fr/guide/commands.md b/docs/site/fr/guide/commands.md new file mode 100644 index 0000000..0a448e0 --- /dev/null +++ b/docs/site/fr/guide/commands.md @@ -0,0 +1,66 @@ +# Commandes + +Ce guide explique comment créer des commandes slash Discord dans vos modules. Les commandes ne sont disponibles que si le module est activé sur le serveur. + +## Création d'une commande + +```typescript +// src/modules/mon-module/commands/test.command.ts + +import { SlashCommandBuilder } from "discord.js"; +import { declareCommand } from "#lib/command.js"; + +export default declareCommand({ + data: new SlashCommandBuilder() + .setName("test") + .setDescription("Une commande de test"), + + async execute(interaction) { + await interaction.reply("Test !"); + }, +}); +``` + +## Interface Command + +```typescript +interface Command { + data: SlashCommandBuilder; // Configuration de la commande + execute: ( + // Fonction d'exécution (obligatoire) + interaction, + config + ) => Promise; + complete?: ( + // Autocomplétion (optionnelle) + interaction, + config + ) => Promise; +} +``` + +Le paramètre `config` est un `ConfigProvider` qui donne accès à la configuration du module (voir [Configuration](./configuration)). + +## Enregistrement dans le module + +```typescript +// src/modules/mon-module/mon-module.module.ts +import testCommand from "./commands/test.command.js"; + +export default defineModule({ + onLoad(_client, registry) { + registry.register(testCommand); + }, +}); +``` + +## Enregistrement et propagation + +| Commandes | Production | Développement (`NODE_ENV=development`) | +| -------------------------------- | ---------------------------------------------------------------- | ----------------------------------------------------------------------------------- | +| **core** (`/config`, `/modules`) | Globales (propagation ~1 h) | Enregistrées sur `DEV_GUILD_ID` — **instantané** | +| **module** | Par serveur, (ré)installées au changement de `version` du module | Re-synchronisées **à chaque démarrage** sur `DEV_GUILD_ID` pour les modules activés | + +En production, le versionnage évite de re-pousser les commandes d'un module sur toutes les guildes à chaque démarrage. En développement, les commandes sont enregistrées en un seul PUT sur la guild de développement, ce qui est instantané. + +> `DEV_GUILD_ID` est **requis** en mode développement. Voir `.env.example`. diff --git a/docs/site/fr/guide/configuration.md b/docs/site/fr/guide/configuration.md new file mode 100644 index 0000000..02b1a29 --- /dev/null +++ b/docs/site/fr/guide/configuration.md @@ -0,0 +1,153 @@ +# Configuration + +OmniBot fournit un système de configuration typée pour les modules. Chaque module peut déclarer un schéma de configuration qui est automatiquement exposé via une interface interactive Discord accessible aux administrateurs. + +## Déclarer un schéma de configuration + +Un module déclare son schéma dans `defineModule()` via la propriété `config` : + +```typescript +// src/modules/mon-module/mon-module.module.ts + +import { ConfigType } from "#lib/config.js"; +import { defineModule } from "#lib/module.js"; + +export default defineModule({ + id: "mon-module", + name: "Mon Module", + description: "Description de mon module.", + version: "1.0.0", + + config: { + logChannel: { + name: "Salon de logs", + description: "Salon où poster les logs", + type: ConfigType.CHANNEL, + }, + maxWarnings: { + name: "Avertissements max", + description: "Nombre d'avertissements avant sanction", + type: ConfigType.NUMBER, + defaultValue: 3, + }, + logLevel: { + name: "Niveau de log", + description: "Verbosité des logs", + type: ConfigType.ENUM, + options: ["debug", "info", "warn", "error"] as const, + defaultValue: "info", + }, + }, + + onLoad(_client, registry) { + // ... + }, +}); +``` + +## Types de champs supportés + +| Type | Saisie | Stockage (JSON) | Lecture | +| ---------- | ------------------------ | ---------------- | ----------------- | +| `STRING` | Modal (texte) | `string` | `string` | +| `NUMBER` | Modal (texte → nombre) | `number` | `number` | +| `BOOLEAN` | Bouton toggle | `boolean` | `boolean` | +| `USER` | Select menu utilisateur | `string` (id) | `User` | +| `ROLE` | Select menu rôle | `string` (id) | `Role` | +| `CHANNEL` | Select menu salon | `string` (id) | `Channel` | +| `CATEGORY` | Select menu catégorie | `string` (id) | `CategoryChannel` | +| `ENUM` | Select menu (choix fixe) | `string` (choix) | Union littérale | + +## Listes + +N'importe quel type peut être déclaré en **liste** via `type: [ConfigType.X]` : + +```typescript +config: { + channels: { + name: "Salons surveillés", + description: "Salons à surveiller (un ou plusieurs)", + type: [ConfigType.CHANNEL], // Liste de salons + }, + warnings: { + name: "Avertissements", + description: "Liste d'avertissements", + type: [ConfigType.NUMBER], // Liste de nombres + }, +} +``` + +Le type retourné par `config.get("channels")` est alors `Channel[]`. + +## Type ENUM (choix fixe) + +Le type `ENUM` permet de restreindre un champ à un ensemble fixe de valeurs : + +```typescript +config: { + theme: { + name: "Thème", + description: "Thème d'affichage", + type: ConfigType.ENUM, + options: ["clair", "sombre", "auto"] as const, + defaultValue: "auto", + }, + features: { + name: "Fonctionnalités", + description: "Fonctionnalités activées", + type: [ConfigType.ENUM], // Liste multi-choix + options: ["welcome", "logs", "automod"] as const, + }, +} +``` + +- `options` est **obligatoire** sur une entrée `ENUM` +- L'utilisation de `as const` donne un typage précis : `config.get("theme")` retourne `"clair" | "sombre" | "auto"` +- Sans `as const`, le type retourné est `string` + +## Valeurs par défaut + +- Un champ **avec** `defaultValue` est toujours présent (`T`) +- Un champ **sans** `defaultValue` peut être `undefined` (`T | undefined`) +- Les entités Discord sont automatiquement désérialisées (du stockage par id vers l'objet Discord) + +## Accès à la configuration + +La configuration est accessible via le paramètre `config` dans les commandes, écouteurs et interactions : + +```typescript +// Dans une commande +async execute(interaction, config) { + const channel = config.get("logChannel"); // Channel | undefined + const max = config.get("maxWarnings"); // number (a un defaultValue) + const level = config.get("logLevel"); // "debug" | "info" | "warn" | "error" +} +``` + +## Interface administrateur + +La commande `/config ` (réservée aux administrateurs) affiche un panneau de configuration interactif avec : + +- La liste de tous les champs configurables, leur type et valeur courante +- Des boutons d'édition adaptés à chaque type (modal, toggle, select menu) +- Une pagination automatique si le module a plus de 10 champs configurables +- Barre de navigation si plusieurs pages + +Les modifications sont persistées et prennent effet immédiatement. + +## Bonnes pratiques + +### Nommage des champs + +- **Clé** : camelCase (`logChannel`, `maxWarnings`) +- **name** : lisible, affiché dans l'interface (`Salon de logs`) +- **description** : explique le rôle du champ + +### Validation + +La validation est automatique selon le type déclaré. Pour `ENUM`, seules les valeurs listées dans `options` sont acceptées. + +## Prochaines étapes + +- [Services](./services) pour organiser la logique métier +- [Base de données](./database) pour des données persistantes avancées diff --git a/docs/site/fr/guide/creating-a-module.md b/docs/site/fr/guide/creating-a-module.md new file mode 100644 index 0000000..f8b8acf --- /dev/null +++ b/docs/site/fr/guide/creating-a-module.md @@ -0,0 +1,148 @@ +# Créer un module + +Un module dans OmniBot est une unité fonctionnelle autonome qui peut être installée et désinstallée par serveur Discord. Chaque module peut contenir des commandes, des écouteurs d'événements, des interactions et ses propres données. + +## Structure d'un module + +``` +src/modules/mon-module/ +├── mon-module.module.ts # Définition du module principal +├── commands/ # Commandes du module +│ └── ma-commande.command.ts +├── listeners/ # Écouteurs d'événements +│ └── mon-listener.listener.ts +├── interactions/ # Handlers d'interactions (boutons, etc.) +│ └── mon-bouton.button.ts +├── services/ # Services métier +│ └── mon-service.service.ts +└── models/ # Modèles Prisma (optionnel) + └── mon-modele.prisma +``` + +## Création d'un module + +### 1. Définition du module principal + +Créez le fichier principal de votre module : + +```typescript +// src/modules/mon-module/mon-module.module.ts + +import { GatewayIntentBits } from "discord.js"; +import { defineModule } from "#lib/module.js"; +import logger from "#lib/logger.js"; + +export default defineModule({ + id: "mon-module", + name: "Mon Module", + description: "Description de mon module personnalisé.", + version: "1.0.0", + author: "Votre Nom", // Optionnel + + // Intents Discord requis par le module + intents: [ + GatewayIntentBits.Guilds, + GatewayIntentBits.GuildMessages, + GatewayIntentBits.MessageContent, + ], + + // Appelé au démarrage du bot + onLoad(_client, registry) { + // Enregistrer les commandes, listeners et interactions + // registry.register(maCommande); + // registry.register(monListener); + // registry.register(monInteraction); + + logger.info("Mon module chargé avec succès"); + }, + + // Appelé quand le module est installé sur un serveur + onInstall(_client, guild) { + logger.info(`Mon module installé sur ${guild.name}`); + }, + + // Appelé quand le module est désinstallé d'un serveur + onUninstall(_client, guild) { + logger.info(`Mon module désinstallé de ${guild.name}`); + }, +}); +``` + +### 2. Cycles de vie + +Un module expose trois hooks : + +| Hook | Quand | Usage | +| ------------- | ---------------------------- | -------------------------------------------------- | +| `onLoad` | Au démarrage du bot | Enregistrer commandes, listeners, interactions | +| `onInstall` | Installation sur un serveur | Créer des rôles, initialiser des données | +| `onUninstall` | Désinstallation d'un serveur | Nettoyer des données, supprimer des configurations | + +### 3. Interface ModuleDeclaration + +```typescript +interface ModuleDeclaration { + id: string; // Identifiant unique (kebab-case) + name: string; // Nom d'affichage + description: string; // Description + version: string; // Version semver (ex: "1.0.0") + author?: string; // Auteur (optionnel) + intents?: GatewayIntentBits[]; // Intents Discord requis + devOnly?: boolean; // Chargé seulement en mode développement + config?: ConfigSchema; // Schéma de configuration (voir Configuration) + + onLoad: (client, registry) => void; + onInstall: (client, guild, registry) => void; + onUninstall: (client, guild, registry) => void; +} +``` + +## Bonnes pratiques + +### Conventions de nommage + +- **ID du module** : kebab-case (`mon-super-module`) +- **Nom du fichier** : `{id}.module.ts` +- **Dossier** : même nom que l'ID du module + +### Gestion des intents + +Déclarez uniquement les intents nécessaires à votre module. Consultez la [documentation Discord](https://discord.com/developers/docs/events/gateway#list-of-intents) pour savoir quels intents sont requis. + +### Gestion des erreurs + +```typescript +onInstall(_client, guild) { + try { + // Logique d'installation + } catch (error) { + logger.error(`Erreur lors de l'installation : ${error.message}`); + } +} +``` + +### Organisation des fichiers + +- Gardez le fichier principal du module léger +- Séparez la logique complexe dans des services +- Utilisez des sous-dossiers pour organiser commandes, listeners et interactions + +## Découverte automatique + +Les modules sont chargés automatiquement depuis `src/modules/` au démarrage. Le chargeur (`module-loader.ts`) : + +1. Parcourt chaque sous-dossier de `src/modules/` +2. Cherche un fichier `*.module.ts` (ou `*.module.js`) +3. Importe le module et vérifie qu'il expose un `default` de type `Module` +4. Ignore les modules marqués `devOnly: true` en production + +Aucun enregistrement manuel n'est nécessaire. + +## Prochaines étapes + +- [Commandes](./commands) — Ajouter des commandes slash à votre module +- [Écouteurs](./listeners) — Réagir aux événements Discord +- [Interactions](./interactions) — Boutons, modales et menus déroulants +- [Configuration](./configuration) — Déclarer un schéma de configuration +- [Services](./services) — Organiser la logique métier +- [Base de données](./database) — Utiliser Prisma avec votre module diff --git a/docs/site/fr/guide/database.md b/docs/site/fr/guide/database.md new file mode 100644 index 0000000..f2300a8 --- /dev/null +++ b/docs/site/fr/guide/database.md @@ -0,0 +1,118 @@ +# Base de données + +OmniBot utilise Prisma comme ORM avec une architecture multi-fichiers qui permet de distribuer les modèles de base de données à travers les différents modules. + +## Architecture + +### Structure des fichiers Prisma + +``` +src/ +├── prisma/ +│ ├── dbinfo.prisma # Configuration du générateur et datasource +│ └── schema.prisma # Schéma consolidé (généré) +├── core/ +│ └── models/ +│ └── modules.prisma # Modèles du système de modules +└── modules/ + └── [module-name]/ + └── models/ + └── *.prisma # Modèles spécifiques au module +``` + +### Configuration + +Le fichier `dbinfo.prisma` contient la configuration de base : + +```prisma +generator client { + provider = "prisma-client-js" + output = "../src/generated/prisma" +} + +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} +``` + +## Ajouter un modèle à votre module + +1. Créez un fichier `.prisma` dans le dossier `models/` de votre module : + +``` +src/modules/mon-module/models/mon-modele.prisma +``` + +2. Définissez votre modèle : + +```prisma +model MonModele { + id String @id @default(cuid()) + nom String + createdAt DateTime @default(now()) +} +``` + +3. Générez le client Prisma : + +```bash +pnpm prisma:generate +``` + +4. Créez une migration : + +```bash +pnpm prisma:migrate +``` + +## Système de consolidation + +Au lieu d'avoir tous les modèles dans un seul fichier `schema.prisma`, le projet permet de distribuer les modèles dans différents fichiers `.prisma` à travers les modules. Un script de consolidation (`scripts/consolidate-schema.ts`) rassemble automatiquement tous ces fichiers avec le header `dbinfo.prisma` dans le schéma principal. + +Le script : + +1. Parcourt tous les dossiers dans `src/` pour trouver les fichiers `.prisma` (sauf dans `src/prisma/`) +2. Combine le header et tous les modèles dans un seul `schema.prisma` +3. Ajoute des commentaires pour identifier la source de chaque modèle + +### Scripts npm + +```bash +pnpm prisma:consolidate # Consolide uniquement les modèles +pnpm prisma:generate # Consolide + génère le client Prisma +pnpm prisma:migrate # Consolide + crée/applique une migration +pnpm prisma:studio # Consolide + ouvre Prisma Studio +``` + +## Utilisation dans le code + +```typescript +import prisma from "#lib/database.js"; + +// Dans un service ou une commande +const users = await prisma.user.findMany(); +const newUser = await prisma.user.create({ + data: { userId: "123", username: "test" }, +}); +``` + +## Bonnes pratiques + +- **Modèles du core** : `src/core/models/` +- **Modèles spécifiques aux modules** : `src/modules/[module-name]/models/` +- **Nommage des fichiers** : `nom-descriptif.prisma` +- **Nommage des modèles** : PascalCase (`ModuleActivation`) +- **Nommage des champs** : camelCase (`moduleId`) +- Réduisez le nombre de modèles par fichier pour faciliter la maintenance + +## Variables d'environnement + +```ini +DATABASE_URL="postgresql://username:password@localhost:5432/omnibot" +``` + +## Ressources utiles + +- [Documentation Prisma](https://www.prisma.io/docs/) +- [Prisma Schema Reference](https://www.prisma.io/docs/reference/api-reference/prisma-schema-reference) diff --git a/docs/site/fr/guide/getting-started.md b/docs/site/fr/guide/getting-started.md new file mode 100644 index 0000000..21b93b3 --- /dev/null +++ b/docs/site/fr/guide/getting-started.md @@ -0,0 +1,73 @@ +# Pour commencer + +Bienvenue dans le guide de développement d'OmniBot ! Cette documentation vous explique comment créer des modules, commandes et écouteurs pour étendre les fonctionnalités du bot Discord. + +OmniBot est un bot Discord modulaire où chaque fonctionnalité est encapsulée dans son propre module. Les modules sont auto-découverts au démarrage — **aucun enregistrement manuel n'est nécessaire** en dehors du dossier du module. + +## Architecture du projet + +``` +src/ +├── modules/ # Dossier contenant tous les modules du bot +│ ├── mon-module/ # Module +│ │ ├── commands/ # Commandes du module +│ │ ├── listeners/ # Écouteurs du module +│ │ ├── models/ # Modèles Prisma (optionnel) +│ │ ├── services/ # Services du module +│ │ ├── interactions/ # Handlers d'interactions (boutons, etc.) +│ │ └── mon-module.module.ts # Fichier principal du module +├── lib/ # Bibliothèque exposée aux modules +│ ├── module.ts # defineModule() +│ ├── command.ts # declareCommand() +│ ├── listener.ts # declareEventListener() +│ ├── interaction.ts # declareInteractionHandler() +│ ├── service.ts # declareService() +│ ├── config.ts # ConfigType, ConfigProvider +│ ├── database.ts # Client Prisma +│ └── logger.ts # Logger structuré +├── core/ # Système central du bot +│ ├── commands/ # Commandes système (/modules, /config) +│ ├── loaders/ # Chargeurs de modules/commandes +│ └── services/ # Services internes +└── prisma/ # Configuration Prisma + ├── dbinfo.prisma # Configuration générateur + datasource + └── schema.prisma # Schéma consolidé (généré automatiquement) +``` + +## Conventions de nommage + +Le projet utilise un système de suffixes pour identifier le rôle de chaque fichier : + +- `*.module.ts` — Définition du module (ex: `mon-module.module.ts`) +- `*.command.ts` — Commande slash (ex: `test.command.ts`) +- `*.listener.ts` — Écouteur d'événement (ex: `message-create.listener.ts`) +- `*.button.ts` / `*.modal.ts` / `*.select.ts` — Handlers d'interactions +- `*.service.ts` — Service métier (ex: `user.service.ts`) +- `*.prisma` — Modèle de base de données (ex: `users.prisma`) + +## Imports + +Les imports entre modules se font via des **subpath imports** avec `#lib/...` : + +```typescript +import { defineModule } from "#lib/module.js"; +import { declareCommand } from "#lib/command.js"; +import { declareEventListener } from "#lib/listener.js"; +import { declareInteractionHandler } from "#lib/interaction.js"; +import { declareService } from "#lib/service.js"; +import { ConfigType } from "#lib/config.js"; +import logger from "#lib/logger.js"; +import prisma from "#lib/database.js"; +``` + +Les imports dans un même répertoire restent relatifs (`./fichier.js`). L'extension `.js` est toujours présente (NodeNext). + +## Prochaines étapes + +- [Créer un module](./creating-a-module) — Structure et cycle de vie d'un module +- [Commandes](./commands) — Créer des commandes slash +- [Écouteurs](./listeners) — Réagir aux événements Discord +- [Interactions](./interactions) — Boutons, menus déroulants et modales +- [Configuration](./configuration) — Schéma de configuration typé +- [Services](./services) — Logique métier +- [Base de données](./database) — Prisma ORM diff --git a/docs/interactions.md b/docs/site/fr/guide/interactions.md similarity index 54% rename from docs/interactions.md rename to docs/site/fr/guide/interactions.md index 45a0356..1d395aa 100644 --- a/docs/interactions.md +++ b/docs/site/fr/guide/interactions.md @@ -1,21 +1,19 @@ -# Guide des Interactions Discord +# Interactions -Les interactions Discord permettent à votre module de réagir aux interactions des utilisateurs avec des composants UI comme les boutons, menus déroulants et modales. Ce système complète les commandes slash et les listeners d'événements. +Les interactions Discord permettent à votre module de réagir aux actions des utilisateurs sur des composants d'interface : boutons, menus déroulants et modales. -## Types d'Interactions Supportées +## Types d'interactions supportées -AsyncMod supporte deux types d'interactions : +OmniBot supporte deux types d'interactions : -- **Message Component Interactions** : Boutons, menus déroulants, etc. -- **Modal Submit Interactions** : Soumissions de formulaires modaux +- **Message Component Interactions** : boutons, menus déroulants (select menus) +- **Modal Submit Interactions** : soumissions de formulaires modaux -## Structure des Interactions - -### Structure de fichier +## Structure des fichiers ``` src/modules/mon-module/ -├── module.ts +├── mon-module.module.ts ├── commands/ │ └── ma-commande.command.ts └── interactions/ @@ -24,26 +22,37 @@ src/modules/mon-module/ └── ma-modale.modal.ts ``` -## API des Interactions - -### Interface InteractionHandler +## API ```typescript -export interface InteractionHandler { +interface InteractionHandler { customId: string; // Identifiant unique - check: (interaction: CompatibleInteraction) => interaction is T; // Vérification du type - execute: (interaction: T, args: string[]) => Promise; // Fonction d'exécution + requiresAdmin?: boolean; // Réservé aux admins + check: ( + interaction, + config // Garde de type + ) => interaction is Interaction; + execute: ( + interaction, + args: string[], + config // Fonction d'exécution + ) => Promise; } ``` -## Création d'une Interaction +Le paramètre `config` est un `ConfigProvider` qui donne accès à la configuration du module (voir [Configuration](./configuration)). + +Le paramètre `requiresAdmin` permet de réserver l'interaction aux administrateurs du serveur. La vérification est faite centralement par le système — aucun code supplémentaire n'est nécessaire. -### Exemple : Bouton +## Création d'une interaction + +### Bouton ```typescript // src/modules/mon-module/interactions/confirm.button.ts + import { MessageFlags } from "discord.js"; -import { declareInteractionHandler } from "../../../lib/interaction.js"; +import { declareInteractionHandler } from "#lib/interaction.js"; export default declareInteractionHandler({ customId: "confirm-action", @@ -58,11 +67,12 @@ export default declareInteractionHandler({ }); ``` -### Exemple : Menu Déroulant +### Menu déroulant ```typescript // src/modules/mon-module/interactions/role-select.select.ts -import { declareInteractionHandler } from "../../../lib/interaction.js"; + +import { declareInteractionHandler } from "#lib/interaction.js"; export default declareInteractionHandler({ customId: "role-select", @@ -79,11 +89,12 @@ export default declareInteractionHandler({ }); ``` -### Exemple : Modal +### Modal ```typescript // src/modules/mon-module/interactions/feedback.modal.ts -import { declareInteractionHandler } from "../../../lib/interaction.js"; + +import { declareInteractionHandler } from "#lib/interaction.js"; export default declareInteractionHandler({ customId: "feedback-modal", @@ -92,9 +103,6 @@ export default declareInteractionHandler({ async execute(interaction, [category]) { const feedback = interaction.fields.getTextInputValue("feedback-input"); - // Traitement du feedback - console.log(`Feedback reçu (${category}): ${feedback}`); - await interaction.reply({ content: "Merci pour votre retour !", ephemeral: true, @@ -103,9 +111,9 @@ export default declareInteractionHandler({ }); ``` -## Système de Custom ID avec Arguments +## Système de Custom ID avec arguments -Le système AsyncMod permet de passer des arguments via le `customId` : +Le système permet de passer des arguments via le `customId` en utilisant le séparateur `:` : ``` customId:arg1:arg2:arg3 @@ -114,40 +122,43 @@ customId:arg1:arg2:arg3 ### Utilisation dans une commande ```typescript -// Dans une commande qui crée un bouton +import { ButtonBuilder, ButtonStyle } from "discord.js"; + const button = new ButtonBuilder() .setCustomId(`confirm-action:delete:${userId}`) .setLabel("Confirmer") .setStyle(ButtonStyle.Danger); ``` -## Enregistrement dans le Module +Les arguments sont automatiquement extraits et passés au handler sous forme de tableau (`args`). + +## Enregistrement dans le module ```typescript // src/modules/mon-module/mon-module.module.ts import confirmButton from "./interactions/confirm.button.js"; -import roleSelect from "./interactions/role-select.select.js -"; +import roleSelect from "./interactions/role-select.select.js"; export default defineModule({ - onLoad(client, registry) { + onLoad(_client, registry) { registry.register(confirmButton); registry.register(roleSelect); }, }); ``` -## Gestion Automatique de l'Activation +## Gestion automatique de l'activation -Le système AsyncMod gère automatiquement l'activation/désactivation des interactions : +Le système gère automatiquement l'activation et la désactivation des interactions : -- ✅ Les interactions ne fonctionnent que si le module est activé sur le serveur -- ✅ Pas de code supplémentaire nécessaire pour vérifier l'état -- ✅ Messages d'erreur automatiques si le module est désactivé +- Les interactions ne fonctionnent que si le module est activé sur le serveur +- Aucun code supplémentaire nécessaire pour vérifier l'état du module +- Messages d'erreur automatiques si le module est désactivé +- Les interactions marquées `requiresAdmin: true` sont protégées automatiquement -## Bonnes Pratiques +## Bonnes pratiques -### 1. Validation des Arguments +### Validation des arguments ```typescript async execute(interaction, [userId, action]) { @@ -158,11 +169,10 @@ async execute(interaction, [userId, action]) { }); return; } - // Logique principale } ``` -### 2. Gestion des Erreurs +### Gestion des erreurs ```typescript async execute(interaction, [targetId]) { @@ -170,16 +180,16 @@ async execute(interaction, [targetId]) { await performRiskyOperation(targetId); await interaction.reply({ content: "Opération réussie !", ephemeral: true }); } catch (error) { - console.error("Erreur:", error); + logger.error("Erreur:", error); await interaction.reply({ - content: "Une erreur s'est produite. Veuillez réessayer.", + content: "Une erreur s'est produite.", ephemeral: true, }); } } ``` -### 3. Types de Réponses +### Types de réponses ```typescript // Action instantanée @@ -194,8 +204,8 @@ await interaction.editReply({ content: "Terminé !" }); await interaction.update({ content: "Mis à jour", components: [] }); ``` -## Prochaines Étapes +## Prochaines étapes -- [Configuration des Modules](./modules.md) pour gérer les paramètres -- [Commandes Discord](./commands.md) pour créer des commandes slash -- [Guide des Listeners](./listeners.md) pour réagir aux événements +- [Configuration](./configuration) pour gérer les paramètres du module +- [Commandes](./commands) pour créer des commandes slash +- [Écouteurs](./listeners) pour réagir aux événements Discord diff --git a/docs/site/fr/guide/listeners.md b/docs/site/fr/guide/listeners.md new file mode 100644 index 0000000..5d5dfa1 --- /dev/null +++ b/docs/site/fr/guide/listeners.md @@ -0,0 +1,44 @@ +# Écouteurs + +Les écouteurs d'événements (listeners) permettent à votre module de réagir aux événements Discord. Le système de modules gère automatiquement l'activation/désactivation des écouteurs selon l'état du module. + +> **Important** : Pour les événements pouvant se produire en messages privés, vous devez gérer manuellement la vérification d'activation du module, car le système ne peut pas détecter automatiquement si le module est actif en dehors d'un serveur. + +> **Intents Discord** : Assurez-vous d'ajouter les intents nécessaires dans `defineModule()` et dans votre configuration Discord Developer Portal. + +## Déclaration d'un écouteur + +```typescript +// src/modules/mon-module/listeners/message-create.listener.ts + +import { declareEventListener } from "#lib/listener.js"; + +export default declareEventListener({ + eventType: "messageCreate", + + async execute(message, config) { + // config est un ConfigProvider | undefined + // Votre logique ici + }, +}); +``` + +## Enregistrement dans le module + +```typescript +// src/modules/mon-module/mon-module.module.ts +import messageListener from "./listeners/message-create.listener.js"; + +export default defineModule({ + onLoad(_client, registry) { + registry.register(messageListener); + }, +}); +``` + +Le système active ou désactive automatiquement l'écouteur selon que le module est installé sur le serveur concerné. + +## Prochaines étapes + +- [Configuration](./configuration) pour gérer les paramètres du module +- [Base de données](./database) pour les données persistantes diff --git a/docs/site/fr/guide/services.md b/docs/site/fr/guide/services.md new file mode 100644 index 0000000..ed61176 --- /dev/null +++ b/docs/site/fr/guide/services.md @@ -0,0 +1,53 @@ +# Services + +Les services permettent d'encapsuler la logique métier et de la réutiliser à travers votre module. + +## Création d'un service + +```typescript +// src/modules/mon-module/services/user.service.ts + +import { declareService, type Service } from "#lib/service.js"; +import prisma from "#lib/database.js"; + +class UserService implements Service { + async createUser(userId: string, username: string) { + return await prisma.user.create({ + data: { userId, username }, + }); + } + + async getUser(userId: string) { + return await prisma.user.findUnique({ + where: { userId }, + }); + } +} + +export default declareService(new UserService()); +``` + +## Interface Service + +La fonction `declareService` accepte n'importe quelle instance de classe. L'interface `Service` est pour l'instant vide, ce qui permet une grande flexibilité dans la structure de vos services. + +## Utilisation + +Les services s'importent et s'utilisent dans vos commandes, écouteurs ou autres services : + +```typescript +// Dans une commande +import userService from "../services/user.service.js"; + +async execute(interaction) { + const user = await userService.getUser(interaction.user.id); + // ... +} +``` + +## Bonnes pratiques + +- Utilisez les services pour encapsuler la logique métier complexe +- Gardez les services focalisés sur une responsabilité spécifique +- Utilisez les services pour les interactions avec la base de données +- Exportez toujours une instance déclarée avec `declareService` diff --git a/docs/site/fr/index.md b/docs/site/fr/index.md new file mode 100644 index 0000000..816b540 --- /dev/null +++ b/docs/site/fr/index.md @@ -0,0 +1,29 @@ +--- +layout: home + +hero: + name: OmniBot + text: Bot Discord modulaire + tagline: Documentation pour créer et étendre les fonctionnalités du bot. + actions: + - theme: brand + text: Commencer + link: /fr/guide/getting-started + - theme: alt + text: Créer un module + link: /fr/guide/creating-a-module + +features: + - icon: 🧩 + title: Modules autonomes + details: Chaque fonctionnalité est un module indépendant, installable par serveur. + - icon: ⚡ + title: Commandes slash + details: Créez des commandes slash typées avec autocomplétion. + - icon: 🔔 + title: Écouteurs d'événements + details: Réagissez aux événements Discord avec activation automatique par module. + - icon: 🎛️ + title: Configuration typée + details: Déclarez un schéma de configuration et obtenez une UI interactive. +--- diff --git a/docs/site/index.md b/docs/site/index.md new file mode 100644 index 0000000..c70b88f --- /dev/null +++ b/docs/site/index.md @@ -0,0 +1,37 @@ +--- +layout: home + +hero: + name: OmniBot + text: Bot Discord modulaire + tagline: Un bot Discord modulaire, extensible et configurable. + actions: + - theme: brand + text: Documentation + link: /fr/guide/getting-started + - theme: alt + text: English docs + link: /en/guide/getting-started + +features: + - icon: 🧩 + title: Modules autonomes + details: Chaque fonctionnalité est un module indépendant, installable par serveur. + - icon: ⚡ + title: Commandes slash + details: Créez des commandes slash typées avec autocomplétion. + - icon: 🔔 + title: Écouteurs d'événements + details: Réagissez aux événements Discord avec activation automatique. + - icon: 🎛️ + title: Configuration typée + details: Déclarez un schéma de configuration et obtenez une UI interactive. + - icon: 🇫🇷 + title: Documentation française + details: Documentation complète en français pour créer des modules. + link: /fr/guide/getting-started + - icon: 🇬🇧 + title: English documentation + details: Full English documentation for module development. + link: /en/guide/getting-started +--- diff --git a/docs/site/package.json b/docs/site/package.json new file mode 100644 index 0000000..c909a20 --- /dev/null +++ b/docs/site/package.json @@ -0,0 +1,15 @@ +{ + "name": "omnibot-docs", + "version": "1.0.0", + "private": true, + "description": "OmniBot documentation", + "type": "module", + "scripts": { + "dev": "vitepress dev", + "build": "vitepress build", + "preview": "vitepress preview" + }, + "dependencies": { + "vitepress": "^1.6.4" + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c93c6c7..f9a0470 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -56,16 +56,115 @@ importers: specifier: ^4.1.8 version: 4.1.8(@types/node@24.13.2)(vite@8.0.16(@types/node@24.13.2)(esbuild@0.28.1)(jiti@2.6.1)(tsx@4.22.4)(yaml@2.9.0)) + docs/site: + dependencies: + vitepress: + specifier: ^1.6.4 + version: 1.6.4(@algolia/client-search@5.54.0)(@types/node@24.13.2)(lightningcss@1.32.0)(postcss@8.5.15)(search-insights@2.17.3)(typescript@5.9.3) + packages: + '@algolia/abtesting@1.20.0': + resolution: {integrity: sha512-5CqkS592H3+24b6H6CQ2RVpphdmAuIElZzv0Hngqo/ZEZpUZJ+KGLcueBhx33fv2wYBXyuuvskG5aQ7Ti+lR0g==} + engines: {node: '>= 14.0.0'} + + '@algolia/autocomplete-core@1.17.7': + resolution: {integrity: sha512-BjiPOW6ks90UKl7TwMv7oNQMnzU+t/wk9mgIDi6b1tXpUek7MW0lbNOUHpvam9pe3lVCf4xPFT+lK7s+e+fs7Q==} + + '@algolia/autocomplete-plugin-algolia-insights@1.17.7': + resolution: {integrity: sha512-Jca5Ude6yUOuyzjnz57og7Et3aXjbwCSDf/8onLHSQgw1qW3ALl9mrMWaXb5FmPVkV3EtkD2F/+NkT6VHyPu9A==} + peerDependencies: + search-insights: '>= 1 < 3' + + '@algolia/autocomplete-preset-algolia@1.17.7': + resolution: {integrity: sha512-ggOQ950+nwbWROq2MOCIL71RE0DdQZsceqrg32UqnhDz8FlO9rL8ONHNsI2R1MH0tkgVIDKI/D0sMiUchsFdWA==} + peerDependencies: + '@algolia/client-search': '>= 4.9.1 < 6' + algoliasearch: '>= 4.9.1 < 6' + + '@algolia/autocomplete-shared@1.17.7': + resolution: {integrity: sha512-o/1Vurr42U/qskRSuhBH+VKxMvkkUVTLU6WZQr+L5lGZZLYWyhdzWjW0iGXY7EkwRTjBqvN2EsR81yCTGV/kmg==} + peerDependencies: + '@algolia/client-search': '>= 4.9.1 < 6' + algoliasearch: '>= 4.9.1 < 6' + + '@algolia/client-abtesting@5.54.0': + resolution: {integrity: sha512-IXnH1x3DBsPQA4/FRO0Pvkfy79tKy5Qr+ugAV9jdcGkpzHc476D0WV1MFJ+pZxtbts0xh2JqzUVmYEqA6LkOpA==} + engines: {node: '>= 14.0.0'} + + '@algolia/client-analytics@5.54.0': + resolution: {integrity: sha512-pBpRqm1wpE0GnGy2rNLk9rjDn0Le4iywNRtnAWblLeqfjxpWKg8lWnk7nmSoTShFO31sz2jatXXzxK2lz8ipbA==} + engines: {node: '>= 14.0.0'} + + '@algolia/client-common@5.54.0': + resolution: {integrity: sha512-WbuwRUlFvSOsuxqTDjSSmgusuF5KFt+oFPzobvPDvodra6EWnVwUXjz0elkNSsnsIlZGtcXlX3LhxkO7rF90jw==} + engines: {node: '>= 14.0.0'} + + '@algolia/client-insights@5.54.0': + resolution: {integrity: sha512-/HNLVi3kPI+JhO59WbglLjPM2c4uECU+x4gk1iADseKtE5eYqJ/RJ+FIwM8xzPFKhFJaw+8hVq4lkd0nn8HDDg==} + engines: {node: '>= 14.0.0'} + + '@algolia/client-personalization@5.54.0': + resolution: {integrity: sha512-6TolyyDRumIKeLGBGSFAZsSIJ8hrm6NFCGR1jO7pQTiOtSWgAIxcFE5/JRpZ3g+unG4OkNOFy+I0dUEquIDbig==} + engines: {node: '>= 14.0.0'} + + '@algolia/client-query-suggestions@5.54.0': + resolution: {integrity: sha512-AxW2MLBhjBtGX5kIZrVLO2SP2vRkZxJw5qHGxnQJfLcYhA04mFNP0fWRtHshlcVnDk9M8L9lwfr2lGpf3Er+hw==} + engines: {node: '>= 14.0.0'} + + '@algolia/client-search@5.54.0': + resolution: {integrity: sha512-ngdgVGp05lJzUyA+sUzr0MDZ7AMtANcJpwIzq4ZsfpZL5B3S7A4XYfMcU2sECZc3bx3ysOhYcdbbaTjc3ve0WQ==} + engines: {node: '>= 14.0.0'} + + '@algolia/ingestion@1.54.0': + resolution: {integrity: sha512-hee59Z7FgZ6/13FYL05ANPwRJY1pfvIlrwC8eBZYdiRFXTJVvf94IyTWOxLqRVpbseDP6eQQTW+PT7/DxTZIng==} + engines: {node: '>= 14.0.0'} + + '@algolia/monitoring@1.54.0': + resolution: {integrity: sha512-diCjZVbIO7Pzw98tEKqLWIAgmQBI3Zt1sHsXyAPNGZgn32derpIXTnjjpJbZl+uAhSSznd6SfxFGdC9uYdN1TA==} + engines: {node: '>= 14.0.0'} + + '@algolia/recommend@5.54.0': + resolution: {integrity: sha512-6zeslypRAGWDgVJEJYAPuqmyquHvnw4MQwG+XXdrw5dTNDjXYIcCJdQQcCY06xPF9tUvmzy/1vHiH9QxhgwuOQ==} + engines: {node: '>= 14.0.0'} + + '@algolia/requester-browser-xhr@5.54.0': + resolution: {integrity: sha512-iHZax214LPXd7XizQ4BNnTsegl8f3IeKm8JcrmSNZ/5x1rZ5xLkbG/anltAWtLpoRNnfpr4Z80YdYeVVPpx6wQ==} + engines: {node: '>= 14.0.0'} + + '@algolia/requester-fetch@5.54.0': + resolution: {integrity: sha512-YKtuG5YwPxZ+kkfd4HmUO7Z9aICPUSMlHslzKTmtMMhxGnetxEqGj9T/v2r/PdcuOUC5oW0CHe8akJk8cpS3gQ==} + engines: {node: '>= 14.0.0'} + + '@algolia/requester-node-http@5.54.0': + resolution: {integrity: sha512-zyZDJ4WS5TnjZZ5pqywTBFO9olW7QMtY2kf2dbLnu+UTzfc9ri/HGf27jRN2NTbX9FcRPxSqPqzUhF/BZIx0VA==} + engines: {node: '>= 14.0.0'} + '@babel/code-frame@7.29.0': resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} engines: {node: '>=6.9.0'} + '@babel/helper-string-parser@7.29.7': + resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} + engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@7.28.5': resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.29.7': + resolution: {integrity: sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/types@7.29.7': + resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==} + engines: {node: '>=6.9.0'} + '@commitlint/cli@21.0.2': resolution: {integrity: sha512-YMmfLbqBg+ZRvvmPhc+cilSQFrh/AgzVgCT1U/OifmUZEwPbvCtA8rN//YNaF9d5eoZphxVMGYtmwA2QgQORgg==} engines: {node: '>=22.12.0'} @@ -175,6 +274,29 @@ packages: resolution: {integrity: sha512-wPlQDxEmlDg5IxhJPuxXr3Vy9AjYq5xCvFWGJyD7w7Np8ZGu+Mc+97LCoEc/+AYCo2IDpKioiH0/c/mj5ZR9Uw==} engines: {node: '>=16.11.0'} + '@docsearch/css@3.8.2': + resolution: {integrity: sha512-y05ayQFyUmCXze79+56v/4HpycYF3uFqB78pLPrSV5ZKAlDuIAAJNhaRi8tTdRNXh05yxX/TyNnzD6LwSM89vQ==} + + '@docsearch/js@3.8.2': + resolution: {integrity: sha512-Q5wY66qHn0SwA7Taa0aDbHiJvaFJLOJyHmooQ7y8hlwwQLQ/5WwCcoX0g7ii04Qi2DJlHsd0XXzJ8Ypw9+9YmQ==} + + '@docsearch/react@3.8.2': + resolution: {integrity: sha512-xCRrJQlTt8N9GU0DG4ptwHRkfnSnD/YpdeaXe02iKfqs97TkZJv60yE+1eq/tjPcVnTW8dP5qLP7itifFVV5eg==} + peerDependencies: + '@types/react': '>= 16.8.0 < 19.0.0' + react: '>= 16.8.0 < 19.0.0' + react-dom: '>= 16.8.0 < 19.0.0' + search-insights: '>= 1 < 3' + peerDependenciesMeta: + '@types/react': + optional: true + react: + optional: true + react-dom: + optional: true + search-insights: + optional: true + '@emnapi/core@1.10.0': resolution: {integrity: sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==} @@ -184,102 +306,204 @@ packages: '@emnapi/wasi-threads@1.2.1': resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} + '@esbuild/aix-ppc64@0.21.5': + resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [aix] + '@esbuild/aix-ppc64@0.28.1': resolution: {integrity: sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] + '@esbuild/android-arm64@0.21.5': + resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + '@esbuild/android-arm64@0.28.1': resolution: {integrity: sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==} engines: {node: '>=18'} cpu: [arm64] os: [android] + '@esbuild/android-arm@0.21.5': + resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + '@esbuild/android-arm@0.28.1': resolution: {integrity: sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==} engines: {node: '>=18'} cpu: [arm] os: [android] + '@esbuild/android-x64@0.21.5': + resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + '@esbuild/android-x64@0.28.1': resolution: {integrity: sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==} engines: {node: '>=18'} cpu: [x64] os: [android] + '@esbuild/darwin-arm64@0.21.5': + resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + '@esbuild/darwin-arm64@0.28.1': resolution: {integrity: sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] + '@esbuild/darwin-x64@0.21.5': + resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + '@esbuild/darwin-x64@0.28.1': resolution: {integrity: sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==} engines: {node: '>=18'} cpu: [x64] os: [darwin] + '@esbuild/freebsd-arm64@0.21.5': + resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + '@esbuild/freebsd-arm64@0.28.1': resolution: {integrity: sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] + '@esbuild/freebsd-x64@0.21.5': + resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + '@esbuild/freebsd-x64@0.28.1': resolution: {integrity: sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] + '@esbuild/linux-arm64@0.21.5': + resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + '@esbuild/linux-arm64@0.28.1': resolution: {integrity: sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==} engines: {node: '>=18'} cpu: [arm64] os: [linux] + '@esbuild/linux-arm@0.21.5': + resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + '@esbuild/linux-arm@0.28.1': resolution: {integrity: sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==} engines: {node: '>=18'} cpu: [arm] os: [linux] + '@esbuild/linux-ia32@0.21.5': + resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + '@esbuild/linux-ia32@0.28.1': resolution: {integrity: sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==} engines: {node: '>=18'} cpu: [ia32] os: [linux] + '@esbuild/linux-loong64@0.21.5': + resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + '@esbuild/linux-loong64@0.28.1': resolution: {integrity: sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==} engines: {node: '>=18'} cpu: [loong64] os: [linux] + '@esbuild/linux-mips64el@0.21.5': + resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + '@esbuild/linux-mips64el@0.28.1': resolution: {integrity: sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] + '@esbuild/linux-ppc64@0.21.5': + resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + '@esbuild/linux-ppc64@0.28.1': resolution: {integrity: sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] + '@esbuild/linux-riscv64@0.21.5': + resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + '@esbuild/linux-riscv64@0.28.1': resolution: {integrity: sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] + '@esbuild/linux-s390x@0.21.5': + resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + '@esbuild/linux-s390x@0.28.1': resolution: {integrity: sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==} engines: {node: '>=18'} cpu: [s390x] os: [linux] + '@esbuild/linux-x64@0.21.5': + resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + '@esbuild/linux-x64@0.28.1': resolution: {integrity: sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==} engines: {node: '>=18'} @@ -292,6 +516,12 @@ packages: cpu: [arm64] os: [netbsd] + '@esbuild/netbsd-x64@0.21.5': + resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + '@esbuild/netbsd-x64@0.28.1': resolution: {integrity: sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==} engines: {node: '>=18'} @@ -304,6 +534,12 @@ packages: cpu: [arm64] os: [openbsd] + '@esbuild/openbsd-x64@0.21.5': + resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + '@esbuild/openbsd-x64@0.28.1': resolution: {integrity: sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==} engines: {node: '>=18'} @@ -316,30 +552,60 @@ packages: cpu: [arm64] os: [openharmony] + '@esbuild/sunos-x64@0.21.5': + resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + '@esbuild/sunos-x64@0.28.1': resolution: {integrity: sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==} engines: {node: '>=18'} cpu: [x64] os: [sunos] + '@esbuild/win32-arm64@0.21.5': + resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + '@esbuild/win32-arm64@0.28.1': resolution: {integrity: sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==} engines: {node: '>=18'} cpu: [arm64] os: [win32] + '@esbuild/win32-ia32@0.21.5': + resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + '@esbuild/win32-ia32@0.28.1': resolution: {integrity: sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==} engines: {node: '>=18'} cpu: [ia32] os: [win32] + '@esbuild/win32-x64@0.21.5': + resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + '@esbuild/win32-x64@0.28.1': resolution: {integrity: sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==} engines: {node: '>=18'} cpu: [x64] os: [win32] + '@iconify-json/simple-icons@1.2.86': + resolution: {integrity: sha512-t3jck5qPQuK1qy+bRn9eCoDQhIB7XSazKz1Fjp8hcan3XOAsTI5Mq/s3F0ekOKSvMQqkVORYK6ns6o6T9f5EMA==} + + '@iconify/types@2.0.0': + resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} + '@jridgewell/sourcemap-codec@1.5.5': resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} @@ -727,6 +993,144 @@ packages: '@rolldown/pluginutils@1.0.1': resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} + '@rollup/rollup-android-arm-eabi@4.62.0': + resolution: {integrity: sha512-IPIQ55ythEHkfEd9jMEi32OQ7SxURsGA43JI22lj01OLZNt2NUbJX8YUHxkVWyQ6daHPNn0truF5nSj3DQp6YQ==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.62.0': + resolution: {integrity: sha512-M6s9cr10MibETyo8JsOkq+Lo1+lU6hcvb1MApnUql5qte/5hMEgzlN8/ReIKNfRV8rrqX50W1BX9zoUhC192RA==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.62.0': + resolution: {integrity: sha512-BqCoMoIbn0keKys+dEAdBa70EtOwV1bEsQCUgU9FdiZmmMge/Zk7LlkYGqbrdHR+Frnt0E1FOanly+rlwvvQzw==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.62.0': + resolution: {integrity: sha512-SIMzST3VFNXDAbeIWDWiFCNM5qncUBDWaEV7NfE7oZbDt2mgfW4MvbKdbYiGOLoM32gbTv608UMd0XktEYSD7w==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.62.0': + resolution: {integrity: sha512-ezjfSQMP7ArdUsbBwbQIfwAlhE84I2iVnzQNCFSveqV42q+BmKlzVpf7mxv5EchLcoWU4y6/heFzVg1F+hodUQ==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.62.0': + resolution: {integrity: sha512-9+qTWGW9AZRhnUgwtTwzNwcPlL87ngkeN0LA+q1bADvmY9aNvWaF2TFW8BZgnQPYxpDI7+rMVLivcd4V737TAQ==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.62.0': + resolution: {integrity: sha512-T1dMEQhXA/jkJ/jyMIw9IovK8bSUq7A8kLIlvZTb/6YIVsp2zLavr4F3oyllHWo7eIVJRyE5n3tUjQJEbE1IuQ==} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm-musleabihf@4.62.0': + resolution: {integrity: sha512-2as0LgT7qQpyceQq6VUJYnumUMUrgGQCWIiDIN9DE0/tglsk6o66uCB4f3djRawAltvfCNLyZZrsqbPA6inCsA==} + cpu: [arm] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-arm64-gnu@4.62.0': + resolution: {integrity: sha512-bVURMg+6eNN9C/yc0aVjooZcwTTtYF4YW3xta5pP0//r3o1V8gXEHXWCndj47w/HhwsFroZrFhR+6uQP5T0n0g==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm64-musl@4.62.0': + resolution: {integrity: sha512-Ful8pM/2yYI83PViWdFdpZhdI8HJ5qsXANe5atypbHDf+KIBBDsZsbyy8hbXnULVvW9NsTh5DHwbcBftyLTfiw==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-loong64-gnu@4.62.0': + resolution: {integrity: sha512-9Gp/DgrkzfUBmNPVTyPTvay+4xEP7M/clXpj3efXBcm6uTIVIgDg4rqUpqKXvLEuFRVuEpSAOkhgNeecvaZ4Cg==} + cpu: [loong64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-loong64-musl@4.62.0': + resolution: {integrity: sha512-m9tsJz54LUXkSYM8+8PG81B9IKK5r+2T0clMq4QrS16xFosufU7firBDAZEsDheDs7wTlP7h3++S7lMsU955HA==} + cpu: [loong64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-ppc64-gnu@4.62.0': + resolution: {integrity: sha512-3UvJ5PNVU16aJf6M3tFI24pWzAl2/ynfbyRN3ICyQajK1lSkrnVYNnLz3v04J32qKa0FczJc22zeToc0lr2A3w==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-ppc64-musl@4.62.0': + resolution: {integrity: sha512-vRWUAbYLGHBZS6Q8Msb2sfnf1fvJf+47t8l/TwOerM2qArzy+IeNMTHrYLHXh95h8MoatPHI5hhSZNs+mGXKPg==} + cpu: [ppc64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-riscv64-gnu@4.62.0': + resolution: {integrity: sha512-c00T5SYENHAt86cfW47URaP3Us5vLC/4QO7GYud1G5VNRffCwwCuBspwqYrriuJB+5m0WFzClCn9wed0FBjKvg==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-riscv64-musl@4.62.0': + resolution: {integrity: sha512-krrCDilhXOwFkSkO3Wm9I/f9H0L92XHHwy2fwxjukxIbh0dem8gZqOW5Y8BsHrpJv5qwlRBV+Wl4ZFyRWhUpwg==} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-s390x-gnu@4.62.0': + resolution: {integrity: sha512-7pfYFSTc4/rUC/FtAI0Qp6QthDBCIi6/AuP1xYqFk5vanI6KnL5dWKP60OM/05LOsbwTmIcvr6eXC4CJuJ75IA==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-gnu@4.62.0': + resolution: {integrity: sha512-7SDIalKeIpG0Ifogbbdn58HmSotYMlf23K3dCJEmiVd9Fg36Vmni82iPQec27N3wY4Bvbxftkxz6vSx9OcouTg==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-musl@4.62.0': + resolution: {integrity: sha512-eRZevouTH2i1HeAVLqJuLnt256krQkGY0TN6WsTmsIhuzbh457HuWDMakKwmi0Cjadux983CoSr8Lim2QhUIFw==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rollup/rollup-openbsd-x64@4.62.0': + resolution: {integrity: sha512-3oVS7FLGa4U1qcvao9ylGxrjXZyUQqR8UwxEcnUEyPX53O/C/mKDZegNXTdHCP+h3e6ta/f1EN38Yif1mmZHYg==} + cpu: [x64] + os: [openbsd] + + '@rollup/rollup-openharmony-arm64@4.62.0': + resolution: {integrity: sha512-yTB9TgfWj5wHe5QgktAgXTLLot1gvEjl1NiPPAUiCs4oPrIWFl5V4nC3GrkNdj9LaAU4s94nVrGbGOCqUpyWsg==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.62.0': + resolution: {integrity: sha512-5LOhoaesY3doG1c+ac/2JtgREpKoJr5bUHH8tKY0V8di7+uSV6BwLs2PlR0/yzefGOkR+wE7ZolZphHCsyG5Rw==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.62.0': + resolution: {integrity: sha512-yYkWHhmbhRTWTnWos5HC4GcPQfjlzzCNbM9e/+GXrLuaBXYA3qSDR9f0Vgufd5S8yX81U8jPKp7ZnAjZFMtRnw==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.62.0': + resolution: {integrity: sha512-SoTb6lPg25xZlA2ibwQ++ahCCnH+FP0qmEuafMJ4gznZKOlXioKEAeJLgCrqjM98ACziXM9V1amFjICVL4IFoA==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.62.0': + resolution: {integrity: sha512-5L+T1fMX4RIEBoZzT0+sQ0PhTS36NULFmMXtl1TZo44TMAROIMHbZufSOjVWt/Y622BtxgxtaNOokbTDvfsrZA==} + cpu: [x64] + os: [win32] + '@sapphire/async-queue@1.5.5': resolution: {integrity: sha512-cvGzxbba6sav2zZkH8GPf2oGk9yYoD5qrNWdu9fRehifgnFZJMV+nuy2nON2roRO4yQQ+v7MK/Pktl/HgfsUXg==} engines: {node: '>=v14.0.0', npm: '>=7.0.0'} @@ -743,6 +1147,30 @@ packages: resolution: {integrity: sha512-xzvBr1Q1c4lCe7i6sRnrofxeO1QTP/LKQ6A6qy0iB4x5yfiSfARMEQEghojzTNALDTcv8En04qYNIco9/K9eZQ==} engines: {node: '>=v14.0.0', npm: '>=7.0.0'} + '@shikijs/core@2.5.0': + resolution: {integrity: sha512-uu/8RExTKtavlpH7XqnVYBrfBkUc20ngXiX9NSrBhOVZYv/7XQRKUyhtkeflY5QsxC0GbJThCerruZfsUaSldg==} + + '@shikijs/engine-javascript@2.5.0': + resolution: {integrity: sha512-VjnOpnQf8WuCEZtNUdjjwGUbtAVKuZkVQ/5cHy/tojVVRIRtlWMYVjyWhxOmIq05AlSOv72z7hRNRGVBgQOl0w==} + + '@shikijs/engine-oniguruma@2.5.0': + resolution: {integrity: sha512-pGd1wRATzbo/uatrCIILlAdFVKdxImWJGQ5rFiB5VZi2ve5xj3Ax9jny8QvkaV93btQEwR/rSz5ERFpC5mKNIw==} + + '@shikijs/langs@2.5.0': + resolution: {integrity: sha512-Qfrrt5OsNH5R+5tJ/3uYBBZv3SuGmnRPejV9IlIbFH3HTGLDlkqgHymAlzklVmKBjAaVmkPkyikAV/sQ1wSL+w==} + + '@shikijs/themes@2.5.0': + resolution: {integrity: sha512-wGrk+R8tJnO0VMzmUExHR+QdSaPUl/NKs+a4cQQRWyoc3YFbUzuLEi/KWK1hj+8BfHRKm2jNhhJck1dfstJpiw==} + + '@shikijs/transformers@2.5.0': + resolution: {integrity: sha512-SI494W5X60CaUwgi8u4q4m4s3YAFSxln3tzNjOSYqq54wlVgz0/NbbXEb3mdLbqMBztcmS7bVTaEd2w0qMmfeg==} + + '@shikijs/types@2.5.0': + resolution: {integrity: sha512-ygl5yhxki9ZLNuNpPitBWvcy9fsSKKaRuO4BAlMyagszQidxcpLAr0qiW/q43DtSIDxO6hEbtYLiFZNXO/hdGw==} + + '@shikijs/vscode-textmate@10.0.2': + resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} + '@simple-libs/child-process-utils@1.0.2': resolution: {integrity: sha512-/4R8QKnd/8agJynkNdJmNw2MBxuFTRcNFnE5Sg/G+jkSsV8/UBgULMzhizWWW42p8L5H7flImV2ATi79Ove2Tw==} engines: {node: '>=18'} @@ -766,12 +1194,43 @@ packages: '@types/estree@1.0.9': resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + '@types/hast@3.0.4': + resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} + + '@types/linkify-it@5.0.0': + resolution: {integrity: sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==} + + '@types/markdown-it@14.1.2': + resolution: {integrity: sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==} + + '@types/mdast@4.0.4': + resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} + + '@types/mdurl@2.0.0': + resolution: {integrity: sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==} + '@types/node@24.13.2': resolution: {integrity: sha512-fRa09kZTgu8o71KFcDjUFuc7F+dEbZYZmkI0mg5YBTRs0yMKjYHsq/c0urDKeDb+D5qVgXOdFcuu+DZPKOITwA==} + '@types/unist@3.0.3': + resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + + '@types/web-bluetooth@0.0.21': + resolution: {integrity: sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==} + '@types/ws@8.18.1': resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} + '@ungap/structured-clone@1.3.1': + resolution: {integrity: sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ==} + + '@vitejs/plugin-vue@5.2.4': + resolution: {integrity: sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==} + engines: {node: ^18.0.0 || >=20.0.0} + peerDependencies: + vite: ^5.0.0 || ^6.0.0 + vue: ^3.2.25 + '@vitest/expect@4.1.8': resolution: {integrity: sha512-h3nDO677RDLEGlBxyQ5CW8RlMThSKSRLUePLOx09gNIWRL40edgA1GCZSZgf1W55MFAG6/Sw14KeaAnqv0NKdQ==} @@ -805,9 +1264,101 @@ packages: resolution: {integrity: sha512-Xfe6rpCTxSxfbswi/W/Pz7zp1WWSNn4A0eW4mLkQUewCrXXtMj31lCg+iQyTkh/CkusZSq9eDflu7tjEDXUY6g==} engines: {node: '>=v14.0.0', npm: '>=7.0.0'} + '@vue/compiler-core@3.5.38': + resolution: {integrity: sha512-s99aGxWYig9ErHbct27KXEGhrBYlRI6c4MwAgXErOAbX9xiW37/uMa+XUDO69zLz83dng8UUZ70CTOJrLrYrEQ==} + + '@vue/compiler-dom@3.5.38': + resolution: {integrity: sha512-JTqp25l8aFfJYF7/KmsXZjAxJz7T+SjmTJLoXVjHtc2BrSgSiW2n9Aem/cWq1OPe68A8JL06B3eVdhlP0H4TVw==} + + '@vue/compiler-sfc@3.5.38': + resolution: {integrity: sha512-DuA2GiZawSEW442iw/9+Fkol8hTgb4Ke5KkhmSry65QA7YuyMbIdy8p0XZRMvNwJdgRz307W8g1CSzdvS4nuNg==} + + '@vue/compiler-ssr@3.5.38': + resolution: {integrity: sha512-7s+W5Gc42FGxZMcuwl8H5B29T8BJPMdBT7KHFE+BbAuZ/iTEdTtv7z2XiMjiaUUw4w3ZcCEdHs36RuYJ2VA7bA==} + + '@vue/devtools-api@7.7.9': + resolution: {integrity: sha512-kIE8wvwlcZ6TJTbNeU2HQNtaxLx3a84aotTITUuL/4bzfPxzajGBOoqjMhwZJ8L9qFYDU/lAYMEEm11dnZOD6g==} + + '@vue/devtools-kit@7.7.9': + resolution: {integrity: sha512-PyQ6odHSgiDVd4hnTP+aDk2X4gl2HmLDfiyEnn3/oV+ckFDuswRs4IbBT7vacMuGdwY/XemxBoh302ctbsptuA==} + + '@vue/devtools-shared@7.7.9': + resolution: {integrity: sha512-iWAb0v2WYf0QWmxCGy0seZNDPdO3Sp5+u78ORnyeonS6MT4PC7VPrryX2BpMJrwlDeaZ6BD4vP4XKjK0SZqaeA==} + + '@vue/reactivity@3.5.38': + resolution: {integrity: sha512-pG6LV/NDNRbKizcUjFFLAfjaL8mcv4DmR9avNcUw2gDHBzZneuS2TWCmp633ynzxz9YYKNeEPK2I8Wraqy2HUQ==} + + '@vue/runtime-core@3.5.38': + resolution: {integrity: sha512-iyW8WVfF1CpCXxncZY5Ei6rSd6oZr5DgEom//fUjRBRl56AXPD+s9ATvukRt77ZFTuYlnVA1bxY+dJB94tWVYw==} + + '@vue/runtime-dom@3.5.38': + resolution: {integrity: sha512-apX2wt9sdfDshS+a2xueFZLVpt0GkRJZSoPmrW/SA4yzXTznhfcMVW59gr7h4YQeY0vJhdJkk2rsIDwgfFgC5A==} + + '@vue/server-renderer@3.5.38': + resolution: {integrity: sha512-vue8vbf2QlV4quHqzwmJy6dWfmRhP1J8l4wtZg60CL6VoKqcPY2oe7may3+1d9qfpedjK5PRLFqd5k3Isj9mUw==} + peerDependencies: + vue: 3.5.38 + + '@vue/shared@3.5.38': + resolution: {integrity: sha512-FTW0AFZNaK5/mOqvGBwVfUlNLU38TiQn4+DQgIFUnrBBJQ1crMJ82yeGQLV5jyKFsO8yRukpbuP7x+nRbH6aug==} + + '@vueuse/core@12.8.2': + resolution: {integrity: sha512-HbvCmZdzAu3VGi/pWYm5Ut+Kd9mn1ZHnn4L5G8kOQTPs/IwIAmJoBrmYk2ckLArgMXZj0AW3n5CAejLUO+PhdQ==} + + '@vueuse/integrations@12.8.2': + resolution: {integrity: sha512-fbGYivgK5uBTRt7p5F3zy6VrETlV9RtZjBqd1/HxGdjdckBgBM4ugP8LHpjolqTj14TXTxSK1ZfgPbHYyGuH7g==} + peerDependencies: + async-validator: ^4 + axios: ^1 + change-case: ^5 + drauu: ^0.4 + focus-trap: ^7 + fuse.js: ^7 + idb-keyval: ^6 + jwt-decode: ^4 + nprogress: ^0.2 + qrcode: ^1.5 + sortablejs: ^1 + universal-cookie: ^7 + peerDependenciesMeta: + async-validator: + optional: true + axios: + optional: true + change-case: + optional: true + drauu: + optional: true + focus-trap: + optional: true + fuse.js: + optional: true + idb-keyval: + optional: true + jwt-decode: + optional: true + nprogress: + optional: true + qrcode: + optional: true + sortablejs: + optional: true + universal-cookie: + optional: true + + '@vueuse/metadata@12.8.2': + resolution: {integrity: sha512-rAyLGEuoBJ/Il5AmFHiziCPdQzRt88VxR+Y/A/QhJ1EWtWqPBBAxTAFaSkviwEuOEZNtW8pvkPgoCZQ+HxqW1A==} + + '@vueuse/shared@12.8.2': + resolution: {integrity: sha512-dznP38YzxZoNloI0qpEfpkms8knDtaoQ6Y/sfS0L7Yki4zh40LFHEhur0odJC6xTHG5dxWVPiUWBXn+wCG2s5w==} + ajv@8.20.0: resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==} + algoliasearch@5.54.0: + resolution: {integrity: sha512-APAX4ajIOgsmYoUlGe++oNZkSTBgmXYM4maHC0OxC+Yo7xkaKQElV0ATZYCZA7jzrSJX1OBiqEs7mk+ZxXgYqA==} + engines: {node: '>= 14.0.0'} + ansi-regex@6.2.2: resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} engines: {node: '>=12'} @@ -830,6 +1381,9 @@ packages: resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} engines: {node: '>=8.0.0'} + birpc@2.9.0: + resolution: {integrity: sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==} + c12@3.1.0: resolution: {integrity: sha512-uWoS8OU1MEIsOv8p/5a82c3H31LsWVR5qiyXVfBNOzfffjUWtPnhAb4BYI2uG2HfGmZmFjCtui5XNWaps+iFuw==} peerDependencies: @@ -842,10 +1396,19 @@ packages: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} + ccount@2.0.1: + resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + chai@6.2.2: resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} engines: {node: '>=18'} + 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==} + chokidar@4.0.3: resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} engines: {node: '>= 14.16.0'} @@ -863,6 +1426,9 @@ packages: colorette@2.0.20: resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} + comma-separated-tokens@2.0.3: + resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + compare-func@2.0.0: resolution: {integrity: sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==} @@ -889,6 +1455,10 @@ packages: convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + copy-anything@4.0.5: + resolution: {integrity: sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA==} + engines: {node: '>=18'} + cosmiconfig-typescript-loader@6.3.0: resolution: {integrity: sha512-Akr82WH1Wfqatyiqpj8HDkO2o2KmJRu1FhKfSNJP3K4IdXwHfEyL7MOb62i1AGQVLtIQM+iCE9CGOtrfhR+mmA==} engines: {node: '>=v18'} @@ -906,6 +1476,9 @@ packages: typescript: optional: true + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + dateformat@4.6.3: resolution: {integrity: sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==} @@ -916,6 +1489,10 @@ packages: defu@6.1.7: resolution: {integrity: sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==} + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + destr@2.0.5: resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==} @@ -923,6 +1500,9 @@ packages: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} + devlop@1.1.0: + resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + discord-api-types@0.38.47: resolution: {integrity: sha512-XgXQodHQBAE6kfD7kMvVo30863iHX1LHSqNq6MGUTDwIFCCvHva13+rwxyxVXDqudyApMNAd32PGjgVETi5rjA==} @@ -941,6 +1521,9 @@ packages: effect@3.21.0: resolution: {integrity: sha512-PPN80qRokCd1f015IANNhrwOnLO7GrrMQfk4/lnZRE/8j7UPWrNNjPV0uBrZutI/nHzernbW+J0hdqQysHiSnQ==} + emoji-regex-xs@1.0.0: + resolution: {integrity: sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg==} + emoji-regex@10.6.0: resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} @@ -951,6 +1534,10 @@ packages: end-of-stream@1.4.5: resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} + entities@7.0.1: + resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} + engines: {node: '>=0.12'} + env-paths@2.2.1: resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} engines: {node: '>=6'} @@ -964,6 +1551,11 @@ packages: es-toolkit@1.46.1: resolution: {integrity: sha512-5eNtXOs3tbfxXOj04tjjseeWkRWaoCjdEI+96DgwzZoe6c9juL49pXlzAFTI72aWC9Y8p7168g6XIKjh7k6pyQ==} + esbuild@0.21.5: + resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} + engines: {node: '>=12'} + hasBin: true + esbuild@0.28.1: resolution: {integrity: sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==} engines: {node: '>=18'} @@ -973,6 +1565,9 @@ packages: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + estree-walker@3.0.3: resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} @@ -1008,6 +1603,9 @@ packages: picomatch: optional: true + focus-trap@7.8.0: + resolution: {integrity: sha512-/yNdlIkpWbM0ptxno3ONTuf+2g318kh2ez3KSeZN5dZ8YC6AAmgeWz+GasYYiBJPFaYcSAPeu4GfhUaChzIJXA==} + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -1034,9 +1632,21 @@ packages: resolution: {integrity: sha512-1pgFdhK3J2LeM+dVf2Pd424yHx2ou338lC0ErNP2hPx4j8eW1Sp0XqSjNxtk6Tc4Kr5wlWtSvz8cn2yb7/SG/w==} engines: {node: '>=20'} + hast-util-to-html@9.0.5: + resolution: {integrity: sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==} + + hast-util-whitespace@3.0.0: + resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} + help-me@5.0.0: resolution: {integrity: sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==} + hookable@5.5.3: + resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==} + + html-void-elements@3.0.0: + resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} + import-fresh@3.3.1: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} engines: {node: '>=6'} @@ -1056,6 +1666,10 @@ packages: resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} engines: {node: '>=12'} + is-what@5.5.0: + resolution: {integrity: sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw==} + engines: {node: '>=18'} + jiti@2.6.1: resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} hasBin: true @@ -1220,13 +1834,40 @@ packages: magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + mark.js@8.11.1: + resolution: {integrity: sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==} + + mdast-util-to-hast@13.2.1: + resolution: {integrity: sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==} + meow@13.2.0: resolution: {integrity: sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==} engines: {node: '>=18'} + micromark-util-character@2.1.1: + resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} + + micromark-util-encode@2.0.1: + resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} + + micromark-util-sanitize-uri@2.0.1: + resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} + + micromark-util-symbol@2.0.1: + resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} + + micromark-util-types@2.0.2: + resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==} + minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + minisearch@7.2.0: + resolution: {integrity: sha512-dqT2XBYUOZOiC5t2HRnwADjhNS2cecp9u+TJRiJ1Qp/f5qjkeT5APcGPjHw+bz89Ms8Jp+cG4AlE+QZ/QnDglg==} + + mitt@3.0.1: + resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==} + nanoid@3.3.12: resolution: {integrity: sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -1254,6 +1895,9 @@ packages: once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + oniguruma-to-es@3.1.1: + resolution: {integrity: sha512-bUH8SDvPkH3ho3dvwJwfonjlQ4R80vjyvrU8YpxuROddv55vAEJrTuCuCVUhhsHbtlD9tGGbaNApGQckXhS8iQ==} + oxfmt@0.54.0: resolution: {integrity: sha512-DjnMwn7smSLF+Mc2+pRItnuPftm/dkUFpY/d4+33y9TfKrsHZo8GLhmUg9BrOIUEy94Rlom1Q11N6vuhE+e0oQ==} engines: {node: ^20.19.0 || >=22.12.0} @@ -1322,6 +1966,9 @@ packages: resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==} engines: {node: ^10 || ^12 || >=14} + preact@10.29.2: + resolution: {integrity: sha512-7tNmwg/7mzzAoB/8kSg6Hl37JraAZw3Z3A0JSY7VXlZwo82Xn0G7wKbNNs2qoF4ZEEsQGTwDAroNdqKs1ofJxQ==} + prisma@6.19.3: resolution: {integrity: sha512-++ZJ0ijLrDJF6hNB4t4uxg2br3fC4H9Yc9tcbjr2fcNFP3rh/SBNrAgjhsqBU4Ght8JPrVofG/ZkXfnSfnYsFg==} engines: {node: '>=18.18'} @@ -1335,6 +1982,9 @@ packages: process-warning@5.0.0: resolution: {integrity: sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==} + property-information@7.2.0: + resolution: {integrity: sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==} + pump@3.0.4: resolution: {integrity: sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==} @@ -1358,6 +2008,15 @@ packages: real-require@1.0.0: resolution: {integrity: sha512-P4nbQYQfePJxRSmY+v/KINxVucm4NF3p3s7pJveMTtom52FR4YGltUQLB8idDXwDDWW+eYrWDFbuzUnjoWHF7g==} + regex-recursion@6.0.2: + resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==} + + regex-utilities@2.3.0: + resolution: {integrity: sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==} + + regex@6.1.0: + resolution: {integrity: sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==} + require-from-string@2.0.2: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} @@ -1370,15 +2029,26 @@ packages: resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} engines: {node: '>=8'} + rfdc@1.4.1: + resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} + rolldown@1.0.3: resolution: {integrity: sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true + rollup@4.62.0: + resolution: {integrity: sha512-nc72Wgq62I7rtDV4izT5/aaS0zxy3kttkinf9586ApknY3jZO9NYsmtc24fUckA0X7Q2v+ML4a15pdUlV5V/jA==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + safe-stable-stringify@2.5.0: resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} engines: {node: '>=10'} + search-insights@2.17.3: + resolution: {integrity: sha512-RQPdCYTa8A68uM2jwxoY842xDhvx3E5LFL1LxvxCNMev4o5mLuokczhzjAgGwUZBAmOKZknArSxLKmXtIi2AxQ==} + secure-json-parse@4.1.0: resolution: {integrity: sha512-l4KnYfEyqYJxDwlNVyRfO2E4NTHfMKAWdUuA8J0yve2Dz/E/PdBepY03RvyJpssIpRFwJoCD55wA+mEDs6ByWA==} @@ -1387,6 +2057,9 @@ packages: engines: {node: '>=10'} hasBin: true + shiki@2.5.0: + resolution: {integrity: sha512-mI//trrsaiCIPsja5CNfsyNOqgAZUb6VpJA+340toL42UpzQlXpwRV9nch69X6gaUxrr9kaOOa6e3y3uAkGFxQ==} + siginfo@2.0.0: resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} @@ -1397,6 +2070,13 @@ packages: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} + space-separated-tokens@2.0.2: + resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} + + speakingurl@14.0.1: + resolution: {integrity: sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==} + engines: {node: '>=0.10.0'} + split2@4.2.0: resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} engines: {node: '>= 10.x'} @@ -1411,6 +2091,9 @@ packages: 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@7.2.0: resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} engines: {node: '>=12'} @@ -1419,6 +2102,13 @@ packages: resolution: {integrity: sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==} engines: {node: '>=14.16'} + superjson@2.2.6: + resolution: {integrity: sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA==} + engines: {node: '>=16'} + + tabbable@6.4.0: + resolution: {integrity: sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg==} + thread-stream@4.2.0: resolution: {integrity: sha512-e2zZ96wSChazBsbENf/Pcm/4swHt2cEKQ92rhUjkL9GCKiTDJIaTBenjE/m9DXi0QBmTMDkFDdOomUy20A1tDQ==} engines: {node: '>=20'} @@ -1442,6 +2132,9 @@ packages: resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} engines: {node: '>=14.0.0'} + trim-lines@3.0.1: + resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} + ts-mixer@6.0.4: resolution: {integrity: sha512-ufKpbmrugz5Aou4wcr5Wc1UUFWOLhq+Fm6qa6P0w0K5Qw2yhaUoiWszhCVuNQyNwrlGiscHOmqYoAox1PtvgjA==} @@ -1465,6 +2158,58 @@ packages: resolution: {integrity: sha512-sC+b0tB1whOCzbtlx20fx3WgCXwkW627p4EA9uM+/tNNPkSS+eSEld6pAs9nDv7WbY1UUljBMYPtu9BCOrCWKA==} engines: {node: '>=18.17'} + unist-util-is@6.0.1: + resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==} + + unist-util-position@5.0.0: + resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} + + unist-util-stringify-position@4.0.0: + resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} + + unist-util-visit-parents@6.0.2: + resolution: {integrity: sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==} + + unist-util-visit@5.1.0: + resolution: {integrity: sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==} + + vfile-message@4.0.3: + resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==} + + vfile@6.0.3: + resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} + + vite@5.4.21: + resolution: {integrity: sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || >=20.0.0 + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + vite@8.0.16: resolution: {integrity: sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw==} engines: {node: ^20.19.0 || >=22.12.0} @@ -1508,6 +2253,18 @@ packages: yaml: optional: true + vitepress@1.6.4: + resolution: {integrity: sha512-+2ym1/+0VVrbhNyRoFFesVvBvHAVMZMK0rw60E3X/5349M1GuVdKeazuksqopEdvkKwKGs21Q729jX81/bkBJg==} + hasBin: true + peerDependencies: + markdown-it-mathjax3: ^4 + postcss: ^8 + peerDependenciesMeta: + markdown-it-mathjax3: + optional: true + postcss: + optional: true + vitest@4.1.8: resolution: {integrity: sha512-flY6ScbCIt9HThs+C5HS7jvGOB560DJtk/Z15IQROTA6zEy49Nh8T/dofWTQL+n3vswqn87sbJNiuqw1SDp5Ig==} engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -1549,6 +2306,14 @@ packages: jsdom: optional: true + vue@3.5.38: + resolution: {integrity: sha512-vAMKHfImQlYSy0C+PBue4s3ERZ2xGKfgZg5GXAsLInq1dyh2H78ILVP5sK0KPFPVW4kv+OGCIvBEondcjpZp7A==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + why-is-node-running@2.3.0: resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} engines: {node: '>=8'} @@ -1590,16 +2355,144 @@ packages: resolution: {integrity: sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==} engines: {node: ^20.19.0 || ^22.12.0 || >=23} + zwitch@2.0.4: + resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} + snapshots: + '@algolia/abtesting@1.20.0': + dependencies: + '@algolia/client-common': 5.54.0 + '@algolia/requester-browser-xhr': 5.54.0 + '@algolia/requester-fetch': 5.54.0 + '@algolia/requester-node-http': 5.54.0 + + '@algolia/autocomplete-core@1.17.7(@algolia/client-search@5.54.0)(algoliasearch@5.54.0)(search-insights@2.17.3)': + dependencies: + '@algolia/autocomplete-plugin-algolia-insights': 1.17.7(@algolia/client-search@5.54.0)(algoliasearch@5.54.0)(search-insights@2.17.3) + '@algolia/autocomplete-shared': 1.17.7(@algolia/client-search@5.54.0)(algoliasearch@5.54.0) + transitivePeerDependencies: + - '@algolia/client-search' + - algoliasearch + - search-insights + + '@algolia/autocomplete-plugin-algolia-insights@1.17.7(@algolia/client-search@5.54.0)(algoliasearch@5.54.0)(search-insights@2.17.3)': + dependencies: + '@algolia/autocomplete-shared': 1.17.7(@algolia/client-search@5.54.0)(algoliasearch@5.54.0) + search-insights: 2.17.3 + transitivePeerDependencies: + - '@algolia/client-search' + - algoliasearch + + '@algolia/autocomplete-preset-algolia@1.17.7(@algolia/client-search@5.54.0)(algoliasearch@5.54.0)': + dependencies: + '@algolia/autocomplete-shared': 1.17.7(@algolia/client-search@5.54.0)(algoliasearch@5.54.0) + '@algolia/client-search': 5.54.0 + algoliasearch: 5.54.0 + + '@algolia/autocomplete-shared@1.17.7(@algolia/client-search@5.54.0)(algoliasearch@5.54.0)': + dependencies: + '@algolia/client-search': 5.54.0 + algoliasearch: 5.54.0 + + '@algolia/client-abtesting@5.54.0': + dependencies: + '@algolia/client-common': 5.54.0 + '@algolia/requester-browser-xhr': 5.54.0 + '@algolia/requester-fetch': 5.54.0 + '@algolia/requester-node-http': 5.54.0 + + '@algolia/client-analytics@5.54.0': + dependencies: + '@algolia/client-common': 5.54.0 + '@algolia/requester-browser-xhr': 5.54.0 + '@algolia/requester-fetch': 5.54.0 + '@algolia/requester-node-http': 5.54.0 + + '@algolia/client-common@5.54.0': {} + + '@algolia/client-insights@5.54.0': + dependencies: + '@algolia/client-common': 5.54.0 + '@algolia/requester-browser-xhr': 5.54.0 + '@algolia/requester-fetch': 5.54.0 + '@algolia/requester-node-http': 5.54.0 + + '@algolia/client-personalization@5.54.0': + dependencies: + '@algolia/client-common': 5.54.0 + '@algolia/requester-browser-xhr': 5.54.0 + '@algolia/requester-fetch': 5.54.0 + '@algolia/requester-node-http': 5.54.0 + + '@algolia/client-query-suggestions@5.54.0': + dependencies: + '@algolia/client-common': 5.54.0 + '@algolia/requester-browser-xhr': 5.54.0 + '@algolia/requester-fetch': 5.54.0 + '@algolia/requester-node-http': 5.54.0 + + '@algolia/client-search@5.54.0': + dependencies: + '@algolia/client-common': 5.54.0 + '@algolia/requester-browser-xhr': 5.54.0 + '@algolia/requester-fetch': 5.54.0 + '@algolia/requester-node-http': 5.54.0 + + '@algolia/ingestion@1.54.0': + dependencies: + '@algolia/client-common': 5.54.0 + '@algolia/requester-browser-xhr': 5.54.0 + '@algolia/requester-fetch': 5.54.0 + '@algolia/requester-node-http': 5.54.0 + + '@algolia/monitoring@1.54.0': + dependencies: + '@algolia/client-common': 5.54.0 + '@algolia/requester-browser-xhr': 5.54.0 + '@algolia/requester-fetch': 5.54.0 + '@algolia/requester-node-http': 5.54.0 + + '@algolia/recommend@5.54.0': + dependencies: + '@algolia/client-common': 5.54.0 + '@algolia/requester-browser-xhr': 5.54.0 + '@algolia/requester-fetch': 5.54.0 + '@algolia/requester-node-http': 5.54.0 + + '@algolia/requester-browser-xhr@5.54.0': + dependencies: + '@algolia/client-common': 5.54.0 + + '@algolia/requester-fetch@5.54.0': + dependencies: + '@algolia/client-common': 5.54.0 + + '@algolia/requester-node-http@5.54.0': + dependencies: + '@algolia/client-common': 5.54.0 + '@babel/code-frame@7.29.0': dependencies: '@babel/helper-validator-identifier': 7.28.5 js-tokens: 4.0.0 picocolors: 1.1.1 + '@babel/helper-string-parser@7.29.7': {} + '@babel/helper-validator-identifier@7.28.5': {} + '@babel/helper-validator-identifier@7.29.7': {} + + '@babel/parser@7.29.7': + dependencies: + '@babel/types': 7.29.7 + + '@babel/types@7.29.7': + dependencies: + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + '@commitlint/cli@21.0.2(@types/node@24.13.2)(conventional-commits-parser@6.4.0)(typescript@5.9.3)': dependencies: '@commitlint/format': 21.0.1 @@ -1765,6 +2658,30 @@ snapshots: - bufferutil - utf-8-validate + '@docsearch/css@3.8.2': {} + + '@docsearch/js@3.8.2(@algolia/client-search@5.54.0)(search-insights@2.17.3)': + dependencies: + '@docsearch/react': 3.8.2(@algolia/client-search@5.54.0)(search-insights@2.17.3) + preact: 10.29.2 + transitivePeerDependencies: + - '@algolia/client-search' + - '@types/react' + - react + - react-dom + - search-insights + + '@docsearch/react@3.8.2(@algolia/client-search@5.54.0)(search-insights@2.17.3)': + dependencies: + '@algolia/autocomplete-core': 1.17.7(@algolia/client-search@5.54.0)(algoliasearch@5.54.0)(search-insights@2.17.3) + '@algolia/autocomplete-preset-algolia': 1.17.7(@algolia/client-search@5.54.0)(algoliasearch@5.54.0) + '@docsearch/css': 3.8.2 + algoliasearch: 5.54.0 + optionalDependencies: + search-insights: 2.17.3 + transitivePeerDependencies: + - '@algolia/client-search' + '@emnapi/core@1.10.0': dependencies: '@emnapi/wasi-threads': 1.2.1 @@ -1781,84 +2698,159 @@ snapshots: tslib: 2.8.1 optional: true + '@esbuild/aix-ppc64@0.21.5': + optional: true + '@esbuild/aix-ppc64@0.28.1': optional: true + '@esbuild/android-arm64@0.21.5': + optional: true + '@esbuild/android-arm64@0.28.1': optional: true + '@esbuild/android-arm@0.21.5': + optional: true + '@esbuild/android-arm@0.28.1': optional: true - '@esbuild/android-x64@0.28.1': + '@esbuild/android-x64@0.21.5': + optional: true + + '@esbuild/android-x64@0.28.1': + optional: true + + '@esbuild/darwin-arm64@0.21.5': optional: true '@esbuild/darwin-arm64@0.28.1': optional: true + '@esbuild/darwin-x64@0.21.5': + optional: true + '@esbuild/darwin-x64@0.28.1': optional: true + '@esbuild/freebsd-arm64@0.21.5': + optional: true + '@esbuild/freebsd-arm64@0.28.1': optional: true + '@esbuild/freebsd-x64@0.21.5': + optional: true + '@esbuild/freebsd-x64@0.28.1': optional: true + '@esbuild/linux-arm64@0.21.5': + optional: true + '@esbuild/linux-arm64@0.28.1': optional: true + '@esbuild/linux-arm@0.21.5': + optional: true + '@esbuild/linux-arm@0.28.1': optional: true + '@esbuild/linux-ia32@0.21.5': + optional: true + '@esbuild/linux-ia32@0.28.1': optional: true + '@esbuild/linux-loong64@0.21.5': + optional: true + '@esbuild/linux-loong64@0.28.1': optional: true + '@esbuild/linux-mips64el@0.21.5': + optional: true + '@esbuild/linux-mips64el@0.28.1': optional: true + '@esbuild/linux-ppc64@0.21.5': + optional: true + '@esbuild/linux-ppc64@0.28.1': optional: true + '@esbuild/linux-riscv64@0.21.5': + optional: true + '@esbuild/linux-riscv64@0.28.1': optional: true + '@esbuild/linux-s390x@0.21.5': + optional: true + '@esbuild/linux-s390x@0.28.1': optional: true + '@esbuild/linux-x64@0.21.5': + optional: true + '@esbuild/linux-x64@0.28.1': optional: true '@esbuild/netbsd-arm64@0.28.1': optional: true + '@esbuild/netbsd-x64@0.21.5': + optional: true + '@esbuild/netbsd-x64@0.28.1': optional: true '@esbuild/openbsd-arm64@0.28.1': optional: true + '@esbuild/openbsd-x64@0.21.5': + optional: true + '@esbuild/openbsd-x64@0.28.1': optional: true '@esbuild/openharmony-arm64@0.28.1': optional: true + '@esbuild/sunos-x64@0.21.5': + optional: true + '@esbuild/sunos-x64@0.28.1': optional: true + '@esbuild/win32-arm64@0.21.5': + optional: true + '@esbuild/win32-arm64@0.28.1': optional: true + '@esbuild/win32-ia32@0.21.5': + optional: true + '@esbuild/win32-ia32@0.28.1': optional: true + '@esbuild/win32-x64@0.21.5': + optional: true + '@esbuild/win32-x64@0.28.1': optional: true + '@iconify-json/simple-icons@1.2.86': + dependencies: + '@iconify/types': 2.0.0 + + '@iconify/types@2.0.0': {} + '@jridgewell/sourcemap-codec@1.5.5': {} '@napi-rs/wasm-runtime@1.1.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': @@ -2072,6 +3064,81 @@ snapshots: '@rolldown/pluginutils@1.0.1': {} + '@rollup/rollup-android-arm-eabi@4.62.0': + optional: true + + '@rollup/rollup-android-arm64@4.62.0': + optional: true + + '@rollup/rollup-darwin-arm64@4.62.0': + optional: true + + '@rollup/rollup-darwin-x64@4.62.0': + optional: true + + '@rollup/rollup-freebsd-arm64@4.62.0': + optional: true + + '@rollup/rollup-freebsd-x64@4.62.0': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.62.0': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.62.0': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.62.0': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.62.0': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.62.0': + optional: true + + '@rollup/rollup-linux-loong64-musl@4.62.0': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.62.0': + optional: true + + '@rollup/rollup-linux-ppc64-musl@4.62.0': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.62.0': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.62.0': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.62.0': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.62.0': + optional: true + + '@rollup/rollup-linux-x64-musl@4.62.0': + optional: true + + '@rollup/rollup-openbsd-x64@4.62.0': + optional: true + + '@rollup/rollup-openharmony-arm64@4.62.0': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.62.0': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.62.0': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.62.0': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.62.0': + optional: true + '@sapphire/async-queue@1.5.5': {} '@sapphire/shapeshift@4.0.0': @@ -2083,6 +3150,46 @@ snapshots: '@sapphire/snowflake@3.5.5': {} + '@shikijs/core@2.5.0': + dependencies: + '@shikijs/engine-javascript': 2.5.0 + '@shikijs/engine-oniguruma': 2.5.0 + '@shikijs/types': 2.5.0 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + hast-util-to-html: 9.0.5 + + '@shikijs/engine-javascript@2.5.0': + dependencies: + '@shikijs/types': 2.5.0 + '@shikijs/vscode-textmate': 10.0.2 + oniguruma-to-es: 3.1.1 + + '@shikijs/engine-oniguruma@2.5.0': + dependencies: + '@shikijs/types': 2.5.0 + '@shikijs/vscode-textmate': 10.0.2 + + '@shikijs/langs@2.5.0': + dependencies: + '@shikijs/types': 2.5.0 + + '@shikijs/themes@2.5.0': + dependencies: + '@shikijs/types': 2.5.0 + + '@shikijs/transformers@2.5.0': + dependencies: + '@shikijs/core': 2.5.0 + '@shikijs/types': 2.5.0 + + '@shikijs/types@2.5.0': + dependencies: + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + + '@shikijs/vscode-textmate@10.0.2': {} + '@simple-libs/child-process-utils@1.0.2': dependencies: '@simple-libs/stream-utils': 1.2.0 @@ -2105,14 +3212,42 @@ snapshots: '@types/estree@1.0.9': {} + '@types/hast@3.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/linkify-it@5.0.0': {} + + '@types/markdown-it@14.1.2': + dependencies: + '@types/linkify-it': 5.0.0 + '@types/mdurl': 2.0.0 + + '@types/mdast@4.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/mdurl@2.0.0': {} + '@types/node@24.13.2': dependencies: undici-types: 7.18.2 + '@types/unist@3.0.3': {} + + '@types/web-bluetooth@0.0.21': {} + '@types/ws@8.18.1': dependencies: '@types/node': 24.13.2 + '@ungap/structured-clone@1.3.1': {} + + '@vitejs/plugin-vue@5.2.4(vite@5.4.21(@types/node@24.13.2)(lightningcss@1.32.0))(vue@3.5.38(typescript@5.9.3))': + dependencies: + vite: 5.4.21(@types/node@24.13.2)(lightningcss@1.32.0) + vue: 3.5.38(typescript@5.9.3) + '@vitest/expect@4.1.8': dependencies: '@standard-schema/spec': 1.1.0 @@ -2156,6 +3291,105 @@ snapshots: '@vladfrangu/async_event_emitter@2.4.7': {} + '@vue/compiler-core@3.5.38': + dependencies: + '@babel/parser': 7.29.7 + '@vue/shared': 3.5.38 + entities: 7.0.1 + estree-walker: 2.0.2 + source-map-js: 1.2.1 + + '@vue/compiler-dom@3.5.38': + dependencies: + '@vue/compiler-core': 3.5.38 + '@vue/shared': 3.5.38 + + '@vue/compiler-sfc@3.5.38': + dependencies: + '@babel/parser': 7.29.7 + '@vue/compiler-core': 3.5.38 + '@vue/compiler-dom': 3.5.38 + '@vue/compiler-ssr': 3.5.38 + '@vue/shared': 3.5.38 + estree-walker: 2.0.2 + magic-string: 0.30.21 + postcss: 8.5.15 + source-map-js: 1.2.1 + + '@vue/compiler-ssr@3.5.38': + dependencies: + '@vue/compiler-dom': 3.5.38 + '@vue/shared': 3.5.38 + + '@vue/devtools-api@7.7.9': + dependencies: + '@vue/devtools-kit': 7.7.9 + + '@vue/devtools-kit@7.7.9': + dependencies: + '@vue/devtools-shared': 7.7.9 + birpc: 2.9.0 + hookable: 5.5.3 + mitt: 3.0.1 + perfect-debounce: 1.0.0 + speakingurl: 14.0.1 + superjson: 2.2.6 + + '@vue/devtools-shared@7.7.9': + dependencies: + rfdc: 1.4.1 + + '@vue/reactivity@3.5.38': + dependencies: + '@vue/shared': 3.5.38 + + '@vue/runtime-core@3.5.38': + dependencies: + '@vue/reactivity': 3.5.38 + '@vue/shared': 3.5.38 + + '@vue/runtime-dom@3.5.38': + dependencies: + '@vue/reactivity': 3.5.38 + '@vue/runtime-core': 3.5.38 + '@vue/shared': 3.5.38 + csstype: 3.2.3 + + '@vue/server-renderer@3.5.38(vue@3.5.38(typescript@5.9.3))': + dependencies: + '@vue/compiler-ssr': 3.5.38 + '@vue/shared': 3.5.38 + vue: 3.5.38(typescript@5.9.3) + + '@vue/shared@3.5.38': {} + + '@vueuse/core@12.8.2(typescript@5.9.3)': + dependencies: + '@types/web-bluetooth': 0.0.21 + '@vueuse/metadata': 12.8.2 + '@vueuse/shared': 12.8.2(typescript@5.9.3) + vue: 3.5.38(typescript@5.9.3) + transitivePeerDependencies: + - typescript + + '@vueuse/integrations@12.8.2(focus-trap@7.8.0)(typescript@5.9.3)': + dependencies: + '@vueuse/core': 12.8.2(typescript@5.9.3) + '@vueuse/shared': 12.8.2(typescript@5.9.3) + vue: 3.5.38(typescript@5.9.3) + optionalDependencies: + focus-trap: 7.8.0 + transitivePeerDependencies: + - typescript + + '@vueuse/metadata@12.8.2': {} + + '@vueuse/shared@12.8.2(typescript@5.9.3)': + dependencies: + vue: 3.5.38(typescript@5.9.3) + transitivePeerDependencies: + - typescript + ajv@8.20.0: dependencies: fast-deep-equal: 3.1.3 @@ -2163,6 +3397,23 @@ snapshots: json-schema-traverse: 1.0.0 require-from-string: 2.0.2 + algoliasearch@5.54.0: + dependencies: + '@algolia/abtesting': 1.20.0 + '@algolia/client-abtesting': 5.54.0 + '@algolia/client-analytics': 5.54.0 + '@algolia/client-common': 5.54.0 + '@algolia/client-insights': 5.54.0 + '@algolia/client-personalization': 5.54.0 + '@algolia/client-query-suggestions': 5.54.0 + '@algolia/client-search': 5.54.0 + '@algolia/ingestion': 1.54.0 + '@algolia/monitoring': 1.54.0 + '@algolia/recommend': 5.54.0 + '@algolia/requester-browser-xhr': 5.54.0 + '@algolia/requester-fetch': 5.54.0 + '@algolia/requester-node-http': 5.54.0 + ansi-regex@6.2.2: {} ansi-styles@6.2.3: {} @@ -2175,6 +3426,8 @@ snapshots: atomic-sleep@1.0.0: {} + birpc@2.9.0: {} + c12@3.1.0: dependencies: chokidar: 4.0.3 @@ -2192,8 +3445,14 @@ snapshots: callsites@3.1.0: {} + ccount@2.0.1: {} + chai@6.2.2: {} + character-entities-html4@2.1.0: {} + + character-entities-legacy@3.0.0: {} + chokidar@4.0.3: dependencies: readdirp: 4.1.2 @@ -2212,6 +3471,8 @@ snapshots: colorette@2.0.20: {} + comma-separated-tokens@2.0.3: {} + compare-func@2.0.0: dependencies: array-ify: 1.0.0 @@ -2236,6 +3497,10 @@ snapshots: convert-source-map@2.0.0: {} + copy-anything@4.0.5: + dependencies: + is-what: 5.5.0 + cosmiconfig-typescript-loader@6.3.0(@types/node@24.13.2)(cosmiconfig@9.0.1(typescript@5.9.3))(typescript@5.9.3): dependencies: '@types/node': 24.13.2 @@ -2252,16 +3517,24 @@ snapshots: optionalDependencies: typescript: 5.9.3 + csstype@3.2.3: {} + dateformat@4.6.3: {} deepmerge-ts@7.1.5: {} defu@6.1.7: {} + dequal@2.0.3: {} + destr@2.0.5: {} detect-libc@2.1.2: {} + devlop@1.1.0: + dependencies: + dequal: 2.0.3 + discord-api-types@0.38.47: {} discord.js@14.26.3: @@ -2294,6 +3567,8 @@ snapshots: '@standard-schema/spec': 1.1.0 fast-check: 3.23.2 + emoji-regex-xs@1.0.0: {} + emoji-regex@10.6.0: {} empathic@2.0.0: {} @@ -2302,6 +3577,8 @@ snapshots: dependencies: once: 1.4.0 + entities@7.0.1: {} + env-paths@2.2.1: {} error-ex@1.3.4: @@ -2312,6 +3589,32 @@ snapshots: es-toolkit@1.46.1: {} + esbuild@0.21.5: + optionalDependencies: + '@esbuild/aix-ppc64': 0.21.5 + '@esbuild/android-arm': 0.21.5 + '@esbuild/android-arm64': 0.21.5 + '@esbuild/android-x64': 0.21.5 + '@esbuild/darwin-arm64': 0.21.5 + '@esbuild/darwin-x64': 0.21.5 + '@esbuild/freebsd-arm64': 0.21.5 + '@esbuild/freebsd-x64': 0.21.5 + '@esbuild/linux-arm': 0.21.5 + '@esbuild/linux-arm64': 0.21.5 + '@esbuild/linux-ia32': 0.21.5 + '@esbuild/linux-loong64': 0.21.5 + '@esbuild/linux-mips64el': 0.21.5 + '@esbuild/linux-ppc64': 0.21.5 + '@esbuild/linux-riscv64': 0.21.5 + '@esbuild/linux-s390x': 0.21.5 + '@esbuild/linux-x64': 0.21.5 + '@esbuild/netbsd-x64': 0.21.5 + '@esbuild/openbsd-x64': 0.21.5 + '@esbuild/sunos-x64': 0.21.5 + '@esbuild/win32-arm64': 0.21.5 + '@esbuild/win32-ia32': 0.21.5 + '@esbuild/win32-x64': 0.21.5 + esbuild@0.28.1: optionalDependencies: '@esbuild/aix-ppc64': 0.28.1 @@ -2343,6 +3646,8 @@ snapshots: escalade@3.2.0: {} + estree-walker@2.0.2: {} + estree-walker@3.0.3: dependencies: '@types/estree': 1.0.9 @@ -2367,6 +3672,10 @@ snapshots: optionalDependencies: picomatch: 4.0.4 + focus-trap@7.8.0: + dependencies: + tabbable: 6.4.0 + fsevents@2.3.3: optional: true @@ -2395,8 +3704,30 @@ snapshots: dependencies: ini: 6.0.0 + 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.1 + property-information: 7.2.0 + space-separated-tokens: 2.0.2 + stringify-entities: 4.0.4 + zwitch: 2.0.4 + + hast-util-whitespace@3.0.0: + dependencies: + '@types/hast': 3.0.4 + help-me@5.0.0: {} + hookable@5.5.3: {} + + html-void-elements@3.0.0: {} + import-fresh@3.3.1: dependencies: parent-module: 1.0.1 @@ -2410,6 +3741,8 @@ snapshots: is-plain-obj@4.1.0: {} + is-what@5.5.0: {} + jiti@2.6.1: {} joycon@3.1.1: {} @@ -2528,10 +3861,45 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 + mark.js@8.11.1: {} + + mdast-util-to-hast@13.2.1: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@ungap/structured-clone': 1.3.1 + devlop: 1.1.0 + micromark-util-sanitize-uri: 2.0.1 + trim-lines: 3.0.1 + unist-util-position: 5.0.0 + unist-util-visit: 5.1.0 + vfile: 6.0.3 + meow@13.2.0: {} + micromark-util-character@2.1.1: + dependencies: + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-encode@2.0.1: {} + + 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-symbol@2.0.1: {} + + micromark-util-types@2.0.2: {} + minimist@1.2.8: {} + minisearch@7.2.0: {} + + mitt@3.0.1: {} + nanoid@3.3.12: {} node-fetch-native@1.6.7: {} @@ -2552,6 +3920,12 @@ snapshots: dependencies: wrappy: 1.0.2 + oniguruma-to-es@3.1.1: + dependencies: + emoji-regex-xs: 1.0.0 + regex: 6.1.0 + regex-recursion: 6.0.2 + oxfmt@0.54.0: dependencies: tinypool: 2.1.0 @@ -2665,6 +4039,8 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 + preact@10.29.2: {} + prisma@6.19.3(typescript@5.9.3): dependencies: '@prisma/config': 6.19.3 @@ -2676,6 +4052,8 @@ snapshots: process-warning@5.0.0: {} + property-information@7.2.0: {} + pump@3.0.4: dependencies: end-of-stream: 1.4.5 @@ -2696,12 +4074,24 @@ snapshots: real-require@1.0.0: {} + regex-recursion@6.0.2: + dependencies: + regex-utilities: 2.3.0 + + regex-utilities@2.3.0: {} + + regex@6.1.0: + dependencies: + regex-utilities: 2.3.0 + require-from-string@2.0.2: {} resolve-from@4.0.0: {} resolve-from@5.0.0: {} + rfdc@1.4.1: {} + rolldown@1.0.3: dependencies: '@oxc-project/types': 0.133.0 @@ -2723,12 +4113,56 @@ snapshots: '@rolldown/binding-win32-arm64-msvc': 1.0.3 '@rolldown/binding-win32-x64-msvc': 1.0.3 + rollup@4.62.0: + dependencies: + '@types/estree': 1.0.9 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.62.0 + '@rollup/rollup-android-arm64': 4.62.0 + '@rollup/rollup-darwin-arm64': 4.62.0 + '@rollup/rollup-darwin-x64': 4.62.0 + '@rollup/rollup-freebsd-arm64': 4.62.0 + '@rollup/rollup-freebsd-x64': 4.62.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.62.0 + '@rollup/rollup-linux-arm-musleabihf': 4.62.0 + '@rollup/rollup-linux-arm64-gnu': 4.62.0 + '@rollup/rollup-linux-arm64-musl': 4.62.0 + '@rollup/rollup-linux-loong64-gnu': 4.62.0 + '@rollup/rollup-linux-loong64-musl': 4.62.0 + '@rollup/rollup-linux-ppc64-gnu': 4.62.0 + '@rollup/rollup-linux-ppc64-musl': 4.62.0 + '@rollup/rollup-linux-riscv64-gnu': 4.62.0 + '@rollup/rollup-linux-riscv64-musl': 4.62.0 + '@rollup/rollup-linux-s390x-gnu': 4.62.0 + '@rollup/rollup-linux-x64-gnu': 4.62.0 + '@rollup/rollup-linux-x64-musl': 4.62.0 + '@rollup/rollup-openbsd-x64': 4.62.0 + '@rollup/rollup-openharmony-arm64': 4.62.0 + '@rollup/rollup-win32-arm64-msvc': 4.62.0 + '@rollup/rollup-win32-ia32-msvc': 4.62.0 + '@rollup/rollup-win32-x64-gnu': 4.62.0 + '@rollup/rollup-win32-x64-msvc': 4.62.0 + fsevents: 2.3.3 + safe-stable-stringify@2.5.0: {} + search-insights@2.17.3: {} + secure-json-parse@4.1.0: {} semver@7.7.4: {} + shiki@2.5.0: + dependencies: + '@shikijs/core': 2.5.0 + '@shikijs/engine-javascript': 2.5.0 + '@shikijs/engine-oniguruma': 2.5.0 + '@shikijs/langs': 2.5.0 + '@shikijs/themes': 2.5.0 + '@shikijs/types': 2.5.0 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + siginfo@2.0.0: {} sonic-boom@4.2.1: @@ -2737,6 +4171,10 @@ snapshots: source-map-js@1.2.1: {} + space-separated-tokens@2.0.2: {} + + speakingurl@14.0.1: {} + split2@4.2.0: {} stackback@0.0.2: {} @@ -2749,12 +4187,23 @@ snapshots: get-east-asian-width: 1.6.0 strip-ansi: 7.2.0 + stringify-entities@4.0.4: + dependencies: + character-entities-html4: 2.1.0 + character-entities-legacy: 3.0.0 + strip-ansi@7.2.0: dependencies: ansi-regex: 6.2.2 strip-json-comments@5.0.3: {} + superjson@2.2.6: + dependencies: + copy-anything: 4.0.5 + + tabbable@6.4.0: {} + thread-stream@4.2.0: dependencies: real-require: 1.0.0 @@ -2772,6 +4221,8 @@ snapshots: tinyrainbow@3.1.0: {} + trim-lines@3.0.1: {} + ts-mixer@6.0.4: {} tslib@2.8.1: {} @@ -2788,6 +4239,49 @@ snapshots: undici@6.24.1: {} + unist-util-is@6.0.1: + dependencies: + '@types/unist': 3.0.3 + + unist-util-position@5.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-stringify-position@4.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-visit-parents@6.0.2: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + + unist-util-visit@5.1.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + unist-util-visit-parents: 6.0.2 + + vfile-message@4.0.3: + dependencies: + '@types/unist': 3.0.3 + unist-util-stringify-position: 4.0.0 + + vfile@6.0.3: + dependencies: + '@types/unist': 3.0.3 + vfile-message: 4.0.3 + + vite@5.4.21(@types/node@24.13.2)(lightningcss@1.32.0): + dependencies: + esbuild: 0.21.5 + postcss: 8.5.15 + rollup: 4.62.0 + optionalDependencies: + '@types/node': 24.13.2 + fsevents: 2.3.3 + lightningcss: 1.32.0 + vite@8.0.16(@types/node@24.13.2)(esbuild@0.28.1)(jiti@2.6.1)(tsx@4.22.4)(yaml@2.9.0): dependencies: lightningcss: 1.32.0 @@ -2803,6 +4297,55 @@ snapshots: tsx: 4.22.4 yaml: 2.9.0 + vitepress@1.6.4(@algolia/client-search@5.54.0)(@types/node@24.13.2)(lightningcss@1.32.0)(postcss@8.5.15)(search-insights@2.17.3)(typescript@5.9.3): + dependencies: + '@docsearch/css': 3.8.2 + '@docsearch/js': 3.8.2(@algolia/client-search@5.54.0)(search-insights@2.17.3) + '@iconify-json/simple-icons': 1.2.86 + '@shikijs/core': 2.5.0 + '@shikijs/transformers': 2.5.0 + '@shikijs/types': 2.5.0 + '@types/markdown-it': 14.1.2 + '@vitejs/plugin-vue': 5.2.4(vite@5.4.21(@types/node@24.13.2)(lightningcss@1.32.0))(vue@3.5.38(typescript@5.9.3)) + '@vue/devtools-api': 7.7.9 + '@vue/shared': 3.5.38 + '@vueuse/core': 12.8.2(typescript@5.9.3) + '@vueuse/integrations': 12.8.2(focus-trap@7.8.0)(typescript@5.9.3) + focus-trap: 7.8.0 + mark.js: 8.11.1 + minisearch: 7.2.0 + shiki: 2.5.0 + vite: 5.4.21(@types/node@24.13.2)(lightningcss@1.32.0) + vue: 3.5.38(typescript@5.9.3) + optionalDependencies: + postcss: 8.5.15 + transitivePeerDependencies: + - '@algolia/client-search' + - '@types/node' + - '@types/react' + - async-validator + - axios + - change-case + - drauu + - fuse.js + - idb-keyval + - jwt-decode + - less + - lightningcss + - nprogress + - qrcode + - react + - react-dom + - sass + - sass-embedded + - search-insights + - sortablejs + - stylus + - sugarss + - terser + - typescript + - universal-cookie + vitest@4.1.8(@types/node@24.13.2)(vite@8.0.16(@types/node@24.13.2)(esbuild@0.28.1)(jiti@2.6.1)(tsx@4.22.4)(yaml@2.9.0)): dependencies: '@vitest/expect': 4.1.8 @@ -2830,6 +4373,16 @@ snapshots: transitivePeerDependencies: - msw + vue@3.5.38(typescript@5.9.3): + dependencies: + '@vue/compiler-dom': 3.5.38 + '@vue/compiler-sfc': 3.5.38 + '@vue/runtime-dom': 3.5.38 + '@vue/server-renderer': 3.5.38(vue@3.5.38(typescript@5.9.3)) + '@vue/shared': 3.5.38 + optionalDependencies: + typescript: 5.9.3 + why-is-node-running@2.3.0: dependencies: siginfo: 2.0.0 @@ -2860,3 +4413,5 @@ snapshots: string-width: 7.2.0 y18n: 5.0.8 yargs-parser: 22.0.0 + + zwitch@2.0.4: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index f9a0079..f4569b0 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,7 +1,11 @@ +packages: + - "." + - "docs/site" + allowBuilds: '@prisma/client': true '@prisma/engines': true - esbuild: false + esbuild: true lefthook: false prisma: true