Skip to content
scroll to zoom · drag to pan

UploadResult

type UploadResult = {
assetId: string;
cdnUrl: string | null;
kind: AssetDTO["kind"];
mime: string;
oext?: string | null;
presets: string;
sha: string;
sha256: string;
variants: AssetVariant[];
visibility: "public" | "private";
};

Defined in: packages/sdk/src/index.ts:1136

assetId: string;

Defined in: packages/sdk/src/index.ts:1137


cdnUrl: string | null;

Defined in: packages/sdk/src/index.ts:1202

La URL pública de una variante razonable — o null si el asset es privado, que no tiene ninguna.

⭐⭐ ERA string, Y ESO HACÍA QUE upload() PERDIERA EL HANDLE

Los tres retornos de upload() arman este campo con urlFor, que llama a assertPublic y tira sobre un asset privado. Medido 2026-08-25 por una auditoría externa: subir bytes que deduplican contra una fila privada hacía que upload() lanzara DESPUÉS del PUT — los bytes quedaban guardados y el assetId se perdía, porque el error es un Error pelado sin assetId, sin sha y sin cause.

⚠️ Y el mensaje nombraba getAssetUrl y un preset "lg" que el llamador nunca pidió, así que iba a buscar en su código una función que no llamó.

Un asset privado no tiene URL pública: eso es la feature. Lo que no puede pasar es que no tenerla cueste el resultado de una subida que ya ocurrió. Para servirlo, mirá visibility y usá getPrivateAssetUrl con la signing key, en tu backend.


kind: AssetDTO["kind"];

Defined in: packages/sdk/src/index.ts:1226

What the asset IS (image · video · audio · document · other).

⭐ It exists because it did not, and the guard caught it before anyone else did — the FIFTH member of this family, after sha, mime, oext and presets. When getAssetUrl learned to refuse hls on something that can never have a ladder, it started READING kind; an UploadResult without it silently opted out of the refusal and went right back to building /t/format=hls/<sha>.m3u8 — which does not 404, it answers 200 with the image bytes.

The rule this keeps re-teaching: a return type that omits what the next call reads is a silent 404 (or worse, a silent 200). The fix is always to carry the field, never to write a better error message about its absence.


mime: string;

Defined in: packages/sdk/src/index.ts:1173

The MIME the upload was stored under.

⭐ It exists because it did not, and that cost the THIRD 404 of this exact family. Found 2026-08-23 by an agent running the getting-started doc verbatim: getAssetUrl(up, "original") built <sha>-o.bin and 404’d, while the object served fine at -o.jpg.

original is the one preset whose extension is not fixed — it is the bytes you uploaded, so the extension comes from the MIME (ORIGINAL_EXT_BY_MIME), and PRESET_EXT.original is only the "bin" fallback for when nothing says otherwise. UploadResult said nothing, so every caller handing an upload result straight to a URL builder got the fallback.

The pattern is now three for three — sha256 vs sha, and this: a result type that omits what the next call needs turns the obvious call into a silent 404. The fix is never a better error message.


optional oext?: string | null;

Defined in: packages/sdk/src/index.ts:1180

The extension the server actually stored the original under, when it said so. Authoritative — it beats any client-side MIME table, because the server keys the object off the uploaded filename for the cases no table can close (.mpga, .docx, .m4a all arrive as octet-stream).


presets: string;

Defined in: packages/sdk/src/index.ts:1252

La cadena compacta de variantes que EXISTEN ("lmoqs", "o", …).

⭐⭐ EL QUINTO DEFECTO DE LA MISMA FAMILIA

getAssetUrl cambia de estrategia según este campo. Con él, un preset de imagen ausente cae a /t/…width=N/ y el borde lo genera al vuelo: 200. Sin él, el builder no sabe qué existe, asume que todo existe, y devuelve la clave de variante — que da 404 en silencio.

Medido 2026-08-25 sobre un asset con presets: "lmoqs":

sin `presets` → /f/v/<sha>-x.webp 404
con `presets` → /t/format=webp,width=3840/<sha>.webp 200

UploadResult no lo llevaba, así que getAssetUrl(up, "xl") —el patrón que enseña toda la doc— caía del lado malo.

⚠️ Es el mismo error que sha, mime y oext, por cuarta vez: un tipo de retorno que omite lo que la próxima llamada necesita convierte la llamada obvia en un 404 silencioso. Y el guarda que escribí para esto fijaba los tres hints de original; presets no es un hint, es lo que gobierna el fallback, así que quedaba afuera de lo que el guarda miraba.


sha: string;

Defined in: packages/sdk/src/index.ts:1154

The SAME 16-hex prefix an AssetDTO carries, so the result of an upload can be handed straight to any URL builder.

⭐ It exists because it did not, and that cost a real 404. A Haiku agent evaluating the SDK on 2026-08-23 did the most natural thing there is — transform(await upload(file), { width: 1280 }) — and got https://8ok.uk/t/width=1280/undefined.webp. The builders read sha; this type only had sha256. TypeScript caught it; running through bun, or in plain JS, nothing did.

The guard (assertSha) is the backstop. This field is the actual fix: the obvious call is now the correct one, which is worth more than a good error message about the wrong one.


sha256: string;

Defined in: packages/sdk/src/index.ts:1138


variants: AssetVariant[];

Defined in: packages/sdk/src/index.ts:1266

La lista de variantes tal como el servidor las guardó.

⭐ La SEXTA instancia del mismo defecto, y la primera que encontró un guarda en vez de un usuario.

Para original, getAssetUrl prefiere la URL almacenada sobre cualquier derivación — porque ESA es la clave real, y derivarla del mime es una suposición que ya falló dos veces (-o.bin, -o.jpeg). Sin este campo, el resultado de upload() obligaba a suponer.

Sale gratis: upload() ya espera el DTO, que la trae.


visibility: "public" | "private";

Defined in: packages/sdk/src/index.ts:1227