Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
9bddb48
chore(python): update fastapi seed (#13592)
fern-support Mar 16, 2026
b10f0e3
chore(python): update python-sdk seed (#13582)
fern-support Mar 16, 2026
1e0af58
fix(typescript): resolve environment objects to string in passthrough…
Swimburger Mar 17, 2026
240c55b
chore(python): update python-sdk seed (#13598)
fern-support Mar 17, 2026
9b9fddb
chore(python): update fastapi seed (#13599)
fern-support Mar 17, 2026
f8c22c5
chore(typescript): update ts-sdk seed (#13601)
fern-support Mar 17, 2026
0ad65e1
feat(seed): add namespace-client-collision variant to csharp-namespac…
Swimburger Mar 17, 2026
f80a47b
chore(csharp): update csharp-model seed (#13595)
fern-support Mar 17, 2026
02db778
feat(go): use `any` instead of `interface{}` for unknown type schemas…
jsklan Mar 17, 2026
ff4f72d
fix(python): add docstring emission for unknown/any type alias defini…
jsklan Mar 17, 2026
68ff6d5
chore(go): update go-sdk seed (#13604)
fern-support Mar 17, 2026
396d12b
fix(typescript): remove spurious `| undefined` from map value types f…
jsklan Mar 17, 2026
bfaf01a
chore(python): update python-sdk seed (#13606)
fern-support Mar 17, 2026
50156c4
chore(python): update pydantic seed (#13607)
fern-support Mar 17, 2026
350ca01
chore(python): update fastapi seed (#13608)
fern-support Mar 17, 2026
29cc295
chore(rust): update rust-sdk seed (#13609)
fern-support Mar 17, 2026
ffeb3b0
chore(ruby): update ruby-sdk-v2 seed (#13611)
fern-support Mar 17, 2026
dd846cb
chore(rust): update rust-model seed (#13610)
fern-support Mar 17, 2026
abcc6a4
chore(python): update fastapi seed (#13612)
fern-support Mar 17, 2026
aa32d11
chore(python): update pydantic seed (#13613)
fern-support Mar 17, 2026
6961e82
chore(csharp): update csharp-sdk seed (#13614)
fern-support Mar 17, 2026
e92d8eb
chore(python): update python-sdk seed (#13615)
fern-support Mar 17, 2026
8535eb4
chore(typescript): update ts-sdk seed (#13616)
fern-support Mar 17, 2026
052ea4f
chore(typescript): update ts-express seed (#13617)
fern-support Mar 17, 2026
00352eb
chore(python): update pydantic seed (#13618)
fern-support Mar 17, 2026
69205d2
chore(python): update python-sdk seed (#13619)
fern-support Mar 17, 2026
34ddf9a
fix(openapi): ensure stream-condition literal isn't overwritten by nu…
Swimburger Mar 17, 2026
1bf85e3
fix(typescript): add isolatedDeclarations type annotation and enable …
Swimburger Mar 17, 2026
3ee4132
chore(typescript): update ts-sdk seed (#13621)
fern-support Mar 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion generators/go/internal/generator/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -2038,7 +2038,7 @@ func getExtraPropertiesFieldName(extraPropertiesEnabled bool) string {

// unknownToGoType maps the given unknown into its Go-equivalent.
func unknownToGoType(_ any) string {
return "interface{}"
return "any"
}

// literalToUndiscriminatedUnionField maps Fern's literal types to the field name used in an
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,6 @@ func (u *Union) validate() error {
return nil
}

type Unknown = interface{}
type Unknown = any

type Uuid = uuid.UUID
10 changes: 10 additions & 0 deletions generators/go/sdk/versions.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
# yaml-language-server: $schema=../../../fern-versions-yml.schema.json
- version: 1.30.3
changelogEntry:
- summary: |
Use `any` instead of `interface{}` for unknown type schemas. Since Go 1.18+,
`any` is the idiomatic alias for `interface{}`. This is a purely cosmetic
modernization with no behavioral change.
type: fix
createdAt: "2026-03-16"
irVersion: 61

- version: 1.30.2
changelogEntry:
- summary: |
Expand Down
11 changes: 11 additions & 0 deletions generators/python/sdk/versions.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# yaml-language-server: $schema=../../../fern-versions-yml.schema.json
# For unreleased changes, use unreleased.yml
- version: 5.0.2
changelogEntry:
- summary: |
Add docstring emission for unknown/any type alias definitions. When a type alias
resolves to `typing.Any` and has a description in the API definition, the generated
Python code now includes the docstring below the type alias declaration.
type: fix
createdAt: "2026-03-17"
irVersion: 65

- version: 5.0.1
changelogEntry:
- summary: |
Expand Down Expand Up @@ -52,6 +62,7 @@
type: fix
createdAt: "2026-03-12"
irVersion: 65

- version: 4.63.5
changelogEntry:
- summary: |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@


class TypeAliasDeclaration(AstNode):
def __init__(self, name: str, type_hint: TypeHint, snippet: Optional[str] = None):
def __init__(self, name: str, type_hint: TypeHint, snippet: Optional[str] = None, docs: Optional[str] = None):
self.name = name
self.type_hint = type_hint
self.snippet = snippet
self.docs = docs
self.ghost_references: Set[Reference] = set()

def get_metadata(self) -> AstNodeMetadata:
Expand All @@ -32,3 +33,9 @@ def write(self, writer: NodeWriter, should_write_as_snippet: Optional[bool] = No
writer.write(f"{self.name} = ")
self.type_hint.write(writer=writer)
writer.write_newline_if_last_line_not()

if self.docs is not None:
writer.write_line('"""')
writer.write(self.docs)
writer.write_newline_if_last_line_not()
writer.write_line('"""')
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ def generate(
name=self._context.get_class_name_for_type_id(self._name.type_id, as_request=False),
type_hint=self._type_hint,
snippet=self._snippet,
docs=self._docs,
),
should_export=True,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ def generate(
name=self._context.get_class_name_for_type_id(self._name.type_id, as_request=True),
type_hint=self._type_hint,
snippet=self._snippet,
docs=self._docs,
),
should_export=True,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,4 +103,4 @@ class MockServerPool {
}
}

export const mockServerPool = new MockServerPool();
export const mockServerPool: MockServerPool = new MockServerPool();
Original file line number Diff line number Diff line change
Expand Up @@ -368,19 +368,26 @@ export abstract class AbstractTypeReferenceToTypeNodeConverter extends AbstractT
const keyTypeNode = this.convert({ ...params, typeReference: map.keyType });
const valueTypeNode = this.convert({ ...params, typeReference: map.valueType });
return this.generateNonOptionalTypeReferenceNode({
typeNode: ts.factory.createTypeReferenceNode("Record", [keyTypeNode.typeNode, valueTypeNode.typeNode]),
typeNode: ts.factory.createTypeReferenceNode("Record", [
keyTypeNode.typeNode,
valueTypeNode.typeNodeWithoutUndefined
]),
requestTypeNode:
keyTypeNode.requestTypeNode || valueTypeNode.requestTypeNode
? ts.factory.createTypeReferenceNode("Record", [
keyTypeNode.requestTypeNode ?? keyTypeNode.typeNode,
valueTypeNode.requestTypeNode ?? valueTypeNode.typeNode
valueTypeNode.requestTypeNodeWithoutUndefined ??
valueTypeNode.requestTypeNode ??
valueTypeNode.typeNodeWithoutUndefined
])
: undefined,
responseTypeNode:
keyTypeNode.responseTypeNode || valueTypeNode.responseTypeNode
? ts.factory.createTypeReferenceNode("Record", [
keyTypeNode.responseTypeNode ?? keyTypeNode.typeNode,
valueTypeNode.responseTypeNode ?? valueTypeNode.typeNode
valueTypeNode.responseTypeNodeWithoutUndefined ??
valueTypeNode.responseTypeNode ??
valueTypeNode.typeNodeWithoutUndefined
])
: undefined
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -874,16 +874,30 @@ export class GeneratedSdkClientClassImpl implements GeneratedSdkClientClass {
getAuthHeadersCode = "";
}

// For multi-URL environments, this._options.environment is an object (e.g. { ec2: string; s3: string }),
// not a single string URL. Only pass `environment` when it resolves to a single base URL string.
const isMultiUrlEnvironment =
this.intermediateRepresentation.environments?.environments.type === "multipleBaseUrls";
const environmentCode = isMultiUrlEnvironment ? "" : "environment: this._options.environment,";
// Resolve the base URL from either the explicit baseUrl option or the environment.
// For multi-URL environments (e.g. { ec2: string; s3: string }), the environment is an object,
// so we project it to a string via its first base URL property to avoid passing an object where a string is expected.
// The property name is read from the IR's baseUrls definition rather than hardcoded.
// For single-URL or no-IR-defined environments, the environment is already a string, so we fall back to it directly.
const envs = this.intermediateRepresentation.environments?.environments;
let baseUrlCode: string;
if (envs != null && envs.type === "multipleBaseUrls") {
const firstBaseUrl = envs.baseUrls[0];
if (firstBaseUrl == null) {
throw new Error("Multi-URL environment has no base URLs defined");
}
const firstBaseUrlName = firstBaseUrl.name.camelCase.unsafeName;
baseUrlCode = `baseUrl: this._options.baseUrl ?? (async () => {
const env = await core.Supplier.get(this._options.environment);
return typeof env === "string" ? env : (env as Record<string, string>)?.${firstBaseUrlName};
}),`;
} else {
baseUrlCode = "baseUrl: this._options.baseUrl ?? this._options.environment,";
}

const fetchMethodBody = `
return core.makePassthroughRequest(input, init, {
${environmentCode}
baseUrl: this._options.baseUrl,
${baseUrlCode}
headers: this._options.headers,
timeoutInSeconds: this._options.timeoutInSeconds,
maxRetries: this._options.maxRetries,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,10 @@ export class TestGenerator {
import { defineConfig } from "vitest/config";
export default defineConfig({
test: {
typecheck: {
enabled: true,
tsconfig: "./${this.relativeTestPath}/tsconfig.json"
},
projects: [
{
test: {
Expand Down
37 changes: 36 additions & 1 deletion generators/typescript/sdk/versions.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,40 @@
# yaml-language-server: $schema=../../../fern-versions-yml.schema.json
- version: 3.58.0
changelogEntry:
- summary: |
Add explicit type annotation to `mockServerPool` export in generated test
mock-server code to satisfy `--isolatedDeclarations`.
type: fix
- summary: |
Enable vitest typechecking in generated vitest config so that type errors
in test and source files are caught when running `vitest`.
type: feat
createdAt: "2026-03-17"
irVersion: 65

- version: 3.57.2
changelogEntry:
- summary: |
Fix map types with unknown/any value types to not include spurious `| undefined`.
Previously, `map<string, UnknownType>` generated `Record<string, UnknownType | undefined>`
instead of `Record<string, UnknownType>`. The `| undefined` is now correctly omitted
from map value types.
type: fix
createdAt: "2026-03-16"
irVersion: 65

- version: 3.57.1
changelogEntry:
- summary: |
Fix passthrough `fetch()` method producing 404s when the SDK environment is an
object (e.g. `{ base: string; production: string }`) instead of a plain string URL.
The generated code now resolves the environment supplier inline and projects object
environments to their `.base` property, instead of passing the raw object to
`makePassthroughRequest` where it would be stringified as `[object Object]`.
type: fix
createdAt: "2026-03-16"
irVersion: 65

- version: 3.57.0
changelogEntry:
- summary: |
Expand All @@ -10,7 +46,6 @@
type: feat
createdAt: "2026-03-16"
irVersion: 65

- version: 3.56.4
changelogEntry:
- summary: |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -389,9 +389,9 @@ export class RequestBodyConverter extends Converters.AbstractConverters.Abstract
properties: {
...resolvedMediaTypeSchema.properties,
[this.streamingExtension.streamConditionProperty]: {
...streamConditionProperty,
type: "boolean",
const: isStreaming,
...streamConditionProperty
const: isStreaming
} as OpenAPIV3_1.SchemaObject
},
required: [...(resolvedMediaTypeSchema.required ?? []), this.streamingExtension.streamConditionProperty]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,9 +196,9 @@ function getRequestBody({
properties: {
...resolvedRequestBodySchema.properties,
[streamingExtension.streamConditionProperty]: {
...(streamingProperty ?? {}),
type: "boolean",
"x-fern-boolean-literal": isStreaming,
...(streamingProperty ?? {})
"x-fern-boolean-literal": isStreaming
// biome-ignore lint/suspicious/noExplicitAny: allow explicit any
} as any
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,9 @@ components:
example: How can I use the Vectara platform?
stream_response:
description: Indicates whether the response should be streamed or not.
type: boolean
type:
- boolean
- "null"
default: false
required:
- query
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,14 +101,20 @@
"valueType": {
"container": {
"optional": {
"primitive": {
"v1": "BOOLEAN",
"v2": {
"default": false,
"type": "boolean"
}
"container": {
"nullable": {
"primitive": {
"v1": "BOOLEAN",
"v2": {
"default": false,
"type": "boolean"
}
},
"type": "primitive"
},
"type": "nullable"
},
"type": "primitive"
"type": "container"
},
"type": "optional"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,9 @@ components:
example: How can I use the Vectara platform?
stream_response:
description: Indicates whether the response should be streamed or not.
type: boolean
type:
- boolean
- "null"
default: false
required:
- query
Expand Down
11 changes: 11 additions & 0 deletions packages/cli/cli/versions.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,15 @@
# yaml-language-server: $schema=../../../fern-versions-yml.schema.json
- version: 4.31.1
changelogEntry:
- summary: |
Fix `x-fern-streaming` with `stream-condition` emitting the condition field
as an optional boolean instead of a required literal in split request types.
When the source property was nullable (`type: [boolean, "null"]`), the spread
order allowed the original property to overwrite the literal overrides.
type: fix
createdAt: "2026-03-17"
irVersion: 65

- version: 4.31.0
changelogEntry:
- summary: |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,15 @@
},
"user": {
"$ref": "#/definitions/System.User"
},
"owner": {
"$ref": "#/definitions/System.User"
}
},
"required": [
"name",
"user"
"user",
"owner"
],
"additionalProperties": false,
"definitions": {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/types.object.DocumentedUnknownType"
},
"definitions": {
"types.object.DocumentedUnknownType": {
"type": [
"string",
"number",
"boolean",
"object",
"array",
"null"
]
}
}
}
Loading
Loading