---
id: 0027
title: Tipo Webpage + cache de documentos descargados
status: completed
priority: high
created: 2026-05-01
depends_on: [0026]
blocks: [0028, 0029, 0030]
---

## Objetivo

Anadir un tipo `Webpage` al `examples/types.yaml` y un layout
estandarizado de cache donde los enrichers guardan HTML, markdown y
screenshots descargados. El tipo `Url` existente queda como link suelto;
`Webpage` es un documento descargado con cuerpo.

## Cambios en `examples/types.yaml`

Anadir tras el bloque `Url`:

```yaml
- name: Webpage
  color: "#89E0FC"
  icon: ti-file-text
  principal_field: url
  fields:
    - { name: url,            type: url, required: true }
    - { name: title,          type: string }
    - { name: status_code,    type: int }
    - { name: content_type,   type: string }
    - { name: fetched_at,     type: date }
    - { name: html_path,      type: string }   # cache/<sha256[0:2]>/<sha256>.html
    - { name: markdown_path,  type: string }   # cache/<sha256[0:2]>/<sha256>.md
    - { name: screenshot_path,type: string }   # cache/<sha256[0:2]>/<sha256>.png
    - { name: text_length,    type: int }
    - { name: lang,           type: string }
```

## Layout del cache

```
<app_dir>/cache/
  ab/
    abcd1234...ef.html
    abcd1234...ef.md
    abcd1234...ef.png
  cd/
    cdef5678...01.html
    ...
```

- `sha256[0:2]` para evitar miles de archivos en un solo dir.
- Path absoluto desde `<app_dir>` para que sea portable entre PCs (paths relativos en metadata).
- `cache/` se anade al `.gitignore` del sub-repo.

## Helper C++

Funcion en `data.{h,cpp}` (o nuevo `cache_paths.{h,cpp}`):

```cpp
namespace ge {
// Resuelve el path absoluto donde un enricher debe escribir el blob.
// Crea el dir si no existe. ext sin punto: "html", "md", "png".
// hash_input: tipicamente la URL canonica (normalizada).
std::string cache_path(const char* app_dir,
                        const char* hash_input,
                        const char* ext);
}
```

- SHA256 calculado en C++ (usar implementacion existente en cpp/functions/core/ si la hay; si no, vendor/sqlite3 trae uno o se anade simple).
- O: el enricher Python calcula el sha256 (mas simple) y lo devuelve como parte de `node_updates`. Decidido: Python calcula el sha256, C++ solo expone `app_dir/cache/` como path absoluto al enricher.

## Definicion de hecho

- `Webpage` aparece en types.yaml con icono `ti-file-text`.
- El icono se renderiza correctamente (existe en tabler_codepoint_by_name).
- `cache/` esta en `.gitignore` del sub-repo del app.
- C++ pasa `cache_dir` al enricher en el JSON de stdin.
- Test manual: crear nodo `Webpage` desde el inspector, comprobar que
  aparece con el color/icono correctos.
