Skip to content

json-schema-to-typescript emits Foo+Foo1 for inline asset variants referenced from multiple parents #3459

@bokelley

Description

@bokelley

Context

@adcp/client 5.x uses json-schema-to-typescript to generate TypeScript types from the AdCP JSON schemas. When the same asset schema is referenced from two parent schemas, the generator emits a numerically-suffixed duplicate type for the inline form. This surfaces as the following exported types in core.generated.ts:

  • VASTAsset (from creative/asset-types) and VASTAsset1 (from creative-asset.json's asset map)
  • DAASTAsset and DAASTAsset1
  • BriefAsset and BriefAsset1
  • CatalogAsset and CatalogAsset1
  • CreativeAsset1 (from creative-manifest.json referencing creative-asset.json)

The two forms are NOT byte-identical — VASTAsset is the wrapped form ({ asset_type: 'vast', vast_version, vpaid_enabled, captions_url, … } & (url|inline)), while VASTAsset1 is just the inner discriminated (url|inline) union exposed inline inside creative-asset.json's asset map. Both shapes are valid public types — but the 1 suffix reads as a versioning artifact and makes consumer imports harder to review.

AgeVerificationMethod was the same artifact and got fixed upstream by introducing a single $ref to enums/age-verification-method.json so json-schema-to-typescript only emits one type. This issue tracks the equivalent fix for the asset-variant refs.

Repro

git clone https://github.com/adcontextprotocol/adcp-client
cd adcp-client
npm i && npm run sync-schemas && npm run generate-types
grep -E '^export type (VAST|DAAST|Brief|Catalog)Asset1' src/lib/types/core.generated.ts
# → emits VASTAsset1, DAASTAsset1, BriefAsset1, CatalogAsset1

Where the duplication originates

The asset map in core/creative-asset.json and core/creative-manifest.json both inline a oneOf over the asset variant schemas:

{
  "oneOf": [
    { "$ref": "/schemas/3.0.1/core/assets/vast-asset.json" },
    { "$ref": "/schemas/3.0.1/core/assets/daast-asset.json" },
    { "$ref": "/schemas/3.0.1/core/assets/brief-asset.json" },
    { "$ref": "/schemas/3.0.1/core/assets/catalog-asset.json" },
    
  ]
}

The generator hoists the inline form a second time when it crosses the second parent.

Suggested fix

Two options, in increasing order of churn:

Option A — promote the shared inline-variant union to its own schema file. Create something like core/assets/asset-variant.json that is the oneOf union, and have both creative-asset.json and creative-manifest.json reference that single schema. json-schema-to-typescript then emits one shared type instead of two suffixed copies.

Option B — inline the variant union as a JSON Schema $defs in a single canonical location (e.g. inside creative-asset.json's top-level $defs) and have other parents reference creative-asset.json#/$defs/asset-variant. Less invasive than (A) but still produces a single emitted type.

Either is fine; option (A) is cleaner because the variant list isn't a 'private' concept of creative-asset.json — it's the canonical list of inline asset forms.

SDK-side workaround we are NOT pursuing

Renaming *Asset1 to Inline*AssetDelivery via a post-process step on core.generated.ts is straightforward (~50 LOC + alias map). We've decided against it because the right fix is at the schema source — bandaiding in the SDK compounds drift across implementations.

Cross-ref

🤖 Filed by Claude Code on behalf of @bokelley

Metadata

Metadata

Assignees

No one assigned

    Labels

    claude-triagedIssue has been triaged by the Claude Code triage routine. Remove to re-triage.spec / protocol

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions