90 lines · 3.5 KB
import { all, batch, chunks, first, placeholders, stmt } from "./db";
import { allocateIssueNumber, indexIssue } from "./issues";
import type { IssueRow } from "./issues";
import { addIssueEvent } from "./pulls";
import { assertBelowCap } from "./limits";
import type { RepoRow } from "~/lib/types";

/** Per-scope count caps for workstream 6 entities; kept here so `limits.ts` stays merge-friendly. */
export const PLANNING_CAPS = {
  milestonesPerRepo: 100,
  issueTypesPerRepo: 25,
  subIssuesPerParent: 100,
  pinnedPerRepo: 3,
  projectsPerOwner: 100,
  fieldsPerProject: 30,
  optionsPerField: 50,
  itemsPerProject: 1000,
  categoriesPerRepo: 25,
  discussionCommentsPerThread: 2000,
} as const;

export async function assertCount(sql: string, param: string, cap: number, what: string): Promise<void> {
  const row = await first<{ count: number }>(sql, param);
  assertBelowCap(row?.count ?? 0, cap, what);
}

export type Progress = { total: number; closed: number };

/** Closed/total counts of sub-issues for each parent issue id. */
export async function subIssueProgress(parentIds: string[]): Promise<Map<string, Progress>> {
  const out = new Map<string, Progress>();
  for (const part of chunks([...new Set(parentIds)])) {
    const rows = await all<{ parent_id: string; total: number; closed: number }>(
      `SELECT parent_id, COUNT(*) AS total, SUM(state = 'closed') AS closed FROM issues
       WHERE parent_id IN (${placeholders(part.length)}) GROUP BY parent_id`,
      ...part,
    );
    for (const row of rows) out.set(row.parent_id, { total: row.total, closed: row.closed ?? 0 });
  }
  return out;