Make og:image work for SVG: pasted markup, svg: payloads, and UTF-8 - #1
Open
alcor wants to merge 3 commits into
Open
Make og:image work for SVG: pasted markup, svg: payloads, and UTF-8#1alcor wants to merge 3 commits into
alcor wants to merge 3 commits into
Conversation
This repo pointed at /.netlify/functions/rasterize, which it never contained, so the svg: image feature has never worked at all. The payload is passed through byte-for-byte rather than re-encoded, since it may be base64 or percent-encoded SVG depending on who wrote the URL; og-svg tries base64 first and falls back to percent-decoding. Renderer: https://github.com/arfct/og-svg Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Same pin as main (b83d6e2) so the deploy preview builds with a Node version that can install netlify-cli 27. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The editor invites SVG code ("Choose an emoji, image URL or SVG code")
and base64s whatever it is given with no marker, so a pasted SVG arrived
in metadata.js as raw markup. Only an explicit svg: prefix was routed to
the renderer, so markup fell through to the bare-hostname branch and
produced og:image="https://<svg xmlns=..." — no preview at all. This was
the default path for anyone using the editor's image field.
resolveImageUrl now detects markup directly, so a pasted SVG, an explicit
svg: payload, and a bare fragment all reach the renderer. Extracted as a
pure function so the branching is testable; the handler just calls it.
Also fixes UTF-8. btoa is Latin-1 only: an accent encoded as a raw 0xE9
byte that is not valid UTF-8, and resvg rejected the document with a 422,
while anything above U+00FF made btoa throw outright. The editor now uses
a UTF-8-safe encoder and decodeURL recovers UTF-8, falling back to the raw
bytes so links written by the old editor still work.
Adds vitest and 14 tests; the repo had none.
Co-Authored-By: Claude Opus 5 <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.
Makes
og:imagework for SVG. Two independent bugs, both of which meant links got no preview image at all.Bug 1 — the rasterizer was never reachable
metadata.jsbuilt its rasterizer URL as a path:This repo has never contained a
rasterize.js. And in the repo that does (itty-bitty), that function readsevent.rawQuery— never the path — so a path-shaped call could not have worked either. Probed against production using itty-bitty's copy:/.netlify/functions/rasterize/svg:<b64>— as this code built it502/.netlify/functions/rasterize?<b64>— as the function reads it200 image/jpeg, 8707 bytesNow points at the shared og-svg renderer with the payload as a query parameter.
Bug 2 — pasted SVG bypassed the renderer entirely
This is the bigger one, because it was the default path rather than an edge case.
The editor invites SVG code —
prompt("Choose an emoji, image URL or SVG code")— and then base64s whatever it is given, with nosvg:marker:metadata.jsonly routed to the renderer on an explicitsvg:prefix. AfterdecodeURLbase64-decoded the value, a pasted SVG was raw markup, matched no branch, and fell through to the bare-hostname case:resolveImageUrlnow detects markup directly, so all three shapes reach the renderer: pasted markup, an explicitsvg:payload, and a bare fragment (og-svg wraps fragments in an<svg>root with a 1200×630 viewport).It is extracted as a pure exported function so the branching is testable — the handler just calls it.
Bug 3 — UTF-8 was mangled
Found only by rendering against the live service; the unit tests were passing.
btoais Latin-1 only. An accented character encoded as a raw0xE9byte that is not valid UTF-8, so resvg rejected the whole document with a422. Anything above U+00FF — an emoji, a checkmark — madebtoathrow outright before the URL was even built.The editor now uses a UTF-8-safe encoder, and
decodeURLrecovers UTF-8 with a fallback to the raw bytes, so links written by the old editor still resolve.Verified
Every shape, through the editor's real encode path, against the live renderer:
https://<svg…200PNG 600×315https://<circle…200PNG 1200×630422200PNG 400×200✓btoathrows200PNG 400×200svg:prefixed payload502200PNGTests
Adds vitest and 14 tests. The repo had none.
Worth noting the unit tests alone did not catch the UTF-8 bug — it surfaced only when the end-to-end check against the live renderer disagreed with a green suite. The encoding boundaries are where this code breaks, so the tests now pin them explicitly, including a case asserting that Latin-1 payloads from the old editor still work.
Note on output format
og-svg returns PNG; the old rasterizer produced JPEG. Since no caller ever reached it successfully there is no existing behaviour to preserve, and every crawler accepts PNG.
RENDER_ORIGINis a single exported constant, so moving og-svg to a dedicated domain later is a one-line change.🤖 Generated with Claude Code