# UploadResult

```ts
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

## Properties

### assetId

```ts
assetId: string;
```

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

***

### cdnUrl

```ts
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

```ts
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

```ts
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.

***

### oext?

```ts
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

```ts
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

```ts
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

```ts
sha256: string;
```

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

***

### variants

```ts
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

```ts
visibility: "public" | "private";
```

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