/** * lib/implicit-suffixes.ts — SSOT of the DynamicRouter implicit view suffixes. * * MIRRORS @atlashub/smartstack DynamicRouter's IMPLICIT_SUFFIXES table (the * package resolves `${nodeKey}.${suffix}` registry keys into routes under the * node's base URL). Source of truth in the socle: web/src/router/DynamicRouter.tsx. * * A componentKey whose LAST segment is one of these suffixes is reachable * through its parent node's route; any other 4th-level segment must exist as a * seeded NavigationResource or the URL falls into ProtectedCatchAll (silent * redirect to /applications) — that is what DEV-UI-046 audits. * * Consumers: uat/cli/uat-plan/project-roles.ts (typed wrappers over this map), * audit-dev-frontend DEV-UI-046. The prose copy lives in * development/audit/routing-dynamic/SKILL.md and points here. */ /** Implicit-suffix → URL-pattern map. Id-bearing suffixes render under `/:id`. */ export const SUFFIX_URL: Readonly> = { detail: '/:id', edit: '/:id/edit', create: '/create', new: '/new', duplicate: '/:id/duplicate', settings: '/:id/settings', configure: '/:id/configure', permissions: '/:id/permissions', members: '/:id/members', history: '/:id/history', logs: '/:id/logs', analytics: '/:id/analytics', preview: '/:id/preview', versions: '/:id/versions', comments: '/:id/comments', attachments: '/:id/attachments', audit: '/:id/audit', export: '/:id/export', notifications: '/:id/notifications', schedule: '/:id/schedule', workflow: '/:id/workflow', summary: '/:id/summary', test: '/:id/test', runs: '/:id/runs', import: '/import', }; /** The implicit view suffixes — registry keys `${node}.${suffix}` are checked against these. */ export const IMPLICIT_VIEW_SUFFIXES: readonly string[] = Object.keys(SUFFIX_URL); /** Views that render the node's BASE route (no implicit suffix appended). */ export const BASE_VIEW_KEYS: readonly string[] = ['list', 'section-home', 'module-home']; /** Whether a registry-key last segment is an implicit suffix (O(1)). */ const SUFFIX_SET = new Set(IMPLICIT_VIEW_SUFFIXES); export function isImplicitSuffix(segment: string): boolean { return SUFFIX_SET.has(segment); }