Skip to content

Refactor: extract paginatedFetch and fetchWithRetry into src/api-utils.ts#20

Merged
shouze merged 1 commit intomainfrom
refactor/dry-api-pagination-retry
Feb 23, 2026
Merged

Refactor: extract paginatedFetch and fetchWithRetry into src/api-utils.ts#20
shouze merged 1 commit intomainfrom
refactor/dry-api-pagination-retry

Conversation

@shouze
Copy link
Contributor

@shouze shouze commented Feb 22, 2026

Root cause / motivation

src/api.ts contained duplicated pagination patterns and no rate-limit handling. A 429 or 503 from GitHub caused an immediate crash or silent data loss (the break on team-repo errors was swallowing errors without any diagnostic).

Changes

New: src/api-utils.ts

Two async helpers (performs network I/O — documented in AGENTS.md as part of the allowed side-effectful surface alongside api.ts):

  • paginatedFetch<T>(fetchPage, pageSize?, delayMs?) — calls fetchPage(page) starting at 1, accumulates results, stops when a page returns fewer items than pageSize.
  • fetchWithRetry(url, options, maxRetries?) — retries on 429 and 503 with exponential backoff (1s × 2^attempt, capped at 60 s, ±10 % jitter). Reads Retry-After header when present. Drains the response body with res.body?.cancel() before sleeping to allow HTTP connection reuse. Returns the last Response after maxRetries exhausted — callers still check res.ok.

New: src/api-utils.test.ts

14 unit tests covering all branches with mocked fetch and instant setTimeout.

Refactored: src/api.ts

  • searchCode: bare fetchfetchWithRetry
  • fetchAllResults (search pagination): while loop → paginatedFetch (250 ms inter-page delay preserved)
  • fetchAllResults (raw file resolution): bare fetchfetchWithRetry
  • fetchRepoTeams (list teams): bare fetchfetchWithRetry; pagination remains a manual while loop to filter matching slugs per page and avoid accumulating the full team list in memory for large orgs
  • fetchRepoTeams (repos per team): bare fetchfetchWithRetry; pagination remains a manual while loop to update repoTeams incrementally per page and avoid holding a full repos array per team in memory
  • Fix: silent break on HTTP error for team repos now reads and logs the body (truncated at 200 chars) as a dim warning; 404 (nested/secret teams) is still silent
  • Extract githubHeaders() helper to deduplicate the three identical header objects

Updated: AGENTS.md

  • Added api-utils.ts to the project layout table
  • Updated the architectural note on side effects and the testing note to cover api-utils.ts

How to verify

bun test               # 309 pass, 0 fail
bun run lint           # 0 warnings, 0 errors
bun run format:check   # all files formatted
bun run knip           # no unused exports
bun run build.ts       # binary compiles

Behaviour

No observable behaviour change for the end user. The only semantic differences are:

  • Transient 429/503 errors are now retried instead of crashing
  • Non-404 HTTP errors on team-repo fetches now log a diagnostic warning instead of silently stopping

Closes #17

Copilot AI review requested due to automatic review settings February 22, 2026 23:02
@github-actions
Copy link

Coverage after merging refactor/dry-api-pagination-retry into main will be

97.35%

Coverage Report
FileStmtsBranchesFuncsLinesUncovered Lines
src
   aggregate.ts100%100%100%100%
   api-utils.ts97.96%100%83.33%100%
   api.ts98.40%100%100%98.10%243–245
   group.ts100%100%100%100%
   output.ts99.12%100%94.74%99.52%58
   render.ts97.20%100%100%97.14%142–145
   upgrade.ts77.01%100%88.89%75.64%128, 130–132, 79–93
src/render
   filter.ts100%100%100%100%
   highlight.ts99.29%100%100%99.01%184–185
   rows.ts100%100%100%100%
   selection.ts100%100%100%100%
   summary.ts100%100%100%100%

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR refactors GitHub API access by extracting shared pagination and retry behavior out of src/api.ts into a dedicated utility module, with accompanying unit tests, and then updates the API client to use those helpers.

