release: http-client@0.7.0 - #10
Draft
afoures wants to merge 1 commit into
Draft
Conversation
afoures
force-pushed
the
release/http-client
branch
5 times, most recently
from
August 2, 2026 08:10
ee50eb3 to
ef4310f
Compare
afoures
force-pushed
the
release/http-client
branch
3 times, most recently
from
August 6, 2026 06:16
6aad427 to
dd96108
Compare
afoures
force-pushed
the
release/http-client
branch
from
August 26, 2026 07:01
dd96108 to
551eaae
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR is managed by auto-release. Do not edit it manually.
Automated release for
http-clientVersion:
0.6.0→0.7.0Changelog
Breaking Changes
HttpClientConfigis now parameterized by the endpoint treeHttpClientConfig<client_context>becameHttpClientConfig<endpoints, default_context?>, so the client-levelcontextshape is derived from the endpoints instead of being restated by hand:The merged context shape is also exported as
ClientContext<endpoints>, to constrain a wrapper's own context type parameter and keep the client-level defaults precise.default_contextdefaults tonever: without it, a config declares no client-level defaults, so itscontextis rejected and every declared context key stays required at the call site. Thread the type parameter to record the defaults a caller actually passed, which is what makes those keys optional per call.http_clientnow throws on an invalidbase_urlinstead of returning an error per callAn unparsable
base_urlis a static misconfiguration: it cannot depend on call input, so it was either always broken or never broken for a given client. It is now validated once inhttp_client, which throws aTypeError, rather than making every call return anUnexpectedErrorwithoperation: "base_url_validation".This is the only failure the client throws instead of returning as a value. Call sites that matched on
operation === "base_url_validation"no longer need that branch.Replace
@remix-run/route-patternwith a built-in pathname parserThe client no longer depends on
@remix-run/route-pattern. Pathname patterns are parsed in-house, which leaves the package with no runtime dependencies. The supported syntax is unchanged: static text,:param, optional groups(...)that nest, and several params in one segment (/v:major.:minor). Param names keep the JavaScript identifier charset,[a-zA-Z_$][a-zA-Z_$0-9]*.Percent-encoding of param values, dropping an optional group whose param is
undefinedornull, and reporting every missing required param rather than the first all behave as before.A leading optional group that is dropped no longer leaves a protocol-relative
//. Given/(:lang)/userswith nolang, the generated pathname is now/usersrather than//users, whichnew URL()resolved as the hostusersand so sent the request to a different origin.A
?or#in apathnameis now rejected, at the type level on the endpoint definition and at runtime when the pattern is compiled. Search params are declared withquery; previously a?was parsed as a search-constraint pattern and a#was emitted as path text thatnew URL()then reinterpreted as a fragment.Undocumented pattern syntax that came from the library is gone: wildcards (
*rest), enums ({a,b}), and protocol, hostname, port or search patterns. Only pathnames are supported.generate_urland theEndpointconstructor now throwPathnameErrorandMissingParamsError, exported from the package root, in place of the library'sCreateHrefError.A client-level
contextdefault for a key that endpoints declare with conflicting types is now a compile errorClientContextchecks, per key, that every endpoint declaring it agrees on its type. A key two endpoints declare asstringandnumberresolves to anErrorMessageinstead ofstring | number, so supplying a client-level default for it fails to compile.Previously this was accepted and unsound: the default made
tenantoptional at every call site, including the one needing anumber, so a string reached a schema factory expecting a number. The check fires on the value, not the tree: the same endpoints are fine as long as no client-level default is set for the conflicting key. Fix by aligning the type in every endpoint that declares the key, or by using separate clients. Mutually assignable declarations stay valid, sobooleanon both sides and a single endpoint'sstring | numberare unaffected.EndpointMapis no longer exported from the package rootThe default retry condition now retries transient failures instead of every non-ok response.
timeoutis now the call deadline, not a per-attempt boundIt used to bound each attempt, so
{ timeout: 5000, retry: { attempts: 4, delay: 1000 } }could run for 23 seconds while the config said 5. It now covers the whole call: every attempt, every retry delay, and response parsing.timeoutaccepts{ total?, attempt? }and merges per key, so a client-level{ attempt: 2000 }survives a per-call{ total: 5000 }.totalis terminal, so an expiry never reaches the retry condition;attemptis retryable, which is the point of it. This also fixes an attempt timeout firing during the retry delay, and an abort during a delay surfacing asUnexpectedError: Failed to check retry policyinstead of anAbortedErrorwithoperation: "retry_delay".timeout: 0now means immediately, not neverA truthy gate made
0disable the timeout, which was an oversight. Onlyundefineddisables it now, so{ total: 0 }gives aTimeoutErrorand zero attempts. That is the reading a call deadline needs: people writetimeout: { total: budget_remaining() }, and an exhausted budget must fail fast.Both keys are also floored and clamped to
0, so1.5and-1no longer throw aRangeErrorout of the call.NaNandInfinitycome back as anUnexpectedErrorwithoperation: "resolve_timeout"naming the key.ErrorContext.request.timeoutis now aTimeoutConfig, not a numberIt carries the normalized
{ total?, attempt? }the call ran under, soresult.context.request?.timeoutreads{ total: 5000 }where it used to read5000. Breaking for anyone reading it as a number.The default
"urlencoded"query encoder now handles array values and entry lists correctlyA value it cannot express (a nested object, or an entry that isn't a
[key, value]pair) now returns aSerializationErrornaming the key instead of writing[object Object].nullandundefinedare still skipped.serializealso stays optional for more schemas: numbers, booleans and array values are urlencoded-compatible now, so the cast that used to be needed to reach the comma-join is gone.Features
Add a
kinddiscriminant to every response envelope and error classEach arm of a call result now carries a
kindliteral named after its own type or class, exportedas
HTTPFetch.ResponseKindandErrorKind. Its everyday use is telling the redirect arm apartfrom the error responses, which
ok: falsealone does not:Because the error classes carry it too,
kindcan also narrow the whole union in oneswitchwithno
instanceofand no value import. That form is for when a prototype check cannot be trusted:after a spread, a clone or a serialization round-trip, or when two copies of this package end up
installed.
Reading a result is unaffected, since
kindis an added field. Code that builds an envelope byhand, such as a test fixture or a mock, has to add the matching
kindfor it to satisfy the type.