(sessionId: string, topN: number = 10)
| 151 | * Cache-first: overview cache + members cache, fallback to live SQL via core. |
| 152 | */ |
| 153 | export function getChatOverview(sessionId: string, topN: number = 10) { |
| 154 | const db = openDatabase(sessionId) |
| 155 | if (!db) return null |
| 156 | |
| 157 | const coreDb = asCoreDb(db) |
| 158 | const meta = getSessionMeta(coreDb) |
| 159 | if (!meta) return null |
| 160 | |
| 161 | const cacheDir = getCacheDir() |
| 162 | const overview = resolveOverview(db, sessionId, cacheDir) |
| 163 | const summaryCount = getSummaryCount(coreDb) |
| 164 | |
| 165 | let membersCache = getCache<MembersCache>(sessionId, CACHE_KEY_MEMBERS, cacheDir) |
| 166 | if (!membersCache) { |
| 167 | try { |
| 168 | membersCache = computeAndSetMembersCache(new BetterSqliteAdapter(db), sessionId, cacheDir) |
| 169 | } catch { |
| 170 | // fallback: no member data |
| 171 | } |
| 172 | } |
| 173 | |
| 174 | let topMembers: Array<{ id: number; name: string; count: number }> = [] |
| 175 | if (membersCache?.members) { |
| 176 | topMembers = Object.entries(membersCache.members) |
| 177 | .map(([id, stat]) => ({ id: Number(id), name: stat.name, count: stat.count })) |
| 178 | .sort((a, b) => b.count - a.count) |
| 179 | .slice(0, topN) |
| 180 | } |
| 181 | |
| 182 | return { |
| 183 | name: meta.name, |
| 184 | platform: meta.platform, |
| 185 | type: meta.type, |
| 186 | totalMessages: overview.totalMessages, |
| 187 | totalMembers: overview.totalMembers, |
| 188 | firstMessageTs: overview.firstMessageTs, |
| 189 | lastMessageTs: overview.lastMessageTs, |
| 190 | topMembers, |
| 191 | summaryCount, |
| 192 | } |
| 193 | } |
no test coverage detected