UNPKG

everything-dev

Version:

A consolidated product package for building Module Federation apps with oRPC APIs.

1 lines 15.7 kB
{"version":3,"file":"dev-view.mjs","names":[],"sources":["../../src/components/dev-view.tsx"],"sourcesContent":["import { Box, render, Text, useApp, useInput } from \"ink\";\nimport { useEffect, useState } from \"react\";\nimport type { SourceMode } from \"../types\";\nimport { linkify } from \"../utils/linkify\";\nimport { colors, divider, frames, gradients, icons } from \"../utils/theme\";\n\nconst PLUGIN_PREFIX = \"plugin:\";\n\nexport type ProcessStatus = \"pending\" | \"starting\" | \"ready\" | \"error\";\n\nexport interface ProcessState {\n name: string;\n status: ProcessStatus;\n port: number;\n message?: string;\n source?: SourceMode;\n}\n\nexport interface LogEntry {\n id: string;\n source: string;\n line: string;\n timestamp: number;\n isError?: boolean;\n}\n\ninterface DevViewProps {\n processes: ProcessState[];\n logs: LogEntry[];\n description: string;\n proxyTarget?: string;\n onExit?: () => Promise<void> | void;\n onExportLogs?: () => Promise<void> | void;\n}\n\nfunction StatusIcon({ status }: { status: ProcessStatus }) {\n switch (status) {\n case \"pending\":\n return <Text color=\"gray\">{icons.pending}</Text>;\n case \"starting\":\n return <Text color=\"#00ffff\">{icons.scan}</Text>;\n case \"ready\":\n return <Text color=\"#00ff41\">{icons.ok}</Text>;\n case \"error\":\n return <Text color=\"#ff3366\">{icons.err}</Text>;\n }\n}\n\nfunction getServiceColor(name: string): string {\n if (name.startsWith(PLUGIN_PREFIX)) return \"#ffaa00\";\n return name === \"host\" ? \"#00ffff\" : name === \"ui\" ? \"#ff00ff\" : \"#0080ff\";\n}\n\nfunction getDisplayName(name: string): string {\n return name.startsWith(PLUGIN_PREFIX)\n ? name.slice(PLUGIN_PREFIX.length).toUpperCase()\n : name.toUpperCase();\n}\n\nfunction isPlugin(name: string): boolean {\n return name.startsWith(PLUGIN_PREFIX);\n}\n\nfunction getSectionedProcesses(processes: ProcessState[]): Array<{\n key: string;\n title: string;\n processes: ProcessState[];\n}> {\n const plugins = processes.filter((p) => isPlugin(p.name));\n const services = processes.filter((p) => !isPlugin(p.name));\n const sections: Array<{ key: string; title: string; processes: ProcessState[] }> = [];\n if (plugins.length > 0) sections.push({ key: \"plugins\", title: \"PLUGINS\", processes: plugins });\n if (services.length > 0)\n sections.push({ key: \"services\", title: \"SERVICES\", processes: services });\n return sections;\n}\n\nfunction getColumnWidths(processes: ProcessState[]): { name: number; source: number } {\n const name = Math.max(6, ...processes.map((p) => getDisplayName(p.name).length));\n const source = Math.max(10, ...processes.map((p) => (p.source ? `(${p.source})`.length : 0)));\n return { name, source };\n}\n\nfunction ProcessRow({\n proc,\n nameWidth,\n sourceWidth,\n}: {\n proc: ProcessState;\n nameWidth: number;\n sourceWidth: number;\n}) {\n const color = getServiceColor(proc.name);\n const isRemote = proc.source === \"remote\";\n const isHost = proc.name === \"host\";\n const showPort = proc.port > 0 && (isHost || !isRemote);\n const portStr = showPort ? `:${proc.port}` : \"\";\n const sourceLabel = proc.source ? ` (${proc.source})` : \"\";\n\n const statusText =\n proc.status === \"pending\"\n ? \"waiting\"\n : proc.status === \"starting\"\n ? \"starting\"\n : proc.status === \"ready\"\n ? isRemote && !isHost\n ? \"loaded\"\n : \"running\"\n : \"failed\";\n\n return (\n <Box>\n <Text>{\" \"}</Text>\n <StatusIcon status={proc.status} />\n <Text> </Text>\n <Text color={color} bold>\n {getDisplayName(proc.name).padEnd(nameWidth)}\n </Text>\n <Text color=\"gray\">{sourceLabel.padEnd(sourceWidth)}</Text>\n <Text color={proc.status === \"ready\" ? \"#00ff41\" : \"gray\"}>{statusText}</Text>\n {showPort && <Text color=\"#00ffff\"> {portStr}</Text>}\n </Box>\n );\n}\n\nfunction SectionHeader({ title }: { title: string }) {\n return (\n <Box marginBottom={0} marginTop={1}>\n <Text color=\"#00ffff\" bold>\n {title}\n </Text>\n </Box>\n );\n}\n\nfunction LogLine({ entry }: { entry: LogEntry }) {\n const color = getServiceColor(entry.source);\n\n return (\n <Box>\n <Text color={color}>[{entry.source}]</Text>\n <Text color={entry.isError ? \"#ff3366\" : undefined}> {linkify(entry.line)}</Text>\n </Box>\n );\n}\n\nfunction truncateUrl(url: string, maxLen: number): string {\n if (url.length <= maxLen) return url;\n try {\n const parsed = new URL(url);\n const host = parsed.host;\n if (host.length > maxLen - 10) {\n return `${host.slice(0, maxLen - 13)}...`;\n }\n return host;\n } catch {\n return `${url.slice(0, maxLen - 3)}...`;\n }\n}\n\nfunction DevView({\n processes,\n logs,\n description,\n proxyTarget,\n onExit,\n onExportLogs,\n}: DevViewProps) {\n const { exit } = useApp();\n const [isShuttingDown, setIsShuttingDown] = useState(false);\n\n useInput((input, key) => {\n if (isShuttingDown) return;\n\n if (input === \"q\" || (key.ctrl && input === \"c\")) {\n setIsShuttingDown(true);\n Promise.resolve(onExit?.()).then(() => {\n exit();\n });\n }\n if (input === \"l\") {\n setIsShuttingDown(true);\n Promise.resolve(onExportLogs?.()).then(() => {\n exit();\n });\n }\n });\n\n const readyCount = processes.filter((p) => p.status === \"ready\").length;\n const total = processes.length;\n const allReady = readyCount === total;\n const hostProcess = processes.find((p) => p.name === \"host\");\n const hostPort = hostProcess?.port || 3000;\n const recentLogs = logs.slice(-12);\n const sectionedProcesses = getSectionedProcesses(processes);\n const columnWidths = getColumnWidths(processes);\n\n return (\n <Box flexDirection=\"column\">\n <Box marginBottom={0}>\n <Text color=\"#00ffff\">{frames.top(52)}</Text>\n </Box>\n <Box>\n <Text>\n {\" \"}\n {icons.run} {gradients.cyber(description.toUpperCase())}\n </Text>\n </Box>\n <Box marginBottom={1}>\n <Text color=\"#00ffff\">{frames.bottom(52)}</Text>\n </Box>\n\n {allReady && (\n <Box marginBottom={1} flexDirection=\"column\">\n <Box>\n <Text color=\"#00ff41\">\n {\" \"}\n {icons.app} APP READY\n </Text>\n </Box>\n <Box>\n <Text color=\"#00ff41\" bold>\n {\" \"}\n {icons.arrow} http://localhost:{hostPort}\n </Text>\n </Box>\n </Box>\n )}\n\n {proxyTarget && (\n <Box marginBottom={1}>\n <Text color=\"#ffaa00\">\n {\" \"}\n {icons.arrow} API PROXY → {truncateUrl(proxyTarget, 38)}\n </Text>\n </Box>\n )}\n\n <Box marginTop={0} marginBottom={0}>\n <Text>{colors.dim(divider(52))}</Text>\n </Box>\n\n {sectionedProcesses.map((section) => (\n <Box key={section.key} flexDirection=\"column\">\n <SectionHeader title={section.title} />\n {section.processes.map((proc) => (\n <ProcessRow\n key={proc.name}\n proc={proc}\n nameWidth={columnWidths.name}\n sourceWidth={columnWidths.source}\n />\n ))}\n </Box>\n ))}\n\n <Box marginTop={1} marginBottom={0}>\n <Text>{colors.dim(divider(52))}</Text>\n </Box>\n\n <Box marginTop={0}>\n <Text color={allReady ? \"#00ff41\" : \"#00ffff\"}>\n {\" \"}\n {allReady\n ? `${icons.ok} All ${total} services running`\n : `${icons.scan} ${readyCount}/${total} ready`}\n </Text>\n <Text color=\"gray\">\n {\" \"}\n {icons.dot} q quit {icons.dot} l logs\n </Text>\n </Box>\n\n {recentLogs.length > 0 && (\n <>\n <Box marginTop={1} marginBottom={0}>\n <Text>{colors.dim(divider(52))}</Text>\n </Box>\n <Box flexDirection=\"column\" marginTop={0}>\n {recentLogs.map((entry) => (\n <LogLine key={entry.id} entry={entry} />\n ))}\n </Box>\n </>\n )}\n </Box>\n );\n}\n\nexport interface DevViewHandle {\n updateProcess: (name: string, status: ProcessStatus, message?: string) => void;\n addLog: (source: string, line: string, isError?: boolean) => void;\n unmount: () => void;\n}\n\nexport function renderDevView(\n initialProcesses: ProcessState[],\n description: string,\n env: Record<string, string>,\n onExit?: () => Promise<void> | void,\n onExportLogs?: () => Promise<void> | void,\n): DevViewHandle {\n let processes = [...initialProcesses];\n let logs: LogEntry[] = [];\n let rerender: (() => void) | null = null;\n const proxyTarget = env.API_PROXY;\n let logSeq = 0;\n let lastLogKey: string | null = null;\n\n const updateProcess = (name: string, status: ProcessStatus, message?: string) => {\n processes = processes.map((p) => (p.name === name ? { ...p, status, message } : p));\n rerender?.();\n };\n\n const addLog = (source: string, line: string, isError = false) => {\n const nextKey = `${source}:${isError ? \"1\" : \"0\"}:${line}`;\n if (nextKey === lastLogKey) return;\n lastLogKey = nextKey;\n\n logs = [\n ...logs,\n { id: `${Date.now()}-${++logSeq}`, source, line, timestamp: Date.now(), isError },\n ];\n if (logs.length > 100) logs = logs.slice(-100);\n rerender?.();\n };\n\n function DevViewWrapper() {\n const [, forceUpdate] = useState(0);\n\n useEffect(() => {\n rerender = () => forceUpdate((n: number) => n + 1);\n return () => {\n rerender = null;\n };\n }, []);\n\n return (\n <DevView\n processes={processes}\n logs={logs}\n description={description}\n proxyTarget={proxyTarget}\n onExit={onExit}\n onExportLogs={onExportLogs}\n />\n );\n }\n\n const { unmount } = render(<DevViewWrapper />);\n return { updateProcess, addLog, unmount };\n}\n"],"mappings":"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"}