From 141a64d888294a0a44bbf1f47d6a4c4c133c3725 Mon Sep 17 00:00:00 2001 From: Ajax Davis Date: Thu, 4 Dec 2025 06:39:58 +1000 Subject: [PATCH] feat: implement multi-tool package architecture with manual tool registry MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BREAKING CHANGE: Complete refactoring from single-tool to multi-tool package support Database Schema: - Split Tool model into Package (1) and Tool (many) with one-to-many relationship - Package stores npm metadata and package-level tpmjs fields (category, env, frameworks, tier) - Tool stores individual tool exports with tool-level metadata (exportName, description, parameters, returns, aiAgent) - Unique constraint on (packageId, exportName) to prevent duplicate tools - Cascade deletes when packages are removed Type System: - Updated tpmjs field schema to support tools array - Each tool has exportName, description, parameters, returns, aiAgent - Package-level fields: category, env, frameworks shared across all tools - Backward compatible with legacy single-tool format (auto-migrates to exportName: "default") API Updates: - Updated all /api/tools routes to query Tool model with Package relations - Updated /api/tools/[slug] to accept package/export path segments - Updated tool-executor-agent to use actual exportName instead of hardcoded "default" - Updated metrics sync to calculate quality scores per Tool Frontend Updates: - Updated tool search page to display exportName as primary heading - Updated tool detail pages to show package name as secondary info - Removed tag-based filtering (tags moved to package level) Manual Tool Registry: - Added manual-tools.ts with 23 curated tools from major providers - Created sync-manual-tools.ts script to sync manual tools to database - Added MANUAL_TOOLS.md documentation for manual tool system - Added GitHub workflow for automated daily sync - Includes tools from: Vercel, Exa, Firecrawl, AWS Bedrock, Perplexity, Tavily, Superagent, Valyu Playground Updates: - Updated tool loader to load multiple tools per package - Added sanitizeToolName for OpenAI API compatibility Sync System Updates: - Updated changes feed sync to handle multi-tool packages - Updated keyword sync to upsert multiple tools per package - Added orphaned tool deletion when tools removed from package.json Migration Strategy: - Database uses same Neon instance for dev and prod - Schema updated via prisma db push (no migration files yet) - All data repopulates from npm via sync system šŸ¤– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .github/workflows/sync-manual.yml | 97 + MANUAL_TOOLS.md | 293 +++ apps/playground/package.json | 2 +- .../src/components/chat/ChatInterface.tsx | 2 +- .../src/components/chat/ChatMessages.tsx | 20 +- .../src/components/chat/MessageBubble.tsx | 21 +- apps/web/next-env.d.ts | 2 +- apps/web/src/app/api/stats/route.ts | 49 +- apps/web/src/app/api/sync/changes/route.ts | 147 +- apps/web/src/app/api/sync/keyword/route.ts | 147 +- apps/web/src/app/api/sync/metrics/route.ts | 95 +- apps/web/src/app/api/tools/[...slug]/route.ts | 94 +- .../app/api/tools/execute/[...slug]/route.ts | 35 +- apps/web/src/app/api/tools/route.ts | 59 +- .../api/tools/simulations/[...slug]/route.ts | 33 +- apps/web/src/app/page.tsx | 51 +- apps/web/src/app/publish/page.tsx | 120 +- apps/web/src/app/spec/page.tsx | 79 +- apps/web/src/app/tool/tool-search/page.tsx | 104 +- apps/web/src/components/ToolPlayground.tsx | 8 +- .../src/lib/ai-agent/tool-executor-agent.ts | 23 +- manual-tools.ts | 762 ++++++++ package.json | 1 + packages/db/prisma/schema.prisma | 123 +- packages/tools/hello/CHANGELOG.md | 9 + packages/tools/hello/package.json | 60 +- packages/types/src/tpmjs.ts | 148 +- pnpm-lock.yaml | 1643 ++++++++++++++--- sync-hello.ts | 116 ++ sync-manual-tools.ts | 138 ++ test-schema.ts | 88 + 31 files changed, 3783 insertions(+), 786 deletions(-) create mode 100644 .github/workflows/sync-manual.yml create mode 100644 MANUAL_TOOLS.md create mode 100644 manual-tools.ts create mode 100644 packages/tools/hello/CHANGELOG.md create mode 100644 sync-hello.ts create mode 100644 sync-manual-tools.ts create mode 100644 test-schema.ts diff --git a/.github/workflows/sync-manual.yml b/.github/workflows/sync-manual.yml new file mode 100644 index 0000000..135f5af --- /dev/null +++ b/.github/workflows/sync-manual.yml @@ -0,0 +1,97 @@ +name: Sync Manual Tools + +on: + schedule: + # Run daily at midnight UTC + - cron: '0 0 * * *' + workflow_dispatch: + # Run on pushes to main that modify manual-tools.ts + push: + branches: + - main + paths: + - 'manual-tools.ts' + - 'sync-manual-tools.ts' + +jobs: + sync-manual: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup pnpm + uses: pnpm/action-setup@v2 + with: + version: 8 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'pnpm' + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Generate Prisma Client + run: pnpm --filter=@tpmjs/db db:generate + + - name: Run manual tools sync + id: sync + run: | + # Run the sync script and capture output + output=$(pnpm tsx sync-manual-tools.ts 2>&1) + echo "$output" + + # Extract statistics from output + processed=$(echo "$output" | grep "Processed:" | awk '{print $2}') + skipped=$(echo "$output" | grep "Skipped:" | awk '{print $2}') + errors=$(echo "$output" | grep "Errors:" | awk '{print $2}') + total=$(echo "$output" | grep "Total manual tools:" | awk '{print $4}') + + # Set outputs for Discord notification + echo "processed=${processed:-0}" >> $GITHUB_OUTPUT + echo "skipped=${skipped:-0}" >> $GITHUB_OUTPUT + echo "errors=${errors:-0}" >> $GITHUB_OUTPUT + echo "total=${total:-0}" >> $GITHUB_OUTPUT + + # Determine status + if [ "${errors:-0}" -gt 0 ]; then + echo "status_emoji=āš ļø" >> $GITHUB_OUTPUT + echo "status_color=16776960" >> $GITHUB_OUTPUT # Yellow + else + echo "status_emoji=āœ…" >> $GITHUB_OUTPUT + echo "status_color=5763719" >> $GITHUB_OUTPUT # Green + fi + env: + DATABASE_URL: ${{ secrets.DATABASE_URL }} + + - name: Send Discord notification + if: always() + run: | + # Build Discord payload + payload=$(jq -n \ + --arg title "${{ steps.sync.outputs.status_emoji }} Manual Tools Sync" \ + --argjson color ${{ steps.sync.outputs.status_color }} \ + --arg timestamp "$(date -u +%Y-%m-%dT%H:%M:%S.000Z)" \ + ' + { + embeds: [{ + title: $title, + color: $color, + fields: [ + { name: "šŸ“¦ Total Tools", value: "${{ steps.sync.outputs.total }}", inline: true }, + { name: "✨ Processed", value: "${{ steps.sync.outputs.processed }}", inline: true }, + { name: "ā­ļø Skipped", value: "${{ steps.sync.outputs.skipped }}", inline: true }, + { name: "āŒ Errors", value: "${{ steps.sync.outputs.errors }}", inline: true }, + { name: "šŸ”— Run", value: "[View Logs](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})", inline: true } + ], + timestamp: $timestamp + }] + }') + + # Send to Discord + curl -X POST "${{ secrets.DISCORD_WEBHOOK }}" \ + -H "Content-Type: application/json" \ + -d "$payload" diff --git a/MANUAL_TOOLS.md b/MANUAL_TOOLS.md new file mode 100644 index 0000000..fe27e30 --- /dev/null +++ b/MANUAL_TOOLS.md @@ -0,0 +1,293 @@ +# Manual Tools Registry + +## Overview + +This system allows TPMJS to include high-quality tools that don't follow the standard `tpmjs` field specification in their package.json. These tools are manually curated and synced to the database. + +## Why Manual Tools? + +Some excellent tools (like Vercel's code execution, Exa search, Firecrawl, etc.) don't include the `tpmjs` field in their package.json. Rather than wait for these package maintainers to adopt the spec, we manually curate metadata for these tools. + +## Architecture + +### Files + +1. **`manual-tools.ts`** - The registry of manually curated tools +2. **`sync-manual-tools.ts`** - Script to sync manual tools to database +3. **`MANUAL_TOOLS.md`** - This documentation + +### How It Works + +1. **Manual Tool Registry** (`manual-tools.ts`) + - Exports a `manualTools` array with metadata for each tool + - Each entry includes npm package name, export name, category, description, parameters, etc. + - Follows the same schema as the standard `tpmjs` field + +2. **Sync Script** (`sync-manual-tools.ts`) + - Fetches latest package metadata from npm + - Combines npm metadata with manual metadata + - Upserts Package + Tool records to database + - Marks tools with `discoveryMethod: 'manual'` + +3. **Database Storage** + - Manual tools stored in same `packages` and `tools` tables as auto-discovered tools + - No special handling needed in API or frontend + - `discoveryMethod: 'manual'` field distinguishes them + +## Adding a New Manual Tool + +### Step 1: Add to Registry + +Edit `manual-tools.ts` and add a new entry: + +```typescript +{ + npmPackageName: 'example-package', + category: 'search', + frameworks: ['vercel-ai'], + exportName: 'exampleTool', + description: 'A clear, concise description of what this tool does', + + // Optional but recommended for 'rich' tier + parameters: [ + { + name: 'query', + type: 'string', + description: 'The search query', + required: true, + }, + ], + + returns: { + type: 'array', + description: 'Array of search results', + }, + + aiAgent: { + useCase: 'Use when you need to search for X', + limitations: 'Rate limits apply', + examples: [ + 'Search for current news', + 'Find specific information', + ], + }, + + // Environment variables + env: [ + { + name: 'EXAMPLE_API_KEY', + description: 'API key for the service', + required: true, + }, + ], + + // Additional metadata + tags: ['search', 'web'], + docsUrl: 'https://example.com/docs', + apiKeyUrl: 'https://example.com/api-keys', + websiteUrl: 'https://example.com', +} +``` + +### Step 2: Run Sync Script + +```bash +# From repository root +pnpm tsx sync-manual-tools.ts +``` + +This will: +1. Fetch the package from npm +2. Create/update Package record +3. Create/update Tool record(s) +4. Set `discoveryMethod: 'manual'` + +### Step 3: Verify + +Check that the tool appears on tpmjs.com: + +```bash +# Start dev server +pnpm dev --filter=@tpmjs/web + +# Visit http://localhost:3000/tool/tool-search +# Search for your package name +``` + +## Multi-Tool Packages + +If a package exports multiple tools, add multiple entries with the same `npmPackageName` but different `exportName`: + +```typescript +{ + npmPackageName: 'firecrawl-aisdk', + exportName: 'scrapeTool', + description: 'Scrape websites...', + // ... +}, +{ + npmPackageName: 'firecrawl-aisdk', + exportName: 'searchTool', + description: 'Search the web...', + // ... +}, +{ + npmPackageName: 'firecrawl-aisdk', + exportName: 'crawlTool', + description: 'Crawl entire websites...', + // ... +}, +``` + +## Tier Calculation + +Tools are automatically assigned a tier: + +- **Rich tier**: Has `parameters` OR `returns` OR `aiAgent` fields +- **Minimal tier**: Only has basic metadata + +Rich tier tools get 4x quality score multiplier, so add detailed metadata when possible. + +## Maintenance + +### Updating Manual Tools + +1. Edit the entry in `manual-tools.ts` +2. Run `pnpm tsx sync-manual-tools.ts` +3. The upsert will update existing records + +### Removing Manual Tools + +1. Remove the entry from `manual-tools.ts` +2. Manually delete from database OR wait for metrics sync to mark as stale + +### Version Updates + +The sync script automatically fetches the latest version from npm unless you specify `npmVersion` in the manual tool entry. + +## Production Deployment + +### Option 1: Manual Sync on Deploy + +Add to your deployment workflow: + +```yaml +# .github/workflows/deploy.yml +- name: Sync manual tools + run: pnpm tsx sync-manual-tools.ts + env: + DATABASE_URL: ${{ secrets.DATABASE_URL }} +``` + +### Option 2: Scheduled Sync + +Create a cron job or GitHub Action to sync periodically: + +```yaml +# .github/workflows/sync-manual.yml +name: Sync Manual Tools + +on: + schedule: + - cron: '0 0 * * 0' # Weekly on Sunday + workflow_dispatch: # Manual trigger + +jobs: + sync: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: pnpm/action-setup@v2 + - uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'pnpm' + - run: pnpm install + - run: pnpm tsx sync-manual-tools.ts + env: + DATABASE_URL: ${{ secrets.DATABASE_URL }} +``` + +### Option 3: API Endpoint + +Create a sync endpoint (similar to keyword/changes sync): + +```typescript +// apps/web/src/app/api/sync/manual/route.ts +import { manualTools } from '@/manual-tools'; +// ... sync logic + +export async function POST(request: Request) { + // Verify CRON_SECRET + // Run manual sync + // Return results +} +``` + +## Currently Included Manual Tools + +As of this documentation: + +- **ai-sdk-tool-code-execution** - Vercel Sandbox code execution +- **@exalabs/ai-sdk** - Exa web search +- **@parallel-web/ai-sdk-tools** - Parallel search and extraction (2 tools) +- **ctx-zip** - MCP + Vercel Sandbox integration +- **@perplexity-ai/ai-sdk** - Perplexity search +- **@tavily/ai-sdk** - Tavily web research +- **firecrawl-aisdk** - Firecrawl scraping, search, crawling (3 tools) +- **bedrock-agentcore** - AWS Bedrock code interpreter and browser (2 tools) +- **@superagent-ai/ai-sdk** - Superagent security tools (3 tools) +- **@valyu/ai-sdk** - Valyu domain-specific search tools (8 tools) + +**Total: 24 manually curated tools across 10 packages** + +## FAQ + +### Why not just ask package maintainers to add the tpmjs field? + +We should! But: +1. Some packages are from large companies (Vercel, AWS, etc.) with slow adoption cycles +2. We want these tools available on TPMJS now +3. Manual curation lets us provide better metadata than package authors might + +### Will manual tools be replaced by auto-discovered ones? + +Yes! If a package adds a proper `tpmjs` field, the auto-discovery sync will update it with `discoveryMethod: 'keyword'` or `'changes-feed'`. Manual entries can then be removed from `manual-tools.ts`. + +### Can I mix manual and auto-discovered tools from the same package? + +Yes. If a package has some tools in the `tpmjs` field but is missing others, you can manually add the missing ones. The sync scripts will coexist peacefully. + +### How do I know if a tool is manually curated? + +Check the `discoveryMethod` field in the database: +- `'manual'` = Manually curated +- `'keyword'` = Auto-discovered via keyword search +- `'changes-feed'` = Auto-discovered via npm changes feed + +## Best Practices + +1. **Complete Metadata** - Provide as much metadata as possible for rich tier +2. **Accurate Descriptions** - Tool descriptions should be clear and specific +3. **AI-Friendly** - Write `aiAgent.useCase` as guidance for LLMs +4. **Keep Updated** - Periodically check if packages have added native `tpmjs` support +5. **Link to Docs** - Always include `docsUrl` when available +6. **API Key URLs** - Include `apiKeyUrl` for tools requiring authentication + +## Contributing + +To contribute new manual tools: + +1. Fork the repository +2. Add your tool to `manual-tools.ts` +3. Test with `pnpm tsx sync-manual-tools.ts` +4. Open a pull request with: + - Why this tool should be included + - Link to the npm package + - Screenshot of it working in TPMJS + +## Related Documentation + +- [HOW_TO_PUBLISH_A_TOOL.md](./HOW_TO_PUBLISH_A_TOOL.md) - Standard tpmjs field spec +- [CLAUDE.md](./CLAUDE.md) - General project documentation +- [packages/types/src/tpmjs.ts](./packages/types/src/tpmjs.ts) - TypeScript schema definitions diff --git a/apps/playground/package.json b/apps/playground/package.json index 7b73335..889684d 100644 --- a/apps/playground/package.json +++ b/apps/playground/package.json @@ -20,12 +20,12 @@ "@tpmjs/utils": "workspace:*", "ai": "6.0.0-beta.124", "firecrawl-aisdk": "^0.7.2", - "fish-joke-generator": "^1.1.0", "next": "^16.0.4", "next-themes": "^0.4.6", "openai": "^6.9.1", "react": "^19.0.0", "react-dom": "^19.0.0", + "streamdown": "^1.6.9", "zod": "^4.0.0" }, "devDependencies": { diff --git a/apps/playground/src/components/chat/ChatInterface.tsx b/apps/playground/src/components/chat/ChatInterface.tsx index c76e00e..174bcc3 100644 --- a/apps/playground/src/components/chat/ChatInterface.tsx +++ b/apps/playground/src/components/chat/ChatInterface.tsx @@ -25,7 +25,7 @@ export function ChatInterface(): React.ReactElement { return (
- + (null); // Auto-scroll to bottom when new messages arrive @@ -41,9 +45,17 @@ export function ChatMessages({ messages }: ChatMessagesProps): React.ReactElemen return (
- {messages.map((message) => ( - - ))} + {messages.map((message, idx) => { + // Only animate the last message if it's streaming + const isLastMessage = idx === messages.length - 1; + return ( + + ); + })}
); diff --git a/apps/playground/src/components/chat/MessageBubble.tsx b/apps/playground/src/components/chat/MessageBubble.tsx index eac2682..5f1be82 100644 --- a/apps/playground/src/components/chat/MessageBubble.tsx +++ b/apps/playground/src/components/chat/MessageBubble.tsx @@ -2,6 +2,7 @@ import { Badge } from '@tpmjs/ui/Badge/Badge'; import { Card, CardContent } from '@tpmjs/ui/Card/Card'; +import { Streamdown } from 'streamdown'; interface MessagePart { type: string; // Can be 'text', 'tool-{toolName}', 'step-start', etc. @@ -23,9 +24,13 @@ interface Message { interface MessageBubbleProps { message: Message; + isStreaming?: boolean; } -export function MessageBubble({ message }: MessageBubbleProps): React.ReactElement { +export function MessageBubble({ + message, + isStreaming = false, +}: MessageBubbleProps): React.ReactElement { const isUser = message.role === 'user'; // Debug: Log message structure @@ -59,11 +64,13 @@ export function MessageBubble({ message }: MessageBubbleProps): React.ReactEleme return null; } - // Render text parts + // Render text parts with markdown support if (part.type === 'text') { return ( -
- {part.text} +
+ + {part.text || ''} +
); } @@ -116,7 +123,11 @@ export function MessageBubble({ message }: MessageBubbleProps): React.ReactEleme
) : ( // Fallback to content if no parts -
{message.content || '...'}
+
+ + {message.content || '...'} + +
)} diff --git a/apps/web/next-env.d.ts b/apps/web/next-env.d.ts index 9edff1c..c4b7818 100644 --- a/apps/web/next-env.d.ts +++ b/apps/web/next-env.d.ts @@ -1,6 +1,6 @@ /// /// -import "./.next/types/routes.d.ts"; +import "./.next/dev/types/routes.d.ts"; // NOTE: This file should not be edited // see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/apps/web/src/app/api/stats/route.ts b/apps/web/src/app/api/stats/route.ts index e2dccc5..268a1f9 100644 --- a/apps/web/src/app/api/stats/route.ts +++ b/apps/web/src/app/api/stats/route.ts @@ -18,25 +18,14 @@ export const dynamic = 'force-dynamic'; export async function GET() { try { // Run all aggregations in parallel - const [totalTools, officialTools, categoryStats, recentCount, downloadSum] = await Promise.all([ + const [totalTools, officialTools, recentCount, packages] = await Promise.all([ // Total tools count prisma.tool.count(), - // Official tools count + // Official tools count (isOfficial is at package level) prisma.tool.count({ - where: { isOfficial: true }, - }), - - // Group by category - prisma.tool.groupBy({ - by: ['category'], - _count: { - id: true, - }, - orderBy: { - _count: { - id: 'desc', - }, + where: { + package: { isOfficial: true } }, }), @@ -49,21 +38,31 @@ export async function GET() { }, }), - // Sum of all downloads - prisma.tool.aggregate({ - _sum: { + // Get all packages with their tool counts and download stats + prisma.package.findMany({ + select: { + category: true, npmDownloadsLastMonth: true, + _count: { + select: { tools: true }, + }, }, }), ]); - // Format category stats - const categories = categoryStats.reduce>((acc, stat) => { - if (stat.category) { - acc[stat.category] = stat._count.id; + // Calculate stats from packages + const categories: Record = {}; + let totalDownloads = 0; + + for (const pkg of packages) { + // Count tools by category + if (pkg.category) { + categories[pkg.category] = (categories[pkg.category] || 0) + pkg._count.tools; } - return acc; - }, {}); + + // Sum downloads + totalDownloads += pkg.npmDownloadsLastMonth || 0; + } return NextResponse.json({ success: true, @@ -72,7 +71,7 @@ export async function GET() { officialTools, categories, recentTools: recentCount, - totalDownloads: downloadSum._sum.npmDownloadsLastMonth || 0, + totalDownloads, }, }); } catch (error) { diff --git a/apps/web/src/app/api/sync/changes/route.ts b/apps/web/src/app/api/sync/changes/route.ts index 2889b9b..f5952bf 100644 --- a/apps/web/src/app/api/sync/changes/route.ts +++ b/apps/web/src/app/api/sync/changes/route.ts @@ -66,74 +66,119 @@ export async function POST(request: NextRequest) { continue; } - // Validate tpmjs field + // Validate tpmjs field (supports both new multi-tool and legacy formats) const validation = validateTpmjsField(pkg.tpmjs); - if (!validation.valid || !validation.data) { + if (!validation.valid || !validation.packageData || !validation.tools) { skipped++; continue; } + // Log auto-migration from legacy format + if (validation.wasLegacyFormat) { + console.log(`Auto-migrated legacy package: ${pkg.name}`); + } + // Extract repository URL and GitHub stars const githubStars: number | null = null; - // Cast to TpmjsRich to access optional fields (they'll be undefined if not present) - const tpmjsData = validation.data as { - category: string; - description: string; - example: string; - parameters?: unknown; - returns?: unknown; - authentication?: unknown; - pricing?: unknown; - frameworks?: string[]; - links?: unknown; - tags?: string[]; - status?: string; - aiAgent?: unknown; - }; - - // Prepare data for upsert - const toolData = { - npmVersion: pkg.version, - npmPublishedAt: pkg.publishedAt ? new Date(pkg.publishedAt) : new Date(), - npmDescription: pkg.description ?? undefined, - npmRepository: pkg.repository ?? undefined, - npmHomepage: pkg.homepage ?? undefined, - npmLicense: pkg.license ?? undefined, - npmKeywords: pkg.topLevelKeywords || pkg.keywords || [], - npmReadme: pkg.readme ?? undefined, - npmAuthor: pkg.author ?? undefined, - npmMaintainers: pkg.maintainers ?? undefined, - category: tpmjsData.category, - description: tpmjsData.description, - example: tpmjsData.example, - parameters: tpmjsData.parameters ?? undefined, - returns: tpmjsData.returns ?? undefined, - authentication: tpmjsData.authentication ?? undefined, - pricing: tpmjsData.pricing ?? undefined, - frameworks: tpmjsData.frameworks || [], - links: tpmjsData.links ?? undefined, - tags: tpmjsData.tags || [], - status: tpmjsData.status ?? undefined, - aiAgent: tpmjsData.aiAgent ?? undefined, - isOfficial: pkg.keywords?.includes('tpmjs-tool') || false, - tier: validation.tier || 'minimal', - }; - - // Upsert tool to database - await prisma.tool.upsert({ + // Upsert Package record + const packageRecord = await prisma.package.upsert({ where: { npmPackageName: pkg.name }, create: { npmPackageName: pkg.name, - ...toolData, + npmVersion: pkg.version, + npmPublishedAt: pkg.publishedAt ? new Date(pkg.publishedAt) : new Date(), + npmDescription: pkg.description ?? undefined, + npmRepository: pkg.repository ?? undefined, + npmHomepage: pkg.homepage ?? undefined, + npmLicense: pkg.license ?? undefined, + npmKeywords: pkg.topLevelKeywords || pkg.keywords || [], + npmReadme: pkg.readme ?? undefined, + npmAuthor: pkg.author ?? undefined, + npmMaintainers: pkg.maintainers ?? undefined, + category: validation.packageData.category, + env: validation.packageData.env ?? undefined, + frameworks: validation.packageData.frameworks || [], + tier: validation.tier || 'minimal', discoveryMethod: 'changes-feed', + isOfficial: pkg.keywords?.includes('tpmjs-tool') || false, npmDownloadsLastMonth: 0, // Will be updated by metrics sync githubStars: githubStars, - qualityScore: null, // Will be calculated by metrics sync }, - update: toolData, + update: { + npmVersion: pkg.version, + npmPublishedAt: pkg.publishedAt ? new Date(pkg.publishedAt) : new Date(), + npmDescription: pkg.description ?? undefined, + npmRepository: pkg.repository ?? undefined, + npmHomepage: pkg.homepage ?? undefined, + npmLicense: pkg.license ?? undefined, + npmKeywords: pkg.topLevelKeywords || pkg.keywords || [], + npmReadme: pkg.readme ?? undefined, + npmAuthor: pkg.author ?? undefined, + npmMaintainers: pkg.maintainers ?? undefined, + category: validation.packageData.category, + env: validation.packageData.env ?? undefined, + frameworks: validation.packageData.frameworks || [], + tier: validation.tier || 'minimal', + isOfficial: pkg.keywords?.includes('tpmjs-tool') || false, + }, }); + // Get existing tools for this package + const existingTools = await prisma.tool.findMany({ + where: { packageId: packageRecord.id }, + }); + + // Upsert each tool in the tools array + for (const toolDef of validation.tools) { + await prisma.tool.upsert({ + where: { + packageId_exportName: { + packageId: packageRecord.id, + exportName: toolDef.exportName, + }, + }, + create: { + packageId: packageRecord.id, + exportName: toolDef.exportName, + description: toolDef.description, + // biome-ignore lint/suspicious/noExplicitAny: Prisma Json type compatibility workaround + parameters: toolDef.parameters ? (toolDef.parameters as any) : undefined, + // biome-ignore lint/suspicious/noExplicitAny: Prisma Json type compatibility workaround + returns: toolDef.returns ? (toolDef.returns as any) : undefined, + // biome-ignore lint/suspicious/noExplicitAny: Prisma Json type compatibility workaround + aiAgent: toolDef.aiAgent ? (toolDef.aiAgent as any) : undefined, + qualityScore: null, // Will be calculated by metrics sync + }, + update: { + description: toolDef.description, + // biome-ignore lint/suspicious/noExplicitAny: Prisma Json type compatibility workaround + parameters: toolDef.parameters ? (toolDef.parameters as any) : undefined, + // biome-ignore lint/suspicious/noExplicitAny: Prisma Json type compatibility workaround + returns: toolDef.returns ? (toolDef.returns as any) : undefined, + // biome-ignore lint/suspicious/noExplicitAny: Prisma Json type compatibility workaround + aiAgent: toolDef.aiAgent ? (toolDef.aiAgent as any) : undefined, + }, + }); + } + + // Delete orphaned tools (tools removed from package.json) + const orphanedTools = existingTools.filter( + (existingTool) => + !validation.tools?.some((toolDef) => toolDef.exportName === existingTool.exportName) + ); + + if (orphanedTools.length > 0) { + await prisma.tool.deleteMany({ + where: { + id: { in: orphanedTools.map((t) => t.id) }, + }, + }); + console.log( + `Deleted ${orphanedTools.length} orphaned tools from package: ${pkg.name}` + ); + } + processed++; } catch (error) { errors++; diff --git a/apps/web/src/app/api/sync/keyword/route.ts b/apps/web/src/app/api/sync/keyword/route.ts index 7c68d71..0305235 100644 --- a/apps/web/src/app/api/sync/keyword/route.ts +++ b/apps/web/src/app/api/sync/keyword/route.ts @@ -75,9 +75,9 @@ export async function POST(request: NextRequest) { continue; } - // Validate tpmjs field + // Validate tpmjs field (supports both new multi-tool and legacy formats) const validation = validateTpmjsField(pkg.tpmjs); - if (!validation.valid || !validation.data) { + if (!validation.valid || !validation.packageData || !validation.tools) { skipped++; skippedPackages.push({ name: pkg.name, @@ -87,67 +87,112 @@ export async function POST(request: NextRequest) { continue; } + // Log auto-migration from legacy format + if (validation.wasLegacyFormat) { + console.log(`Auto-migrated legacy package: ${pkg.name}`); + } + // Extract repository URL and GitHub stars const githubStars: number | null = null; - // Cast to TpmjsRich to access optional fields (they'll be undefined if not present) - const tpmjsData = validation.data as { - category: string; - description: string; - example: string; - parameters?: unknown; - returns?: unknown; - authentication?: unknown; - pricing?: unknown; - frameworks?: string[]; - links?: unknown; - tags?: string[]; - status?: string; - aiAgent?: unknown; - }; - - // Prepare data for upsert - const toolData = { - npmVersion: pkg.version, - npmPublishedAt: pkg.publishedAt ? new Date(pkg.publishedAt) : new Date(), - npmDescription: pkg.description ?? undefined, - npmRepository: pkg.repository ?? undefined, - npmHomepage: pkg.homepage ?? undefined, - npmLicense: pkg.license ?? undefined, - npmKeywords: pkg.topLevelKeywords || pkg.keywords || [], - npmReadme: pkg.readme ?? undefined, - npmAuthor: pkg.author ?? undefined, - npmMaintainers: pkg.maintainers ?? undefined, - category: tpmjsData.category, - description: tpmjsData.description, - example: tpmjsData.example, - parameters: tpmjsData.parameters ?? undefined, - returns: tpmjsData.returns ?? undefined, - authentication: tpmjsData.authentication ?? undefined, - pricing: tpmjsData.pricing ?? undefined, - frameworks: tpmjsData.frameworks || [], - links: tpmjsData.links ?? undefined, - tags: tpmjsData.tags || [], - status: tpmjsData.status ?? undefined, - aiAgent: tpmjsData.aiAgent ?? undefined, - isOfficial: pkg.keywords?.includes('tpmjs-tool') || false, - tier: validation.tier || 'minimal', - }; - - // Upsert tool to database - await prisma.tool.upsert({ + // Upsert Package record + const packageRecord = await prisma.package.upsert({ where: { npmPackageName: pkg.name }, create: { npmPackageName: pkg.name, - ...toolData, + npmVersion: pkg.version, + npmPublishedAt: pkg.publishedAt ? new Date(pkg.publishedAt) : new Date(), + npmDescription: pkg.description ?? undefined, + npmRepository: pkg.repository ?? undefined, + npmHomepage: pkg.homepage ?? undefined, + npmLicense: pkg.license ?? undefined, + npmKeywords: pkg.topLevelKeywords || pkg.keywords || [], + npmReadme: pkg.readme ?? undefined, + npmAuthor: pkg.author ?? undefined, + npmMaintainers: pkg.maintainers ?? undefined, + category: validation.packageData.category, + env: validation.packageData.env ?? undefined, + frameworks: validation.packageData.frameworks || [], + tier: validation.tier || 'minimal', discoveryMethod: 'keyword', + isOfficial: pkg.keywords?.includes('tpmjs-tool') || false, npmDownloadsLastMonth: 0, // Will be updated by metrics sync githubStars: githubStars, - qualityScore: null, // Will be calculated by metrics sync }, - update: toolData, + update: { + npmVersion: pkg.version, + npmPublishedAt: pkg.publishedAt ? new Date(pkg.publishedAt) : new Date(), + npmDescription: pkg.description ?? undefined, + npmRepository: pkg.repository ?? undefined, + npmHomepage: pkg.homepage ?? undefined, + npmLicense: pkg.license ?? undefined, + npmKeywords: pkg.topLevelKeywords || pkg.keywords || [], + npmReadme: pkg.readme ?? undefined, + npmAuthor: pkg.author ?? undefined, + npmMaintainers: pkg.maintainers ?? undefined, + category: validation.packageData.category, + env: validation.packageData.env ?? undefined, + frameworks: validation.packageData.frameworks || [], + tier: validation.tier || 'minimal', + isOfficial: pkg.keywords?.includes('tpmjs-tool') || false, + }, }); + // Get existing tools for this package + const existingTools = await prisma.tool.findMany({ + where: { packageId: packageRecord.id }, + }); + + // Upsert each tool in the tools array + for (const toolDef of validation.tools) { + await prisma.tool.upsert({ + where: { + packageId_exportName: { + packageId: packageRecord.id, + exportName: toolDef.exportName, + }, + }, + create: { + packageId: packageRecord.id, + exportName: toolDef.exportName, + description: toolDef.description, + // biome-ignore lint/suspicious/noExplicitAny: Prisma Json type compatibility workaround + parameters: toolDef.parameters ? (toolDef.parameters as any) : undefined, + // biome-ignore lint/suspicious/noExplicitAny: Prisma Json type compatibility workaround + returns: toolDef.returns ? (toolDef.returns as any) : undefined, + // biome-ignore lint/suspicious/noExplicitAny: Prisma Json type compatibility workaround + aiAgent: toolDef.aiAgent ? (toolDef.aiAgent as any) : undefined, + qualityScore: null, // Will be calculated by metrics sync + }, + update: { + description: toolDef.description, + // biome-ignore lint/suspicious/noExplicitAny: Prisma Json type compatibility workaround + parameters: toolDef.parameters ? (toolDef.parameters as any) : undefined, + // biome-ignore lint/suspicious/noExplicitAny: Prisma Json type compatibility workaround + returns: toolDef.returns ? (toolDef.returns as any) : undefined, + // biome-ignore lint/suspicious/noExplicitAny: Prisma Json type compatibility workaround + aiAgent: toolDef.aiAgent ? (toolDef.aiAgent as any) : undefined, + }, + }); + } + + // Delete orphaned tools (tools removed from package.json) + const orphanedTools = existingTools.filter( + (existingTool) => + !validation.tools?.some((toolDef) => toolDef.exportName === existingTool.exportName) + ); + + if (orphanedTools.length > 0) { + await prisma.tool.deleteMany({ + where: { + id: { in: orphanedTools.map((t) => t.id) }, + }, + }); + console.log( + `Deleted ${orphanedTools.length} orphaned tools from package: ${pkg.name}` + ); + } + processed++; } catch (error) { errors++; diff --git a/apps/web/src/app/api/sync/metrics/route.ts b/apps/web/src/app/api/sync/metrics/route.ts index 0b452b4..39bf41f 100644 --- a/apps/web/src/app/api/sync/metrics/route.ts +++ b/apps/web/src/app/api/sync/metrics/route.ts @@ -9,7 +9,7 @@ export const maxDuration = 300; // 5 minutes max for cron jobs /** * POST /api/sync/metrics - * Update download stats and quality scores for all tools + * Update download stats and quality scores for all packages and tools * * This endpoint is called by Vercel Cron (every hour) * Requires Authorization: Bearer @@ -31,43 +31,51 @@ export async function POST(request: NextRequest) { const errorMessages: string[] = []; try { - // Get all tools from database - const tools = await prisma.tool.findMany({ - select: { - id: true, - npmPackageName: true, - tier: true, - npmDownloadsLastMonth: true, - githubStars: true, + // Get all packages with their tools from database + const packages = await prisma.package.findMany({ + include: { + tools: true, }, }); - // Process each tool - for (const tool of tools) { + // Process each package + for (const pkg of packages) { try { - // Fetch download stats from NPM - const downloads = await fetchDownloadStats(tool.npmPackageName); + // Fetch download stats from NPM (package-level metric) + const downloads = await fetchDownloadStats(pkg.npmPackageName); - // Calculate quality score (0.00 to 1.00) - const qualityScore = calculateQualityScore({ - tier: tool.tier, - downloads, - githubStars: tool.githubStars || 0, - }); - - // Update tool metrics - await prisma.tool.update({ - where: { id: tool.id }, + // Update package metrics + await prisma.package.update({ + where: { id: pkg.id }, data: { npmDownloadsLastMonth: downloads, - qualityScore, + // githubStars would be updated here if we had GitHub API integration }, }); + // Calculate and update quality score for each tool in this package + for (const tool of pkg.tools) { + const qualityScore = calculateQualityScore({ + tier: pkg.tier, // Tier is at package level + downloads, // Package downloads + githubStars: pkg.githubStars || 0, // Package stars + hasParameters: !!tool.parameters, + hasReturns: !!tool.returns, + hasAiAgent: !!tool.aiAgent, + }); + + await prisma.tool.update({ + where: { id: tool.id }, + data: { + qualityScore, + }, + }); + } + processed++; } catch (error) { errors++; - const errorMsg = `Failed to process ${tool.npmPackageName}: ${error instanceof Error ? error.message : 'Unknown error'}`; + const errorMsg = `Failed to process ${pkg.npmPackageName}: ${error instanceof Error ? error.message : 'Unknown error'}`; errorMessages.push(errorMsg); console.error(errorMsg); } @@ -80,13 +88,15 @@ export async function POST(request: NextRequest) { source: 'metrics', checkpoint: { lastRun: new Date().toISOString(), - totalTools: tools.length, + totalPackages: packages.length, + totalTools: packages.reduce((sum, pkg) => sum + pkg.tools.length, 0), }, }, update: { checkpoint: { lastRun: new Date().toISOString(), - totalTools: tools.length, + totalPackages: packages.length, + totalTools: packages.reduce((sum, pkg) => sum + pkg.tools.length, 0), }, }, }); @@ -102,10 +112,11 @@ export async function POST(request: NextRequest) { message: errors > 0 ? `Processed with errors: ${errorMessages.slice(0, 3).join('; ')}` - : `Successfully updated metrics for ${processed} tools`, + : `Successfully updated metrics for ${processed} packages`, metadata: { durationMs: Date.now() - startTime, - totalTools: tools.length, + totalPackages: packages.length, + totalTools: packages.reduce((sum, pkg) => sum + pkg.tools.length, 0), }, }, }); @@ -116,7 +127,8 @@ export async function POST(request: NextRequest) { processed, skipped, errors, - totalTools: tools.length, + totalPackages: packages.length, + totalTools: packages.reduce((sum, pkg) => sum + pkg.tools.length, 0), durationMs: Date.now() - startTime, }, }); @@ -152,25 +164,40 @@ export async function POST(request: NextRequest) { /** * Calculate quality score based on multiple factors * Returns a value between 0.00 and 1.00 + * + * Score components: + * - Tier (0.4 minimal, 0.6 rich) + * - Downloads (logarithmic, max 0.2) + * - GitHub stars (logarithmic, max 0.1) + * - Tool metadata richness (0.1 for each: parameters, returns, aiAgent) */ function calculateQualityScore(params: { tier: string; downloads: number; githubStars: number; + hasParameters: boolean; + hasReturns: boolean; + hasAiAgent: boolean; }): number { - const { tier, downloads, githubStars } = params; + const { tier, downloads, githubStars, hasParameters, hasReturns, hasAiAgent } = params; // Base score from tier const tierScore = tier === 'rich' ? 0.6 : 0.4; - // Downloads score (logarithmic scale, max 0.3) - const downloadsScore = Math.min(0.3, Math.log10(downloads + 1) / 10); + // Downloads score (logarithmic scale, max 0.2) + const downloadsScore = Math.min(0.2, Math.log10(downloads + 1) / 15); // GitHub stars score (logarithmic scale, max 0.1) const starsScore = Math.min(0.1, Math.log10(githubStars + 1) / 10); + // Tool metadata richness score (max 0.1) + let richnessScore = 0; + if (hasParameters) richnessScore += 0.04; + if (hasReturns) richnessScore += 0.03; + if (hasAiAgent) richnessScore += 0.03; + // Total score (capped at 1.00) - const totalScore = Math.min(1.0, tierScore + downloadsScore + starsScore); + const totalScore = Math.min(1.0, tierScore + downloadsScore + starsScore + richnessScore); // Round to 2 decimal places return Math.round(totalScore * 100) / 100; diff --git a/apps/web/src/app/api/tools/[...slug]/route.ts b/apps/web/src/app/api/tools/[...slug]/route.ts index 964e639..48d22a8 100644 --- a/apps/web/src/app/api/tools/[...slug]/route.ts +++ b/apps/web/src/app/api/tools/[...slug]/route.ts @@ -18,31 +18,83 @@ export async function GET( try { const { slug } = await params; - // Join slug array to reconstruct package name (e.g., ['@tpmjs', 'text-transformer'] -> '@tpmjs/text-transformer') - const packageName = slug.join('/'); + // Slug can be: + // - ['@scope', 'package'] -> Get all tools for @scope/package + // - ['@scope', 'package', 'exportName'] -> Get specific tool @scope/package/exportName + // - ['package'] -> Get all tools for package + // - ['package', 'exportName'] -> Get specific tool package/exportName - // Find the tool by npmPackageName - const tool = await prisma.tool.findUnique({ - where: { - npmPackageName: packageName, - }, - }); + let packageName: string; + let exportName: string | undefined; - if (!tool) { - return NextResponse.json( - { - success: false, - error: 'Tool not found', - }, - { status: 404 } - ); + if (slug.length === 1) { + // Single slug - package name without scope + packageName = slug[0] || ''; + } else if (slug.length === 2) { + // Could be: @scope/package OR package/exportName + if (slug[0]?.startsWith('@')) { + // @scope/package + packageName = slug.join('/'); + } else { + // package + exportName + packageName = slug[0] || ''; + exportName = slug[1]; + } + } else { + // 3+ slugs: @scope/package/exportName + packageName = slug.slice(0, slug[0]?.startsWith('@') ? 2 : 1).join('/'); + exportName = slug[slug.length - 1]; } - // Return the tool data - return NextResponse.json({ - success: true, - data: tool, - }); + if (exportName) { + // Find specific tool by package name and export name + const tool = await prisma.tool.findFirst({ + where: { + package: { npmPackageName: packageName }, + exportName: exportName, + }, + include: { package: true }, + }); + + if (!tool) { + return NextResponse.json( + { + success: false, + error: 'Tool not found', + }, + { status: 404 } + ); + } + + return NextResponse.json({ + success: true, + data: tool, + }); + } else { + // Find all tools for the package + const pkg = await prisma.package.findUnique({ + where: { npmPackageName: packageName }, + include: { tools: true }, + }); + + if (!pkg) { + return NextResponse.json( + { + success: false, + error: 'Package not found', + }, + { status: 404 } + ); + } + + return NextResponse.json({ + success: true, + data: { + package: pkg, + tools: pkg.tools, + }, + }); + } } catch (error) { console.error('Error fetching tool:', error); return NextResponse.json( diff --git a/apps/web/src/app/api/tools/execute/[...slug]/route.ts b/apps/web/src/app/api/tools/execute/[...slug]/route.ts index 2cc2632..7f001d1 100644 --- a/apps/web/src/app/api/tools/execute/[...slug]/route.ts +++ b/apps/web/src/app/api/tools/execute/[...slug]/route.ts @@ -18,15 +18,19 @@ interface ExecuteRequest { } /** - * POST /api/tools/[...slug]/execute + * POST /api/tools/execute/[...slug] * Executes a tool with an AI agent and streams the response via SSE + * + * Slug format: [toolId] or [packageName, exportName] + * Examples: + * /api/tools/execute/clx123abc (by tool ID) + * /api/tools/execute/@tpmjs/hello/helloWorldTool (by package and export name) */ export async function POST( request: NextRequest, { params }: { params: Promise<{ slug: string[] }> } ) { const { slug } = await params; - const packageName = decodeURIComponent(slug.join('/')); try { // Parse request body @@ -63,10 +67,29 @@ export async function POST( ); } - // Fetch tool from database - const tool = await prisma.tool.findUnique({ - where: { npmPackageName: packageName }, - }); + // Fetch tool from database with package relation + // Support both ID-based lookup and packageName/exportName lookup + let tool; + + if (slug.length === 1) { + // Single slug - treat as tool ID + tool = await prisma.tool.findUnique({ + where: { id: slug[0] || '' }, + include: { package: true }, + }); + } else { + // Multiple slugs - treat as packageName/exportName + const packageName = decodeURIComponent(slug.slice(0, -1).join('/')); + const exportName = decodeURIComponent(slug[slug.length - 1] || ''); + + tool = await prisma.tool.findFirst({ + where: { + package: { npmPackageName: packageName }, + exportName: exportName, + }, + include: { package: true }, + }); + } if (!tool) { return NextResponse.json({ error: 'Tool not found' }, { status: 404 }); diff --git a/apps/web/src/app/api/tools/route.ts b/apps/web/src/app/api/tools/route.ts index abeab4d..8b89ab8 100644 --- a/apps/web/src/app/api/tools/route.ts +++ b/apps/web/src/app/api/tools/route.ts @@ -10,10 +10,10 @@ export const maxDuration = 60; * Search and list tools with filtering, sorting, and pagination * * Query params: - * - q: Search query (searches name, description, tags) + * - q: Search query (searches package name, tool description) * - category: Filter by category * - official: Filter by official status (true/false) - * - limit: Results per page (default: 20, max: 100) + * - limit: Results per page (default: 20, max: 50) * - offset: Pagination offset (default: 0) */ export async function GET(request: NextRequest) { @@ -27,44 +27,52 @@ export async function GET(request: NextRequest) { const limitParam = searchParams.get('limit'); const offsetParam = searchParams.get('offset'); - // Validate and set defaults (reduced max from 100 to 50 for faster queries) + // Validate and set defaults const limit = Math.min( Number.parseInt(limitParam || '20', 10), - 50 // Reduced from 100 for better performance + 50 // Max 50 for better performance ); const offset = Math.max(Number.parseInt(offsetParam || '0', 10), 0); - // Build where clause + // Build where clause for Tool table const where: Prisma.ToolWhereInput = {}; - // Search filter (case-insensitive partial match) + // Build package filter separately + const packageFilter: Prisma.PackageWhereInput = {}; + + // Category filter (category is at package level) + if (category) { + packageFilter.category = category; + } + + // Official filter (isOfficial is at package level) + if (officialParam !== null) { + packageFilter.isOfficial = officialParam === 'true'; + } + + // Search filter (searches tool description and package name) if (query) { where.OR = [ - { npmPackageName: { contains: query, mode: 'insensitive' } }, { description: { contains: query, mode: 'insensitive' } }, - { - tags: { - hasSome: [query], - }, - }, + { package: { npmPackageName: { contains: query, mode: 'insensitive' }, ...packageFilter } }, ]; + } else if (Object.keys(packageFilter).length > 0) { + // Apply package filter if no search query + where.package = packageFilter; } - // Category filter - if (category) { - where.category = category; - } - - // Official filter - if (officialParam !== null) { - where.isOfficial = officialParam === 'true'; - } - - // Execute queries - run count separately only if needed for pagination - // For first page, we can skip count if we don't need total pages + // Execute query - fetch tools with package relation + // We fetch limit+1 to check if there are more results (avoid expensive count) const tools = await prisma.tool.findMany({ where, - orderBy: [{ qualityScore: 'desc' }, { npmDownloadsLastMonth: 'desc' }, { createdAt: 'desc' }], + include: { + package: true, // Include package data for each tool + }, + orderBy: [ + { qualityScore: 'desc' }, // Tool quality score + { package: { npmDownloadsLastMonth: 'desc' } }, // Package downloads + { createdAt: 'desc' }, // Tool creation time + ], take: limit + 1, // Fetch one extra to check if there are more skip: offset, }); @@ -81,7 +89,6 @@ export async function GET(request: NextRequest) { offset, hasMore, // Note: total count omitted for performance (can be expensive) - // Only return count if explicitly requested }, }); } catch (error) { diff --git a/apps/web/src/app/api/tools/simulations/[...slug]/route.ts b/apps/web/src/app/api/tools/simulations/[...slug]/route.ts index d2748c8..67ee400 100644 --- a/apps/web/src/app/api/tools/simulations/[...slug]/route.ts +++ b/apps/web/src/app/api/tools/simulations/[...slug]/route.ts @@ -7,22 +7,41 @@ import { prisma } from '@tpmjs/db'; import { type NextRequest, NextResponse } from 'next/server'; /** - * GET /api/tools/[...slug]/simulations + * GET /api/tools/simulations/[...slug] * Returns the last 10 simulations for a tool + * + * Slug can be: + * - Tool ID (single slug) + * - Package name + export name (multiple slugs) */ export async function GET( _request: NextRequest, { params }: { params: Promise<{ slug: string[] }> } ) { const { slug } = await params; - const packageName = decodeURIComponent(slug.join('/')); try { - // Fetch tool - const tool = await prisma.tool.findUnique({ - where: { npmPackageName: packageName }, - select: { id: true }, - }); + let tool; + + if (slug.length === 1) { + // Single slug - treat as tool ID + tool = await prisma.tool.findUnique({ + where: { id: slug[0] || '' }, + select: { id: true }, + }); + } else { + // Multiple slugs - treat as packageName/exportName + const packageName = decodeURIComponent(slug.slice(0, -1).join('/')); + const exportName = decodeURIComponent(slug[slug.length - 1] || ''); + + tool = await prisma.tool.findFirst({ + where: { + package: { npmPackageName: packageName }, + exportName: exportName, + }, + select: { id: true }, + }); + } if (!tool) { return NextResponse.json({ error: 'Tool not found' }, { status: 404 }); diff --git a/apps/web/src/app/page.tsx b/apps/web/src/app/page.tsx index a8b66c4..6d3bef9 100644 --- a/apps/web/src/app/page.tsx +++ b/apps/web/src/app/page.tsx @@ -20,24 +20,33 @@ async function getHomePageData() { // Top 6 featured tools by quality score prisma.tool.findMany({ - orderBy: [{ qualityScore: 'desc' }, { npmDownloadsLastMonth: 'desc' }], + orderBy: [ + { qualityScore: 'desc' }, + { package: { npmDownloadsLastMonth: 'desc' } }, + ], take: 6, select: { id: true, - npmPackageName: true, + exportName: true, description: true, - category: true, - tags: true, qualityScore: true, - npmDownloadsLastMonth: true, - isOfficial: true, + package: { + select: { + npmPackageName: true, + category: true, + npmDownloadsLastMonth: true, + isOfficial: true, + }, + }, }, }), - // Category distribution for stats - prisma.tool.groupBy({ + // Category distribution for stats (group by package category) + prisma.package.groupBy({ by: ['category'], - _count: true, + _count: { + _all: true, + }, }), ]); @@ -70,7 +79,7 @@ async function getHomePageData() { featuredTools, categories: categoryStats.slice(0, 5).map((c) => ({ name: c.category, - count: c._count, + count: c._count._all, })), }; } catch (error) { @@ -113,13 +122,20 @@ export default async function HomePage(): Promise { {data.featuredTools.length > 0 ? (
{data.featuredTools.map((tool) => ( - +

- {tool.npmPackageName} + {tool.package.npmPackageName} + + ({tool.exportName}) +

- {tool.isOfficial && ( + {tool.package.isOfficial && ( Official @@ -132,13 +148,8 @@ export default async function HomePage(): Promise {
- {tool.category} + {tool.package.category} - {tool.tags.slice(0, 2).map((tag) => ( - - {tag} - - ))}
@@ -147,7 +158,7 @@ export default async function HomePage(): Promise { {tool.qualityScore ? Number(tool.qualityScore).toFixed(2) : 'N/A'} - {tool.npmDownloadsLastMonth?.toLocaleString() || '0'} downloads/mo + {tool.package.npmDownloadsLastMonth?.toLocaleString() || '0'} downloads/mo
diff --git a/apps/web/src/app/publish/page.tsx b/apps/web/src/app/publish/page.tsx index 4625baf..ffb0e2a 100644 --- a/apps/web/src/app/publish/page.tsx +++ b/apps/web/src/app/publish/page.tsx @@ -96,7 +96,13 @@ export default function PublishPage(): React.ReactElement { code={`{ "tpmjs": { "category": "text-analysis", - "description": "A concise description of what your tool does" + "frameworks": ["vercel-ai"], + "tools": [ + { + "exportName": "myTool", + "description": "A concise description of what your tool does" + } + ] } }`} /> @@ -118,26 +124,32 @@ export default function PublishPage(): React.ReactElement { code={`{ "tpmjs": { "category": "text-analysis", - "description": "Analyzes sentiment in text", - "parameters": [ + "frameworks": ["vercel-ai"], + "tools": [ { - "name": "text", - "type": "string", - "description": "The text to analyze", - "required": true - }, - { - "name": "language", - "type": "string", - "description": "Language code (e.g., 'en')", - "required": false, - "default": "en" + "exportName": "sentimentAnalysisTool", + "description": "Analyzes sentiment in text", + "parameters": [ + { + "name": "text", + "type": "string", + "description": "The text to analyze", + "required": true + }, + { + "name": "language", + "type": "string", + "description": "Language code (e.g., 'en')", + "required": false, + "default": "en" + } + ], + "returns": { + "type": "SentimentResult", + "description": "Object with score and label" + } } - ], - "returns": { - "type": "SentimentResult", - "description": "Object with score and label" - } + ] } }`} /> @@ -159,9 +171,7 @@ export default function PublishPage(): React.ReactElement { code={`{ "tpmjs": { "category": "text-analysis", - "description": "Advanced sentiment analysis with emotion detection", - "parameters": [...], - "returns": {...}, + "frameworks": ["vercel-ai", "langchain"], "env": [ { "name": "SENTIMENT_API_KEY", @@ -169,15 +179,22 @@ export default function PublishPage(): React.ReactElement { "required": true } ], - "frameworks": ["vercel-ai", "langchain"], - "aiAgent": { - "useCase": "Use when users need to analyze sentiment or detect emotions", - "limitations": "English and Spanish only. Max 10,000 characters", - "examples": [ - "Analyze customer review sentiment", - "Detect emotions in feedback" - ] - } + "tools": [ + { + "exportName": "sentimentAnalysisTool", + "description": "Advanced sentiment analysis with emotion detection", + "parameters": [...], + "returns": {...}, + "aiAgent": { + "useCase": "Use when users need to analyze sentiment or detect emotions", + "limitations": "English and Spanish only. Max 10,000 characters", + "examples": [ + "Analyze customer review sentiment", + "Detect emotions in feedback" + ] + } + } + ] } }`} /> @@ -279,26 +296,31 @@ npm publish --access public "keywords": ["tpmjs-tool", "blog", "content"], "tpmjs": { "category": "text-analysis", - "description": "Creates structured blog posts with frontmatter and SEO metadata", - "parameters": [ + "frameworks": ["vercel-ai", "langchain"], + "tools": [ { - "name": "title", - "type": "string", - "description": "The title of the blog post", - "required": true - }, - { - "name": "content", - "type": "string", - "description": "The main content", - "required": true + "exportName": "createBlogPostTool", + "description": "Creates structured blog posts with frontmatter and SEO metadata", + "parameters": [ + { + "name": "title", + "type": "string", + "description": "The title of the blog post", + "required": true + }, + { + "name": "content", + "type": "string", + "description": "The main content", + "required": true + } + ], + "returns": { + "type": "BlogPost", + "description": "Structured blog post with frontmatter" + } } - ], - "returns": { - "type": "BlogPost", - "description": "Structured blog post with frontmatter" - }, - "frameworks": ["vercel-ai", "langchain"] + ] } }`} /> diff --git a/apps/web/src/app/spec/page.tsx b/apps/web/src/app/spec/page.tsx index 1984d61..d40d637 100644 --- a/apps/web/src/app/spec/page.tsx +++ b/apps/web/src/app/spec/page.tsx @@ -163,7 +163,13 @@ export default function SpecPage(): React.ReactElement { code={`{ "tpmjs": { "category": "text-analysis", - "description": "Analyzes sentiment in text and returns positive/negative/neutral classification" + "frameworks": ["vercel-ai"], + "tools": [ + { + "exportName": "sentimentAnalysisTool", + "description": "Analyzes sentiment in text and returns positive/negative/neutral classification" + } + ] } }`} /> @@ -237,26 +243,32 @@ export default function SpecPage(): React.ReactElement { code={`{ "tpmjs": { "category": "text-analysis", - "description": "Analyzes sentiment in text", - "parameters": [ + "frameworks": ["vercel-ai"], + "tools": [ { - "name": "text", - "type": "string", - "description": "The text to analyze", - "required": true - }, - { - "name": "language", - "type": "string", - "description": "Language code (e.g., 'en', 'es')", - "required": false, - "default": "en" + "exportName": "sentimentAnalysisTool", + "description": "Analyzes sentiment in text", + "parameters": [ + { + "name": "text", + "type": "string", + "description": "The text to analyze", + "required": true + }, + { + "name": "language", + "type": "string", + "description": "Language code (e.g., 'en', 'es')", + "required": false, + "default": "en" + } + ], + "returns": { + "type": "SentimentResult", + "description": "Object with score (-1 to 1) and label (positive/negative/neutral)" + } } - ], - "returns": { - "type": "SentimentResult", - "description": "Object with score (-1 to 1) and label (positive/negative/neutral)" - } + ] } }`} /> @@ -361,9 +373,7 @@ export default function SpecPage(): React.ReactElement { code={`{ "tpmjs": { "category": "text-analysis", - "description": "Advanced sentiment analysis with emotion detection", - "parameters": [...], - "returns": {...}, + "frameworks": ["vercel-ai", "langchain"], "env": [ { "name": "SENTIMENT_API_KEY", @@ -371,15 +381,22 @@ export default function SpecPage(): React.ReactElement { "required": true } ], - "frameworks": ["vercel-ai", "langchain"], - "aiAgent": { - "useCase": "Use when users need to analyze sentiment or detect emotions in text", - "limitations": "English and Spanish only. Max 10,000 characters per request.", - "examples": [ - "Analyze customer review sentiment", - "Detect emotions in user feedback" - ] - } + "tools": [ + { + "exportName": "sentimentAnalysisTool", + "description": "Advanced sentiment analysis with emotion detection", + "parameters": [...], + "returns": {...}, + "aiAgent": { + "useCase": "Use when users need to analyze sentiment or detect emotions in text", + "limitations": "English and Spanish only. Max 10,000 characters per request.", + "examples": [ + "Analyze customer review sentiment", + "Detect emotions in user feedback" + ] + } + } + ] } }`} /> diff --git a/apps/web/src/app/tool/tool-search/page.tsx b/apps/web/src/app/tool/tool-search/page.tsx index affd1de..7d4e266 100644 --- a/apps/web/src/app/tool/tool-search/page.tsx +++ b/apps/web/src/app/tool/tool-search/page.tsx @@ -23,15 +23,17 @@ import { AppHeader } from '~/components/AppHeader'; interface Tool { id: string; - npmPackageName: string; - npmVersion: string; + exportName: string; description: string; - category: string; - tags: string[]; - npmRepository: { url: string; type: string } | null; qualityScore: string; - isOfficial: boolean; - npmDownloadsLastMonth: number; + package: { + npmPackageName: string; + npmVersion: string; + category: string; + npmRepository: { url: string; type: string } | null; + isOfficial: boolean; + npmDownloadsLastMonth: number; + }; } /** @@ -43,12 +45,10 @@ export default function ToolSearchPage(): React.ReactElement { const [activeTab, setActiveTab] = useState('all'); const [searchQuery, setSearchQuery] = useState(''); const [categoryFilter, setCategoryFilter] = useState('all'); - const [selectedTags, setSelectedTags] = useState([]); const [tools, setTools] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [availableCategories, setAvailableCategories] = useState([]); - const [availableTags, setAvailableTags] = useState([]); // Fetch tools from API useEffect(() => { @@ -77,21 +77,16 @@ export default function ToolSearchPage(): React.ReactElement { setTools(fetchedTools); setError(null); - // Extract unique categories and tags from all tools + // Extract unique categories from all tools const categories = new Set(); - const tags = new Set(); for (const tool of fetchedTools) { - if (tool.category) { - categories.add(tool.category); - } - for (const tag of tool.tags) { - tags.add(tag); + if (tool.package.category) { + categories.add(tool.package.category); } } setAvailableCategories(Array.from(categories).sort()); - setAvailableTags(Array.from(tags).sort()); } else { setError(data.error || 'Failed to fetch tools'); } @@ -105,12 +100,6 @@ export default function ToolSearchPage(): React.ReactElement { fetchTools(); }, [searchQuery, activeTab, categoryFilter]); - // Filter tools by selected tags (client-side) - const displayedTools = - selectedTags.length > 0 - ? tools.filter((tool) => selectedTags.some((tag) => tool.tags.includes(tag))) - : tools; - return (
@@ -154,43 +143,18 @@ export default function ToolSearchPage(): React.ReactElement {
{/* Clear filters button */} - {(categoryFilter !== 'all' || selectedTags.length > 0) && ( + {categoryFilter !== 'all' && ( )}
- - {/* Popular tags */} - {availableTags.length > 0 && ( -
- - Filter by tag: - - {availableTags.slice(0, 10).map((tag) => ( - { - setSelectedTags((prev) => - prev.includes(tag) ? prev.filter((t) => t !== tag) : [...prev, tag] - ); - }} - > - {tag} - - ))} -
- )}
{/* Tabs */} @@ -200,7 +164,7 @@ export default function ToolSearchPage(): React.ReactElement { { id: 'featured', label: 'Official', - count: tools.filter((t) => t.isOfficial).length, + count: tools.filter((t) => t.package.isOfficial).length, }, ]} activeTab={activeTab} @@ -220,15 +184,22 @@ export default function ToolSearchPage(): React.ReactElement { {/* Tool grid */} {!loading && !error && (
- {displayedTools.length > 0 ? ( - displayedTools.map((tool) => ( + {tools.length > 0 ? ( + tools.map((tool) => (
- {tool.npmPackageName} - {tool.npmRepository && ( +
+ + {tool.exportName !== 'default' ? tool.exportName : tool.package.npmPackageName} + +
+ {tool.package.npmPackageName} +
+
+ {tool.package.npmRepository && ( - {tool.category} + {tool.package.category} - v{tool.npmVersion} - {tool.isOfficial && ( + v{tool.package.npmVersion} + {tool.package.isOfficial && ( Official )}
- {/* Tags */} - {tool.tags.length > 0 && ( -
- {tool.tags.slice(0, 5).map((tag) => ( - - {tag} - - ))} -
- )} - {/* Quality score and downloads */}
Quality Score - {tool.npmDownloadsLastMonth.toLocaleString()} downloads/mo + {tool.package.npmDownloadsLastMonth.toLocaleString()} downloads/mo
- + diff --git a/apps/web/src/components/ToolPlayground.tsx b/apps/web/src/components/ToolPlayground.tsx index ad06574..837eaf6 100644 --- a/apps/web/src/components/ToolPlayground.tsx +++ b/apps/web/src/components/ToolPlayground.tsx @@ -6,14 +6,14 @@ */ import type { TokenBreakdown as TokenData } from '@/lib/ai-agent/tool-executor-agent'; -import type { Tool } from '@tpmjs/db'; +import type { Package, Tool } from '@tpmjs/db'; import { useState } from 'react'; import ReactMarkdown from 'react-markdown'; import remarkGfm from 'remark-gfm'; import { TokenBreakdown } from './TokenBreakdown'; interface ToolPlaygroundProps { - tool: Tool; + tool: Tool & { package: Package }; } type Tab = 'input' | 'output' | 'logs' | 'tokens'; @@ -47,7 +47,7 @@ export function ToolPlayground({ tool }: ToolPlaygroundProps): React.ReactElemen try { const response = await fetch( - `/api/tools/execute/${encodeURIComponent(tool.npmPackageName)}`, + `/api/tools/execute/${encodeURIComponent(tool.package.npmPackageName)}/${encodeURIComponent(tool.exportName)}`, { method: 'POST', headers: { @@ -194,7 +194,7 @@ export function ToolPlayground({ tool }: ToolPlaygroundProps): React.ReactElemen

Interactive Playground

- Test {tool.npmPackageName} with AI-powered execution + Test {tool.package.npmPackageName} ({tool.exportName}) with AI-powered execution

{rateLimitInfo && ( diff --git a/apps/web/src/lib/ai-agent/tool-executor-agent.ts b/apps/web/src/lib/ai-agent/tool-executor-agent.ts index abea737..574987d 100644 --- a/apps/web/src/lib/ai-agent/tool-executor-agent.ts +++ b/apps/web/src/lib/ai-agent/tool-executor-agent.ts @@ -4,7 +4,7 @@ */ import { openai } from '@ai-sdk/openai'; -import type { Tool } from '@tpmjs/db'; +import type { Package, Tool } from '@tpmjs/db'; import { executePackage } from '@tpmjs/package-executor'; import { type CoreMessage, generateText } from 'ai'; import { z } from 'zod'; @@ -90,13 +90,14 @@ export function tpmjsParamsToZodSchema(parameters: TPMJSParameter[]): z.ZodObjec /** * Create AI SDK v6 tool definition from TPMJS Tool + * Requires Tool with Package relation */ -export function createToolDefinition(tool: Tool) { +export function createToolDefinition(tool: Tool & { package: Package }) { const parameters = Array.isArray(tool.parameters) ? (tool.parameters as unknown as TPMJSParameter[]) : []; - console.log('[createToolDefinition] Tool:', tool.npmPackageName); + console.log('[createToolDefinition] Tool:', tool.package.npmPackageName, '/', tool.exportName); console.log('[createToolDefinition] Parameters array:', JSON.stringify(parameters)); console.log('[createToolDefinition] Parameters length:', parameters.length); @@ -108,7 +109,9 @@ export function createToolDefinition(tool: Tool) { console.log('[createToolDefinition] Created Zod schema:', inputSchema); - const sanitizedName = sanitizeToolName(tool.npmPackageName); + const sanitizedName = sanitizeToolName( + `${tool.package.npmPackageName}-${tool.exportName}` + ); // AI SDK v6 tool definition return { @@ -118,9 +121,10 @@ export function createToolDefinition(tool: Tool) { console.log('[Tool execute] Running:', sanitizedName, params); // Execute the actual npm package in a sandbox + // Use the actual export name from the Tool record const result = await executePackage( - tool.npmPackageName, - 'default', // Most TPMJS packages export a default function + tool.package.npmPackageName, + tool.exportName, // Use actual export name (e.g., "helloWorldTool", "default") params, { timeout: 5000 } ); @@ -186,15 +190,18 @@ function sanitizeToolName(npmPackageName: string): string { /** * Execute tool with AI agent using AI SDK v6 + * Requires Tool with Package relation */ export async function executeToolWithAgent( - tool: Tool, + tool: Tool & { package: Package }, userPrompt: string, onChunk?: (chunk: string) => void, onTokenUpdate?: (tokens: Partial) => void ) { const toolDef = createToolDefinition(tool); - const sanitizedToolName = sanitizeToolName(tool.npmPackageName); + const sanitizedToolName = sanitizeToolName( + `${tool.package.npmPackageName}-${tool.exportName}` + ); console.log('[executeToolWithAgent] Tool name:', sanitizedToolName); diff --git a/manual-tools.ts b/manual-tools.ts new file mode 100644 index 0000000..fd5e5ff --- /dev/null +++ b/manual-tools.ts @@ -0,0 +1,762 @@ +/** + * Manual Tools Registry + * + * This file contains tools that should be included in TPMJS but don't follow + * the standard tpmjs field specification in their package.json. + * + * These tools are manually curated and synced to the database via the + * manual sync script. + */ + +export interface ManualTool { + // Package metadata + npmPackageName: string; + npmVersion?: string; // Optional - will fetch latest if not specified + category: 'text-analysis' | 'code-generation' | 'data-processing' | 'image-generation' | 'audio-processing' | 'search' | 'integration' | 'other'; + frameworks: Array<'vercel-ai' | 'langchain' | 'llamaindex' | 'other'>; + + // Tool definition + exportName: string; + description: string; + + // Optional rich metadata + parameters?: Array<{ + name: string; + type: string; + description: string; + required: boolean; + default?: string; + }>; + + returns?: { + type: string; + description: string; + }; + + aiAgent?: { + useCase: string; + limitations?: string; + examples?: string[]; + }; + + // Environment variables + env?: Array<{ + name: string; + description: string; + required: boolean; + }>; + + // Additional metadata not in npm + tags?: string[]; + docsUrl?: string; + apiKeyUrl?: string; + websiteUrl?: string; +} + +export const manualTools: ManualTool[] = [ + { + npmPackageName: 'ai-sdk-tool-code-execution', + category: 'code-generation', + frameworks: ['vercel-ai'], + exportName: 'executeCode', + description: 'Execute Python code in a sandboxed environment using Vercel Sandbox. Run calculations, data processing, and other computational tasks safely in an isolated environment with Python 3.13.', + tags: ['code-execution', 'sandbox'], + env: [ + { + name: 'VERCEL_OIDC_TOKEN', + description: 'Vercel OIDC token for sandbox authentication', + required: true, + }, + ], + parameters: [ + { + name: 'code', + type: 'string', + description: 'Python code to execute in the sandbox', + required: true, + }, + ], + returns: { + type: 'object', + description: 'Execution result with stdout, stderr, and return value', + }, + aiAgent: { + useCase: 'Use when you need to perform calculations, data processing, or execute Python code safely', + limitations: 'Python 3.13 only. No network access. 30 second execution timeout.', + examples: [ + 'Perform complex mathematical calculations', + 'Process data with pandas/numpy', + 'Generate plots with matplotlib', + ], + }, + docsUrl: 'https://vercel.com/docs/vercel-sandbox', + apiKeyUrl: 'https://vercel.com/docs/vercel-sandbox#authentication', + websiteUrl: 'https://vercel.com/docs/vercel-sandbox', + }, + { + npmPackageName: '@exalabs/ai-sdk', + category: 'search', + frameworks: ['vercel-ai'], + exportName: 'webSearch', + description: 'Search the web for current information using Exa\'s AI-powered search API. Returns high-quality, relevant results optimized for LLM consumption.', + tags: ['search', 'web', 'extraction'], + env: [ + { + name: 'EXA_API_KEY', + description: 'API key for Exa search service', + required: true, + }, + ], + parameters: [ + { + name: 'query', + type: 'string', + description: 'Search query', + required: true, + }, + { + name: 'numResults', + type: 'number', + description: 'Number of results to return (1-10)', + required: false, + default: '5', + }, + ], + returns: { + type: 'array', + description: 'Array of search results with title, URL, and content', + }, + aiAgent: { + useCase: 'Use when you need current information, news, research papers, or web content', + limitations: 'Requires API key. Rate limits apply based on plan.', + examples: [ + 'Find latest AI developments', + 'Search for technical documentation', + 'Research current events', + ], + }, + docsUrl: 'https://docs.exa.ai/reference/vercel', + apiKeyUrl: 'https://dashboard.exa.ai/api-keys', + websiteUrl: 'https://exa.ai', + }, + { + npmPackageName: '@parallel-web/ai-sdk-tools', + category: 'search', + frameworks: ['vercel-ai'], + exportName: 'searchTool', + description: 'Search and extract context from the web with token-optimized results. Parallel compresses web results for optimal inference efficiency.', + tags: ['search', 'web', 'extraction'], + env: [ + { + name: 'PARALLEL_API_KEY', + description: 'API key for Parallel search service', + required: true, + }, + ], + aiAgent: { + useCase: 'Use when you need web search with minimal token usage', + examples: [ + 'Search for current information', + 'Extract structured data from websites', + ], + }, + apiKeyUrl: 'https://platform.parallel.ai', + websiteUrl: 'https://parallel.ai', + }, + { + npmPackageName: '@parallel-web/ai-sdk-tools', + category: 'search', + frameworks: ['vercel-ai'], + exportName: 'extractTool', + description: 'Extract structured content from web pages with token-optimized compression.', + tags: ['extraction', 'web'], + env: [ + { + name: 'PARALLEL_API_KEY', + description: 'API key for Parallel search service', + required: true, + }, + ], + parameters: [ + { + name: 'url', + type: 'string', + description: 'URL of the page to extract content from', + required: true, + }, + ], + returns: { + type: 'object', + description: 'Extracted and compressed page content', + }, + aiAgent: { + useCase: 'Use to extract clean content from specific URLs', + examples: [ + 'Extract article content', + 'Parse documentation pages', + ], + }, + websiteUrl: 'https://parallel.ai', + }, + { + npmPackageName: 'ctx-zip', + category: 'code-generation', + frameworks: ['vercel-ai'], + exportName: 'createVercelSandboxCodeMode', + description: 'Transform MCP tools and AI SDK tools into code, write to Vercel sandbox filesystem, and execute in isolated environment.', + tags: ['code-execution', 'sandbox', 'mcp', 'code-mode'], + env: [ + { + name: 'VERCEL_OIDC_TOKEN', + description: 'Vercel OIDC token for sandbox authentication', + required: true, + }, + ], + aiAgent: { + useCase: 'Use when you need to combine MCP tools with code execution in a sandbox', + limitations: 'Requires Vercel Sandbox access', + examples: [ + 'Execute code using MCP server tools', + 'Combine multiple tool sources', + ], + }, + docsUrl: 'https://github.com/karthikscale3/ctx-zip/blob/main/README.md', + apiKeyUrl: 'https://vercel.com/docs/vercel-sandbox#authentication', + websiteUrl: 'https://github.com/karthikscale3/ctx-zip', + }, + { + npmPackageName: '@perplexity-ai/ai-sdk', + category: 'search', + frameworks: ['vercel-ai'], + exportName: 'perplexitySearch', + description: 'Search the web with real-time results and advanced filtering powered by Perplexity Search API. Supports ranked results with domain, language, date range, and recency filters.', + tags: ['search', 'web'], + env: [ + { + name: 'PERPLEXITY_API_KEY', + description: 'API key for Perplexity search service', + required: true, + }, + ], + parameters: [ + { + name: 'query', + type: 'string', + description: 'Search query', + required: true, + }, + { + name: 'filters', + type: 'object', + description: 'Optional filters for domain, language, date range, etc.', + required: false, + }, + ], + returns: { + type: 'array', + description: 'Ranked search results with metadata', + }, + aiAgent: { + useCase: 'Use for comprehensive web search with filtering capabilities', + examples: [ + 'Find latest AI developments', + 'Search with date range filters', + 'Domain-specific searches', + ], + }, + docsUrl: 'https://docs.perplexity.ai/guides/search-quickstart', + apiKeyUrl: 'https://www.perplexity.ai/account/api/keys', + websiteUrl: 'https://www.perplexity.ai', + }, + { + npmPackageName: '@tavily/ai-sdk', + category: 'search', + frameworks: ['vercel-ai'], + exportName: 'tavilySearch', + description: 'Real-time web search optimized for AI applications. Provides comprehensive web research including search, content extraction, website crawling, and site mapping.', + tags: ['search', 'extract', 'crawl'], + env: [ + { + name: 'TAVILY_API_KEY', + description: 'API key for Tavily search service', + required: true, + }, + ], + aiAgent: { + useCase: 'Use for comprehensive web research and content extraction', + examples: [ + 'Research complex topics', + 'Extract structured information', + 'Crawl entire websites', + ], + }, + docsUrl: 'https://docs.tavily.com/documentation/integrations/vercel', + apiKeyUrl: 'https://app.tavily.com/home', + websiteUrl: 'https://tavily.com', + }, + { + npmPackageName: 'firecrawl-aisdk', + category: 'search', + frameworks: ['vercel-ai'], + exportName: 'scrapeTool', + description: 'Scrape any website into clean markdown format. Convert web pages to LLM-friendly markdown with automatic cleaning and formatting.', + tags: ['scraping', 'web', 'extraction'], + env: [ + { + name: 'FIRECRAWL_API_KEY', + description: 'API key for Firecrawl service', + required: true, + }, + ], + parameters: [ + { + name: 'url', + type: 'string', + description: 'URL of the website to scrape', + required: true, + }, + ], + returns: { + type: 'object', + description: 'Scraped content in clean markdown format', + }, + aiAgent: { + useCase: 'Use when you need to convert web pages to clean, structured markdown', + examples: [ + 'Scrape documentation pages', + 'Extract article content', + 'Convert web pages to markdown', + ], + }, + docsUrl: 'https://docs.firecrawl.dev/integrations/ai-sdk', + apiKeyUrl: 'https://firecrawl.dev/app/api-keys', + websiteUrl: 'https://firecrawl.dev', + }, + { + npmPackageName: 'firecrawl-aisdk', + category: 'search', + frameworks: ['vercel-ai'], + exportName: 'searchTool', + description: 'Search the web and get results in clean markdown format optimized for AI consumption.', + tags: ['search', 'web'], + env: [ + { + name: 'FIRECRAWL_API_KEY', + description: 'API key for Firecrawl service', + required: true, + }, + ], + websiteUrl: 'https://firecrawl.dev', + }, + { + npmPackageName: 'firecrawl-aisdk', + category: 'search', + frameworks: ['vercel-ai'], + exportName: 'crawlTool', + description: 'Crawl entire websites and extract structured data. Automatically discover and process multiple pages.', + tags: ['crawling', 'web', 'extraction'], + env: [ + { + name: 'FIRECRAWL_API_KEY', + description: 'API key for Firecrawl service', + required: true, + }, + ], + parameters: [ + { + name: 'url', + type: 'string', + description: 'Starting URL to begin crawling', + required: true, + }, + { + name: 'maxPages', + type: 'number', + description: 'Maximum number of pages to crawl', + required: false, + default: '10', + }, + ], + aiAgent: { + useCase: 'Use when you need to crawl and extract data from entire websites', + examples: [ + 'Crawl documentation sites', + 'Extract all articles from a blog', + 'Build knowledge base from website', + ], + }, + websiteUrl: 'https://firecrawl.dev', + }, + { + npmPackageName: 'bedrock-agentcore', + category: 'code-generation', + frameworks: ['vercel-ai'], + exportName: 'CodeInterpreterTools', + description: 'Isolated sandbox for executing Python, JavaScript, and TypeScript code to solve complex tasks. Fully managed by Amazon Bedrock.', + tags: ['code-execution', 'sandbox'], + env: [ + { + name: 'AWS_ROLE_ARN', + description: 'AWS IAM role ARN for Bedrock access', + required: true, + }, + ], + aiAgent: { + useCase: 'Use for secure code execution in AWS-managed sandbox', + limitations: 'Requires AWS credentials and Bedrock access', + examples: [ + 'Execute Python data analysis', + 'Run JavaScript computations', + 'TypeScript code execution', + ], + }, + docsUrl: 'https://github.com/aws/bedrock-agentcore-sdk-typescript', + apiKeyUrl: 'https://vercel.com/docs/oidc/aws', + websiteUrl: 'https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/built-in-tools.html', + }, + { + npmPackageName: 'bedrock-agentcore', + category: 'integration', + frameworks: ['vercel-ai'], + exportName: 'BrowserTools', + description: 'Fast and secure cloud-based browser runtime for web automation. Fill forms, navigate websites, and extract information in managed environment.', + tags: ['browser-automation', 'web'], + env: [ + { + name: 'AWS_ROLE_ARN', + description: 'AWS IAM role ARN for Bedrock access', + required: true, + }, + ], + aiAgent: { + useCase: 'Use for browser automation and web interaction tasks', + examples: [ + 'Fill web forms', + 'Navigate websites', + 'Extract dynamic content', + ], + }, + websiteUrl: 'https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/built-in-tools.html', + }, + { + npmPackageName: '@superagent-ai/ai-sdk', + category: 'other', + frameworks: ['vercel-ai'], + exportName: 'guard', + description: 'Protect AI apps from prompt injection and security threats. Detect and block malicious inputs before they reach your LLM.', + tags: ['security', 'guardrails', 'prompt-injection'], + env: [ + { + name: 'SUPERAGENT_API_KEY', + description: 'API key for Superagent security service', + required: true, + }, + ], + parameters: [ + { + name: 'input', + type: 'string', + description: 'User input to check for security threats', + required: true, + }, + ], + returns: { + type: 'object', + description: 'Security analysis with threat detection results', + }, + aiAgent: { + useCase: 'Use to validate user inputs for security threats before processing', + examples: [ + 'Detect prompt injection attempts', + 'Block malicious inputs', + 'Security validation', + ], + }, + docsUrl: 'https://docs.superagent.sh', + apiKeyUrl: 'https://dashboard.superagent.sh', + websiteUrl: 'https://superagent.sh', + }, + { + npmPackageName: '@superagent-ai/ai-sdk', + category: 'other', + frameworks: ['vercel-ai'], + exportName: 'redact', + description: 'Redact PII/PHI from text including SSNs, emails, phone numbers, and other sensitive information.', + tags: ['security', 'pii', 'redaction'], + env: [ + { + name: 'SUPERAGENT_API_KEY', + description: 'API key for Superagent security service', + required: true, + }, + ], + parameters: [ + { + name: 'text', + type: 'string', + description: 'Text containing potentially sensitive information', + required: true, + }, + ], + returns: { + type: 'object', + description: 'Redacted text with PII/PHI removed or masked', + }, + aiAgent: { + useCase: 'Use to remove sensitive information from text before processing', + examples: [ + 'Redact SSNs from documents', + 'Remove email addresses', + 'Mask phone numbers', + ], + }, + websiteUrl: 'https://superagent.sh', + }, + { + npmPackageName: '@superagent-ai/ai-sdk', + category: 'other', + frameworks: ['vercel-ai'], + exportName: 'verify', + description: 'Verify AI-generated claims against source materials. Fact-check and validate LLM outputs for accuracy.', + tags: ['verification', 'fact-checking'], + env: [ + { + name: 'SUPERAGENT_API_KEY', + description: 'API key for Superagent security service', + required: true, + }, + ], + parameters: [ + { + name: 'claim', + type: 'string', + description: 'Claim to verify', + required: true, + }, + { + name: 'sources', + type: 'array', + description: 'Source materials to verify against', + required: true, + }, + ], + returns: { + type: 'object', + description: 'Verification result with confidence score', + }, + aiAgent: { + useCase: 'Use to fact-check LLM outputs against trusted sources', + examples: [ + 'Verify factual claims', + 'Check citations', + 'Validate information accuracy', + ], + }, + websiteUrl: 'https://superagent.sh', + }, + { + npmPackageName: '@valyu/ai-sdk', + category: 'search', + frameworks: ['vercel-ai'], + exportName: 'webSearch', + description: 'Real-time web search for current information and news.', + tags: ['search', 'web'], + env: [ + { + name: 'VALYU_API_KEY', + description: 'API key for Valyu search service', + required: true, + }, + ], + aiAgent: { + useCase: 'Use for general web search and current information', + examples: [ + 'Find latest news', + 'Search for current events', + ], + }, + docsUrl: 'https://docs.valyu.ai/integrations/vercel-ai-sdk', + apiKeyUrl: 'https://platform.valyu.ai', + websiteUrl: 'https://valyu.ai', + }, + { + npmPackageName: '@valyu/ai-sdk', + category: 'search', + frameworks: ['vercel-ai'], + exportName: 'financeSearch', + description: 'Search financial data including stock prices, earnings, income statements, cash flows, and market data.', + tags: ['search', 'finance', 'domain-search'], + env: [ + { + name: 'VALYU_API_KEY', + description: 'API key for Valyu search service', + required: true, + }, + ], + aiAgent: { + useCase: 'Use for financial data and market research', + examples: [ + 'Get stock prices', + 'Find earnings reports', + 'Search financial statements', + ], + }, + websiteUrl: 'https://valyu.ai', + }, + { + npmPackageName: '@valyu/ai-sdk', + category: 'search', + frameworks: ['vercel-ai'], + exportName: 'paperSearch', + description: 'Full-text search across PubMed, arXiv, bioRxiv, and medRxiv research papers.', + tags: ['search', 'research', 'domain-search'], + env: [ + { + name: 'VALYU_API_KEY', + description: 'API key for Valyu search service', + required: true, + }, + ], + aiAgent: { + useCase: 'Use to search academic and research papers', + examples: [ + 'Find medical research', + 'Search arXiv papers', + 'Look up scientific publications', + ], + }, + websiteUrl: 'https://valyu.ai', + }, + { + npmPackageName: '@valyu/ai-sdk', + category: 'search', + frameworks: ['vercel-ai'], + exportName: 'bioSearch', + description: 'Search biomedical information including clinical trials, FDA drug labels, PubMed, medRxiv, and bioRxiv.', + tags: ['search', 'biomedical', 'domain-search'], + env: [ + { + name: 'VALYU_API_KEY', + description: 'API key for Valyu search service', + required: true, + }, + ], + aiAgent: { + useCase: 'Use for biomedical and clinical research', + examples: [ + 'Search clinical trials', + 'Find FDA drug information', + 'Research medical studies', + ], + }, + websiteUrl: 'https://valyu.ai', + }, + { + npmPackageName: '@valyu/ai-sdk', + category: 'search', + frameworks: ['vercel-ai'], + exportName: 'patentSearch', + description: 'Search USPTO patent database for patents and patent applications.', + tags: ['search', 'patents', 'domain-search'], + env: [ + { + name: 'VALYU_API_KEY', + description: 'API key for Valyu search service', + required: true, + }, + ], + aiAgent: { + useCase: 'Use to search and research patents', + examples: [ + 'Find existing patents', + 'Research patent applications', + 'Prior art search', + ], + }, + websiteUrl: 'https://valyu.ai', + }, + { + npmPackageName: '@valyu/ai-sdk', + category: 'search', + frameworks: ['vercel-ai'], + exportName: 'secSearch', + description: 'Search SEC filings including 10-K, 10-Q, and 8-K reports.', + tags: ['search', 'sec', 'domain-search'], + env: [ + { + name: 'VALYU_API_KEY', + description: 'API key for Valyu search service', + required: true, + }, + ], + aiAgent: { + useCase: 'Use to search SEC filings and corporate reports', + examples: [ + 'Find 10-K annual reports', + 'Search quarterly filings', + 'Research corporate disclosures', + ], + }, + websiteUrl: 'https://valyu.ai', + }, + { + npmPackageName: '@valyu/ai-sdk', + category: 'search', + frameworks: ['vercel-ai'], + exportName: 'economicsSearch', + description: 'Search economic data from BLS, FRED, and World Bank databases.', + tags: ['search', 'economics', 'domain-search'], + env: [ + { + name: 'VALYU_API_KEY', + description: 'API key for Valyu search service', + required: true, + }, + ], + aiAgent: { + useCase: 'Use to search economic indicators and statistics', + examples: [ + 'Find employment data', + 'Search GDP statistics', + 'Research economic indicators', + ], + }, + websiteUrl: 'https://valyu.ai', + }, + { + npmPackageName: '@valyu/ai-sdk', + category: 'search', + frameworks: ['vercel-ai'], + exportName: 'companyResearch', + description: 'Generate comprehensive company research reports with financial data, news, and analysis.', + tags: ['search', 'research', 'company-analysis'], + env: [ + { + name: 'VALYU_API_KEY', + description: 'API key for Valyu search service', + required: true, + }, + ], + parameters: [ + { + name: 'company', + type: 'string', + description: 'Company name or ticker symbol', + required: true, + }, + ], + returns: { + type: 'object', + description: 'Comprehensive research report with financial and market data', + }, + aiAgent: { + useCase: 'Use to generate detailed company research and analysis', + examples: [ + 'Company financial analysis', + 'Competitive research', + 'Market position analysis', + ], + }, + websiteUrl: 'https://valyu.ai', + }, +]; diff --git a/package.json b/package.json index 0669b4f..1ecac4e 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "dependency-cruiser": "^17.3.1", "knip": "^5.70.2", "lefthook": "^1.10.1", + "tsx": "^4.21.0", "turbo": "^2.6.1", "type-coverage": "^2.29.7", "typescript": "^5.9.3" diff --git a/packages/db/prisma/schema.prisma b/packages/db/prisma/schema.prisma index 103dc13..da53165 100644 --- a/packages/db/prisma/schema.prisma +++ b/packages/db/prisma/schema.prisma @@ -10,46 +10,68 @@ datasource db { url = env("DATABASE_URL") } -/// Main tools table - stores all discovered NPM packages with TPMJS metadata -model Tool { +/// Package table - stores NPM package metadata (package-level) +model Package { id String @id @default(cuid()) // NPM Metadata - npmPackageName String @unique @map("npm_package_name") @db.VarChar(214) - npmVersion String @map("npm_version") @db.VarChar(50) - npmPublishedAt DateTime @map("npm_published_at") - npmDescription String? @map("npm_description") @db.Text - npmRepository Json? @map("npm_repository") @db.JsonB - npmHomepage String? @map("npm_homepage") @db.Text - npmLicense String? @map("npm_license") @db.VarChar(50) - npmKeywords String[] @default([]) @map("npm_keywords") @db.Text - npmReadme String? @map("npm_readme") @db.Text - npmAuthor Json? @map("npm_author") @db.JsonB - npmMaintainers Json? @map("npm_maintainers") @db.JsonB + npmPackageName String @unique @map("npm_package_name") @db.VarChar(214) + npmVersion String @map("npm_version") @db.VarChar(50) + npmPublishedAt DateTime @map("npm_published_at") + npmDescription String? @map("npm_description") @db.Text + npmRepository Json? @map("npm_repository") @db.JsonB + npmHomepage String? @map("npm_homepage") @db.Text + npmLicense String? @map("npm_license") @db.VarChar(50) + npmKeywords String[] @default([]) @map("npm_keywords") @db.Text + npmReadme String? @map("npm_readme") @db.Text + npmAuthor Json? @map("npm_author") @db.JsonB + npmMaintainers Json? @map("npm_maintainers") @db.JsonB - // TPMJS Metadata (from package.json tpmjs field) + // TPMJS Package-Level Metadata category String @db.VarChar(50) - description String @db.Text - example String? @db.Text - parameters Json? @db.JsonB - returns Json? @db.JsonB - authentication Json? @db.JsonB - pricing Json? @db.JsonB + env Json? @db.JsonB // Environment variables (shared across tools) frameworks String[] @default([]) @db.Text - links Json? @db.JsonB - tags String[] @default([]) @db.Text - status String? @db.VarChar(20) - aiAgent Json? @map("ai_agent") @db.JsonB - - // Discovery Metadata + tier String @db.VarChar(20) // 'minimal' | 'rich' discoveryMethod String @map("discovery_method") @db.VarChar(20) // 'keyword' | 'changes-feed' - isOfficial Boolean @default(false) @map("is_official") - tier String @db.VarChar(20) // 'minimal' | 'rich' + isOfficial Boolean @default(false) @map("is_official") - // Metrics - npmDownloadsLastMonth Int? @default(0) @map("npm_downloads_last_month") - githubStars Int? @default(0) @map("github_stars") - qualityScore Decimal? @map("quality_score") @db.Decimal(3, 2) // 0.00 to 1.00 + // Package Metrics + npmDownloadsLastMonth Int? @default(0) @map("npm_downloads_last_month") + githubStars Int? @default(0) @map("github_stars") + + // Timestamps + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") + + // Relations + tools Tool[] + + @@index([category]) + @@index([isOfficial]) + @@index([npmDownloadsLastMonth]) + @@index([createdAt]) + @@map("packages") +} + +/// Tool table - stores individual tools within packages +model Tool { + id String @id @default(cuid()) + + // Package Relation + packageId String @map("package_id") + package Package @relation(fields: [packageId], references: [id], onDelete: Cascade) + + // Tool Identity + exportName String @map("export_name") @db.VarChar(100) // e.g., "helloWorldTool", "default" + + // Tool Metadata + description String @db.Text + parameters Json? @db.JsonB + returns Json? @db.JsonB + aiAgent Json? @map("ai_agent") @db.JsonB + + // Tool Metrics + qualityScore Decimal? @map("quality_score") @db.Decimal(3, 2) // 0.00 to 1.00 // Timestamps createdAt DateTime @default(now()) @map("created_at") @@ -58,11 +80,8 @@ model Tool { // Relations simulations Simulation[] - @@index([category]) - @@index([isOfficial]) + @@unique([packageId, exportName]) @@index([qualityScore]) - @@index([npmDownloadsLastMonth]) - @@index([createdAt]) @@map("tools") } @@ -81,14 +100,14 @@ model SyncCheckpoint { model SyncLog { id String @id @default(cuid()) - source String @db.VarChar(50) // 'changes-feed' | 'keyword-search' | 'metrics' - status String @db.VarChar(20) // 'success' | 'error' | 'partial' - processed Int @default(0) // Number of packages processed - skipped Int @default(0) // Number of packages skipped - errors Int @default(0) // Number of errors encountered - message String? @db.Text // Error message or summary - metadata Json? @db.JsonB // Additional context - createdAt DateTime @default(now()) @map("created_at") + source String @db.VarChar(50) // 'changes-feed' | 'keyword-search' | 'metrics' + status String @db.VarChar(20) // 'success' | 'error' | 'partial' + processed Int @default(0) // Number of packages processed + skipped Int @default(0) // Number of packages skipped + errors Int @default(0) // Number of errors encountered + message String? @db.Text // Error message or summary + metadata Json? @db.JsonB // Additional context + createdAt DateTime @default(now()) @map("created_at") @@index([source]) @@index([status]) @@ -121,8 +140,8 @@ model Simulation { model String? @db.VarChar(50) // Relations - tokenUsage TokenUsage? - logs ExecutionLog[] + tokenUsage TokenUsage? + logs ExecutionLog[] createdAt DateTime @default(now()) @map("created_at") completedAt DateTime? @map("completed_at") @@ -140,12 +159,12 @@ model TokenUsage { simulationId String @unique @map("simulation_id") simulation Simulation @relation(fields: [simulationId], references: [id], onDelete: Cascade) - inputTokens Int @default(0) @map("input_tokens") - toolDescTokens Int @default(0) @map("tool_desc_tokens") - schemaTokens Int @default(0) @map("schema_tokens") - outputTokens Int @default(0) @map("output_tokens") - totalTokens Int @default(0) @map("total_tokens") - estimatedCost Decimal? @map("estimated_cost") @db.Decimal(10, 6) + inputTokens Int @default(0) @map("input_tokens") + toolDescTokens Int @default(0) @map("tool_desc_tokens") + schemaTokens Int @default(0) @map("schema_tokens") + outputTokens Int @default(0) @map("output_tokens") + totalTokens Int @default(0) @map("total_tokens") + estimatedCost Decimal? @map("estimated_cost") @db.Decimal(10, 6) createdAt DateTime @default(now()) @map("created_at") @@map("token_usage") diff --git a/packages/tools/hello/CHANGELOG.md b/packages/tools/hello/CHANGELOG.md new file mode 100644 index 0000000..4f9b6e2 --- /dev/null +++ b/packages/tools/hello/CHANGELOG.md @@ -0,0 +1,9 @@ +# @tpmjs/hello + +## 0.0.2 + +### Patch Changes + +- feat: add multi-tool example package with helloWorldTool and helloNameTool + + This is the first TPMJS package published with the new multi-tool format, demonstrating support for multiple tool exports in a single package. diff --git a/packages/tools/hello/package.json b/packages/tools/hello/package.json index 4594914..da3ffcb 100644 --- a/packages/tools/hello/package.json +++ b/packages/tools/hello/package.json @@ -1,7 +1,6 @@ { "name": "@tpmjs/hello", - "version": "0.0.1", - "private": true, + "version": "0.0.2", "description": "Example TPMJS tools - Hello World and Hello Name", "main": "dist/index.js", "types": "dist/index.d.ts", @@ -11,17 +10,68 @@ "clean": "rm -rf dist", "type-check": "tsc --noEmit" }, - "keywords": ["tpmjs-tool", "ai-sdk", "hello", "example"], + "keywords": [ + "tpmjs-tool", + "ai-sdk", + "hello", + "example" + ], "tpmjs": { "category": "text-analysis", - "description": "Simple greeting tools - Hello World and personalized Hello Name greetings" + "frameworks": [ + "vercel-ai" + ], + "tools": [ + { + "exportName": "helloWorldTool", + "description": "Returns a simple 'Hello, World!' greeting with optional timestamp and customizable message", + "parameters": [ + { + "name": "includeTimestamp", + "type": "boolean", + "description": "Whether to include the current timestamp in the greeting", + "required": false + } + ], + "returns": { + "type": "string", + "description": "A greeting message, optionally with timestamp" + } + }, + { + "exportName": "helloNameTool", + "description": "Returns a personalized greeting with the provided name", + "parameters": [ + { + "name": "name", + "type": "string", + "description": "The name to greet", + "required": true + }, + { + "name": "formal", + "type": "boolean", + "description": "Whether to use formal greeting (Mr./Ms.)", + "required": false + } + ], + "returns": { + "type": "string", + "description": "A personalized greeting message" + } + } + ] }, "dependencies": { "ai": "6.0.0-beta.124" }, "devDependencies": { "@tpmjs/tsconfig": "workspace:*", + "tsx": "^4.20.6", "typescript": "^5.9.3" }, - "files": ["dist", "README.md"] + "files": [ + "dist", + "README.md" + ] } diff --git a/packages/types/src/tpmjs.ts b/packages/types/src/tpmjs.ts index 46e58ed..def5486 100644 --- a/packages/types/src/tpmjs.ts +++ b/packages/types/src/tpmjs.ts @@ -67,23 +67,53 @@ export const TpmjsAiAgentSchema = z.object({ export type TpmjsAiAgent = z.infer; /** - * Minimal tier schema - required fields only - * This is the minimum required to publish a tool to TPMJS + * Individual tool definition within a multi-tool package */ -export const TpmjsMinimalSchema = z.object({ +export const TpmjsToolDefinitionSchema = z.object({ + exportName: z.string().min(1, 'Export name is required'), + description: z.string().min(20, 'Description must be at least 20 characters').max(500), + parameters: z.array(TpmjsParameterSchema).optional(), + returns: TpmjsReturnsSchema.optional(), + aiAgent: TpmjsAiAgentSchema.optional(), +}); + +export type TpmjsToolDefinition = z.infer; + +/** + * Multi-tool format - NEW SCHEMA + * Package-level metadata with array of tools + */ +export const TpmjsMultiToolSchema = z.object({ + category: z.enum(TPMJS_CATEGORIES, { + message: `Category must be one of: ${TPMJS_CATEGORIES.join(', ')}`, + }), + tools: z.array(TpmjsToolDefinitionSchema).min(1, 'At least one tool is required'), + env: z.array(TpmjsEnvSchema).optional(), + frameworks: z + .array(z.enum(['vercel-ai', 'langchain', 'llamaindex', 'haystack', 'semantic-kernel'])) + .optional(), +}); + +export type TpmjsMultiTool = z.infer; + +/** + * Legacy minimal tier schema - DEPRECATED + * Kept for backward compatibility with auto-migration + */ +export const TpmjsLegacyMinimalSchema = z.object({ category: z.enum(TPMJS_CATEGORIES, { message: `Category must be one of: ${TPMJS_CATEGORIES.join(', ')}`, }), description: z.string().min(20, 'Description must be at least 20 characters').max(500), }); -export type TpmjsMinimal = z.infer; +export type TpmjsLegacyMinimal = z.infer; /** - * Rich tier schema - includes optional enhanced metadata - * Tools with these fields get better visibility and quality scores + * Legacy rich tier schema - DEPRECATED + * Kept for backward compatibility with auto-migration */ -export const TpmjsRichSchema = TpmjsMinimalSchema.extend({ +export const TpmjsLegacyRichSchema = TpmjsLegacyMinimalSchema.extend({ parameters: z.array(TpmjsParameterSchema).optional(), returns: TpmjsReturnsSchema.optional(), env: z.array(TpmjsEnvSchema).optional(), @@ -93,57 +123,129 @@ export const TpmjsRichSchema = TpmjsMinimalSchema.extend({ aiAgent: TpmjsAiAgentSchema.optional(), }); -export type TpmjsRich = z.infer; +export type TpmjsLegacyRich = z.infer; /** - * Union type for either tier + * Union type for legacy formats */ -export type TpmjsField = TpmjsMinimal | TpmjsRich; +export type TpmjsLegacy = TpmjsLegacyMinimal | TpmjsLegacyRich; /** - * Validation result type + * Union type for all formats (new multi-tool + legacy) + */ +export type TpmjsField = TpmjsMultiTool | TpmjsLegacy; + +// Backward compatibility aliases +export type TpmjsMinimal = TpmjsLegacyMinimal; +export type TpmjsRich = TpmjsLegacyRich; +export const TpmjsMinimalSchema = TpmjsLegacyMinimalSchema; +export const TpmjsRichSchema = TpmjsLegacyRichSchema; + +/** + * Extended validation result type for multi-tool support */ export interface ValidationResult { valid: boolean; tier: 'minimal' | 'rich' | null; data?: TpmjsField; errors?: z.ZodError; + // New fields for multi-tool support + packageData?: { + category: TpmjsCategory; + env?: TpmjsEnv[]; + frameworks?: string[]; + }; + tools?: TpmjsToolDefinition[]; + wasLegacyFormat?: boolean; } /** * Validates a tpmjs field and determines its tier + * Supports both new multi-tool format and legacy single-tool format with auto-migration */ export function validateTpmjsField(tpmjs: unknown): ValidationResult { - // Try rich tier first - const richResult = TpmjsRichSchema.safeParse(tpmjs); - if (richResult.success) { - // Check if it has any rich-tier fields - const data = richResult.data; - const hasRichFields = - data.parameters || data.returns || data.env || data.frameworks || data.aiAgent; + // Try new multi-tool format first + const multiResult = TpmjsMultiToolSchema.safeParse(tpmjs); + if (multiResult.success) { + const data = multiResult.data; + + // Determine tier based on tool richness + const hasRichFields = data.tools.some( + (tool) => tool.parameters || tool.returns || tool.aiAgent + ) || data.env || data.frameworks; return { valid: true, tier: hasRichFields ? 'rich' : 'minimal', - data: richResult.data, + data: data, + packageData: { + category: data.category, + env: data.env, + frameworks: data.frameworks, + }, + tools: data.tools, + wasLegacyFormat: false, }; } - // Try minimal tier - const minimalResult = TpmjsMinimalSchema.safeParse(tpmjs); + // Try legacy rich tier format with auto-migration + const richResult = TpmjsLegacyRichSchema.safeParse(tpmjs); + if (richResult.success) { + const legacyData = richResult.data; + + // Auto-migrate to multi-tool format + const tool: TpmjsToolDefinition = { + exportName: 'default', + description: legacyData.description, + parameters: legacyData.parameters, + returns: legacyData.returns, + aiAgent: legacyData.aiAgent, + }; + + const hasRichFields = + legacyData.parameters || legacyData.returns || legacyData.env || + legacyData.frameworks || legacyData.aiAgent; + + return { + valid: true, + tier: hasRichFields ? 'rich' : 'minimal', + data: legacyData, + packageData: { + category: legacyData.category, + env: legacyData.env, + frameworks: legacyData.frameworks, + }, + tools: [tool], + wasLegacyFormat: true, + }; + } + + // Try legacy minimal tier format with auto-migration + const minimalResult = TpmjsLegacyMinimalSchema.safeParse(tpmjs); if (minimalResult.success) { + // Auto-migrate to multi-tool format + const tool: TpmjsToolDefinition = { + exportName: 'default', + description: minimalResult.data.description, + }; + return { valid: true, tier: 'minimal', data: minimalResult.data, + packageData: { + category: minimalResult.data.category, + }, + tools: [tool], + wasLegacyFormat: true, }; } - // Invalid + // Invalid - return error from multi-tool schema (most informative) return { valid: false, tier: null, - errors: minimalResult.error, + errors: multiResult.error, }; } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 16c6a38..255716e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -29,6 +29,9 @@ importers: lefthook: specifier: ^1.10.1 version: 1.13.6 + tsx: + specifier: ^4.21.0 + version: 4.21.0 turbo: specifier: ^2.6.1 version: 2.6.1 @@ -68,9 +71,6 @@ importers: firecrawl-aisdk: specifier: ^0.7.2 version: 0.7.2 - fish-joke-generator: - specifier: ^1.1.0 - version: 1.1.0(openai@6.9.1(ws@8.18.3)(zod@4.1.13))(zod@4.1.13) next: specifier: ^16.0.4 version: 16.0.4(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) @@ -86,13 +86,16 @@ importers: react-dom: specifier: ^19.0.0 version: 19.2.0(react@19.2.0) + streamdown: + specifier: ^1.6.9 + version: 1.6.9(@types/mdast@4.0.4)(micromark-util-types@2.0.2)(micromark@4.0.2)(react@19.2.0) zod: specifier: ^4.0.0 version: 4.1.13 devDependencies: '@tailwindcss/typography': specifier: ^0.5.19 - version: 0.5.19(tailwindcss@3.4.18(tsx@4.20.6)) + version: 0.5.19(tailwindcss@3.4.18(tsx@4.21.0)) '@tpmjs/eslint-config': specifier: workspace:* version: link:../../packages/config/eslint @@ -116,16 +119,16 @@ importers: version: 10.4.22(postcss@8.5.6) eslint: specifier: ^9.39.1 - version: 9.39.1(jiti@1.21.7) + version: 9.39.1(jiti@2.6.1) eslint-config-next: specifier: ^16.0.4 - version: 16.0.4(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) + version: 16.0.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) postcss: specifier: ^8.5.1 version: 8.5.6 tailwindcss: specifier: ^3.4.17 - version: 3.4.18(tsx@4.20.6) + version: 3.4.18(tsx@4.21.0) typescript: specifier: ^5.9.3 version: 5.9.3 @@ -198,7 +201,7 @@ importers: devDependencies: '@tailwindcss/typography': specifier: ^0.5.19 - version: 0.5.19(tailwindcss@3.4.18(tsx@4.20.6)) + version: 0.5.19(tailwindcss@3.4.18(tsx@4.21.0)) '@tpmjs/eslint-config': specifier: workspace:* version: link:../../packages/config/eslint @@ -231,7 +234,7 @@ importers: version: 8.5.6 tailwindcss: specifier: ^3.4.17 - version: 3.4.18(tsx@4.20.6) + version: 3.4.18(tsx@4.21.0) typescript: specifier: ^5.9.3 version: 5.9.3 @@ -273,7 +276,7 @@ importers: dependencies: tailwindcss: specifier: ^3.4.17 - version: 3.4.18(tsx@4.20.6) + version: 3.4.18(tsx@4.21.0) zod: specifier: ^4.1.13 version: 4.1.13 @@ -327,7 +330,7 @@ importers: version: 22.19.1 tsup: specifier: ^8.3.5 - version: 8.5.1(jiti@2.6.1)(postcss@8.5.6)(tsx@4.20.6)(typescript@5.9.3) + version: 8.5.1(jiti@2.6.1)(postcss@8.5.6)(tsx@4.21.0)(typescript@5.9.3) typescript: specifier: ^5.9.3 version: 5.9.3 @@ -346,7 +349,7 @@ importers: version: link:../config/tsconfig tsup: specifier: ^8.3.5 - version: 8.5.1(jiti@2.6.1)(postcss@8.5.6)(tsx@4.20.6)(typescript@5.9.3) + version: 8.5.1(jiti@2.6.1)(postcss@8.5.6)(tsx@4.21.0)(typescript@5.9.3) typescript: specifier: ^5.9.3 version: 5.9.3 @@ -421,7 +424,7 @@ importers: version: 8.6.14(@storybook/test@8.6.14(storybook@8.6.14(prettier@2.8.8)))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(storybook@8.6.14(prettier@2.8.8))(typescript@5.9.3) '@storybook/react-vite': specifier: ^8.5.0 - version: 8.6.14(@storybook/test@8.6.14(storybook@8.6.14(prettier@2.8.8)))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(rollup@4.53.3)(storybook@8.6.14(prettier@2.8.8))(typescript@5.9.3)(vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(tsx@4.20.6)) + version: 8.6.14(@storybook/test@8.6.14(storybook@8.6.14(prettier@2.8.8)))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(rollup@4.53.3)(storybook@8.6.14(prettier@2.8.8))(typescript@5.9.3)(vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(tsx@4.21.0)) '@storybook/test': specifier: ^8.5.0 version: 8.6.14(storybook@8.6.14(prettier@2.8.8)) @@ -445,10 +448,10 @@ importers: version: 8.6.14(prettier@2.8.8) tailwindcss: specifier: ^3.4.17 - version: 3.4.18(tsx@4.20.6) + version: 3.4.18(tsx@4.21.0) vite: specifier: ^6.0.7 - version: 6.4.1(@types/node@22.19.1)(jiti@2.6.1)(tsx@4.20.6) + version: 6.4.1(@types/node@22.19.1)(jiti@2.6.1)(tsx@4.21.0) packages/test: dependencies: @@ -486,7 +489,7 @@ importers: version: link:../../config/tsconfig tsup: specifier: ^8.3.5 - version: 8.5.1(jiti@2.6.1)(postcss@8.5.6)(tsx@4.20.6)(typescript@5.9.3) + version: 8.5.1(jiti@2.6.1)(postcss@8.5.6)(tsx@4.21.0)(typescript@5.9.3) typescript: specifier: ^5.9.3 version: 5.9.3 @@ -500,6 +503,9 @@ importers: '@tpmjs/tsconfig': specifier: workspace:* version: link:../../config/tsconfig + tsx: + specifier: ^4.20.6 + version: 4.20.6 typescript: specifier: ^5.9.3 version: 5.9.3 @@ -515,7 +521,7 @@ importers: version: link:../config/tsconfig tsup: specifier: ^8.3.5 - version: 8.5.1(jiti@2.6.1)(postcss@8.5.6)(tsx@4.20.6)(typescript@5.9.3) + version: 8.5.1(jiti@2.6.1)(postcss@8.5.6)(tsx@4.21.0)(typescript@5.9.3) typescript: specifier: ^5.9.3 version: 5.9.3 @@ -570,7 +576,7 @@ importers: version: 19.2.0(react@19.2.0) tsup: specifier: ^8.3.5 - version: 8.5.1(jiti@2.6.1)(postcss@8.5.6)(tsx@4.20.6)(typescript@5.9.3) + version: 8.5.1(jiti@2.6.1)(postcss@8.5.6)(tsx@4.21.0)(typescript@5.9.3) typescript: specifier: ^5.9.3 version: 5.9.3 @@ -598,7 +604,7 @@ importers: version: link:../config/tsconfig tsup: specifier: ^8.3.5 - version: 8.5.1(jiti@2.6.1)(postcss@8.5.6)(tsx@4.20.6)(typescript@5.9.3) + version: 8.5.1(jiti@2.6.1)(postcss@8.5.6)(tsx@4.21.0)(typescript@5.9.3) typescript: specifier: ^5.9.3 version: 5.9.3 @@ -673,6 +679,9 @@ packages: resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} engines: {node: '>=10'} + '@antfu/install-pkg@1.1.0': + resolution: {integrity: sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==} + '@babel/code-frame@7.27.1': resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} engines: {node: '>=6.9.0'} @@ -797,6 +806,9 @@ packages: cpu: [x64] os: [win32] + '@braintree/sanitize-url@7.1.1': + resolution: {integrity: sha512-i1L7noDNxtFyL5DmZafWy1wRVhGehQmzZaz1HiN5e7iylJMSZR7ekOV7NsIqa5qBldlLrsKv4HbgFUVlQrz8Mw==} + '@changesets/apply-release-plan@7.0.13': resolution: {integrity: sha512-BIW7bofD2yAWoE8H4V40FikC+1nNFEKBisMECccS16W1rt6qqhNTBDmIw5HaqmMgtLNz9e7oiALiEUuKrQ4oHg==} @@ -852,6 +864,21 @@ packages: '@changesets/write@0.4.0': resolution: {integrity: sha512-CdTLvIOPiCNuH71pyDu3rA+Q0n65cmAbXnwWH84rKGiFumFzkmHNT8KHTMEchcxN+Kl8I54xGUhJ7l3E7X396Q==} + '@chevrotain/cst-dts-gen@11.0.3': + resolution: {integrity: sha512-BvIKpRLeS/8UbfxXxgC33xOumsacaeCKAjAeLyOn7Pcp95HiRbrpl14S+9vaZLolnbssPIUuiUd8IvgkRyt6NQ==} + + '@chevrotain/gast@11.0.3': + resolution: {integrity: sha512-+qNfcoNk70PyS/uxmj3li5NiECO+2YKZZQMbmjTqRI3Qchu8Hig/Q9vgkHpI3alNjr7M+a2St5pw5w5F6NL5/Q==} + + '@chevrotain/regexp-to-ast@11.0.3': + resolution: {integrity: sha512-1fMHaBZxLFvWI067AVbGJav1eRY7N8DDvYCTwGBiE/ytKBgP8azTdgyrKyWZ9Mfh09eHWb5PgTSO8wi7U824RA==} + + '@chevrotain/types@11.0.3': + resolution: {integrity: sha512-gsiM3G8b58kZC2HaWR50gu6Y1440cHiJ+i3JUvcp/35JchYejb2+5MVeJK0iKThYpAa/P2PYFV4hoi44HD+aHQ==} + + '@chevrotain/utils@11.0.3': + resolution: {integrity: sha512-YslZMgtJUyuMbZ+aKvfF3x1f5liK4mWNxghFRv7jqRR9C3R3fAOGTTKvxXDa2Y1s9zSbcpuO0cAxDYsc9SrXoQ==} + '@emnapi/core@1.7.1': resolution: {integrity: sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg==} @@ -1365,6 +1392,12 @@ packages: resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} engines: {node: '>=18.18'} + '@iconify/types@2.0.0': + resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} + + '@iconify/utils@3.1.0': + resolution: {integrity: sha512-Zlzem1ZXhI1iHeeERabLNzBHdOa4VhQbqAcOQaMKuTuyZCpwKbC2R4Dd0Zo3g9EAc+Y4fiarO8HIHRAth7+skw==} + '@img/colour@1.0.0': resolution: {integrity: sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==} engines: {node: '>=18'} @@ -1599,6 +1632,9 @@ packages: resolution: {integrity: sha512-40fW83AuGziIrhONyexdc/EvfSUFvlqgcwmpAoUxY7yAl84kfSbqTgxJ/5ycd4SoIDW2plQOXzC/rxwtwFUnYg==} engines: {node: '>=22.0.0'} + '@mermaid-js/parser@0.6.3': + resolution: {integrity: sha512-lnjOhe7zyHjc+If7yT4zoedx2vo4sHaTmtkl1+or8BRTnCtDmcTpAjpzDSfCZrshM5bCoz0GyidzadJAH1xobA==} + '@mswjs/interceptors@0.40.0': resolution: {integrity: sha512-EFd6cVbHsgLa6wa4RljGj6Wk75qoHxUSyc5asLyyPSyuhIcdS2Q3Phw6ImS1q+CkALthJRShiYfKANcQMuMqsQ==} engines: {node: '>=18'} @@ -1943,6 +1979,27 @@ packages: '@rtsao/scc@1.1.0': resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} + '@shikijs/core@3.19.0': + resolution: {integrity: sha512-L7SrRibU7ZoYi1/TrZsJOFAnnHyLTE1SwHG1yNWjZIVCqjOEmCSuK2ZO9thnRbJG6TOkPp+Z963JmpCNw5nzvA==} + + '@shikijs/engine-javascript@3.19.0': + resolution: {integrity: sha512-ZfWJNm2VMhKkQIKT9qXbs76RRcT0SF/CAvEz0+RkpUDAoDaCx0uFdCGzSRiD9gSlhm6AHkjdieOBJMaO2eC1rQ==} + + '@shikijs/engine-oniguruma@3.19.0': + resolution: {integrity: sha512-1hRxtYIJfJSZeM5ivbUXv9hcJP3PWRo5prG/V2sWwiubUKTa+7P62d2qxCW8jiVFX4pgRHhnHNp+qeR7Xl+6kg==} + + '@shikijs/langs@3.19.0': + resolution: {integrity: sha512-dBMFzzg1QiXqCVQ5ONc0z2ebyoi5BKz+MtfByLm0o5/nbUu3Iz8uaTCa5uzGiscQKm7lVShfZHU1+OG3t5hgwg==} + + '@shikijs/themes@3.19.0': + resolution: {integrity: sha512-H36qw+oh91Y0s6OlFfdSuQ0Ld+5CgB/VE6gNPK+Hk4VRbVG/XQgkjnt4KzfnnoO6tZPtKJKHPjwebOCfjd6F8A==} + + '@shikijs/types@3.19.0': + resolution: {integrity: sha512-Z2hdeEQlzuntf/BZpFG8a+Fsw9UVXdML7w0o3TgSXV3yNESGon+bs9ITkQb3Ki7zxoXOOu5oJWqZ2uto06V9iQ==} + + '@shikijs/vscode-textmate@10.0.2': + resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} + '@standard-schema/spec@1.0.0': resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} @@ -2184,6 +2241,99 @@ packages: '@types/babel__traverse@7.28.0': resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} + '@types/d3-array@3.2.2': + resolution: {integrity: sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==} + + '@types/d3-axis@3.0.6': + resolution: {integrity: sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==} + + '@types/d3-brush@3.0.6': + resolution: {integrity: sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==} + + '@types/d3-chord@3.0.6': + resolution: {integrity: sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==} + + '@types/d3-color@3.1.3': + resolution: {integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==} + + '@types/d3-contour@3.0.6': + resolution: {integrity: sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==} + + '@types/d3-delaunay@6.0.4': + resolution: {integrity: sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==} + + '@types/d3-dispatch@3.0.7': + resolution: {integrity: sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA==} + + '@types/d3-drag@3.0.7': + resolution: {integrity: sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==} + + '@types/d3-dsv@3.0.7': + resolution: {integrity: sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==} + + '@types/d3-ease@3.0.2': + resolution: {integrity: sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==} + + '@types/d3-fetch@3.0.7': + resolution: {integrity: sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==} + + '@types/d3-force@3.0.10': + resolution: {integrity: sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==} + + '@types/d3-format@3.0.4': + resolution: {integrity: sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==} + + '@types/d3-geo@3.1.0': + resolution: {integrity: sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==} + + '@types/d3-hierarchy@3.1.7': + resolution: {integrity: sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==} + + '@types/d3-interpolate@3.0.4': + resolution: {integrity: sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==} + + '@types/d3-path@3.1.1': + resolution: {integrity: sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==} + + '@types/d3-polygon@3.0.2': + resolution: {integrity: sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==} + + '@types/d3-quadtree@3.0.6': + resolution: {integrity: sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==} + + '@types/d3-random@3.0.3': + resolution: {integrity: sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==} + + '@types/d3-scale-chromatic@3.1.0': + resolution: {integrity: sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==} + + '@types/d3-scale@4.0.9': + resolution: {integrity: sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==} + + '@types/d3-selection@3.0.11': + resolution: {integrity: sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==} + + '@types/d3-shape@3.1.7': + resolution: {integrity: sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==} + + '@types/d3-time-format@4.0.3': + resolution: {integrity: sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==} + + '@types/d3-time@3.0.4': + resolution: {integrity: sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==} + + '@types/d3-timer@3.0.2': + resolution: {integrity: sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==} + + '@types/d3-transition@3.0.9': + resolution: {integrity: sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==} + + '@types/d3-zoom@3.0.8': + resolution: {integrity: sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==} + + '@types/d3@7.4.3': + resolution: {integrity: sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==} + '@types/debug@4.1.12': resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} @@ -2196,6 +2346,9 @@ packages: '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + '@types/geojson@7946.0.16': + resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==} + '@types/hast@3.0.4': resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} @@ -2205,6 +2358,9 @@ packages: '@types/json5@0.0.29': resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} + '@types/katex@0.16.7': + resolution: {integrity: sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ==} + '@types/mdast@4.0.4': resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} @@ -2243,6 +2399,9 @@ packages: '@types/statuses@2.0.6': resolution: {integrity: sha512-xMAgYwceFhRA2zY+XbEA7mxYbA093wdiW8Vu6gZPGWy9cmOyU9XesH1tNcEWsKFd5Vzrqx5T3D38PWx1FIIXkA==} + '@types/trusted-types@2.0.7': + resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} + '@types/unist@2.0.11': resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} @@ -2731,6 +2890,14 @@ packages: resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} engines: {node: '>= 16'} + chevrotain-allstar@0.3.1: + resolution: {integrity: sha512-b7g+y9A0v4mxCW1qUhf3BSVPg+/NvGErk/dOkrDaHA0nQIQGAtrOjlX//9OQtRlSCy+x9rfB5N8yC71lH1nvMw==} + peerDependencies: + chevrotain: ^11.0.0 + + chevrotain@11.0.3: + resolution: {integrity: sha512-ci2iJH6LeIkvP9eJW6gpueU8cnZhv85ELY8w8WiFtNjMHA5ad6pQLaJo9mEly/9qUyCpvqX8/POVUTf18/HFdw==} + chokidar@3.6.0: resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} engines: {node: '>= 8.10.0'} @@ -2783,6 +2950,14 @@ packages: resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} engines: {node: '>= 6'} + commander@7.2.0: + resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} + engines: {node: '>= 10'} + + commander@8.3.0: + resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} + engines: {node: '>= 12'} + concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} @@ -2803,6 +2978,12 @@ packages: resolution: {integrity: sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==} engines: {node: '>=18'} + cose-base@1.0.3: + resolution: {integrity: sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==} + + cose-base@2.2.0: + resolution: {integrity: sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==} + cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} @@ -2818,6 +2999,162 @@ packages: csstype@3.2.3: resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + cytoscape-cose-bilkent@4.1.0: + resolution: {integrity: sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==} + peerDependencies: + cytoscape: ^3.2.0 + + cytoscape-fcose@2.2.0: + resolution: {integrity: sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==} + peerDependencies: + cytoscape: ^3.2.0 + + cytoscape@3.33.1: + resolution: {integrity: sha512-iJc4TwyANnOGR1OmWhsS9ayRS3s+XQ185FmuHObThD+5AeJCakAAbWv8KimMTt08xCCLNgneQwFp+JRJOr9qGQ==} + engines: {node: '>=0.10'} + + d3-array@2.12.1: + resolution: {integrity: sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==} + + d3-array@3.2.4: + resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==} + engines: {node: '>=12'} + + d3-axis@3.0.0: + resolution: {integrity: sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==} + engines: {node: '>=12'} + + d3-brush@3.0.0: + resolution: {integrity: sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==} + engines: {node: '>=12'} + + d3-chord@3.0.1: + resolution: {integrity: sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==} + engines: {node: '>=12'} + + d3-color@3.1.0: + resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==} + engines: {node: '>=12'} + + d3-contour@4.0.2: + resolution: {integrity: sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==} + engines: {node: '>=12'} + + d3-delaunay@6.0.4: + resolution: {integrity: sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==} + engines: {node: '>=12'} + + d3-dispatch@3.0.1: + resolution: {integrity: sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==} + engines: {node: '>=12'} + + d3-drag@3.0.0: + resolution: {integrity: sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==} + engines: {node: '>=12'} + + d3-dsv@3.0.1: + resolution: {integrity: sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==} + engines: {node: '>=12'} + hasBin: true + + d3-ease@3.0.1: + resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==} + engines: {node: '>=12'} + + d3-fetch@3.0.1: + resolution: {integrity: sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==} + engines: {node: '>=12'} + + d3-force@3.0.0: + resolution: {integrity: sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==} + engines: {node: '>=12'} + + d3-format@3.1.0: + resolution: {integrity: sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==} + engines: {node: '>=12'} + + d3-geo@3.1.1: + resolution: {integrity: sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==} + engines: {node: '>=12'} + + d3-hierarchy@3.1.2: + resolution: {integrity: sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==} + engines: {node: '>=12'} + + d3-interpolate@3.0.1: + resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==} + engines: {node: '>=12'} + + d3-path@1.0.9: + resolution: {integrity: sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==} + + d3-path@3.1.0: + resolution: {integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==} + engines: {node: '>=12'} + + d3-polygon@3.0.1: + resolution: {integrity: sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==} + engines: {node: '>=12'} + + d3-quadtree@3.0.1: + resolution: {integrity: sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==} + engines: {node: '>=12'} + + d3-random@3.0.1: + resolution: {integrity: sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==} + engines: {node: '>=12'} + + d3-sankey@0.12.3: + resolution: {integrity: sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==} + + d3-scale-chromatic@3.1.0: + resolution: {integrity: sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==} + engines: {node: '>=12'} + + d3-scale@4.0.2: + resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==} + engines: {node: '>=12'} + + d3-selection@3.0.0: + resolution: {integrity: sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==} + engines: {node: '>=12'} + + d3-shape@1.3.7: + resolution: {integrity: sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==} + + d3-shape@3.2.0: + resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==} + engines: {node: '>=12'} + + d3-time-format@4.1.0: + resolution: {integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==} + engines: {node: '>=12'} + + d3-time@3.1.0: + resolution: {integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==} + engines: {node: '>=12'} + + d3-timer@3.0.1: + resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==} + engines: {node: '>=12'} + + d3-transition@3.0.1: + resolution: {integrity: sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==} + engines: {node: '>=12'} + peerDependencies: + d3-selection: 2 - 3 + + d3-zoom@3.0.0: + resolution: {integrity: sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==} + engines: {node: '>=12'} + + d3@7.9.0: + resolution: {integrity: sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==} + engines: {node: '>=12'} + + dagre-d3-es@7.0.13: + resolution: {integrity: sha512-efEhnxpSuwpYOKRm/L5KbqoZmNNukHa/Flty4Wp62JRvgH2ojwVgPgdYyr4twpieZnyRDdIH7PY2mopX26+j2Q==} + damerau-levenshtein@1.0.8: resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==} @@ -2833,6 +3170,9 @@ packages: resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} engines: {node: '>= 0.4'} + dayjs@1.11.19: + resolution: {integrity: sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==} + debug@3.2.7: resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} peerDependencies: @@ -2879,6 +3219,9 @@ packages: defu@6.1.4: resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} + delaunator@5.0.1: + resolution: {integrity: sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==} + delayed-stream@1.0.0: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} @@ -2930,6 +3273,9 @@ packages: dom-accessibility-api@0.6.3: resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==} + dompurify@3.3.0: + resolution: {integrity: sha512-r+f6MYR1gGN1eJv0TVQbhA7if/U7P87cdPl3HN5rikqaBSBxLiCb/b9O+2eG0cxz0ghyU+mU1QkbsOwERMYlWQ==} + dotenv@16.6.1: resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} engines: {node: '>=12'} @@ -3255,13 +3601,6 @@ packages: resolution: {integrity: sha512-JyqKs12ScYcKmHnvkc/h/ItvcJxLjxn1WkFTe6w6RVb/LXVw7gqjOuRg2bvfsZeeqlTOyiLAzy5tbj4eMOgjRA==} engines: {node: '>=18.0.0'} - fish-joke-generator@1.1.0: - resolution: {integrity: sha512-w5NE7hIVEOBRcIYIc8AhOJyQSFhDfJF7/0www2p5g3t3XcQuYpRjQ8sclw601eXqrygBPbYchSM0+KfdlpESNg==} - engines: {node: '>=18.0.0'} - peerDependencies: - openai: ^6.0.0 - zod: ^4.0.0 - fix-dts-default-cjs-exports@1.0.1: resolution: {integrity: sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg==} @@ -3340,6 +3679,10 @@ packages: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} + get-east-asian-width@1.4.0: + resolution: {integrity: sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==} + engines: {node: '>=18'} + get-intrinsic@1.3.0: resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} engines: {node: '>= 0.4'} @@ -3409,6 +3752,9 @@ packages: resolution: {integrity: sha512-DKKrynuQRne0PNpEbzuEdHlYOMksHSUI8Zc9Unei5gTsMNA2/vMpoMz/yKba50pejK56qj98qM0SjYxAKi13gQ==} engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} + hachure-fill@0.5.2: + resolution: {integrity: sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==} + happy-dom@15.11.7: resolution: {integrity: sha512-KyrFvnl+J9US63TEzwoiJOQzZBJY7KgBushJA8X61DMbNsH+2ONkDuLDnCnwUiPTF42tLoEmrPyoqbenVA5zrg==} engines: {node: '>=18.0.0'} @@ -3440,9 +3786,21 @@ packages: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} + hast-util-from-dom@5.0.1: + resolution: {integrity: sha512-N+LqofjR2zuzTjCPzyDUdSshy4Ma6li7p/c3pA78uTwzFgENbgbUrm2ugwsOdcjI1muO+o6Dgzp9p8WHtn/39Q==} + + hast-util-from-html-isomorphic@2.0.0: + resolution: {integrity: sha512-zJfpXq44yff2hmE0XmwEOzdWin5xwH+QIhMLOScpX91e/NSGPsAzNCvLQDIEPyO2TXi+lBmU6hjLIhV8MwP2kw==} + + hast-util-from-html@2.0.3: + resolution: {integrity: sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==} + hast-util-from-parse5@8.0.3: resolution: {integrity: sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==} + hast-util-is-element@3.0.0: + resolution: {integrity: sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==} + hast-util-parse-selector@4.0.0: resolution: {integrity: sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==} @@ -3452,15 +3810,25 @@ packages: hast-util-sanitize@5.0.2: resolution: {integrity: sha512-3yTWghByc50aGS7JlGhk61SPenfE/p1oaFeNwkOOyrscaOkMGrcW9+Cy/QAIOBpZxP1yqDIzFMR0+Np0i0+usg==} + hast-util-to-html@9.0.5: + resolution: {integrity: sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==} + hast-util-to-jsx-runtime@2.3.6: resolution: {integrity: sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==} hast-util-to-parse5@8.0.0: resolution: {integrity: sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw==} + hast-util-to-text@4.0.2: + resolution: {integrity: sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A==} + hast-util-whitespace@3.0.0: resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} + hast@1.0.0: + resolution: {integrity: sha512-vFUqlRV5C+xqP76Wwq2SrM0kipnmpxJm7OfvVXpB35Fp+Fn4MV+ozr+JZr5qFvyR1q/U+Foim2x+3P+x9S1PLA==} + deprecated: Renamed to rehype + hastscript@9.0.1: resolution: {integrity: sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==} @@ -3489,6 +3857,10 @@ packages: resolution: {integrity: sha512-v/J+4Z/1eIJovEBdlV5TYj1IR+ZiohcYGRY+qN/oC9dAfKzVT023N/Bgw37hrKCoVRBvk3bqyzpr2PP5YeTMSg==} hasBin: true + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + iconv-lite@0.7.0: resolution: {integrity: sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==} engines: {node: '>=0.10.0'} @@ -3527,6 +3899,13 @@ packages: resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} engines: {node: '>= 0.4'} + internmap@1.0.1: + resolution: {integrity: sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==} + + internmap@2.0.3: + resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} + engines: {node: '>=12'} + interpret@3.1.1: resolution: {integrity: sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==} engines: {node: '>=10.13.0'} @@ -3766,9 +4145,16 @@ packages: resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} engines: {node: '>=4.0'} + katex@0.16.25: + resolution: {integrity: sha512-woHRUZ/iF23GBP1dkDQMh1QBad9dmr8/PAwNA54VrSOVYgI12MAcE14TqnDdQOdzyEonGzMepYnqBMYdsoAr8Q==} + hasBin: true + keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + khroma@2.1.0: + resolution: {integrity: sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==} + kleur@3.0.3: resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} engines: {node: '>=6'} @@ -3781,6 +4167,10 @@ packages: '@types/node': '>=18' typescript: '>=5.0.4 <7' + langium@3.3.1: + resolution: {integrity: sha512-QJv/h939gDpvT+9SiLVlY7tZC3xB2qK57v0J04Sh9wpMb6MP1q8gB21L3WIo8T5P1MSMg3Ep14L7KkDCFG3y4w==} + engines: {node: '>=16.0.0'} + language-subtag-registry@0.3.23: resolution: {integrity: sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==} @@ -3788,6 +4178,12 @@ packages: resolution: {integrity: sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==} engines: {node: '>=0.10'} + layout-base@1.0.2: + resolution: {integrity: sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==} + + layout-base@2.0.1: + resolution: {integrity: sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==} + lefthook-darwin-arm64@1.13.6: resolution: {integrity: sha512-m6Lb77VGc84/Qo21Lhq576pEvcgFCnvloEiP02HbAHcIXD0RTLy9u2yAInrixqZeaz13HYtdDaI7OBYAAdVt8A==} cpu: [arm64] @@ -3865,6 +4261,9 @@ packages: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} + lodash-es@4.17.21: + resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==} + lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} @@ -3897,6 +4296,11 @@ packages: lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + lucide-react@0.542.0: + resolution: {integrity: sha512-w3hD8/SQB7+lzU2r4VdFyzzOzKnUjTZIF/MQJGSSvni7Llewni4vuViRppfRAa2guOsY5k4jZyxw/i9DQHv+dw==} + peerDependencies: + react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 + lz-string@1.5.0: resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} hasBin: true @@ -3914,6 +4318,11 @@ packages: markdown-table@3.0.4: resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} + marked@16.4.2: + resolution: {integrity: sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA==} + engines: {node: '>= 20'} + hasBin: true + math-intrinsics@1.1.0: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} @@ -3942,6 +4351,9 @@ packages: mdast-util-gfm@3.1.0: resolution: {integrity: sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==} + mdast-util-math@3.0.0: + resolution: {integrity: sha512-Tl9GBNeG/AhJnQM221bJR2HPvLOSnLE/T9cJI9tlc6zwQk2nPk/4f0cHkOdEixQPC/j8UtKDdITswvLAy1OZ1w==} + mdast-util-mdx-expression@2.0.1: resolution: {integrity: sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==} @@ -3974,9 +4386,41 @@ packages: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} + mermaid@11.12.2: + resolution: {integrity: sha512-n34QPDPEKmaeCG4WDMGy0OT6PSyxKCfy2pJgShP+Qow2KLrvWjclwbc3yXfSIf4BanqWEhQEpngWwNp/XhZt6w==} + micromark-core-commonmark@2.0.3: resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} + micromark-extension-cjk-friendly-gfm-strikethrough@1.2.3: + resolution: {integrity: sha512-gSPnxgHDDqXYOBvQRq6lerrq9mjDhdtKn+7XETuXjxWcL62yZEfUdA28Ml1I2vDIPfAOIKLa0h2XDSGkInGHFQ==} + engines: {node: '>=16'} + peerDependencies: + micromark: ^4.0.0 + micromark-util-types: ^2.0.0 + peerDependenciesMeta: + micromark-util-types: + optional: true + + micromark-extension-cjk-friendly-util@2.1.1: + resolution: {integrity: sha512-egs6+12JU2yutskHY55FyR48ZiEcFOJFyk9rsiyIhcJ6IvWB6ABBqVrBw8IobqJTDZ/wdSr9eoXDPb5S2nW1bg==} + engines: {node: '>=16'} + peerDependencies: + micromark-util-types: '*' + peerDependenciesMeta: + micromark-util-types: + optional: true + + micromark-extension-cjk-friendly@1.2.3: + resolution: {integrity: sha512-gRzVLUdjXBLX6zNPSnHGDoo+ZTp5zy+MZm0g3sv+3chPXY7l9gW+DnrcHcZh/jiPR6MjPKO4AEJNp4Aw6V9z5Q==} + engines: {node: '>=16'} + peerDependencies: + micromark: ^4.0.0 + micromark-util-types: ^2.0.0 + peerDependenciesMeta: + micromark-util-types: + optional: true + micromark-extension-gfm-autolink-literal@2.1.0: resolution: {integrity: sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==} @@ -3998,6 +4442,9 @@ packages: micromark-extension-gfm@3.0.0: resolution: {integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==} + micromark-extension-math@3.1.0: + resolution: {integrity: sha512-lvEqd+fHjATVs+2v/8kg9i5Q0AP2k85H0WUOwpIVvUML8BapsMvh1XAogmQjOCsLpoKRCVQqEkQBB3NhVBcsOg==} + micromark-factory-destination@2.0.1: resolution: {integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==} @@ -4145,6 +4592,7 @@ packages: next@16.0.4: resolution: {integrity: sha512-vICcxKusY8qW7QFOzTvnRL1ejz2ClTqDKtm1AcUjm2mPv/lVAdgpGNsftsPRIDJOXOjRQO68i1dM8Lp8GZnqoA==} engines: {node: '>=20.9.0'} + deprecated: This version has a security vulnerability. Please upgrade to a patched version. See https://nextjs.org/blog/CVE-2025-66478 for more details. hasBin: true peerDependencies: '@opentelemetry/api': ^1.1.0 @@ -4221,6 +4669,12 @@ packages: ohash@2.0.11: resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==} + oniguruma-parser@0.12.1: + resolution: {integrity: sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w==} + + oniguruma-to-es@4.3.4: + resolution: {integrity: sha512-3VhUGN3w2eYxnTzHn+ikMI+fp/96KoRSVK9/kMTcFqj1NRDh2IhQCKvYxDnWePKRXY/AqH+Fuiyb7VHSzBjHfA==} + open@8.4.2: resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} engines: {node: '>=12'} @@ -4288,6 +4742,9 @@ packages: package-manager-detector@0.2.11: resolution: {integrity: sha512-BEnLolu+yuz22S56CU1SUKq3XC3PkwD5wv4ikR4MfGvnRVcmzXR9DwSlW2fEamyTPyXHomBJRzgapeuBvRNzJQ==} + package-manager-detector@1.6.0: + resolution: {integrity: sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==} + parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} @@ -4298,6 +4755,9 @@ packages: parse5@7.3.0: resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} + path-data-parser@0.1.0: + resolution: {integrity: sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w==} + path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -4366,6 +4826,12 @@ packages: pkg-types@2.3.0: resolution: {integrity: sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==} + points-on-curve@0.2.0: + resolution: {integrity: sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A==} + + points-on-path@0.2.1: + resolution: {integrity: sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g==} + polished@4.3.1: resolution: {integrity: sha512-OBatVyC/N7SCW/FaDHrSd+vn0o5cS855TOmYi4OkdWUMSJCET/xip//ch8xGUvtr3i44X9LVyWwQlRMTN3pwSA==} engines: {node: '>=10'} @@ -4562,6 +5028,15 @@ packages: refractor@5.0.0: resolution: {integrity: sha512-QXOrHQF5jOpjjLfiNk5GFnWhRXvxjUVnlFxkeDmewR5sXkr3iM46Zo+CnRR8B+MDVqkULW4EcLVcRBNOPXHosw==} + regex-recursion@6.0.2: + resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==} + + regex-utilities@2.3.0: + resolution: {integrity: sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==} + + regex@6.0.1: + resolution: {integrity: sha512-uorlqlzAKjKQZ5P+kTJr3eeJGSVroLKoHmquUj4zHWuR+hEyNqlXsSKlYYF5F4NI6nl7tWCs0apKJ0lmfsXAPA==} + regexp-tree@0.1.27: resolution: {integrity: sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==} hasBin: true @@ -4570,15 +5045,44 @@ packages: resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} engines: {node: '>= 0.4'} + rehype-harden@1.1.6: + resolution: {integrity: sha512-5WyX6BFEWYmmbCF/S2gNRklfgPGTiGjviAjbseO4XlpqEilWBkvWwve6uU/JB3C0JvG/qxCZa3rBn8+ajy4i/A==} + + rehype-katex@7.0.1: + resolution: {integrity: sha512-OiM2wrZ/wuhKkigASodFoo8wimG3H12LWQaH8qSPVJn9apWKFSH3YOCtbKpBorTVw/eI7cuT21XBbvwEswbIOA==} + rehype-raw@7.0.0: resolution: {integrity: sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==} rehype-sanitize@6.0.0: resolution: {integrity: sha512-CsnhKNsyI8Tub6L4sm5ZFsme4puGfc6pYylvXo1AeqaGbjOYyzNv3qZPwvs0oMJ39eryyeOdmxwUIo94IpEhqg==} + remark-cjk-friendly-gfm-strikethrough@1.2.3: + resolution: {integrity: sha512-bXfMZtsaomK6ysNN/UGRIcasQAYkC10NtPmP0oOHOV8YOhA2TXmwRXCku4qOzjIFxAPfish5+XS0eIug2PzNZA==} + engines: {node: '>=16'} + peerDependencies: + '@types/mdast': ^4.0.0 + unified: ^11.0.0 + peerDependenciesMeta: + '@types/mdast': + optional: true + + remark-cjk-friendly@1.2.3: + resolution: {integrity: sha512-UvAgxwlNk+l9Oqgl/9MWK2eWRS7zgBW/nXX9AthV7nd/3lNejF138E7Xbmk9Zs4WjTJGs721r7fAEc7tNFoH7g==} + engines: {node: '>=16'} + peerDependencies: + '@types/mdast': ^4.0.0 + unified: ^11.0.0 + peerDependenciesMeta: + '@types/mdast': + optional: true + remark-gfm@4.0.1: resolution: {integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==} + remark-math@6.0.0: + resolution: {integrity: sha512-MMqgnP74Igy+S3WwnhQ7kqGlEerTETXMvJhrUzDikVZ2/uogJCb+WHUg97hK9/jcfc0dkD73s3LN8zU49cTEtA==} + remark-parse@11.0.0: resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==} @@ -4623,14 +5127,23 @@ packages: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + robust-predicates@3.0.2: + resolution: {integrity: sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==} + rollup@4.53.3: resolution: {integrity: sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true + roughjs@4.6.6: + resolution: {integrity: sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ==} + run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + rw@1.3.3: + resolution: {integrity: sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==} + safe-array-concat@1.1.3: resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} engines: {node: '>=0.4'} @@ -4685,6 +5198,9 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} + shiki@3.19.0: + resolution: {integrity: sha512-77VJr3OR/VUZzPiStyRhADmO2jApMM0V2b1qf0RpfWya8Zr1PeZev5AEpPGAAKWdiYUtcZGBE4F5QvJml1PvWA==} + side-channel-list@1.0.0: resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} engines: {node: '>= 0.4'} @@ -4766,6 +5282,11 @@ packages: prettier: optional: true + streamdown@1.6.9: + resolution: {integrity: sha512-rtUZcRvDYNEgduq1OxNJzuYYmchZVXq+1Pw3T445RrYwrT+SGNK1drtt1eaqC4HaD8YYIscdtMSlZFaNM+yYGA==} + peerDependencies: + react: ^18.0.0 || ^19.0.0 + strict-event-emitter@0.5.1: resolution: {integrity: sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==} @@ -4850,6 +5371,9 @@ packages: babel-plugin-macros: optional: true + stylis@4.3.6: + resolution: {integrity: sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==} + sucrase@3.35.1: resolution: {integrity: sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==} engines: {node: '>=16 || 14 >=14.17'} @@ -4875,6 +5399,9 @@ packages: tailwind-merge@2.6.0: resolution: {integrity: sha512-P+Vu1qXfzediirmHOC3xKGAYeZtPcV9g76X+xg2FD4tYgR71ewMA35Y3sCz3zhiN/dwefRpJX0yBcgwi1fXNQA==} + tailwind-merge@3.4.0: + resolution: {integrity: sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g==} + tailwindcss@3.4.18: resolution: {integrity: sha512-6A2rnmW5xZMdw11LYjhcI5846rt9pbLSabY5XPxo+XWdxwZaFEn47Go4NzFiHu9sNNmr/kXivP1vStfvMaK1GQ==} engines: {node: '>=14.0.0'} @@ -5013,6 +5540,11 @@ packages: engines: {node: '>=18.0.0'} hasBin: true + tsx@4.21.0: + resolution: {integrity: sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==} + engines: {node: '>=18.0.0'} + hasBin: true + turbo-darwin-64@2.6.1: resolution: {integrity: sha512-Dm0HwhyZF4J0uLqkhUyCVJvKM9Rw7M03v3J9A7drHDQW0qAbIGBrUijQ8g4Q9Cciw/BXRRd8Uzkc3oue+qn+ZQ==} cpu: [x64] @@ -5108,12 +5640,18 @@ packages: unified@11.0.5: resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} + unist-util-find-after@5.0.0: + resolution: {integrity: sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==} + unist-util-is@6.0.1: resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==} unist-util-position@5.0.0: resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} + unist-util-remove-position@5.0.0: + resolution: {integrity: sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==} + unist-util-stringify-position@4.0.0: resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} @@ -5157,6 +5695,10 @@ packages: util@0.12.5: resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==} + uuid@11.1.0: + resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==} + hasBin: true + uuid@9.0.1: resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} hasBin: true @@ -5271,6 +5813,26 @@ packages: jsdom: optional: true + vscode-jsonrpc@8.2.0: + resolution: {integrity: sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==} + engines: {node: '>=14.0.0'} + + vscode-languageserver-protocol@3.17.5: + resolution: {integrity: sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==} + + vscode-languageserver-textdocument@1.0.12: + resolution: {integrity: sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==} + + vscode-languageserver-types@3.17.5: + resolution: {integrity: sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==} + + vscode-languageserver@9.0.1: + resolution: {integrity: sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==} + hasBin: true + + vscode-uri@3.0.8: + resolution: {integrity: sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==} + walk-up-path@4.0.0: resolution: {integrity: sha512-3hu+tD8YzSLGuFYtPRb48vdhKMi0KQV5sn+uWr8+7dMEq/2G/dtLrdDinkLjqq5TIbIBjYJ4Ax/n3YiaW7QM8A==} engines: {node: 20 || >=22} @@ -5473,6 +6035,11 @@ snapshots: '@alloc/quick-lru@5.2.0': {} + '@antfu/install-pkg@1.1.0': + dependencies: + package-manager-detector: 1.6.0 + tinyexec: 1.0.2 + '@babel/code-frame@7.27.1': dependencies: '@babel/helper-validator-identifier': 7.28.5 @@ -5610,6 +6177,8 @@ snapshots: '@biomejs/cli-win32-x64@1.9.4': optional: true + '@braintree/sanitize-url@7.1.1': {} + '@changesets/apply-release-plan@7.0.13': dependencies: '@changesets/config': 3.1.1 @@ -5754,6 +6323,23 @@ snapshots: human-id: 4.1.2 prettier: 2.8.8 + '@chevrotain/cst-dts-gen@11.0.3': + dependencies: + '@chevrotain/gast': 11.0.3 + '@chevrotain/types': 11.0.3 + lodash-es: 4.17.21 + + '@chevrotain/gast@11.0.3': + dependencies: + '@chevrotain/types': 11.0.3 + lodash-es: 4.17.21 + + '@chevrotain/regexp-to-ast@11.0.3': {} + + '@chevrotain/types@11.0.3': {} + + '@chevrotain/utils@11.0.3': {} + '@emnapi/core@1.7.1': dependencies: '@emnapi/wasi-threads': 1.1.0 @@ -5995,11 +6581,6 @@ snapshots: '@esbuild/win32-x64@0.27.0': optional: true - '@eslint-community/eslint-utils@4.9.0(eslint@9.39.1(jiti@1.21.7))': - dependencies: - eslint: 9.39.1(jiti@1.21.7) - eslint-visitor-keys: 3.4.3 - '@eslint-community/eslint-utils@4.9.0(eslint@9.39.1(jiti@2.6.1))': dependencies: eslint: 9.39.1(jiti@2.6.1) @@ -6057,6 +6638,14 @@ snapshots: '@humanwhocodes/retry@0.4.3': {} + '@iconify/types@2.0.0': {} + + '@iconify/utils@3.1.0': + dependencies: + '@antfu/install-pkg': 1.1.0 + '@iconify/types': 2.0.0 + mlly: 1.8.0 + '@img/colour@1.0.0': optional: true @@ -6204,12 +6793,12 @@ snapshots: wrap-ansi: 8.1.0 wrap-ansi-cjs: wrap-ansi@7.0.0 - '@joshwooding/vite-plugin-react-docgen-typescript@0.5.0(typescript@5.9.3)(vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(tsx@4.20.6))': + '@joshwooding/vite-plugin-react-docgen-typescript@0.5.0(typescript@5.9.3)(vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(tsx@4.21.0))': dependencies: glob: 10.5.0 magic-string: 0.27.0 react-docgen-typescript: 2.4.0(typescript@5.9.3) - vite: 6.4.1(@types/node@22.19.1)(jiti@2.6.1)(tsx@4.20.6) + vite: 6.4.1(@types/node@22.19.1)(jiti@2.6.1)(tsx@4.21.0) optionalDependencies: typescript: 5.9.3 @@ -6263,6 +6852,10 @@ snapshots: transitivePeerDependencies: - debug + '@mermaid-js/parser@0.6.3': + dependencies: + langium: 3.3.1 + '@mswjs/interceptors@0.40.0': dependencies: '@open-draft/deferred-promise': 2.2.0 @@ -6514,6 +7107,39 @@ snapshots: '@rtsao/scc@1.1.0': {} + '@shikijs/core@3.19.0': + dependencies: + '@shikijs/types': 3.19.0 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + hast-util-to-html: 9.0.5 + + '@shikijs/engine-javascript@3.19.0': + dependencies: + '@shikijs/types': 3.19.0 + '@shikijs/vscode-textmate': 10.0.2 + oniguruma-to-es: 4.3.4 + + '@shikijs/engine-oniguruma@3.19.0': + dependencies: + '@shikijs/types': 3.19.0 + '@shikijs/vscode-textmate': 10.0.2 + + '@shikijs/langs@3.19.0': + dependencies: + '@shikijs/types': 3.19.0 + + '@shikijs/themes@3.19.0': + dependencies: + '@shikijs/types': 3.19.0 + + '@shikijs/types@3.19.0': + dependencies: + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + + '@shikijs/vscode-textmate@10.0.2': {} + '@standard-schema/spec@1.0.0': {} '@storybook/addon-actions@8.6.14(storybook@8.6.14(prettier@2.8.8))': @@ -6620,13 +7246,13 @@ snapshots: react: 19.2.0 react-dom: 19.2.0(react@19.2.0) - '@storybook/builder-vite@8.6.14(storybook@8.6.14(prettier@2.8.8))(vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(tsx@4.20.6))': + '@storybook/builder-vite@8.6.14(storybook@8.6.14(prettier@2.8.8))(vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(tsx@4.21.0))': dependencies: '@storybook/csf-plugin': 8.6.14(storybook@8.6.14(prettier@2.8.8)) browser-assert: 1.2.1 storybook: 8.6.14(prettier@2.8.8) ts-dedent: 2.2.0 - vite: 6.4.1(@types/node@22.19.1)(jiti@2.6.1)(tsx@4.20.6) + vite: 6.4.1(@types/node@22.19.1)(jiti@2.6.1)(tsx@4.21.0) '@storybook/components@8.6.14(storybook@8.6.14(prettier@2.8.8))': dependencies: @@ -6685,11 +7311,11 @@ snapshots: react-dom: 19.2.0(react@19.2.0) storybook: 8.6.14(prettier@2.8.8) - '@storybook/react-vite@8.6.14(@storybook/test@8.6.14(storybook@8.6.14(prettier@2.8.8)))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(rollup@4.53.3)(storybook@8.6.14(prettier@2.8.8))(typescript@5.9.3)(vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(tsx@4.20.6))': + '@storybook/react-vite@8.6.14(@storybook/test@8.6.14(storybook@8.6.14(prettier@2.8.8)))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(rollup@4.53.3)(storybook@8.6.14(prettier@2.8.8))(typescript@5.9.3)(vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(tsx@4.21.0))': dependencies: - '@joshwooding/vite-plugin-react-docgen-typescript': 0.5.0(typescript@5.9.3)(vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(tsx@4.20.6)) + '@joshwooding/vite-plugin-react-docgen-typescript': 0.5.0(typescript@5.9.3)(vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(tsx@4.21.0)) '@rollup/pluginutils': 5.3.0(rollup@4.53.3) - '@storybook/builder-vite': 8.6.14(storybook@8.6.14(prettier@2.8.8))(vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(tsx@4.20.6)) + '@storybook/builder-vite': 8.6.14(storybook@8.6.14(prettier@2.8.8))(vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(tsx@4.21.0)) '@storybook/react': 8.6.14(@storybook/test@8.6.14(storybook@8.6.14(prettier@2.8.8)))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(storybook@8.6.14(prettier@2.8.8))(typescript@5.9.3) find-up: 5.0.0 magic-string: 0.30.21 @@ -6699,7 +7325,7 @@ snapshots: resolve: 1.22.11 storybook: 8.6.14(prettier@2.8.8) tsconfig-paths: 4.2.0 - vite: 6.4.1(@types/node@22.19.1)(jiti@2.6.1)(tsx@4.20.6) + vite: 6.4.1(@types/node@22.19.1)(jiti@2.6.1)(tsx@4.21.0) optionalDependencies: '@storybook/test': 8.6.14(storybook@8.6.14(prettier@2.8.8)) transitivePeerDependencies: @@ -6741,10 +7367,10 @@ snapshots: dependencies: tslib: 2.8.1 - '@tailwindcss/typography@0.5.19(tailwindcss@3.4.18(tsx@4.20.6))': + '@tailwindcss/typography@0.5.19(tailwindcss@3.4.18(tsx@4.21.0))': dependencies: postcss-selector-parser: 6.0.10 - tailwindcss: 3.4.18(tsx@4.20.6) + tailwindcss: 3.4.18(tsx@4.21.0) '@testing-library/dom@10.4.0': dependencies: @@ -6824,6 +7450,123 @@ snapshots: dependencies: '@babel/types': 7.28.5 + '@types/d3-array@3.2.2': {} + + '@types/d3-axis@3.0.6': + dependencies: + '@types/d3-selection': 3.0.11 + + '@types/d3-brush@3.0.6': + dependencies: + '@types/d3-selection': 3.0.11 + + '@types/d3-chord@3.0.6': {} + + '@types/d3-color@3.1.3': {} + + '@types/d3-contour@3.0.6': + dependencies: + '@types/d3-array': 3.2.2 + '@types/geojson': 7946.0.16 + + '@types/d3-delaunay@6.0.4': {} + + '@types/d3-dispatch@3.0.7': {} + + '@types/d3-drag@3.0.7': + dependencies: + '@types/d3-selection': 3.0.11 + + '@types/d3-dsv@3.0.7': {} + + '@types/d3-ease@3.0.2': {} + + '@types/d3-fetch@3.0.7': + dependencies: + '@types/d3-dsv': 3.0.7 + + '@types/d3-force@3.0.10': {} + + '@types/d3-format@3.0.4': {} + + '@types/d3-geo@3.1.0': + dependencies: + '@types/geojson': 7946.0.16 + + '@types/d3-hierarchy@3.1.7': {} + + '@types/d3-interpolate@3.0.4': + dependencies: + '@types/d3-color': 3.1.3 + + '@types/d3-path@3.1.1': {} + + '@types/d3-polygon@3.0.2': {} + + '@types/d3-quadtree@3.0.6': {} + + '@types/d3-random@3.0.3': {} + + '@types/d3-scale-chromatic@3.1.0': {} + + '@types/d3-scale@4.0.9': + dependencies: + '@types/d3-time': 3.0.4 + + '@types/d3-selection@3.0.11': {} + + '@types/d3-shape@3.1.7': + dependencies: + '@types/d3-path': 3.1.1 + + '@types/d3-time-format@4.0.3': {} + + '@types/d3-time@3.0.4': {} + + '@types/d3-timer@3.0.2': {} + + '@types/d3-transition@3.0.9': + dependencies: + '@types/d3-selection': 3.0.11 + + '@types/d3-zoom@3.0.8': + dependencies: + '@types/d3-interpolate': 3.0.4 + '@types/d3-selection': 3.0.11 + + '@types/d3@7.4.3': + dependencies: + '@types/d3-array': 3.2.2 + '@types/d3-axis': 3.0.6 + '@types/d3-brush': 3.0.6 + '@types/d3-chord': 3.0.6 + '@types/d3-color': 3.1.3 + '@types/d3-contour': 3.0.6 + '@types/d3-delaunay': 6.0.4 + '@types/d3-dispatch': 3.0.7 + '@types/d3-drag': 3.0.7 + '@types/d3-dsv': 3.0.7 + '@types/d3-ease': 3.0.2 + '@types/d3-fetch': 3.0.7 + '@types/d3-force': 3.0.10 + '@types/d3-format': 3.0.4 + '@types/d3-geo': 3.1.0 + '@types/d3-hierarchy': 3.1.7 + '@types/d3-interpolate': 3.0.4 + '@types/d3-path': 3.1.1 + '@types/d3-polygon': 3.0.2 + '@types/d3-quadtree': 3.0.6 + '@types/d3-random': 3.0.3 + '@types/d3-scale': 4.0.9 + '@types/d3-scale-chromatic': 3.1.0 + '@types/d3-selection': 3.0.11 + '@types/d3-shape': 3.1.7 + '@types/d3-time': 3.0.4 + '@types/d3-time-format': 4.0.3 + '@types/d3-timer': 3.0.2 + '@types/d3-transition': 3.0.9 + '@types/d3-zoom': 3.0.8 + '@types/debug@4.1.12': dependencies: '@types/ms': 2.1.0 @@ -6836,6 +7579,8 @@ snapshots: '@types/estree@1.0.8': {} + '@types/geojson@7946.0.16': {} + '@types/hast@3.0.4': dependencies: '@types/unist': 3.0.3 @@ -6844,6 +7589,8 @@ snapshots: '@types/json5@0.0.29': {} + '@types/katex@0.16.7': {} + '@types/mdast@4.0.4': dependencies: '@types/unist': 3.0.3 @@ -6878,29 +7625,15 @@ snapshots: '@types/statuses@2.0.6': {} + '@types/trusted-types@2.0.7': + optional: true + '@types/unist@2.0.11': {} '@types/unist@3.0.3': {} '@types/uuid@9.0.8': {} - '@typescript-eslint/eslint-plugin@8.48.0(@typescript-eslint/parser@8.48.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3)': - dependencies: - '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.48.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - '@typescript-eslint/scope-manager': 8.48.0 - '@typescript-eslint/type-utils': 8.48.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - '@typescript-eslint/utils': 8.48.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.48.0 - eslint: 9.39.1(jiti@1.21.7) - graphemer: 1.4.0 - ignore: 7.0.5 - natural-compare: 1.4.0 - ts-api-utils: 2.1.0(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - '@typescript-eslint/eslint-plugin@8.48.0(@typescript-eslint/parser@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)': dependencies: '@eslint-community/regexpp': 4.12.2 @@ -6918,18 +7651,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.48.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3)': - dependencies: - '@typescript-eslint/scope-manager': 8.48.0 - '@typescript-eslint/types': 8.48.0 - '@typescript-eslint/typescript-estree': 8.48.0(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.48.0 - debug: 4.4.3 - eslint: 9.39.1(jiti@1.21.7) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - '@typescript-eslint/parser@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)': dependencies: '@typescript-eslint/scope-manager': 8.48.0 @@ -6960,18 +7681,6 @@ snapshots: dependencies: typescript: 5.9.3 - '@typescript-eslint/type-utils@8.48.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3)': - dependencies: - '@typescript-eslint/types': 8.48.0 - '@typescript-eslint/typescript-estree': 8.48.0(typescript@5.9.3) - '@typescript-eslint/utils': 8.48.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - debug: 4.4.3 - eslint: 9.39.1(jiti@1.21.7) - ts-api-utils: 2.1.0(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - '@typescript-eslint/type-utils@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)': dependencies: '@typescript-eslint/types': 8.48.0 @@ -7001,17 +7710,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.48.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3)': - dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@1.21.7)) - '@typescript-eslint/scope-manager': 8.48.0 - '@typescript-eslint/types': 8.48.0 - '@typescript-eslint/typescript-estree': 8.48.0(typescript@5.9.3) - eslint: 9.39.1(jiti@1.21.7) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - '@typescript-eslint/utils@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)': dependencies: '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@2.6.1)) @@ -7470,6 +8168,20 @@ snapshots: check-error@2.1.1: {} + chevrotain-allstar@0.3.1(chevrotain@11.0.3): + dependencies: + chevrotain: 11.0.3 + lodash-es: 4.17.21 + + chevrotain@11.0.3: + dependencies: + '@chevrotain/cst-dts-gen': 11.0.3 + '@chevrotain/gast': 11.0.3 + '@chevrotain/regexp-to-ast': 11.0.3 + '@chevrotain/types': 11.0.3 + '@chevrotain/utils': 11.0.3 + lodash-es: 4.17.21 + chokidar@3.6.0: dependencies: anymatch: 3.1.3 @@ -7520,6 +8232,10 @@ snapshots: commander@4.1.1: {} + commander@7.2.0: {} + + commander@8.3.0: {} + concat-map@0.0.1: {} confbox@0.1.8: {} @@ -7532,6 +8248,14 @@ snapshots: cookie@1.0.2: {} + cose-base@1.0.3: + dependencies: + layout-base: 1.0.2 + + cose-base@2.2.0: + dependencies: + layout-base: 2.0.1 + cross-spawn@7.0.6: dependencies: path-key: 3.1.1 @@ -7544,6 +8268,190 @@ snapshots: csstype@3.2.3: {} + cytoscape-cose-bilkent@4.1.0(cytoscape@3.33.1): + dependencies: + cose-base: 1.0.3 + cytoscape: 3.33.1 + + cytoscape-fcose@2.2.0(cytoscape@3.33.1): + dependencies: + cose-base: 2.2.0 + cytoscape: 3.33.1 + + cytoscape@3.33.1: {} + + d3-array@2.12.1: + dependencies: + internmap: 1.0.1 + + d3-array@3.2.4: + dependencies: + internmap: 2.0.3 + + d3-axis@3.0.0: {} + + d3-brush@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-drag: 3.0.0 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-transition: 3.0.1(d3-selection@3.0.0) + + d3-chord@3.0.1: + dependencies: + d3-path: 3.1.0 + + d3-color@3.1.0: {} + + d3-contour@4.0.2: + dependencies: + d3-array: 3.2.4 + + d3-delaunay@6.0.4: + dependencies: + delaunator: 5.0.1 + + d3-dispatch@3.0.1: {} + + d3-drag@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-selection: 3.0.0 + + d3-dsv@3.0.1: + dependencies: + commander: 7.2.0 + iconv-lite: 0.6.3 + rw: 1.3.3 + + d3-ease@3.0.1: {} + + d3-fetch@3.0.1: + dependencies: + d3-dsv: 3.0.1 + + d3-force@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-quadtree: 3.0.1 + d3-timer: 3.0.1 + + d3-format@3.1.0: {} + + d3-geo@3.1.1: + dependencies: + d3-array: 3.2.4 + + d3-hierarchy@3.1.2: {} + + d3-interpolate@3.0.1: + dependencies: + d3-color: 3.1.0 + + d3-path@1.0.9: {} + + d3-path@3.1.0: {} + + d3-polygon@3.0.1: {} + + d3-quadtree@3.0.1: {} + + d3-random@3.0.1: {} + + d3-sankey@0.12.3: + dependencies: + d3-array: 2.12.1 + d3-shape: 1.3.7 + + d3-scale-chromatic@3.1.0: + dependencies: + d3-color: 3.1.0 + d3-interpolate: 3.0.1 + + d3-scale@4.0.2: + dependencies: + d3-array: 3.2.4 + d3-format: 3.1.0 + d3-interpolate: 3.0.1 + d3-time: 3.1.0 + d3-time-format: 4.1.0 + + d3-selection@3.0.0: {} + + d3-shape@1.3.7: + dependencies: + d3-path: 1.0.9 + + d3-shape@3.2.0: + dependencies: + d3-path: 3.1.0 + + d3-time-format@4.1.0: + dependencies: + d3-time: 3.1.0 + + d3-time@3.1.0: + dependencies: + d3-array: 3.2.4 + + d3-timer@3.0.1: {} + + d3-transition@3.0.1(d3-selection@3.0.0): + dependencies: + d3-color: 3.1.0 + d3-dispatch: 3.0.1 + d3-ease: 3.0.1 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-timer: 3.0.1 + + d3-zoom@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-drag: 3.0.0 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-transition: 3.0.1(d3-selection@3.0.0) + + d3@7.9.0: + dependencies: + d3-array: 3.2.4 + d3-axis: 3.0.0 + d3-brush: 3.0.0 + d3-chord: 3.0.1 + d3-color: 3.1.0 + d3-contour: 4.0.2 + d3-delaunay: 6.0.4 + d3-dispatch: 3.0.1 + d3-drag: 3.0.0 + d3-dsv: 3.0.1 + d3-ease: 3.0.1 + d3-fetch: 3.0.1 + d3-force: 3.0.0 + d3-format: 3.1.0 + d3-geo: 3.1.1 + d3-hierarchy: 3.1.2 + d3-interpolate: 3.0.1 + d3-path: 3.1.0 + d3-polygon: 3.0.1 + d3-quadtree: 3.0.1 + d3-random: 3.0.1 + d3-scale: 4.0.2 + d3-scale-chromatic: 3.1.0 + d3-selection: 3.0.0 + d3-shape: 3.2.0 + d3-time: 3.1.0 + d3-time-format: 4.1.0 + d3-timer: 3.0.1 + d3-transition: 3.0.1(d3-selection@3.0.0) + d3-zoom: 3.0.0 + + dagre-d3-es@7.0.13: + dependencies: + d3: 7.9.0 + lodash-es: 4.17.21 + damerau-levenshtein@1.0.8: {} data-view-buffer@1.0.2: @@ -7564,6 +8472,8 @@ snapshots: es-errors: 1.3.0 is-data-view: 1.0.2 + dayjs@1.11.19: {} + debug@3.2.7: dependencies: ms: 2.1.3 @@ -7598,6 +8508,10 @@ snapshots: defu@6.1.4: {} + delaunator@5.0.1: + dependencies: + robust-predicates: 3.0.2 + delayed-stream@1.0.0: {} dependency-cruiser@17.3.1: @@ -7656,6 +8570,10 @@ snapshots: dom-accessibility-api@0.6.3: {} + dompurify@3.3.0: + optionalDependencies: + '@types/trusted-types': 2.0.7 + dotenv@16.6.1: {} dunder-proto@1.0.1: @@ -7893,32 +8811,12 @@ snapshots: escape-string-regexp@5.0.0: {} - eslint-config-next@16.0.4(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3): - dependencies: - '@next/eslint-plugin-next': 16.0.4 - eslint: 9.39.1(jiti@1.21.7) - eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@1.21.7)) - eslint-plugin-import: 2.32.0(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@1.21.7)) - eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.1(jiti@1.21.7)) - eslint-plugin-react: 7.37.5(eslint@9.39.1(jiti@1.21.7)) - eslint-plugin-react-hooks: 7.0.1(eslint@9.39.1(jiti@1.21.7)) - globals: 16.4.0 - typescript-eslint: 8.48.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - optionalDependencies: - typescript: 5.9.3 - transitivePeerDependencies: - - '@typescript-eslint/parser' - - eslint-import-resolver-webpack - - eslint-plugin-import-x - - supports-color - eslint-config-next@16.0.4(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3): dependencies: '@next/eslint-plugin-next': 16.0.4 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)(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@9.39.1(jiti@2.6.1)) eslint-plugin-import: 2.32.0(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@2.6.1)) eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.1(jiti@2.6.1)) eslint-plugin-react: 7.37.5(eslint@9.39.1(jiti@2.6.1)) @@ -7941,22 +8839,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@1.21.7)): - dependencies: - '@nolyfill/is-core-module': 1.0.39 - debug: 4.4.3 - eslint: 9.39.1(jiti@1.21.7) - 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(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@1.21.7)) - transitivePeerDependencies: - - supports-color - - eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(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@9.39.1(jiti@2.6.1)): dependencies: '@nolyfill/is-core-module': 1.0.39 debug: 4.4.3 @@ -7981,23 +8864,13 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@1.21.7)): - dependencies: - debug: 3.2.7 - optionalDependencies: - eslint: 9.39.1(jiti@1.21.7) - eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@1.21.7)) - transitivePeerDependencies: - - supports-color - - eslint-module-utils@2.12.1(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@2.6.1)): + eslint-module-utils@2.12.1(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1)): dependencies: debug: 3.2.7 optionalDependencies: 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)(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@9.39.1(jiti@2.6.1)) transitivePeerDependencies: - supports-color @@ -8030,33 +8903,6 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-plugin-import@2.32.0(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@1.21.7)): - dependencies: - '@rtsao/scc': 1.1.0 - array-includes: 3.1.9 - array.prototype.findlastindex: 1.2.6 - array.prototype.flat: 1.3.3 - array.prototype.flatmap: 1.3.3 - debug: 3.2.7 - doctrine: 2.1.0 - eslint: 9.39.1(jiti@1.21.7) - eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@1.21.7)) - hasown: 2.0.2 - is-core-module: 2.16.1 - is-glob: 4.0.3 - minimatch: 3.1.2 - object.fromentries: 2.0.8 - object.groupby: 1.0.3 - object.values: 1.2.1 - semver: 6.3.1 - string.prototype.trimend: 1.0.9 - tsconfig-paths: 3.15.0 - transitivePeerDependencies: - - eslint-import-resolver-typescript - - eslint-import-resolver-webpack - - supports-color - eslint-plugin-import@2.32.0(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@2.6.1)): dependencies: '@rtsao/scc': 1.1.0 @@ -8068,7 +8914,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(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@2.6.1)) + eslint-module-utils: 2.12.1(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1)) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -8084,25 +8930,6 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-plugin-jsx-a11y@6.10.2(eslint@9.39.1(jiti@1.21.7)): - dependencies: - aria-query: 5.3.2 - array-includes: 3.1.9 - array.prototype.flatmap: 1.3.3 - ast-types-flow: 0.0.8 - axe-core: 4.11.0 - axobject-query: 4.1.0 - damerau-levenshtein: 1.0.8 - emoji-regex: 9.2.2 - eslint: 9.39.1(jiti@1.21.7) - hasown: 2.0.2 - jsx-ast-utils: 3.3.5 - language-tags: 1.0.9 - minimatch: 3.1.2 - object.fromentries: 2.0.8 - safe-regex-test: 1.1.0 - string.prototype.includes: 2.0.1 - eslint-plugin-jsx-a11y@6.10.2(eslint@9.39.1(jiti@2.6.1)): dependencies: aria-query: 5.3.2 @@ -8126,17 +8953,6 @@ snapshots: dependencies: eslint: 9.39.1(jiti@2.6.1) - eslint-plugin-react-hooks@7.0.1(eslint@9.39.1(jiti@1.21.7)): - dependencies: - '@babel/core': 7.28.5 - '@babel/parser': 7.28.5 - eslint: 9.39.1(jiti@1.21.7) - hermes-parser: 0.25.1 - zod: 4.1.13 - zod-validation-error: 4.0.2(zod@4.1.13) - transitivePeerDependencies: - - supports-color - eslint-plugin-react-hooks@7.0.1(eslint@9.39.1(jiti@2.6.1)): dependencies: '@babel/core': 7.28.5 @@ -8148,28 +8964,6 @@ snapshots: transitivePeerDependencies: - supports-color - 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 @@ -8201,47 +8995,6 @@ snapshots: eslint-visitor-keys@4.2.1: {} - eslint@9.39.1(jiti@1.21.7): - dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@1.21.7)) - '@eslint-community/regexpp': 4.12.2 - '@eslint/config-array': 0.21.1 - '@eslint/config-helpers': 0.4.2 - '@eslint/core': 0.17.0 - '@eslint/eslintrc': 3.3.1 - '@eslint/js': 9.39.1 - '@eslint/plugin-kit': 0.4.1 - '@humanfs/node': 0.16.7 - '@humanwhocodes/module-importer': 1.0.1 - '@humanwhocodes/retry': 0.4.3 - '@types/estree': 1.0.8 - ajv: 6.12.6 - chalk: 4.1.2 - cross-spawn: 7.0.6 - debug: 4.4.3 - escape-string-regexp: 4.0.0 - eslint-scope: 8.4.0 - eslint-visitor-keys: 4.2.1 - espree: 10.4.0 - esquery: 1.6.0 - esutils: 2.0.3 - fast-deep-equal: 3.1.3 - file-entry-cache: 8.0.0 - find-up: 5.0.0 - glob-parent: 6.0.2 - ignore: 5.3.2 - imurmurhash: 0.1.4 - is-glob: 4.0.3 - json-stable-stringify-without-jsonify: 1.0.1 - lodash.merge: 4.6.2 - minimatch: 3.1.2 - natural-compare: 1.4.0 - optionator: 0.9.4 - optionalDependencies: - jiti: 1.21.7 - transitivePeerDependencies: - - supports-color - eslint@9.39.1(jiti@2.6.1): dependencies: '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@2.6.1)) @@ -8391,11 +9144,6 @@ snapshots: transitivePeerDependencies: - debug - fish-joke-generator@1.1.0(openai@6.9.1(ws@8.18.3)(zod@4.1.13))(zod@4.1.13): - dependencies: - openai: 6.9.1(ws@8.18.3)(zod@4.1.13) - zod: 4.1.13 - fix-dts-default-cjs-exports@1.0.1: dependencies: magic-string: 0.30.21 @@ -8470,6 +9218,8 @@ snapshots: get-caller-file@2.0.5: {} + get-east-asian-width@1.4.0: {} + get-intrinsic@1.3.0: dependencies: call-bind-apply-helpers: 1.0.2 @@ -8560,6 +9310,8 @@ snapshots: graphql@16.12.0: {} + hachure-fill@0.5.2: {} + happy-dom@15.11.7: dependencies: entities: 4.5.0 @@ -8588,6 +9340,28 @@ snapshots: dependencies: function-bind: 1.1.2 + hast-util-from-dom@5.0.1: + dependencies: + '@types/hast': 3.0.4 + hastscript: 9.0.1 + web-namespaces: 2.0.1 + + hast-util-from-html-isomorphic@2.0.0: + dependencies: + '@types/hast': 3.0.4 + hast-util-from-dom: 5.0.1 + hast-util-from-html: 2.0.3 + unist-util-remove-position: 5.0.0 + + hast-util-from-html@2.0.3: + dependencies: + '@types/hast': 3.0.4 + devlop: 1.1.0 + hast-util-from-parse5: 8.0.3 + parse5: 7.3.0 + vfile: 6.0.3 + vfile-message: 4.0.3 + hast-util-from-parse5@8.0.3: dependencies: '@types/hast': 3.0.4 @@ -8599,6 +9373,10 @@ snapshots: vfile-location: 5.0.3 web-namespaces: 2.0.1 + hast-util-is-element@3.0.0: + dependencies: + '@types/hast': 3.0.4 + hast-util-parse-selector@4.0.0: dependencies: '@types/hast': 3.0.4 @@ -8625,6 +9403,20 @@ snapshots: '@ungap/structured-clone': 1.3.0 unist-util-position: 5.0.0 + hast-util-to-html@9.0.5: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + ccount: 2.0.1 + comma-separated-tokens: 2.0.3 + hast-util-whitespace: 3.0.0 + html-void-elements: 3.0.0 + mdast-util-to-hast: 13.2.1 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + stringify-entities: 4.0.4 + zwitch: 2.0.4 + hast-util-to-jsx-runtime@2.3.6: dependencies: '@types/estree': 1.0.8 @@ -8655,10 +9447,19 @@ snapshots: web-namespaces: 2.0.1 zwitch: 2.0.4 + hast-util-to-text@4.0.2: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + hast-util-is-element: 3.0.0 + unist-util-find-after: 5.0.0 + hast-util-whitespace@3.0.0: dependencies: '@types/hast': 3.0.4 + hast@1.0.0: {} + hastscript@9.0.1: dependencies: '@types/hast': 3.0.4 @@ -8685,6 +9486,10 @@ snapshots: human-id@4.1.2: {} + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + iconv-lite@0.7.0: dependencies: safer-buffer: 2.1.2 @@ -8714,6 +9519,10 @@ snapshots: hasown: 2.0.2 side-channel: 1.1.0 + internmap@1.0.1: {} + + internmap@2.0.3: {} + interpret@3.1.1: {} is-alphabetical@2.0.1: {} @@ -8940,10 +9749,16 @@ snapshots: object.assign: 4.1.7 object.values: 1.2.1 + katex@0.16.25: + dependencies: + commander: 8.3.0 + keyv@4.5.4: dependencies: json-buffer: 3.0.1 + khroma@2.1.0: {} + kleur@3.0.3: {} knip@5.70.2(@types/node@22.19.1)(typescript@5.9.3): @@ -8963,12 +9778,24 @@ snapshots: typescript: 5.9.3 zod: 4.1.13 + langium@3.3.1: + dependencies: + chevrotain: 11.0.3 + chevrotain-allstar: 0.3.1(chevrotain@11.0.3) + vscode-languageserver: 9.0.1 + vscode-languageserver-textdocument: 1.0.12 + vscode-uri: 3.0.8 + language-subtag-registry@0.3.23: {} language-tags@1.0.9: dependencies: language-subtag-registry: 0.3.23 + layout-base@1.0.2: {} + + layout-base@2.0.1: {} + lefthook-darwin-arm64@1.13.6: optional: true @@ -9031,6 +9858,8 @@ snapshots: dependencies: p-locate: 5.0.0 + lodash-es@4.17.21: {} + lodash.merge@4.6.2: {} lodash.startcase@4.4.0: {} @@ -9058,6 +9887,10 @@ snapshots: dependencies: yallist: 3.1.1 + lucide-react@0.542.0(react@19.2.0): + dependencies: + react: 19.2.0 + lz-string@1.5.0: {} magic-string@0.27.0: @@ -9072,6 +9905,8 @@ snapshots: markdown-table@3.0.4: {} + marked@16.4.2: {} + math-intrinsics@1.1.0: {} mdast-util-find-and-replace@3.0.2: @@ -9155,6 +9990,18 @@ snapshots: transitivePeerDependencies: - supports-color + mdast-util-math@3.0.0: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + devlop: 1.1.0 + longest-streak: 3.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + unist-util-remove-position: 5.0.0 + transitivePeerDependencies: + - supports-color + mdast-util-mdx-expression@2.0.1: dependencies: '@types/estree-jsx': 1.0.5 @@ -9237,6 +10084,29 @@ snapshots: merge2@1.4.1: {} + mermaid@11.12.2: + dependencies: + '@braintree/sanitize-url': 7.1.1 + '@iconify/utils': 3.1.0 + '@mermaid-js/parser': 0.6.3 + '@types/d3': 7.4.3 + cytoscape: 3.33.1 + cytoscape-cose-bilkent: 4.1.0(cytoscape@3.33.1) + cytoscape-fcose: 2.2.0(cytoscape@3.33.1) + d3: 7.9.0 + d3-sankey: 0.12.3 + dagre-d3-es: 7.0.13 + dayjs: 1.11.19 + dompurify: 3.3.0 + katex: 0.16.25 + khroma: 2.1.0 + lodash-es: 4.17.21 + marked: 16.4.2 + roughjs: 4.6.6 + stylis: 4.3.6 + ts-dedent: 2.2.0 + uuid: 11.1.0 + micromark-core-commonmark@2.0.3: dependencies: decode-named-character-reference: 1.2.0 @@ -9256,6 +10126,38 @@ snapshots: micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.2 + micromark-extension-cjk-friendly-gfm-strikethrough@1.2.3(micromark-util-types@2.0.2)(micromark@4.0.2): + dependencies: + devlop: 1.1.0 + get-east-asian-width: 1.4.0 + micromark: 4.0.2 + micromark-extension-cjk-friendly-util: 2.1.1(micromark-util-types@2.0.2) + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-symbol: 2.0.1 + optionalDependencies: + micromark-util-types: 2.0.2 + + micromark-extension-cjk-friendly-util@2.1.1(micromark-util-types@2.0.2): + dependencies: + get-east-asian-width: 1.4.0 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + optionalDependencies: + micromark-util-types: 2.0.2 + + micromark-extension-cjk-friendly@1.2.3(micromark-util-types@2.0.2)(micromark@4.0.2): + dependencies: + devlop: 1.1.0 + micromark: 4.0.2 + micromark-extension-cjk-friendly-util: 2.1.1(micromark-util-types@2.0.2) + micromark-util-chunked: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-symbol: 2.0.1 + optionalDependencies: + micromark-util-types: 2.0.2 + micromark-extension-gfm-autolink-literal@2.1.0: dependencies: micromark-util-character: 2.1.1 @@ -9314,6 +10216,16 @@ snapshots: micromark-util-combine-extensions: 2.0.1 micromark-util-types: 2.0.2 + micromark-extension-math@3.1.0: + dependencies: + '@types/katex': 0.16.7 + devlop: 1.1.0 + katex: 0.16.25 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + micromark-factory-destination@2.0.1: dependencies: micromark-util-character: 2.1.1 @@ -9600,6 +10512,14 @@ snapshots: ohash@2.0.11: {} + oniguruma-parser@0.12.1: {} + + oniguruma-to-es@4.3.4: + dependencies: + oniguruma-parser: 0.12.1 + regex: 6.0.1 + regex-recursion: 6.0.2 + open@8.4.2: dependencies: define-lazy-prop: 2.0.0 @@ -9682,6 +10602,8 @@ snapshots: dependencies: quansync: 0.2.11 + package-manager-detector@1.6.0: {} + parent-module@1.0.1: dependencies: callsites: 3.1.0 @@ -9700,6 +10622,8 @@ snapshots: dependencies: entities: 6.0.1 + path-data-parser@0.1.0: {} + path-exists@4.0.0: {} path-key@3.1.1: {} @@ -9752,6 +10676,13 @@ snapshots: exsolve: 1.0.8 pathe: 2.0.3 + points-on-curve@0.2.0: {} + + points-on-path@0.2.1: + dependencies: + path-data-parser: 0.1.0 + points-on-curve: 0.2.0 + polished@4.3.1: dependencies: '@babel/runtime': 7.28.4 @@ -9770,21 +10701,21 @@ snapshots: camelcase-css: 2.0.1 postcss: 8.5.6 - postcss-load-config@6.0.1(jiti@1.21.7)(postcss@8.5.6)(tsx@4.20.6): + postcss-load-config@6.0.1(jiti@1.21.7)(postcss@8.5.6)(tsx@4.21.0): dependencies: lilconfig: 3.1.3 optionalDependencies: jiti: 1.21.7 postcss: 8.5.6 - tsx: 4.20.6 + tsx: 4.21.0 - postcss-load-config@6.0.1(jiti@2.6.1)(postcss@8.5.6)(tsx@4.20.6): + postcss-load-config@6.0.1(jiti@2.6.1)(postcss@8.5.6)(tsx@4.21.0): dependencies: lilconfig: 3.1.3 optionalDependencies: jiti: 2.6.1 postcss: 8.5.6 - tsx: 4.20.6 + tsx: 4.21.0 postcss-nested@6.2.0(postcss@8.5.6): dependencies: @@ -9978,6 +10909,16 @@ snapshots: hastscript: 9.0.1 parse-entities: 4.0.2 + regex-recursion@6.0.2: + dependencies: + regex-utilities: 2.3.0 + + regex-utilities@2.3.0: {} + + regex@6.0.1: + dependencies: + regex-utilities: 2.3.0 + regexp-tree@0.1.27: {} regexp.prototype.flags@1.5.4: @@ -9989,6 +10930,20 @@ snapshots: gopd: 1.2.0 set-function-name: 2.0.2 + rehype-harden@1.1.6: + dependencies: + unist-util-visit: 5.0.0 + + rehype-katex@7.0.1: + dependencies: + '@types/hast': 3.0.4 + '@types/katex': 0.16.7 + hast-util-from-html-isomorphic: 2.0.0 + hast-util-to-text: 4.0.2 + katex: 0.16.25 + unist-util-visit-parents: 6.0.2 + vfile: 6.0.3 + rehype-raw@7.0.0: dependencies: '@types/hast': 3.0.4 @@ -10000,6 +10955,26 @@ snapshots: '@types/hast': 3.0.4 hast-util-sanitize: 5.0.2 + remark-cjk-friendly-gfm-strikethrough@1.2.3(@types/mdast@4.0.4)(micromark-util-types@2.0.2)(micromark@4.0.2)(unified@11.0.5): + dependencies: + micromark-extension-cjk-friendly-gfm-strikethrough: 1.2.3(micromark-util-types@2.0.2)(micromark@4.0.2) + unified: 11.0.5 + optionalDependencies: + '@types/mdast': 4.0.4 + transitivePeerDependencies: + - micromark + - micromark-util-types + + remark-cjk-friendly@1.2.3(@types/mdast@4.0.4)(micromark-util-types@2.0.2)(micromark@4.0.2)(unified@11.0.5): + dependencies: + micromark-extension-cjk-friendly: 1.2.3(micromark-util-types@2.0.2)(micromark@4.0.2) + unified: 11.0.5 + optionalDependencies: + '@types/mdast': 4.0.4 + transitivePeerDependencies: + - micromark + - micromark-util-types + remark-gfm@4.0.1: dependencies: '@types/mdast': 4.0.4 @@ -10011,6 +10986,15 @@ snapshots: transitivePeerDependencies: - supports-color + remark-math@6.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-math: 3.0.0 + micromark-extension-math: 3.1.0 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + remark-parse@11.0.0: dependencies: '@types/mdast': 4.0.4 @@ -10060,6 +11044,8 @@ snapshots: reusify@1.1.0: {} + robust-predicates@3.0.2: {} + rollup@4.53.3: dependencies: '@types/estree': 1.0.8 @@ -10088,10 +11074,19 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.53.3 fsevents: 2.3.3 + roughjs@4.6.6: + dependencies: + hachure-fill: 0.5.2 + path-data-parser: 0.1.0 + points-on-curve: 0.2.0 + points-on-path: 0.2.1 + run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 + rw@1.3.3: {} + safe-array-concat@1.1.3: dependencies: call-bind: 1.0.8 @@ -10183,6 +11178,17 @@ snapshots: shebang-regex@3.0.0: {} + shiki@3.19.0: + dependencies: + '@shikijs/core': 3.19.0 + '@shikijs/engine-javascript': 3.19.0 + '@shikijs/engine-oniguruma': 3.19.0 + '@shikijs/langs': 3.19.0 + '@shikijs/themes': 3.19.0 + '@shikijs/types': 3.19.0 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + side-channel-list@1.0.0: dependencies: es-errors: 1.3.0 @@ -10259,6 +11265,36 @@ snapshots: - supports-color - utf-8-validate + streamdown@1.6.9(@types/mdast@4.0.4)(micromark-util-types@2.0.2)(micromark@4.0.2)(react@19.2.0): + dependencies: + clsx: 2.1.1 + hast: 1.0.0 + hast-util-to-jsx-runtime: 2.3.6 + html-url-attributes: 3.0.1 + katex: 0.16.25 + lucide-react: 0.542.0(react@19.2.0) + marked: 16.4.2 + mermaid: 11.12.2 + react: 19.2.0 + rehype-harden: 1.1.6 + rehype-katex: 7.0.1 + rehype-raw: 7.0.0 + remark-cjk-friendly: 1.2.3(@types/mdast@4.0.4)(micromark-util-types@2.0.2)(micromark@4.0.2)(unified@11.0.5) + remark-cjk-friendly-gfm-strikethrough: 1.2.3(@types/mdast@4.0.4)(micromark-util-types@2.0.2)(micromark@4.0.2)(unified@11.0.5) + remark-gfm: 4.0.1 + remark-math: 6.0.0 + remark-parse: 11.0.0 + remark-rehype: 11.1.2 + shiki: 3.19.0 + tailwind-merge: 3.4.0 + unified: 11.0.5 + unist-util-visit: 5.0.0 + transitivePeerDependencies: + - '@types/mdast' + - micromark + - micromark-util-types + - supports-color + strict-event-emitter@0.5.1: {} string-width@4.2.3: @@ -10363,6 +11399,8 @@ snapshots: optionalDependencies: '@babel/core': 7.28.5 + stylis@4.3.6: {} + sucrase@3.35.1: dependencies: '@jridgewell/gen-mapping': 0.3.13 @@ -10389,7 +11427,9 @@ snapshots: tailwind-merge@2.6.0: {} - tailwindcss@3.4.18(tsx@4.20.6): + tailwind-merge@3.4.0: {} + + tailwindcss@3.4.18(tsx@4.21.0): dependencies: '@alloc/quick-lru': 5.2.0 arg: 5.0.2 @@ -10408,7 +11448,7 @@ snapshots: postcss: 8.5.6 postcss-import: 15.1.0(postcss@8.5.6) postcss-js: 4.1.0(postcss@8.5.6) - postcss-load-config: 6.0.1(jiti@1.21.7)(postcss@8.5.6)(tsx@4.20.6) + postcss-load-config: 6.0.1(jiti@1.21.7)(postcss@8.5.6)(tsx@4.21.0) postcss-nested: 6.2.0(postcss@8.5.6) postcss-selector-parser: 6.1.2 resolve: 1.22.11 @@ -10502,7 +11542,7 @@ snapshots: tslib@2.8.1: {} - tsup@8.5.1(jiti@2.6.1)(postcss@8.5.6)(tsx@4.20.6)(typescript@5.9.3): + tsup@8.5.1(jiti@2.6.1)(postcss@8.5.6)(tsx@4.21.0)(typescript@5.9.3): dependencies: bundle-require: 5.1.0(esbuild@0.27.0) cac: 6.7.14 @@ -10513,7 +11553,7 @@ snapshots: fix-dts-default-cjs-exports: 1.0.1 joycon: 3.1.1 picocolors: 1.1.1 - postcss-load-config: 6.0.1(jiti@2.6.1)(postcss@8.5.6)(tsx@4.20.6) + postcss-load-config: 6.0.1(jiti@2.6.1)(postcss@8.5.6)(tsx@4.21.0) resolve-from: 5.0.0 rollup: 4.53.3 source-map: 0.7.6 @@ -10542,6 +11582,13 @@ snapshots: optionalDependencies: fsevents: 2.3.3 + tsx@4.21.0: + dependencies: + esbuild: 0.27.0 + get-tsconfig: 4.13.0 + optionalDependencies: + fsevents: 2.3.3 + turbo-darwin-64@2.6.1: optional: true @@ -10627,17 +11674,6 @@ snapshots: possible-typed-array-names: 1.1.0 reflect.getprototypeof: 1.0.10 - typescript-eslint@8.48.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3): - dependencies: - '@typescript-eslint/eslint-plugin': 8.48.0(@typescript-eslint/parser@8.48.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - '@typescript-eslint/parser': 8.48.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - '@typescript-eslint/typescript-estree': 8.48.0(typescript@5.9.3) - '@typescript-eslint/utils': 8.48.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - eslint: 9.39.1(jiti@1.21.7) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - typescript-eslint@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3): dependencies: '@typescript-eslint/eslint-plugin': 8.48.0(@typescript-eslint/parser@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) @@ -10674,6 +11710,11 @@ snapshots: trough: 2.2.0 vfile: 6.0.3 + unist-util-find-after@5.0.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + unist-util-is@6.0.1: dependencies: '@types/unist': 3.0.3 @@ -10682,6 +11723,11 @@ snapshots: dependencies: '@types/unist': 3.0.3 + unist-util-remove-position@5.0.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-visit: 5.0.0 + unist-util-stringify-position@4.0.0: dependencies: '@types/unist': 3.0.3 @@ -10754,6 +11800,8 @@ snapshots: is-typed-array: 1.1.15 which-typed-array: 1.1.19 + uuid@11.1.0: {} + uuid@9.0.1: {} vfile-location@5.0.3: @@ -10798,7 +11846,7 @@ snapshots: '@types/node': 22.19.1 fsevents: 2.3.3 - vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(tsx@4.20.6): + vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(tsx@4.21.0): dependencies: esbuild: 0.25.12 fdir: 6.5.0(picomatch@4.0.3) @@ -10810,7 +11858,7 @@ snapshots: '@types/node': 22.19.1 fsevents: 2.3.3 jiti: 2.6.1 - tsx: 4.20.6 + tsx: 4.21.0 vitest@2.1.9(@types/node@22.19.1)(happy-dom@15.11.7)(msw@2.12.3(@types/node@22.19.1)(typescript@5.9.3)): dependencies: @@ -10848,6 +11896,23 @@ snapshots: - supports-color - terser + vscode-jsonrpc@8.2.0: {} + + vscode-languageserver-protocol@3.17.5: + dependencies: + vscode-jsonrpc: 8.2.0 + vscode-languageserver-types: 3.17.5 + + vscode-languageserver-textdocument@1.0.12: {} + + vscode-languageserver-types@3.17.5: {} + + vscode-languageserver@9.0.1: + dependencies: + vscode-languageserver-protocol: 3.17.5 + + vscode-uri@3.0.8: {} + walk-up-path@4.0.0: {} watskeburt@5.0.0: {} diff --git a/sync-hello.ts b/sync-hello.ts new file mode 100644 index 0000000..7f3df6b --- /dev/null +++ b/sync-hello.ts @@ -0,0 +1,116 @@ +import { PrismaClient } from '@prisma/client'; +import { validateTpmjsField } from '@tpmjs/types/tpmjs'; + +const prisma = new PrismaClient(); + +async function syncHello() { + try { + console.log('Fetching @tpmjs/hello from npm...'); + + const response = await fetch('https://registry.npmjs.org/@tpmjs/hello'); + const data = await response.json(); + + const latest = data['dist-tags'].latest; + const pkg = data.versions[latest]; + + console.log(`\nPackage: ${pkg.name}@${pkg.version}`); + console.log(`Keywords: ${pkg.keywords.join(', ')}`); + console.log(`\ntpmjs field:`, JSON.stringify(pkg.tpmjs, null, 2)); + + // Validate + const validation = validateTpmjsField(pkg.tpmjs); + + if (!validation.valid) { + console.error('\nāŒ Invalid tpmjs field:', validation.errors); + process.exit(1); + } + + console.log(`\nāœ… Valid tpmjs field (${validation.tier} tier)`); + console.log(`Tools to create: ${validation.tools?.length || 0}`); + + // Upsert Package + const packageRecord = await prisma.package.upsert({ + where: { npmPackageName: pkg.name }, + create: { + npmPackageName: pkg.name, + npmVersion: pkg.version, + npmPublishedAt: new Date(pkg.time[pkg.version]), + npmDownloadsLastMonth: 0, + category: validation.packageData!.category, + env: validation.packageData!.env || null, + frameworks: validation.packageData!.frameworks || [], + tier: validation.tier!, + discoveryMethod: 'manual', + isOfficial: pkg.name.startsWith('@tpmjs/'), + }, + update: { + npmVersion: pkg.version, + npmPublishedAt: new Date(pkg.time[pkg.version]), + category: validation.packageData!.category, + env: validation.packageData!.env || null, + frameworks: validation.packageData!.frameworks || [], + tier: validation.tier!, + }, + }); + + console.log(`\nāœ… Package upserted: ${packageRecord.id}`); + + // Get existing tools + const existingTools = await prisma.tool.findMany({ + where: { packageId: packageRecord.id }, + }); + + console.log(`\nExisting tools: ${existingTools.length}`); + + // Upsert each tool + for (const toolDef of validation.tools || []) { + const tool = await prisma.tool.upsert({ + where: { + packageId_exportName: { + packageId: packageRecord.id, + exportName: toolDef.exportName, + }, + }, + create: { + packageId: packageRecord.id, + exportName: toolDef.exportName, + description: toolDef.description, + parameters: toolDef.parameters ? (toolDef.parameters as any) : undefined, + returns: toolDef.returns ? (toolDef.returns as any) : undefined, + aiAgent: toolDef.aiAgent ? (toolDef.aiAgent as any) : undefined, + qualityScore: null, + }, + update: { + description: toolDef.description, + parameters: toolDef.parameters ? (toolDef.parameters as any) : undefined, + returns: toolDef.returns ? (toolDef.returns as any) : undefined, + aiAgent: toolDef.aiAgent ? (toolDef.aiAgent as any) : undefined, + }, + }); + + console.log(`āœ… Tool upserted: ${tool.exportName} (${tool.id})`); + } + + // Delete orphaned tools + const orphanedTools = existingTools.filter( + (existingTool) => + !validation.tools?.some((toolDef) => toolDef.exportName === existingTool.exportName) + ); + + if (orphanedTools.length > 0) { + await prisma.tool.deleteMany({ + where: { id: { in: orphanedTools.map((t) => t.id) } }, + }); + console.log(`\nšŸ—‘ļø Deleted ${orphanedTools.length} orphaned tools`); + } + + console.log('\nāœ… Sync complete!'); + } catch (error) { + console.error('\nāŒ Sync failed:', error); + process.exit(1); + } finally { + await prisma.$disconnect(); + } +} + +syncHello(); diff --git a/sync-manual-tools.ts b/sync-manual-tools.ts new file mode 100644 index 0000000..3ffad02 --- /dev/null +++ b/sync-manual-tools.ts @@ -0,0 +1,138 @@ +import { prisma } from './packages/db/src/index.js'; +import { manualTools } from './manual-tools.js'; +import { fetchLatestPackageWithMetadata } from './packages/npm-client/src/package.js'; + +async function syncManualTools() { + console.log('\nšŸ”§ Starting manual tools sync...\n'); + + let processed = 0; + let skipped = 0; + let errors = 0; + + for (const manualTool of manualTools) { + try { + console.log(`Processing: ${manualTool.npmPackageName} (${manualTool.exportName})`); + + // Fetch package metadata from npm + const npmData = await fetchLatestPackageWithMetadata(manualTool.npmPackageName); + + if (!npmData) { + console.error(` āŒ Package not found on npm: ${manualTool.npmPackageName}`); + errors++; + continue; + } + + // Use manual version if specified, otherwise use latest from npm + const version = manualTool.npmVersion || npmData.version; + + // Get published date + const publishedAt = npmData.time?.[version] || npmData.time?.modified || new Date().toISOString(); + + // Upsert Package record + const packageRecord = await prisma.package.upsert({ + where: { npmPackageName: manualTool.npmPackageName }, + create: { + npmPackageName: manualTool.npmPackageName, + npmVersion: version, + npmPublishedAt: new Date(publishedAt), + npmDescription: npmData.description || null, + npmRepository: npmData.repository || null, + npmHomepage: npmData.homepage || manualTool.websiteUrl || null, + npmLicense: npmData.license || null, + npmKeywords: npmData.keywords || [], + npmReadme: npmData.readme || null, + npmAuthor: npmData.author || null, + npmMaintainers: npmData.maintainers || null, + category: manualTool.category, + env: manualTool.env ? (manualTool.env as any) : null, + frameworks: manualTool.frameworks, + tier: calculateTier(manualTool), + discoveryMethod: 'manual', + isOfficial: manualTool.npmPackageName.startsWith('@tpmjs/'), + npmDownloadsLastMonth: 0, + }, + update: { + npmVersion: version, + npmPublishedAt: new Date(publishedAt), + npmDescription: npmData.description || null, + npmRepository: npmData.repository || null, + npmHomepage: npmData.homepage || manualTool.websiteUrl || null, + npmLicense: npmData.license || null, + npmKeywords: npmData.keywords || [], + npmReadme: npmData.readme || null, + npmAuthor: npmData.author || null, + npmMaintainers: npmData.maintainers || null, + category: manualTool.category, + env: manualTool.env ? (manualTool.env as any) : null, + frameworks: manualTool.frameworks, + tier: calculateTier(manualTool), + isOfficial: manualTool.npmPackageName.startsWith('@tpmjs/'), + }, + }); + + console.log(` āœ… Package upserted: ${packageRecord.id}`); + + // Get existing tools for this package + const existingTools = await prisma.tool.findMany({ + where: { packageId: packageRecord.id }, + }); + + // Upsert the tool + const tool = await prisma.tool.upsert({ + where: { + packageId_exportName: { + packageId: packageRecord.id, + exportName: manualTool.exportName, + }, + }, + create: { + packageId: packageRecord.id, + exportName: manualTool.exportName, + description: manualTool.description, + parameters: manualTool.parameters ? (manualTool.parameters as any) : null, + returns: manualTool.returns ? (manualTool.returns as any) : null, + aiAgent: manualTool.aiAgent ? (manualTool.aiAgent as any) : null, + }, + update: { + description: manualTool.description, + parameters: manualTool.parameters ? (manualTool.parameters as any) : null, + returns: manualTool.returns ? (manualTool.returns as any) : null, + aiAgent: manualTool.aiAgent ? (manualTool.aiAgent as any) : null, + }, + }); + + console.log(` āœ… Tool upserted: ${tool.exportName} (${tool.id})`); + processed++; + } catch (error) { + console.error( + ` āŒ Error processing ${manualTool.npmPackageName} (${manualTool.exportName}):`, + error + ); + errors++; + } + } + + console.log('\nšŸ“Š Manual sync complete!'); + console.log(` Processed: ${processed}`); + console.log(` Skipped: ${skipped}`); + console.log(` Errors: ${errors}`); + console.log(` Total manual tools: ${manualTools.length}\n`); +} + +function calculateTier(tool: typeof manualTools[0]): 'minimal' | 'rich' { + // Tier is 'rich' if tool has parameters OR returns OR aiAgent + if (tool.parameters || tool.returns || tool.aiAgent) { + return 'rich'; + } + return 'minimal'; +} + +syncManualTools() + .then(() => process.exit(0)) + .catch((error) => { + console.error('āŒ Manual sync failed:', error); + process.exit(1); + }) + .finally(() => { + prisma.$disconnect(); + }); diff --git a/test-schema.ts b/test-schema.ts new file mode 100644 index 0000000..902d6cb --- /dev/null +++ b/test-schema.ts @@ -0,0 +1,88 @@ +import { PrismaClient } from '@prisma/client'; +const prisma = new PrismaClient(); + +async function testSchema() { + console.log('Testing new Package + Tool schema...\n'); + + // Test 1: Create a package + console.log('1. Creating test package...'); + const pkg = await prisma.package.create({ + data: { + npmPackageName: '@test/hello', + npmVersion: '1.0.0', + npmPublishedAt: new Date(), + category: 'text-analysis', + tier: 'rich', + discoveryMethod: 'test', + isOfficial: false, + frameworks: ['vercel-ai'], + npmDownloadsLastMonth: 100, + }, + }); + console.log(`āœ… Package created: ${pkg.npmPackageName} (${pkg.id})\n`); + + // Test 2: Create multiple tools for the package + console.log('2. Creating tools for package...'); + const tool1 = await prisma.tool.create({ + data: { + packageId: pkg.id, + exportName: 'helloWorldTool', + description: 'Returns a simple Hello World greeting', + }, + }); + console.log(`āœ… Tool 1 created: ${tool1.exportName}`); + + const tool2 = await prisma.tool.create({ + data: { + packageId: pkg.id, + exportName: 'helloNameTool', + description: 'Returns a personalized greeting with name', + }, + }); + console.log(`āœ… Tool 2 created: ${tool2.exportName}\n`); + + // Test 3: Query package with tools + console.log('3. Querying package with tools...'); + const packageWithTools = await prisma.package.findUnique({ + where: { npmPackageName: '@test/hello' }, + include: { tools: true }, + }); + console.log(`āœ… Found package with ${packageWithTools?.tools.length} tools:`); + packageWithTools?.tools.forEach((t) => { + console.log(` - ${t.exportName}: ${t.description}`); + }); + console.log(); + + // Test 4: Query tool with package + console.log('4. Querying tool with package...'); + const toolWithPackage = await prisma.tool.findFirst({ + where: { + package: { npmPackageName: '@test/hello' }, + exportName: 'helloWorldTool', + }, + include: { package: true }, + }); + console.log(`āœ… Found tool: ${toolWithPackage?.exportName}`); + console.log(` Package: ${toolWithPackage?.package.npmPackageName}`); + console.log(` Category: ${toolWithPackage?.package.category}\n`); + + // Test 5: Delete package (should cascade to tools) + console.log('5. Testing cascade delete...'); + await prisma.package.delete({ + where: { id: pkg.id }, + }); + const remainingTools = await prisma.tool.count({ + where: { packageId: pkg.id }, + }); + console.log(`āœ… Package deleted, remaining tools: ${remainingTools}`); + console.log(` (Should be 0 due to cascade delete)\n`); + + console.log('āœ… All schema tests passed!'); +} + +testSchema() + .then(() => process.exit(0)) + .catch((error) => { + console.error('āŒ Test failed:', error); + process.exit(1); + });