fix(types): restore typed Zod for per-asset-type AssetRequirements#1660
Merged
Conversation
…1659) Strip the cross-file `import type { ... } from './core.generated';` block from tools.generated.ts before merging into the combined source passed to ts-to-zod. With that import in place, ts-to-zod treated the 12 *AssetRequirements names as external and emitted z.any() stubs — even though the actual interfaces were inlined in the merged source. The import was added in #1654 so tools.generated.ts typechecks standalone; it's redundant inside the merged Zod codegen source, where core.generated is already concatenated. Restores typed Zod for ImageAssetRequirementsSchema, TextAssetRequirements- Schema, and the 10 siblings, plus the AssetRequirementsSchema union and the `requirements` field on every Individual*AssetSchema / Group*AssetSchema slot. Adds a regression test that probes wrong-typed fields — a z.any() regression would silently accept them. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two cheap build-time defenses, both class-level not instance-level:
1. After stripping `import type { ... } from './core.generated'`, assert
the strip actually removed it. If the injector in generate-types.ts
ever changes shape (different specifier, single-line form), the silent
no-op would regress to z.any() stubs.
2. After ts-to-zod runs, scan for `const FooSchema = z.any();` lines and
throw. This catches any future codegen path that degrades a named
schema to z.any(), not just the import-shadow class.
Both surface re-regressions at the codegen step rather than in a
downstream consumer's test suite.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.
Closes #1659.
Why
#1654 introduced a 6.19.0 regression on the runtime-Zod side of the per-asset-type
requirementsbag: the 12*AssetRequirementsSchemaexports (ImageAssetRequirementsSchema,TextAssetRequirementsSchema, …) and the parentAssetRequirementsSchemaunion were emitted asz.any()stubs. Therequirementsfield on everyIndividual*AssetSchema/Group*AssetSchemaslot collapsed toz.optional(z.any()). TypeScript types were unaffected — only Zod regressed.Concretely,
IndividualImageAssetSchemaaccepted{ asset_type: 'image', requirements: { max_animation_duration_ms: 'not-a-number' } }cleanly in 6.19, even thoughmax_animation_duration_msis typed asnumber. Downstream consumers that imported*AssetRequirementsSchema(e.g. agentic-api in scope3data/agentic-api#2366) saw the exports vanish.Root cause
scripts/generate-types.ts::applyIndividualAssetDiscriminators(added in #1654) injects animport type { ImageAssetRequirements, … } from './core.generated';block at the top oftools.generated.tsso the file typechecks standalone. The Zod codegen step atscripts/generate-zod-from-ts.tsthen concatenatescore.generated.ts+tools.generated.tsand passes the combined source tots-to-zod. Butts-to-zodstill parses that cross-fileimport typedeclaration and treats those names as external — emittingz.any()stubs even though the matching interfaces are inlined in the same source.What
Strip cross-file
import type { … } from './core.generated';declarations fromtools.generated.tsbefore merging into the combined source. The imported types are already inlined fromcore.generated.ts, so the declarations are redundant in the merged context — and removing them letsts-to-zodresolve the inline definitions normally.The 12 per-asset-type schemas now emit as typed
z.object({...}), e.g.ImageAssetRequirementsSchemacarriesmin_width,aspect_ratio,max_animation_duration_ms, thebleedunion, etc.AssetRequirementsSchemais restored to a realz.unionof those 12 schemas. Therequirementsfield on every slot points at the matching typed schema again.Test plan
test/lib/zod-schemas.test.js— asserts each*AssetRequirementsSchemarejects wrong-typed fields (az.any()regression would silently accept them) and thatAssetRequirementsSchemarejects non-object values.npx tsc --noEmit— cleannpm run format:check— cleannode --test test/lib/zod-schemas.test.js— 42/42 passzod-schemas+format-assets+format-asset-slot-builders+validation) — 91/91 pass🤖 Generated with Claude Code