ixp-server
Version:
A comprehensive SDK for building Intent Exchange Protocol (IXP) servers with ease
217 lines • 6.5 kB
JavaScript
import fs from 'fs';
import path from 'path';
import { watch } from 'chokidar';
/**
* Intent Registry - Manages intent definitions and their lifecycle
*/
export class IntentRegistry {
constructor(config) {
this.intents = new Map();
this.listeners = new Set();
if (typeof config === 'string') {
this.configPath = config;
this.loadFromFile(config);
}
else if (Array.isArray(config)) {
this.loadFromArray(config);
}
}
/**
* Load intents from file path
*/
loadFromFile(configPath) {
try {
const resolvedPath = path.resolve(configPath);
if (!fs.existsSync(resolvedPath)) {
throw new Error(`Intent configuration file not found: ${resolvedPath}`);
}
const fileContent = fs.readFileSync(resolvedPath, 'utf8');
const data = JSON.parse(fileContent);
if (!data.intents || !Array.isArray(data.intents)) {
throw new Error('Invalid intent configuration: missing or invalid "intents" array');
}
this.loadFromArray(data.intents);
console.log(`✅ Loaded ${this.intents.size} intents from ${configPath}`);
}
catch (error) {
console.error('❌ Failed to load intents:', error);
throw error;
}
}
/**
* Load intents from array
*/
loadFromArray(intents) {
this.intents.clear();
for (const intent of intents) {
this.validateIntent(intent);
this.intents.set(intent.name, intent);
}
}
/**
* Validate intent definition
*/
validateIntent(intent) {
if (!intent.name || typeof intent.name !== 'string') {
throw new Error('Intent must have a valid name');
}
if (!intent.description || typeof intent.description !== 'string') {
throw new Error(`Intent "${intent.name}" must have a description`);
}
if (!intent.component || typeof intent.component !== 'string') {
throw new Error(`Intent "${intent.name}" must specify a component`);
}
if (!intent.parameters || typeof intent.parameters !== 'object') {
throw new Error(`Intent "${intent.name}" must have parameters definition`);
}
if (intent.parameters.type !== 'object') {
throw new Error(`Intent "${intent.name}" parameters must be of type "object"`);
}
if (!intent.version || typeof intent.version !== 'string') {
throw new Error(`Intent "${intent.name}" must have a version`);
}
}
/**
* Get all intents
*/
getAll() {
return Array.from(this.intents.values());
}
/**
* Get intent by name
*/
get(name) {
return this.intents.get(name);
}
/**
* Add new intent
*/
add(intent) {
this.validateIntent(intent);
this.intents.set(intent.name, intent);
this.notifyListeners();
}
/**
* Remove intent by name
*/
remove(name) {
const removed = this.intents.delete(name);
if (removed) {
this.notifyListeners();
}
return removed;
}
/**
* Reload intents from file
*/
async reload() {
if (this.configPath) {
this.loadFromFile(this.configPath);
this.notifyListeners();
}
}
/**
* Enable file watching for hot reload
*/
enableFileWatching() {
if (!this.configPath) {
console.warn('Cannot enable file watching: no config path specified');
return;
}
if (this.watcher) {
console.warn('File watching is already enabled');
return;
}
this.watcher = watch(this.configPath, {
persistent: true,
ignoreInitial: true
});
this.watcher.on('change', async () => {
console.log('📁 Intent configuration changed, reloading...');
try {
await this.reload();
console.log('✅ Intents reloaded successfully');
}
catch (error) {
console.error('❌ Failed to reload intents:', error);
}
});
console.log('👀 File watching enabled for intent configuration');
}
/**
* Disable file watching
*/
disableFileWatching() {
if (this.watcher) {
this.watcher.close();
this.watcher = undefined;
console.log('👁️ File watching disabled');
}
}
/**
* Add change listener
*/
onChange(listener) {
this.listeners.add(listener);
return () => this.listeners.delete(listener);
}
/**
* Notify all listeners of changes
*/
notifyListeners() {
this.listeners.forEach(listener => {
try {
listener();
}
catch (error) {
console.error('Error in intent registry listener:', error);
}
});
}
/**
* Get intents by criteria
*/
findByCriteria(criteria) {
return this.getAll().filter(intent => {
if (criteria.crawlable !== undefined && intent.crawlable !== criteria.crawlable) {
return false;
}
if (criteria.deprecated !== undefined && intent.deprecated !== criteria.deprecated) {
return false;
}
if (criteria.component && intent.component !== criteria.component) {
return false;
}
return true;
});
}
/**
* Get registry statistics
*/
getStats() {
const intents = this.getAll();
const stats = {
total: intents.length,
crawlable: 0,
deprecated: 0,
byComponent: {}
};
for (const intent of intents) {
if (intent.crawlable)
stats.crawlable++;
if (intent.deprecated)
stats.deprecated++;
stats.byComponent[intent.component] = (stats.byComponent[intent.component] || 0) + 1;
}
return stats;
}
/**
* Cleanup resources
*/
destroy() {
this.disableFileWatching();
this.listeners.clear();
this.intents.clear();
}
}
//# sourceMappingURL=IntentRegistry.js.map