···437437 return out, nil
438438}
439439440440-// RecentRepos returns repos created since `since`, optionally filtered by
441441-// primary language. Pass limit <= 0 for no limit. Uses raw SQL for the
442442-// scalar star-count subquery and handle JOIN — both awkward in ent.
443443-func (s *Store) RecentRepos(ctx context.Context, language string, since time.Time, limit int) ([]Repo, error) {
440440+// ReposFilter narrows the result of RecentRepos. Zero values mean "no
441441+// filter" so callers can mix and match.
442442+type ReposFilter struct {
443443+ Language string // primary language match (post-filter in Go over JSON map)
444444+ Since time.Time // repos created at or after this time (zero = no time floor)
445445+ ForksOnly bool // only rows with non-empty source
446446+ NoForks bool // exclude rows with non-empty source
447447+}
448448+449449+// RecentRepos returns repos matching the filter, sorted newest first. Pass
450450+// limit <= 0 for no limit. Uses raw SQL because the scalar star-count
451451+// subquery and handle LEFT JOIN are awkward to express via ent's query API.
452452+func (s *Store) RecentRepos(ctx context.Context, f ReposFilter, limit int) ([]Repo, error) {
444453 candidateLimit := -1
445454 if limit > 0 {
446455 candidateLimit = limit
447447- if language != "" {
456456+ if f.Language != "" {
448457 candidateLimit = limit * 5
449458 }
450459 }
460460+ forksOnly := 0
461461+ if f.ForksOnly {
462462+ forksOnly = 1
463463+ }
464464+ noForks := 0
465465+ if f.NoForks {
466466+ noForks = 1
467467+ }
451468 rows, err := s.db.QueryContext(ctx, `
452469 SELECT r.at_uri, r.did, r.rkey, r.name, r.knot, r.description, r.topics,
453470 r.website, r.source, r.spindle, r.repo_did, r.created_at, r.seen_at,
···457474 LEFT JOIN handles h ON h.did = r.did
458475 WHERE r.created_at >= ?
459476 AND (? = '' OR r.languages IS NOT NULL)
477477+ AND (? = 0 OR (r.source IS NOT NULL AND r.source != ''))
478478+ AND (? = 0 OR r.source IS NULL OR r.source = '')
460479 ORDER BY r.created_at DESC
461480 LIMIT ?
462462- `, since.UnixMilli(), language, candidateLimit)
481481+ `, f.Since.UnixMilli(), f.Language, forksOnly, noForks, candidateLimit)
463482 if err != nil {
464483 return nil, err
465484 }
···471490 if err != nil {
472491 return nil, err
473492 }
474474- if language != "" && r.Primary() != language {
493493+ if f.Language != "" && r.Primary() != f.Language {
475494 continue
476495 }
477496 out = append(out, r)