(opts: {
tableId: string
workspaceId: string
mode: DispatchMode
requestId: string
groupIds?: string[]
rowIds?: string[]
/** "Select all under a filter" — run every row matching this filter (mutually exclusive with
* `rowIds`). Threaded into the dispatch scope so the dispatcher walks only matching rows. */
filter?: Filter
/** Select-all scope only: deselected rows — the dispatcher walk, eager clear, and pre-run
* cancel all skip them. */
excludeRowIds?: string[]
/** Optional cap on work before the dispatch completes (e.g. run only the
* first N eligible rows). Null/omitted = process every row in scope. */
limit?: DispatchLimit | null
/** When false, eligibility honors `autoRun: false` and treats completed
* cells as terminal — appropriate for auto-fire after row writes or
* schema changes. Defaults to true (user-initiated "Run column"). */
isManualRun?: boolean
/** User who triggered the run, for usage attribution. Omitted by auto-fire
* callers (row writes, CSV import) → falls back to the workspace billed
* account at billing time. */
triggeredByUserId?: string | null
})
| 621 | * workflow group on the table. `rowIds` omitted = every row. |
| 622 | */ |
| 623 | export async function runWorkflowColumn(opts: { |
| 624 | tableId: string |
| 625 | workspaceId: string |
| 626 | mode: DispatchMode |
| 627 | requestId: string |
| 628 | groupIds?: string[] |
| 629 | rowIds?: string[] |
| 630 | /** "Select all under a filter" — run every row matching this filter (mutually exclusive with |
| 631 | * `rowIds`). Threaded into the dispatch scope so the dispatcher walks only matching rows. */ |
| 632 | filter?: Filter |
| 633 | /** Select-all scope only: deselected rows — the dispatcher walk, eager clear, and pre-run |
| 634 | * cancel all skip them. */ |
| 635 | excludeRowIds?: string[] |
| 636 | /** Optional cap on work before the dispatch completes (e.g. run only the |
| 637 | * first N eligible rows). Null/omitted = process every row in scope. */ |
| 638 | limit?: DispatchLimit | null |
| 639 | /** When false, eligibility honors `autoRun: false` and treats completed |
| 640 | * cells as terminal — appropriate for auto-fire after row writes or |
| 641 | * schema changes. Defaults to true (user-initiated "Run column"). */ |
| 642 | isManualRun?: boolean |
| 643 | /** User who triggered the run, for usage attribution. Omitted by auto-fire |
| 644 | * callers (row writes, CSV import) → falls back to the workspace billed |
| 645 | * account at billing time. */ |
| 646 | triggeredByUserId?: string | null |
| 647 | }): Promise<{ dispatchId: string | null }> { |
| 648 | const { |
| 649 | tableId, |
| 650 | workspaceId, |
| 651 | mode, |
| 652 | requestId, |
| 653 | groupIds, |
| 654 | rowIds, |
| 655 | filter, |
| 656 | excludeRowIds, |
| 657 | limit, |
| 658 | triggeredByUserId, |
| 659 | } = opts |
| 660 | const isManualRun = opts.isManualRun ?? true |
| 661 | // Empty `rowIds` array means "scope explicitly empty" — auto-fire callers |
| 662 | // (CSV import on zero matches, etc.) end up here. Skip the dispatch entirely |
| 663 | // rather than walk the table with a no-match filter. |
| 664 | if (rowIds && rowIds.length === 0) return { dispatchId: null } |
| 665 | // Lazy imports: `./service` and `./dispatcher` both close cycles back to |
| 666 | // this module; `@trigger.dev/sdk` is heavy and only needed on this op. |
| 667 | const { getTableById } = await import('@/lib/table/service') |
| 668 | const table = await getTableById(tableId) |
| 669 | if (!table) throw new Error('Table not found') |
| 670 | if (table.workspaceId !== workspaceId) throw new Error('Invalid workspace ID') |
| 671 | |
| 672 | const allGroups = table.schema.workflowGroups ?? [] |
| 673 | const targetGroups = groupIds ? allGroups.filter((g) => groupIds.includes(g.id)) : allGroups |
| 674 | // Tables with no workflow groups are the majority. Auto-fire callers from |
| 675 | // every row write would otherwise produce error-level log spam on every |
| 676 | // PATCH/insert. Manual run-column callers always pass `groupIds` so they |
| 677 | // can't reach here with an empty target. |
| 678 | if (targetGroups.length === 0) return { dispatchId: null } |
| 679 | const targetGroupIds = targetGroups.map((g) => g.id) |
| 680 |
no test coverage detected