Beta
How CodeForge names the payload models it generates from allOf, and why it no longer produces AllOf_N in a model name.
CodeForge names a generated payload model after its schema's own title
(or, failing that, its $id). Only a schema with neither falls back to a
structural name built from the path it was reached by. allOf used to leak
that structural path into names that were otherwise perfectly readable —
that no longer happens for the common case, and is muted for the rest.
An OpenAPI document commonly wraps a single reference in allOf so it can
attach sibling keywords next to it — most often a description on a response
body that is otherwise just a $ref:
responses:
'201':
content:
application/json:
schema:
description: The created widget
allOf:
- $ref: '#/components/schemas/Widget'
Before generating anything, CodeForge flattens a single-member allOf into
its one member, merging the sibling keywords (description here) losslessly
into it. properties and required are combined rather than overwritten,
since both the member and its siblings routinely declare their own share of
each. The flattened schema is exactly as if allOf had never wrapped it —
including the member's own title, which is what names the model. The
Widget schema's title wins, and the model is generated as Widget, not
as a structural name derived from allOf's first member.
A multi-member allOf is a real intersection of two or more schemas, not
sibling annotations on one, and is left untouched — only its members are
flattened, recursively, wherever a single-member allOf occurs inside them.
A multi-member allOf still produces one model per unnamed schema nested
inside it, and that model's structural name used to carry the member's index
literally: AllOf_0, AllOf_1, and so on. Modelina infers that name from the
member's own position in the document, not from the response it happens to
sit inside — so a response combining a base schema (allOf[0], an id
property) with an untitled status enum (allOf[1]) used to name that enum
AllOf_1Status, with no trace of the surrounding response in it at all.
CodeForge now strips every AllOf_<n> fragment from a generated model name
before it is used, so that same enum generates as plain Status — every bit
as unique, with none of the structural noise about which allOf member it
came from.
Stripping AllOf_N can make two models that used to have distinct names
collide. Two operations that each return a response shaped this way — an
id at allOf[0] and an untitled status enum at allOf[1] — both strip
to the same bare name, Status. Naming has never guaranteed uniqueness on
its own; the resolution CodeForge already applies to every other name
collision applies here too, unchanged:
1.The first response generated keeps the bare name, Status. The second is
renamed to SessionsUpdateResponse_200Status, and the run logs why:
Model name collision on 'Status': claimed by 'SessionsCreateResponse' and
generated again with different content for 'SessionsUpdateResponse'.
Renaming the nested 'SessionsUpdateResponse' model to
'SessionsUpdateResponse_200Status'.
Both outcomes are deterministic: regenerating the same document always produces the same names, and the generation run's logs name every collision it resolved, so you can always trace a model back to why it is named the way it is.
Windows and macOS resolve MetaData.ts and Metadata.ts to the same file;
Linux does not. CodeForge treats two model names as colliding the moment they
are the same after lower-casing, whether that happens between two nested
models in one schema tree or between two models generated on different runs —
so a package that builds locally on Linux CI cannot still fail to compile on a
customer's Windows machine.
The same resolution as above applies: the model reached first keeps its name,
and the second is prefixed with its nearest containing model's name, falling
back to a numeric suffix. A response nesting both a MetaData and a
Metadata object renames the second and logs why:
Model name collision on 'Metadata': differs only in case from the nested
model 'MetaData' already claimed in the same schema. Renaming to
'ContainerMetadata'.
A rename always reaches every place the model's name is used: the file it is written to, the identifier declared in that file, every export a package's root module re-exports it under, and every generated consumer of the model — including the HTTP client and server code that types an operation's parameters and headers against it. A model that is renamed after another part of the document already generated it — the cross-run case above — updates the earlier-generated files' imports too, so nothing is left pointing at a name no file declares.
CodeForge names a generated property after its JSON Schema key, formatted to
camelCase. Two keys that are spelled differently but format to the same
identifier — team_Id and team_id, both teamId — used to collapse into
one property, silently dropping whichever the formatter processed first. A
parameter class or payload model referencing the dropped property by its
original key no longer compiled.
The first property in schema order keeps the plain identifier. Each later
property whose formatted name is already taken gets the shortest free numeric
suffix, with no separator: teamId, teamId1, teamId2. The wire name — the
raw key sent and read on the wire — is never affected; only the generated
TypeScript identifier changes. The run logs which key had to give way:
Property name collision on 'GetTaskParameters': 'team_id' formats to
'teamId', which another property already claims. Renaming it to 'teamId1'.
This applies everywhere CodeForge constrains an object's properties: payload models, parameter classes and header models alike.
Payload models come from the document's own schemas; a parameter or header
class's name is the engine's invention — <operationId>Parameters or
<operationId>Headers. When the two coincide, both used to land in one
generated file: the payload as an interface, the parameter or header class
as a class, and tsc reported both TS2300 (duplicate identifier) and
TS2693 (a type used as a value) on the same name.
CodeForge now claims TypeScript identifiers for the payloads, parameters and
headers presets in one shared space, in a fixed priority: payloads first,
since the document named them; parameters next; headers last. A parameter or
header class whose name is already claimed is renamed with the same
shortest-free-numeric-suffix scheme every other collision uses — its companion
<Name>Interface, when it has one, moves with it, since both ship from one
file:
An operation settleCharge next to a component schema SettleChargeParameters
renders:
payloads/SettleChargeParameters.ts → interface SettleChargeParameters
parameters/SettleChargeParameters1.ts → class SettleChargeParameters1
interface SettleChargeParameters1Interface
The run logs why:
The parameters class 'SettleChargeParameters' for operation 'settleCharge'
collides with a payload model's TypeScript identifier. Renaming it to
'SettleChargeParameters1'.
The rename reaches every consumer the same way every other rename in this document does: the emitted file, the barrel export, and every channels and client import and call site. A parameter or header class is only accepted once both it and its companion (when it has one) are free — a name that is merely unclaimed by a payload but still taken by an earlier parameter or header class keeps searching. Wire names are never affected: the raw path, query and header parameter names sent and read on the wire come straight from the document, whichever TypeScript identifier the class ends up with.
A document with no such collision generates exactly as it did before.
This is a one-time, breaking rename. There is no flag to opt back into the
old, AllOf_N-bearing names, the pre-collision case-only names, the
overwritten property names, or a parameter or header class's pre-collision
name — regenerate your SDK and update any code that imports a model, a
parameter or header class, or reads a property by its old name.