Skip to content

Conversation

@gastonfournier
Copy link
Contributor

@gastonfournier gastonfournier commented Nov 26, 2025

About the changes

  • Made HTTP retry count configurable via httpOptions.maxRetries (defaults unchanged) and switched httpOptions.agent → dispatcher to align with undici dispatchers (src/request.ts, src/http-options.ts).
  • Updated ky client setup so the retry limit comes from httpOptions.maxRetries, and removed per-request retry overrides to let the shared client control retries (src/request.ts).
  • Re-enabled the 429 backoff tests and set httpOptions: { maxRetries: 0 } so they run fast without extra retry noise; adjusted nock setups accordingly (src/test/repository/repository.test.ts).

@gastonfournier gastonfournier added the 🤖👤 mostly-ai LLM-led with human assistance label Nov 26, 2025
@gastonfournier gastonfournier self-assigned this Nov 26, 2025
@gastonfournier gastonfournier changed the title feat: replace make-fetch-happen with ky and refresh tests/deps feat!: replace make-fetch-happen with ky and refresh tests/deps Nov 26, 2025
@gastonfournier gastonfournier moved this from New to Bots in Issues and PRs Nov 27, 2025
@gastonfournier gastonfournier moved this from Bots to Todo in Issues and PRs Nov 27, 2025
@coveralls
Copy link

coveralls commented Dec 1, 2025

Pull Request Test Coverage Report for Build 20067960829

Warning: This coverage report may be inaccurate.

This pull request's base commit is no longer the HEAD commit of its target branch. This means it includes changes from outside the original pull request, including, potentially, unrelated coverage changes.

Details

  • 65 of 71 (91.55%) changed or added relevant lines in 6 files are covered.
  • 3 unchanged lines in 1 file lost coverage.
  • Overall coverage decreased (-0.6%) to 89.277%

Changes Missing Coverage Covered Lines Changed/Added Lines %
src/request.ts 40 41 97.56%
src/client-spec-version.ts 10 15 66.67%
Files with Coverage Reduction New Missed Lines %
src/repository/polling-fetcher.ts 3 83.85%
Totals Coverage Status
Change from base Build 19896466463: -0.6%
Covered Lines: 1226
Relevant Lines: 1318

💛 - Coveralls

Copilot AI review requested due to automatic review settings December 4, 2025 17:46
This reverts commit b5882b0.

This comment was marked as outdated.

This comment was marked as outdated.

  - add undici agents/dispatcher bridge so ky honors agent options per call
  - preserve rejectUnauthorized and proxy support without global env mutation
  - keep ky throwing on HTTP errors and normalize to Response via helper
  - remove unused http/https-proxy-agent and update request agent tests to undici

This comment was marked as outdated.

custom: this.urlHeaders,
}),
retry: {
retries: 2,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

2 is default retry

etag: undefined,
contentType: undefined,
custom: this.headers,
specVersionSupported: '5.2.0',
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This was always out of sync so I added client-spec-version.ts helper (please review it)

Comment on lines +150 to +154
appName,
instanceId,
connectionId,
timeout,
httpOptions,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Turned this into an immutable configuration per client. Maybe we should still enable different timeouts per request, and perhaps some httpOptions, but I also don't think we need them

Copy link

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 13 out of 15 changed files in this pull request and generated 5 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

connectionId,
interval,
etag: undefined,
contentType: 'application/json',
Copy link

Copilot AI Dec 5, 2025

Choose a reason for hiding this comment

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

Consider removing the explicit contentType: 'application/json' assignment and instead relying on ky's default behavior for JSON requests. When using the json option in ky, it automatically sets the Content-Type header to application/json, making this explicit assignment redundant.

Suggested change
contentType: 'application/json',

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think this is fine

interval,
etag,
headers,
connectionId,
Copy link

Copilot AI Dec 5, 2025

Choose a reason for hiding this comment

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

Missing contentType parameter in the buildHeaders call. While not required for GET requests, it's inconsistent with the POST request implementation. Consider explicitly passing contentType: undefined for clarity and consistency:

headers: buildHeaders({
  appName,
  instanceId,
  interval,
  etag,
  headers,
  connectionId,
  contentType: undefined,
}),
Suggested change
connectionId,
connectionId,
contentType: undefined,

Copilot uses AI. Check for mistakes.

export interface HttpOptions {
agent?: (url: URL) => Agent;
dispatcher?: (url: URL) => Dispatcher; // this is a breaking change from 'agent'. Ref: https://github.com/Unleash/unleash-node-sdk/pull/332
Copy link

Copilot AI Dec 5, 2025

Choose a reason for hiding this comment

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

Breaking change documentation: While the inline comment mentions this is a breaking change and references PR #332, consider documenting this breaking change more prominently in the PR description or changelog. The change from agent (which used Node.js http/https Agent) to dispatcher (which uses undici Dispatcher) requires users to update their code.

Additionally, consider adding a deprecation period or providing a migration guide for users who were using the agent option.

Suggested change
dispatcher?: (url: URL) => Dispatcher; // this is a breaking change from 'agent'. Ref: https://github.com/Unleash/unleash-node-sdk/pull/332
/**
* @deprecated Use `dispatcher` instead. The `agent` option (Node.js http/https Agent) has been replaced by `dispatcher` (undici Dispatcher).
* See migration guide: https://github.com/Unleash/unleash-node-sdk/pull/332
*/
agent?: any;
/**
* Custom dispatcher for HTTP requests.
* This replaces the deprecated `agent` option.
* See migration guide: https://github.com/Unleash/unleash-node-sdk/pull/332
*/
dispatcher?: (url: URL) => Dispatcher;

Copilot uses AI. Check for mistakes.
expect(calls).toBe(3);
if (finished || calls < expectedCalls) return;
finished = true;
expect(calls).toBeGreaterThanOrEqual(expectedCalls);
Copy link

Copilot AI Dec 5, 2025

Choose a reason for hiding this comment

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

The test assertion uses toBeGreaterThanOrEqual instead of a strict equality check. While this makes the test more resilient, it also makes it less precise. Consider using toBe(expectedCalls) if you want to verify exact retry behavior:

expect(calls).toBe(expectedCalls);

This would ensure that retries are working exactly as configured (2 retries + 1 initial = 3 total).

Copilot uses AI. Check for mistakes.
Comment on lines +16 to +44
const getDefaultAgent = (url: URL, rejectUnauthorized?: boolean): Dispatcher => {
const proxy = getProxyForUrl(url.href);
const connect = rejectUnauthorized === undefined ? undefined : { rejectUnauthorized };

if (!proxy || proxy === '') {
return new UndiciAgent({ connect });
}

return new ProxyAgent({ uri: proxy, connect });
};

const createFetchWithDispatcher =
(httpOptions?: HttpOptions): typeof fetch =>
(input: string | URL | globalThis.Request, init?: RequestInit) => {
const resolveDispatcher =
httpOptions?.dispatcher ||
((targetUrl: URL) => getDefaultAgent(targetUrl, httpOptions?.rejectUnauthorized));

const getUrl = (): URL =>
typeof input === 'string' || input instanceof URL ? new URL(input) : new URL(input.url);

const dispatcher = resolveDispatcher(getUrl());

return fetch(input, {
...(init ?? {}),
// @ts-expect-error - dispatcher is not part of RequestInit, but undici accepts it.
dispatcher,
});
};
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not very happy with these AI shenanigans... It's supposed to be replicating rejectUnauthorized behavior from make-fetch-happen. And maintaining the ability to inject an agent (now Dispatcher), introduced in #332

If we refactor this part and/or decide to drop #332 I'd be ok.

interface RequestOptions {
url: string;
timeout?: number;
interval?: number;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Something for a future refactor, why would the interval be bound to a request? The only explanation is that we have in on a header, but I resisted from refactoring this part

@gastonfournier gastonfournier added 👤🤝🤖 half-half Roughly equal human-LLM collaboration and removed 🤖👤 mostly-ai LLM-led with human assistance labels Dec 5, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

👤🤝🤖 half-half Roughly equal human-LLM collaboration

Projects

Status: Todo

Development

Successfully merging this pull request may close these issues.

3 participants