Skip to content

OpenApi: drop the storage-type component orphaned by inlining a strong-type wrapper#111

Merged
KaliCZ merged 1 commit into
mainfrom
claude/fervent-spence-2e61f8
Jun 25, 2026
Merged

OpenApi: drop the storage-type component orphaned by inlining a strong-type wrapper#111
KaliCZ merged 1 commit into
mainfrom
claude/fervent-spence-2e61f8

Conversation

@KaliCZ

@KaliCZ KaliCZ commented Jun 25, 2026

Copy link
Copy Markdown
Owner

Summary

Fixes #110. Using the Email strong type on a DTO left an orphaned MailAddress component schema in the generated OpenAPI document. Swashbuckle registers MailAddress as its own component while walking Email's CLR properties (Email.Value is a System.Net.Mail.MailAddress); painting Email down to its wire primitive severs the only edge to MailAddress, so once Email is inlined and dropped, MailAddress is an orphan with zero inbound $refs. openapi-typescript then emits it as an unused interface.

Approach

The inliner already visits every $ref while inlining, so it now records the ones it leaves in place. Afterwards it drops a small, explicit list of generated storage component names (currently just MailAddress) — but only those still unreferenced:

private static readonly string[] GeneratedStorageComponentNames = ["MailAddress"];
  • A consumer that uses the type directly (its own DTO with a MailAddress property) keeps it — the reference check sees it.
  • A consumer's own schemas are never candidates — only names on the list are considered, so blanket pruning of unrelated unused components can't happen.
  • Extend the list when a future wrapper drags in another storage type.

