feat(types): inline-union value arrays for anonymous string-literal unions (#932)#938
Conversation
…nions Closes #932. Companion to the named-enum exports landed in 5.17 (#931). Where the earlier shipment covered every spec enum with a stable named type (`MediaChannelValues`, `PacingValues`, etc.), this adds the inline anonymous unions inside named schemas — exactly the cases where adapters were re-declaring spec literal sets (image formats, video containers, audio formats, codecs, channels, etc.) as drift bait in their own code. Naming: every `z.union([z.literal(...), ...])` inside a named object schema gets `${ParentSchema}_${PropertyName}Values` (PascalCased). Property paths that reference a named enum (e.g. `unit: DimensionUnitSchema.optional()`) are intentionally skipped — those already have `${TypeName}Values` exports from `enums.generated.ts`. Implementation: `scripts/generate-inline-enum-arrays.ts` walks the compiled Zod schemas via runtime introspection (Zod 4 `_def`) rather than regex on the generated TS — cleaner and future-proofs against codegen output format changes. Wired into `generate-zod-schemas` so it runs after Zod codegen. New test cross-validates every emitted array against the parent Zod schema property — drift in either side fails fast. 104 inline-union arrays across 51 parent schemas, including all user-flagged cases (Image/Video/AudioAssetRequirements_FormatsValues, VideoAssetRequirements_ContainersValues / _CodecsValues, etc.). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three review-driven hardenings: 1. Drop `'pipe'` from UNWRAP_TYPES. `z.pipe(in, out)` chains transforms and the inner `in` schema may not match the wire-accepted set after `out` runs — unwrapping it would silently extract the wrong half for `string → enum` style transforms. Current `schemas.generated.ts` has no `z.pipe(...)` constructions; if a future codegen change adds one, the extractor now correctly bails at a non-`union` core rather than emitting a wrong literal set, and the bumped floor catches the count collapse. 2. Add fingerprint-based fallback to the named-enum dedup gate. Identity-based check is exact and fast in the common case (Zod re-uses schema instances on property access), but breaks if codegen ever clones — e.g., a future `json-schema-to-zod` pass that inlines a referenced type as a fresh union. The fingerprint set keyed by sorted-literal-tuple catches that regression and prevents duplicate emissions of named-enum values under wrapped property names. This correctly drops 4 entries that previously double-emitted named enums under inline names: ListAccountsRequest_StatusValues (== AccountStatusValues), ReportingWebhook_ReportingFrequencyValues (== ReportingFrequencyValues), VASTAssetRequirements_VastVersionValues (== VASTVersionValues), WebhookAssetRequirements_MethodsValues (== HTTPMethodValues). Net: 100 inline arrays, no consumer-facing regression (each dropped name's values are reachable via the matching named-enum export). 3. Bump floor from <20 to <90. Catches partial-regression scenarios the previous floor only caught for catastrophic collapse. Plus test improvements: - Cross-validation regex switched from first-underscore split to last-underscore split, so parents with SCREAMING_SNAKE prefixes (e.g. `RATE_LIMITEDDetails`) get cross-validated instead of silently skipped. - New negative-case test pins the invariant that mixed-type unions (string + number, all-boolean) are skipped — defensive against a future spec change adding non-string members to a string-literal enum. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Ran three expert reviews in parallel (code-reviewer, dx-expert, ad-tech-protocol-expert). Convergence: ship the convention. Three pre-merge hardenings applied in c2c6283, four follow-up issues filed. Pre-merge fixes (c2c6283)
Deferred to follow-up issues
Spot-check verdictProtocol expert verified all 100 arrays match the spec (image formats, video containers/codecs/audio_codecs, audio formats/channels, vast_version, webhook methods, destination_type, image color_space). One legitimate quirk: ConvergenceThree experts, three lenses (correctness, DX, protocol). Zero blockers, all "ship-with-targeted-fixes." The capability-gate footgun pattern from PR #931 didn't repeat here — this PR's contract is "expose values that exist in the spec; consumers handle drift via versioning" — narrower and easier to keep honest. |
|
Correction to issue numbers in my prior comment — the deferred follow-ups landed as #941 (hoist duplicate inline-union literal sets to named enums) and #942 (rename SCREAMING_SNAKE schema artifacts). The two adcontextprotocol/adcp spec-side issues (vast_version x-extensible + enum stability policy) still pending. |
Summary
Closes #932. Companion to the named-enum exports landed in 5.17 (#931). The earlier shipment covered every spec enum that has a stable named type (
MediaChannelValues,PacingValues, etc., 122 total). This adds the inline anonymous unions inside named schemas — exactly the cases where consumers were re-declaring spec literal sets in their own validation code:Naming convention
Every
z.union([z.literal(...), ...])(orz.array(...)-wrapped variant) inside a named object schema gets${ParentSchema}_${PropertyName}Values. Property names PascalCase. Property paths that reference a named enum (e.g.unit: DimensionUnitSchema.optional()) are intentionally skipped — those already have${TypeName}Valuesexports fromenums.generated.ts.Coverage
104 inline-union arrays across 51 parent schemas. Includes all user-flagged cases:
ImageAssetRequirements_FormatsValues(jpg, jpeg, png, gif, webp, svg, avif, tiff, pdf, eps)VideoAssetRequirements_FormatsValues,_ContainersValues(mp4, webm, mov, avi, mkv),_CodecsValues,_FrameRateTypeValues,_ScanTypeValues,_GopTypeValues,_MoovAtomPositionValues,_AudioCodecsValues,_AudioChannelsValuesAudioAssetRequirements_FormatsValues(mp3, aac, wav, ogg, flac),_ChannelsValuesAccount,AppItem,BrandResolveRequest,CatalogItemError, etc.Implementation
New script
scripts/generate-inline-enum-arrays.tswalks the compiled Zod schemas via runtime introspection (Zod 4_def) rather than regex on the generated TS — cleaner and future-proofs against codegen output format changes. Output goes tosrc/lib/types/inline-enums.generated.ts. Wired intogenerate-zod-schemas(runs after Zod codegen since it depends on the schemas being current).The extractor reads source TS (
schemas.generated.ts) directly via tsx rather than thedist/build output, so it can run as part of pre-build codegen without a circular dependency onbuild:lib.Test plan
npm run build:lib— clean compilation, including the new generated filenode --test test/lib/inline-enum-arrays.test.js— 7/7 passunit: DimensionUnitSchema) are NOT re-emitted as inline-union exports@adcp/client/typesnpm run test:lib— 4973 pass, 0 fail, 6 skipped (pre-existing)npm run format:check— cleantsc --noEmit— cleanBehavior unchanged
Pure addition. No public-API rename, no breaking change to
enums.generated.ts, no behavior change increateAdcpServeror any other module. Adapters can drop their hand-maintainedVALID_IMAGE_FORMATS-style constants in a follow-up PR.🤖 Generated with Claude Code