import React from 'react' import type { LucideIcon } from 'lucide-react' import type { FileItem } from '@/lib/api' import type { TOCItem } from '@/lib/utils' import type { FrontendModuleConfig, InternalModuleConfig, ModuleEndpoints } from '@shared/types/module' import type { ModuleDefinition } from '@shared/modules/types' import { moduleApiRegistry } from './api/modules/ModuleApiRegistry' import { createModuleApi, ModuleApiInstance } from './api/createModuleApi' export type { FrontendModuleConfig, InternalModuleConfig } export interface FrontendModule extends InternalModuleConfig { match: (file: FileItem) => boolean render: (file: FileItem, isActive: boolean, onTocUpdated?: (toc: TOCItem[]) => void) => React.ReactNode } const createFileItem = (name: string, path: string): FileItem => ({ name, path, type: 'file', size: 0, modified: new Date().toISOString(), }) class ModuleRegistryImpl { private modules = new Map() register(config: FrontendModuleConfig): void { if (this.modules.has(config.id)) { throw new Error(`Module with ID '${config.id}' is already registered`) } const tabId = `${config.id}-tab` const fileItem = createFileItem(config.name, tabId) const internalConfig: InternalModuleConfig = { ...config, tabId, fileItem, } this.modules.set(config.id, internalConfig) console.log(`[ModuleRegistry] Registering module: ${config.id}, basePath: ${config.basePath}, hasEndpoints: ${!!config.endpoints}, endpoints:`, config.endpoints) if (config.endpoints && config.basePath) { const apiInstance = createModuleApi({ id: config.id, name: config.name, basePath: config.basePath, order: config.order, endpoints: config.endpoints, }) moduleApiRegistry.register(apiInstance) console.log(`[ModuleRegistry] API registered for: ${config.id}`) } else { console.warn(`[ModuleRegistry] Skipping API registration for ${config.id}: endpoints=${!!config.endpoints}, basePath=${config.basePath}`) } } get(id: string): InternalModuleConfig | undefined { return this.modules.get(id) } getAll(): InternalModuleConfig[] { return Array.from(this.modules.values()) .sort((a, b) => a.order - b.order) } has(id: string): boolean { return this.modules.has(id) } getFileItem(id: string): FileItem | undefined { return this.modules.get(id)?.fileItem } getTabId(id: string): string | undefined { return this.modules.get(id)?.tabId } getDefault(): InternalModuleConfig | undefined { const all = this.getAll() return all.length > 0 ? all[0] : undefined } } export const registry = new ModuleRegistryImpl() export function defineModule< TEndpoints extends ModuleEndpoints >(config: FrontendModuleConfig): FrontendModuleConfig { return config } export interface FrontendModuleOptions< TEndpoints extends ModuleEndpoints = ModuleEndpoints > { icon: LucideIcon component: React.ComponentType } export function createFrontendModule< TEndpoints extends ModuleEndpoints >( definition: ModuleDefinition, options: FrontendModuleOptions ): FrontendModuleConfig { return { id: definition.id, name: definition.name, order: definition.order, basePath: definition.basePath, endpoints: definition.endpoints, icon: options.icon, component: options.component, } } export function getModuleLegacy(file: FileItem): InternalModuleConfig | undefined { return registry.getAll().find(m => m.fileItem?.path === file.path) } export function matchModule(file: FileItem): InternalModuleConfig | undefined { return getModuleLegacy(file) } export const getAllModules = (): InternalModuleConfig[] => { return registry.getAll() } export const getFileItem = (moduleId: string): FileItem | undefined => { return registry.getFileItem(moduleId) } export const getModuleFileItem = getFileItem export const getModuleTabId = (moduleId: string): string | undefined => { return registry.getTabId(moduleId) } export const getModule = (moduleId: string): InternalModuleConfig | undefined => { return registry.get(moduleId) } export const getDefaultModule = (): InternalModuleConfig | undefined => { return registry.getDefault() } export function getModuleApi( moduleId: string ): ModuleApiInstance | undefined { const api = moduleApiRegistry.get>(moduleId) if (!api) { console.error(`[getModuleApi] Failed to get API for module: ${moduleId}. Available APIs:`, Array.from((moduleApiRegistry as any).apis?.keys?.() || [])) } return api }