fix(puter-js): size img2txt data URIs by decoded bytes (#3457)

Match speech2txt/speech2speech and reject on decoded payload size, not
data-URI string length. Base64 expands ~4/3, so string-length checks
falsely rejected valid images under the 10MB limit.
This commit is contained in:
Felix-Ayush
2026-07-29 12:21:21 -07:00
committed by GitHub
parent fd1f4e2ac2
commit 710e4da4c0
2 changed files with 22 additions and 2 deletions
+20
View File
@@ -289,6 +289,26 @@ describe('ai.img2txt driver payloads', () => {
it('img2txt rejects without a source', async () => {
await expect(ai.img2txt({})).rejects.toMatchObject({ code: 'source_required' });
});
it('img2txt sizes data URIs by decoded bytes, not string length', async () => {
FakeXHR.respondWith = () => ({ success: true, result: { text: 'ok' } });
const prefix = 'data:image/png;base64,';
// String length just over 10MB; base64 still decodes under the 10MB limit (~7.5MB).
const targetLen = 10 * 1024 * 1024 + 4;
let base64Len = targetLen - prefix.length;
base64Len += (4 - (base64Len % 4)) % 4;
const uri = prefix + 'A'.repeat(base64Len);
expect(uri.length).toBeGreaterThan(10 * 1024 * 1024);
await expect(ai.img2txt(uri)).resolves.toBe('ok');
});
it('img2txt rejects when decoded data-URI bytes exceed 10MB', async () => {
const prefix = 'data:image/png;base64,';
let base64Len = Math.ceil(((10 * 1024 * 1024) + 1) * 4 / 3);
base64Len += (4 - (base64Len % 4)) % 4;
const uri = prefix + 'A'.repeat(base64Len);
await expect(ai.img2txt(uri)).rejects.toMatchObject({ code: 'input_too_large' });
});
});
describe('ai.txt2speech driver payloads', () => {
+2 -2
View File
@@ -1,5 +1,5 @@
import * as utils from '../../lib/utils.js';
import { isBlobLike, isPlainObject } from './lib/args.js';
import { dataUriByteLength, isBlobLike, isPlainObject } from './lib/args.js';
/** @typedef {import('../../../types/modules/ai').Img2TxtOptions} Img2TxtOptions */
@@ -124,7 +124,7 @@ export async function img2txt (sourceOrOptions, optionsOrTestMode, testModeOrOpt
if ( typeof options.source === 'string' &&
options.source.startsWith('data:') &&
options.source.length > MAX_INPUT_SIZE ) {
dataUriByteLength(options.source) > MAX_INPUT_SIZE ) {
throw { message: `Input size cannot be larger than ${ MAX_INPUT_SIZE}`, code: 'input_too_large' };
}