From 145b0b659904ab4423c4f16330675dfcd04a6a5d Mon Sep 17 00:00:00 2001 From: Till-JS <101404291+Till-JS@users.noreply.github.com> Date: Sun, 1 Feb 2026 01:02:55 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat:=20create=20@manacore/matrix-b?= =?UTF-8?q?ot-common=20shared=20package?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New package with shared utilities for Matrix bots: **Components:** - `BaseMatrixService` - Abstract base class with client lifecycle - `HealthController` - Standardized health endpoint - `MatrixMessageService` - Message/reply/reaction helpers - `markdownToHtml` - Markdown to HTML conversion - `KeywordCommandDetector` - Natural language command detection - `SessionHelper` - Type-safe session data wrapper - `UserListMapper` - Number-based reference system **Estimated Impact:** - ~4,000 lines of duplicate code can be eliminated - 19 Matrix bots can use these shared utilities - Consistent behavior across all bots Documentation in packages/matrix-bot-common/CLAUDE.md Co-Authored-By: Claude Opus 4.5 --- packages/matrix-bot-common/CLAUDE.md | 265 +++++++ packages/matrix-bot-common/package.json | 38 + .../src/base/base-matrix.service.ts | 250 +++++++ packages/matrix-bot-common/src/base/index.ts | 10 + packages/matrix-bot-common/src/base/types.ts | 75 ++ .../src/health/health.controller.ts | 58 ++ .../matrix-bot-common/src/health/index.ts | 6 + packages/matrix-bot-common/src/index.ts | 68 ++ .../matrix-bot-common/src/keywords/index.ts | 6 + .../src/keywords/keyword-detector.ts | 118 +++ .../src/list-mapper/index.ts | 1 + .../src/list-mapper/list-mapper.ts | 113 +++ .../matrix-bot-common/src/markdown/index.ts | 6 + .../src/markdown/markdown-formatter.ts | 53 ++ .../matrix-bot-common/src/message/index.ts | 5 + .../src/message/message.service.ts | 227 ++++++ .../matrix-bot-common/src/session/index.ts | 1 + .../src/session/session-helper.ts | 85 +++ packages/matrix-bot-common/tsconfig.json | 25 + pnpm-lock.yaml | 700 ++++++------------ 20 files changed, 1632 insertions(+), 478 deletions(-) create mode 100644 packages/matrix-bot-common/CLAUDE.md create mode 100644 packages/matrix-bot-common/package.json create mode 100644 packages/matrix-bot-common/src/base/base-matrix.service.ts create mode 100644 packages/matrix-bot-common/src/base/index.ts create mode 100644 packages/matrix-bot-common/src/base/types.ts create mode 100644 packages/matrix-bot-common/src/health/health.controller.ts create mode 100644 packages/matrix-bot-common/src/health/index.ts create mode 100644 packages/matrix-bot-common/src/index.ts create mode 100644 packages/matrix-bot-common/src/keywords/index.ts create mode 100644 packages/matrix-bot-common/src/keywords/keyword-detector.ts create mode 100644 packages/matrix-bot-common/src/list-mapper/index.ts create mode 100644 packages/matrix-bot-common/src/list-mapper/list-mapper.ts create mode 100644 packages/matrix-bot-common/src/markdown/index.ts create mode 100644 packages/matrix-bot-common/src/markdown/markdown-formatter.ts create mode 100644 packages/matrix-bot-common/src/message/index.ts create mode 100644 packages/matrix-bot-common/src/message/message.service.ts create mode 100644 packages/matrix-bot-common/src/session/index.ts create mode 100644 packages/matrix-bot-common/src/session/session-helper.ts create mode 100644 packages/matrix-bot-common/tsconfig.json diff --git a/packages/matrix-bot-common/CLAUDE.md b/packages/matrix-bot-common/CLAUDE.md new file mode 100644 index 000000000..47d38cbd2 --- /dev/null +++ b/packages/matrix-bot-common/CLAUDE.md @@ -0,0 +1,265 @@ +# @manacore/matrix-bot-common + +Shared utilities and base classes for Matrix bots. + +## Purpose + +This package consolidates common code patterns found across all 19 Matrix bots: + +- ~4,000 lines of duplicate code reduced to shared utilities +- Consistent behavior across all bots +- Easier maintenance and updates +- Type-safe helpers for common patterns + +## Available Components + +### BaseMatrixService + +Abstract base class that handles Matrix client lifecycle: + +```typescript +import { BaseMatrixService, MatrixBotConfig, MatrixRoomEvent } from '@manacore/matrix-bot-common'; + +@Injectable() +export class MyBotService extends BaseMatrixService { + constructor(configService: ConfigService) { + super(configService); + } + + protected getConfig(): MatrixBotConfig { + return { + homeserverUrl: this.configService.get('matrix.homeserverUrl'), + accessToken: this.configService.get('matrix.accessToken'), + storagePath: this.configService.get('matrix.storagePath'), + allowedRooms: this.configService.get('matrix.allowedRooms') || [], + }; + } + + protected async handleTextMessage( + roomId: string, + event: MatrixRoomEvent, + message: string, + sender: string + ) { + if (message === '!hello') { + await this.sendReply(roomId, event, 'Hello!'); + } + } + + // Optional: Handle voice messages + protected async handleAudioMessage(roomId: string, event: MatrixRoomEvent, sender: string) { + // Transcribe and process + } + + // Optional: Send intro on room join + protected getIntroductionMessage(): string | null { + return 'Hello! I am a bot.'; + } +} +``` + +**Provides:** + +- `onModuleInit()` - Client setup, storage, auto-join +- `onModuleDestroy()` - Graceful shutdown +- `sendMessage(roomId, message)` - Send markdown message +- `sendReply(roomId, event, message)` - Reply to event +- `sendNotice(roomId, message)` - Non-highlighted message +- `downloadMedia(mxcUrl)` - Download from Matrix +- `uploadMedia(buffer, contentType, filename)` - Upload to Matrix + +### HealthController + +Shared health endpoint: + +```typescript +import { HealthController, createHealthProvider } from '@manacore/matrix-bot-common'; + +@Module({ + controllers: [HealthController], + providers: [createHealthProvider('matrix-todo-bot')], +}) +export class AppModule {} +``` + +Returns: + +```json +{ + "status": "ok", + "service": "matrix-todo-bot", + "timestamp": "2026-02-01T12:00:00.000Z", + "uptime": 3600 +} +``` + +### MatrixMessageService + +Injectable service for message operations: + +```typescript +import { MatrixMessageService } from '@manacore/matrix-bot-common'; + +@Injectable() +export class MyService { + constructor(private messageService: MatrixMessageService) {} + + async doSomething(client: MatrixClient, roomId: string) { + await this.messageService.sendMessage(client, roomId, '**Bold** message'); + await this.messageService.sendReaction(client, roomId, eventId, '👍'); + await this.messageService.editMessage(client, roomId, eventId, 'Updated text'); + } +} +``` + +### KeywordCommandDetector + +Natural language command detection: + +```typescript +import { KeywordCommandDetector, COMMON_KEYWORDS } from '@manacore/matrix-bot-common'; + +const detector = new KeywordCommandDetector([ + ...COMMON_KEYWORDS, // hilfe, help, status, etc. + { keywords: ['liste', 'list', 'zeige'], command: 'list' }, + { keywords: ['neu', 'new', 'erstelle'], command: 'create' }, +]); + +const command = detector.detect('zeige mir alles'); // Returns 'list' +const command2 = detector.detect('random text'); // Returns null +``` + +### Markdown Utilities + +```typescript +import { markdownToHtml, formatNumberedList, formatBulletList } from '@manacore/matrix-bot-common'; + +const html = markdownToHtml('**bold** and *italic*'); +// 'bold and italic' + +const list = formatNumberedList(items, (item, i) => `${item.name} - ${item.status}`); +// '1. Item A - active\n2. Item B - done' +``` + +### SessionHelper + +Type-safe session data wrapper: + +```typescript +import { SessionHelper } from '@manacore/matrix-bot-common'; + +interface MySessionData { + currentItemId: string; + selectedModel: string; + itemList: string[]; +} + +const session = new SessionHelper(sessionService, matrixUserId); + +session.set('currentItemId', 'abc123'); +const itemId = session.get('currentItemId'); // string | null +session.delete('currentItemId'); + +if (session.isLoggedIn()) { + const token = session.getToken(); +} +``` + +### UserListMapper + +Number-based reference system: + +```typescript +import { UserListMapper } from '@manacore/matrix-bot-common'; + +const mapper = new UserListMapper(); + +// After listing contacts to user +mapper.setList(userId, contacts); + +// User says "!select 3" +const contact = mapper.getByNumber(userId, 3); + +// For items with id field +import { UserIdListMapper } from '@manacore/matrix-bot-common'; +const idMapper = new UserIdListMapper<{ id: string; name: string }>(); +const itemId = idMapper.getIdByNumber(userId, 2); +``` + +## Migration Guide + +### Before (duplicate code in each bot) + +```typescript +// matrix.service.ts - 50+ lines duplicated across 12 bots +private markdownToHtml(text: string): string { + return text + .replace(/\*\*(.+?)\*\*/g, '$1') + .replace(/\*(.+?)\*/g, '$1') + // ... +} + +private async sendReply(roomId: string, event: any, message: string) { + const reply = RichReply.createFor(roomId, event, message, this.markdownToHtml(message)); + reply.msgtype = 'm.text'; + await this.client.sendMessage(roomId, reply); +} +``` + +### After (using shared package) + +```typescript +import { markdownToHtml } from '@manacore/matrix-bot-common'; + +// Or extend BaseMatrixService which includes sendReply() +await this.sendReply(roomId, event, message); +``` + +## Installation + +```bash +pnpm --filter matrix-xxx-bot add @manacore/matrix-bot-common +``` + +## File Structure + +``` +packages/matrix-bot-common/ +├── src/ +│ ├── index.ts # Main exports +│ ├── base/ +│ │ ├── base-matrix.service.ts +│ │ ├── types.ts +│ │ └── index.ts +│ ├── health/ +│ │ ├── health.controller.ts +│ │ └── index.ts +│ ├── message/ +│ │ ├── message.service.ts +│ │ └── index.ts +│ ├── markdown/ +│ │ ├── markdown-formatter.ts +│ │ └── index.ts +│ ├── keywords/ +│ │ ├── keyword-detector.ts +│ │ └── index.ts +│ ├── session/ +│ │ ├── session-helper.ts +│ │ └── index.ts +│ └── list-mapper/ +│ ├── list-mapper.ts +│ └── index.ts +├── package.json +├── tsconfig.json +└── CLAUDE.md +``` + +## Development + +```bash +# Type check +pnpm --filter @manacore/matrix-bot-common type-check + +# Add to a bot +pnpm --filter matrix-xxx-bot add @manacore/matrix-bot-common +``` diff --git a/packages/matrix-bot-common/package.json b/packages/matrix-bot-common/package.json new file mode 100644 index 000000000..15218c53b --- /dev/null +++ b/packages/matrix-bot-common/package.json @@ -0,0 +1,38 @@ +{ + "name": "@manacore/matrix-bot-common", + "version": "0.1.0", + "private": true, + "description": "Shared utilities and base classes for Matrix bots", + "main": "./src/index.ts", + "types": "./src/index.ts", + "exports": { + ".": "./src/index.ts", + "./base": "./src/base/index.ts", + "./health": "./src/health/index.ts", + "./message": "./src/message/index.ts", + "./markdown": "./src/markdown/index.ts", + "./keywords": "./src/keywords/index.ts", + "./session": "./src/session/index.ts", + "./list-mapper": "./src/list-mapper/index.ts" + }, + "scripts": { + "type-check": "tsc --noEmit", + "clean": "rm -rf dist", + "lint": "eslint ." + }, + "dependencies": { + "@manacore/bot-services": "workspace:*", + "@nestjs/common": "^11.0.20", + "@nestjs/config": "^4.0.2", + "matrix-bot-sdk": "^0.7.1" + }, + "peerDependencies": { + "@nestjs/common": "^10.0.0 || ^11.0.0", + "@nestjs/config": "^3.0.0 || ^4.0.0", + "matrix-bot-sdk": "^0.7.0" + }, + "devDependencies": { + "@types/node": "^24.10.1", + "typescript": "^5.9.3" + } +} diff --git a/packages/matrix-bot-common/src/base/base-matrix.service.ts b/packages/matrix-bot-common/src/base/base-matrix.service.ts new file mode 100644 index 000000000..dbfa81333 --- /dev/null +++ b/packages/matrix-bot-common/src/base/base-matrix.service.ts @@ -0,0 +1,250 @@ +import { Logger, OnModuleInit, OnModuleDestroy } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { + MatrixClient, + SimpleFsStorageProvider, + AutojoinRoomsMixin, + RichReply, +} from 'matrix-bot-sdk'; +import * as path from 'path'; +import * as fs from 'fs'; +import { MatrixBotConfig, MatrixRoomEvent, isTextMessage, isAudioMessage } from './types'; +import { markdownToHtml } from '../markdown/markdown-formatter'; + +/** + * Abstract base class for Matrix bot services + * + * Provides common functionality: + * - Matrix client initialization + * - Room join handling + * - Message routing + * - Markdown message sending + * - Graceful shutdown + * + * @example + * ```typescript + * @Injectable() + * export class MyBotService extends BaseMatrixService { + * protected async handleTextMessage(roomId: string, event: MatrixRoomEvent, message: string) { + * if (message.startsWith('!hello')) { + * await this.sendReply(roomId, event, 'Hello!'); + * } + * } + * + * protected getConfig(): MatrixBotConfig { + * return { + * homeserverUrl: this.configService.get('matrix.homeserverUrl'), + * accessToken: this.configService.get('matrix.accessToken'), + * storagePath: this.configService.get('matrix.storagePath'), + * allowedRooms: this.configService.get('matrix.allowedRooms'), + * }; + * } + * } + * ``` + */ +export abstract class BaseMatrixService implements OnModuleInit, OnModuleDestroy { + protected readonly logger = new Logger(this.constructor.name); + protected client!: MatrixClient; + protected botUserId: string = ''; + protected readonly allowedRooms: string[]; + + constructor(protected configService: ConfigService) { + this.allowedRooms = this.getConfig().allowedRooms; + } + + /** + * Get Matrix configuration - must be implemented by subclass + */ + protected abstract getConfig(): MatrixBotConfig; + + /** + * Handle a text message - must be implemented by subclass + */ + protected abstract handleTextMessage( + roomId: string, + event: MatrixRoomEvent, + message: string, + sender: string + ): Promise; + + /** + * Handle an audio message (optional override) + */ + protected async handleAudioMessage( + _roomId: string, + _event: MatrixRoomEvent, + _sender: string + ): Promise { + // Default: no-op, override in subclass for voice support + } + + /** + * Get welcome/introduction message (optional override) + */ + protected getIntroductionMessage(): string | null { + return null; + } + + /** + * Initialize the Matrix client + */ + async onModuleInit(): Promise { + const config = this.getConfig(); + + if (!config.accessToken) { + this.logger.error('MATRIX_ACCESS_TOKEN is required'); + return; + } + + // Ensure storage directory exists + const storageDir = path.dirname(config.storagePath); + if (!fs.existsSync(storageDir)) { + fs.mkdirSync(storageDir, { recursive: true }); + this.logger.log(`Created storage directory: ${storageDir}`); + } + + // Initialize client + const storage = new SimpleFsStorageProvider(config.storagePath); + this.client = new MatrixClient(config.homeserverUrl, config.accessToken, storage); + + // Setup auto-join for allowed rooms + AutojoinRoomsMixin.setupOnClient(this.client); + + // Get bot user ID + this.botUserId = await this.client.getUserId(); + this.logger.log(`Bot user ID: ${this.botUserId}`); + + // Setup room join handler + this.client.on('room.join', async (roomId: string) => { + await this.onRoomJoin(roomId); + }); + + // Setup message handler + this.client.on('room.message', async (roomId: string, event: MatrixRoomEvent) => { + await this.onRoomMessage(roomId, event); + }); + + // Start the client + await this.client.start(); + this.logger.log('Matrix client started'); + } + + /** + * Graceful shutdown + */ + async onModuleDestroy(): Promise { + if (this.client) { + await this.client.stop(); + this.logger.log('Matrix client stopped'); + } + } + + /** + * Handle room join event + */ + protected async onRoomJoin(roomId: string): Promise { + this.logger.log(`Joined room: ${roomId}`); + + // Send introduction message if defined + const intro = this.getIntroductionMessage(); + if (intro) { + await this.sendMessage(roomId, intro); + } + } + + /** + * Handle incoming room message + */ + protected async onRoomMessage(roomId: string, event: MatrixRoomEvent): Promise { + // Ignore own messages + if (event.sender === this.botUserId) return; + + // Check room permissions + if (this.allowedRooms.length > 0 && !this.allowedRooms.includes(roomId)) { + return; + } + + try { + if (isTextMessage(event)) { + const message = event.content.body.trim(); + await this.handleTextMessage(roomId, event, message, event.sender); + } else if (isAudioMessage(event)) { + await this.handleAudioMessage(roomId, event, event.sender); + } + } catch (error) { + this.logger.error(`Error handling message: ${error}`); + await this.sendReply(roomId, event, '❌ Ein Fehler ist aufgetreten.'); + } + } + + /** + * Send a message to a room + */ + protected async sendMessage(roomId: string, message: string): Promise { + return this.client.sendMessage(roomId, { + msgtype: 'm.text', + body: message, + format: 'org.matrix.custom.html', + formatted_body: markdownToHtml(message), + }); + } + + /** + * Send a reply to an event + */ + protected async sendReply( + roomId: string, + event: MatrixRoomEvent, + message: string + ): Promise { + const reply = RichReply.createFor(roomId, event, message, markdownToHtml(message)); + reply.msgtype = 'm.text'; + return this.client.sendMessage(roomId, reply); + } + + /** + * Send a notice (non-highlighted message) + */ + protected async sendNotice(roomId: string, message: string): Promise { + return this.client.sendMessage(roomId, { + msgtype: 'm.notice', + body: message, + format: 'org.matrix.custom.html', + formatted_body: markdownToHtml(message), + }); + } + + /** + * Download media from Matrix + */ + protected async downloadMedia(mxcUrl: string): Promise { + const result = await this.client.downloadContent(mxcUrl); + return result.data; + } + + /** + * Upload media to Matrix + */ + protected async uploadMedia( + buffer: Buffer, + contentType: string, + filename: string + ): Promise { + return this.client.uploadContent(buffer, contentType, filename); + } + + /** + * Get the Matrix client (for advanced operations) + */ + protected getClient(): MatrixClient { + return this.client; + } + + /** + * Check if a room is allowed + */ + protected isRoomAllowed(roomId: string): boolean { + if (this.allowedRooms.length === 0) return true; + return this.allowedRooms.includes(roomId); + } +} diff --git a/packages/matrix-bot-common/src/base/index.ts b/packages/matrix-bot-common/src/base/index.ts new file mode 100644 index 000000000..81669d5c1 --- /dev/null +++ b/packages/matrix-bot-common/src/base/index.ts @@ -0,0 +1,10 @@ +export { BaseMatrixService } from './base-matrix.service'; +export { + type MatrixBotConfig, + type MatrixRoomEvent, + type MatrixMessageEvent, + isTextMessage, + isAudioMessage, + isImageMessage, + isFileMessage, +} from './types'; diff --git a/packages/matrix-bot-common/src/base/types.ts b/packages/matrix-bot-common/src/base/types.ts new file mode 100644 index 000000000..d611feb0d --- /dev/null +++ b/packages/matrix-bot-common/src/base/types.ts @@ -0,0 +1,75 @@ +/** + * Matrix bot configuration + */ +export interface MatrixBotConfig { + /** Matrix homeserver URL */ + homeserverUrl: string; + /** Bot access token */ + accessToken: string; + /** Path to store bot state */ + storagePath: string; + /** Allowed room IDs (empty = all rooms) */ + allowedRooms: string[]; +} + +/** + * Matrix room event + */ +export interface MatrixRoomEvent { + event_id: string; + type: string; + sender: string; + room_id: string; + origin_server_ts: number; + content: { + msgtype?: string; + body?: string; + format?: string; + formatted_body?: string; + url?: string; + info?: Record; + 'm.relates_to'?: { + 'm.in_reply_to'?: { event_id: string }; + rel_type?: string; + event_id?: string; + }; + }; +} + +/** + * Matrix message event (subset of room event) + */ +export interface MatrixMessageEvent extends MatrixRoomEvent { + content: MatrixRoomEvent['content'] & { + msgtype: string; + body: string; + }; +} + +/** + * Check if event is a text message + */ +export function isTextMessage(event: MatrixRoomEvent): event is MatrixMessageEvent { + return event.content?.msgtype === 'm.text' && typeof event.content?.body === 'string'; +} + +/** + * Check if event is an audio message + */ +export function isAudioMessage(event: MatrixRoomEvent): boolean { + return event.content?.msgtype === 'm.audio'; +} + +/** + * Check if event is an image message + */ +export function isImageMessage(event: MatrixRoomEvent): boolean { + return event.content?.msgtype === 'm.image'; +} + +/** + * Check if event is a file message + */ +export function isFileMessage(event: MatrixRoomEvent): boolean { + return event.content?.msgtype === 'm.file'; +} diff --git a/packages/matrix-bot-common/src/health/health.controller.ts b/packages/matrix-bot-common/src/health/health.controller.ts new file mode 100644 index 000000000..5412f564c --- /dev/null +++ b/packages/matrix-bot-common/src/health/health.controller.ts @@ -0,0 +1,58 @@ +import { Controller, Get, Inject, Optional } from '@nestjs/common'; + +export const HEALTH_SERVICE_NAME = 'HEALTH_SERVICE_NAME'; + +export interface HealthResponse { + status: 'ok' | 'error'; + service: string; + timestamp: string; + uptime?: number; + version?: string; +} + +/** + * Shared health controller for Matrix bots + * + * Returns standardized health check response. + * + * @example + * ```typescript + * // In app.module.ts + * @Module({ + * controllers: [HealthController], + * providers: [ + * { provide: HEALTH_SERVICE_NAME, useValue: 'matrix-todo-bot' } + * ], + * }) + * ``` + */ +@Controller('health') +export class HealthController { + private readonly startTime = Date.now(); + + constructor( + @Optional() + @Inject(HEALTH_SERVICE_NAME) + private readonly serviceName?: string + ) {} + + @Get() + check(): HealthResponse { + return { + status: 'ok', + service: this.serviceName || 'matrix-bot', + timestamp: new Date().toISOString(), + uptime: Math.floor((Date.now() - this.startTime) / 1000), + }; + } +} + +/** + * Create a health controller provider with service name + */ +export function createHealthProvider(serviceName: string) { + return { + provide: HEALTH_SERVICE_NAME, + useValue: serviceName, + }; +} diff --git a/packages/matrix-bot-common/src/health/index.ts b/packages/matrix-bot-common/src/health/index.ts new file mode 100644 index 000000000..20e33addb --- /dev/null +++ b/packages/matrix-bot-common/src/health/index.ts @@ -0,0 +1,6 @@ +export { + HealthController, + HEALTH_SERVICE_NAME, + createHealthProvider, + type HealthResponse, +} from './health.controller'; diff --git a/packages/matrix-bot-common/src/index.ts b/packages/matrix-bot-common/src/index.ts new file mode 100644 index 000000000..c77830fe0 --- /dev/null +++ b/packages/matrix-bot-common/src/index.ts @@ -0,0 +1,68 @@ +/** + * @manacore/matrix-bot-common + * + * Shared utilities and base classes for Matrix bots. + * Reduces code duplication across 19 Matrix bots. + * + * @example + * ```typescript + * import { + * BaseMatrixService, + * HealthController, + * MatrixMessageService, + * KeywordCommandDetector, + * markdownToHtml, + * SessionHelper, + * UserListMapper, + * } from '@manacore/matrix-bot-common'; + * ``` + */ + +// Base Matrix Service +export { + BaseMatrixService, + type MatrixBotConfig, + type MatrixRoomEvent, + type MatrixMessageEvent, + isTextMessage, + isAudioMessage, + isImageMessage, + isFileMessage, +} from './base'; + +// Health Controller +export { + HealthController, + HEALTH_SERVICE_NAME, + createHealthProvider, + type HealthResponse, +} from './health'; + +// Message Service +export { + MatrixMessageService, + type MatrixMessageContent, + type SendMessageOptions, +} from './message'; + +// Markdown Utilities +export { + markdownToHtml, + escapeHtml, + formatNumberedList, + formatBulletList, +} from './markdown'; + +// Keyword Detection +export { + KeywordCommandDetector, + COMMON_KEYWORDS, + type KeywordCommand, + type KeywordDetectorOptions, +} from './keywords'; + +// Session Helper +export { SessionHelper, createSessionHelper } from './session'; + +// List Mapper +export { UserListMapper, UserIdListMapper } from './list-mapper'; diff --git a/packages/matrix-bot-common/src/keywords/index.ts b/packages/matrix-bot-common/src/keywords/index.ts new file mode 100644 index 000000000..51e2686b9 --- /dev/null +++ b/packages/matrix-bot-common/src/keywords/index.ts @@ -0,0 +1,6 @@ +export { + KeywordCommandDetector, + COMMON_KEYWORDS, + type KeywordCommand, + type KeywordDetectorOptions, +} from './keyword-detector'; diff --git a/packages/matrix-bot-common/src/keywords/keyword-detector.ts b/packages/matrix-bot-common/src/keywords/keyword-detector.ts new file mode 100644 index 000000000..fb706c46a --- /dev/null +++ b/packages/matrix-bot-common/src/keywords/keyword-detector.ts @@ -0,0 +1,118 @@ +/** + * Keyword command mapping + */ +export interface KeywordCommand { + /** Keywords that trigger this command (lowercase) */ + keywords: string[]; + /** Command name to return when matched */ + command: string; +} + +/** + * Options for keyword detection + */ +export interface KeywordDetectorOptions { + /** Maximum message length to check (default: 60) */ + maxLength?: number; + /** Whether to match partial words (default: false) */ + partialMatch?: boolean; +} + +/** + * Detect commands from natural language keywords + * + * Used by Matrix bots to respond to natural language instead of just !commands. + * + * @example + * ```typescript + * const detector = new KeywordCommandDetector([ + * { keywords: ['hilfe', 'help'], command: 'help' }, + * { keywords: ['status', 'info'], command: 'status' }, + * ]); + * + * detector.detect('hilfe bitte'); // Returns 'help' + * detector.detect('was ist los'); // Returns null + * ``` + */ +export class KeywordCommandDetector { + private readonly maxLength: number; + private readonly partialMatch: boolean; + + constructor( + private readonly commands: KeywordCommand[], + options: KeywordDetectorOptions = {} + ) { + this.maxLength = options.maxLength ?? 60; + this.partialMatch = options.partialMatch ?? false; + } + + /** + * Detect a command from a message + * + * @param message - The user's message + * @returns The command name if matched, null otherwise + */ + detect(message: string): string | null { + const lowerMessage = message.toLowerCase().trim(); + + // Skip long messages (likely not commands) + if (lowerMessage.length > this.maxLength) { + return null; + } + + for (const { keywords, command } of this.commands) { + for (const keyword of keywords) { + if (this.matches(lowerMessage, keyword)) { + return command; + } + } + } + + return null; + } + + /** + * Check if message matches a keyword + */ + private matches(message: string, keyword: string): boolean { + // Exact match + if (message === keyword) { + return true; + } + + // Message starts with keyword followed by space + if (message.startsWith(keyword + ' ')) { + return true; + } + + // Partial match (keyword appears anywhere) + if (this.partialMatch && message.includes(keyword)) { + return true; + } + + return false; + } + + /** + * Add more commands dynamically + */ + addCommands(commands: KeywordCommand[]): void { + this.commands.push(...commands); + } + + /** + * Get all registered commands + */ + getCommands(): KeywordCommand[] { + return [...this.commands]; + } +} + +/** + * Common German/English keywords used across bots + */ +export const COMMON_KEYWORDS: KeywordCommand[] = [ + { keywords: ['hilfe', 'help', 'befehle', 'commands', '?'], command: 'help' }, + { keywords: ['status', 'info'], command: 'status' }, + { keywords: ['abbrechen', 'cancel', 'stop'], command: 'cancel' }, +]; diff --git a/packages/matrix-bot-common/src/list-mapper/index.ts b/packages/matrix-bot-common/src/list-mapper/index.ts new file mode 100644 index 000000000..f048338dd --- /dev/null +++ b/packages/matrix-bot-common/src/list-mapper/index.ts @@ -0,0 +1 @@ +export { UserListMapper, UserIdListMapper } from './list-mapper'; diff --git a/packages/matrix-bot-common/src/list-mapper/list-mapper.ts b/packages/matrix-bot-common/src/list-mapper/list-mapper.ts new file mode 100644 index 000000000..ba870430a --- /dev/null +++ b/packages/matrix-bot-common/src/list-mapper/list-mapper.ts @@ -0,0 +1,113 @@ +/** + * User list mapper for number-based reference system + * + * Allows users to reference items by number after listing them. + * Used by Matrix bots for commands like "!select 3" or "!delete 2". + * + * @example + * ```typescript + * const mapper = new UserListMapper(); + * + * // After showing a list to the user + * mapper.setList('@user:matrix.org', contacts); + * + * // User says "!select 3" + * const contact = mapper.getByNumber('@user:matrix.org', 3); + * ``` + */ +export class UserListMapper { + private lists: Map = new Map(); + + /** + * Store a list for a user + */ + setList(userId: string, items: T[]): void { + this.lists.set(userId, [...items]); + } + + /** + * Get an item by its 1-based number + * + * @param userId - The user ID + * @param number - 1-based index (as shown to user) + * @returns The item or null if invalid + */ + getByNumber(userId: string, number: number): T | null { + const items = this.lists.get(userId); + if (!items || number < 1 || number > items.length) { + return null; + } + return items[number - 1]; + } + + /** + * Get the full list for a user + */ + getList(userId: string): T[] { + return this.lists.get(userId) || []; + } + + /** + * Check if user has a stored list + */ + hasList(userId: string): boolean { + return this.lists.has(userId) && this.lists.get(userId)!.length > 0; + } + + /** + * Get the count of items in user's list + */ + getCount(userId: string): number { + return this.lists.get(userId)?.length || 0; + } + + /** + * Clear the list for a user + */ + clearList(userId: string): void { + this.lists.delete(userId); + } + + /** + * Clear all lists + */ + clearAll(): void { + this.lists.clear(); + } +} + +/** + * Extended list mapper that also stores IDs separately + * Useful when items have an id field that needs quick lookup + */ +export class UserIdListMapper extends UserListMapper { + private idMaps: Map> = new Map(); + + override setList(userId: string, items: T[]): void { + super.setList(userId, items); + + // Build ID map + const idMap = new Map(); + items.forEach((item, index) => { + idMap.set(index + 1, item.id); + }); + this.idMaps.set(userId, idMap); + } + + /** + * Get just the ID by number (without loading full item) + */ + getIdByNumber(userId: string, number: number): string | null { + return this.idMaps.get(userId)?.get(number) || null; + } + + override clearList(userId: string): void { + super.clearList(userId); + this.idMaps.delete(userId); + } + + override clearAll(): void { + super.clearAll(); + this.idMaps.clear(); + } +} diff --git a/packages/matrix-bot-common/src/markdown/index.ts b/packages/matrix-bot-common/src/markdown/index.ts new file mode 100644 index 000000000..b985125a7 --- /dev/null +++ b/packages/matrix-bot-common/src/markdown/index.ts @@ -0,0 +1,6 @@ +export { + markdownToHtml, + escapeHtml, + formatNumberedList, + formatBulletList, +} from './markdown-formatter'; diff --git a/packages/matrix-bot-common/src/markdown/markdown-formatter.ts b/packages/matrix-bot-common/src/markdown/markdown-formatter.ts new file mode 100644 index 000000000..1aeff3949 --- /dev/null +++ b/packages/matrix-bot-common/src/markdown/markdown-formatter.ts @@ -0,0 +1,53 @@ +/** + * Convert Markdown text to HTML for Matrix messages + * + * Supports: + * - **bold** -> bold + * - *italic* -> italic + * - ~~strikethrough~~ -> strikethrough + * - `code` -> code + * - Newlines ->
+ * + * @example + * ```typescript + * const html = markdownToHtml('**Hello** *world*'); + * // Returns: 'Hello world' + * ``` + */ +export function markdownToHtml(text: string): string { + return text + .replace(/\*\*(.+?)\*\*/g, '$1') + .replace(/\*(.+?)\*/g, '$1') + .replace(/~~(.+?)~~/g, '$1') + .replace(/`(.+?)`/g, '$1') + .replace(/\n/g, '
'); +} + +/** + * Escape HTML special characters to prevent XSS + */ +export function escapeHtml(text: string): string { + return text + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); +} + +/** + * Format a list of items as numbered markdown list + */ +export function formatNumberedList( + items: T[], + formatter: (item: T, index: number) => string +): string { + return items.map((item, i) => `${i + 1}. ${formatter(item, i)}`).join('\n'); +} + +/** + * Format a list of items as bullet markdown list + */ +export function formatBulletList(items: T[], formatter: (item: T) => string): string { + return items.map((item) => `• ${formatter(item)}`).join('\n'); +} diff --git a/packages/matrix-bot-common/src/message/index.ts b/packages/matrix-bot-common/src/message/index.ts new file mode 100644 index 000000000..2fcea7736 --- /dev/null +++ b/packages/matrix-bot-common/src/message/index.ts @@ -0,0 +1,5 @@ +export { + MatrixMessageService, + type MatrixMessageContent, + type SendMessageOptions, +} from './message.service'; diff --git a/packages/matrix-bot-common/src/message/message.service.ts b/packages/matrix-bot-common/src/message/message.service.ts new file mode 100644 index 000000000..07b5b8d25 --- /dev/null +++ b/packages/matrix-bot-common/src/message/message.service.ts @@ -0,0 +1,227 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { MatrixClient, RichReply } from 'matrix-bot-sdk'; +import { markdownToHtml } from '../markdown/markdown-formatter'; + +/** + * Message content for Matrix + */ +export interface MatrixMessageContent { + msgtype: string; + body: string; + format?: string; + formatted_body?: string; + 'm.relates_to'?: { + 'm.in_reply_to'?: { event_id: string }; + event_id?: string; + rel_type?: string; + }; +} + +/** + * Options for sending messages + */ +export interface SendMessageOptions { + /** Convert markdown to HTML (default: true) */ + markdown?: boolean; + /** Message type (default: 'm.text') */ + msgtype?: string; +} + +/** + * Shared message service for Matrix bots + * + * Provides standardized methods for sending messages, replies, and reactions. + * + * @example + * ```typescript + * const messageService = new MatrixMessageService(); + * + * // Send a simple message + * await messageService.sendMessage(client, roomId, 'Hello!'); + * + * // Send a reply to an event + * await messageService.sendReply(client, roomId, event, 'Thanks!'); + * + * // Send a reaction + * await messageService.sendReaction(client, roomId, eventId, '👍'); + * ``` + */ +@Injectable() +export class MatrixMessageService { + private readonly logger = new Logger(MatrixMessageService.name); + + /** + * Send a message to a room + */ + async sendMessage( + client: MatrixClient, + roomId: string, + message: string, + options: SendMessageOptions = {} + ): Promise { + const { markdown = true, msgtype = 'm.text' } = options; + + const content: MatrixMessageContent = { + msgtype, + body: message, + }; + + if (markdown) { + content.format = 'org.matrix.custom.html'; + content.formatted_body = markdownToHtml(message); + } + + return client.sendMessage(roomId, content); + } + + /** + * Send a reply to a specific event + */ + async sendReply( + client: MatrixClient, + roomId: string, + event: { event_id: string; content?: { body?: string } }, + message: string, + options: SendMessageOptions = {} + ): Promise { + const { markdown = true, msgtype = 'm.text' } = options; + + const htmlMessage = markdown ? markdownToHtml(message) : message; + const reply = RichReply.createFor(roomId, event, message, htmlMessage); + reply.msgtype = msgtype; + + return client.sendMessage(roomId, reply); + } + + /** + * Send a reaction to an event + */ + async sendReaction( + client: MatrixClient, + roomId: string, + eventId: string, + emoji: string + ): Promise { + return client.sendEvent(roomId, 'm.reaction', { + 'm.relates_to': { + rel_type: 'm.annotation', + event_id: eventId, + key: emoji, + }, + }); + } + + /** + * Send a notice (non-highlighted message) + */ + async sendNotice( + client: MatrixClient, + roomId: string, + message: string, + options: Omit = {} + ): Promise { + return this.sendMessage(client, roomId, message, { ...options, msgtype: 'm.notice' }); + } + + /** + * Send an image to a room + */ + async sendImage( + client: MatrixClient, + roomId: string, + mxcUrl: string, + filename: string, + info?: { w?: number; h?: number; mimetype?: string; size?: number } + ): Promise { + return client.sendMessage(roomId, { + msgtype: 'm.image', + body: filename, + url: mxcUrl, + info: info || {}, + }); + } + + /** + * Send a file to a room + */ + async sendFile( + client: MatrixClient, + roomId: string, + mxcUrl: string, + filename: string, + info?: { mimetype?: string; size?: number } + ): Promise { + return client.sendMessage(roomId, { + msgtype: 'm.file', + body: filename, + url: mxcUrl, + info: info || {}, + }); + } + + /** + * Edit an existing message + */ + async editMessage( + client: MatrixClient, + roomId: string, + originalEventId: string, + newMessage: string, + options: SendMessageOptions = {} + ): Promise { + const { markdown = true, msgtype = 'm.text' } = options; + + const content: MatrixMessageContent = { + msgtype, + body: `* ${newMessage}`, + 'm.relates_to': { + rel_type: 'm.replace', + event_id: originalEventId, + }, + }; + + if (markdown) { + content.format = 'org.matrix.custom.html'; + content.formatted_body = `* ${markdownToHtml(newMessage)}`; + } + + return client.sendMessage(roomId, { + ...content, + 'm.new_content': { + msgtype, + body: newMessage, + format: markdown ? 'org.matrix.custom.html' : undefined, + formatted_body: markdown ? markdownToHtml(newMessage) : undefined, + }, + }); + } + + /** + * Set room topic + */ + async setRoomTopic(client: MatrixClient, roomId: string, topic: string): Promise { + await client.sendStateEvent(roomId, 'm.room.topic', '', { topic }); + } + + /** + * Pin a message in a room + */ + async pinMessage(client: MatrixClient, roomId: string, eventId: string): Promise { + try { + // Get current pinned events + const pinnedEvents = await client + .getRoomStateEvent(roomId, 'm.room.pinned_events', '') + .catch(() => ({ pinned: [] })); + + const pinned: string[] = pinnedEvents?.pinned || []; + + // Add new event if not already pinned + if (!pinned.includes(eventId)) { + pinned.push(eventId); + await client.sendStateEvent(roomId, 'm.room.pinned_events', '', { pinned }); + } + } catch (error) { + this.logger.warn(`Failed to pin message: ${error}`); + } + } +} diff --git a/packages/matrix-bot-common/src/session/index.ts b/packages/matrix-bot-common/src/session/index.ts new file mode 100644 index 000000000..ac6d69b3f --- /dev/null +++ b/packages/matrix-bot-common/src/session/index.ts @@ -0,0 +1 @@ +export { SessionHelper, createSessionHelper } from './session-helper'; diff --git a/packages/matrix-bot-common/src/session/session-helper.ts b/packages/matrix-bot-common/src/session/session-helper.ts new file mode 100644 index 000000000..3ed652824 --- /dev/null +++ b/packages/matrix-bot-common/src/session/session-helper.ts @@ -0,0 +1,85 @@ +import { SessionService } from '@manacore/bot-services'; + +/** + * Typed session helper for bot-specific session data + * + * Provides type-safe access to session data stored in SessionService. + * + * @example + * ```typescript + * interface ChatSessionData { + * currentConversationId: string; + * selectedModelId: string; + * conversationList: string[]; + * } + * + * const session = new SessionHelper(sessionService, matrixUserId); + * session.set('currentConversationId', 'abc123'); + * const convId = session.get('currentConversationId'); // string | null + * ``` + */ +export class SessionHelper> { + constructor( + private readonly sessionService: SessionService, + private readonly userId: string + ) {} + + /** + * Set a session value + */ + set(key: K, value: T[K]): void { + this.sessionService.setSessionData(this.userId, key as string, value); + } + + /** + * Get a session value + */ + get(key: K): T[K] | null { + return this.sessionService.getSessionData(this.userId, key as string); + } + + /** + * Delete a session value + */ + delete(key: K): void { + this.sessionService.setSessionData(this.userId, key as string, null); + } + + /** + * Check if a session value exists + */ + has(key: K): boolean { + return this.get(key) !== null; + } + + /** + * Get the underlying user ID + */ + getUserId(): string { + return this.userId; + } + + /** + * Check if user is logged in + */ + isLoggedIn(): boolean { + return this.sessionService.isLoggedIn(this.userId); + } + + /** + * Get JWT token for API calls + */ + getToken(): string | null { + return this.sessionService.getToken(this.userId); + } +} + +/** + * Factory function to create session helper + */ +export function createSessionHelper>( + sessionService: SessionService, + userId: string +): SessionHelper { + return new SessionHelper(sessionService, userId); +} diff --git a/packages/matrix-bot-common/tsconfig.json b/packages/matrix-bot-common/tsconfig.json new file mode 100644 index 000000000..71802e564 --- /dev/null +++ b/packages/matrix-bot-common/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "module": "commonjs", + "declaration": true, + "removeComments": true, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "allowSyntheticDefaultImports": true, + "target": "ES2022", + "sourceMap": true, + "outDir": "./dist", + "baseUrl": "./", + "incremental": true, + "skipLibCheck": true, + "strict": true, + "strictNullChecks": true, + "noImplicitAny": true, + "strictBindCallApply": true, + "forceConsistentCasingInFileNames": true, + "noFallthroughCasesInSwitch": true, + "esModuleInterop": true + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4dac649d6..01c44264f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -137,7 +137,7 @@ importers: devDependencies: '@nestjs/cli': specifier: ^10.4.9 - version: 10.4.9(esbuild@0.19.12) + version: 10.4.9 '@nestjs/schematics': specifier: ^10.2.3 version: 10.2.3(chokidar@3.6.0)(typescript@5.9.3) @@ -185,10 +185,10 @@ importers: version: 0.5.21 ts-jest: specifier: ^29.2.5 - version: 29.4.5(@babel/core@7.28.5)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.28.5))(esbuild@0.19.12)(jest-util@30.2.0)(jest@29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))(typescript@5.9.3) + version: 29.4.5(@babel/core@7.28.5)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.28.5))(jest-util@30.2.0)(jest@29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))(typescript@5.9.3) ts-loader: specifier: ^9.5.1 - version: 9.5.4(typescript@5.9.3)(webpack@5.97.1(esbuild@0.19.12)) + version: 9.5.4(typescript@5.9.3)(webpack@5.100.2) ts-node: specifier: ^10.9.2 version: 10.9.2(@types/node@22.19.1)(typescript@5.9.3) @@ -212,14 +212,14 @@ importers: version: link:../../../../packages/shared-landing-ui astro: specifier: ^5.16.0 - version: 5.16.0(@netlify/blobs@10.4.1)(@types/node@20.19.25)(ioredis@5.9.2)(jiti@1.21.7)(lightningcss@1.30.2)(rollup@4.53.3)(terser@5.44.1)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1) + version: 5.16.0(@netlify/blobs@10.4.1)(@types/node@20.19.25)(ioredis@5.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.53.3)(terser@5.44.1)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1) typescript: specifier: ^5.9.2 version: 5.9.3 devDependencies: '@astrojs/tailwind': specifier: ^6.0.2 - version: 6.0.2(astro@5.16.0(@netlify/blobs@10.4.1)(@types/node@20.19.25)(ioredis@5.9.2)(jiti@1.21.7)(lightningcss@1.30.2)(rollup@4.53.3)(terser@5.44.1)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1))(tailwindcss@3.4.18(tsx@4.20.6)(yaml@2.8.1))(ts-node@10.9.2(@types/node@20.19.25)(typescript@5.9.3)) + version: 6.0.2(astro@5.16.0(@netlify/blobs@10.4.1)(@types/node@20.19.25)(ioredis@5.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.53.3)(terser@5.44.1)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1))(tailwindcss@3.4.18(tsx@4.20.6)(yaml@2.8.1))(ts-node@10.9.2(@types/node@20.19.25)(typescript@5.9.3)) '@tailwindcss/typography': specifier: ^0.5.18 version: 0.5.19(tailwindcss@3.4.18(tsx@4.20.6)(yaml@2.8.1)) @@ -228,13 +228,13 @@ importers: version: 20.19.25 eslint: specifier: ^9.0.0 - version: 9.39.1(jiti@1.21.7) + version: 9.39.1(jiti@2.6.1) eslint-config-prettier: specifier: ^9.1.0 - version: 9.1.2(eslint@9.39.1(jiti@1.21.7)) + version: 9.1.2(eslint@9.39.1(jiti@2.6.1)) eslint-plugin-astro: specifier: ^1.0.0 - version: 1.5.0(eslint@9.39.1(jiti@1.21.7)) + version: 1.5.0(eslint@9.39.1(jiti@2.6.1)) prettier: specifier: ^3.6.2 version: 3.6.2 @@ -609,19 +609,19 @@ importers: version: 18.3.27 '@typescript-eslint/eslint-plugin': specifier: ^7.7.0 - version: 7.18.0(@typescript-eslint/parser@7.18.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3) + version: 7.18.0(@typescript-eslint/parser@7.18.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3))(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3) '@typescript-eslint/parser': specifier: ^7.7.0 - version: 7.18.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3) + version: 7.18.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3) dotenv: specifier: ^16.4.7 version: 16.6.1 eslint: specifier: ^9.39.1 - version: 9.39.1(jiti@2.6.1) + version: 9.39.1(jiti@1.21.7) eslint-config-universe: specifier: ^12.0.1 - version: 12.1.0(@types/eslint@9.6.1)(eslint@9.39.1(jiti@2.6.1))(prettier@3.6.2)(typescript@5.3.3) + version: 12.1.0(@types/eslint@9.6.1)(eslint@9.39.1(jiti@1.21.7))(prettier@3.6.2)(typescript@5.3.3) prettier: specifier: ^3.2.5 version: 3.6.2 @@ -2333,7 +2333,7 @@ importers: version: 0.5.21 ts-jest: specifier: ^29.2.5 - version: 29.4.5(@babel/core@7.28.5)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.28.5))(esbuild@0.19.12)(jest-util@30.2.0)(jest@29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))(typescript@5.9.3) + version: 29.4.5(@babel/core@7.28.5)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.28.5))(jest-util@30.2.0)(jest@29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))(typescript@5.9.3) ts-loader: specifier: ^9.5.1 version: 9.5.4(typescript@5.9.3)(webpack@5.100.2) @@ -4594,6 +4594,28 @@ importers: specifier: ^5.7.3 version: 5.9.3 + packages/matrix-bot-common: + dependencies: + '@manacore/bot-services': + specifier: workspace:* + version: link:../bot-services + '@nestjs/common': + specifier: ^11.0.20 + version: 11.1.9(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/config': + specifier: ^4.0.2 + version: 4.0.2(@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(rxjs@7.8.2) + matrix-bot-sdk: + specifier: ^0.7.1 + version: 0.7.1 + devDependencies: + '@types/node': + specifier: ^24.10.1 + version: 24.10.1 + typescript: + specifier: ^5.9.3 + version: 5.9.3 + packages/notify-client: devDependencies: '@nestjs/common': @@ -5208,7 +5230,7 @@ importers: version: 1.57.0 jest: specifier: ^29.0.0 - version: 29.7.0(@types/node@24.10.1) + version: 29.7.0(@types/node@24.10.1)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)) vitest: specifier: ^3.0.0 version: 3.2.4(@types/debug@4.1.12)(@types/node@24.10.1)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(jiti@2.6.1)(jsdom@27.2.0)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1) @@ -5340,7 +5362,7 @@ importers: version: 29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)) ts-jest: specifier: ^29.2.5 - version: 29.4.5(@babel/core@7.28.5)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.28.5))(esbuild@0.19.12)(jest-util@30.2.0)(jest@29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))(typescript@5.9.3) + version: 29.4.5(@babel/core@7.28.5)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.28.5))(jest-util@30.2.0)(jest@29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))(typescript@5.9.3) ts-node: specifier: ^10.9.2 version: 10.9.2(@types/node@22.19.1)(typescript@5.9.3) @@ -5500,7 +5522,7 @@ importers: version: 7.1.4 ts-jest: specifier: ^29.2.5 - version: 29.4.5(@babel/core@7.28.5)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.28.5))(esbuild@0.19.12)(jest-util@30.2.0)(jest@29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))(typescript@5.9.3) + version: 29.4.5(@babel/core@7.28.5)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.28.5))(jest-util@30.2.0)(jest@29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))(typescript@5.9.3) ts-loader: specifier: ^9.5.1 version: 9.5.4(typescript@5.9.3)(webpack@5.100.2) @@ -5612,7 +5634,7 @@ importers: version: 29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)) ts-jest: specifier: ^29.2.5 - version: 29.4.5(@babel/core@7.28.5)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.28.5))(esbuild@0.19.12)(jest-util@30.2.0)(jest@29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))(typescript@5.9.3) + version: 29.4.5(@babel/core@7.28.5)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.28.5))(jest-util@30.2.0)(jest@29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))(typescript@5.9.3) ts-node: specifier: ^10.9.2 version: 10.9.2(@types/node@22.19.1)(typescript@5.9.3) @@ -5712,7 +5734,7 @@ importers: version: 29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)) ts-jest: specifier: ^29.2.5 - version: 29.4.5(@babel/core@7.28.5)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.28.5))(esbuild@0.19.12)(jest-util@30.2.0)(jest@29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))(typescript@5.9.3) + version: 29.4.5(@babel/core@7.28.5)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.28.5))(jest-util@30.2.0)(jest@29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))(typescript@5.9.3) ts-node: specifier: ^10.9.2 version: 10.9.2(@types/node@22.19.1)(typescript@5.9.3) @@ -5785,7 +5807,7 @@ importers: version: 29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)) ts-jest: specifier: ^29.2.5 - version: 29.4.5(@babel/core@7.28.5)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.28.5))(esbuild@0.19.12)(jest-util@30.2.0)(jest@29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))(typescript@5.9.3) + version: 29.4.5(@babel/core@7.28.5)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.28.5))(jest-util@30.2.0)(jest@29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))(typescript@5.9.3) ts-node: specifier: ^10.9.2 version: 10.9.2(@types/node@22.19.1)(typescript@5.9.3) @@ -8891,7 +8913,7 @@ packages: '@expo/bunyan@4.0.1': resolution: {integrity: sha512-+Lla7nYSiHZirgK+U/uYzsLv/X+HaJienbD5AKX1UQZHYfWaP+9uuQluRB4GrEVWF0GZ7vEVp/jzaOT9k/SQlg==} - engines: {'0': node >=0.10.0} + engines: {node: '>=0.10.0'} '@expo/cli@0.22.26': resolution: {integrity: sha512-I689wc8Fn/AX7aUGiwrh3HnssiORMJtR2fpksX+JIe8Cj/EDleblYMSwRPd0025wrwOV9UN1KM/RuEt/QjCS3Q==} @@ -23341,16 +23363,6 @@ snapshots: transitivePeerDependencies: - ts-node - '@astrojs/tailwind@6.0.2(astro@5.16.0(@netlify/blobs@10.4.1)(@types/node@20.19.25)(ioredis@5.9.2)(jiti@1.21.7)(lightningcss@1.30.2)(rollup@4.53.3)(terser@5.44.1)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1))(tailwindcss@3.4.18(tsx@4.20.6)(yaml@2.8.1))(ts-node@10.9.2(@types/node@20.19.25)(typescript@5.9.3))': - dependencies: - astro: 5.16.0(@netlify/blobs@10.4.1)(@types/node@20.19.25)(ioredis@5.9.2)(jiti@1.21.7)(lightningcss@1.30.2)(rollup@4.53.3)(terser@5.44.1)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1) - autoprefixer: 10.4.22(postcss@8.5.6) - postcss: 8.5.6 - postcss-load-config: 4.0.2(postcss@8.5.6)(ts-node@10.9.2(@types/node@20.19.25)(typescript@5.9.3)) - tailwindcss: 3.4.18(tsx@4.20.6)(yaml@2.8.1) - transitivePeerDependencies: - - ts-node - '@astrojs/tailwind@6.0.2(astro@5.16.0(@netlify/blobs@10.4.1)(@types/node@20.19.25)(ioredis@5.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.53.3)(terser@5.44.1)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1))(tailwindcss@3.4.18(tsx@4.20.6)(yaml@2.8.1))(ts-node@10.9.2(@types/node@20.19.25)(typescript@5.9.3))': dependencies: astro: 5.16.0(@netlify/blobs@10.4.1)(@types/node@20.19.25)(ioredis@5.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.53.3)(terser@5.44.1)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1) @@ -26420,7 +26432,7 @@ snapshots: wrap-ansi: 7.0.0 ws: 8.18.3 optionalDependencies: - expo-router: 6.0.15(7mqaurqidri6vkknnsci36yp4e) + expo-router: 6.0.15(vmxlpuhz6xqbe2ee7fdabyqx3y) react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0) transitivePeerDependencies: - '@modelcontextprotocol/sdk' @@ -27728,7 +27740,7 @@ snapshots: jest-util: 30.2.0 slash: 3.0.0 - '@jest/core@29.7.0': + '@jest/core@29.7.0(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))': dependencies: '@jest/console': 29.7.0 '@jest/reporters': 29.7.0 @@ -27742,7 +27754,7 @@ snapshots: exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@22.19.1) + jest-config: 29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -27763,7 +27775,7 @@ snapshots: - supports-color - ts-node - '@jest/core@29.7.0(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))': + '@jest/core@29.7.0(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3))': dependencies: '@jest/console': 29.7.0 '@jest/reporters': 29.7.0 @@ -27777,7 +27789,7 @@ snapshots: exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)) + jest-config: 29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -28316,32 +28328,6 @@ snapshots: - uglify-js - webpack-cli - '@nestjs/cli@10.4.9(esbuild@0.19.12)': - dependencies: - '@angular-devkit/core': 17.3.11(chokidar@3.6.0) - '@angular-devkit/schematics': 17.3.11(chokidar@3.6.0) - '@angular-devkit/schematics-cli': 17.3.11(chokidar@3.6.0) - '@nestjs/schematics': 10.2.3(chokidar@3.6.0)(typescript@5.7.2) - chalk: 4.1.2 - chokidar: 3.6.0 - cli-table3: 0.6.5 - commander: 4.1.1 - fork-ts-checker-webpack-plugin: 9.0.2(typescript@5.7.2)(webpack@5.97.1(esbuild@0.19.12)) - glob: 10.4.5 - inquirer: 8.2.6 - node-emoji: 1.11.0 - ora: 5.4.1 - tree-kill: 1.2.2 - tsconfig-paths: 4.2.0 - tsconfig-paths-webpack-plugin: 4.2.0 - typescript: 5.7.2 - webpack: 5.97.1(esbuild@0.19.12) - webpack-node-externals: 3.0.0 - transitivePeerDependencies: - - esbuild - - uglify-js - - webpack-cli - '@nestjs/cli@10.4.9(esbuild@0.27.0)': dependencies: '@angular-devkit/core': 17.3.11(chokidar@3.6.0) @@ -31937,19 +31923,6 @@ snapshots: picocolors: 1.1.1 pretty-format: 27.5.1 - '@testing-library/react-native@13.3.3(jest@29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react-test-renderer@19.1.0(react@19.1.0))(react@19.1.0)': - dependencies: - jest-matcher-utils: 30.2.0 - picocolors: 1.1.1 - pretty-format: 30.2.0 - react: 19.1.0 - react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0) - react-test-renderer: 19.1.0(react@19.1.0) - redent: 3.0.0 - optionalDependencies: - jest: 29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)) - optional: true - '@testing-library/react-native@13.3.3(jest@30.2.0(@types/node@20.19.25)(esbuild-register@3.6.0(esbuild@0.27.0)))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react-test-renderer@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: jest-matcher-utils: 30.2.0 @@ -32520,16 +32493,16 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3)': + '@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3))(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 6.21.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3) + '@typescript-eslint/parser': 6.21.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3) '@typescript-eslint/scope-manager': 6.21.0 - '@typescript-eslint/type-utils': 6.21.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3) - '@typescript-eslint/utils': 6.21.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3) + '@typescript-eslint/type-utils': 6.21.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3) + '@typescript-eslint/utils': 6.21.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3) '@typescript-eslint/visitor-keys': 6.21.0 debug: 4.4.3 - eslint: 9.39.1(jiti@2.6.1) + eslint: 9.39.1(jiti@1.21.7) graphemer: 1.4.0 ignore: 5.3.2 natural-compare: 1.4.0 @@ -32578,15 +32551,15 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@7.18.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3)': + '@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@7.18.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3))(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 7.18.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3) + '@typescript-eslint/parser': 7.18.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3) '@typescript-eslint/scope-manager': 7.18.0 - '@typescript-eslint/type-utils': 7.18.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3) - '@typescript-eslint/utils': 7.18.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3) + '@typescript-eslint/type-utils': 7.18.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3) + '@typescript-eslint/utils': 7.18.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3) '@typescript-eslint/visitor-keys': 7.18.0 - eslint: 9.39.1(jiti@2.6.1) + eslint: 9.39.1(jiti@1.21.7) graphemer: 1.4.0 ignore: 5.3.2 natural-compare: 1.4.0 @@ -32678,14 +32651,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@6.21.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3)': + '@typescript-eslint/parser@6.21.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3)': dependencies: '@typescript-eslint/scope-manager': 6.21.0 '@typescript-eslint/types': 6.21.0 '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.3.3) '@typescript-eslint/visitor-keys': 6.21.0 debug: 4.4.3 - eslint: 9.39.1(jiti@2.6.1) + eslint: 9.39.1(jiti@1.21.7) optionalDependencies: typescript: 5.3.3 transitivePeerDependencies: @@ -32717,14 +32690,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@7.18.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3)': + '@typescript-eslint/parser@7.18.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3)': dependencies: '@typescript-eslint/scope-manager': 7.18.0 '@typescript-eslint/types': 7.18.0 '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.3.3) '@typescript-eslint/visitor-keys': 7.18.0 debug: 4.4.3 - eslint: 9.39.1(jiti@2.6.1) + eslint: 9.39.1(jiti@1.21.7) optionalDependencies: typescript: 5.3.3 transitivePeerDependencies: @@ -32850,12 +32823,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/type-utils@6.21.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3)': + '@typescript-eslint/type-utils@6.21.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3)': dependencies: '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.3.3) - '@typescript-eslint/utils': 6.21.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3) + '@typescript-eslint/utils': 6.21.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3) debug: 4.4.3 - eslint: 9.39.1(jiti@2.6.1) + eslint: 9.39.1(jiti@1.21.7) ts-api-utils: 1.4.3(typescript@5.3.3) optionalDependencies: typescript: 5.3.3 @@ -32886,12 +32859,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/type-utils@7.18.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3)': + '@typescript-eslint/type-utils@7.18.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3)': dependencies: '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.3.3) - '@typescript-eslint/utils': 7.18.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3) + '@typescript-eslint/utils': 7.18.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3) debug: 4.4.3 - eslint: 9.39.1(jiti@2.6.1) + eslint: 9.39.1(jiti@1.21.7) ts-api-utils: 1.4.3(typescript@5.3.3) optionalDependencies: typescript: 5.3.3 @@ -33073,15 +33046,15 @@ snapshots: - supports-color - typescript - '@typescript-eslint/utils@6.21.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3)': + '@typescript-eslint/utils@6.21.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3)': dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@2.6.1)) + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@1.21.7)) '@types/json-schema': 7.0.15 '@types/semver': 7.7.1 '@typescript-eslint/scope-manager': 6.21.0 '@typescript-eslint/types': 6.21.0 '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.3.3) - eslint: 9.39.1(jiti@2.6.1) + eslint: 9.39.1(jiti@1.21.7) semver: 7.7.3 transitivePeerDependencies: - supports-color @@ -33112,13 +33085,13 @@ snapshots: - supports-color - typescript - '@typescript-eslint/utils@7.18.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3)': + '@typescript-eslint/utils@7.18.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3)': dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@2.6.1)) + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@1.21.7)) '@typescript-eslint/scope-manager': 7.18.0 '@typescript-eslint/types': 7.18.0 '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.3.3) - eslint: 9.39.1(jiti@2.6.1) + eslint: 9.39.1(jiti@1.21.7) transitivePeerDependencies: - supports-color - typescript @@ -34017,108 +33990,6 @@ snapshots: transitivePeerDependencies: - supports-color - astro@5.16.0(@netlify/blobs@10.4.1)(@types/node@20.19.25)(ioredis@5.9.2)(jiti@1.21.7)(lightningcss@1.30.2)(rollup@4.53.3)(terser@5.44.1)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1): - dependencies: - '@astrojs/compiler': 2.13.0 - '@astrojs/internal-helpers': 0.7.5 - '@astrojs/markdown-remark': 6.3.9 - '@astrojs/telemetry': 3.3.0 - '@capsizecss/unpack': 3.0.1 - '@oslojs/encoding': 1.1.0 - '@rollup/pluginutils': 5.3.0(rollup@4.53.3) - acorn: 8.15.0 - aria-query: 5.3.2 - axobject-query: 4.1.0 - boxen: 8.0.1 - ci-info: 4.3.1 - clsx: 2.1.1 - common-ancestor-path: 1.0.1 - cookie: 1.1.0 - cssesc: 3.0.0 - debug: 4.4.3 - deterministic-object-hash: 2.0.2 - devalue: 5.5.0 - diff: 5.2.0 - dlv: 1.1.3 - dset: 3.1.4 - es-module-lexer: 1.7.0 - esbuild: 0.25.12 - estree-walker: 3.0.3 - flattie: 1.1.1 - fontace: 0.3.1 - github-slugger: 2.0.0 - html-escaper: 3.0.3 - http-cache-semantics: 4.2.0 - import-meta-resolve: 4.2.0 - js-yaml: 4.1.1 - magic-string: 0.30.21 - magicast: 0.5.1 - mrmime: 2.0.1 - neotraverse: 0.6.18 - p-limit: 6.2.0 - p-queue: 8.1.1 - package-manager-detector: 1.5.0 - piccolore: 0.1.3 - picomatch: 4.0.3 - prompts: 2.4.2 - rehype: 13.0.2 - semver: 7.7.3 - shiki: 3.15.0 - smol-toml: 1.5.2 - svgo: 4.0.0 - tinyexec: 1.0.2 - tinyglobby: 0.2.15 - tsconfck: 3.1.6(typescript@5.9.3) - ultrahtml: 1.6.0 - unifont: 0.6.0 - unist-util-visit: 5.0.0 - unstorage: 1.17.3(@netlify/blobs@10.4.1)(ioredis@5.9.2) - vfile: 6.0.3 - vite: 6.4.1(@types/node@20.19.25)(jiti@1.21.7)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1) - vitefu: 1.1.1(vite@6.4.1(@types/node@20.19.25)(jiti@1.21.7)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1)) - xxhash-wasm: 1.1.0 - yargs-parser: 21.1.1 - yocto-spinner: 0.2.3 - zod: 3.25.76 - zod-to-json-schema: 3.25.0(zod@3.25.76) - zod-to-ts: 1.2.0(typescript@5.9.3)(zod@3.25.76) - optionalDependencies: - sharp: 0.34.5 - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@netlify/blobs' - - '@planetscale/database' - - '@types/node' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/functions' - - '@vercel/kv' - - aws4fetch - - db0 - - idb-keyval - - ioredis - - jiti - - less - - lightningcss - - rollup - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser - - tsx - - typescript - - uploadthing - - yaml - astro@5.16.0(@netlify/blobs@10.4.1)(@types/node@20.19.25)(ioredis@5.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.53.3)(terser@5.44.1)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1): dependencies: '@astrojs/compiler': 2.13.0 @@ -35405,13 +35276,13 @@ snapshots: - supports-color - ts-node - create-jest@29.7.0(@types/node@24.10.1): + create-jest@29.7.0(@types/node@24.10.1)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)): dependencies: '@jest/types': 29.6.3 chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@24.10.1) + jest-config: 29.7.0(@types/node@24.10.1)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)) jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -36515,11 +36386,6 @@ snapshots: escape-string-regexp@5.0.0: {} - eslint-compat-utils@0.6.5(eslint@9.39.1(jiti@1.21.7)): - dependencies: - eslint: 9.39.1(jiti@1.21.7) - semver: 7.7.3 - eslint-compat-utils@0.6.5(eslint@9.39.1(jiti@2.6.1)): dependencies: eslint: 9.39.1(jiti@2.6.1) @@ -36530,9 +36396,9 @@ snapshots: '@typescript-eslint/eslint-plugin': 8.48.1(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) '@typescript-eslint/parser': 8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) eslint: 9.39.1(jiti@2.6.1) - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@2.6.1)) eslint-plugin-expo: 1.0.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@2.6.1)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1)) eslint-plugin-react: 7.37.5(eslint@9.39.1(jiti@2.6.1)) eslint-plugin-react-hooks: 5.2.0(eslint@9.39.1(jiti@2.6.1)) globals: 16.5.0 @@ -36547,9 +36413,9 @@ snapshots: '@typescript-eslint/eslint-plugin': 8.48.1(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.8.3) '@typescript-eslint/parser': 8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.8.3) eslint: 9.39.1(jiti@2.6.1) - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@2.6.1)) eslint-plugin-expo: 0.1.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.8.3) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@2.6.1)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.1(jiti@2.6.1)) eslint-plugin-react: 7.37.5(eslint@9.39.1(jiti@2.6.1)) eslint-plugin-react-hooks: 5.2.0(eslint@9.39.1(jiti@2.6.1)) globals: 16.5.0 @@ -36567,14 +36433,14 @@ snapshots: dependencies: eslint: 8.57.1 + eslint-config-prettier@8.10.2(eslint@9.39.1(jiti@1.21.7)): + dependencies: + eslint: 9.39.1(jiti@1.21.7) + eslint-config-prettier@8.10.2(eslint@9.39.1(jiti@2.6.1)): dependencies: eslint: 9.39.1(jiti@2.6.1) - eslint-config-prettier@9.1.2(eslint@9.39.1(jiti@1.21.7)): - dependencies: - eslint: 9.39.1(jiti@1.21.7) - eslint-config-prettier@9.1.2(eslint@9.39.1(jiti@2.6.1)): dependencies: eslint: 9.39.1(jiti@2.6.1) @@ -36599,17 +36465,17 @@ snapshots: - supports-color - typescript - eslint-config-universe@12.1.0(@types/eslint@9.6.1)(eslint@9.39.1(jiti@2.6.1))(prettier@3.6.2)(typescript@5.3.3): + eslint-config-universe@12.1.0(@types/eslint@9.6.1)(eslint@9.39.1(jiti@1.21.7))(prettier@3.6.2)(typescript@5.3.3): dependencies: - '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3) - '@typescript-eslint/parser': 6.21.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3) - eslint: 9.39.1(jiti@2.6.1) - eslint-config-prettier: 8.10.2(eslint@9.39.1(jiti@2.6.1)) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@6.21.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3))(eslint@9.39.1(jiti@2.6.1)) - eslint-plugin-node: 11.1.0(eslint@9.39.1(jiti@2.6.1)) - eslint-plugin-prettier: 5.5.4(@types/eslint@9.6.1)(eslint-config-prettier@8.10.2(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1))(prettier@3.6.2) - eslint-plugin-react: 7.37.5(eslint@9.39.1(jiti@2.6.1)) - eslint-plugin-react-hooks: 4.6.2(eslint@9.39.1(jiti@2.6.1)) + '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3))(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3) + '@typescript-eslint/parser': 6.21.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3) + eslint: 9.39.1(jiti@1.21.7) + eslint-config-prettier: 8.10.2(eslint@9.39.1(jiti@1.21.7)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@6.21.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3))(eslint@9.39.1(jiti@1.21.7)) + eslint-plugin-node: 11.1.0(eslint@9.39.1(jiti@1.21.7)) + eslint-plugin-prettier: 5.5.4(@types/eslint@9.6.1)(eslint-config-prettier@8.10.2(eslint@9.39.1(jiti@1.21.7)))(eslint@9.39.1(jiti@1.21.7))(prettier@3.6.2) + eslint-plugin-react: 7.37.5(eslint@9.39.1(jiti@1.21.7)) + eslint-plugin-react-hooks: 4.6.2(eslint@9.39.1(jiti@1.21.7)) optionalDependencies: prettier: 3.6.2 transitivePeerDependencies: @@ -36647,7 +36513,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1)): + eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@2.6.1)): dependencies: '@nolyfill/is-core-module': 1.0.39 debug: 4.4.3 @@ -36658,22 +36524,7 @@ snapshots: tinyglobby: 0.2.15 unrs-resolver: 1.11.1 optionalDependencies: - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@2.6.1)) - transitivePeerDependencies: - - supports-color - - eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1)): - dependencies: - '@nolyfill/is-core-module': 1.0.39 - debug: 4.4.3 - eslint: 9.39.1(jiti@2.6.1) - get-tsconfig: 4.13.0 - is-bun-module: 2.0.0 - stable-hash: 0.0.5 - tinyglobby: 0.2.15 - unrs-resolver: 1.11.1 - optionalDependencies: - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@2.6.1)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1)) transitivePeerDependencies: - supports-color @@ -36687,12 +36538,12 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@6.21.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.1(jiti@2.6.1)): + eslint-module-utils@2.12.1(@typescript-eslint/parser@6.21.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.1(jiti@1.21.7)): dependencies: debug: 3.2.7 optionalDependencies: - '@typescript-eslint/parser': 6.21.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3) - eslint: 9.39.1(jiti@2.6.1) + '@typescript-eslint/parser': 6.21.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3) + eslint: 9.39.1(jiti@1.21.7) eslint-import-resolver-node: 0.3.9 transitivePeerDependencies: - supports-color @@ -36707,39 +36558,25 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1)): + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@2.6.1)): dependencies: debug: 3.2.7 optionalDependencies: '@typescript-eslint/parser': 8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.8.3) eslint: 9.39.1(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@2.6.1)) transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1)): + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@2.6.1)): dependencies: debug: 3.2.7 optionalDependencies: '@typescript-eslint/parser': 8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) eslint: 9.39.1(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1)) - transitivePeerDependencies: - - supports-color - - eslint-plugin-astro@1.5.0(eslint@9.39.1(jiti@1.21.7)): - dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@1.21.7)) - '@jridgewell/sourcemap-codec': 1.5.5 - '@typescript-eslint/types': 8.48.0 - astro-eslint-parser: 1.2.2 - eslint: 9.39.1(jiti@1.21.7) - eslint-compat-utils: 0.6.5(eslint@9.39.1(jiti@1.21.7)) - globals: 16.5.0 - postcss: 8.5.6 - postcss-selector-parser: 7.1.0 + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@2.6.1)) transitivePeerDependencies: - supports-color @@ -36763,6 +36600,12 @@ snapshots: eslint-utils: 2.1.0 regexpp: 3.2.0 + eslint-plugin-es@3.0.1(eslint@9.39.1(jiti@1.21.7)): + dependencies: + eslint: 9.39.1(jiti@1.21.7) + eslint-utils: 2.1.0 + regexpp: 3.2.0 + eslint-plugin-es@3.0.1(eslint@9.39.1(jiti@2.6.1)): dependencies: eslint: 9.39.1(jiti@2.6.1) @@ -36816,7 +36659,7 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-plugin-import@2.32.0(@typescript-eslint/parser@6.21.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3))(eslint@9.39.1(jiti@2.6.1)): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@6.21.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3))(eslint@9.39.1(jiti@1.21.7)): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -36825,9 +36668,9 @@ snapshots: array.prototype.flatmap: 1.3.3 debug: 3.2.7 doctrine: 2.1.0 - eslint: 9.39.1(jiti@2.6.1) + eslint: 9.39.1(jiti@1.21.7) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@6.21.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.1(jiti@2.6.1)) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@6.21.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.1(jiti@1.21.7)) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -36839,7 +36682,7 @@ snapshots: string.prototype.trimend: 1.0.9 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 6.21.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3) + '@typescript-eslint/parser': 6.21.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack @@ -36874,7 +36717,7 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@2.6.1)): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.1(jiti@2.6.1)): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -36885,7 +36728,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.39.1(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.8.3))(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1)) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@2.6.1)) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -36903,7 +36746,7 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@2.6.1)): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1)): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -36914,7 +36757,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.39.1(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1)) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@2.6.1)) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -36942,6 +36785,16 @@ snapshots: resolve: 1.22.11 semver: 6.3.1 + eslint-plugin-node@11.1.0(eslint@9.39.1(jiti@1.21.7)): + dependencies: + eslint: 9.39.1(jiti@1.21.7) + eslint-plugin-es: 3.0.1(eslint@9.39.1(jiti@1.21.7)) + eslint-utils: 2.1.0 + ignore: 5.3.2 + minimatch: 3.1.2 + resolve: 1.22.11 + semver: 6.3.1 + eslint-plugin-node@11.1.0(eslint@9.39.1(jiti@2.6.1)): dependencies: eslint: 9.39.1(jiti@2.6.1) @@ -36972,6 +36825,16 @@ snapshots: '@types/eslint': 9.6.1 eslint-config-prettier: 8.10.2(eslint@8.57.1) + eslint-plugin-prettier@5.5.4(@types/eslint@9.6.1)(eslint-config-prettier@8.10.2(eslint@9.39.1(jiti@1.21.7)))(eslint@9.39.1(jiti@1.21.7))(prettier@3.6.2): + dependencies: + eslint: 9.39.1(jiti@1.21.7) + prettier: 3.6.2 + prettier-linter-helpers: 1.0.0 + synckit: 0.11.11 + optionalDependencies: + '@types/eslint': 9.6.1 + eslint-config-prettier: 8.10.2(eslint@9.39.1(jiti@1.21.7)) + eslint-plugin-prettier@5.5.4(@types/eslint@9.6.1)(eslint-config-prettier@8.10.2(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1))(prettier@3.6.2): dependencies: eslint: 9.39.1(jiti@2.6.1) @@ -36996,6 +36859,10 @@ snapshots: dependencies: eslint: 8.57.1 + eslint-plugin-react-hooks@4.6.2(eslint@9.39.1(jiti@1.21.7)): + dependencies: + eslint: 9.39.1(jiti@1.21.7) + eslint-plugin-react-hooks@4.6.2(eslint@9.39.1(jiti@2.6.1)): dependencies: eslint: 9.39.1(jiti@2.6.1) @@ -37026,6 +36893,28 @@ snapshots: string.prototype.matchall: 4.0.12 string.prototype.repeat: 1.0.0 + eslint-plugin-react@7.37.5(eslint@9.39.1(jiti@1.21.7)): + dependencies: + array-includes: 3.1.9 + array.prototype.findlast: 1.2.5 + array.prototype.flatmap: 1.3.3 + array.prototype.tosorted: 1.1.4 + doctrine: 2.1.0 + es-iterator-helpers: 1.2.1 + eslint: 9.39.1(jiti@1.21.7) + estraverse: 5.3.0 + hasown: 2.0.2 + jsx-ast-utils: 3.3.5 + minimatch: 3.1.2 + object.entries: 1.1.9 + object.fromentries: 2.0.8 + object.values: 1.2.1 + prop-types: 15.8.1 + resolve: 2.0.0-next.5 + semver: 6.3.1 + string.prototype.matchall: 4.0.12 + string.prototype.repeat: 1.0.0 + eslint-plugin-react@7.37.5(eslint@9.39.1(jiti@2.6.1)): dependencies: array-includes: 3.1.9 @@ -38168,53 +38057,6 @@ snapshots: - react-native - supports-color - expo-router@6.0.15(7mqaurqidri6vkknnsci36yp4e): - dependencies: - '@expo/metro-runtime': 6.1.2(expo@54.0.25)(react-dom@19.1.0(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) - '@expo/schema-utils': 0.1.7 - '@radix-ui/react-slot': 1.2.0(@types/react@19.2.7)(react@19.1.0) - '@radix-ui/react-tabs': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@react-navigation/bottom-tabs': 7.8.6(@react-navigation/native@7.1.21(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-screens@4.16.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) - '@react-navigation/native': 7.1.21(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) - '@react-navigation/native-stack': 7.8.0(@react-navigation/native@7.1.21(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-screens@4.16.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) - client-only: 0.0.1 - debug: 4.4.3 - escape-string-regexp: 4.0.0 - expo: 54.0.25(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.15)(react-native-webview@13.12.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) - expo-constants: 18.0.10(expo@54.0.25)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0)) - expo-linking: 8.0.9(expo@54.0.25)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) - expo-server: 1.0.4 - fast-deep-equal: 3.1.3 - invariant: 2.2.4 - nanoid: 3.3.11 - query-string: 7.1.3 - react: 19.1.0 - react-fast-compare: 3.2.2 - react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0) - react-native-is-edge-to-edge: 1.2.1(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) - react-native-safe-area-context: 5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) - react-native-screens: 4.16.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) - semver: 7.6.3 - server-only: 0.0.1 - sf-symbols-typescript: 2.1.0 - shallowequal: 1.1.0 - use-latest-callback: 0.2.6(react@19.1.0) - vaul: 1.1.2(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - optionalDependencies: - '@react-navigation/drawer': 7.7.4(@react-navigation/native@7.1.21(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-gesture-handler@2.28.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-reanimated@4.1.5(@babel/core@7.28.5)(react-native-worklets@0.6.1(@babel/core@7.28.5)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-screens@4.16.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) - '@testing-library/react-native': 13.3.3(jest@29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react-test-renderer@19.1.0(react@19.1.0))(react@19.1.0) - react-dom: 19.1.0(react@19.1.0) - react-native-gesture-handler: 2.28.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) - react-native-reanimated: 4.1.5(@babel/core@7.28.5)(react-native-worklets@0.6.1(@babel/core@7.28.5)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) - react-native-web: 0.21.2(encoding@0.1.13)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - react-server-dom-webpack: 19.0.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(webpack@5.97.1(esbuild@0.19.12)) - transitivePeerDependencies: - - '@react-native-masked-view/masked-view' - - '@types/react' - - '@types/react-dom' - - supports-color - optional: true - expo-router@6.0.15(k2muy65dii4k2uiuhg4mwyy6ki): dependencies: '@expo/metro-runtime': 6.1.2(expo@54.0.25)(react-dom@19.1.0(react@18.3.1))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.27)(react@18.3.1))(react@18.3.1) @@ -39337,23 +39179,6 @@ snapshots: forever-agent@0.6.1: {} - fork-ts-checker-webpack-plugin@9.0.2(typescript@5.7.2)(webpack@5.97.1(esbuild@0.19.12)): - dependencies: - '@babel/code-frame': 7.27.1 - chalk: 4.1.2 - chokidar: 3.6.0 - cosmiconfig: 8.3.6(typescript@5.7.2) - deepmerge: 4.3.1 - fs-extra: 10.1.0 - memfs: 3.5.3 - minimatch: 3.1.2 - node-abort-controller: 3.1.1 - schema-utils: 3.3.0 - semver: 7.7.3 - tapable: 2.3.0 - typescript: 5.7.2 - webpack: 5.97.1(esbuild@0.19.12) - fork-ts-checker-webpack-plugin@9.0.2(typescript@5.7.2)(webpack@5.97.1(esbuild@0.27.0)): dependencies: '@babel/code-frame': 7.27.1 @@ -40809,16 +40634,16 @@ snapshots: - supports-color - ts-node - jest-cli@29.7.0(@types/node@24.10.1): + jest-cli@29.7.0(@types/node@24.10.1)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)): dependencies: - '@jest/core': 29.7.0 + '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)) '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 chalk: 4.1.2 - create-jest: 29.7.0(@types/node@24.10.1) + create-jest: 29.7.0(@types/node@24.10.1)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)) exit: 0.1.2 import-local: 3.2.0 - jest-config: 29.7.0(@types/node@24.10.1) + jest-config: 29.7.0(@types/node@24.10.1)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)) jest-util: 29.7.0 jest-validate: 29.7.0 yargs: 17.7.2 @@ -40906,36 +40731,6 @@ snapshots: - ts-node optional: true - jest-config@29.7.0(@types/node@22.19.1): - dependencies: - '@babel/core': 7.28.5 - '@jest/test-sequencer': 29.7.0 - '@jest/types': 29.6.3 - babel-jest: 29.7.0(@babel/core@7.28.5) - chalk: 4.1.2 - ci-info: 3.9.0 - deepmerge: 4.3.1 - glob: 7.2.3 - graceful-fs: 4.2.11 - jest-circus: 29.7.0 - jest-environment-node: 29.7.0 - jest-get-type: 29.6.3 - jest-regex-util: 29.6.3 - jest-resolve: 29.7.0 - jest-runner: 29.7.0 - jest-util: 29.7.0 - jest-validate: 29.7.0 - micromatch: 4.0.8 - parse-json: 5.2.0 - pretty-format: 29.7.0 - slash: 3.0.0 - strip-json-comments: 3.1.1 - optionalDependencies: - '@types/node': 22.19.1 - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - jest-config@29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)): dependencies: '@babel/core': 7.28.5 @@ -40967,7 +40762,38 @@ snapshots: - babel-plugin-macros - supports-color - jest-config@29.7.0(@types/node@24.10.1): + jest-config@29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)): + dependencies: + '@babel/core': 7.28.5 + '@jest/test-sequencer': 29.7.0 + '@jest/types': 29.6.3 + babel-jest: 29.7.0(@babel/core@7.28.5) + chalk: 4.1.2 + ci-info: 3.9.0 + deepmerge: 4.3.1 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-circus: 29.7.0 + jest-environment-node: 29.7.0 + jest-get-type: 29.6.3 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-runner: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + micromatch: 4.0.8 + parse-json: 5.2.0 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 + optionalDependencies: + '@types/node': 22.19.1 + ts-node: 10.9.2(@types/node@24.10.1)(typescript@5.9.3) + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + + jest-config@29.7.0(@types/node@24.10.1)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)): dependencies: '@babel/core': 7.28.5 '@jest/test-sequencer': 29.7.0 @@ -40993,6 +40819,7 @@ snapshots: strip-json-comments: 3.1.1 optionalDependencies: '@types/node': 24.10.1 + ts-node: 10.9.2(@types/node@24.10.1)(typescript@5.9.3) transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -41579,12 +41406,12 @@ snapshots: - supports-color - ts-node - jest@29.7.0(@types/node@24.10.1): + jest@29.7.0(@types/node@24.10.1)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)): dependencies: - '@jest/core': 29.7.0 + '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)) '@jest/types': 29.6.3 import-local: 3.2.0 - jest-cli: 29.7.0(@types/node@24.10.1) + jest-cli: 29.7.0(@types/node@24.10.1)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -45958,16 +45785,6 @@ snapshots: webpack-sources: 3.3.3 optional: true - react-server-dom-webpack@19.0.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(webpack@5.97.1(esbuild@0.19.12)): - dependencies: - acorn-loose: 8.5.2 - neo-async: 2.6.2 - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) - webpack: 5.97.1(esbuild@0.19.12) - webpack-sources: 3.3.3 - optional: true - react-style-singleton@2.2.3(@types/react@18.3.27)(react@18.3.1): dependencies: get-nonce: 1.0.1 @@ -47429,17 +47246,6 @@ snapshots: ansi-escapes: 4.3.2 supports-hyperlinks: 2.3.0 - terser-webpack-plugin@5.3.14(esbuild@0.19.12)(webpack@5.97.1(esbuild@0.19.12)): - dependencies: - '@jridgewell/trace-mapping': 0.3.31 - jest-worker: 27.5.1 - schema-utils: 4.3.3 - serialize-javascript: 6.0.2 - terser: 5.44.1 - webpack: 5.97.1(esbuild@0.19.12) - optionalDependencies: - esbuild: 0.19.12 - terser-webpack-plugin@5.3.14(esbuild@0.27.0)(webpack@5.100.2(esbuild@0.27.0)): dependencies: '@jridgewell/trace-mapping': 0.3.31 @@ -47640,27 +47446,6 @@ snapshots: ts-interface-checker@0.1.13: {} - ts-jest@29.4.5(@babel/core@7.28.5)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.28.5))(esbuild@0.19.12)(jest-util@30.2.0)(jest@29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))(typescript@5.9.3): - dependencies: - bs-logger: 0.2.6 - fast-json-stable-stringify: 2.1.0 - handlebars: 4.7.8 - jest: 29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)) - json5: 2.2.3 - lodash.memoize: 4.1.2 - make-error: 1.3.6 - semver: 7.7.3 - type-fest: 4.41.0 - typescript: 5.9.3 - yargs-parser: 21.1.1 - optionalDependencies: - '@babel/core': 7.28.5 - '@jest/transform': 30.2.0 - '@jest/types': 30.2.0 - babel-jest: 30.2.0(@babel/core@7.28.5) - esbuild: 0.19.12 - jest-util: 30.2.0 - ts-jest@29.4.5(@babel/core@7.28.5)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.28.5))(esbuild@0.27.0)(jest-util@30.2.0)(jest@30.2.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.27.0))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))(typescript@5.9.3): dependencies: bs-logger: 0.2.6 @@ -47703,6 +47488,26 @@ snapshots: esbuild: 0.27.0 jest-util: 30.2.0 + ts-jest@29.4.5(@babel/core@7.28.5)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.28.5))(jest-util@30.2.0)(jest@29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))(typescript@5.9.3): + dependencies: + bs-logger: 0.2.6 + fast-json-stable-stringify: 2.1.0 + handlebars: 4.7.8 + jest: 29.7.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)) + json5: 2.2.3 + lodash.memoize: 4.1.2 + make-error: 1.3.6 + semver: 7.7.3 + type-fest: 4.41.0 + typescript: 5.9.3 + yargs-parser: 21.1.1 + optionalDependencies: + '@babel/core': 7.28.5 + '@jest/transform': 30.2.0 + '@jest/types': 30.2.0 + babel-jest: 30.2.0(@babel/core@7.28.5) + jest-util: 30.2.0 + ts-loader@9.5.4(typescript@5.9.3)(webpack@5.100.2(esbuild@0.27.0)): dependencies: chalk: 4.1.2 @@ -47723,16 +47528,6 @@ snapshots: typescript: 5.9.3 webpack: 5.100.2 - ts-loader@9.5.4(typescript@5.9.3)(webpack@5.97.1(esbuild@0.19.12)): - dependencies: - chalk: 4.1.2 - enhanced-resolve: 5.18.3 - micromatch: 4.0.8 - semver: 7.7.3 - source-map: 0.7.6 - typescript: 5.9.3 - webpack: 5.97.1(esbuild@0.19.12) - ts-node@10.9.2(@types/node@20.19.25)(typescript@5.9.3): dependencies: '@cspotcode/source-map-support': 0.8.1 @@ -48413,23 +48208,6 @@ snapshots: lightningcss: 1.30.2 terser: 5.44.1 - vite@6.4.1(@types/node@20.19.25)(jiti@1.21.7)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1): - dependencies: - esbuild: 0.25.12 - fdir: 6.5.0(picomatch@4.0.3) - picomatch: 4.0.3 - postcss: 8.5.6 - rollup: 4.53.3 - tinyglobby: 0.2.15 - optionalDependencies: - '@types/node': 20.19.25 - fsevents: 2.3.3 - jiti: 1.21.7 - lightningcss: 1.30.2 - terser: 5.44.1 - tsx: 4.20.6 - yaml: 2.8.1 - vite@6.4.1(@types/node@20.19.25)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1): dependencies: esbuild: 0.25.12 @@ -48533,10 +48311,6 @@ snapshots: tsx: 4.20.6 yaml: 2.8.1 - vitefu@1.1.1(vite@6.4.1(@types/node@20.19.25)(jiti@1.21.7)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1)): - optionalDependencies: - vite: 6.4.1(@types/node@20.19.25)(jiti@1.21.7)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1) - vitefu@1.1.1(vite@6.4.1(@types/node@20.19.25)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1)): optionalDependencies: vite: 6.4.1(@types/node@20.19.25)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1) @@ -48991,36 +48765,6 @@ snapshots: - esbuild - uglify-js - webpack@5.97.1(esbuild@0.19.12): - dependencies: - '@types/eslint-scope': 3.7.7 - '@types/estree': 1.0.8 - '@webassemblyjs/ast': 1.14.1 - '@webassemblyjs/wasm-edit': 1.14.1 - '@webassemblyjs/wasm-parser': 1.14.1 - acorn: 8.15.0 - browserslist: 4.28.0 - chrome-trace-event: 1.0.4 - enhanced-resolve: 5.18.3 - es-module-lexer: 1.7.0 - eslint-scope: 5.1.1 - events: 3.3.0 - glob-to-regexp: 0.4.1 - graceful-fs: 4.2.11 - json-parse-even-better-errors: 2.3.1 - loader-runner: 4.3.1 - mime-types: 2.1.35 - neo-async: 2.6.2 - schema-utils: 3.3.0 - tapable: 2.3.0 - terser-webpack-plugin: 5.3.14(esbuild@0.19.12)(webpack@5.97.1(esbuild@0.19.12)) - watchpack: 2.4.4 - webpack-sources: 3.3.3 - transitivePeerDependencies: - - '@swc/core' - - esbuild - - uglify-js - webpack@5.97.1(esbuild@0.27.0): dependencies: '@types/eslint-scope': 3.7.7