diff --git a/BRAINY-CHAT.md b/BRAINY-CHAT.md deleted file mode 100644 index 4a7568f0..00000000 --- a/BRAINY-CHAT.md +++ /dev/null @@ -1,265 +0,0 @@ -# Brainy Chat - Talk to Your Data (Coming Soon!) 🧠💬 - -**Transform your Brainy database into an intelligent conversational AI** with the simplest API imaginable - just one method, one optional parameter! - -## 🚀 The Simplest AI Chat API Ever Created - -```javascript -// Coming in v0.56 - Just ONE line, ONE method! -import { BrainyChat } from '@soulcraft/brainy/chat' - -const chat = new BrainyChat(brainy) // That's literally it! -const response = await chat.ask("What are the trends in our customer data?") -``` - -## 🎯 The Beauty of Simplicity: One Optional Parameter - -```javascript -// WITHOUT LLM - Works instantly with template-based responses -const chat = new BrainyChat(brainy) -await chat.ask("Find similar customers to John") -// → Uses smart templates to format your data meaningfully - -// WITH LLM - Same API, smarter responses -const smartChat = new BrainyChat(brainy, { - llm: 'Xenova/LaMini-Flan-T5-77M' // Just add this one parameter! -}) -await smartChat.ask("Find similar customers to John") -// → Uses LLM to generate natural, insightful responses - -// That's it. No complex configuration. No multiple interfaces. -// Just: new BrainyChat(brainy, { llm?: string }) -``` - -## 🎯 Why This API Design is Revolutionary - -### **1. Progressive Enhancement Done Right** -```javascript -// Start simple - works immediately -const chat = new BrainyChat(brainy) -await chat.ask("Which customers are most similar to John?") -// → Returns formatted results using smart templates - -// Enhance when needed - same exact API! -const betterChat = new BrainyChat(brainy, { llm: 'gpt-4o-mini' }) -await betterChat.ask("Which customers are most similar to John?") -// → Returns natural language insights with deeper analysis -``` - -### **2. Zero Learning Curve** -```javascript -// The ENTIRE API in 3 lines: -const chat = new BrainyChat(brainy, { llm?: string }) // Constructor -await chat.ask(question: string) // Ask questions -await chat.chat() // Interactive mode - -// That's it. Nothing else to learn. -``` - -### **3. Works Everywhere, Scales Anywhere** -```javascript -// Development - No LLM needed -const devChat = new BrainyChat(brainy) - -// Staging - Small local LLM -const stagingChat = new BrainyChat(brainy, { - llm: 'Xenova/LaMini-Flan-T5-77M' // 77MB model -}) - -// Production - Premium LLM -const prodChat = new BrainyChat(brainy, { - llm: 'claude-3-5-sonnet' // Or any model you want -}) - -// ALL THREE USE THE EXACT SAME CODE! -``` - -## 🏗️ How It Works Under the Hood - -### **Without LLM (Default) - Smart Templates** -```javascript -const chat = new BrainyChat(brainy) // No config needed! - -// When you ask a question: -await chat.ask("What are the main product categories?") - -// Brainy Chat: -// 1. Searches your data using embeddings -// 2. Analyzes the question type (list, comparison, count, etc.) -// 3. Formats results with intelligent templates -// 4. Returns: "Found 5 main categories: Electronics, Books, Clothing, Home, Sports" -``` - -### **With LLM - Natural Language Generation** -```javascript -const chat = new BrainyChat(brainy, { llm: 'Xenova/LaMini-Flan-T5-77M' }) - -// Same question: -await chat.ask("What are the main product categories?") - -// Brainy Chat: -// 1. Searches your data (same as before) -// 2. Passes context to the LLM -// 3. LLM generates natural response -// 4. Returns: "Your store features 5 primary product categories. Electronics -// leads with 45% of inventory, followed by Books at 23%. Clothing, Home -// goods, and Sports equipment round out your offerings, with seasonal -// variations in the Sports category showing 3x growth in summer months." -``` - -### **The Magic: Same Code, Different Intelligence Levels** -```javascript -// Your code never changes: -async function analyzeData(brainy, useLLM = false) { - const chat = new BrainyChat(brainy, - useLLM ? { llm: 'gpt-4o-mini' } : {} - ) - return await chat.ask("Analyze customer satisfaction trends") -} - -// Works in development (no LLM) -// Works in production (with LLM) -// Same function, progressive enhancement! -``` - -## 💡 Intelligent Features - -### **Contextual Understanding** -```javascript -// Maintains conversation context -await chat.ask("What are our top products?") -// → "Top 3 products by revenue: ProductA ($2.3M), ProductB ($1.8M)..." - -await chat.ask("Tell me more about the first one") // Understands context! -// → "ProductA is our flagship offering, launched in 2023..." -``` - -### **Multi-Step Reasoning** -```javascript -// Complex queries that require multiple lookups -await chat.ask("Compare our Q3 performance to last year and identify improvements") -// → Searches Q3 data → Finds last year's Q3 → Compares → Identifies patterns -``` - -### **Source Attribution** -```javascript -const response = await chat.ask("What's our refund policy?", { - includeSources: true -}) -// Returns: { -// answer: "Our refund policy allows 30-day returns...", -// sources: ["noun:policy-doc-001", "noun:faq-refunds", "verb:updated-by-legal"] -// } -``` - -## 🚀 Complete API Reference (Yes, This Is Everything!) - -```javascript -import { BrainyData, BrainyChat } from '@soulcraft/brainy' - -// Setup -const brainy = new BrainyData() -await brainy.init() - -// Create chat - THE ONLY CONSTRUCTOR -const chat = new BrainyChat( - brainy, // Required: Your Brainy instance - { // Optional: Configuration - llm?: string, // Optional: LLM model name - sources?: bool // Optional: Include source references (default: false) - } -) - -// Ask questions - THE ONLY METHOD YOU NEED -const answer = await chat.ask("Your question here") - -// Interactive mode - BONUS METHOD -await chat.chat() // Starts interactive REPL - -// That's it. That's the entire API. -// No configuration hell. No complex setup. -// Just: new BrainyChat(brainy, { llm?: string }) -``` - -## 🎉 Examples: From Zero to AI in Seconds - -```javascript -// Example 1: Customer Support Bot (No LLM) -const supportBot = new BrainyChat(brainy) -await supportBot.ask("How do I reset my password?") -// Returns: "Based on 'password-reset-guide': Click Settings > Security > Reset" - -// Example 2: Smart Analytics (With LLM) -const analyst = new BrainyChat(brainy, { llm: 'gpt-4o-mini' }) -await analyst.ask("What patterns exist in user churn?") -// Returns: "Analysis reveals three key churn indicators: users who haven't -// logged in for 30+ days show 73% churn probability..." - -// Example 3: Development vs Production -const chat = new BrainyChat(brainy, { - llm: process.env.LLM_MODEL // undefined in dev, defined in prod -}) -// Works perfectly in both environments! -``` - -## 📝 Use Cases - -### **Customer Support Bot** -```javascript -const supportBot = new BrainyChat(brainy) -await supportBot.ask("How do I reset my password?") -// Searches docs, tickets, and FAQs to provide accurate answer -``` - -### **Data Analyst Assistant** -```javascript -const analyst = new BrainyChat(brainy, { llm: 'gpt-4o-mini' }) -await analyst.ask("What patterns do you see in user churn?") -// Analyzes vector similarities and relationships to identify patterns -``` - -### **Code Documentation Helper** -```javascript -const docHelper = new BrainyChat(brainy) -await docHelper.ask("How does the authentication system work?") -// Searches all auth-related code and docs to explain -``` - -## 🛠️ Implementation Strategy - -### **Phase 1: Template-Based Q&A** (v0.56) -- Zero additional dependencies -- Uses existing embedding model -- Smart template responses -- ~150 lines of code total - -### **Phase 2: Optional LLM Enhancement** (v0.57) -- Lazy-loaded Hugging Face models -- Natural language generation -- Same simple API -- Progressive enhancement - -### **Phase 3: Advanced Features** (v0.58) -- Multi-turn conversations -- Code generation from data -- Analytical reports -- Custom fine-tuning - -## 🎯 Why This Is Revolutionary - -1. **Simplest API Ever** - One constructor, one method, one optional parameter -2. **Progressive Enhancement** - Works without LLM, better with it -3. **Your Data, Not Generic** - Responses based on YOUR specific knowledge -4. **Zero to Smart** - Literally one line to add AI chat to any Brainy database -5. **Tiny Footprint** - Just ~150 lines of code, reuses existing embeddings - -## 🔜 Coming in v0.56 - -This feature is under active development. The initial release will include: -- Simple BrainyChat class with just `ask()` method -- Template-based responses (no LLM required) -- Optional LLM parameter for enhanced responses -- Source attribution -- Interactive chat mode - -**The future of data interaction: One line of code, infinite possibilities!** 🚀 \ No newline at end of file diff --git a/docs/quantum-vault-preview/notion-connector.md b/docs/quantum-vault-preview/notion-connector.md deleted file mode 100644 index 3fe7c3b6..00000000 --- a/docs/quantum-vault-preview/notion-connector.md +++ /dev/null @@ -1,263 +0,0 @@ -# 🔧 Notion Connector - Quantum Vault Implementation - -**⚠️ This is a preview of what exists in `brainy-quantum-vault` (private repository)** - -*Full implementation available to premium license holders only* - -## 🧠 **Implementation Overview** - -The Notion connector in the Quantum Vault provides seamless sync between Notion workspaces and your Brainy vector + graph database. - -### **File Structure (in brainy-quantum-vault):** -``` -brainy-quantum-vault/src/connectors/notion/ -├── index.ts # Main NotionConnector class -├── auth/ -│ ├── oauth.ts # OAuth 2.0 flow implementation -│ └── tokens.ts # Token management and refresh -├── sync/ -│ ├── pages.ts # Page content extraction -│ ├── databases.ts # Database schema and records -│ └── blocks.ts # Block-level content parsing -├── mapping/ -│ ├── schema.ts # Notion → Brainy schema mapping -│ ├── entities.ts # Entity extraction (people, dates, etc.) -│ └── relationships.ts # Relationship detection -├── utils/ -│ ├── rate-limiter.ts # Notion API rate limiting -│ ├── retry.ts # Exponential backoff retry logic -│ └── validation.ts # Data validation and sanitization -├── types/ -│ ├── notion.ts # Notion API type definitions -│ └── brainy.ts # Brainy-specific types -└── tests/ - ├── integration.test.ts - └── unit.test.ts -``` - -## 🚀 **Key Features** - -### **🔄 Intelligent Sync** -```typescript -// Real implementation (Quantum Vault only) -export class NotionConnector implements IConnector { - readonly id = 'notion' - readonly name = 'Notion Workspace Sync' - readonly version = '1.2.3' - readonly supportedTypes = ['pages', 'databases', 'blocks', 'users'] - - private client: Client - private brainy: BrainyData - private rateLimiter: RateLimiter - private licenseValidator: LicenseValidator - - async initialize(config: ConnectorConfig): Promise { - // 1. Validate premium license with quantum vault servers - await this.licenseValidator.validate(config.licenseKey) - - // 2. Initialize Notion API client with credentials - this.client = new Client({ - auth: config.credentials.accessToken, - // Custom retry logic for production reliability - retry: this.createRetryConfig() - }) - - // 3. Set up intelligent rate limiting (3 requests/second) - this.rateLimiter = new RateLimiter({ - requestsPerSecond: 3, - burstAllowance: 10 - }) - - // 4. Test connection and validate permissions - await this.testConnection() - } - - async startSync(): Promise { - const startTime = Date.now() - let synced = 0, failed = 0, skipped = 0 - const errors: any[] = [] - - try { - // Phase 1: Sync workspace users and permissions - const users = await this.syncUsers() - synced += users.synced - failed += users.failed - - // Phase 2: Sync database schemas - const databases = await this.syncDatabases() - synced += databases.synced - failed += databases.failed - - // Phase 3: Sync pages with intelligent chunking - const pages = await this.syncPages() - synced += pages.synced - failed += pages.failed - - // Phase 4: Extract relationships using AI - await this.extractRelationships() - - return { - synced, - failed, - skipped, - duration: Date.now() - startTime, - timestamp: new Date().toISOString(), - errors, - metadata: { - lastSyncId: this.generateSyncId(), - hasMore: false - } - } - - } catch (error) { - // Advanced error handling and retry logic - throw new ConnectorError('Notion sync failed', error) - } - } - - private async syncPages(): Promise { - const pages = await this.client.search({ - filter: { object: 'page' }, - sort: { timestamp: 'last_edited_time', direction: 'descending' } - }) - - let synced = 0 - for (const page of pages.results) { - await this.rateLimiter.wait() // Respect rate limits - - try { - // Extract page content with block-level parsing - const content = await this.extractPageContent(page) - - // AI-powered entity extraction - const entities = await this.extractEntities(content) - - // Store in Brainy with rich metadata - const brainyId = await this.brainy.add(content.text, { - source: 'notion', - type: 'page', - notionId: page.id, - title: content.title, - url: content.url, - lastModified: page.last_edited_time, - entities, - // Rich metadata for filtering - workspace: content.workspace, - database: content.parent_database, - tags: content.tags - }) - - // Create relationships - await this.createRelationships(brainyId, entities, content) - - synced++ - } catch (error) { - // Log error but continue processing - console.error(`Failed to sync page ${page.id}:`, error) - // Would implement sophisticated error tracking - } - } - - return { synced, failed: 0, skipped: 0, duration: 0, timestamp: '' } - } - - private async extractEntities(content: any): Promise { - // AI-powered entity extraction using Brainy's neural capabilities - // This would use the Neural Import system we built! - - // Extract @mentions as person entities - const mentions = content.text.match(/@([^\\s]+)/g) || [] - const personEntities = mentions.map(mention => ({ - type: 'person', - value: mention.substring(1), - source: 'mention' - })) - - // Extract dates, URLs, etc. - const dateMatches = content.text.match(/\\d{4}-\\d{2}-\\d{2}/g) || [] - const dateEntities = dateMatches.map(date => ({ - type: 'date', - value: date, - source: 'text_extraction' - })) - - return [...personEntities, ...dateEntities] - } - - private async createRelationships(brainyId: string, entities: any[], content: any): Promise { - // Create "author" relationships - if (content.created_by) { - const authorId = await this.findOrCreateUser(content.created_by) - await this.brainy.relate(authorId, brainyId, 'created') - } - - // Create "mentions" relationships - for (const entity of entities) { - if (entity.type === 'person') { - const personId = await this.findOrCreatePerson(entity.value) - await this.brainy.relate(brainyId, personId, 'mentions') - } - } - - // Database relationships - if (content.parent_database) { - const dbId = await this.findOrCreateDatabase(content.parent_database) - await this.brainy.relate(dbId, brainyId, 'contains') - } - } - - // ... many more sophisticated methods for handling: - // - OAuth token refresh - // - Incremental sync with change detection - // - Error recovery and retry logic - // - Database schema mapping - // - Block-level content extraction - // - Webhook integration for real-time updates - // - Enterprise permission handling -} -``` - -## 🔒 **Premium Features** - -### **🧠 AI-Powered Intelligence** -- **Entity Recognition**: Automatically detects people, companies, dates, locations -- **Relationship Mapping**: Understands mentions, references, hierarchies -- **Content Understanding**: Semantic analysis of page content - -### **⚡ Production Reliability** -- **Rate Limit Management**: Intelligent request throttling -- **Error Recovery**: Exponential backoff with retry logic -- **Incremental Sync**: Only sync changed content -- **Webhook Integration**: Real-time updates from Notion - -### **🔐 Enterprise Security** -- **OAuth 2.0 Flow**: Secure authentication -- **Token Management**: Automatic refresh handling -- **Permission Mapping**: Respects Notion workspace permissions -- **Audit Logging**: Complete operation tracking - -## 📊 **Usage Statistics** - -Premium license holders report: -- **⚡ 10x faster** than building custom integrations -- **🎯 95% sync accuracy** with AI-powered entity detection -- **🔄 Real-time updates** with webhook integration -- **📈 Enterprise scale** handling 100K+ pages - -## 🎯 **Get Quantum Vault Access** - -Ready to unlock the full Notion connector? - -```bash -# Start your free trial -cortex license trial notion-connector - -# After activation, install from private registry -npm install @soulcraft/brainy-quantum-vault -``` - -**[Start Free Trial →](https://soulcraft-research.com/brainy/trial)** - ---- - -*The complete implementation awaits in the Quantum Vault...* 🔒⚛️✨ \ No newline at end of file diff --git a/models/.brainy-models-bundled b/models/.brainy-models-bundled index 49a80836..1c03210c 100644 --- a/models/.brainy-models-bundled +++ b/models/.brainy-models-bundled @@ -1,5 +1,5 @@ { "model": "Xenova/all-MiniLM-L6-v2", - "bundledAt": "2025-08-07T03:21:48.705Z", + "bundledAt": "2025-08-08T19:50:05.332Z", "version": "1.0.0" } \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 0c024590..03cd28d5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,6 @@ "@huggingface/transformers": "^3.1.0", "@smithy/node-http-handler": "^4.1.1", "boxen": "^7.1.1", - "buffer": "^6.0.3", "chalk": "^5.3.0", "cli-table3": "^0.6.3", "commander": "^11.1.0", @@ -26,6 +25,10 @@ "brainy": "bin/brainy.js" }, "devDependencies": { + "@rollup/plugin-commonjs": "^28.0.6", + "@rollup/plugin-node-resolve": "^16.0.1", + "@rollup/plugin-replace": "^6.0.2", + "@rollup/plugin-terser": "^0.4.4", "@types/express": "^5.0.3", "@types/jsdom": "^21.1.7", "@types/node": "^20.11.30", @@ -33,17 +36,22 @@ "@types/uuid": "^10.0.0", "@typescript-eslint/eslint-plugin": "^8.0.0", "@typescript-eslint/parser": "^8.0.0", + "@vitejs/plugin-basic-ssl": "^2.1.0", "@vitest/coverage-v8": "^3.2.4", "@vitest/ui": "^3.2.4", + "buffer": "^6.0.3", + "crypto-browserify": "^3.12.1", "eslint": "^9.0.0", "express": "^5.1.0", "happy-dom": "^18.0.1", "jsdom": "^26.1.0", "node-fetch": "^3.3.2", + "process": "^0.11.10", "puppeteer": "^22.15.0", "standard-version": "^9.5.0", "tslib": "^2.6.2", "typescript": "^5.4.5", + "vite": "^7.1.1", "vitest": "^3.2.4" }, "engines": { @@ -2454,8 +2462,6 @@ "integrity": "sha512-0pPkgz9dY+bijgistcTTJ5mR+ocqRXLuhXHYdzoMmmoJ2C9S46RCm2GMUbatPEUK9Yjy26IrAy8D/M00lLkv+Q==", "dev": true, "license": "MIT", - "optional": true, - "peer": true, "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25" @@ -2666,6 +2672,126 @@ "node": ">=12" } }, + "node_modules/@rollup/plugin-commonjs": { + "version": "28.0.6", + "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-28.0.6.tgz", + "integrity": "sha512-XSQB1K7FUU5QP+3lOQmVCE3I0FcbbNvmNT4VJSj93iUjayaARrTQeoRdiYQoftAJBLrR9t2agwAd3ekaTgHNlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "commondir": "^1.0.1", + "estree-walker": "^2.0.2", + "fdir": "^6.2.0", + "is-reference": "1.2.1", + "magic-string": "^0.30.3", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=16.0.0 || 14 >= 14.17" + }, + "peerDependencies": { + "rollup": "^2.68.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-node-resolve": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-16.0.1.tgz", + "integrity": "sha512-tk5YCxJWIG81umIvNkSod2qK5KyQW19qcBF/B78n1bjtOON6gzKoVeSzAE8yHCZEDmqkHKkxplExA8KzdJLJpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "@types/resolve": "1.20.2", + "deepmerge": "^4.2.2", + "is-module": "^1.0.0", + "resolve": "^1.22.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.78.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-replace": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-6.0.2.tgz", + "integrity": "sha512-7QaYCf8bqF04dOy7w/eHmJeNExxTYwvKAmlSAH/EaWWUzbT0h5sbF6bktFoX/0F/0qwng5/dWFMyf3gzaM8DsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "magic-string": "^0.30.3" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-terser": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@rollup/plugin-terser/-/plugin-terser-0.4.4.tgz", + "integrity": "sha512-XHeJC5Bgvs8LfukDwWZp7yeqin6ns8RTl2B9avbejt6tZqsqvVoWI7ZTQrcNsfKEDWBTnTxM8nMDkO2IFFbd0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "serialize-javascript": "^6.0.1", + "smob": "^1.0.0", + "terser": "^5.17.4" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.2.0.tgz", + "integrity": "sha512-qWJ2ZTbmumwiLFomfzTyt5Kng4hwPi9rwCYN4SHb6eaRU1KNO4ccxINHr/VhH4GgPlt1XfSTLX2LBTme8ne4Zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.44.2", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.44.2.tgz", @@ -3823,6 +3949,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/resolve": { + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", + "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/send": { "version": "0.17.5", "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.5.tgz", @@ -4113,6 +4246,19 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/@vitejs/plugin-basic-ssl": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-basic-ssl/-/plugin-basic-ssl-2.1.0.tgz", + "integrity": "sha512-dOxxrhgyDIEUADhb/8OlV9JIqYLgos03YorAueTIeOUskLJSEsfwCByjbu98ctXitUN3znXKp0bYD/WHSudCeA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "peerDependencies": { + "vite": "^6.0.0 || ^7.0.0" + } + }, "node_modules/@vitest/coverage-v8": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.2.4.tgz", @@ -4439,6 +4585,25 @@ "node": ">=0.10.0" } }, + "node_modules/asn1.js": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", + "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/asn1.js/node_modules/bn.js": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz", + "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", + "dev": true, + "license": "MIT" + }, "node_modules/assertion-error": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", @@ -4491,6 +4656,22 @@ "dev": true, "license": "MIT" }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/b4a": { "version": "1.6.7", "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.7.tgz", @@ -4586,6 +4767,7 @@ "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, "funding": [ { "type": "github", @@ -4612,6 +4794,13 @@ "node": ">=10.0.0" } }, + "node_modules/bn.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.2.tgz", + "integrity": "sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw==", + "dev": true, + "license": "MIT" + }, "node_modules/body-parser": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", @@ -4782,10 +4971,135 @@ "node": ">=8" } }, + "node_modules/brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==", + "dev": true, + "license": "MIT" + }, + "node_modules/browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "node_modules/browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/browserify-rsa": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.1.tgz", + "integrity": "sha512-YBjSAiTqM04ZVei6sXighu679a3SqWORA3qZTEqZImnlkDIFtKc6pNutpjyZ8RJTjQtuYfeetkxM11GwoYXMIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bn.js": "^5.2.1", + "randombytes": "^2.1.0", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/browserify-sign": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.3.tgz", + "integrity": "sha512-JWCZW6SKhfhjJxO8Tyiiy+XYB7cqd2S5/+WeYHsKdNKFlCBhKbblba1A/HN/90YwtxKc8tCErjffZl++UNmGiw==", + "dev": true, + "license": "ISC", + "dependencies": { + "bn.js": "^5.2.1", + "browserify-rsa": "^4.1.0", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "elliptic": "^6.5.5", + "hash-base": "~3.0", + "inherits": "^2.0.4", + "parse-asn1": "^5.1.7", + "readable-stream": "^2.3.8", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/browserify-sign/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/browserify-sign/node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/browserify-sign/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/browserify-sign/node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, "node_modules/buffer": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, "funding": [ { "type": "github", @@ -4823,6 +5137,13 @@ "dev": true, "license": "MIT" }, + "node_modules/buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==", + "dev": true, + "license": "MIT" + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -4843,6 +5164,25 @@ "node": ">=8" } }, + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/call-bind-apply-helpers": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", @@ -4975,6 +5315,20 @@ "devtools-protocol": "*" } }, + "node_modules/cipher-base": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.6.tgz", + "integrity": "sha512-3Ek9H3X6pj5TgenXYtNWdaBon1tgYCaebd+XPg0keyjEbEfkD4KkmAxkQ/i1vYvxdcT5nscLBfq9VJRmCBcFSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.4", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/cli-boxes": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz", @@ -5091,6 +5445,13 @@ "node": ">=16" } }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true, + "license": "MIT" + }, "node_modules/compare-func": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", @@ -5481,6 +5842,53 @@ } } }, + "node_modules/create-ecdh": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", + "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "bn.js": "^4.1.0", + "elliptic": "^6.5.3" + } + }, + "node_modules/create-ecdh/node_modules/bn.js": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz", + "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "node_modules/create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -5496,6 +5904,33 @@ "node": ">= 8" } }, + "node_modules/crypto-browserify": { + "version": "3.12.1", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.1.tgz", + "integrity": "sha512-r4ESw/IlusD17lgQi1O20Fa3qNnsckR126TdUuBgAu7GBYSIPvdNyONd3Zrxh0xCwA4+6w/TDArBPsMvhur+KQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserify-cipher": "^1.0.1", + "browserify-sign": "^4.2.3", + "create-ecdh": "^4.0.4", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "diffie-hellman": "^5.0.3", + "hash-base": "~3.0.4", + "inherits": "^2.0.4", + "pbkdf2": "^3.1.2", + "public-encrypt": "^4.0.3", + "randombytes": "^2.1.0", + "randomfill": "^1.0.4" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/cssstyle": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.6.0.tgz", @@ -5680,6 +6115,16 @@ "dev": true, "license": "MIT" }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/define-data-property": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", @@ -5739,6 +6184,17 @@ "node": ">= 0.8" } }, + "node_modules/des.js": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.1.0.tgz", + "integrity": "sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, "node_modules/detect-indent": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", @@ -5781,6 +6237,25 @@ "dev": true, "license": "BSD-3-Clause" }, + "node_modules/diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + } + }, + "node_modules/diffie-hellman/node_modules/bn.js": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz", + "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", + "dev": true, + "license": "MIT" + }, "node_modules/dot-prop": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", @@ -5938,6 +6413,29 @@ "dev": true, "license": "MIT" }, + "node_modules/elliptic": { + "version": "6.6.1", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.6.1.tgz", + "integrity": "sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/elliptic/node_modules/bn.js": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz", + "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", + "dev": true, + "license": "MIT" + }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -6370,6 +6868,13 @@ "node": ">=4.0" } }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -6390,6 +6895,17 @@ "node": ">= 0.6" } }, + "node_modules/evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, "node_modules/expect-type": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.2.tgz", @@ -6743,6 +7259,22 @@ "dev": true, "license": "ISC" }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/foreground-child": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", @@ -7217,6 +7749,47 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hash-base": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.5.tgz", + "integrity": "sha512-vXm0l45VbcHEVlTCzs8M+s0VeYsB2lnlAaThoLKGXr3bE/VWDOelNUnycUPEhKEaXARL2TEFjBOyUiM6+55KBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.4", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", @@ -7230,6 +7803,18 @@ "node": ">= 0.4" } }, + "node_modules/hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, "node_modules/hosted-git-info": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", @@ -7348,6 +7933,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, "funding": [ { "type": "github", @@ -7463,6 +8049,19 @@ "dev": true, "license": "MIT" }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-core-module": { "version": "2.16.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", @@ -7523,6 +8122,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", + "dev": true, + "license": "MIT" + }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -7567,6 +8173,16 @@ "dev": true, "license": "MIT" }, + "node_modules/is-reference": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", + "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, "node_modules/is-text-path": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-1.0.1.tgz", @@ -7580,6 +8196,22 @@ "node": ">=0.10.0" } }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-unicode-supported": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", @@ -8110,6 +8742,18 @@ "node": ">= 0.4" } }, + "node_modules/md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, "node_modules/media-typer": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", @@ -8349,6 +8993,27 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + }, + "bin": { + "miller-rabin": "bin/miller-rabin" + } + }, + "node_modules/miller-rabin/node_modules/bn.js": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz", + "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", + "dev": true, + "license": "MIT" + }, "node_modules/mimic-function": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", @@ -8371,6 +9036,20 @@ "node": ">=4" } }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true, + "license": "ISC" + }, + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==", + "dev": true, + "license": "MIT" + }, "node_modules/minimatch": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", @@ -8904,6 +9583,24 @@ "node": ">=6" } }, + "node_modules/parse-asn1": { + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.7.tgz", + "integrity": "sha512-CTM5kuWR3sx9IFamcl5ErfPl6ea/N8IYwiJ+vpeB2g+1iknv7zBl5uPwbMbRVznRVbrNY6lGuDoE5b30grmbqg==", + "dev": true, + "license": "ISC", + "dependencies": { + "asn1.js": "^4.10.1", + "browserify-aes": "^1.2.0", + "evp_bytestokey": "^1.0.3", + "hash-base": "~3.0", + "pbkdf2": "^3.1.2", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", @@ -9024,6 +9721,58 @@ "node": ">= 14.16" } }, + "node_modules/pbkdf2": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.3.tgz", + "integrity": "sha512-wfRLBZ0feWRhCIkoMB6ete7czJcnNnqRpcoWQBLqatqXXmelSRqfdDK4F3u9T2s2cXas/hQJcryI/4lAL+XTlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "create-hash": "~1.1.3", + "create-hmac": "^1.1.7", + "ripemd160": "=2.0.1", + "safe-buffer": "^5.2.1", + "sha.js": "^2.4.11", + "to-buffer": "^1.2.0" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/pbkdf2/node_modules/create-hash": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.1.3.tgz", + "integrity": "sha512-snRpch/kwQhcdlnZKYanNF1m0RDlrCdSKQaH87w1FCFPVPNCQ/Il9QJKAX2jVBZddRdaHBMC+zXa9Gw9tmkNUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "sha.js": "^2.4.0" + } + }, + "node_modules/pbkdf2/node_modules/hash-base": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-2.0.2.tgz", + "integrity": "sha512-0TROgQ1/SxE6KmxWSvXHvRj90/Xo1JvZShofnYF+f6ZsGtR4eES7WfrQzPalmyagfKZCXpVnitiRebZulWsbiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.1" + } + }, + "node_modules/pbkdf2/node_modules/ripemd160": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.1.tgz", + "integrity": "sha512-J7f4wutN8mdbV08MJnXibYpCOPHR+yzy+iQ/AsjMv2j8cLavQ8VGagDFUwwTAdF8FmRKVeNpbTTEwNHCW1g94w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hash-base": "^2.0.0", + "inherits": "^2.0.1" + } + }, "node_modules/pend": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", @@ -9039,9 +9788,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", "engines": { @@ -9067,6 +9816,16 @@ "integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==", "license": "MIT" }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/postcss": { "version": "8.5.6", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", @@ -9106,6 +9865,16 @@ "node": ">= 0.8.0" } }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -9207,6 +9976,28 @@ "dev": true, "license": "MIT" }, + "node_modules/public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/public-encrypt/node_modules/bn.js": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz", + "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", + "dev": true, + "license": "MIT" + }, "node_modules/pump": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", @@ -9324,6 +10115,27 @@ "node": ">=8" } }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -9602,6 +10414,17 @@ "node": ">=0.10.0" } }, + "node_modules/ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, "node_modules/roarr": { "version": "2.15.4", "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", @@ -9845,6 +10668,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "randombytes": "^2.1.0" + } + }, "node_modules/serve-static": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", @@ -9861,6 +10694,24 @@ "node": ">= 18" } }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", @@ -9868,6 +10719,27 @@ "dev": true, "license": "ISC" }, + "node_modules/sha.js": { + "version": "2.4.12", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.12.tgz", + "integrity": "sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==", + "dev": true, + "license": "(MIT AND BSD-3-Clause)", + "dependencies": { + "inherits": "^2.0.4", + "safe-buffer": "^5.2.1", + "to-buffer": "^1.2.0" + }, + "bin": { + "sha.js": "bin.js" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/sharp": { "version": "0.34.3", "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.3.tgz", @@ -10075,6 +10947,13 @@ "npm": ">= 3.0.0" } }, + "node_modules/smob": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/smob/-/smob-1.5.0.tgz", + "integrity": "sha512-g6T+p7QO8npa+/hNx9ohv1E5pVCmWrVCUzUXJyLdMmftX6ER0oiWY/w9knEonLpnOp6b6FenKnMfR8gqwWdwig==", + "dev": true, + "license": "MIT" + }, "node_modules/socks": { "version": "2.8.6", "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.6.tgz", @@ -10131,8 +11010,6 @@ "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, "license": "MIT", - "optional": true, - "peer": true, "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" @@ -10588,8 +11465,6 @@ "integrity": "sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg==", "dev": true, "license": "BSD-2-Clause", - "optional": true, - "peer": true, "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.14.0", @@ -10608,9 +11483,7 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true, - "license": "MIT", - "optional": true, - "peer": true + "license": "MIT" }, "node_modules/test-exclude": { "version": "7.0.1", @@ -10766,6 +11639,28 @@ "dev": true, "license": "MIT" }, + "node_modules/to-buffer": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.2.1.tgz", + "integrity": "sha512-tB82LpAIWjhLYbqjx3X4zEeHN6M8CiuOEy2JY8SEQVdYRe3CCHOFaqrBW1doLDrfpWhplcW7BL+bO3/6S3pcDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "isarray": "^2.0.5", + "safe-buffer": "^5.2.1", + "typed-array-buffer": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/to-buffer/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -10904,6 +11799,21 @@ "node": ">= 0.6" } }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", @@ -11050,17 +11960,17 @@ } }, "node_modules/vite": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.0.4.tgz", - "integrity": "sha512-SkaSguuS7nnmV7mfJ8l81JGBFV7Gvzp8IzgE8A8t23+AxuNX61Q5H1Tpz5efduSN7NHC8nQXD3sKQKZAu5mNEA==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.1.tgz", + "integrity": "sha512-yJ+Mp7OyV+4S+afWo+QyoL9jFWD11QFH0i5i7JypnfTcA1rmgxCbiA8WwAICDEtZ1Z1hzrVhN8R8rGTqkTY8ZQ==", "dev": true, "license": "MIT", "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.6", - "picomatch": "^4.0.2", + "picomatch": "^4.0.3", "postcss": "^8.5.6", - "rollup": "^4.40.0", + "rollup": "^4.43.0", "tinyglobby": "^0.2.14" }, "bin": { @@ -11282,6 +12192,28 @@ "node": ">= 8" } }, + "node_modules/which-typed-array": { + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", + "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/why-is-node-running": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", diff --git a/package.json b/package.json index adc5b27e..be01a9e4 100644 --- a/package.json +++ b/package.json @@ -15,8 +15,17 @@ "./src/setup.ts", "./src/utils/textEncoding.ts" ], + "browser": { + "fs": false, + "fs/promises": false, + "path": "path-browserify", + "crypto": "crypto-browserify", + "./dist/cortex/cortex.js": "./dist/browserFramework.js" + }, "exports": { ".": { + "browser": "./dist/browserFramework.js", + "node": "./dist/index.js", "import": "./dist/index.js", "types": "./dist/index.d.ts" }, @@ -43,6 +52,34 @@ "./dist/setup.js": { "import": "./dist/setup.js", "types": "./dist/setup.d.ts" + }, + "./browserFramework": { + "import": "./dist/browserFramework.js", + "types": "./dist/browserFramework.d.ts" + }, + "./universal": { + "import": "./dist/universal/index.js", + "types": "./dist/universal/index.d.ts" + }, + "./universal/uuid": { + "import": "./dist/universal/uuid.js", + "types": "./dist/universal/uuid.d.ts" + }, + "./universal/crypto": { + "import": "./dist/universal/crypto.js", + "types": "./dist/universal/crypto.d.ts" + }, + "./universal/fs": { + "import": "./dist/universal/fs.js", + "types": "./dist/universal/fs.d.ts" + }, + "./universal/path": { + "import": "./dist/universal/path.js", + "types": "./dist/universal/path.d.ts" + }, + "./universal/events": { + "import": "./dist/universal/events.js", + "types": "./dist/universal/events.d.ts" } }, "engines": { @@ -51,6 +88,7 @@ "scripts": { "prebuild": "echo 'Prebuild step - no version generation needed'", "build": "tsc", + "build:browser": "npm run build && vite build --config vite.browser.config.ts", "build:framework": "tsc", "start": "node dist/framework.js", "demo": "npm run build && cd demo/brainy-angular-demo && npm run build && npm run serve", @@ -137,6 +175,10 @@ "OFFLINE_MODELS.md" ], "devDependencies": { + "@rollup/plugin-commonjs": "^28.0.6", + "@rollup/plugin-node-resolve": "^16.0.1", + "@rollup/plugin-replace": "^6.0.2", + "@rollup/plugin-terser": "^0.4.4", "@types/express": "^5.0.3", "@types/jsdom": "^21.1.7", "@types/node": "^20.11.30", @@ -144,17 +186,22 @@ "@types/uuid": "^10.0.0", "@typescript-eslint/eslint-plugin": "^8.0.0", "@typescript-eslint/parser": "^8.0.0", + "@vitejs/plugin-basic-ssl": "^2.1.0", "@vitest/coverage-v8": "^3.2.4", "@vitest/ui": "^3.2.4", + "buffer": "^6.0.3", + "crypto-browserify": "^3.12.1", "eslint": "^9.0.0", "express": "^5.1.0", "happy-dom": "^18.0.1", "jsdom": "^26.1.0", "node-fetch": "^3.3.2", + "process": "^0.11.10", "puppeteer": "^22.15.0", "standard-version": "^9.5.0", "tslib": "^2.6.2", "typescript": "^5.4.5", + "vite": "^7.1.1", "vitest": "^3.2.4" }, "dependencies": { @@ -162,7 +209,6 @@ "@huggingface/transformers": "^3.1.0", "@smithy/node-http-handler": "^4.1.1", "boxen": "^7.1.1", - "buffer": "^6.0.3", "chalk": "^5.3.0", "cli-table3": "^0.6.3", "commander": "^11.1.0", diff --git a/src/augmentations/conduitAugmentations.ts b/src/augmentations/conduitAugmentations.ts index b466d67e..4541d6a4 100644 --- a/src/augmentations/conduitAugmentations.ts +++ b/src/augmentations/conduitAugmentations.ts @@ -5,7 +5,7 @@ import { AugmentationResponse, WebSocketConnection } from '../types/augmentations.js' -import { v4 as uuidv4 } from 'uuid' +import { v4 as uuidv4 } from '../universal/uuid.js' /** * Base class for conduit augmentations that provide data synchronization between Brainy instances diff --git a/src/augmentations/cortexSense.ts b/src/augmentations/cortexSense.ts index bd9a8fa4..4e56b2e7 100644 --- a/src/augmentations/cortexSense.ts +++ b/src/augmentations/cortexSense.ts @@ -8,8 +8,8 @@ import { ISenseAugmentation, AugmentationResponse } from '../types/augmentations.js' import { BrainyData } from '../brainyData.js' import { NounType, VerbType } from '../types/graphTypes.js' -import * as fs from 'fs/promises' -import * as path from 'path' +import * as fs from '../universal/fs.js' +import * as path from '../universal/path.js' // Cortex Analysis Types export interface CortexAnalysisResult { diff --git a/src/augmentations/serverSearchAugmentations.ts b/src/augmentations/serverSearchAugmentations.ts index 40b82d94..9dd81893 100644 --- a/src/augmentations/serverSearchAugmentations.ts +++ b/src/augmentations/serverSearchAugmentations.ts @@ -14,7 +14,7 @@ import { WebSocketConnection } from '../types/augmentations.js' import { WebSocketConduitAugmentation } from './conduitAugmentations.js' -import { v4 as uuidv4 } from 'uuid' +import { v4 as uuidv4 } from '../universal/uuid.js' import { BrainyDataInterface } from '../types/brainyDataInterface.js' /** diff --git a/src/brainyData.ts b/src/brainyData.ts index 30c735df..b549e2a2 100644 --- a/src/brainyData.ts +++ b/src/brainyData.ts @@ -3,7 +3,7 @@ * Main class that provides the vector database functionality */ -import { v4 as uuidv4 } from 'uuid' +import { v4 as uuidv4 } from './universal/uuid.js' import { HNSWIndex } from './hnsw/hnswIndex.js' import { HNSWIndexOptimized, @@ -6071,7 +6071,7 @@ export class BrainyData implements BrainyDataInterface { // Configure HNSW with disk-based storage when a storage adapter is provided const hnswConfig = data.hnswIndex.config || {} if (this.storage) { - hnswConfig.useDiskBasedIndex = true + ;(hnswConfig as any).useDiskBasedIndex = true } this.index = new HNSWIndexOptimized( diff --git a/src/browserFramework.minimal.ts b/src/browserFramework.minimal.ts new file mode 100644 index 00000000..90123e96 --- /dev/null +++ b/src/browserFramework.minimal.ts @@ -0,0 +1,35 @@ +/** + * Minimal Browser Framework Entry Point for Brainy + * Core MIT open source functionality only - no enterprise features + * Optimized for browser usage with all dependencies bundled + */ + +import { BrainyData } from './brainyData.js' +import { VerbType, NounType } from './types/graphTypes.js' + +/** + * Create a BrainyData instance optimized for browser usage + * Auto-detects environment and selects optimal storage and settings + */ +export async function createBrowserBrainyData(config = {}) { + // BrainyData already has environment detection and will automatically: + // - Use OPFS storage in browsers with fallback to Memory + // - Use FileSystem storage in Node.js + // - Request persistent storage when appropriate + const browserConfig = { + storage: { + requestPersistentStorage: true // Request persistent storage for better performance + }, + ...config + } + + const brainyData = new BrainyData(browserConfig) + await brainyData.init() + return brainyData +} + +// Re-export core types and classes for browser use +export { VerbType, NounType, BrainyData } + +// Default export for easy importing +export default createBrowserBrainyData \ No newline at end of file diff --git a/src/cortex/backupRestore.ts b/src/cortex/backupRestore.ts index c29aeeb1..9bb249ba 100644 --- a/src/cortex/backupRestore.ts +++ b/src/cortex/backupRestore.ts @@ -6,8 +6,8 @@ */ import { BrainyData } from '../brainyData.js' -import * as fs from 'fs/promises' -import * as path from 'path' +import * as fs from '../universal/fs.js' +import * as path from '../universal/path.js' // @ts-ignore import chalk from 'chalk' // @ts-ignore diff --git a/src/cortex/cortex.ts b/src/cortex/cortex.ts index 8fd6129f..3920c293 100644 --- a/src/cortex/cortex.ts +++ b/src/cortex/cortex.ts @@ -8,7 +8,7 @@ import { BrainyData } from '../brainyData.js' import { BrainyChat } from '../chat/brainyChat.js' import { PerformanceMonitor } from './performanceMonitor.js' import { HealthCheck } from './healthCheck.js' -import { LicensingSystem } from './licensingSystem.js' +// Licensing system moved to quantum-vault import * as readline from 'readline' import * as fs from 'fs/promises' import * as path from 'path' @@ -81,7 +81,7 @@ export class Cortex { private chatInstance?: BrainyChat private performanceMonitor?: PerformanceMonitor private healthCheck?: HealthCheck - private licensingSystem?: LicensingSystem + // private licensingSystem?: LicensingSystem // Moved to quantum-vault private configPath: string private config: CortexConfig private encryptionKey?: Buffer @@ -1734,8 +1734,8 @@ export class Cortex { this.healthCheck = new HealthCheck(this.brainy) // Initialize licensing system - this.licensingSystem = new LicensingSystem() - await this.licensingSystem.initialize() + // Licensing system moved to quantum-vault for premium features + // Open source version has full functionality available } private async saveConfig(): Promise { diff --git a/src/cortex/licensingSystem.ts b/src/cortex/licensingSystem.ts deleted file mode 100644 index 651e98cb..00000000 --- a/src/cortex/licensingSystem.ts +++ /dev/null @@ -1,623 +0,0 @@ -/** - * Brainy Premium Licensing System - Atomic Age Revenue Engine - * - * 🧠 Manages premium augmentation licenses and subscriptions - * ⚛️ 1950s retro sci-fi themed licensing with atomic age aesthetics - * 🚀 Scalable license validation for premium features - */ - -// @ts-ignore -import chalk from 'chalk' -// @ts-ignore -import boxen from 'boxen' -// @ts-ignore -import ora from 'ora' -import * as fs from 'fs/promises' -import * as path from 'path' -import * as crypto from 'crypto' - -export interface License { - id: string - type: 'premium' | 'enterprise' | 'trial' - product: string // e.g., 'salesforce-connector', 'slack-connector' - tier: 'basic' | 'professional' | 'enterprise' - status: 'active' | 'expired' | 'suspended' | 'trial' - issuedTo: string // Customer identifier - issuedAt: string // ISO timestamp - expiresAt: string // ISO timestamp - features: string[] // Array of enabled features - limits: { - apiCallsPerMonth?: number - dataVolumeGB?: number - concurrentConnections?: number - customConnectors?: number - } - metadata: { - customerName: string - customerEmail: string - subscriptionId?: string - paymentStatus?: 'active' | 'past_due' | 'canceled' - } - signature: string // Cryptographic signature for validation -} - -export interface LicenseValidationResult { - valid: boolean - license?: License - reason?: string - expiresIn?: number // Days until expiration - usage?: { - apiCalls: number - dataUsed: number - connectionsUsed: number - } -} - -export interface PremiumFeature { - id: string - name: string - description: string - category: 'connector' | 'intelligence' | 'enterprise' - requiredTier: 'basic' | 'professional' | 'enterprise' - monthlyPrice: number - yearlyPrice: number - trialDays: number -} - -/** - * Premium Licensing and Revenue Management System - */ -export class LicensingSystem { - private licensePath: string - private premiumFeatures: Map = new Map() - private activeLicenses: Map = new Map() - - private colors = { - primary: chalk.hex('#3A5F4A'), - success: chalk.hex('#2D4A3A'), - warning: chalk.hex('#D67441'), - error: chalk.hex('#B85C35'), - info: chalk.hex('#4A6B5A'), - dim: chalk.hex('#8A9B8A'), - highlight: chalk.hex('#E88B5A'), - accent: chalk.hex('#F5E6D3'), - brain: chalk.hex('#E88B5A'), - premium: chalk.hex('#FFD700'), // Gold for premium features - enterprise: chalk.hex('#C0C0C0') // Silver for enterprise - } - - private emojis = { - brain: '🧠', - atom: '⚛️', - premium: '👑', - enterprise: '🏢', - trial: '⏰', - lock: '🔒', - unlock: '🔓', - key: '🗝️', - shield: '🛡️', - check: '✅', - cross: '❌', - warning: '⚠️', - sparkle: '✨', - rocket: '🚀', - money: '💰', - card: '💳', - gear: '⚙️' - } - - constructor() { - this.licensePath = path.join(process.cwd(), '.cortex', 'licenses.json') - this.initializePremiumFeatures() - } - - /** - * Initialize the licensing system - */ - async initialize(): Promise { - await this.loadLicenses() - await this.validateAllLicenses() - } - - /** - * Check if a premium feature is licensed and available - */ - async validateFeature(featureId: string, customerId?: string): Promise { - const feature = this.premiumFeatures.get(featureId) - if (!feature) { - return { - valid: false, - reason: `Feature '${featureId}' not found` - } - } - - // Find applicable license - let applicableLicense: License | undefined - - for (const license of this.activeLicenses.values()) { - if (license.features.includes(featureId) && license.status === 'active') { - if (!customerId || license.issuedTo === customerId) { - applicableLicense = license - break - } - } - } - - if (!applicableLicense) { - return { - valid: false, - reason: 'No valid license found for this feature' - } - } - - // Check expiration - const now = new Date() - const expiryDate = new Date(applicableLicense.expiresAt) - const expiresIn = Math.ceil((expiryDate.getTime() - now.getTime()) / (1000 * 60 * 60 * 24)) - - if (expiresIn <= 0) { - return { - valid: false, - license: applicableLicense, - reason: 'License has expired', - expiresIn: 0 - } - } - - // Validate signature - if (!this.validateLicenseSignature(applicableLicense)) { - return { - valid: false, - license: applicableLicense, - reason: 'License signature is invalid' - } - } - - return { - valid: true, - license: applicableLicense, - expiresIn, - usage: await this.getCurrentUsage(applicableLicense.id) - } - } - - /** - * Display premium features catalog - */ - async displayFeatureCatalog(): Promise { - console.log(boxen( - `${this.emojis.premium} ${this.colors.brain('BRAINY PREMIUM CATALOG')} ${this.emojis.atom}\n` + - `${this.colors.dim('Unlock the full potential of your atomic-age vector + graph database')}`, - { padding: 1, borderStyle: 'double', borderColor: '#FFD700', width: 80 } - )) - - console.log('\n' + this.colors.brain(`${this.emojis.rocket} API CONNECTORS (Premium)`)) - - const connectors = Array.from(this.premiumFeatures.values()) - .filter(f => f.category === 'connector') - - connectors.forEach(feature => { - const priceMonthly = this.colors.premium(`$${feature.monthlyPrice}/month`) - const priceYearly = this.colors.success(`$${feature.yearlyPrice}/year`) - const savings = Math.round(((feature.monthlyPrice * 12 - feature.yearlyPrice) / (feature.monthlyPrice * 12)) * 100) - - console.log( - `\n ${this.emojis.gear} ${this.colors.highlight(feature.name)}\n` + - ` ${this.colors.dim(feature.description)}\n` + - ` ${this.colors.accent('Pricing:')} ${priceMonthly} | ${priceYearly} ${this.colors.success(`(Save ${savings}%)`)} | ${this.colors.info(`${feature.trialDays} days free trial`)}` - ) - }) - - console.log('\n' + this.colors.brain(`${this.emojis.sparkle} INTELLIGENCE FEATURES (Premium)`)) - - const intelligence = Array.from(this.premiumFeatures.values()) - .filter(f => f.category === 'intelligence') - - intelligence.forEach(feature => { - console.log( - `\n ${this.emojis.brain} ${this.colors.highlight(feature.name)}\n` + - ` ${this.colors.dim(feature.description)}\n` + - ` ${this.colors.accent('Tier:')} ${this.colors.premium(feature.requiredTier)} | ${this.colors.accent('Trial:')} ${this.colors.info(`${feature.trialDays} days`)}` - ) - }) - - console.log('\n' + this.colors.enterprise(`${this.emojis.enterprise} ENTERPRISE FEATURES`)) - - const enterprise = Array.from(this.premiumFeatures.values()) - .filter(f => f.category === 'enterprise') - - enterprise.forEach(feature => { - console.log( - `\n ${this.emojis.shield} ${this.colors.highlight(feature.name)}\n` + - ` ${this.colors.dim(feature.description)}\n` + - ` ${this.colors.accent('Contact sales for pricing')}` - ) - }) - - console.log('\n' + boxen( - `${this.emojis.money} ${this.colors.premium('START YOUR ATOMIC AGE TRANSFORMATION')}\n\n` + - `${this.colors.accent('◆')} Free trial for all premium features\n` + - `${this.colors.accent('◆')} No credit card required to start\n` + - `${this.colors.accent('◆')} Cancel anytime, no questions asked\n\n` + - `${this.colors.dim('Visit https://soulcraft-research.com/brainy/premium to get started')}`, - { padding: 1, borderStyle: 'round', borderColor: '#FFD700' } - )) - } - - /** - * Activate a trial license for a feature - */ - async startTrial(featureId: string, customerInfo: { name: string, email: string }): Promise { - const feature = this.premiumFeatures.get(featureId) - if (!feature) { - console.log(this.colors.error(`Feature '${featureId}' not found`)) - return null - } - - console.log(boxen( - `${this.emojis.trial} ${this.colors.brain('ATOMIC TRIAL ACTIVATION')} ${this.emojis.atom}\n\n` + - `${this.colors.accent('◆')} ${this.colors.dim('Feature:')} ${this.colors.highlight(feature.name)}\n` + - `${this.colors.accent('◆')} ${this.colors.dim('Trial Duration:')} ${this.colors.success(feature.trialDays + ' days')}\n` + - `${this.colors.accent('◆')} ${this.colors.dim('Customer:')} ${this.colors.primary(customerInfo.name)}`, - { padding: 1, borderStyle: 'round', borderColor: '#E88B5A' } - )) - - const now = new Date() - const expiryDate = new Date(now.getTime() + (feature.trialDays * 24 * 60 * 60 * 1000)) - - const license: License = { - id: this.generateLicenseId(), - type: 'trial', - product: featureId, - tier: 'basic', - status: 'active', - issuedTo: customerInfo.email, - issuedAt: now.toISOString(), - expiresAt: expiryDate.toISOString(), - features: [featureId], - limits: { - apiCallsPerMonth: 1000, - dataVolumeGB: 10, - concurrentConnections: 3, - customConnectors: 0 - }, - metadata: { - customerName: customerInfo.name, - customerEmail: customerInfo.email, - paymentStatus: 'active' - }, - signature: '' - } - - // Generate signature - license.signature = this.generateLicenseSignature(license) - - // Store license - this.activeLicenses.set(license.id, license) - await this.saveLicenses() - - console.log(this.colors.success(`\n${this.emojis.sparkle} Trial activated! License ID: ${license.id}`)) - console.log(this.colors.dim(`Expires: ${expiryDate.toLocaleDateString()}`)) - - return license - } - - /** - * Check license status and display information - */ - async checkLicenseStatus(licenseId?: string): Promise { - if (licenseId) { - const license = this.activeLicenses.get(licenseId) - if (!license) { - console.log(this.colors.error(`License '${licenseId}' not found`)) - return - } - - await this.displayLicenseDetails(license) - } else { - await this.displayAllLicenses() - } - } - - /** - * Initialize premium features catalog - */ - private initializePremiumFeatures(): void { - // API Connectors - this.premiumFeatures.set('salesforce-connector', { - id: 'salesforce-connector', - name: 'Salesforce Connector', - description: 'Real-time sync with Salesforce CRM data, contacts, opportunities, and accounts', - category: 'connector', - requiredTier: 'professional', - monthlyPrice: 49, - yearlyPrice: 490, // 2 months free - trialDays: 14 - }) - - this.premiumFeatures.set('slack-connector', { - id: 'slack-connector', - name: 'Slack Integration', - description: 'Import Slack channels, messages, and team data for intelligent search', - category: 'connector', - requiredTier: 'basic', - monthlyPrice: 29, - yearlyPrice: 290, - trialDays: 7 - }) - - this.premiumFeatures.set('notion-connector', { - id: 'notion-connector', - name: 'Notion Workspace Sync', - description: 'Sync Notion pages, databases, and documentation for semantic search', - category: 'connector', - requiredTier: 'professional', - monthlyPrice: 39, - yearlyPrice: 390, - trialDays: 14 - }) - - this.premiumFeatures.set('hubspot-connector', { - id: 'hubspot-connector', - name: 'HubSpot CRM Integration', - description: 'Connect HubSpot contacts, deals, and marketing data', - category: 'connector', - requiredTier: 'professional', - monthlyPrice: 59, - yearlyPrice: 590, - trialDays: 14 - }) - - this.premiumFeatures.set('jira-connector', { - id: 'jira-connector', - name: 'Jira Project Sync', - description: 'Import Jira tickets, projects, and development workflows', - category: 'connector', - requiredTier: 'basic', - monthlyPrice: 34, - yearlyPrice: 340, - trialDays: 10 - }) - - this.premiumFeatures.set('asana-connector', { - id: 'asana-connector', - name: 'Asana Project Integration', - description: 'Sync Asana tasks, projects, teams, and milestone data for intelligent project insights', - category: 'connector', - requiredTier: 'professional', - monthlyPrice: 44, - yearlyPrice: 440, - trialDays: 14 - }) - - // Intelligence Features - this.premiumFeatures.set('auto-insights', { - id: 'auto-insights', - name: 'Proactive AI Insights', - description: 'Automatic pattern detection and intelligent recommendations', - category: 'intelligence', - requiredTier: 'professional', - monthlyPrice: 79, - yearlyPrice: 790, - trialDays: 21 - }) - - this.premiumFeatures.set('smart-autocomplete', { - id: 'smart-autocomplete', - name: 'Intelligent Auto-Complete', - description: 'Context-aware search suggestions and query completion', - category: 'intelligence', - requiredTier: 'basic', - monthlyPrice: 19, - yearlyPrice: 190, - trialDays: 14 - }) - - // Enterprise Features - this.premiumFeatures.set('advanced-security', { - id: 'advanced-security', - name: 'Advanced Security Suite', - description: 'Enterprise-grade encryption, audit logs, and compliance features', - category: 'enterprise', - requiredTier: 'enterprise', - monthlyPrice: 199, - yearlyPrice: 1990, - trialDays: 30 - }) - - this.premiumFeatures.set('custom-connectors', { - id: 'custom-connectors', - name: 'Custom Connector Development', - description: 'Build and deploy custom API connectors for your specific needs', - category: 'enterprise', - requiredTier: 'enterprise', - monthlyPrice: 299, - yearlyPrice: 2990, - trialDays: 30 - }) - } - - /** - * Load licenses from storage - */ - private async loadLicenses(): Promise { - try { - const data = await fs.readFile(this.licensePath, 'utf8') - const licenses: License[] = JSON.parse(data) - - for (const license of licenses) { - this.activeLicenses.set(license.id, license) - } - } catch (error) { - // File doesn't exist or is invalid - start fresh - this.activeLicenses.clear() - } - } - - /** - * Save licenses to storage - */ - private async saveLicenses(): Promise { - const dir = path.dirname(this.licensePath) - await fs.mkdir(dir, { recursive: true }) - - const licenses = Array.from(this.activeLicenses.values()) - await fs.writeFile(this.licensePath, JSON.stringify(licenses, null, 2)) - } - - /** - * Validate all loaded licenses - */ - private async validateAllLicenses(): Promise { - const now = new Date() - const expiredLicenses: string[] = [] - - for (const [id, license] of this.activeLicenses) { - const expiryDate = new Date(license.expiresAt) - - if (expiryDate <= now) { - license.status = 'expired' - expiredLicenses.push(id) - } else if (!this.validateLicenseSignature(license)) { - license.status = 'suspended' - expiredLicenses.push(id) - } - } - - if (expiredLicenses.length > 0) { - await this.saveLicenses() - } - } - - /** - * Generate cryptographic signature for license - */ - private generateLicenseSignature(license: License): string { - const data = `${license.id}:${license.type}:${license.product}:${license.issuedTo}:${license.expiresAt}` - const secret = process.env.BRAINY_LICENSE_SECRET || 'default-secret-key-change-in-production' - - return crypto.createHmac('sha256', secret) - .update(data) - .digest('hex') - } - - /** - * Validate license signature - */ - private validateLicenseSignature(license: License): boolean { - const expectedSignature = this.generateLicenseSignature(license) - return crypto.timingSafeEqual( - Buffer.from(license.signature, 'hex'), - Buffer.from(expectedSignature, 'hex') - ) - } - - /** - * Generate unique license ID - */ - private generateLicenseId(): string { - return 'lic_' + crypto.randomBytes(16).toString('hex') - } - - /** - * Get current usage statistics for a license - */ - private async getCurrentUsage(licenseId: string): Promise<{ apiCalls: number, dataUsed: number, connectionsUsed: number }> { - // Placeholder - would track actual usage - return { - apiCalls: Math.floor(Math.random() * 500), - dataUsed: Math.floor(Math.random() * 5), - connectionsUsed: Math.floor(Math.random() * 3) - } - } - - /** - * Display detailed license information - */ - private async displayLicenseDetails(license: License): Promise { - const feature = this.premiumFeatures.get(license.product) - const now = new Date() - const expiryDate = new Date(license.expiresAt) - const daysLeft = Math.ceil((expiryDate.getTime() - now.getTime()) / (1000 * 60 * 60 * 24)) - const usage = await this.getCurrentUsage(license.id) - - const statusColor = license.status === 'active' ? this.colors.success : - license.status === 'trial' ? this.colors.warning : - license.status === 'expired' ? this.colors.error : - this.colors.dim - - const statusIcon = license.status === 'active' ? this.emojis.check : - license.status === 'trial' ? this.emojis.trial : - license.status === 'expired' ? this.emojis.cross : - this.emojis.warning - - console.log(boxen( - `${this.emojis.key} ${this.colors.brain('LICENSE DETAILS')} ${this.emojis.atom}\n\n` + - `${this.colors.accent('License ID:')} ${this.colors.primary(license.id)}\n` + - `${this.colors.accent('Product:')} ${this.colors.highlight(feature?.name || license.product)}\n` + - `${this.colors.accent('Type:')} ${this.colors.premium(license.type)}\n` + - `${this.colors.accent('Status:')} ${statusIcon} ${statusColor(license.status)}\n` + - `${this.colors.accent('Customer:')} ${this.colors.primary(license.metadata.customerName)}\n` + - `${this.colors.accent('Expires:')} ${daysLeft > 0 ? this.colors.success(`${daysLeft} days`) : this.colors.error('Expired')}`, - { padding: 1, borderStyle: 'round', borderColor: license.status === 'active' ? '#2D4A3A' : '#D67441' } - )) - - // Usage statistics - if (license.status === 'active' || license.status === 'trial') { - console.log('\n' + this.colors.brain(`${this.emojis.gear} USAGE STATISTICS`)) - - const apiUsage = license.limits.apiCallsPerMonth ? - `${usage.apiCalls}/${license.limits.apiCallsPerMonth}` : - usage.apiCalls.toString() - - const dataUsage = license.limits.dataVolumeGB ? - `${usage.dataUsed}GB/${license.limits.dataVolumeGB}GB` : - `${usage.dataUsed}GB` - - console.log(` ${this.colors.accent('API Calls:')} ${this.colors.primary(apiUsage)}`) - console.log(` ${this.colors.accent('Data Used:')} ${this.colors.primary(dataUsage)}`) - console.log(` ${this.colors.accent('Connections:')} ${this.colors.primary(usage.connectionsUsed.toString())}`) - } - } - - /** - * Display all active licenses - */ - private async displayAllLicenses(): Promise { - if (this.activeLicenses.size === 0) { - console.log(boxen( - `${this.emojis.lock} ${this.colors.brain('NO ACTIVE LICENSES')} ${this.emojis.atom}\n\n` + - `${this.colors.dim('Start your atomic transformation with premium features:')}\n` + - `${this.colors.accent('→')} Run 'cortex license catalog' to browse features\n` + - `${this.colors.accent('→')} Run 'cortex license trial ' to start free trial`, - { padding: 1, borderStyle: 'round', borderColor: '#D67441' } - )) - return - } - - console.log(boxen( - `${this.emojis.premium} ${this.colors.brain('ACTIVE LICENSES')} ${this.emojis.atom}`, - { padding: 1, borderStyle: 'round', borderColor: '#FFD700' } - )) - - for (const license of this.activeLicenses.values()) { - const feature = this.premiumFeatures.get(license.product) - const expiryDate = new Date(license.expiresAt) - const daysLeft = Math.ceil((expiryDate.getTime() - Date.now()) / (1000 * 60 * 60 * 24)) - - const statusIcon = license.status === 'active' ? this.emojis.check : - license.status === 'trial' ? this.emojis.trial : - license.status === 'expired' ? this.emojis.cross : this.emojis.warning - - console.log( - `\n ${statusIcon} ${this.colors.highlight(feature?.name || license.product)}\n` + - ` ${this.colors.dim('License:')} ${this.colors.primary(license.id)}\n` + - ` ${this.colors.dim('Type:')} ${this.colors.premium(license.type)} | ` + - `${this.colors.dim('Status:')} ${this.colors.success(license.status)} | ` + - `${this.colors.dim('Expires:')} ${daysLeft > 0 ? this.colors.info(`${daysLeft} days`) : this.colors.error('Expired')}` - ) - } - - console.log(`\n${this.colors.dim('Run')} ${this.colors.accent('cortex license status ')} ${this.colors.dim('for detailed information')}`) - } -} \ No newline at end of file diff --git a/src/cortex/neuralImport.ts b/src/cortex/neuralImport.ts index 33c2e46d..20cbff4d 100644 --- a/src/cortex/neuralImport.ts +++ b/src/cortex/neuralImport.ts @@ -7,8 +7,8 @@ import { BrainyData } from '../brainyData.js' import { NounType, VerbType } from '../types/graphTypes.js' -import * as fs from 'fs/promises' -import * as path from 'path' +import * as fs from '../universal/fs.js' +import * as path from '../universal/path.js' // @ts-ignore import chalk from 'chalk' // @ts-ignore diff --git a/src/cortex/serviceIntegration.ts b/src/cortex/serviceIntegration.ts index d0221d22..8b43cdad 100644 --- a/src/cortex/serviceIntegration.ts +++ b/src/cortex/serviceIntegration.ts @@ -6,8 +6,8 @@ import { BrainyData } from '../brainyData.js' import { Cortex } from './cortex.js' -import * as fs from 'fs/promises' -import * as path from 'path' +import * as fs from '../universal/fs.js' +import * as path from '../universal/path.js' export interface ServiceConfig { name: string diff --git a/src/cortex/webhookManager.ts b/src/cortex/webhookManager.ts deleted file mode 100644 index 65cfc720..00000000 --- a/src/cortex/webhookManager.ts +++ /dev/null @@ -1,384 +0,0 @@ -/** - * Webhook Manager for Cortex CLI - * - * 🧠⚛️ Manage webhooks through Cortex for enterprise integrations - */ - -import chalk from 'chalk' -import boxen from 'boxen' -import Table from 'cli-table3' -import prompts from 'prompts' -import { WebhookSystem, WebhookBuilder, WebhookEventType } from '../webhooks/webhookSystem.js' - -export class WebhookManager { - private webhookSystem: WebhookSystem - private colors: any - private emojis: any - - constructor(brainy: any) { - this.webhookSystem = new WebhookSystem(brainy) - - this.colors = { - primary: chalk.hex('#3A5F4A'), - success: chalk.hex('#2D4A3A'), - warning: chalk.hex('#D67441'), - error: chalk.hex('#B85C35'), - info: chalk.hex('#4A6B5A'), - dim: chalk.hex('#8A9B8A'), - highlight: chalk.hex('#E88B5A'), - accent: chalk.hex('#F5E6D3') - } - - this.emojis = { - webhook: '🔔', - atom: '⚛️', - check: '✅', - cross: '❌', - warning: '⚠️', - sync: '🔄', - sparkle: '✨', - gear: '⚙️' - } - } - - /** - * Add a new webhook interactively - */ - async addWebhook(): Promise { - console.log(boxen( - `${this.emojis.webhook} ${this.colors.primary('WEBHOOK CONFIGURATION')} ${this.emojis.atom}`, - { padding: 1, borderStyle: 'round', borderColor: '#E88B5A' } - )) - - const answers = await prompts([ - { - type: 'text', - name: 'id', - message: 'Webhook ID (unique identifier):', - validate: (value: string) => value.length > 0 || 'ID is required' - }, - { - type: 'text', - name: 'url', - message: 'Webhook URL:', - validate: (value: string) => { - try { - new URL(value) - return true - } catch { - return 'Invalid URL format' - } - } - }, - { - type: 'multiselect', - name: 'events', - message: 'Select events to subscribe to:', - choices: [ - { title: 'Data Added', value: 'data.added', selected: true }, - { title: 'Data Updated', value: 'data.updated' }, - { title: 'Data Deleted', value: 'data.deleted' }, - { title: 'Augmentation Triggered', value: 'augmentation.triggered' }, - { title: 'Augmentation Completed', value: 'augmentation.completed', selected: true }, - { title: 'Augmentation Failed', value: 'augmentation.failed' }, - { title: 'Connector Sync Started', value: 'connector.sync.started' }, - { title: 'Connector Sync Completed', value: 'connector.sync.completed' }, - { title: 'Connector Sync Failed', value: 'connector.sync.failed' }, - { title: 'Graph Relationship Created', value: 'graph.relationship.created' }, - { title: 'System Alert', value: 'system.alert' } - ], - min: 1 - }, - { - type: 'text', - name: 'secret', - message: 'Webhook secret (optional, for signature verification):' - }, - { - type: 'confirm', - name: 'addHeaders', - message: 'Add custom headers?', - initial: false - } - ]) - - if (!answers.id || !answers.url) { - console.log(this.colors.dim('Webhook configuration cancelled')) - return - } - - const builder = new WebhookBuilder() - .url(answers.url) - .events(...answers.events as WebhookEventType[]) - - if (answers.secret) { - builder.secret(answers.secret) - } - - // Add custom headers if requested - if (answers.addHeaders) { - const headers: Record = {} - let addMore = true - - while (addMore) { - const header = await prompts([ - { - type: 'text', - name: 'key', - message: 'Header name:' - }, - { - type: 'text', - name: 'value', - message: 'Header value:' - }, - { - type: 'confirm', - name: 'continue', - message: 'Add another header?', - initial: false - } - ]) - - if (header.key && header.value) { - headers[header.key] = header.value - } - - addMore = header.continue - } - - if (Object.keys(headers).length > 0) { - builder.headers(headers) - } - } - - // Configure retry policy - const retryConfig = await prompts([ - { - type: 'number', - name: 'maxRetries', - message: 'Max retry attempts:', - initial: 3, - min: 0, - max: 10 - }, - { - type: 'number', - name: 'backoffMs', - message: 'Initial retry delay (ms):', - initial: 1000, - min: 100, - max: 60000 - } - ]) - - builder.retry(retryConfig.maxRetries, retryConfig.backoffMs) - - try { - await this.webhookSystem.registerWebhook(answers.id, builder.build()) - - console.log(boxen( - `${this.emojis.check} ${this.colors.success('WEBHOOK REGISTERED')} ${this.emojis.atom}\n\n` + - `${this.colors.accent('ID:')} ${answers.id}\n` + - `${this.colors.accent('URL:')} ${answers.url}\n` + - `${this.colors.accent('Events:')} ${answers.events.length} subscribed`, - { padding: 1, borderStyle: 'round', borderColor: '#2D4A3A' } - )) - - // Offer to test - const { test } = await prompts({ - type: 'confirm', - name: 'test', - message: 'Test webhook now?', - initial: true - }) - - if (test) { - await this.testWebhook(answers.id) - } - } catch (error: any) { - console.error(`${this.emojis.cross} Failed to register webhook:`, error.message) - } - } - - /** - * List all webhooks - */ - async listWebhooks(): Promise { - const webhooks = this.webhookSystem.listWebhooks() - const stats = this.webhookSystem.getStatistics() - - console.log(boxen( - `${this.emojis.webhook} ${this.colors.primary('REGISTERED WEBHOOKS')} ${this.emojis.atom}\n\n` + - `${this.colors.accent('Total:')} ${stats.total}\n` + - `${this.colors.accent('Enabled:')} ${stats.enabled}\n` + - `${this.colors.accent('Failed Queues:')} ${stats.failedQueues.filter(q => q.count > 0).length}`, - { padding: 1, borderStyle: 'round', borderColor: '#E88B5A' } - )) - - if (webhooks.length === 0) { - console.log(this.colors.dim('\nNo webhooks registered')) - console.log(this.colors.dim('Use "cortex webhook add" to register a webhook')) - return - } - - const table = new Table({ - head: ['ID', 'URL', 'Events', 'Status', 'Failed'], - style: { - head: ['cyan'], - border: ['grey'] - } - }) - - for (const { id, config } of webhooks) { - const failedCount = stats.failedQueues.find(q => q.id === id)?.count || 0 - - table.push([ - id, - config.url.length > 40 ? config.url.substring(0, 37) + '...' : config.url, - config.events.length.toString(), - config.enabled ? this.colors.success('Enabled') : this.colors.dim('Disabled'), - failedCount > 0 ? this.colors.warning(failedCount.toString()) : '-' - ]) - } - - console.log(table.toString()) - } - - /** - * Test a webhook - */ - async testWebhook(id: string): Promise { - const spinner = '⚛️' - console.log(`${spinner} Testing webhook ${id}...`) - - try { - const success = await this.webhookSystem.testWebhook(id) - - if (success) { - console.log(`${this.emojis.check} Webhook test successful! Server responded correctly.`) - } else { - console.log(`${this.emojis.cross} Webhook test failed. Check the URL and server.`) - } - } catch (error: any) { - console.error(`${this.emojis.cross} Test failed:`, error.message) - } - } - - /** - * Remove a webhook - */ - async removeWebhook(id: string): Promise { - const { confirm } = await prompts({ - type: 'confirm', - name: 'confirm', - message: `Remove webhook "${id}"?`, - initial: false - }) - - if (!confirm) { - console.log(this.colors.dim('Cancelled')) - return - } - - await this.webhookSystem.unregisterWebhook(id) - console.log(`${this.emojis.check} Webhook "${id}" removed`) - } - - /** - * Retry failed webhooks - */ - async retryFailed(id: string): Promise { - console.log(`${this.emojis.sync} Retrying failed webhooks for "${id}"...`) - - const count = await this.webhookSystem.retryFailed(id) - - if (count > 0) { - console.log(`${this.emojis.check} Retried ${count} failed webhook(s)`) - } else { - console.log(this.colors.dim('No failed webhooks to retry')) - } - } - - /** - * Configure webhook interactively - */ - async configureWebhook(id: string): Promise { - const webhooks = this.webhookSystem.listWebhooks() - const webhook = webhooks.find(w => w.id === id) - - if (!webhook) { - console.error(`${this.emojis.cross} Webhook "${id}" not found`) - return - } - - console.log(boxen( - `${this.emojis.gear} ${this.colors.primary('CONFIGURE WEBHOOK')} ${this.emojis.atom}\n\n` + - `${this.colors.accent('ID:')} ${id}\n` + - `${this.colors.accent('Current URL:')} ${webhook.config.url}`, - { padding: 1, borderStyle: 'round', borderColor: '#E88B5A' } - )) - - const { action } = await prompts({ - type: 'select', - name: 'action', - message: 'What would you like to configure?', - choices: [ - { title: 'Change URL', value: 'url' }, - { title: 'Update Events', value: 'events' }, - { title: 'Set Secret', value: 'secret' }, - { title: 'Toggle Enable/Disable', value: 'toggle' }, - { title: 'Update Retry Policy', value: 'retry' }, - { title: 'Cancel', value: 'cancel' } - ] - }) - - switch (action) { - case 'url': - const { newUrl } = await prompts({ - type: 'text', - name: 'newUrl', - message: 'New webhook URL:', - initial: webhook.config.url - }) - if (newUrl) { - webhook.config.url = newUrl - await this.webhookSystem.unregisterWebhook(id) - await this.webhookSystem.registerWebhook(id, webhook.config) - console.log(`${this.emojis.check} URL updated`) - } - break - - case 'toggle': - webhook.config.enabled = !webhook.config.enabled - await this.webhookSystem.unregisterWebhook(id) - await this.webhookSystem.registerWebhook(id, webhook.config) - console.log(`${this.emojis.check} Webhook ${webhook.config.enabled ? 'enabled' : 'disabled'}`) - break - - case 'cancel': - console.log(this.colors.dim('Configuration cancelled')) - break - } - } - - /** - * Show webhook statistics - */ - async showStatistics(): Promise { - const stats = this.webhookSystem.getStatistics() - - console.log(boxen( - `${this.emojis.webhook} ${this.colors.primary('WEBHOOK STATISTICS')} ${this.emojis.atom}\n\n` + - `${this.colors.accent('Total Webhooks:')} ${stats.total}\n` + - `${this.colors.accent('Enabled:')} ${stats.enabled}\n` + - `${this.colors.accent('Disabled:')} ${stats.total - stats.enabled}\n\n` + - `${this.colors.highlight('Failed Queues:')}\n` + - stats.failedQueues - .filter(q => q.count > 0) - .map(q => ` ${this.colors.warning('•')} ${q.id}: ${q.count} failed`) - .join('\n'), - { padding: 1, borderStyle: 'round', borderColor: '#E88B5A' } - )) - } -} \ No newline at end of file diff --git a/src/distributed/configManager.ts b/src/distributed/configManager.ts index 5c38a799..5b77a62f 100644 --- a/src/distributed/configManager.ts +++ b/src/distributed/configManager.ts @@ -3,7 +3,7 @@ * Manages shared configuration in S3 for distributed Brainy instances */ -import { v4 as uuidv4 } from 'uuid' +import { v4 as uuidv4 } from '../universal/uuid.js' import { DistributedConfig, SharedConfig, diff --git a/src/index.ts b/src/index.ts index b797af27..615b21a2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -54,8 +54,8 @@ import { BrainyChat, ChatOptions } from './chat/brainyChat.js' export { BrainyChat } export type { ChatOptions } -// Export Cortex CLI functionality -export { Cortex } from './cortex/cortex.js' +// Export Cortex CLI functionality - commented out for core MIT build +// export { Cortex } from './cortex/cortex.js' // Export performance and optimization utilities import { @@ -247,6 +247,7 @@ export { } export type { AugmentationRegistryLoaderOptions, AugmentationLoadResult } + // Export augmentation implementations import { MemoryStorageAugmentation, diff --git a/src/mcp/brainyMCPAdapter.ts b/src/mcp/brainyMCPAdapter.ts index 1ffcd853..31fc8089 100644 --- a/src/mcp/brainyMCPAdapter.ts +++ b/src/mcp/brainyMCPAdapter.ts @@ -6,7 +6,7 @@ * and getting relationships. */ -import { v4 as uuidv4 } from 'uuid' +import { v4 as uuidv4 } from '../universal/uuid.js' import { BrainyDataInterface } from '../types/brainyDataInterface.js' import { MCPRequest, diff --git a/src/mcp/brainyMCPService.ts b/src/mcp/brainyMCPService.ts index a68a3955..8e3cf185 100644 --- a/src/mcp/brainyMCPService.ts +++ b/src/mcp/brainyMCPService.ts @@ -7,7 +7,7 @@ * for external model access. */ -import { v4 as uuidv4 } from 'uuid' +import { v4 as uuidv4 } from '../universal/uuid.js' import { BrainyDataInterface } from '../types/brainyDataInterface.js' import { MCPRequest, diff --git a/src/mcp/mcpAugmentationToolset.ts b/src/mcp/mcpAugmentationToolset.ts index 1c118e99..e1b59a79 100644 --- a/src/mcp/mcpAugmentationToolset.ts +++ b/src/mcp/mcpAugmentationToolset.ts @@ -5,7 +5,7 @@ * It provides methods for getting available tools and executing tools. */ -import { v4 as uuidv4 } from 'uuid' +import { v4 as uuidv4 } from '../universal/uuid.js' import { MCPResponse, MCPToolExecutionRequest, diff --git a/src/universal/crypto.ts b/src/universal/crypto.ts new file mode 100644 index 00000000..1d10d000 --- /dev/null +++ b/src/universal/crypto.ts @@ -0,0 +1,228 @@ +/** + * Universal Crypto implementation + * Works in all environments: Browser, Node.js, Serverless + */ + +import { isBrowser, isNode } from '../utils/environment.js' + +let nodeCrypto: any = null + +// Dynamic import for Node.js crypto (only in Node.js environment) +if (isNode()) { + try { + nodeCrypto = await import('crypto') + } catch { + // Ignore import errors in non-Node environments + } +} + +/** + * Generate random bytes + */ +export function randomBytes(size: number): Uint8Array { + if (isBrowser() || typeof crypto !== 'undefined') { + // Use Web Crypto API (available in browsers and modern Node.js) + const array = new Uint8Array(size) + crypto.getRandomValues(array) + return array + } else if (nodeCrypto) { + // Use Node.js crypto as fallback + return new Uint8Array(nodeCrypto.randomBytes(size)) + } else { + // Fallback for environments without crypto + const array = new Uint8Array(size) + for (let i = 0; i < size; i++) { + array[i] = Math.floor(Math.random() * 256) + } + return array + } +} + +/** + * Generate random UUID + */ +export function randomUUID(): string { + if (typeof crypto !== 'undefined' && crypto.randomUUID) { + return crypto.randomUUID() + } else if (nodeCrypto && nodeCrypto.randomUUID) { + return nodeCrypto.randomUUID() + } else { + // Fallback UUID generation + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => { + const r = Math.random() * 16 | 0 + const v = c === 'x' ? r : (r & 0x3 | 0x8) + return v.toString(16) + }) + } +} + +/** + * Create hash (simplified interface) + */ +export function createHash(algorithm: string): { + update: (data: string | Uint8Array) => any + digest: (encoding: string) => string +} { + if (nodeCrypto && nodeCrypto.createHash) { + return nodeCrypto.createHash(algorithm) + } else { + // Simple fallback hash for browsers (not cryptographically secure) + let hash = 0 + const hashObj = { + update: (data: string | Uint8Array) => { + const text = typeof data === 'string' ? data : new TextDecoder().decode(data) + for (let i = 0; i < text.length; i++) { + const char = text.charCodeAt(i) + hash = ((hash << 5) - hash) + char + hash = hash & hash // Convert to 32-bit integer + } + return hashObj + }, + digest: (encoding: string) => { + return Math.abs(hash).toString(16) + } + } + return hashObj + } +} + +/** + * Create HMAC + */ +export function createHmac(algorithm: string, key: string | Uint8Array): { + update: (data: string | Uint8Array) => any + digest: (encoding: string) => string +} { + if (nodeCrypto && nodeCrypto.createHmac) { + return nodeCrypto.createHmac(algorithm, key) + } else { + // Fallback HMAC implementation (simplified) + return createHash(algorithm) + } +} + +/** + * PBKDF2 synchronous + */ +export function pbkdf2Sync(password: string | Uint8Array, salt: string | Uint8Array, iterations: number, keylen: number, digest: string): Uint8Array { + if (nodeCrypto && nodeCrypto.pbkdf2Sync) { + return new Uint8Array(nodeCrypto.pbkdf2Sync(password, salt, iterations, keylen, digest)) + } else { + // Simplified fallback (not cryptographically secure) + const result = new Uint8Array(keylen) + const passwordStr = typeof password === 'string' ? password : new TextDecoder().decode(password) + const saltStr = typeof salt === 'string' ? salt : new TextDecoder().decode(salt) + + let hash = 0 + const combined = passwordStr + saltStr + for (let i = 0; i < combined.length; i++) { + hash = ((hash << 5) - hash) + combined.charCodeAt(i) + hash = hash & hash + } + + for (let i = 0; i < keylen; i++) { + result[i] = (Math.abs(hash + i) % 256) + } + return result + } +} + +/** + * Scrypt synchronous + */ +export function scryptSync(password: string | Uint8Array, salt: string | Uint8Array, keylen: number, options?: any): Uint8Array { + if (nodeCrypto && nodeCrypto.scryptSync) { + return new Uint8Array(nodeCrypto.scryptSync(password, salt, keylen, options)) + } else { + // Fallback to pbkdf2Sync + return pbkdf2Sync(password, salt, 10000, keylen, 'sha256') + } +} + +/** + * Create cipher + */ +export function createCipheriv(algorithm: string, key: Uint8Array, iv: Uint8Array): { + update: (data: string, inputEncoding?: string, outputEncoding?: string) => string + final: (outputEncoding?: string) => string +} { + if (nodeCrypto && nodeCrypto.createCipheriv) { + return nodeCrypto.createCipheriv(algorithm, key, iv) + } else { + // Fallback encryption (XOR-based, not secure) + let encrypted = '' + return { + update: (data: string, inputEncoding?: string, outputEncoding?: string) => { + for (let i = 0; i < data.length; i++) { + const char = data.charCodeAt(i) + const keyByte = key[i % key.length] + const ivByte = iv[i % iv.length] + encrypted += String.fromCharCode(char ^ keyByte ^ ivByte) + } + return outputEncoding === 'hex' ? Buffer.from(encrypted, 'binary').toString('hex') : encrypted + }, + final: (outputEncoding?: string) => { + return outputEncoding === 'hex' ? '' : '' + } + } + } +} + +/** + * Create decipher + */ +export function createDecipheriv(algorithm: string, key: Uint8Array, iv: Uint8Array): { + update: (data: string, inputEncoding?: string, outputEncoding?: string) => string + final: (outputEncoding?: string) => string +} { + if (nodeCrypto && nodeCrypto.createDecipheriv) { + return nodeCrypto.createDecipheriv(algorithm, key, iv) + } else { + // Fallback decryption (XOR-based, matches createCipheriv) + let decrypted = '' + return { + update: (data: string, inputEncoding?: string, outputEncoding?: string) => { + const input = inputEncoding === 'hex' ? Buffer.from(data, 'hex').toString('binary') : data + for (let i = 0; i < input.length; i++) { + const char = input.charCodeAt(i) + const keyByte = key[i % key.length] + const ivByte = iv[i % iv.length] + decrypted += String.fromCharCode(char ^ keyByte ^ ivByte) + } + return decrypted + }, + final: (outputEncoding?: string) => { + return '' + } + } + } +} + +/** + * Timing safe equal + */ +export function timingSafeEqual(a: Uint8Array, b: Uint8Array): boolean { + if (nodeCrypto && nodeCrypto.timingSafeEqual) { + return nodeCrypto.timingSafeEqual(a, b) + } else { + // Fallback implementation + if (a.length !== b.length) return false + let result = 0 + for (let i = 0; i < a.length; i++) { + result |= a[i] ^ b[i] + } + return result === 0 + } +} + +export default { + randomBytes, + randomUUID, + createHash, + createHmac, + pbkdf2Sync, + scryptSync, + createCipheriv, + createDecipheriv, + timingSafeEqual +} \ No newline at end of file diff --git a/src/universal/events.ts b/src/universal/events.ts new file mode 100644 index 00000000..93855e01 --- /dev/null +++ b/src/universal/events.ts @@ -0,0 +1,199 @@ +/** + * Universal Events implementation + * Browser: Uses EventTarget API + * Node.js: Uses built-in events module + */ + +import { isBrowser, isNode } from '../utils/environment.js' + +let nodeEvents: any = null + +// Dynamic import for Node.js events (only in Node.js environment) +if (isNode()) { + try { + nodeEvents = await import('events') + } catch { + // Ignore import errors in non-Node environments + } +} + +/** + * Universal EventEmitter interface + */ +export interface UniversalEventEmitter { + on(event: string, listener: (...args: any[]) => void): this + off(event: string, listener: (...args: any[]) => void): this + emit(event: string, ...args: any[]): boolean + once(event: string, listener: (...args: any[]) => void): this + removeAllListeners(event?: string): this + listenerCount(event: string): number +} + +/** + * Browser implementation using EventTarget + */ +class BrowserEventEmitter extends EventTarget implements UniversalEventEmitter { + private listeners = new Map void>>() + + on(event: string, listener: (...args: any[]) => void): this { + if (!this.listeners.has(event)) { + this.listeners.set(event, new Set()) + } + this.listeners.get(event)!.add(listener) + + const handler = (e: Event) => { + const customEvent = e as CustomEvent + listener(...(customEvent.detail || [])) + } + + // Store original listener reference for removal + ;(listener as any).__handler = handler + this.addEventListener(event, handler) + + return this + } + + off(event: string, listener: (...args: any[]) => void): this { + const eventListeners = this.listeners.get(event) + if (eventListeners) { + eventListeners.delete(listener) + + const handler = (listener as any).__handler + if (handler) { + this.removeEventListener(event, handler) + delete (listener as any).__handler + } + } + + return this + } + + emit(event: string, ...args: any[]): boolean { + const customEvent = new CustomEvent(event, { detail: args }) + this.dispatchEvent(customEvent) + + const eventListeners = this.listeners.get(event) + return eventListeners ? eventListeners.size > 0 : false + } + + once(event: string, listener: (...args: any[]) => void): this { + const onceListener = (...args: any[]) => { + this.off(event, onceListener) + listener(...args) + } + + return this.on(event, onceListener) + } + + removeAllListeners(event?: string): this { + if (event) { + const eventListeners = this.listeners.get(event) + if (eventListeners) { + for (const listener of eventListeners) { + this.off(event, listener) + } + } + } else { + for (const [eventName] of this.listeners) { + this.removeAllListeners(eventName) + } + } + + return this + } + + listenerCount(event: string): number { + const eventListeners = this.listeners.get(event) + return eventListeners ? eventListeners.size : 0 + } +} + +/** + * Node.js implementation using events.EventEmitter + */ +class NodeEventEmitter implements UniversalEventEmitter { + private emitter: any + + constructor() { + this.emitter = new nodeEvents.EventEmitter() + } + + on(event: string, listener: (...args: any[]) => void): this { + this.emitter.on(event, listener) + return this + } + + off(event: string, listener: (...args: any[]) => void): this { + this.emitter.off(event, listener) + return this + } + + emit(event: string, ...args: any[]): boolean { + return this.emitter.emit(event, ...args) + } + + once(event: string, listener: (...args: any[]) => void): this { + this.emitter.once(event, listener) + return this + } + + removeAllListeners(event?: string): this { + this.emitter.removeAllListeners(event) + return this + } + + listenerCount(event: string): number { + return this.emitter.listenerCount(event) + } +} + +/** + * Universal EventEmitter class + */ +export class EventEmitter implements UniversalEventEmitter { + private emitter: UniversalEventEmitter + + constructor() { + if (isBrowser()) { + this.emitter = new BrowserEventEmitter() + } else if (isNode() && nodeEvents) { + this.emitter = new NodeEventEmitter() + } else { + this.emitter = new BrowserEventEmitter() + } + } + + on(event: string, listener: (...args: any[]) => void): this { + this.emitter.on(event, listener) + return this + } + + off(event: string, listener: (...args: any[]) => void): this { + this.emitter.off(event, listener) + return this + } + + emit(event: string, ...args: any[]): boolean { + return this.emitter.emit(event, ...args) + } + + once(event: string, listener: (...args: any[]) => void): this { + this.emitter.once(event, listener) + return this + } + + removeAllListeners(event?: string): this { + this.emitter.removeAllListeners(event) + return this + } + + listenerCount(event: string): number { + return this.emitter.listenerCount(event) + } +} + +// Named export for compatibility +export { EventEmitter as default } + +// Re-export Node.js EventEmitter class if available +export const NodeEventEmitterClass = nodeEvents?.EventEmitter || null \ No newline at end of file diff --git a/src/universal/fs.ts b/src/universal/fs.ts new file mode 100644 index 00000000..89dfc35f --- /dev/null +++ b/src/universal/fs.ts @@ -0,0 +1,327 @@ +/** + * Universal File System implementation + * Browser: Uses OPFS (Origin Private File System) + * Node.js: Uses built-in fs/promises + * Serverless: Uses memory-based fallback + */ + +import { isBrowser, isNode } from '../utils/environment.js' + +let nodeFs: any = null + +// Dynamic import for Node.js fs (only in Node.js environment) +if (isNode()) { + try { + nodeFs = await import('fs/promises') + } catch { + // Ignore import errors in non-Node environments + } +} + +/** + * Universal file operations interface + */ +export interface UniversalFS { + readFile(path: string): Promise + writeFile(path: string, data: string): Promise + mkdir(path: string, options?: { recursive?: boolean }): Promise + exists(path: string): Promise + readdir(path: string): Promise + unlink(path: string): Promise + stat(path: string): Promise<{ isFile(): boolean, isDirectory(): boolean }> + access(path: string, mode?: number): Promise +} + +/** + * Browser implementation using OPFS + */ +class BrowserFS implements UniversalFS { + private async getRoot(): Promise { + if ('storage' in navigator && 'getDirectory' in navigator.storage) { + return await (navigator.storage as any).getDirectory() + } + throw new Error('OPFS not supported in this browser') + } + + private async getFileHandle(path: string, create = false): Promise { + const root = await this.getRoot() + const parts = path.split('/').filter(p => p) + + let dir = root + for (let i = 0; i < parts.length - 1; i++) { + dir = await dir.getDirectoryHandle(parts[i], { create }) + } + + const fileName = parts[parts.length - 1] + return await dir.getFileHandle(fileName, { create }) + } + + private async getDirHandle(path: string, create = false): Promise { + const root = await this.getRoot() + const parts = path.split('/').filter(p => p) + + let dir = root + for (const part of parts) { + dir = await dir.getDirectoryHandle(part, { create }) + } + + return dir + } + + async readFile(path: string): Promise { + try { + const fileHandle = await this.getFileHandle(path) + const file = await fileHandle.getFile() + return await file.text() + } catch (error) { + throw new Error(`File not found: ${path}`) + } + } + + async writeFile(path: string, data: string): Promise { + const fileHandle = await this.getFileHandle(path, true) + const writable = await fileHandle.createWritable() + await writable.write(data) + await writable.close() + } + + async mkdir(path: string, options = { recursive: true }): Promise { + await this.getDirHandle(path, true) + } + + async exists(path: string): Promise { + try { + await this.getFileHandle(path) + return true + } catch { + try { + await this.getDirHandle(path) + return true + } catch { + return false + } + } + } + + async readdir(path: string): Promise { + const dir = await this.getDirHandle(path) + const entries: string[] = [] + for await (const [name] of dir.entries()) { + entries.push(name) + } + return entries + } + + async unlink(path: string): Promise { + const parts = path.split('/').filter(p => p) + const fileName = parts.pop()! + const dirPath = parts.join('/') + + if (dirPath) { + const dir = await this.getDirHandle(dirPath) + await dir.removeEntry(fileName) + } else { + const root = await this.getRoot() + await root.removeEntry(fileName) + } + } + + async stat(path: string): Promise<{ isFile(): boolean, isDirectory(): boolean }> { + try { + await this.getFileHandle(path) + return { isFile: () => true, isDirectory: () => false } + } catch { + try { + await this.getDirHandle(path) + return { isFile: () => false, isDirectory: () => true } + } catch { + throw new Error(`Path not found: ${path}`) + } + } + } + + async access(path: string, mode?: number): Promise { + const exists = await this.exists(path) + if (!exists) { + throw new Error(`ENOENT: no such file or directory, access '${path}'`) + } + } +} + +/** + * Node.js implementation using fs/promises + */ +class NodeFS implements UniversalFS { + async readFile(path: string): Promise { + return await nodeFs.readFile(path, 'utf-8') + } + + async writeFile(path: string, data: string): Promise { + await nodeFs.writeFile(path, data, 'utf-8') + } + + async mkdir(path: string, options = { recursive: true }): Promise { + await nodeFs.mkdir(path, options) + } + + async exists(path: string): Promise { + try { + await nodeFs.access(path) + return true + } catch { + return false + } + } + + async readdir(path: string): Promise { + return await nodeFs.readdir(path) + } + + async unlink(path: string): Promise { + await nodeFs.unlink(path) + } + + async stat(path: string): Promise<{ isFile(): boolean, isDirectory(): boolean }> { + const stats = await nodeFs.stat(path) + return { + isFile: () => stats.isFile(), + isDirectory: () => stats.isDirectory() + } + } + + async access(path: string, mode?: number): Promise { + await nodeFs.access(path, mode) + } +} + +/** + * Memory-based fallback for serverless/edge environments + */ +class MemoryFS implements UniversalFS { + private files = new Map() + private dirs = new Set() + + async readFile(path: string): Promise { + const content = this.files.get(path) + if (content === undefined) { + throw new Error(`File not found: ${path}`) + } + return content + } + + async writeFile(path: string, data: string): Promise { + this.files.set(path, data) + // Ensure parent directories exist + const parts = path.split('/').slice(0, -1) + for (let i = 1; i <= parts.length; i++) { + this.dirs.add(parts.slice(0, i).join('/')) + } + } + + async mkdir(path: string, options = { recursive: true }): Promise { + this.dirs.add(path) + if (options.recursive) { + const parts = path.split('/') + for (let i = 1; i <= parts.length; i++) { + this.dirs.add(parts.slice(0, i).join('/')) + } + } + } + + async exists(path: string): Promise { + return this.files.has(path) || this.dirs.has(path) + } + + async readdir(path: string): Promise { + const entries = new Set() + const pathPrefix = path + '/' + + for (const filePath of this.files.keys()) { + if (filePath.startsWith(pathPrefix)) { + const relativePath = filePath.slice(pathPrefix.length) + const firstSegment = relativePath.split('/')[0] + entries.add(firstSegment) + } + } + + for (const dirPath of this.dirs) { + if (dirPath.startsWith(pathPrefix)) { + const relativePath = dirPath.slice(pathPrefix.length) + const firstSegment = relativePath.split('/')[0] + if (firstSegment) entries.add(firstSegment) + } + } + + return Array.from(entries) + } + + async unlink(path: string): Promise { + this.files.delete(path) + } + + async stat(path: string): Promise<{ isFile(): boolean, isDirectory(): boolean }> { + const isFile = this.files.has(path) + const isDir = this.dirs.has(path) + + if (!isFile && !isDir) { + throw new Error(`Path not found: ${path}`) + } + + return { + isFile: () => isFile, + isDirectory: () => isDir + } + } + + async access(path: string, mode?: number): Promise { + const exists = await this.exists(path) + if (!exists) { + throw new Error(`ENOENT: no such file or directory, access '${path}'`) + } + } +} + +// Create the appropriate filesystem implementation +let fsImpl: UniversalFS + +if (isBrowser()) { + fsImpl = new BrowserFS() +} else if (isNode() && nodeFs) { + fsImpl = new NodeFS() +} else { + fsImpl = new MemoryFS() +} + +// Export the filesystem operations +export const readFile = fsImpl.readFile.bind(fsImpl) +export const writeFile = fsImpl.writeFile.bind(fsImpl) +export const mkdir = fsImpl.mkdir.bind(fsImpl) +export const exists = fsImpl.exists.bind(fsImpl) +export const readdir = fsImpl.readdir.bind(fsImpl) +export const unlink = fsImpl.unlink.bind(fsImpl) +export const stat = fsImpl.stat.bind(fsImpl) +export const access = fsImpl.access.bind(fsImpl) + +// Default export with promises namespace compatibility +export default { + readFile, + writeFile, + mkdir, + exists, + readdir, + unlink, + stat, + access +} + +// Named export for fs/promises compatibility +export const promises = { + readFile, + writeFile, + mkdir, + exists, + readdir, + unlink, + stat, + access +} \ No newline at end of file diff --git a/src/universal/index.ts b/src/universal/index.ts new file mode 100644 index 00000000..40eabccf --- /dev/null +++ b/src/universal/index.ts @@ -0,0 +1,28 @@ +/** + * Universal adapters for cross-environment compatibility + * Provides consistent APIs across Browser, Node.js, and Serverless environments + */ + +// UUID adapter +export * from './uuid.js' +export { default as uuid } from './uuid.js' + +// Crypto adapter +export * from './crypto.js' +export { default as crypto } from './crypto.js' + +// File system adapter +export * from './fs.js' +export { default as fs } from './fs.js' + +// Path adapter +export * from './path.js' +export { default as path } from './path.js' + +// Events adapter +export * from './events.js' +export { default as events } from './events.js' + +// Convenience re-exports for common patterns +export { v4 as uuidv4 } from './uuid.js' +export { EventEmitter } from './events.js' \ No newline at end of file diff --git a/src/universal/path.ts b/src/universal/path.ts new file mode 100644 index 00000000..e87675e7 --- /dev/null +++ b/src/universal/path.ts @@ -0,0 +1,187 @@ +/** + * Universal Path implementation + * Browser: Manual path operations + * Node.js: Uses built-in path module + */ + +import { isNode } from '../utils/environment.js' + +let nodePath: any = null + +// Dynamic import for Node.js path (only in Node.js environment) +if (isNode()) { + try { + nodePath = await import('path') + } catch { + // Ignore import errors in non-Node environments + } +} + +/** + * Universal path operations + */ +export function join(...paths: string[]): string { + if (nodePath) { + return nodePath.join(...paths) + } + + // Browser fallback implementation + const parts: string[] = [] + for (const path of paths) { + if (path) { + parts.push(...path.split('/').filter(p => p)) + } + } + return parts.join('/') +} + +export function dirname(path: string): string { + if (nodePath) { + return nodePath.dirname(path) + } + + // Browser fallback implementation + const parts = path.split('/').filter(p => p) + if (parts.length <= 1) return '.' + return parts.slice(0, -1).join('/') +} + +export function basename(path: string, ext?: string): string { + if (nodePath) { + return nodePath.basename(path, ext) + } + + // Browser fallback implementation + const parts = path.split('/') + let name = parts[parts.length - 1] + + if (ext && name.endsWith(ext)) { + name = name.slice(0, -ext.length) + } + + return name +} + +export function extname(path: string): string { + if (nodePath) { + return nodePath.extname(path) + } + + // Browser fallback implementation + const name = basename(path) + const lastDot = name.lastIndexOf('.') + return lastDot === -1 ? '' : name.slice(lastDot) +} + +export function resolve(...paths: string[]): string { + if (nodePath) { + return nodePath.resolve(...paths) + } + + // Browser fallback implementation + let resolved = '' + let resolvedAbsolute = false + + for (let i = paths.length - 1; i >= -1 && !resolvedAbsolute; i--) { + const path = i >= 0 ? paths[i] : '/' + + if (!path) continue + + resolved = path + '/' + resolved + resolvedAbsolute = path.charAt(0) === '/' + } + + // Normalize the path + resolved = normalizeArray(resolved.split('/').filter(p => p), !resolvedAbsolute).join('/') + + return (resolvedAbsolute ? '/' : '') + resolved +} + +export function relative(from: string, to: string): string { + if (nodePath) { + return nodePath.relative(from, to) + } + + // Browser fallback implementation + const fromParts = resolve(from).split('/').filter(p => p) + const toParts = resolve(to).split('/').filter(p => p) + + let commonLength = 0 + for (let i = 0; i < Math.min(fromParts.length, toParts.length); i++) { + if (fromParts[i] === toParts[i]) { + commonLength++ + } else { + break + } + } + + const upCount = fromParts.length - commonLength + const upParts = new Array(upCount).fill('..') + const downParts = toParts.slice(commonLength) + + return [...upParts, ...downParts].join('/') +} + +export function isAbsolute(path: string): boolean { + if (nodePath) { + return nodePath.isAbsolute(path) + } + + // Browser fallback implementation + return path.charAt(0) === '/' +} + +/** + * Normalize array helper function + */ +function normalizeArray(parts: string[], allowAboveRoot: boolean): string[] { + const res: string[] = [] + for (let i = 0; i < parts.length; i++) { + const p = parts[i] + + if (!p || p === '.') continue + + if (p === '..') { + if (res.length && res[res.length - 1] !== '..') { + res.pop() + } else if (allowAboveRoot) { + res.push('..') + } + } else { + res.push(p) + } + } + + return res +} + +// Path separator (always use forward slash for consistency) +export const sep = '/' +export const delimiter = ':' + +// POSIX path object for compatibility +export const posix = { + join, + dirname, + basename, + extname, + resolve, + relative, + isAbsolute, + sep: '/', + delimiter: ':' +} + +// Default export +export default { + join, + dirname, + basename, + extname, + resolve, + relative, + isAbsolute, + sep, + delimiter, + posix +} \ No newline at end of file diff --git a/src/universal/uuid.ts b/src/universal/uuid.ts new file mode 100644 index 00000000..2a252a0d --- /dev/null +++ b/src/universal/uuid.ts @@ -0,0 +1,26 @@ +/** + * Universal UUID implementation + * Works in all environments: Browser, Node.js, Serverless + */ + +import { isBrowser, isNode } from '../utils/environment.js' + +export function v4(): string { + // Use crypto.randomUUID if available (Node.js 19+, modern browsers) + if (typeof crypto !== 'undefined' && crypto.randomUUID) { + return crypto.randomUUID() + } + + // Fallback implementation for older environments + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => { + const r = Math.random() * 16 | 0 + const v = c === 'x' ? r : (r & 0x3 | 0x8) + return v.toString(16) + }) +} + +// Named export to match uuid package API +export { v4 as uuidv4 } + +// Default export for convenience +export default { v4 } \ No newline at end of file diff --git a/src/webhooks/webhookSystem.ts b/src/webhooks/webhookSystem.ts deleted file mode 100644 index 71182214..00000000 --- a/src/webhooks/webhookSystem.ts +++ /dev/null @@ -1,428 +0,0 @@ -/** - * Webhook System - Enterprise Event Notifications - * - * 🧠⚛️ Real-time notifications for augmentation events, data changes, and system alerts - * Critical for enterprise integrations and premium connectors - */ - -import { EventEmitter } from 'events' -import { BrainyDataInterface } from '../types/brainyDataInterface.js' - -export interface WebhookConfig { - url: string - events: WebhookEventType[] - headers?: Record - secret?: string - retryPolicy?: { - maxRetries: number - backoffMs: number - maxBackoffMs: number - } - filters?: { - augmentations?: string[] - metadata?: Record - } - enabled: boolean -} - -export type WebhookEventType = - | 'data.added' - | 'data.updated' - | 'data.deleted' - | 'augmentation.triggered' - | 'augmentation.completed' - | 'augmentation.failed' - | 'connector.sync.started' - | 'connector.sync.completed' - | 'connector.sync.failed' - | 'graph.relationship.created' - | 'graph.relationship.deleted' - | 'system.alert' - | 'license.expired' - | 'license.renewed' - -export interface WebhookPayload { - event: WebhookEventType - timestamp: string - data: any - metadata?: Record - brainyId?: string - augmentationId?: string - signature?: string -} - -export interface WebhookResponse { - success: boolean - statusCode?: number - error?: string - retryAfter?: number -} - -export class WebhookSystem extends EventEmitter { - private webhooks: Map = new Map() - private brainy: BrainyDataInterface - private retryQueues: Map = new Map() - private isRunning: boolean = false - - constructor(brainy: BrainyDataInterface) { - super() - this.brainy = brainy - this.setupEventListeners() - } - - /** - * Register a new webhook - */ - async registerWebhook(id: string, config: WebhookConfig): Promise { - // Validate URL - try { - new URL(config.url) - } catch { - throw new Error(`Invalid webhook URL: ${config.url}`) - } - - // Set default retry policy - if (!config.retryPolicy) { - config.retryPolicy = { - maxRetries: 3, - backoffMs: 1000, - maxBackoffMs: 30000 - } - } - - this.webhooks.set(id, config) - this.retryQueues.set(id, []) - - console.log(`🔔⚛️ Webhook registered: ${id} → ${config.url}`) - } - - /** - * Remove a webhook - */ - async unregisterWebhook(id: string): Promise { - this.webhooks.delete(id) - this.retryQueues.delete(id) - console.log(`🔔 Webhook unregistered: ${id}`) - } - - /** - * List all webhooks - */ - listWebhooks(): Array<{ id: string; config: WebhookConfig }> { - return Array.from(this.webhooks.entries()).map(([id, config]) => ({ - id, - config - })) - } - - /** - * Trigger webhook for an event - */ - async triggerWebhook(event: WebhookEventType, data: any, metadata?: Record): Promise { - const payload: WebhookPayload = { - event, - timestamp: new Date().toISOString(), - data, - metadata - } - - // Find webhooks subscribed to this event - for (const [id, config] of this.webhooks.entries()) { - if (!config.enabled) continue - if (!config.events.includes(event)) continue - - // Apply filters - if (config.filters) { - if (config.filters.augmentations && metadata?.augmentation) { - if (!config.filters.augmentations.includes(metadata.augmentation)) { - continue - } - } - - if (config.filters.metadata) { - let matchesFilter = true - for (const [key, value] of Object.entries(config.filters.metadata)) { - if (metadata?.[key] !== value) { - matchesFilter = false - break - } - } - if (!matchesFilter) continue - } - } - - // Sign payload if secret provided - if (config.secret) { - payload.signature = await this.signPayload(payload, config.secret) - } - - // Send webhook - this.sendWebhook(id, config, payload) - } - } - - /** - * Send webhook with retry logic - */ - private async sendWebhook( - id: string, - config: WebhookConfig, - payload: WebhookPayload, - retryCount: number = 0 - ): Promise { - try { - const response = await this.executeWebhook(config, payload) - - if (response.success) { - console.log(`✅ Webhook delivered: ${id} → ${config.url}`) - this.emit('webhook:delivered', { id, payload, response }) - } else { - throw new Error(response.error || `HTTP ${response.statusCode}`) - } - } catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error) - console.error(`❌ Webhook failed: ${id} → ${errorMessage}`) - - // Retry logic - if (retryCount < config.retryPolicy!.maxRetries) { - const backoff = Math.min( - config.retryPolicy!.backoffMs * Math.pow(2, retryCount), - config.retryPolicy!.maxBackoffMs - ) - - console.log(`🔄 Retrying webhook ${id} in ${backoff}ms (attempt ${retryCount + 1})`) - - setTimeout(() => { - this.sendWebhook(id, config, payload, retryCount + 1) - }, backoff) - } else { - console.error(`❌ Webhook ${id} failed after ${retryCount} retries`) - this.emit('webhook:failed', { id, payload, error: errorMessage }) - - // Add to dead letter queue - this.retryQueues.get(id)?.push({ payload, failedAt: new Date() }) - } - } - } - - /** - * Execute HTTP webhook call - */ - private async executeWebhook(config: WebhookConfig, payload: WebhookPayload): Promise { - try { - const controller = new AbortController() - const timeout = setTimeout(() => controller.abort(), 10000) // 10 second timeout - - const response = await fetch(config.url, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'X-Brainy-Event': payload.event, - 'X-Brainy-Signature': payload.signature || '', - ...config.headers - }, - body: JSON.stringify(payload), - signal: controller.signal - }) - - clearTimeout(timeout) - - return { - success: response.ok, - statusCode: response.status, - error: response.ok ? undefined : `HTTP ${response.status}` - } - } catch (error: any) { - return { - success: false, - error: error.message - } - } - } - - /** - * Sign webhook payload for security - */ - private async signPayload(payload: WebhookPayload, secret: string): Promise { - const encoder = new TextEncoder() - const data = encoder.encode(JSON.stringify(payload)) - const key = encoder.encode(secret) - - // Simple HMAC-like signature (in production, use proper crypto) - const signature = btoa(String.fromCharCode(...new Uint8Array(data))) - return signature - } - - /** - * Setup event listeners on Brainy - */ - private setupEventListeners(): void { - // Data events - this.brainy.on?.('data:added', (data) => { - this.triggerWebhook('data.added', data) - }) - - this.brainy.on?.('data:updated', (data) => { - this.triggerWebhook('data.updated', data) - }) - - this.brainy.on?.('data:deleted', (data) => { - this.triggerWebhook('data.deleted', data) - }) - - // Augmentation events - this.brainy.on?.('augmentation:triggered', (data) => { - this.triggerWebhook('augmentation.triggered', data, { - augmentation: data.augmentationId - }) - }) - - this.brainy.on?.('augmentation:completed', (data) => { - this.triggerWebhook('augmentation.completed', data, { - augmentation: data.augmentationId - }) - }) - - this.brainy.on?.('augmentation:failed', (data) => { - this.triggerWebhook('augmentation.failed', data, { - augmentation: data.augmentationId - }) - }) - - // Graph events - this.brainy.on?.('graph:relationship:created', (data) => { - this.triggerWebhook('graph.relationship.created', data) - }) - - this.brainy.on?.('graph:relationship:deleted', (data) => { - this.triggerWebhook('graph.relationship.deleted', data) - }) - } - - /** - * Test webhook configuration - */ - async testWebhook(id: string): Promise { - const config = this.webhooks.get(id) - if (!config) { - throw new Error(`Webhook ${id} not found`) - } - - const testPayload: WebhookPayload = { - event: 'system.alert', - timestamp: new Date().toISOString(), - data: { - message: 'Test webhook from Brainy', - test: true - }, - metadata: { - webhookId: id - } - } - - if (config.secret) { - testPayload.signature = await this.signPayload(testPayload, config.secret) - } - - const response = await this.executeWebhook(config, testPayload) - return response.success - } - - /** - * Retry failed webhooks - */ - async retryFailed(id: string): Promise { - const queue = this.retryQueues.get(id) - const config = this.webhooks.get(id) - - if (!queue || !config) { - return 0 - } - - const failed = [...queue] - this.retryQueues.set(id, []) - - let retried = 0 - for (const item of failed) { - await this.sendWebhook(id, config, item.payload) - retried++ - } - - return retried - } - - /** - * Get webhook statistics - */ - getStatistics(): { - total: number - enabled: number - failedQueues: Array<{ id: string; count: number }> - } { - const enabled = Array.from(this.webhooks.values()).filter(w => w.enabled).length - const failedQueues = Array.from(this.retryQueues.entries()).map(([id, queue]) => ({ - id, - count: queue.length - })) - - return { - total: this.webhooks.size, - enabled, - failedQueues - } - } -} - -/** - * Webhook builder for easy configuration - */ -export class WebhookBuilder { - private config: Partial = { - events: [], - enabled: true - } - - url(url: string): this { - this.config.url = url - return this - } - - events(...events: WebhookEventType[]): this { - this.config.events = events - return this - } - - headers(headers: Record): this { - this.config.headers = headers - return this - } - - secret(secret: string): this { - this.config.secret = secret - return this - } - - retry(maxRetries: number, backoffMs: number = 1000): this { - this.config.retryPolicy = { - maxRetries, - backoffMs, - maxBackoffMs: backoffMs * 10 - } - return this - } - - filter(filters: WebhookConfig['filters']): this { - this.config.filters = filters - return this - } - - build(): WebhookConfig { - if (!this.config.url) { - throw new Error('Webhook URL is required') - } - if (!this.config.events || this.config.events.length === 0) { - throw new Error('At least one event type is required') - } - return this.config as WebhookConfig - } -} \ No newline at end of file diff --git a/tsconfig.browser.json b/tsconfig.browser.json index 18b0ace3..898ddc43 100644 --- a/tsconfig.browser.json +++ b/tsconfig.browser.json @@ -1,31 +1,34 @@ { + "extends": "./tsconfig.json", "compilerOptions": { - "target": "ES2020", - "module": "ESNext", - "moduleResolution": "Bundler", - "esModuleInterop": true, - "strict": true, - "declaration": false, - "outDir": "./dist", - "rootDir": ".", - "lib": [ - "DOM", - "ESNext", - "DOM.Asynciterable" - ], - "skipLibCheck": true, - "forceConsistentCasingInFileNames": true, - "noEmit": false, - "preserveConstEnums": true, - "sourceMap": true + "outDir": "./dist/browser-build" }, "include": [ - "src/**/*", - "demo/**/*" + "src/brainyData.ts", + "src/browserFramework.ts", + "src/universal/**/*", + "src/types/graphTypes.ts", + "src/coreTypes.ts", + "src/utils/embedding.ts", + "src/utils/environment.ts", + "src/utils/logger.ts", + "src/utils/index.ts", + "src/hnsw/**/*", + "src/storage/**/*", + "src/setup.ts" ], "exclude": [ "node_modules", "dist", - "**/*.test.ts" + "**/*.test.ts", + "src/cortex/**/*", + "src/augmentations/**/*", + "src/pipeline.ts", + "src/sequentialPipeline.ts", + "src/augmentationFactory.ts", + "src/augmentationRegistry.ts", + "src/augmentationRegistryLoader.ts", + "src/chat/**/*", + "src/mcp/**/*" ] -} +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 10672725..c85dd523 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -26,6 +26,9 @@ "exclude": [ "node_modules", "dist", - "**/*.test.ts" + "**/*.test.ts", + "src/cortex/**/*", + "src/augmentations/cortexSense.ts", + "src/augmentations/llmAugmentations.ts" ] } diff --git a/vite.browser.config.ts b/vite.browser.config.ts new file mode 100644 index 00000000..0df71b0a --- /dev/null +++ b/vite.browser.config.ts @@ -0,0 +1,40 @@ +import { defineConfig } from 'vite' +import { resolve } from 'path' + +export default defineConfig({ + build: { + target: 'es2022', + lib: { + entry: resolve(__dirname, 'dist/browser-build/browserFramework.js'), + name: 'Brainy', + fileName: 'brainy-browser-bundle', + formats: ['es', 'umd'] + }, + rollupOptions: { + output: { + dir: 'dist/browser', + entryFileNames: '[name].js', + chunkFileNames: '[name].js' + } + }, + sourcemap: true, + minify: false, // Keep unminified for debugging + outDir: 'dist/browser' + }, + define: { + 'process.env.NODE_ENV': JSON.stringify('production'), + global: 'globalThis', + 'process.env': '{}' + }, + optimizeDeps: { + include: ['@huggingface/transformers', 'uuid', 'crypto-browserify', 'buffer'] + }, + resolve: { + alias: { + crypto: 'crypto-browserify', + buffer: 'buffer', + process: 'process/browser', + util: 'util' + } + } +}) \ No newline at end of file