mirror of
https://github.com/stablyai/orca.git
synced 2026-09-22 16:02:32 +00:00
Replace the hand-rolled `AbortController` + `setTimeout(() => controller.abort())` + `clearTimeout` in `finally` pattern with `AbortSignal.timeout(ms)` across the main-process fetchers, updaters, and hosted-provider clients. This removes a timer-leak footgun (a thrown/early-returned path that skips the finally leaks the timer) and ~3-4 lines of bookkeeping per site. `AbortSignal.timeout` is Node 17.3+ (Electron main is Node 22+). Two sites compose a caller-cancel signal with the timeout via `AbortSignal.any` (Node 20.3+) instead of a manual abort listener: - git/fork-sync.ts: also fixes a latent bug — the caller's `options.signal` was spread into the git options then immediately clobbered by `signal: controller.signal`, so caller cancellation was silently dropped. `AbortSignal.any` restores it. - rate-limits/claude-fetcher.ts (fetchViaOAuth external signal). hosted-review-api-request.ts: `AbortSignal.timeout()` rejects with a `TimeoutError`, not an `AbortError`, so the timeout-detection branch is updated (otherwise `timedOut` would never be set). minimax-fetcher.test.ts: its timeout test drove the abort with fake timers, which cannot advance `AbortSignal.timeout`'s internal timer. Rewritten to fire the timeout with an already-aborted signal so it genuinely exercises the abort path. Deliberately NOT migrated: - src/relay/git-handler.ts: the relay targets Node 18 (`build-relay.mjs`, MIN_NODE_MAJOR = 18); `AbortSignal.any` needs Node 20.3+, and timeout-only would drop the request context signal. - ipc/feedback.ts: its timeout-driven fallback is verified with fake timers, which can't advance `AbortSignal.timeout`; kept on the manual pattern.
35 lines
1.2 KiB
TypeScript
35 lines
1.2 KiB
TypeScript
import { normalizeGitErrorMessage } from '../../shared/git-remote-error'
|
|
import {
|
|
syncForkDefaultBranch,
|
|
type GitForkSyncExpectedUpstream,
|
|
type GitForkSyncResult
|
|
} from '../../shared/git-fork-sync'
|
|
import type { GitRuntimeOptions } from './git-runtime-options'
|
|
import { gitOptionsForWorktree } from './git-runtime-options'
|
|
import { gitExecFileAsync } from './runner'
|
|
|
|
export async function gitSyncForkDefaultBranch(
|
|
worktreePath: string,
|
|
expectedUpstream: GitForkSyncExpectedUpstream,
|
|
options: GitRuntimeOptions = {}
|
|
): Promise<GitForkSyncResult> {
|
|
// Compose the caller's cancel signal with the 60s timeout so neither is lost —
|
|
// the caller's signal was previously clobbered by the timeout controller.
|
|
const signal = options.signal
|
|
? AbortSignal.any([options.signal, AbortSignal.timeout(60_000)])
|
|
: AbortSignal.timeout(60_000)
|
|
try {
|
|
return await syncForkDefaultBranch(
|
|
(args) =>
|
|
gitExecFileAsync(args, {
|
|
...gitOptionsForWorktree(worktreePath, options),
|
|
timeout: 60_000,
|
|
signal
|
|
}),
|
|
{ expectedUpstream }
|
|
)
|
|
} catch (error) {
|
|
throw new Error(normalizeGitErrorMessage(error, 'push'))
|
|
}
|
|
}
|