import { PLATFORM_ROUTE_RUNTIME_CONTRACT, type RouteRuntimeDefinition, } from "../contracts/route-runtime-contract.ts"; import { PLATFORM_ROUTE_REGISTRY, type RouteDefinition, } from "../contracts/routes.ts"; import { composeSchemaRegistry, PLATFORM_SCHEMA_REGISTRY, } from "../contracts/schema-registry.ts"; import { INSTALLED_PRODUCT_FEATURES, } from "./installed-product-manifest.ts"; import { composeApiOperations, validateApiRuntimeBindings, } from "../contracts/api-operations.ts"; import { validateRestProfileBindings } from "../contracts/rest-profiles.ts"; import { composeRuntimeSchemaCodecs } from "../contracts/schema-registry.ts"; import { composeBoundaryMapperRegistry } from "../contracts/boundary-mapper.ts"; import { TECH_LOG_ROUTE_REGISTRY, TECH_LOG_ROUTE_RUNTIME_CONTRACT, TECH_LOG_ROUTE_SCHEMA_REGISTRY, } from "./tech-log/contracts/tech-log-route-contract.ts"; /** * §3.5. Composed from the product manifest rather than from a literal list, so * a feature the build did not select contributes no routes, no operations, no * schemas and no messages — and is therefore not reachable from any registry. */ export const INSTALLED_FEATURE_CONTRACTS = INSTALLED_PRODUCT_FEATURES; /** * Template merge. Two intents meet here and both are kept. * * The template's manifest is retained above, so narrowing the selection still * withdraws a feature's operations, schemas, mappers and invalidation edges. * * The route registry is *not* composed from `contract.routes`, though. The * reference feature still declares `REFERENCE_RESOURCE_*` routes, but this * product deleted their screens during the UI migration, so reducing over those * routes would register paths with no component behind them — a deep link that * resolves to nothing. The registry therefore lists the platform routes (empty * on this branch) and TechLog's, which are the routes that actually exist. * * Re-adding the reference screens, or dropping the reference feature's route * declarations, is a product decision; a merge only has to avoid inventing a * third state where a route is registered and unmountable. */ export const ROUTE_REGISTRY = Object.freeze({ ...PLATFORM_ROUTE_REGISTRY, ...TECH_LOG_ROUTE_REGISTRY, }) satisfies Readonly>; export const ROUTE_RUNTIME_CONTRACT = Object.freeze({ ...PLATFORM_ROUTE_RUNTIME_CONTRACT, ...TECH_LOG_ROUTE_RUNTIME_CONTRACT, }) satisfies Readonly>; export const API_OPERATIONS = composeApiOperations( INSTALLED_FEATURE_CONTRACTS.map((contract) => contract.apiOperations), ); export const REST_PROFILE_BINDINGS_VALID = validateRestProfileBindings( API_OPERATIONS, Object.freeze({ PRIMARY_API: Object.freeze(["omit"] as const) }), ); export const INVALIDATION_REGISTRY = Object.freeze({ topics: Object.freeze( INSTALLED_FEATURE_CONTRACTS.flatMap( (contract) => contract.invalidation.topics, ), ), namespaces: Object.freeze( INSTALLED_FEATURE_CONTRACTS.flatMap( (contract) => contract.invalidation.namespaces, ), ), edges: Object.freeze( INSTALLED_FEATURE_CONTRACTS.flatMap( (contract) => contract.invalidation.edges, ), ), }); export const INVALIDATION_TOPIC_VERSIONS = Object.freeze( INSTALLED_FEATURE_CONTRACTS.flatMap((contract) => contract.topicVersions), ); export const SCHEMA_REGISTRY = composeSchemaRegistry([ PLATFORM_SCHEMA_REGISTRY, TECH_LOG_ROUTE_SCHEMA_REGISTRY, ...INSTALLED_FEATURE_CONTRACTS.map((contract) => contract.schemas), ]); export const RUNTIME_SCHEMA_CODECS = composeRuntimeSchemaCodecs( INSTALLED_FEATURE_CONTRACTS.map((contract) => contract.runtimeSchemas), ); export const MAPPER_REGISTRY = composeBoundaryMapperRegistry( INSTALLED_FEATURE_CONTRACTS.map((contract) => contract.mappers), ); export const API_RUNTIME_BINDINGS_VALID = validateApiRuntimeBindings( API_OPERATIONS, SCHEMA_REGISTRY, RUNTIME_SCHEMA_CODECS, MAPPER_REGISTRY, ); /** * §3.5. Which feature owns each route. * * A route contributed by a feature disappears with it at build time, and has to * be withdrawn from navigation and from the router when the runtime document * disables that feature. Platform routes have no owner and are always present. */ export const ROUTE_FEATURE_OWNER: Readonly> = Object.freeze( Object.fromEntries( INSTALLED_FEATURE_CONTRACTS.flatMap((contract) => Object.keys(contract.routes) // Template merge. Ownership describes routes this product actually // registered. The reference feature still declares screens this // product removed; attributing an unregistered route to a feature // would claim the kill switch governs something no router can mount. .filter((routeId) => routeId in ROUTE_REGISTRY) .map((routeId) => [routeId, contract.featureId] as const), ), ), ); export const NAVIGATION_ROUTES = Object.freeze( Object.values(ROUTE_REGISTRY) .filter(isNavigableRoute) .sort((left, right) => left.navigationOrder - right.navigationOrder), ); type InstalledRouteDefinition = (typeof ROUTE_REGISTRY)[keyof typeof ROUTE_REGISTRY]; type NavigableRoute = InstalledRouteDefinition & Readonly<{ navigationOrder: number }>; function isNavigableRoute( definition: InstalledRouteDefinition, ): definition is NavigableRoute { return definition.navigationOrder !== null; } export function getRoute(routeId: string): InstalledRouteDefinition { const registry: Readonly> = ROUTE_REGISTRY; const selected = registry[routeId]; if (!selected) throw new Error(`Unregistered route: ${routeId}`); return selected; } export function routePath(routeId: string): string { return getRoute(routeId).path; }