/** * View routing: routes that plugins contribute, and the resolvers that decide * which component displays a store-fs node. * * The types are framework-neutral: a component is whatever the host renders * (`C`, a React `ComponentType` for `@hamak/ui-shell/react`). */ import type { FileNode } from '@hamak/shared-utils'; /** * A route a plugin contributes. * * `RouteContribution` is what plugins add to the {@link IRouteRegistry}, for a * host whose own router (e.g. react-router) renders them. `RouteConfig` is the * route table of the shell's built-in `DefaultRouter` (`ROUTER_TOKEN`), with * lazy components and guards. A host using `DefaultRouter` can map * contributions into it; a host with its own router reads the registry. */ export interface RouteContribution { /** Router path pattern, e.g. `/content/*`. Unique within a registry. */ path: string; /** Rendered for the route. */ component: C; /** Human label, e.g. for a generated navigation. */ title?: string; /** Name of the contributing plugin, for reports. */ plugin?: string; /** Anything else the host's router needs. */ meta?: Record; } /** A registration refused because another entry already holds its key. */ export interface DuplicateContribution { /** The path (routes) or id (resolvers) both entries claim. */ key: string; /** The entry that holds the key; it stays registered. */ existing: T; /** The entry that was refused. */ rejected: T; } export type Registration = { accepted: true; dispose: () => void; } | { accepted: false; duplicate: DuplicateContribution; }; export interface IRouteRegistry { /** * Add a route. A path that is already registered is refused and reported * (the earlier registration stays), never silently replaced. `dispose` * removes this registration once; registrations are not removed when the * contributing plugin deactivates. */ register(route: RouteContribution): Registration>; /** Every accepted route, in registration order. */ list(): readonly RouteContribution[]; /** The most recent refused registrations (at most 100), oldest first. */ duplicates(): readonly DuplicateContribution>[]; /** Called after every change to `list()`. Returns an unsubscribe function. */ subscribe(listener: () => void): () => void; } /** * What a resolver is asked about: one store-fs file, as the store holds it now. */ export interface StoreFsNodeRef { /** Path segments from the store-fs root. */ path: readonly string[]; /** Media type from the file name's suffix (`contentTypeOf`, `@hamak/shared-utils`). */ contentType: string; /** The parsed content, as held in the store (`undefined` until loaded). */ content: T; /** The store-fs node itself: name, schema, state. */ node: FileNode; } /** * Decides whether it displays a store-fs node. Resolvers are asked in priority * order and the first component returned wins. */ export interface ViewResolver { /** Unique within a registry. */ id: string; /** * Higher is asked first; equal priorities keep registration order. Default 0. * Read when the resolver is registered: to change it, unregister and register * the resolver again. */ priority?: number; /** The component that displays `ref`, or `undefined` / `null` to pass. */ resolve(ref: StoreFsNodeRef): C | null | undefined; /** Name of the contributing plugin, for reports. */ plugin?: string; } export interface ResolvedView { resolver: ViewResolver; component: C; } export interface IViewResolverRegistry { /** * Add a resolver. An id that is already registered is refused and reported. * The returned `dispose` removes the resolver. Its `priority` is read now; a * later change to it does not re-order the registry (unregister and register * again instead). */ register(resolver: ViewResolver): Registration>; /** Remove the resolver with this id. Returns whether one was removed. */ unregister(id: string): boolean; /** Every resolver, in the order they are asked. */ list(): readonly ViewResolver[]; /** The first resolver's answer for `ref`, or `undefined` when all pass. */ resolve(ref: StoreFsNodeRef): ResolvedView | undefined; /** The most recent refused registrations (at most 100), oldest first. */ duplicates(): readonly DuplicateContribution>[]; /** Called after every change to `list()`. Returns an unsubscribe function. */ subscribe(listener: () => void): () => void; } //# sourceMappingURL=view-routing-types.d.ts.map