Changelog
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.
3.88.0
(fix): With respectOptionalRequestBody enabled, examples that omit an optional request body are no
longer dropped from the generated reference, snippets, and docs comments.
(feat): Add respectOptionalRequestBody. With it enabled, an endpoint whose request body the API does
not require takes an optional body parameter — refund(id: string, request?: RefundRequest) —
keeping the body’s own type rather than widening it to RefundRequest | undefined, and sending
no body when the caller omits it. Examples that supply no body render as client.refund(id),
and the generated wire test for such an example no longer asserts a request body. Defaults to
false, so signatures and snippets are unchanged until you opt in.
3.87.4
(fix): Skip endpoint examples that omit a request body the endpoint declares. The generated request
type requires the body, so rendering such an example produced a call missing a required
argument in the reference, the docstrings, the snippets and the generated tests.
3.87.3
(fix): Fix dynamic snippets emitting a dangling argument delimiter (e.g. client.refunds.refund("id", ))
when an example omits the request body.
3.87.2
(fix): Send the caller’s request when fetching the first page of a next_uri/next_path paginated
endpoint. Previously the first page was requested through the same headers-only helper used for
subsequent pages, so query parameters, request bodies and requestOptions.queryParams were
silently dropped (and the unused request parameter was emitted as _request). Subsequent pages
keep using the next URL as-is.
3.87.1
(fix): The appInfo User-Agent helper emitted when allowUserAgentAppInfo is enabled no longer
uses a control-character regex range, so generated SDKs lint cleanly under rules such as
Biome’s noControlCharactersInRegex without a suppression comment. Escaping behavior is
unchanged.
3.87.0
(feat): Add a requireBaseUrl config option. When enabled, baseUrl is a required client option and
environment is optional, for APIs whose users always pass an explicit server URL. Generated
snippets, README, and wire tests use baseUrl accordingly. The option is ignored for APIs with
multiple base URLs, whose clients resolve each URL from environment. Defaults to false, so
existing SDKs are unchanged.