refactor(core): avoid encoding rejected image candidates (#46073)

This commit is contained in:
Kit Langton 2026-08-28 23:32:58 -04:00 committed by GitHub
parent 6e954f75ee
commit 6cfffeb031
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 47 additions and 3 deletions

View file

@ -78,9 +78,15 @@ export const make = Effect.gen(function* () {
...JPEG_QUALITIES.map((quality) => ["image/jpeg", () => resized.get_bytes_jpeg(quality)] as const),
]
for (const [mime, encode] of encoders) {
const candidate = Buffer.from(encode()).toString("base64")
if (Buffer.byteLength(candidate, "utf-8") <= limits.maxBase64Bytes)
return { ...content, content: candidate, encoding: "base64" as const, mime }
const candidate = encode()
// Base64 uses four bytes per three input bytes, including padding.
if (Math.ceil(candidate.length / 3) * 4 <= limits.maxBase64Bytes)
return {
...content,
content: Buffer.from(candidate).toString("base64"),
encoding: "base64" as const,
mime,
}
}
} finally {
resized.free()

View file

@ -437,6 +437,44 @@ describe("ReadTool", () => {
}),
)
it.effect("accepts PNG candidates at the exact base64 limit and skips them one byte below", () =>
Effect.gen(function* () {
const photon = yield* Effect.promise(() => import("@silvia-odwyer/photon-node"))
const source = new photon.PhotonImage(
Uint8Array.from({ length: 16 * 4 }, () => 255),
16,
1,
)
const content = {
uri: "file:///wide.png",
content: Buffer.from(source.get_bytes()).toString("base64"),
encoding: "base64" as const,
mime: "image/png",
}
source.free()
const image = yield* Image.Service
for (const [maxWidth, padding] of [
[4, "=="],
[5, "="],
[6, ""],
] as const) {
yield* image.transform((draft) => draft.configure({ maxWidth, maxBase64Bytes: 1_024 }))
const candidate = yield* image.normalize("wide.png", content)
expect(candidate.mime).toBe("image/png")
expect(candidate.content.match(/=*$/)?.[0]).toBe(padding)
yield* image.transform((draft) => draft.configure({ maxBase64Bytes: candidate.content.length }))
expect(yield* image.normalize("wide.png", content)).toEqual(candidate)
yield* image.transform((draft) => draft.configure({ maxBase64Bytes: candidate.content.length - 1 }))
const smaller = yield* image.normalize("wide.png", content)
expect(smaller.mime).toBe("image/png")
expect(smaller.content.length).toBeLessThan(candidate.content.length)
}
}),
)
it.effect("drops images that cannot fit max base64 bytes after resize attempts", () =>
Effect.gen(function* () {
const png = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII="