export interface NavigationRouteLike { path: string meta?: Record } export interface NavigationEntry { screenId: string path: string module: string section: string title: string order: number favoriteAllowed: boolean internalOnly: boolean permissions: readonly string[] } export interface NavigationSection { module: string entries: NavigationEntry[] } function toEntry(route: NavigationRouteLike): NavigationEntry | null { const meta = route.meta if (!meta || typeof meta.screenId !== 'string' || typeof meta.module !== 'string') return null if (meta.module === 'Home') return null if (route.path.includes(':')) return null return { screenId: meta.screenId, path: route.path, module: meta.module, section: typeof meta.section === 'string' ? meta.section : meta.module, title: typeof meta.title === 'string' ? meta.title : route.path, order: typeof meta.order === 'number' ? meta.order : 0, favoriteAllowed: meta.favoriteAllowed !== false, internalOnly: meta.internalOnly === true, permissions: Array.isArray(meta.permissions) && meta.permissions.every(permission => typeof permission === 'string') ? meta.permissions : [] } } /** UI visibility hint only; the API remains the authorization authority. */ export function filterNavigationEntries(entries: readonly NavigationEntry[], grantedPermissions: ReadonlySet): NavigationEntry[] { return entries.filter(entry => entry.permissions.every(permission => grantedPermissions.has(permission))) } /** router.getRoutes()의 flat route 목록에서 즐겨찾기/검색/사이드바가 공유하는 단일 카탈로그를 파생한다. */ export function buildNavigationEntries(routes: readonly NavigationRouteLike[]): NavigationEntry[] { const entries = routes.map(toEntry).filter((entry): entry is NavigationEntry => entry !== null) return entries.sort((a, b) => a.module.localeCompare(b.module) || a.order - b.order) } export function groupByModule(entries: readonly NavigationEntry[]): NavigationSection[] { const byModule = new Map() for (const entry of entries) { const list = byModule.get(entry.module) ?? [] list.push(entry) byModule.set(entry.module, list) } return [...byModule.entries()].map(([module, list]) => ({ module, entries: [...list].sort((a, b) => a.order - b.order) })) }