import { Hono } from "hono"; /** * The contract every rSpace module must implement. * * A module is a self-contained feature area (books, pubs, cart, canvas, etc.) * that exposes Hono routes and metadata. The shell mounts these routes under * `/:space/{moduleId}` in unified mode. In standalone mode, the module's own * `standalone.ts` mounts them at the root with a minimal shell. */ export interface RSpaceModule { /** Short identifier used in URLs: 'books', 'pubs', 'cart', 'canvas', etc. */ id: string; /** Human-readable name: 'rBooks', 'rPubs', 'rCart', etc. */ name: string; /** Emoji or SVG string for the app switcher */ icon: string; /** One-line description */ description: string; /** Mountable Hono sub-app. Routes are relative to the mount point. */ routes: Hono; /** Optional: standalone domain for this module (e.g. 'rbooks.online') */ standaloneDomain?: string; /** Called when a new space is created (e.g. to initialize module-specific data) */ onSpaceCreate?: (spaceSlug: string) => Promise; /** Called when a space is deleted (e.g. to clean up module-specific data) */ onSpaceDelete?: (spaceSlug: string) => Promise; } /** Registry of all loaded modules */ const modules = new Map(); export function registerModule(mod: RSpaceModule): void { modules.set(mod.id, mod); } export function getModule(id: string): RSpaceModule | undefined { return modules.get(id); } export function getAllModules(): RSpaceModule[] { return Array.from(modules.values()); } /** Metadata exposed to the client for the app switcher */ export interface ModuleInfo { id: string; name: string; icon: string; description: string; } export function getModuleInfoList(): ModuleInfo[] { return getAllModules().map((m) => ({ id: m.id, name: m.name, icon: m.icon, description: m.description, })); }