#!/usr/bin/env node const fs = require('fs'); const path = require('path'); const { execSync } = require('child_process'); // Configuration const CRATES = ['graph_reasoner', 'extractors', 'planner']; const OUTPUT_DIR = path.join(__dirname, 'wasm-dist'); const BUNDLE_NAME = 'psycho-symbolic-reasoner'; console.log('๐Ÿ“ฆ Bundling WASM modules...\n'); // Ensure output directory exists function ensureOutputDir() { if (!fs.existsSync(OUTPUT_DIR)) { fs.mkdirSync(OUTPUT_DIR, { recursive: true }); console.log(`๐Ÿ“ Created output directory: ${OUTPUT_DIR}`); } } // Copy WASM files and create unified exports function bundleWasmFiles() { console.log('๐Ÿ”„ Copying WASM files...'); const bundledFiles = { js: [], wasm: [], dts: [] }; for (const crate of CRATES) { const pkgPath = path.join(__dirname, crate, 'pkg'); if (!fs.existsSync(pkgPath)) { console.error(`โŒ Package not found: ${pkgPath}`); continue; } // Copy JavaScript file const jsFile = `${crate}.js`; const jsSource = path.join(pkgPath, jsFile); const jsTarget = path.join(OUTPUT_DIR, jsFile); if (fs.existsSync(jsSource)) { fs.copyFileSync(jsSource, jsTarget); bundledFiles.js.push(jsFile); console.log(` โœ… Copied ${jsFile}`); } // Copy WASM file const wasmFile = `${crate}_bg.wasm`; const wasmSource = path.join(pkgPath, wasmFile); const wasmTarget = path.join(OUTPUT_DIR, wasmFile); if (fs.existsSync(wasmSource)) { fs.copyFileSync(wasmSource, wasmTarget); bundledFiles.wasm.push(wasmFile); console.log(` โœ… Copied ${wasmFile}`); } // Copy TypeScript definitions const dtsFile = `${crate}.d.ts`; const dtsSource = path.join(pkgPath, dtsFile); const dtsTarget = path.join(OUTPUT_DIR, dtsFile); if (fs.existsSync(dtsSource)) { fs.copyFileSync(dtsSource, dtsTarget); bundledFiles.dts.push(dtsFile); console.log(` โœ… Copied ${dtsFile}`); } } return bundledFiles; } // Create unified JavaScript entry point function createUnifiedEntry(bundledFiles) { console.log('๐Ÿ”ง Creating unified entry point...'); const entryContent = ` // Psycho-Symbolic Reasoner - Unified WASM Entry Point // Auto-generated on ${new Date().toISOString()} // Import all WASM modules ${CRATES.map(crate => `import * as ${toCamelCase(crate)}Module from './${crate}.js';` ).join('\n')} // Initialize function for browser environments async function initWasm() { // This will be called automatically in most environments console.log('๐Ÿš€ Initializing Psycho-Symbolic Reasoner WASM modules...'); } // Unified API export class PsychoSymbolicReasoner { constructor() { this.graphReasoner = new ${toCamelCase('graph_reasoner')}Module.GraphReasoner(); this.textExtractor = new ${toCamelCase('extractors')}Module.TextExtractor(); this.plannerSystem = new ${toCamelCase('planner')}Module.PlannerSystem(); } // Graph reasoning methods addFact(subject, predicate, object) { return this.graphReasoner.add_fact(subject, predicate, object); } addRule(ruleJson) { return this.graphReasoner.add_rule(ruleJson); } query(queryJson) { return this.graphReasoner.query(queryJson); } infer(maxIterations = 10) { return this.graphReasoner.infer(maxIterations); } getGraphStats() { return this.graphReasoner.get_graph_stats(); } // Text extraction methods analyzeSentiment(text) { return this.textExtractor.analyze_sentiment(text); } extractPreferences(text) { return this.textExtractor.extract_preferences(text); } detectEmotions(text) { return this.textExtractor.detect_emotions(text); } analyzeText(text) { return this.textExtractor.analyze_all(text); } // Planning methods setState(key, value) { return this.plannerSystem.set_state(key, value); } getState(key) { return this.plannerSystem.get_state(key); } addAction(actionJson) { return this.plannerSystem.add_action(actionJson); } addGoal(goalJson) { return this.plannerSystem.add_goal(goalJson); } plan(goalId) { return this.plannerSystem.plan(goalId); } planToState(targetStateJson) { return this.plannerSystem.plan_to_state(targetStateJson); } executePlan(planJson) { return this.plannerSystem.execute_plan(planJson); } evaluateRules() { return this.plannerSystem.evaluate_rules(); } getWorldState() { return this.plannerSystem.get_world_state(); } getAvailableActions() { return this.plannerSystem.get_available_actions(); } // Utility methods version() { return '0.1.0'; } capabilities() { return { graphReasoning: true, textExtraction: true, planning: true, inference: true, emotionDetection: true, sentimentAnalysis: true, preferenceExtraction: true }; } } // Export individual modules for advanced usage export { ${CRATES.map(crate => `${toCamelCase(crate)}Module`).join(',\n ')} }; // Export main class as default export default PsychoSymbolicReasoner; // Initialize WASM (called automatically) initWasm(); `; fs.writeFileSync(path.join(OUTPUT_DIR, 'index.js'), entryContent); console.log(' โœ… Created index.js'); } // Create unified TypeScript definitions function createUnifiedTypes(bundledFiles) { console.log('๐Ÿ“ Creating unified TypeScript definitions...'); const typesContent = ` // Psycho-Symbolic Reasoner - Unified TypeScript Definitions // Auto-generated on ${new Date().toISOString()} // Import individual module types ${CRATES.map(crate => `export * from './${crate}';` ).join('\n')} // Main unified class interface export declare class PsychoSymbolicReasoner { constructor(); // Graph reasoning methods addFact(subject: string, predicate: string, object: string): string; addRule(ruleJson: string): boolean; query(queryJson: string): string; infer(maxIterations?: number): string; getGraphStats(): string; // Text extraction methods analyzeSentiment(text: string): string; extractPreferences(text: string): string; detectEmotions(text: string): string; analyzeText(text: string): string; // Planning methods setState(key: string, value: string): boolean; getState(key: string): string; addAction(actionJson: string): boolean; addGoal(goalJson: string): boolean; plan(goalId: string): string; planToState(targetStateJson: string): string; executePlan(planJson: string): string; evaluateRules(): string; getWorldState(): string; getAvailableActions(): string; // Utility methods version(): string; capabilities(): { graphReasoning: boolean; textExtraction: boolean; planning: boolean; inference: boolean; emotionDetection: boolean; sentimentAnalysis: boolean; preferenceExtraction: boolean; }; } export default PsychoSymbolicReasoner; `; fs.writeFileSync(path.join(OUTPUT_DIR, 'index.d.ts'), typesContent); console.log(' โœ… Created index.d.ts'); } // Create package.json for the bundle function createBundlePackageJson() { console.log('๐Ÿ“ฆ Creating bundle package.json...'); const packageJson = { name: "@psycho-symbolic/reasoner", version: "0.1.0", description: "Complete WASM bindings for psycho-symbolic reasoning system", main: "index.js", types: "index.d.ts", exports: { ".": { "import": "./index.js", "require": "./index.js", "types": "./index.d.ts" }, "./graph": "./graph_reasoner.js", "./extractors": "./extractors.js", "./planner": "./planner.js" }, files: [ "*.js", "*.wasm", "*.d.ts", "README.md" ], scripts: { "test": "node test-bundle.js" }, keywords: [ "wasm", "webassembly", "rust", "ai", "reasoning", "psycho-symbolic", "graph", "nlp", "planning", "inference", "sentiment", "emotion" ], author: "Psycho-Symbolic AI Team", license: "MIT", engines: { "node": ">=14.0.0" }, repository: { type: "git", url: "https://github.com/your-org/sublinear-time-solver.git", directory: "psycho-symbolic-reasoner" }, dependencies: {}, devDependencies: {} }; fs.writeFileSync( path.join(OUTPUT_DIR, 'package.json'), JSON.stringify(packageJson, null, 2) ); console.log(' โœ… Created package.json'); } // Create README for the bundle function createBundleReadme() { console.log('๐Ÿ“š Creating bundle README...'); const readmeContent = ` # Psycho-Symbolic Reasoner WASM Complete WebAssembly bindings for the Psycho-Symbolic Reasoning System. ## Installation \`\`\`bash npm install @psycho-symbolic/reasoner \`\`\` ## Usage ### Basic Usage \`\`\`javascript import PsychoSymbolicReasoner from '@psycho-symbolic/reasoner'; const reasoner = new PsychoSymbolicReasoner(); // Add facts to the knowledge graph reasoner.addFact("Alice", "knows", "Bob"); reasoner.addFact("Bob", "likes", "coffee"); // Analyze text const sentiment = reasoner.analyzeSentiment("I love this product!"); const emotions = reasoner.detectEmotions("I'm so excited!"); // Plan actions reasoner.setState("has_key", '{"type": "boolean", "value": true}'); const plan = reasoner.plan("unlock_door"); \`\`\` ### Advanced Usage \`\`\`javascript import { graphReasonerModule, extractorsModule, plannerModule } from '@psycho-symbolic/reasoner'; // Use individual modules const graphReasoner = new graphReasonerModule.GraphReasoner(); const textExtractor = new extractorsModule.TextExtractor(); const plannerSystem = new plannerModule.PlannerSystem(); \`\`\` ## Features - **Graph Reasoning**: Knowledge graph construction and inference - **Text Analysis**: Sentiment analysis, emotion detection, preference extraction - **Planning**: Goal-oriented action planning with GOAP algorithm - **Inference**: Rule-based reasoning and knowledge discovery - **TypeScript Support**: Full type definitions included ## API Reference ### PsychoSymbolicReasoner #### Graph Reasoning - \`addFact(subject, predicate, object)\` - Add a fact to the knowledge graph - \`addRule(ruleJson)\` - Add an inference rule - \`query(queryJson)\` - Query the knowledge graph - \`infer(maxIterations)\` - Run inference engine - \`getGraphStats()\` - Get graph statistics #### Text Analysis - \`analyzeSentiment(text)\` - Analyze sentiment of text - \`extractPreferences(text)\` - Extract user preferences - \`detectEmotions(text)\` - Detect emotions in text - \`analyzeText(text)\` - Complete text analysis #### Planning - \`setState(key, value)\` - Set world state - \`getState(key)\` - Get world state - \`addAction(actionJson)\` - Add available action - \`addGoal(goalJson)\` - Add planning goal - \`plan(goalId)\` - Generate action plan - \`executePlan(planJson)\` - Execute action plan ## License MIT `; fs.writeFileSync(path.join(OUTPUT_DIR, 'README.md'), readmeContent.trim()); console.log(' โœ… Created README.md'); } // Create bundle test function createBundleTest() { console.log('๐Ÿงช Creating bundle test...'); const testContent = ` const PsychoSymbolicReasoner = require('./index.js').default; async function testBundle() { console.log('๐Ÿงช Testing unified WASM bundle...'); try { const reasoner = new PsychoSymbolicReasoner(); // Test capabilities const capabilities = reasoner.capabilities(); console.log('โœ… Capabilities:', capabilities); // Test version const version = reasoner.version(); console.log('โœ… Version:', version); // Test basic functionality const factId = reasoner.addFact("test", "type", "demo"); console.log('โœ… Added fact:', factId); const sentiment = reasoner.analyzeSentiment("This is great!"); console.log('โœ… Sentiment analysis:', JSON.parse(sentiment)); const stateSet = reasoner.setState("test_state", '{"type": "boolean", "value": true}'); console.log('โœ… State set:', stateSet); console.log('๐ŸŽ‰ Bundle test completed successfully!'); } catch (error) { console.error('โŒ Bundle test failed:', error); process.exit(1); } } if (require.main === module) { testBundle(); } `; fs.writeFileSync(path.join(OUTPUT_DIR, 'test-bundle.js'), testContent); console.log(' โœ… Created test-bundle.js'); } // Utility function to convert snake_case to camelCase function toCamelCase(str) { return str.replace(/_([a-z])/g, (match, letter) => letter.toUpperCase()); } // Create optimized production bundle with esbuild function createProductionBundle() { console.log('โšก Creating optimized production bundle...'); try { // Check if esbuild is available execSync('npx esbuild --version', { stdio: 'pipe' }); // Bundle for different targets const targets = [ { name: 'esm', format: 'esm', ext: 'mjs' }, { name: 'cjs', format: 'cjs', ext: 'cjs' }, { name: 'iife', format: 'iife', ext: 'bundle.js' } ]; for (const target of targets) { const outputFile = path.join(OUTPUT_DIR, `index.${target.ext}`); const buildCommand = `npx esbuild ${path.join(OUTPUT_DIR, 'index.js')} --bundle --format=${target.format} --outfile=${outputFile} --minify --sourcemap`; try { execSync(buildCommand, { stdio: 'pipe' }); console.log(` โœ… Created ${target.name} bundle: index.${target.ext}`); } catch (error) { console.warn(` โš ๏ธ Could not create ${target.name} bundle:`, error.message); } } } catch (error) { console.warn('โš ๏ธ esbuild not available, skipping production bundles'); } } // Main bundling function function main() { ensureOutputDir(); const bundledFiles = bundleWasmFiles(); createUnifiedEntry(bundledFiles); createUnifiedTypes(bundledFiles); createBundlePackageJson(); createBundleReadme(); createBundleTest(); createProductionBundle(); console.log('\n๐ŸŽ‰ WASM bundling completed successfully!'); console.log(`๐Ÿ“ Bundle location: ${OUTPUT_DIR}`); console.log('\n๐Ÿ“‹ Next steps:'); console.log(' 1. Test bundle: cd wasm-dist && node test-bundle.js'); console.log(' 2. Install as dependency: npm install ./wasm-dist'); console.log(' 3. Publish to npm: cd wasm-dist && npm publish'); } // Run if called directly if (require.main === module) { main(); } module.exports = { bundleWasmFiles, createUnifiedEntry, createUnifiedTypes, OUTPUT_DIR };