The production change is ~35 lines in the shared StrongTypeInliner (StrongTypes.OpenApi.Core), so both the Swashbuckle document filter and the Microsoft document transformer benefit. (Only Swashbuckle exhibited the orphan; the Microsoft pipeline derives Email's schema from its JSON contract and never registers MailAddress.)

Tests

  • Integration (No_Component_Schema_Is_Left_Unreferenced_After_Inlining, runs on both pipelines) — the generated document carries no orphaned component schema. Failed only on Swashbuckle before the fix, reproducing the MailAddress orphan.
  • Core unit — new pipeline-independent StrongTypes.OpenApi.Core.Tests project exercising the inliner against the Microsoft.OpenApi object model directly: the unreferenced storage type is removed, a still-referenced one is kept, and an unrelated unreferenced consumer schema is preserved. The integration suite stays HTTP/JSON-only and no longer references Core. testing.md documents the new project.

All tests pass (297 integration + 2 Core unit).

Note on the runtime warning

The issue also flagged a StrongTypeInliningDocumentFilter warning as "likely the same root cause." It does not reproduce in this repo's test apps (zero occurrences in the test logs), and the reporter attributes it to another OpenAPI transformer in their environment mutating a schema after our adapter runs. The concrete, verifiable defect — the orphaned MailAddress — is what this PR fixes and tests.

🤖 Generated with Claude Code

@KaliCZ KaliCZ self-assigned this Jun 25, 2026
@github-actions

github-actions Bot commented Jun 25, 2026

Copy link
Copy Markdown

Coverage

Lines: 3848 / 5200 (74.0%)    Branches: 2061 / 2758 (74.7%)

Files changed in this PR

File Lines Branches
StrongTypes.OpenApi.Core/Inlining/StrongTypeInliner.cs 182 / 201 (90.5%) 128 / 160 (80.0%)
StrongTypes — lines 93.1% (1520/1632), branches 88.2% (997/1130)
Booleans — lines 100.0% (14/14), branches 84.6% (22/26)
File Lines Branches
BooleanExtensions.cs 2 / 2 (100.0%) 2 / 2 (100.0%)
BooleanMapExtensions.cs 12 / 12 (100.0%) 20 / 24 (83.3%)
Collections — lines 90.2% (276/306), branches 85.1% (114/134)
File Lines Branches
IEnumerableExtensions.cs 7 / 7 (100.0%) 6 / 6 (100.0%)
IEnumerableExtensions_Concatenating.cs 2 / 2 (100.0%) 2 / 2 (100.0%)
IEnumerableExtensions_Flattening.cs 1 / 1 (100.0%) 2 / 2 (100.0%)
IEnumerableExtensions_Null.cs 5 / 5 (100.0%) 10 / 10 (100.0%)
IEnumerableExtensions_Partition.cs 11 / 11 (100.0%) 6 / 6 (100.0%)
IEnumerableExtensions_Types.cs 0 / 8 (0.0%) 0 / 6 (0.0%)
NonEmptyEnumerable.cs 59 / 65 (90.8%) 30 / 38 (78.9%)
NonEmptyEnumerableDebugView.cs 0 / 2 (0.0%) 0 / 0 (n/a)
NonEmptyEnumerableExtensions.cs 144 / 149 (96.6%) 37 / 38 (97.4%)
NonEmptyEnumerableJsonConverter.cs 47 / 50 (94.0%) 21 / 26 (80.8%)
ReadOnlyList.cs 0 / 6 (0.0%) 0 / 0 (n/a)
Digits — lines 97.7% (85/87), branches 93.8% (30/32)
File Lines Branches
Digit.cs 78 / 80 (97.5%) 26 / 28 (92.9%)
DigitExtensions.cs 7 / 7 (100.0%) 4 / 4 (100.0%)
Emails — lines 98.6% (68/69), branches 92.5% (37/40)
File Lines Branches
Email.cs 50 / 50 (100.0%) 27 / 30 (90.0%)
EmailExtensions.cs 10 / 11 (90.9%) 8 / 8 (100.0%)
EmailJsonConverter.cs 8 / 8 (100.0%) 2 / 2 (100.0%)
Enums — lines 100.0% (58/58), branches 95.5% (21/22)
File Lines Branches
EnumExtensions.cs 58 / 58 (100.0%) 21 / 22 (95.5%)
Exceptions — lines 78.9% (15/19), branches 50.0% (7/14)
File Lines Branches
ExceptionEnumerableExtensions.cs 15 / 19 (78.9%) 7 / 14 (50.0%)
Maybe — lines 92.1% (151/164), branches 83.9% (94/112)
File Lines Branches
Maybe.cs 58 / 59 (98.3%) 40 / 44 (90.9%)
MaybeCollectionExtensions.cs 41 / 45 (91.1%) 28 / 30 (93.3%)
MaybeExtensions.cs 9 / 14 (64.3%) 10 / 18 (55.6%)
MaybeJsonConverter.cs 43 / 46 (93.5%) 16 / 20 (80.0%)
Nullables — lines 100.0% (12/12), branches 83.3% (20/24)
File Lines Branches
NullableMapExtensions.cs 12 / 12 (100.0%) 20 / 24 (83.3%)
Numbers — lines 100.0% (84/84), branches 94.4% (17/18)
File Lines Branches
Negative.cs 7 / 7 (100.0%) 2 / 2 (100.0%)
NonNegative.cs 7 / 7 (100.0%) 2 / 2 (100.0%)
NonPositive.cs 7 / 7 (100.0%) 2 / 2 (100.0%)
NumberExtensions.cs 12 / 12 (100.0%) 4 / 4 (100.0%)
NumericStrongTypeJsonConverterFactory.cs 42 / 42 (100.0%) 5 / 6 (83.3%)
NumericWrapperAttribute.cs 2 / 2 (100.0%) 0 / 0 (n/a)
Positive.cs 7 / 7 (100.0%) 2 / 2 (100.0%)
Result — lines 98.5% (333/338), branches 95.6% (417/436)
File Lines Branches
Result.cs 68 / 70 (97.1%) 55 / 68 (80.9%)
ResultAccessExtensions.cs 4 / 4 (100.0%) 8 / 8 (100.0%)
ResultAggregate.cs 188 / 188 (100.0%) 318 / 318 (100.0%)
ResultCatch.cs 20 / 20 (100.0%) 0 / 0 (n/a)
ResultFlattenExtensions.cs 7 / 7 (100.0%) 8 / 8 (100.0%)
ResultFromNullableExtensions.cs 10 / 10 (100.0%) 15 / 20 (75.0%)
ResultPartitionExtensions.cs 20 / 20 (100.0%) 5 / 6 (83.3%)
ResultThrowIfErrorExtensions.cs 16 / 19 (84.2%) 8 / 8 (100.0%)
Strings — lines 84.7% (133/157), branches 84.4% (54/64)
File Lines Branches
NonEmptyString.cs 87 / 87 (100.0%) 32 / 32 (100.0%)
NonEmptyStringExtensions.cs 13 / 27 (48.1%) 0 / 0 (n/a)
NonEmptyStringJsonConverter.cs 12 / 15 (80.0%) 4 / 6 (66.7%)
StringExtensions.cs 21 / 28 (75.0%) 18 / 26 (69.2%)
generated — lines 89.8% (291/324), branches 78.8% (164/208)
File Lines Branches
Negative<T>.Extensions.g.cs 32 / 32 (100.0%) 20 / 20 (100.0%)
Negative<T>.g.cs 45 / 49 (91.8%) 24 / 32 (75.0%)
NonNegative<T>.Extensions.g.cs 32 / 32 (100.0%) 20 / 20 (100.0%)
NonNegative<T>.g.cs 36 / 49 (73.5%) 18 / 32 (56.2%)
NonPositive<T>.Extensions.g.cs 32 / 32 (100.0%) 20 / 20 (100.0%)
NonPositive<T>.g.cs 36 / 49 (73.5%) 18 / 32 (56.2%)
Positive<T>.Extensions.g.cs 32 / 32 (100.0%) 20 / 20 (100.0%)
Positive<T>.g.cs 46 / 49 (93.9%) 24 / 32 (75.0%)
StrongTypes.Analyzers — lines 94.7% (322/340), branches 86.8% (132/152)
(root) — lines 94.7% (322/340), branches 86.8% (132/152)
File Lines Branches
AddEfCorePackageCodeFixProvider.cs 48 / 52 (92.3%) 18 / 20 (90.0%)
AddOpenApiPackageCodeFixProvider.cs 52 / 53 (98.1%) 21 / 26 (80.8%)
MissingEfCorePackageAnalyzer.cs 140 / 153 (91.5%) 53 / 62 (85.5%)
MissingOpenApiPackageAnalyzer.cs 82 / 82 (100.0%) 40 / 44 (90.9%)
StrongTypes.Api — lines 96.7% (327/338), branches 86.3% (88/102)
(root) — lines 100.0% (11/11), branches n/a (0/0)
File Lines Branches
Program.cs 11 / 11 (100.0%) 0 / 0 (n/a)
Controllers — lines 96.9% (221/228), branches 86.3% (88/102)
File Lines Branches
BindingProbeController.cs 55 / 62 (88.7%) 32 / 34 (94.1%)
CollectionJsonController.cs 4 / 4 (100.0%) 0 / 0 (n/a)
EmailEntityController.cs 44 / 44 (100.0%) 26 / 30 (86.7%)
EntityControllerBase.cs 9 / 9 (100.0%) 0 / 0 (n/a)
NegativeDecimalEntityController.cs 1 / 1 (100.0%) 0 / 0 (n/a)
NegativeDoubleEntityController.cs 1 / 1 (100.0%) 0 / 0 (n/a)
NegativeFloatEntityController.cs 1 / 1 (100.0%) 0 / 0 (n/a)
NegativeIntEntityController.cs 1 / 1 (100.0%) 0 / 0 (n/a)
NegativeLongEntityController.cs 1 / 1 (100.0%) 0 / 0 (n/a)
NegativeShortEntityController.cs 1 / 1 (100.0%) 0 / 0 (n/a)
NonEmptyStringEntityController.cs 1 / 1 (100.0%) 0 / 0 (n/a)
NonNegativeDecimalEntityController.cs 1 / 1 (100.0%) 0 / 0 (n/a)
NonNegativeDoubleEntityController.cs 1 / 1 (100.0%) 0 / 0 (n/a)
NonNegativeFloatEntityController.cs 1 / 1 (100.0%) 0 / 0 (n/a)
NonNegativeIntEntityController.cs 1 / 1 (100.0%) 0 / 0 (n/a)
NonNegativeLongEntityController.cs 1 / 1 (100.0%) 0 / 0 (n/a)
NonNegativeShortEntityController.cs 1 / 1 (100.0%) 0 / 0 (n/a)
NonPositiveDecimalEntityController.cs 1 / 1 (100.0%) 0 / 0 (n/a)
NonPositiveDoubleEntityController.cs 1 / 1 (100.0%) 0 / 0 (n/a)
NonPositiveFloatEntityController.cs 1 / 1 (100.0%) 0 / 0 (n/a)
NonPositiveIntEntityController.cs 1 / 1 (100.0%) 0 / 0 (n/a)
NonPositiveLongEntityController.cs 1 / 1 (100.0%) 0 / 0 (n/a)
NonPositiveShortEntityController.cs 1 / 1 (100.0%) 0 / 0 (n/a)
PositiveDecimalEntityController.cs 1 / 1 (100.0%) 0 / 0 (n/a)
PositiveDoubleEntityController.cs 1 / 1 (100.0%) 0 / 0 (n/a)
PositiveFloatEntityController.cs 1 / 1 (100.0%) 0 / 0 (n/a)
PositiveIntEntityController.cs 1 / 1 (100.0%) 0 / 0 (n/a)
PositiveLongEntityController.cs 1 / 1 (100.0%) 0 / 0 (n/a)
PositiveShortEntityController.cs 1 / 1 (100.0%) 0 / 0 (n/a)
ReferenceTypeEntityControllerBase.cs 42 / 42 (100.0%) 14 / 18 (77.8%)
StructTypeEntityControllerBase.cs 42 / 42 (100.0%) 16 / 20 (80.0%)
Data — lines 100.0% (54/54), branches n/a (0/0)
File Lines Branches
PostgreSqlDbContext.cs 27 / 27 (100.0%) 0 / 0 (n/a)
SqlServerDbContext.cs 27 / 27 (100.0%) 0 / 0 (n/a)
Entities — lines 75.0% (12/16), branches n/a (0/0)
File Lines Branches
EntityBase.cs 12 / 12 (100.0%) 0 / 0 (n/a)
IEntity.cs 0 / 4 (0.0%) 0 / 0 (n/a)
Models — lines 100.0% (29/29), branches n/a (0/0)
File Lines Branches
CollectionJsonModels.cs 20 / 20 (100.0%) 0 / 0 (n/a)
EntityModels.cs 9 / 9 (100.0%) 0 / 0 (n/a)
StrongTypes.AspNetCore — lines 89.3% (151/169), branches 84.5% (71/84)
(root) — lines 89.3% (151/169), branches 84.5% (71/84)
File Lines Branches
JsonValidationErrorKeyNormalizer.cs 22 / 22 (100.0%) 20 / 20 (100.0%)
ModelMetadataNullability.cs 12 / 13 (92.3%) 7 / 10 (70.0%)
NonEmptyEnumerableModelBinder.cs 51 / 62 (82.3%) 25 / 30 (83.3%)
NonEmptyEnumerableModelBinderProvider.cs 9 / 9 (100.0%) 4 / 4 (100.0%)
StringElementParser.cs 16 / 19 (84.2%) 2 / 4 (50.0%)
StrongTypesAspNetCoreOptions.cs 2 / 2 (100.0%) 0 / 0 (n/a)
StrongTypesServiceCollectionExtensions.cs 39 / 42 (92.9%) 13 / 16 (81.2%)
StrongTypes.AspNetCore.TestApi — lines 90.7% (39/43), branches 92.5% (37/40)
(root) — lines 100.0% (6/6), branches n/a (0/0)
File Lines Branches
Program.cs 6 / 6 (100.0%) 0 / 0 (n/a)
Controllers — lines 89.2% (33/37), branches 92.5% (37/40)
File Lines Branches
BindingProbeController.cs 29 / 29 (100.0%) 37 / 40 (92.5%)
JsonBodyProbeController.cs 4 / 8 (50.0%) 0 / 0 (n/a)
StrongTypes.EfCore — lines 89.3% (117/131), branches 71.4% (40/56)
(root) — lines 89.3% (117/131), branches 71.4% (40/56)
File Lines Branches
MailAddressValueConverter.cs 3 / 3 (100.0%) 0 / 0 (n/a)
NonEmptyStringValueConverter.cs 3 / 3 (100.0%) 0 / 0 (n/a)
NumericStrongTypeValueConverter.cs 17 / 17 (100.0%) 0 / 0 (n/a)
StrongTypesConvention.cs 46 / 59 (78.0%) 28 / 42 (66.7%)
StrongTypesDbContextOptionsExtension.cs 18 / 19 (94.7%) 2 / 2 (100.0%)
UnwrapMethodCallTranslator.cs 30 / 30 (100.0%) 10 / 12 (83.3%)
StrongTypes.FsCheck — lines 77.4% (96/124), branches n/a (0/0)
(root) — lines 77.4% (96/124), branches n/a (0/0)
File Lines Branches
Generators.cs 96 / 124 (77.4%) 0 / 0 (n/a)
StrongTypes.OpenApi.Core — lines 91.2% (438/480), branches 83.4% (292/350)
(root) — lines 91.8% (256/279), branches 86.3% (164/190)
File Lines Branches
NumericWrapperKinds.cs 11 / 11 (100.0%) 0 / 0 (n/a)
NumericWrapperPainter.cs 12 / 12 (100.0%) 3 / 4 (75.0%)
PrimitiveSchemaMap.cs 19 / 19 (100.0%) 0 / 0 (n/a)
SchemaPaint.cs 84 / 91 (92.3%) 69 / 84 (82.1%)
StrongTypeInlineMarker.cs 6 / 6 (100.0%) 6 / 6 (100.0%)
StrongTypeSchemaTypes.cs 42 / 42 (100.0%) 32 / 36 (88.9%)
WrapperAnnotationApplier.cs 82 / 98 (83.7%) 54 / 60 (90.0%)
Inlining — lines 90.5% (182/201), branches 80.0% (128/160)
File Lines Branches
StrongTypeInliner.cs 182 / 201 (90.5%) 128 / 160 (80.0%)
StrongTypes.OpenApi.Microsoft — lines 39.1% (425/1088), branches 40.3% (171/424)
(root) — lines 94.6% (245/259), branches 74.6% (97/130)
File Lines Branches
MicrosoftSchemaNaming.cs 52 / 52 (100.0%) 8 / 8 (100.0%)
PropertyAnnotationSchemaTransformer.cs 77 / 89 (86.5%) 52 / 74 (70.3%)
Startup.cs 14 / 14 (100.0%) 0 / 0 (n/a)
StrongTypesComponentSchemaFiller.cs 102 / 104 (98.1%) 37 / 48 (77.1%)
Binding — lines 98.1% (103/105), branches 78.6% (55/70)
File Lines Branches
NonBodyStrongTypeOperationTransformer.cs 103 / 105 (98.1%) 55 / 70 (78.6%)
Collections — lines 100.0% (19/19), branches 100.0% (8/8)
File Lines Branches
NonEmptyEnumerableSchemaTransformer.cs 10 / 10 (100.0%) 2 / 2 (100.0%)
StrongTypeCollectionShapeTransformer.cs 9 / 9 (100.0%) 6 / 6 (100.0%)
Digits — lines 100.0% (11/11), branches 100.0% (2/2)
File Lines Branches
DigitSchemaTransformer.cs 11 / 11 (100.0%) 2 / 2 (100.0%)
Emails — lines 100.0% (11/11), branches 100.0% (2/2)
File Lines Branches
EmailSchemaTransformer.cs 11 / 11 (100.0%) 2 / 2 (100.0%)
Inlining — lines 100.0% (7/7), branches 50.0% (1/2)
File Lines Branches
StrongTypeInliningDocumentTransformer.cs 7 / 7 (100.0%) 1 / 2 (50.0%)
Maybe — lines 100.0% (12/12), branches 100.0% (2/2)
File Lines Branches
MaybeSchemaTransformer.cs 12 / 12 (100.0%) 2 / 2 (100.0%)
Numbers — lines 100.0% (8/8), branches 100.0% (2/2)
File Lines Branches
NumericStrongTypeSchemaTransformer.cs 8 / 8 (100.0%) 2 / 2 (100.0%)
Strings — lines 100.0% (9/9), branches 100.0% (2/2)
File Lines Branches
NonEmptyStringSchemaTransformer.cs 9 / 9 (100.0%) 2 / 2 (100.0%)
obj/Debug/net10.0/Microsoft.AspNetCore.OpenApi.SourceGenerators/Microsoft.AspNetCore.OpenApi.SourceGenerators.XmlCommentGenerator — lines 0.0% (0/647), branches 0.0% (0/204)
File Lines Branches
OpenApiXmlCommentSupport.generated.cs 0 / 647 (0.0%) 0 / 204 (0.0%)
StrongTypes.OpenApi.Swashbuckle — lines 94.1% (239/254), branches 78.3% (166/212)
(root) — lines 98.7% (76/77), branches 93.4% (71/76)
File Lines Branches
PropertyAnnotationSchemaFilter.cs 64 / 65 (98.5%) 71 / 76 (93.4%)
Startup.cs 12 / 12 (100.0%) 0 / 0 (n/a)
Binding — lines 88.3% (106/120), branches 64.3% (72/112)
File Lines Branches
NonBodyStrongTypeOperationFilter.cs 106 / 120 (88.3%) 72 / 112 (64.3%)
Collections — lines 100.0% (6/6), branches 100.0% (4/4)
File Lines Branches
NonEmptyEnumerableSchemaFilter.cs 6 / 6 (100.0%) 4 / 4 (100.0%)
Digits — lines 100.0% (10/10), branches 100.0% (4/4)
File Lines Branches
DigitSchemaFilter.cs 10 / 10 (100.0%) 4 / 4 (100.0%)
Emails — lines 100.0% (10/10), branches 100.0% (4/4)
File Lines Branches
EmailSchemaFilter.cs 10 / 10 (100.0%) 4 / 4 (100.0%)
Inlining — lines 100.0% (2/2), branches n/a (0/0)
File Lines Branches
StrongTypeInliningDocumentFilter.cs 2 / 2 (100.0%) 0 / 0 (n/a)
Maybe — lines 100.0% (13/13), branches 75.0% (3/4)
File Lines Branches
MaybeSchemaFilter.cs 13 / 13 (100.0%) 3 / 4 (75.0%)
Numbers — lines 100.0% (8/8), branches 100.0% (4/4)
File Lines Branches
NumericStrongTypeSchemaFilter.cs 8 / 8 (100.0%) 4 / 4 (100.0%)
Strings — lines 100.0% (8/8), branches 100.0% (4/4)
File Lines Branches
NonEmptyStringSchemaFilter.cs 8 / 8 (100.0%) 4 / 4 (100.0%)
StrongTypes.OpenApi.TestApi.Microsoft — lines 41.0% (163/398), branches 31.6% (65/206)
(root) — lines 100.0% (14/14), branches 100.0% (2/2)
File Lines Branches
Program.cs 14 / 14 (100.0%) 2 / 2 (100.0%)
obj/Debug/net10.0/Microsoft.AspNetCore.OpenApi.SourceGenerators/Microsoft.AspNetCore.OpenApi.SourceGenerators.XmlCommentGenerator — lines 38.8% (149/384), branches 30.9% (63/204)
File Lines Branches
OpenApiXmlCommentSupport.generated.cs 149 / 384 (38.8%) 63 / 204 (30.9%)
StrongTypes.OpenApi.TestApi.Shared — lines 0.0% (0/192), branches n/a (0/0)
(root) — lines 0.0% (0/192), branches n/a (0/0)
File Lines Branches
AnnotatedRequests.cs 0 / 84 (0.0%) 0 / 0 (n/a)
BasicControllers.cs 0 / 59 (0.0%) 0 / 0 (n/a)
CustomAnnotationsControllers.cs 0 / 4 (0.0%) 0 / 0 (n/a)
Models.cs 0 / 45 (0.0%) 0 / 0 (n/a)
StrongTypes.OpenApi.TestApi.Swashbuckle — lines 100.0% (11/11), branches 100.0% (2/2)
(root) — lines 100.0% (11/11), branches 100.0% (2/2)
File Lines Branches
Program.cs 11 / 11 (100.0%) 2 / 2 (100.0%)

@KaliCZ KaliCZ force-pushed the claude/fervent-spence-2e61f8 branch from cac259e to cec5d21 Compare June 25, 2026 11:17
@KaliCZ KaliCZ changed the title OpenApi: drop orphaned component schemas after inlining strong-type wrappers OpenApi: drop the storage-type component orphaned by inlining a strong-type wrapper Jun 25, 2026
@KaliCZ KaliCZ force-pushed the claude/fervent-spence-2e61f8 branch from cec5d21 to da9be3a Compare June 25, 2026 11:28
…apper

A wrapper whose CLR storage type is an object (Email, backed by
System.Net.Mail.MailAddress) makes the generator register that storage
type as its own component. Painting the wrapper to its wire primitive
severs the only edge to it, so once the wrapper is inlined and dropped
the storage type is left as an orphan — openapi-typescript emits it as
an unused interface (spurious contract drift the first time Email is used).

The inliner already visits every $ref while inlining, so it now records
the ones it leaves in place and, afterwards, drops a small known list of
generated storage component names (currently just "MailAddress") — but
only those still unreferenced. A consumer that uses the type directly
(its own DTO with a MailAddress property) keeps it, and a consumer's own
schemas are never candidates. Extend the list when a new wrapper drags in
another storage type. Living in the shared Core inliner, both the
Swashbuckle and Microsoft pipelines benefit.

Tests: an integration assertion that the generated document carries no
orphaned component schema (reproduced the MailAddress orphan on the
Swashbuckle pipeline before the fix), plus a new pipeline-independent
StrongTypes.OpenApi.Core.Tests project pinning the scope against the
OpenAPI object model — the unreferenced storage type is removed, a
still-referenced one is kept, and an unrelated unreferenced consumer
schema is preserved.

Fixes #110

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@KaliCZ KaliCZ force-pushed the claude/fervent-spence-2e61f8 branch from da9be3a to b765926 Compare June 25, 2026 11:37
@KaliCZ KaliCZ merged commit f49cb28 into main Jun 25, 2026
2 checks passed
@KaliCZ KaliCZ deleted the claude/fervent-spence-2e61f8 branch June 25, 2026 11:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

OpenApi.Swashbuckle: using Email on a DTO leaves an orphaned MailAddress component schema

1 participant