Initial commit
This commit is contained in:
158
src/lib/module-registry.ts
Normal file
158
src/lib/module-registry.ts
Normal file
@@ -0,0 +1,158 @@
|
||||
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<string, InternalModuleConfig>()
|
||||
|
||||
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<TEndpoints>): FrontendModuleConfig<TEndpoints> {
|
||||
return config
|
||||
}
|
||||
|
||||
export interface FrontendModuleOptions<
|
||||
TEndpoints extends ModuleEndpoints = ModuleEndpoints
|
||||
> {
|
||||
icon: LucideIcon
|
||||
component: React.ComponentType
|
||||
}
|
||||
|
||||
export function createFrontendModule<
|
||||
TEndpoints extends ModuleEndpoints
|
||||
>(
|
||||
definition: ModuleDefinition<string, TEndpoints>,
|
||||
options: FrontendModuleOptions<TEndpoints>
|
||||
): FrontendModuleConfig<TEndpoints> {
|
||||
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<TEndpoints extends ModuleEndpoints>(
|
||||
moduleId: string
|
||||
): ModuleApiInstance<TEndpoints> | undefined {
|
||||
const api = moduleApiRegistry.get<ModuleApiInstance<TEndpoints>>(moduleId)
|
||||
if (!api) {
|
||||
console.error(`[getModuleApi] Failed to get API for module: ${moduleId}. Available APIs:`, Array.from((moduleApiRegistry as any).apis?.keys?.() || []))
|
||||
}
|
||||
return api
|
||||
}
|
||||
Reference in New Issue
Block a user