Changelog
3.97.1
(fix): Fix generated WebSocket socket classes registering their open/message/close/error
forwarding listeners twice when connect() is called after construction (the standard
new Socket(...).connect() usage). ReconnectingWebSocket gains a hasEventListener(type, listener)
method and connect() only registers each forwarding listener if it is not already attached, so
every consumer .on() callback fires exactly once per event while close() followed by
connect() continues to work.
3.97.0
(feat): Generate the shared WebhooksHelper from the API-wide api.settings.webhook-signature
setting in generators.yml (IR sdkConfig.webhookSignatureVerification). The helper is
emitted even when the API definition models no webhooks; webhook-specific signature
overrides still produce their own named helpers.
3.96.3
(fix): Wire tests for paginated endpoints whose page property is nested in the request body (e.g.
$request.options.offset or $request.options.pagination.cursor) no longer fail on getNextPage().
The generated pager creates the parent object (options) when the example omits it, and the mock
server’s jsonBody matcher treated that new parent as an unexpected field even though the nested leaf
was listed in ignoredFields, so the second request never matched the mock and failed with a
connection-refused error. The matcher now ignores a parent object that is absent from the expected body
when every leaf inside it is an ignored field.
3.96.2
(fix): The generated OAuth wire-test mock (tests/wire/mockAuth.ts) now expects the grant_type the generated
OAuthAuthProvider actually sends. When the token endpoint models grant_type as a plain (optional) string
and the example omits it, the provider sends grant_type: "client_credentials" but the mock previously only
matched the example body, so the token request never matched and every authenticated wire test failed with
a connection-refused error.
3.96.1
(chore): Bump bundled @fern-api/generator-cli to 0.10.1. Auto-versioned MAJOR/MINOR bumps no longer
produce a version-only changelog.md block when the AI analysis returns an empty changelog
entry; the PR description, version bump reason, or commit message body is used instead.
3.96.0
(feat): Object types with XML encoding metadata (e.g. TwiML schemas imported from an OpenAPI xml object) are now
generated as classes with toXml(), a static fromXml() parser, and a fluent Builder (including
Builder.fromXml(xml).build() and per-child methods such as response.say(...)). Element names,
attributes, text bodies, nested/union children, wrapped lists, list separators, namespaces and prefixes
are honored; undeclared attributes and child elements are preserved as additionalAttributes /
additionalChildren so documents round-trip. Parsing is strict (root name, scalar/enum validation) and
rejects DOCTYPE declarations. Non-XML types are unaffected and the XML runtime is only emitted when needed.
3.95.1
(fix): TypeScript dynamic snippets honor flattenRequestParameters by spreading referenced object bodies instead of emitting body: {...}, and nest multi-auth constructor options under the camelCase auth scheme key regardless of noSerdeLayer or retainOriginalCasing.
3.95.0
(feat): SDKs now read the base URL from the environment variable configured via base-url-env / x-fern-base-url-env when no baseUrl or environment option is passed, respecting guardProcessEnvAccess for the environment-variable read.
3.94.2
(fix): A union member whose discriminant value collides with a JavaScript built-in
no longer has the disambiguation prefix applied to the string literal.
A branch with condition_type: "Date" emitted
conditionType: "globalThis.Date" — so the property could not be typed
without a cast, since the API expects "Date", and with noSerdeLayer: false the serializer’s inferred discriminant no longer matched the declared
literal and the package failed to compile. Qualification exists to
disambiguate a shadowed type reference; a literal is a value, and
rewriting one changes what the SDK sends. String literals are now skipped,
which also covers builder expressions and visitor signatures, where the same
rewrite could reach an object literal. Type references are unaffected: a
sibling property typed Date still becomes globalThis.Date.
3.94.1
(fix): Fix generated wire tests for cursor, URI, and path pagination when the example
response uses an empty string to indicate there is no next page. Terminal URI
and path continuations are preserved in the mocked response, and the tests skip
hasNextPage() and getNextPage() assertions, matching the generated pager.
3.94.0
(feat): Add a websocketHandlerMode config option ("replace" | "accumulate", defaults to "replace")
for generated WebSocket Socket classes. With accumulate, multiple on() handlers for the same
event are all kept and run in registration order instead of the last one replacing the previous.
Generated sockets now also expose off(event, callback) to detach a handler in both modes.
Defaults to replace, so generated output is unchanged unless the option is enabled.
3.93.1
(fix): The generated README now always includes a Pagination section (and pagination usage snippets)
when the API has paginated endpoints, matching the client which always returns core.Page.
Previously the section was dropped when the CLI ran without org information (e.g. fern generate --local without a FERN_TOKEN).
3.93.0
(feat): Add an opt-in esmOnly configuration flag. When enabled, the generated package ships
only the existing ESM build: package.json is marked type: "module", the exports map
only exposes the .mjs/.d.mts entry points, and the CommonJS build (tsconfig.cjs.json,
build:cjs) is no longer generated. This avoids the dual package hazard for consumers who
want a pure-ESM package. The default behavior (dual CJS + ESM output, including with
outputEsm: true) is unchanged. esmOnly is incompatible with useLegacyExports and
bundle.
3.92.0
(feat): Add a new exactOptionalPropertyTypes config option (default false) that makes the
generated SDK compile cleanly under TypeScript’s exactOptionalPropertyTypes
compiler option. When enabled, every optional property is emitted as
prop?: T | undefined across model types, inlined request wrappers, client
options, errors, and the core/ utilities, and the generated tsconfig
files enable exactOptionalPropertyTypes so the generated package typechecks
itself under the stricter mode. Previously this behavior was partially
available only with noSerdeLayer: true; the new flag applies it uniformly,
including when the serde layer is enabled.
3.91.0
(feat): Add the opt-in deepObjectMapQueryParameters configuration option. When enabled, query parameters typed
as a map (e.g. map<string, string>, which is what an OpenAPI type: object + additionalProperties
schema imports as) are handed to the query builder and serialized in deepObject form
(?metadata[env]=prod) instead of being JSON-stringified (?metadata=%7B%22env%22...). Maps whose
values are objects, nested maps, lists, sets, or date-times are supported too: with the serde layer
enabled they are run through the generated schema first, so nested keys use wire names, sets become
arrays, and dates are ISO strings (?metadata[env][created_at]=2026-09-04T00:00:00.000Z). Defaults to
false, so existing SDKs are unaffected.
3.90.0
(feat): Add the packageJsonMergeStrategy option (shallow | deep, default shallow) controlling how
packageJson overrides in generators.yml are merged into the generated package.json.
shallow is the existing behavior and remains the default: nested objects such as exports["."]
are replaced wholesale by the override, so adding a single custom export condition drops the
generated import/require/default conditions.
3.89.0
(feat): Add a guardProcessEnvAccess config option (boolean, defaults to false).
When enabled, the generated header, bearer, basic and OAuth auth providers read environment
variables through a typeof process !== "undefined" guard, so a missing credential surfaces the
normal error instead of ReferenceError: process is not defined in runtimes without a Node
process global (browsers/Vite, Cloudflare Workers, Deno). Defaults to false, so generated
output is unchanged unless the option is enabled.
3.88.3
(fix): Respect custom OAuth token headers and prefixes for client credentials authentication.
3.88.2
(fix): Stop dropping the examples of endpoints whose request body is bytes or a file upload. The IR’s
ExampleRequestBody cannot carry those bodies, so such examples read as bodyless and the
endpoint lost its reference.md section, snippets and docs comments.
(fix): Positional path parameters that an example does not supply are now rendered with their
client-default value instead of undefined, so generated snippets typecheck.
3.88.1
(fix): Endpoints that disable retries (x-fern-retries: { disabled: true } in OpenAPI, or
retries: { disabled: true } in a Fern definition) now always emit maxRetries: 0,
overriding both client-level and request-level maxRetries. Previously this
configuration was only honored by the Python SDK generator and was silently ignored
in TypeScript.