Changes:

  • Added fetchWithRetry (429/503 retry with backoff/jitter) and paginatedFetch helpers in src/api-utils.ts.
  • Added unit tests for the new helpers in src/api-utils.test.ts.
  • Refactored src/api.ts to deduplicate GitHub request headers and replace several direct fetch / pagination loops with the new helpers.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 7 comments.

File Description
src/api.ts Uses fetchWithRetry/paginatedFetch, deduplicates GitHub headers, and adjusts error handling/logging for team repo fetches.
src/api-utils.ts Introduces shared retry + pagination helpers intended for GitHub API usage.
src/api-utils.test.ts Adds unit tests for retry logic and pagination behavior.

@shouze shouze force-pushed the refactor/dry-api-pagination-retry branch from c727a85 to 7f2887c Compare February 22, 2026 23:08
@github-actions
Copy link

Coverage after merging refactor/dry-api-pagination-retry into main will be

97.35%

Coverage Report
FileStmtsBranchesFuncsLinesUncovered Lines
src
   aggregate.ts100%100%100%100%
   api-utils.ts97.96%100%83.33%100%
   api.ts98.40%100%100%98.10%243–245
   group.ts100%100%100%100%
   output.ts99.12%100%94.74%99.52%58
   render.ts97.20%100%100%97.14%142–145
   upgrade.ts77.01%100%88.89%75.64%128, 130–132, 79–93
src/render
   filter.ts100%100%100%100%
   highlight.ts99.29%100%100%99.01%184–185
   rows.ts100%100%100%100%
   selection.ts100%100%100%100%
   summary.ts100%100%100%100%

@shouze shouze self-assigned this Feb 22, 2026
- Add src/api-utils.ts with two pure-async helpers:
  - paginatedFetch<T>: replaces the three duplicated while-pagination loops
  - fetchWithRetry: retries on 429/503 with exponential backoff and Retry-After
    header support; returns last response after maxRetries exhausted
- Add src/api-utils.test.ts with 14 unit tests (mocked fetch, instant timers)
- Refactor src/api.ts to consume the new helpers:
  - searchCode: use fetchWithRetry instead of bare fetch
  - fetchAllResults: use fetchWithRetry for raw file content resolution
  - fetchRepoTeams (list teams): replace while loop with paginatedFetch
  - fetchRepoTeams (repos per team): replace while loop with paginatedFetch
  - Fix: silent break on HTTP error for team repos now logs a warning and
    returns [], instead of swallowing errors silently
  - Extract githubHeaders() helper to deduplicate header objects

Closes #17
@shouze shouze force-pushed the refactor/dry-api-pagination-retry branch from 7f2887c to 4690cb8 Compare February 22, 2026 23:20
@github-actions
Copy link

Coverage after merging refactor/dry-api-pagination-retry into main will be

96.49%

Coverage Report
FileStmtsBranchesFuncsLinesUncovered Lines
src
   aggregate.ts100%100%100%100%
   api-utils.ts100%100%100%100%
   api.ts93.40%100%100%92.49%251–254, 257–265
   group.ts100%100%100%100%
   output.ts99.12%100%94.74%99.52%58
   render.ts97.20%100%100%97.14%142–145
   upgrade.ts77.01%100%88.89%75.64%128, 130–132, 79–93
src/render
   filter.ts100%100%100%100%
   highlight.ts99.29%100%100%99.01%184–185
   rows.ts100%100%100%100%
   selection.ts100%100%100%100%
   summary.ts100%100%100%100%

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated no new comments.

@shouze shouze merged commit b06d9bb into main Feb 23, 2026
12 checks passed
@shouze shouze deleted the refactor/dry-api-pagination-retry branch February 23, 2026 06:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Refactor: extract pagination helper and exponential backoff retry into src/api-utils.ts

2 participants