Files
ai.interactive.fiction/public/js/module-registry.js
T

132 lines
4.1 KiB
JavaScript

/**
* Module Registry
* Manages module registration and dependency tracking
*/
export class ModuleRegistry {
constructor() {
this.modules = {};
this.readyPromises = {};
this.moduleDependencies = new Map(); // Track module dependencies
}
/**
* Register a module
* @param {BaseModule} module - Module to register
* @param {Array<string>} [dependencies] - Optional array of module dependencies
*/
register(module, dependencies = null) {
if (!module || !module.id) {
console.error('Invalid module - must have an id property');
return;
}
const alreadyRegistered = this.modules[module.id] === module;
// Store the module
this.modules[module.id] = module;
// Store dependencies if provided, otherwise use module.dependencies
if (dependencies) {
this.moduleDependencies.set(module.id, dependencies);
// Mirror explicit dependencies onto the module instance.
if (module.dependencies === undefined) {
module.dependencies = [...dependencies];
}
} else if (module.dependencies) {
// Use the module's own dependencies property
this.moduleDependencies.set(module.id, [...module.dependencies]);
} else {
// No dependencies
this.moduleDependencies.set(module.id, []);
}
if (alreadyRegistered && this.readyPromises[module.id]) {
return;
}
// Create a promise that will resolve when this module is ready
this.readyPromises[module.id] = new Promise((resolve) => {
// Set up a state change listener for this module
document.addEventListener('module:stateChange', (event) => {
if (event.detail.moduleId === module.id &&
(event.detail.state === 'FINISHED' || event.detail.state === 'ERROR')) {
resolve(event.detail.state === 'FINISHED');
}
});
// Check if already in finished state
if (module.state === 'FINISHED') {
resolve(true);
} else if (module.state === 'ERROR') {
resolve(false);
}
});
}
/**
* Get a module by id
* @param {string} id - Module id
* @returns {BaseModule} - The module, or null if not found
*/
getModule(id) {
return this.modules[id] || null;
}
/**
* Get all registered modules
* @returns {Object} - Map of modules
*/
getAllModules() {
return this.modules;
}
/**
* Get dependencies for a module
* @param {string} id - Module id
* @returns {Array<string>} - Array of dependencies
*/
getDependencies(id) {
return this.moduleDependencies.get(id) || [];
}
/**
* Wait for a module to be ready (in FINISHED state)
* @param {string} id - Module id to wait for
* @param {number} timeout - Optional timeout in ms
* @returns {Promise} - Resolves when the module is ready
*/
waitForModule(id, timeout = null) {
if (!this.readyPromises[id]) {
return Promise.resolve(false);
}
if (timeout) {
// Add timeout logic
return Promise.race([
this.readyPromises[id],
new Promise(resolve => setTimeout(() => resolve(false), timeout))
]);
}
return this.readyPromises[id];
}
/**
* Wait for multiple modules to be ready
* @param {Array<string>} ids - Array of module ids to wait for
* @param {number} timeout - Optional timeout in ms
* @returns {Promise} - Resolves when all modules are ready
*/
waitForModules(ids, timeout = null) {
const promises = ids.map(id => this.waitForModule(id, timeout));
return Promise.all(promises);
}
}
// Create and export a singleton instance
export const moduleRegistry = new ModuleRegistry();
// Make registry accessible globally
window.moduleRegistry = moduleRegistry;