fix(content): don't mutate readonly schemas during image reference processing - #17867
fix(content): don't mutate readonly schemas during image reference processing#17867suletetes wants to merge 4 commits into
Conversation
…readonly()` schemas do not throw
🦋 Changeset detectedLatest commit: 8742d5d The changes in this PR will be included in the next version bump. Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Merging this PR will degrade performance by 14.84%
|
| Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|
| ❌ | many-components (markHTMLString, isHTMLString, validateProps) |
9.6 ms | 11.3 ms | -14.84% |
Tip
Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.
Comparing suletetes:fix/content-readonly-image-schema (8742d5d) with main (0389640)1
Footnotes
ematipico
left a comment
There was a problem hiding this comment.
The tests should use the zod.readonly() bug that was reported.
updated |
What this fixes
Fixes #17793.
Using Zod's
.readonly()on an object containing an Astroimage()field causes the Content Layer to throw a runtimeTypeErrorduring content processing:The schema itself is valid Zod and parses the content successfully the failure happens afterward, when Astro processes image references.
Root cause
In
packages/astro/src/content/mutable-data-store.ts, the collection accessor'sset(...)handler walks the parseddatawith neotraverse'sforEachand callsctx.update(src)to strip theIMAGE_IMPORT_PREFIXfrom imagesrcstrings. That is an in-place mutation.When the user's schema uses
.readonly(), Zod returns a frozen object (and frozen nested objects), so mutating it directly throws.The fix
Before traversing/mutating, produce a deep mutable clone with
structuredClone(data), run the traversal over the clone, and store the clone in the entry. The user's original (possibly frozen) object is never written to.This matches the direction suggested in the issue "operate on a mutable copy rather than mutate the object returned by the user's schema."
structuredCloneis safe here because the content-layer data is plain, devalue-serializable data at this point (it is about to be serialized to disk). The image-prefixing schema-parse path is untouched.Tests
packages/astro/test/units/content-collections/mutable-data-store.test.ts: "handles frozen data from a.readonly()schema without throwing (issue Zod .readonly() schemas fail during Content Layer image reference processing #17793)." It builds a frozen object (with a nested frozen object) containing image-prefixed values, then asserts that:set(...)does not throw,imageImportspaths are recorded correctly.mutable-data-storeunit tests: 11/11 pass.tsc --noEmit: no errors (structuredCloneis a Node 22 global).A
patchchangeset is included.