feat: engine, IR, and compat enhancements for dotnet + kotlin emitter support#19
Merged
gjtorikian merged 10 commits intomainfrom Apr 14, 2026
Merged
feat: engine, IR, and compat enhancements for dotnet + kotlin emitter support#19gjtorikian merged 10 commits intomainfrom
gjtorikian merged 10 commits intomainfrom
Conversation
Every run of \`oagen generate\` now writes \`.oagen-manifest.json\` to the output and target directories listing every emitted path. On subsequent runs, files recorded in the previous manifest but absent from the current emission are deleted, preventing stale-file accumulation across regens (alias/dedup targets that fall out of the IR). Safety: - Deletion is gated on the auto-generated header marker. Files missing the marker are preserved and reported (defends against hand-edits). - First-adoption runs (no previous manifest) skip the prune phase and just write the baseline manifest, so existing SDKs aren't surprised. - Opt out per run with \`--no-prune\`; the manifest is still refreshed so the next prune-enabled run has a current baseline. - Empty parent directories are cleaned up after deletion. Motivated by a 347-file cleanup in workos-python where dedup/alias artifacts from prior regens had accumulated silently over time. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
OperationHint gains an optional `urlBuilder: boolean` flag for redirect-style endpoints (e.g. /sso/authorize, /user_management/sessions/logout) where the right thing to generate is a method that returns the constructed URL rather than one that issues an HTTP call. Emitters that ignore the flag retain previous behavior. SplitHint.optionalParams was previously documented but not honored: the resolver always built ResolvedWrapper.optionalParams as []. Wire it through so split operations can mark exposed params as optional even when no addressable variant model exists (e.g. inline oneOf branches). Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
POST /users would derive create_users — but a create call always creates exactly one resource. Treat create as a single-resource verb in deriveMethodName so POST /users -> create_user, POST /organizations -> create_organization, etc. List operations remain plural. Update the two test cases that asserted the old behavior. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Two complementary recognizers in schemaToTypeRef when a schema uses
oneOf (or anyOf) without an explicit `discriminator:` key:
1. oneOf-of-literals → enum. When every non-null variant is a plain
string `const` (or single-element `enum`), collapse the union into
a single EnumRef. Replaces the opaque `union<string, string, …>`
representation with a proper enum whose members match the consts.
Catches patterns like `provider: oneOf [{const: "AppleOAuth"}, ...]`.
2. oneOf-of-objects-with-shared-const → discriminated union. When
every object variant pins the same property to a const value (and
each variant has a resolvable model name via $ref/title), synthesize
a `discriminator: { property, mapping }` on the union. Covers
EventSchema-style shapes where the spec encodes a discriminator via
`const: "..."` on each variant instead of the explicit
`discriminator:` keyword.
Nothing regresses for spec authors who already use explicit
discriminators — those still short-circuit first.
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Detect unpaginated array-of-model responses so emitters can return Model[] and apply elementwise deserialization. Paginated operations continue using the pagination wrapper instead. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Surface the deprecation message on ApiClass so emitters can propagate it to service properties, giving IDE strikethrough at the access site. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Implement extractMembers and shouldSkipDeepMerge for the Kotlin merge adapter. extractKotlinClassMembers parses property/function/companion declarations and folds trailing getters/setters and preceding KDoc into each member's text span. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Separate inserted members with blank lines and add a leading blank line before the first insertion to visually separate from existing members. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Thread targetDir and priorTargetManifestPaths through EmitterContext so emitters can distinguish oagen-written files from hand-maintained ones. Reuse the already-read manifest in the orchestrator instead of reading it twice. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
…ormatted Test files (.spec.ts, .test.ts) are standalone entry points that belong in the target repo. Marking them as roots ensures they're integrated and processed by the post-generation formatter. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
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.
Summary
oagen generatenow tracks emitted files in.oagen-manifest.jsonand deletes stale files on subsequent runs, with safety guards (header-marker gating, first-adoption skip,--no-pruneopt-out)oneOfpatterns, singularize create-method names, addisArrayResponseflag to operation plan, addurlBuilderhint andSplitHint.optionalParamssupport@deprecatedJSDoc tag from class declarations for IDE strikethrough supportTest plan
test/engine/manifest.test.ts)test/engine/orchestrator-prune.test.ts)isArrayResponseoperation plan detection (test/engine/operation-plan.test.ts)oagen generateon a real spec to confirm stale-file pruning works end-to-end🤖 Generated with Claude Code