/** * Shared helper for building a parameterized SQL `IN (...)` clause — one * bound placeholder per id, never a joined/interpolated value list (CWE-89). * First introduced for the report services' optional account * (`transactions.source_id`) filter (Issue #273); reused by every service * that accepts an `accountIds?: number[]` filter so the placeholder * bookkeeping (start index, one `$N` per id) lives in exactly one place. * * Returns `null` when `ids` is empty/undefined so callers can skip adding the * clause entirely — that is the "no filter" case, and the query must stay * byte-identical to how it read before the filter existed. */ export function inPlaceholders(ids: number[] | undefined, startIndex: number): string | null { if (!ids || ids.length === 0) return null; return ids.map((_, i) => `$${startIndex + i}`).join(", "); }