( tableId: string, dispatches?: DispatchRow[] )
| 271 | * count when no dispatch is active (orphan stragglers). `byRowId` stays |
| 272 | * sidecar-based — the client overlay renders queued rows ahead of the cursor. */ |
| 273 | export async function countActiveRunCells( |
| 274 | tableId: string, |
| 275 | dispatches?: DispatchRow[] |
| 276 | ): Promise<{ total: number; byRowId: Record<string, number> }> { |
| 277 | const active = dispatches ?? (await listActiveDispatches(tableId)) |
| 278 | if (active.length === 0) return countRunningCells(tableId) |
| 279 | |
| 280 | const countRowsAhead = async (d: DispatchRow): Promise<number> => { |
| 281 | const groupCount = d.scope.groupIds.length |
| 282 | if (groupCount === 0) return 0 |
| 283 | const filters = [eq(userTableRows.tableId, tableId), gt(userTableRows.position, d.cursor)] |
| 284 | if (d.scope.rowIds && d.scope.rowIds.length > 0) { |
| 285 | filters.push(inArray(userTableRows.id, d.scope.rowIds)) |
| 286 | } |
| 287 | const [row] = await db |
| 288 | .select({ rowsAhead: sql<number>`count(*)::int` }) |
| 289 | .from(userTableRows) |
| 290 | .where(and(...filters)) |
| 291 | let rowsAhead = row?.rowsAhead ?? 0 |
| 292 | // A `rows` cap means at most `max - processed` more rows will run, even if |
| 293 | // many more sit ahead of the cursor — clamp so the badge doesn't over-count. |
| 294 | if (d.limit?.type === 'rows') { |
| 295 | rowsAhead = Math.min(rowsAhead, Math.max(0, d.limit.max - d.processedCount)) |
| 296 | } |
| 297 | return rowsAhead * groupCount |
| 298 | } |
| 299 | |
| 300 | // Include pre-stamps so `byRowId` matches the live SSE count (which counts |
| 301 | // `pending`); otherwise the badge flickers 20→0 on each refetch. |
| 302 | const [sidecar, perDispatch] = await Promise.all([ |
| 303 | countRunningCells(tableId, { includeUnclaimedPreStamps: true }), |
| 304 | Promise.all(active.map(countRowsAhead)), |
| 305 | ]) |
| 306 | const total = perDispatch.reduce((sum, n) => sum + n, 0) |
| 307 | return { total, byRowId: sidecar.byRowId } |
| 308 | } |
| 309 | |
| 310 | export async function listActiveDispatches(tableId: string): Promise<DispatchRow[]> { |
| 311 | const rows = await db |
no test coverage detected