92 lines
2.9 KiB
TypeScript
92 lines
2.9 KiB
TypeScript
import { Hono } from "hono";
|
|
import type { FlowKind } from "../lib/layer-types";
|
|
|
|
/**
|
|
* Feed definition — describes a data feed that a module can expose to other layers.
|
|
* Feeds are the connective tissue between layers: they let data, economic value,
|
|
* trust signals, etc. flow between rApps.
|
|
*/
|
|
export interface FeedDefinition {
|
|
/** Feed identifier (unique within the module) */
|
|
id: string;
|
|
/** Human-readable name */
|
|
name: string;
|
|
/** What kind of flow this feed carries */
|
|
kind: FlowKind;
|
|
/** Description of what this feed provides */
|
|
description: string;
|
|
/** Shape types this feed emits (e.g. "folk-note", "folk-token-mint") */
|
|
emits?: string[];
|
|
/** Whether this feed supports filtering */
|
|
filterable?: boolean;
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
/** Feeds this module exposes to other layers */
|
|
feeds?: FeedDefinition[];
|
|
/** Feed kinds this module can consume from other layers */
|
|
acceptsFeeds?: FlowKind[];
|
|
/** Called when a new space is created (e.g. to initialize module-specific data) */
|
|
onSpaceCreate?: (spaceSlug: string) => Promise<void>;
|
|
/** Called when a space is deleted (e.g. to clean up module-specific data) */
|
|
onSpaceDelete?: (spaceSlug: string) => Promise<void>;
|
|
}
|
|
|
|
/** Registry of all loaded modules */
|
|
const modules = new Map<string, RSpaceModule>();
|
|
|
|
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 and tab bar */
|
|
export interface ModuleInfo {
|
|
id: string;
|
|
name: string;
|
|
icon: string;
|
|
description: string;
|
|
standaloneDomain?: string;
|
|
feeds?: FeedDefinition[];
|
|
acceptsFeeds?: FlowKind[];
|
|
}
|
|
|
|
export function getModuleInfoList(): ModuleInfo[] {
|
|
return getAllModules().map((m) => ({
|
|
id: m.id,
|
|
name: m.name,
|
|
icon: m.icon,
|
|
description: m.description,
|
|
...(m.standaloneDomain ? { standaloneDomain: m.standaloneDomain } : {}),
|
|
...(m.feeds ? { feeds: m.feeds } : {}),
|
|
...(m.acceptsFeeds ? { acceptsFeeds: m.acceptsFeeds } : {}),
|
|
}));
|
|
}
|