( table: TableDefinition, rows: TableRow[], opts?: ScheduleOpts )
| 178 | /** Pure eligibility filter + payload building. Shared by the auto-fire path |
| 179 | * (`scheduleRunsForRows`) and the dispatcher's per-window batch path. */ |
| 180 | export function buildPendingRuns( |
| 181 | table: TableDefinition, |
| 182 | rows: TableRow[], |
| 183 | opts?: ScheduleOpts |
| 184 | ): WorkflowGroupCellPayload[] { |
| 185 | const allGroups = table.schema.workflowGroups ?? [] |
| 186 | if (allGroups.length === 0) return [] |
| 187 | if (rows.length === 0) return [] |
| 188 | |
| 189 | const groupIdFilter = opts?.groupIds |
| 190 | ? new Set(opts.groupIds) |
| 191 | : opts?.groupId |
| 192 | ? new Set([opts.groupId]) |
| 193 | : null |
| 194 | const groups = groupIdFilter ? allGroups.filter((g) => groupIdFilter.has(g.id)) : allGroups |
| 195 | if (groups.length === 0) return [] |
| 196 | |
| 197 | const orderedRows = rows.length <= 1 ? rows : [...rows].sort((a, b) => a.position - b.position) |
| 198 | |
| 199 | const pendingRuns: WorkflowGroupCellPayload[] = [] |
| 200 | const reasonCounts: Partial<Record<EligibilityReason, number>> = {} |
| 201 | |
| 202 | for (const row of orderedRows) { |
| 203 | for (const group of groups) { |
| 204 | const reason = classifyEligibility(group, row, { |
| 205 | isManualRun: opts?.isManualRun, |
| 206 | mode: opts?.mode, |
| 207 | }) |
| 208 | reasonCounts[reason] = (reasonCounts[reason] ?? 0) + 1 |
| 209 | if (reason !== 'eligible' && reason !== 'manual-bypass') continue |
| 210 | pendingRuns.push({ |
| 211 | tableId: table.id, |
| 212 | tableName: table.name, |
| 213 | rowId: row.id, |
| 214 | groupId: group.id, |
| 215 | workflowId: group.workflowId, |
| 216 | ...(group.enrichmentId ? { enrichmentId: group.enrichmentId } : {}), |
| 217 | workspaceId: table.workspaceId, |
| 218 | executionId: generateId(), |
| 219 | }) |
| 220 | } |
| 221 | } |
| 222 | |
| 223 | logger.debug( |
| 224 | `[Cascade] table=${table.id} rows=${rows.length} groups=${groups.length} manual=${opts?.isManualRun ?? false} mode=${opts?.mode ?? 'all'} reasons=${JSON.stringify(reasonCounts)}` |
| 225 | ) |
| 226 | |
| 227 | return pendingRuns |
| 228 | } |
| 229 | |
| 230 | /** Build the per-cell `{payload, options}` items for `queue.batchEnqueue` / |
| 231 | * `queue.batchEnqueueAndWait`. Hydrates trigger.dev tags, concurrency keys, |
no test coverage detected