@spfn/monitor 0.1.0-beta.2 → 0.1.0-beta.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/nextjs/components/monitor-dashboard.tsx","../../src/nextjs/components/stats-overview.tsx","../../src/nextjs/components/error-list-view.tsx","../../src/nextjs/components/error-detail-view.tsx","../../src/nextjs/components/log-viewer.tsx"],"sourcesContent":["/**\n * @spfn/monitor - Monitor Dashboard Component\n *\n * Main entry point combining StatsOverview, ErrorListView, and LogViewer in tabs\n */\n\nimport { useState } from 'react';\nimport { StatsOverview } from './stats-overview';\nimport { ErrorListView } from './error-list-view';\nimport { ErrorDetailView } from './error-detail-view';\nimport { LogViewer } from './log-viewer';\n\ntype Tab = 'errors' | 'logs';\n\nexport function MonitorDashboard()\n{\n const [tab, setTab] = useState<Tab>('errors');\n const [selectedErrorId, setSelectedErrorId] = useState<number | null>(null);\n\n return (\n <div className=\"space-y-6\">\n {/* Stats */}\n <StatsOverview />\n\n {/* Tabs */}\n <div className=\"flex gap-1 border-b border-neutral-200 dark:border-neutral-800\">\n <TabButton active={tab === 'errors'} onClick={() => { setTab('errors'); setSelectedErrorId(null); }}>\n Errors\n </TabButton>\n <TabButton active={tab === 'logs'} onClick={() => setTab('logs')}>\n Logs\n </TabButton>\n </div>\n\n {/* Content */}\n {tab === 'errors' && !selectedErrorId && (\n <ErrorListView onSelect={setSelectedErrorId} />\n )}\n {tab === 'errors' && selectedErrorId && (\n <ErrorDetailView\n errorId={selectedErrorId}\n onBack={() => setSelectedErrorId(null)}\n />\n )}\n {tab === 'logs' && (\n <LogViewer />\n )}\n </div>\n );\n}\n\nfunction TabButton({\n active,\n onClick,\n children,\n}: {\n active: boolean;\n onClick: () => void;\n children: React.ReactNode;\n})\n{\n return (\n <button\n onClick={onClick}\n className={`px-4 py-2 text-sm font-medium border-b-2 -mb-px transition-colors ${\n active\n ? 'border-neutral-900 dark:border-neutral-100 text-neutral-900 dark:text-neutral-100'\n : 'border-transparent text-neutral-500 hover:text-neutral-700 dark:hover:text-neutral-300'\n }`}\n >\n {children}\n </button>\n );\n}\n","/**\n * @spfn/monitor - Stats Overview Component\n *\n * Displays error/log counts and trend indicators\n */\n\nimport { useState, useEffect, useCallback } from 'react';\nimport { monitorApi } from '@spfn/monitor';\nimport type { MonitorStats } from '@spfn/monitor';\n\nexport function StatsOverview()\n{\n const [stats, setStats] = useState<MonitorStats | null>(null);\n const [isLoading, setIsLoading] = useState(true);\n\n const fetchStats = useCallback(async () =>\n {\n try\n {\n const data = await monitorApi.getStats.call({});\n setStats(data as MonitorStats);\n }\n finally\n {\n setIsLoading(false);\n }\n }, []);\n\n useEffect(() =>\n {\n fetchStats();\n const interval = setInterval(fetchStats, 30_000);\n return () => clearInterval(interval);\n }, [fetchStats]);\n\n if (isLoading || !stats)\n {\n return (\n <div className=\"grid grid-cols-2 md:grid-cols-4 gap-4\">\n {[...Array(4)].map((_, i) => (\n <div key={i} className=\"rounded-lg border border-neutral-200 dark:border-neutral-800 p-4 animate-pulse\">\n <div className=\"h-4 w-20 bg-neutral-200 dark:bg-neutral-700 rounded mb-2\" />\n <div className=\"h-8 w-12 bg-neutral-200 dark:bg-neutral-700 rounded\" />\n </div>\n ))}\n </div>\n );\n }\n\n const cards = [\n { label: 'Active Errors', value: stats.errors.active, color: 'text-red-600 dark:text-red-400' },\n { label: 'Resolved', value: stats.errors.resolved, color: 'text-green-600 dark:text-green-400' },\n { label: 'Ignored', value: stats.errors.ignored, color: 'text-neutral-500 dark:text-neutral-400' },\n { label: 'Errors (24h)', value: stats.trends.errorsLast24h, color: 'text-orange-600 dark:text-orange-400' },\n ];\n\n return (\n <div className=\"grid grid-cols-2 md:grid-cols-4 gap-4\">\n {cards.map((card) => (\n <div\n key={card.label}\n className=\"rounded-lg border border-neutral-200 dark:border-neutral-800 p-4\"\n >\n <p className=\"text-sm text-neutral-500 dark:text-neutral-400\">{card.label}</p>\n <p className={`text-2xl font-semibold mt-1 ${card.color}`}>{card.value}</p>\n </div>\n ))}\n </div>\n );\n}\n","/**\n * @spfn/monitor - Error List View Component\n *\n * Displays error groups in a filterable table with status badges\n */\n\nimport { useState, useEffect } from 'react';\nimport { monitorApi } from '@spfn/monitor';\nimport type { ErrorGroupStatus } from '@spfn/monitor';\n\ninterface ErrorListViewProps\n{\n onSelect?: (id: number) => void;\n}\n\nconst STATUS_BADGE: Record<ErrorGroupStatus, string> = {\n active: 'bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400',\n resolved: 'bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400',\n ignored: 'bg-neutral-100 text-neutral-600 dark:bg-neutral-800 dark:text-neutral-400',\n};\n\nexport function ErrorListView({ onSelect }: ErrorListViewProps)\n{\n const [status, setStatus] = useState<string>('');\n const [search, setSearch] = useState('');\n const [errors, setErrors] = useState<any[]>([]);\n const [isLoading, setIsLoading] = useState(true);\n\n useEffect(() =>\n {\n let cancelled = false;\n setIsLoading(true);\n\n monitorApi.listErrors.call({\n query: {\n ...(status ? { status } : {}),\n ...(search ? { search } : {}),\n limit: 50,\n },\n }).then((data) =>\n {\n if (!cancelled)\n {\n setErrors(data as any[]);\n setIsLoading(false);\n }\n }).catch(() =>\n {\n if (!cancelled)\n {\n setIsLoading(false);\n }\n });\n\n return () => { cancelled = true; };\n }, [status, search]);\n\n return (\n <div className=\"space-y-4\">\n {/* Filters */}\n <div className=\"flex gap-3\">\n <select\n value={status}\n onChange={(e) => setStatus(e.target.value)}\n className=\"rounded border border-neutral-300 dark:border-neutral-700 bg-white dark:bg-neutral-900 px-3 py-1.5 text-sm\"\n >\n <option value=\"\">All statuses</option>\n <option value=\"active\">Active</option>\n <option value=\"resolved\">Resolved</option>\n <option value=\"ignored\">Ignored</option>\n </select>\n <input\n type=\"text\"\n placeholder=\"Search errors...\"\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n className=\"rounded border border-neutral-300 dark:border-neutral-700 bg-white dark:bg-neutral-900 px-3 py-1.5 text-sm flex-1\"\n />\n </div>\n\n {/* Table */}\n {isLoading ? (\n <div className=\"text-sm text-neutral-500\">Loading...</div>\n ) : errors.length === 0 ? (\n <div className=\"text-sm text-neutral-500 py-8 text-center\">No errors found</div>\n ) : (\n <div className=\"overflow-x-auto\">\n <table className=\"w-full text-sm\">\n <thead>\n <tr className=\"border-b border-neutral-200 dark:border-neutral-800 text-left text-neutral-500\">\n <th className=\"py-2 pr-4\">Status</th>\n <th className=\"py-2 pr-4\">Error</th>\n <th className=\"py-2 pr-4\">Path</th>\n <th className=\"py-2 pr-4 text-right\">Count</th>\n <th className=\"py-2 text-right\">Last Seen</th>\n </tr>\n </thead>\n <tbody>\n {errors.map((group: any) => (\n <tr\n key={group.id}\n onClick={() => onSelect?.(group.id)}\n className=\"border-b border-neutral-100 dark:border-neutral-800/50 hover:bg-neutral-50 dark:hover:bg-neutral-800/50 cursor-pointer\"\n >\n <td className=\"py-2 pr-4\">\n <span className={`inline-block px-2 py-0.5 rounded text-xs font-medium ${STATUS_BADGE[group.status as ErrorGroupStatus]}`}>\n {group.status}\n </span>\n </td>\n <td className=\"py-2 pr-4\">\n <div className=\"font-medium text-neutral-900 dark:text-neutral-100\">{group.name}</div>\n <div className=\"text-neutral-500 truncate max-w-xs\">{group.message}</div>\n </td>\n <td className=\"py-2 pr-4 font-mono text-xs text-neutral-600 dark:text-neutral-400\">\n {group.method} {group.path}\n </td>\n <td className=\"py-2 pr-4 text-right font-mono\">{group.count}</td>\n <td className=\"py-2 text-right text-neutral-500\">\n {formatRelativeTime(group.lastSeenAt)}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n )}\n </div>\n );\n}\n\nfunction formatRelativeTime(date: string | Date): string\n{\n const d = typeof date === 'string' ? new Date(date) : date;\n const now = Date.now();\n const diff = now - d.getTime();\n const mins = Math.floor(diff / 60_000);\n\n if (mins < 1)\n {\n return 'just now';\n }\n\n if (mins < 60)\n {\n return `${mins}m ago`;\n }\n\n const hours = Math.floor(mins / 60);\n if (hours < 24)\n {\n return `${hours}h ago`;\n }\n\n const days = Math.floor(hours / 24);\n return `${days}d ago`;\n}\n","/**\n * @spfn/monitor - Error Detail View Component\n *\n * Shows error group details with event timeline and status change buttons\n */\n\nimport { useState, useEffect, useCallback } from 'react';\nimport { monitorApi } from '@spfn/monitor';\nimport type { ErrorGroupStatus } from '@spfn/monitor';\n\ninterface ErrorDetailViewProps\n{\n errorId: number;\n onBack?: () => void;\n}\n\nconst STATUS_ACTIONS: Record<ErrorGroupStatus, { label: string; target: ErrorGroupStatus }[]> = {\n active: [\n { label: 'Resolve', target: 'resolved' },\n { label: 'Ignore', target: 'ignored' },\n ],\n resolved: [\n { label: 'Reopen', target: 'active' },\n ],\n ignored: [\n { label: 'Reopen', target: 'active' },\n { label: 'Resolve', target: 'resolved' },\n ],\n};\n\nexport function ErrorDetailView({ errorId, onBack }: ErrorDetailViewProps)\n{\n const [data, setData] = useState<any>(null);\n const [isLoading, setIsLoading] = useState(true);\n const [isMutating, setIsMutating] = useState(false);\n\n const fetchDetail = useCallback(async () =>\n {\n setIsLoading(true);\n try\n {\n const result = await monitorApi.getErrorDetail.call({ params: { id: errorId } });\n setData(result);\n }\n finally\n {\n setIsLoading(false);\n }\n }, [errorId]);\n\n useEffect(() =>\n {\n fetchDetail();\n }, [fetchDetail]);\n\n async function handleStatusChange(status: string)\n {\n setIsMutating(true);\n try\n {\n await monitorApi.updateErrorStatus.call({\n params: { id: errorId },\n body: { status },\n });\n await fetchDetail();\n }\n finally\n {\n setIsMutating(false);\n }\n }\n\n if (isLoading || !data)\n {\n return <div className=\"text-sm text-neutral-500\">Loading...</div>;\n }\n\n const { group, events } = data;\n const actions = STATUS_ACTIONS[group.status as ErrorGroupStatus] ?? [];\n\n return (\n <div className=\"space-y-6\">\n {/* Header */}\n <div className=\"flex items-center gap-3\">\n {onBack && (\n <button\n onClick={onBack}\n className=\"text-sm text-neutral-500 hover:text-neutral-700 dark:hover:text-neutral-300\"\n >\n &larr; Back\n </button>\n )}\n <h2 className=\"text-lg font-semibold text-neutral-900 dark:text-neutral-100\">\n {group.name} — {group.statusCode}\n </h2>\n </div>\n\n {/* Error Info */}\n <div className=\"rounded-lg border border-neutral-200 dark:border-neutral-800 p-4 space-y-3\">\n <p className=\"text-neutral-700 dark:text-neutral-300\">{group.message}</p>\n <div className=\"grid grid-cols-2 md:grid-cols-4 gap-4 text-sm\">\n <div>\n <span className=\"text-neutral-500\">Method</span>\n <p className=\"font-mono\">{group.method}</p>\n </div>\n <div>\n <span className=\"text-neutral-500\">Path</span>\n <p className=\"font-mono\">{group.path}</p>\n </div>\n <div>\n <span className=\"text-neutral-500\">Count</span>\n <p className=\"font-mono\">{group.count}</p>\n </div>\n <div>\n <span className=\"text-neutral-500\">Status</span>\n <p className=\"font-medium\">{group.status}</p>\n </div>\n </div>\n\n {/* Status Actions */}\n {actions.length > 0 && (\n <div className=\"flex gap-2 pt-2\">\n {actions.map((action) => (\n <button\n key={action.target}\n onClick={() => handleStatusChange(action.target)}\n disabled={isMutating}\n className=\"px-3 py-1.5 text-sm rounded border border-neutral-300 dark:border-neutral-700 hover:bg-neutral-100 dark:hover:bg-neutral-800 disabled:opacity-50\"\n >\n {action.label}\n </button>\n ))}\n </div>\n )}\n </div>\n\n {/* Event Timeline */}\n <div>\n <h3 className=\"text-sm font-medium text-neutral-500 mb-3\">Recent Events</h3>\n <div className=\"space-y-2\">\n {(events as any[]).map((event: any) => (\n <div\n key={event.id}\n className=\"rounded border border-neutral-200 dark:border-neutral-800 p-3 text-sm\"\n >\n <div className=\"flex justify-between items-start\">\n <div className=\"space-y-1\">\n <div className=\"flex gap-3 text-neutral-500\">\n <span>User: {event.userId ?? '(anonymous)'}</span>\n <span>Request: {event.requestId ?? '(none)'}</span>\n </div>\n {event.stackTrace && (\n <pre className=\"text-xs text-neutral-600 dark:text-neutral-400 overflow-x-auto whitespace-pre-wrap mt-2 bg-neutral-50 dark:bg-neutral-900 p-2 rounded\">\n {event.stackTrace.split('\\n').slice(0, 5).join('\\n')}\n </pre>\n )}\n </div>\n <span className=\"text-xs text-neutral-400 whitespace-nowrap ml-4\">\n {new Date(event.createdAt).toLocaleString()}\n </span>\n </div>\n </div>\n ))}\n </div>\n </div>\n </div>\n );\n}\n","/**\n * @spfn/monitor - Log Viewer Component\n *\n * Searchable, filterable log list with expandable metadata\n */\n\nimport { useState, useEffect } from 'react';\nimport { monitorApi } from '@spfn/monitor';\n\nconst LEVEL_BADGE: Record<string, string> = {\n debug: 'bg-neutral-100 text-neutral-600 dark:bg-neutral-800 dark:text-neutral-400',\n info: 'bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400',\n warn: 'bg-yellow-100 text-yellow-700 dark:bg-yellow-900/30 dark:text-yellow-400',\n error: 'bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400',\n fatal: 'bg-red-200 text-red-800 dark:bg-red-900/50 dark:text-red-300',\n};\n\nexport function LogViewer()\n{\n const [level, setLevel] = useState('');\n const [search, setSearch] = useState('');\n const [logs, setLogs] = useState<any[]>([]);\n const [isLoading, setIsLoading] = useState(true);\n const [expanded, setExpanded] = useState<Set<number>>(new Set());\n\n useEffect(() =>\n {\n let cancelled = false;\n setIsLoading(true);\n\n monitorApi.listLogs.call({\n query: {\n ...(level ? { level } : {}),\n ...(search ? { search } : {}),\n limit: 100,\n },\n }).then((data) =>\n {\n if (!cancelled)\n {\n setLogs(data as any[]);\n setIsLoading(false);\n }\n }).catch(() =>\n {\n if (!cancelled)\n {\n setIsLoading(false);\n }\n });\n\n return () => { cancelled = true; };\n }, [level, search]);\n\n function toggleExpand(id: number)\n {\n setExpanded((prev) =>\n {\n const next = new Set(prev);\n if (next.has(id))\n {\n next.delete(id);\n }\n else\n {\n next.add(id);\n }\n\n return next;\n });\n }\n\n return (\n <div className=\"space-y-4\">\n {/* Filters */}\n <div className=\"flex gap-3\">\n <select\n value={level}\n onChange={(e) => setLevel(e.target.value)}\n className=\"rounded border border-neutral-300 dark:border-neutral-700 bg-white dark:bg-neutral-900 px-3 py-1.5 text-sm\"\n >\n <option value=\"\">All levels</option>\n <option value=\"debug\">Debug</option>\n <option value=\"info\">Info</option>\n <option value=\"warn\">Warn</option>\n <option value=\"error\">Error</option>\n <option value=\"fatal\">Fatal</option>\n </select>\n <input\n type=\"text\"\n placeholder=\"Search logs...\"\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n className=\"rounded border border-neutral-300 dark:border-neutral-700 bg-white dark:bg-neutral-900 px-3 py-1.5 text-sm flex-1\"\n />\n </div>\n\n {/* Log List */}\n {isLoading ? (\n <div className=\"text-sm text-neutral-500\">Loading...</div>\n ) : logs.length === 0 ? (\n <div className=\"text-sm text-neutral-500 py-8 text-center\">No logs found</div>\n ) : (\n <div className=\"space-y-1\">\n {logs.map((log: any) => (\n <div\n key={log.id}\n className=\"rounded border border-neutral-200 dark:border-neutral-800 text-sm\"\n >\n <div\n onClick={() => log.metadata && toggleExpand(log.id)}\n className={`flex items-start gap-3 p-2 ${log.metadata ? 'cursor-pointer hover:bg-neutral-50 dark:hover:bg-neutral-800/50' : ''}`}\n >\n <span className={`inline-block px-1.5 py-0.5 rounded text-xs font-medium ${LEVEL_BADGE[log.level] ?? ''}`}>\n {log.level}\n </span>\n <span className=\"flex-1 text-neutral-800 dark:text-neutral-200\">\n {log.message}\n </span>\n {log.source && (\n <span className=\"text-xs text-neutral-400 font-mono\">{log.source}</span>\n )}\n <span className=\"text-xs text-neutral-400 whitespace-nowrap\">\n {new Date(log.createdAt).toLocaleTimeString()}\n </span>\n </div>\n {expanded.has(log.id) && log.metadata && (\n <pre className=\"px-3 py-2 text-xs bg-neutral-50 dark:bg-neutral-900 border-t border-neutral-200 dark:border-neutral-800 overflow-x-auto\">\n {JSON.stringify(log.metadata, null, 2)}\n </pre>\n )}\n </div>\n ))}\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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","names":["useState","useState","useEffect","monitorApi","jsx","jsxs","useState","useEffect","useCallback","monitorApi","jsx","jsxs","useState","useEffect","monitorApi","jsx","jsxs","jsx","jsxs","useState"]}
1
+ {"version":3,"sources":["../../src/nextjs/components/monitor-dashboard.tsx","../../src/nextjs/components/stats-overview.tsx","../../src/nextjs/components/error-list-view.tsx","../../src/nextjs/components/error-detail-view.tsx","../../src/nextjs/components/log-viewer.tsx"],"sourcesContent":["/**\n * @spfn/monitor - Monitor Dashboard Component\n *\n * Main entry point combining StatsOverview, ErrorListView, and LogViewer in tabs\n */\n\nimport { useState } from 'react';\nimport { StatsOverview } from './stats-overview';\nimport { ErrorListView } from './error-list-view';\nimport { ErrorDetailView } from './error-detail-view';\nimport { LogViewer } from './log-viewer';\n\ntype Tab = 'errors' | 'logs';\n\nexport function MonitorDashboard()\n{\n const [tab, setTab] = useState<Tab>('errors');\n const [selectedErrorId, setSelectedErrorId] = useState<number | null>(null);\n\n return (\n <div className=\"space-y-6\">\n {/* Stats */}\n <StatsOverview />\n\n {/* Tabs */}\n <div className=\"flex gap-1 border-b border-neutral-200 dark:border-neutral-800\">\n <TabButton active={tab === 'errors'} onClick={() => \n {\n setTab('errors'); setSelectedErrorId(null); \n }}>\n Errors\n </TabButton>\n <TabButton active={tab === 'logs'} onClick={() => setTab('logs')}>\n Logs\n </TabButton>\n </div>\n\n {/* Content */}\n {tab === 'errors' && !selectedErrorId && (\n <ErrorListView onSelect={setSelectedErrorId} />\n )}\n {tab === 'errors' && selectedErrorId && (\n <ErrorDetailView\n errorId={selectedErrorId}\n onBack={() => setSelectedErrorId(null)}\n />\n )}\n {tab === 'logs' && (\n <LogViewer />\n )}\n </div>\n );\n}\n\nfunction TabButton({\n active,\n onClick,\n children,\n}: {\n active: boolean;\n onClick: () => void;\n children: React.ReactNode;\n})\n{\n return (\n <button\n onClick={onClick}\n className={`px-4 py-2 text-sm font-medium border-b-2 -mb-px transition-colors ${\n active\n ? 'border-neutral-900 dark:border-neutral-100 text-neutral-900 dark:text-neutral-100'\n : 'border-transparent text-neutral-500 hover:text-neutral-700 dark:hover:text-neutral-300'\n }`}\n >\n {children}\n </button>\n );\n}\n","/**\n * @spfn/monitor - Stats Overview Component\n *\n * Displays error/log counts and trend indicators\n */\n\nimport { useState, useEffect, useCallback } from 'react';\nimport { monitorApi } from '@spfn/monitor';\nimport type { MonitorStats } from '@spfn/monitor';\n\nexport function StatsOverview()\n{\n const [stats, setStats] = useState<MonitorStats | null>(null);\n const [isLoading, setIsLoading] = useState(true);\n\n const fetchStats = useCallback(async () =>\n {\n try\n {\n const data = await monitorApi.getStats.call({});\n setStats(data as MonitorStats);\n }\n finally\n {\n setIsLoading(false);\n }\n }, []);\n\n useEffect(() =>\n {\n fetchStats();\n const interval = setInterval(fetchStats, 30_000);\n\n return () => clearInterval(interval);\n }, [fetchStats]);\n\n if (isLoading || !stats)\n {\n return (\n <div className=\"grid grid-cols-2 md:grid-cols-4 gap-4\">\n {[...Array(4)].map((_, i) => (\n <div key={i} className=\"rounded-lg border border-neutral-200 dark:border-neutral-800 p-4 animate-pulse\">\n <div className=\"h-4 w-20 bg-neutral-200 dark:bg-neutral-700 rounded mb-2\" />\n <div className=\"h-8 w-12 bg-neutral-200 dark:bg-neutral-700 rounded\" />\n </div>\n ))}\n </div>\n );\n }\n\n const cards = [\n { label: 'Active Errors', value: stats.errors.active, color: 'text-red-600 dark:text-red-400' },\n { label: 'Resolved', value: stats.errors.resolved, color: 'text-green-600 dark:text-green-400' },\n { label: 'Ignored', value: stats.errors.ignored, color: 'text-neutral-500 dark:text-neutral-400' },\n { label: 'Errors (24h)', value: stats.trends.errorsLast24h, color: 'text-orange-600 dark:text-orange-400' },\n ];\n\n return (\n <div className=\"grid grid-cols-2 md:grid-cols-4 gap-4\">\n {cards.map((card) => (\n <div\n key={card.label}\n className=\"rounded-lg border border-neutral-200 dark:border-neutral-800 p-4\"\n >\n <p className=\"text-sm text-neutral-500 dark:text-neutral-400\">{card.label}</p>\n <p className={`text-2xl font-semibold mt-1 ${card.color}`}>{card.value}</p>\n </div>\n ))}\n </div>\n );\n}\n","/**\n * @spfn/monitor - Error List View Component\n *\n * Displays error groups in a filterable table with status badges\n */\n\nimport { useState, useEffect } from 'react';\nimport { monitorApi } from '@spfn/monitor';\nimport type { ErrorGroupStatus } from '@spfn/monitor';\n\ninterface ErrorListViewProps\n{\n onSelect?: (id: number) => void;\n}\n\nconst STATUS_BADGE: Record<ErrorGroupStatus, string> = {\n active: 'bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400',\n resolved: 'bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400',\n ignored: 'bg-neutral-100 text-neutral-600 dark:bg-neutral-800 dark:text-neutral-400',\n};\n\nexport function ErrorListView({ onSelect }: ErrorListViewProps)\n{\n const [status, setStatus] = useState<string>('');\n const [search, setSearch] = useState('');\n const [errors, setErrors] = useState<any[]>([]);\n const [isLoading, setIsLoading] = useState(true);\n\n useEffect(() =>\n {\n let cancelled = false;\n setIsLoading(true);\n\n monitorApi.listErrors.call({\n query: {\n ...(status ? { status } : {}),\n ...(search ? { search } : {}),\n limit: 50,\n },\n }).then((data) =>\n {\n if (!cancelled)\n {\n setErrors(data as any[]);\n setIsLoading(false);\n }\n }).catch(() =>\n {\n if (!cancelled)\n {\n setIsLoading(false);\n }\n });\n\n return () => \n {\n cancelled = true; \n };\n }, [status, search]);\n\n return (\n <div className=\"space-y-4\">\n {/* Filters */}\n <div className=\"flex gap-3\">\n <select\n value={status}\n onChange={(e) => setStatus(e.target.value)}\n className=\"rounded border border-neutral-300 dark:border-neutral-700 bg-white dark:bg-neutral-900 px-3 py-1.5 text-sm\"\n >\n <option value=\"\">All statuses</option>\n <option value=\"active\">Active</option>\n <option value=\"resolved\">Resolved</option>\n <option value=\"ignored\">Ignored</option>\n </select>\n <input\n type=\"text\"\n placeholder=\"Search errors...\"\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n className=\"rounded border border-neutral-300 dark:border-neutral-700 bg-white dark:bg-neutral-900 px-3 py-1.5 text-sm flex-1\"\n />\n </div>\n\n {/* Table */}\n {isLoading ? (\n <div className=\"text-sm text-neutral-500\">Loading...</div>\n ) : errors.length === 0 ? (\n <div className=\"text-sm text-neutral-500 py-8 text-center\">No errors found</div>\n ) : (\n <div className=\"overflow-x-auto\">\n <table className=\"w-full text-sm\">\n <thead>\n <tr className=\"border-b border-neutral-200 dark:border-neutral-800 text-left text-neutral-500\">\n <th className=\"py-2 pr-4\">Status</th>\n <th className=\"py-2 pr-4\">Error</th>\n <th className=\"py-2 pr-4\">Path</th>\n <th className=\"py-2 pr-4 text-right\">Count</th>\n <th className=\"py-2 text-right\">Last Seen</th>\n </tr>\n </thead>\n <tbody>\n {errors.map((group: any) => (\n <tr\n key={group.id}\n onClick={() => onSelect?.(group.id)}\n className=\"border-b border-neutral-100 dark:border-neutral-800/50 hover:bg-neutral-50 dark:hover:bg-neutral-800/50 cursor-pointer\"\n >\n <td className=\"py-2 pr-4\">\n <span className={`inline-block px-2 py-0.5 rounded text-xs font-medium ${STATUS_BADGE[group.status as ErrorGroupStatus]}`}>\n {group.status}\n </span>\n </td>\n <td className=\"py-2 pr-4\">\n <div className=\"font-medium text-neutral-900 dark:text-neutral-100\">{group.name}</div>\n <div className=\"text-neutral-500 truncate max-w-xs\">{group.message}</div>\n </td>\n <td className=\"py-2 pr-4 font-mono text-xs text-neutral-600 dark:text-neutral-400\">\n {group.method} {group.path}\n </td>\n <td className=\"py-2 pr-4 text-right font-mono\">{group.count}</td>\n <td className=\"py-2 text-right text-neutral-500\">\n {formatRelativeTime(group.lastSeenAt)}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n )}\n </div>\n );\n}\n\nfunction formatRelativeTime(date: string | Date): string\n{\n const d = typeof date === 'string' ? new Date(date) : date;\n const now = Date.now();\n const diff = now - d.getTime();\n const mins = Math.floor(diff / 60_000);\n\n if (mins < 1)\n {\n return 'just now';\n }\n\n if (mins < 60)\n {\n return `${mins}m ago`;\n }\n\n const hours = Math.floor(mins / 60);\n if (hours < 24)\n {\n return `${hours}h ago`;\n }\n\n const days = Math.floor(hours / 24);\n\n return `${days}d ago`;\n}\n","/**\n * @spfn/monitor - Error Detail View Component\n *\n * Shows error group details with event timeline and status change buttons\n */\n\nimport { useState, useEffect, useCallback } from 'react';\nimport { monitorApi } from '@spfn/monitor';\nimport type { ErrorGroupStatus } from '@spfn/monitor';\n\ninterface ErrorDetailViewProps\n{\n errorId: number;\n onBack?: () => void;\n}\n\nconst STATUS_ACTIONS: Record<ErrorGroupStatus, { label: string; target: ErrorGroupStatus }[]> = {\n active: [\n { label: 'Resolve', target: 'resolved' },\n { label: 'Ignore', target: 'ignored' },\n ],\n resolved: [\n { label: 'Reopen', target: 'active' },\n ],\n ignored: [\n { label: 'Reopen', target: 'active' },\n { label: 'Resolve', target: 'resolved' },\n ],\n};\n\nexport function ErrorDetailView({ errorId, onBack }: ErrorDetailViewProps)\n{\n const [data, setData] = useState<any>(null);\n const [isLoading, setIsLoading] = useState(true);\n const [isMutating, setIsMutating] = useState(false);\n\n const fetchDetail = useCallback(async () =>\n {\n setIsLoading(true);\n try\n {\n const result = await monitorApi.getErrorDetail.call({ params: { id: errorId } });\n setData(result);\n }\n finally\n {\n setIsLoading(false);\n }\n }, [errorId]);\n\n useEffect(() =>\n {\n fetchDetail();\n }, [fetchDetail]);\n\n async function handleStatusChange(status: string)\n {\n setIsMutating(true);\n try\n {\n await monitorApi.updateErrorStatus.call({\n params: { id: errorId },\n body: { status },\n });\n await fetchDetail();\n }\n finally\n {\n setIsMutating(false);\n }\n }\n\n if (isLoading || !data)\n {\n return <div className=\"text-sm text-neutral-500\">Loading...</div>;\n }\n\n const { group, events } = data;\n const actions = STATUS_ACTIONS[group.status as ErrorGroupStatus] ?? [];\n\n return (\n <div className=\"space-y-6\">\n {/* Header */}\n <div className=\"flex items-center gap-3\">\n {onBack && (\n <button\n onClick={onBack}\n className=\"text-sm text-neutral-500 hover:text-neutral-700 dark:hover:text-neutral-300\"\n >\n &larr; Back\n </button>\n )}\n <h2 className=\"text-lg font-semibold text-neutral-900 dark:text-neutral-100\">\n {group.name} — {group.statusCode}\n </h2>\n </div>\n\n {/* Error Info */}\n <div className=\"rounded-lg border border-neutral-200 dark:border-neutral-800 p-4 space-y-3\">\n <p className=\"text-neutral-700 dark:text-neutral-300\">{group.message}</p>\n <div className=\"grid grid-cols-2 md:grid-cols-4 gap-4 text-sm\">\n <div>\n <span className=\"text-neutral-500\">Method</span>\n <p className=\"font-mono\">{group.method}</p>\n </div>\n <div>\n <span className=\"text-neutral-500\">Path</span>\n <p className=\"font-mono\">{group.path}</p>\n </div>\n <div>\n <span className=\"text-neutral-500\">Count</span>\n <p className=\"font-mono\">{group.count}</p>\n </div>\n <div>\n <span className=\"text-neutral-500\">Status</span>\n <p className=\"font-medium\">{group.status}</p>\n </div>\n </div>\n\n {/* Status Actions */}\n {actions.length > 0 && (\n <div className=\"flex gap-2 pt-2\">\n {actions.map((action) => (\n <button\n key={action.target}\n onClick={() => handleStatusChange(action.target)}\n disabled={isMutating}\n className=\"px-3 py-1.5 text-sm rounded border border-neutral-300 dark:border-neutral-700 hover:bg-neutral-100 dark:hover:bg-neutral-800 disabled:opacity-50\"\n >\n {action.label}\n </button>\n ))}\n </div>\n )}\n </div>\n\n {/* Event Timeline */}\n <div>\n <h3 className=\"text-sm font-medium text-neutral-500 mb-3\">Recent Events</h3>\n <div className=\"space-y-2\">\n {(events as any[]).map((event: any) => (\n <div\n key={event.id}\n className=\"rounded border border-neutral-200 dark:border-neutral-800 p-3 text-sm\"\n >\n <div className=\"flex justify-between items-start\">\n <div className=\"space-y-1\">\n <div className=\"flex gap-3 text-neutral-500\">\n <span>User: {event.userId ?? '(anonymous)'}</span>\n <span>Request: {event.requestId ?? '(none)'}</span>\n </div>\n {event.stackTrace && (\n <pre className=\"text-xs text-neutral-600 dark:text-neutral-400 overflow-x-auto whitespace-pre-wrap mt-2 bg-neutral-50 dark:bg-neutral-900 p-2 rounded\">\n {event.stackTrace.split('\\n').slice(0, 5).join('\\n')}\n </pre>\n )}\n </div>\n <span className=\"text-xs text-neutral-400 whitespace-nowrap ml-4\">\n {new Date(event.createdAt).toLocaleString()}\n </span>\n </div>\n </div>\n ))}\n </div>\n </div>\n </div>\n );\n}\n","/**\n * @spfn/monitor - Log Viewer Component\n *\n * Searchable, filterable log list with expandable metadata\n */\n\nimport { useState, useEffect } from 'react';\nimport { monitorApi } from '@spfn/monitor';\n\nconst LEVEL_BADGE: Record<string, string> = {\n debug: 'bg-neutral-100 text-neutral-600 dark:bg-neutral-800 dark:text-neutral-400',\n info: 'bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400',\n warn: 'bg-yellow-100 text-yellow-700 dark:bg-yellow-900/30 dark:text-yellow-400',\n error: 'bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400',\n fatal: 'bg-red-200 text-red-800 dark:bg-red-900/50 dark:text-red-300',\n};\n\nexport function LogViewer()\n{\n const [level, setLevel] = useState('');\n const [search, setSearch] = useState('');\n const [logs, setLogs] = useState<any[]>([]);\n const [isLoading, setIsLoading] = useState(true);\n const [expanded, setExpanded] = useState<Set<number>>(new Set());\n\n useEffect(() =>\n {\n let cancelled = false;\n setIsLoading(true);\n\n monitorApi.listLogs.call({\n query: {\n ...(level ? { level } : {}),\n ...(search ? { search } : {}),\n limit: 100,\n },\n }).then((data) =>\n {\n if (!cancelled)\n {\n setLogs(data as any[]);\n setIsLoading(false);\n }\n }).catch(() =>\n {\n if (!cancelled)\n {\n setIsLoading(false);\n }\n });\n\n return () => \n {\n cancelled = true; \n };\n }, [level, search]);\n\n function toggleExpand(id: number)\n {\n setExpanded((prev) =>\n {\n const next = new Set(prev);\n if (next.has(id))\n {\n next.delete(id);\n }\n else\n {\n next.add(id);\n }\n\n return next;\n });\n }\n\n return (\n <div className=\"space-y-4\">\n {/* Filters */}\n <div className=\"flex gap-3\">\n <select\n value={level}\n onChange={(e) => setLevel(e.target.value)}\n className=\"rounded border border-neutral-300 dark:border-neutral-700 bg-white dark:bg-neutral-900 px-3 py-1.5 text-sm\"\n >\n <option value=\"\">All levels</option>\n <option value=\"debug\">Debug</option>\n <option value=\"info\">Info</option>\n <option value=\"warn\">Warn</option>\n <option value=\"error\">Error</option>\n <option value=\"fatal\">Fatal</option>\n </select>\n <input\n type=\"text\"\n placeholder=\"Search logs...\"\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n className=\"rounded border border-neutral-300 dark:border-neutral-700 bg-white dark:bg-neutral-900 px-3 py-1.5 text-sm flex-1\"\n />\n </div>\n\n {/* Log List */}\n {isLoading ? (\n <div className=\"text-sm text-neutral-500\">Loading...</div>\n ) : logs.length === 0 ? (\n <div className=\"text-sm text-neutral-500 py-8 text-center\">No logs found</div>\n ) : (\n <div className=\"space-y-1\">\n {logs.map((log: any) => (\n <div\n key={log.id}\n className=\"rounded border border-neutral-200 dark:border-neutral-800 text-sm\"\n >\n <div\n onClick={() => log.metadata && toggleExpand(log.id)}\n className={`flex items-start gap-3 p-2 ${log.metadata ? 'cursor-pointer hover:bg-neutral-50 dark:hover:bg-neutral-800/50' : ''}`}\n >\n <span className={`inline-block px-1.5 py-0.5 rounded text-xs font-medium ${LEVEL_BADGE[log.level] ?? ''}`}>\n {log.level}\n </span>\n <span className=\"flex-1 text-neutral-800 dark:text-neutral-200\">\n {log.message}\n </span>\n {log.source && (\n <span className=\"text-xs text-neutral-400 font-mono\">{log.source}</span>\n )}\n <span className=\"text-xs text-neutral-400 whitespace-nowrap\">\n {new Date(log.createdAt).toLocaleTimeString()}\n </span>\n </div>\n {expanded.has(log.id) && log.metadata && (\n <pre className=\"px-3 py-2 text-xs bg-neutral-50 dark:bg-neutral-900 border-t border-neutral-200 dark:border-neutral-800 overflow-x-auto\">\n {JSON.stringify(log.metadata, null, 2)}\n </pre>\n )}\n </div>\n ))}\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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","names":["useState","useState","useEffect","monitorApi","jsx","jsxs","useState","useEffect","useCallback","monitorApi","jsx","jsxs","useState","useEffect","monitorApi","jsx","jsxs","jsx","jsxs","useState"]}
package/dist/server.d.ts CHANGED
@@ -85,11 +85,11 @@ declare const errorEvents: drizzle_orm_pg_core.PgTableWithColumns<{
85
85
  name: `${string}_id`;
86
86
  tableName: "error_events";
87
87
  dataType: "number";
88
- columnType: "PgBigSerial53";
88
+ columnType: "PgBigInt53";
89
89
  data: number;
90
- driverParam: number;
90
+ driverParam: string | number;
91
91
  notNull: true;
92
- hasDefault: true;
92
+ hasDefault: false;
93
93
  isPrimaryKey: false;
94
94
  isAutoincrement: false;
95
95
  hasRuntimeDefault: false;
package/dist/server.js CHANGED
@@ -963,14 +963,14 @@ function Number2(options) {
963
963
  }
964
964
 
965
965
  // ../../node_modules/.pnpm/@sinclair+typebox@0.34.41/node_modules/@sinclair/typebox/build/esm/type/string/string.mjs
966
- function String(options) {
966
+ function String2(options) {
967
967
  return CreateType({ [Kind]: "String", type: "string" }, options);
968
968
  }
969
969
 
970
970
  // ../../node_modules/.pnpm/@sinclair+typebox@0.34.41/node_modules/@sinclair/typebox/build/esm/type/template-literal/syntax.mjs
971
971
  function* FromUnion(syntax) {
972
972
  const trim = syntax.trim().replace(/"|'/g, "");
973
- return trim === "boolean" ? yield Boolean() : trim === "number" ? yield Number2() : trim === "bigint" ? yield BigInt() : trim === "string" ? yield String() : yield (() => {
973
+ return trim === "boolean" ? yield Boolean() : trim === "number" ? yield Number2() : trim === "bigint" ? yield BigInt() : trim === "string" ? yield String2() : yield (() => {
974
974
  const literals = trim.split("|").map((literal) => Literal(literal.trim()));
975
975
  return literals.length === 0 ? Never() : literals.length === 1 ? literals[0] : UnionEvaluated(literals);
976
976
  })();
@@ -1687,7 +1687,7 @@ function FromPromise2(left, right) {
1687
1687
  return IsStructuralRight(right) ? StructuralRight(left, right) : type_exports.IsObject(right) && IsObjectPromiseLike(right) ? ExtendsResult.True : !type_exports.IsPromise(right) ? ExtendsResult.False : IntoBooleanResult(Visit3(left.item, right.item));
1688
1688
  }
1689
1689
  function RecordKey(schema) {
1690
- return PatternNumberExact in schema.patternProperties ? Number2() : PatternStringExact in schema.patternProperties ? String() : Throw("Unknown record key pattern");
1690
+ return PatternNumberExact in schema.patternProperties ? Number2() : PatternStringExact in schema.patternProperties ? String2() : Throw("Unknown record key pattern");
1691
1691
  }
1692
1692
  function RecordValue(schema) {
1693
1693
  return PatternNumberExact in schema.patternProperties ? schema.patternProperties[PatternNumberExact] : PatternStringExact in schema.patternProperties ? schema.patternProperties[PatternStringExact] : Throw("Unable to get record value schema");
@@ -1707,8 +1707,8 @@ function FromRecord(left, right) {
1707
1707
  return IsStructuralRight(right) ? StructuralRight(left, right) : type_exports.IsObject(right) ? FromObjectRight(left, right) : !type_exports.IsRecord(right) ? ExtendsResult.False : Visit3(RecordValue(left), RecordValue(right));
1708
1708
  }
1709
1709
  function FromRegExp(left, right) {
1710
- const L = type_exports.IsRegExp(left) ? String() : left;
1711
- const R = type_exports.IsRegExp(right) ? String() : right;
1710
+ const L = type_exports.IsRegExp(left) ? String2() : left;
1711
+ const R = type_exports.IsRegExp(right) ? String2() : right;
1712
1712
  return Visit3(L, R);
1713
1713
  }
1714
1714
  function FromStringRight(left, right) {
@@ -1937,7 +1937,7 @@ function RecordPattern(record) {
1937
1937
  }
1938
1938
  function RecordKey2(type) {
1939
1939
  const pattern = RecordPattern(type);
1940
- return pattern === PatternStringExact ? String() : pattern === PatternNumberExact ? Number2() : String({ pattern });
1940
+ return pattern === PatternStringExact ? String2() : pattern === PatternNumberExact ? Number2() : String2({ pattern });
1941
1941
  }
1942
1942
  function RecordValue2(type) {
1943
1943
  return type.patternProperties[RecordPattern(type)];
@@ -2606,7 +2606,7 @@ __export(type_exports2, {
2606
2606
  Required: () => Required,
2607
2607
  Rest: () => Rest,
2608
2608
  ReturnType: () => ReturnType,
2609
- String: () => String,
2609
+ String: () => String2,
2610
2610
  Symbol: () => Symbol2,
2611
2611
  TemplateLiteral: () => TemplateLiteral,
2612
2612
  Transform: () => Transform,
@@ -3045,26 +3045,30 @@ async function trackError(err, ctx, metadata) {
3045
3045
  firstSeenAt: now,
3046
3046
  lastSeenAt: now
3047
3047
  });
3048
- const event = await createEvent(group.id, err, ctx, metadata);
3048
+ const event = await safeCreateEvent(group.id, err, ctx, metadata);
3049
3049
  logger2.info("New error group tracked", { fingerprint, groupId: group.id });
3050
- notifyErrorToSlack(group, event, "new", ctx.environment).catch((e) => logger2.warn("Slack notification failed", e));
3050
+ if (event) {
3051
+ notifyErrorToSlack(group, event, "new", ctx.environment).catch((e) => logger2.warn("Slack notification failed", e));
3052
+ }
3051
3053
  return;
3052
3054
  }
3053
3055
  if (existing.status === "resolved") {
3054
3056
  await errorGroupsRepository.updateStatus(existing.id, "active");
3055
3057
  await errorGroupsRepository.incrementCount(existing.id);
3056
- const event = await createEvent(existing.id, err, ctx, metadata);
3058
+ const event = await safeCreateEvent(existing.id, err, ctx, metadata);
3057
3059
  logger2.info("Error group reopened", { fingerprint, groupId: existing.id });
3058
- notifyErrorToSlack(
3059
- { ...existing, status: "active", count: existing.count + 1 },
3060
- event,
3061
- "reopened",
3062
- ctx.environment
3063
- ).catch((e) => logger2.warn("Slack notification failed", e));
3060
+ if (event) {
3061
+ notifyErrorToSlack(
3062
+ { ...existing, status: "active", count: existing.count + 1 },
3063
+ event,
3064
+ "reopened",
3065
+ ctx.environment
3066
+ ).catch((e) => logger2.warn("Slack notification failed", e));
3067
+ }
3064
3068
  return;
3065
3069
  }
3066
3070
  await errorGroupsRepository.incrementCount(existing.id);
3067
- await createEvent(existing.id, err, ctx, metadata);
3071
+ await safeCreateEvent(existing.id, err, ctx, metadata);
3068
3072
  }
3069
3073
  async function updateErrorGroupStatus(groupId, newStatus) {
3070
3074
  const group = await errorGroupsRepository.findById(groupId);
@@ -3082,17 +3086,23 @@ async function updateErrorGroupStatus(groupId, newStatus) {
3082
3086
  });
3083
3087
  return updated;
3084
3088
  }
3085
- async function createEvent(groupId, err, ctx, metadata) {
3086
- return await errorEventsRepository.create({
3087
- groupId,
3088
- requestId: ctx.requestId,
3089
- userId: ctx.userId,
3090
- statusCode: ctx.statusCode,
3091
- headers: ctx.headers,
3092
- query: ctx.query,
3093
- stackTrace: err.stack,
3094
- metadata
3095
- });
3089
+ async function safeCreateEvent(groupId, err, ctx, metadata) {
3090
+ try {
3091
+ return await errorEventsRepository.create({
3092
+ groupId,
3093
+ requestId: ctx.requestId,
3094
+ userId: ctx.userId,
3095
+ statusCode: ctx.statusCode,
3096
+ headers: ctx.headers,
3097
+ query: ctx.query,
3098
+ stackTrace: err.stack,
3099
+ metadata
3100
+ });
3101
+ } catch (e) {
3102
+ const cause = e instanceof Error && e.cause instanceof Error ? e.cause : e;
3103
+ logger2.warn("Failed to create error event", cause, { groupId });
3104
+ return null;
3105
+ }
3096
3106
  }
3097
3107
 
3098
3108
  // src/server/services/log.service.ts
@@ -3292,7 +3302,7 @@ function createMonitorErrorHandler(options = {}) {
3292
3302
  path: ctx.path,
3293
3303
  method: ctx.method,
3294
3304
  requestId: ctx.requestId,
3295
- userId: ctx.userId,
3305
+ userId: ctx.userId != null ? String(ctx.userId) : void 0,
3296
3306
  headers: ctx.request.headers,
3297
3307
  query: ctx.request.query,
3298
3308
  environment: options.environment