# Contexto de navegación del storefront (window.FINDALO_CTX)

> Documentación para desarrolladores de Findalo (buscador SaaS para tiendas online). Versión HTML: https://findalo.io/devs/contexto/
> Contrato estable: los tokens, clases, variables y eventos documentados solo se amplían, nunca se renombran ni eliminan. Todo lo NO documentado es interno del widget y puede cambiar sin aviso.

El módulo de Findalo publica en la tienda una variable JS, `window.FINDALO_CTX`, con lo que el SERVIDOR sabe de la página actual: tipo de página, ids de producto/categoría, idioma, divisa, carrito y grupo de cliente. Misma forma en PrestaShop, WooCommerce, Magento y Shopware, así que el código que la consume no necesita ramas por plataforma.

Existe para no adivinar: deducir la página por las clases del `<body>` o el producto por el JSON-LD falla en temas a medida. Aquí el dato viene del servidor.

## Dos mitades (por la caché de página)

- **Estática** — tipo de página, ids, idioma, divisa, URLs, datos de la ficha. Va en el HTML: es igual para todos los visitantes de esa URL.
- **Privada** — carrito, cliente y precio del producto. NO va en el HTML: con caché de página (LiteSpeed, Varnish, plugins de WordPress, Full Page Cache de Magento) ese HTML se sirve a otro visitante y el carrito llegaría ajeno. Se pide a un endpoint propio con la sesión del visitante y `Cache-Control: no-store`, y el widget la mezcla en el MISMO objeto al llegar.

Por eso: para leer tipo de página o producto vale `window.FINDALO_CTX` directo; **si dependes del carrito o del cliente, usa `findalo.onContext(cb)`** (o el evento `context`), que se dispara cuando la parte privada ya está dentro. Y en el carrito `null` significa DESCONOCIDO, no cero: un carrito que no se pudo leer no debe disparar una regla por importe.

```js
const ctx = window.FINDALO_CTX;
if (ctx.page.type === 'product') console.log(ctx.product.id, ctx.product.sku);

findalo.onContext((ctx) => {
  if (ctx.cart?.total !== null && ctx.cart.total < 20) { /* … */ }
});
```

## Campos

| Campo | Tipo | Qué es |
|---|---|---|
| `v` | `number` | Versión del contrato. Hoy 1. Si sube, los campos actuales siguen existiendo. |
| `ready` | `boolean` | true cuando la parte privada (carrito y cliente) ya está resuelta. Si dependes de ellos, usa findalo.onContext en vez de leer el objeto directamente. |
| `source` | `"connector" | "fallback"` | «connector» = lo emitió el módulo de tu plataforma. «fallback» = el módulo es anterior a esta feature y el widget reconstruyó lo que pudo. |
| `emittedAt` | `number` | Epoch (s) en que el servidor generó la parte estática. Si tu tienda cachea páginas, delata HTML viejo. |
| `shop` | `object` | platform, platformVersion, connector (versión del módulo), id de tienda/canal, name, baseUrl, homeUrl, cartUrl, checkoutUrl, accountUrl. |
| `locale` | `object` | lang (ISO), locale, currency (ISO), country, langId nativo. |
| `page.type` | `string` | home · product · category · brand · search · cart · checkout · order-confirmation · cms · account · other. |
| `page.legacyType` | `string` | El mismo tipo reducido a los 5 valores históricos (home/product/category/cart/other). |
| `page.route` | `string` | El controlador o ruta NATIVA sin traducir: php_self en PrestaShop, full action name en Magento, _route en Shopware, plantilla en WordPress. |
| `page.ids` | `object` | Ids de la página: product, category, brand, supplier, cms, order, variant. Solo aparece el que corresponde al tipo de página. |
| `page.query` | `string` | Término de la búsqueda NATIVA de la tienda (no la del widget), en páginas de tipo search. |
| `product` | `object | null` | Ficha actual: id (en el id-space del catálogo indexado), uuid nativo si difiere, sku, name, url, image, inStock, categoryId(s), brandId, variantId. price y purchasable llegan con la parte privada. |
| `category` | `object | null` | Listado actual: id, name, count y path (migas desde la raíz). |
| `cart` | `object | null` | count, quantity, total, subtotal, currency, lines[]. OJO: null en un campo significa DESCONOCIDO, no cero. |
| `customer` | `object` | logged, id, groups[], pricingGroup y token de visibilidad. Sin datos personales: nunca email ni nombre. |
| `native` | `object` | El crudo de tu plataforma: lo que manda el módulo (ids nativos) más los globals que ya publica la tienda — prestashop, los params de WooCommerce, mage-cache-storage, los globals del storefront de Shopware. |
| `dynamic` | `object | null` | URL del endpoint de la parte privada, por si quieres refrescar el carrito tú mismo. |

## Qué trae cada plataforma

| Plataforma | Desde | Carrito | Notas |
|---|---|---|---|
| PrestaShop | módulo 1.6.45+ | completo | Carrito con líneas, cliente con todos sus grupos y precio del producto para su tarifa. |
| WooCommerce | plugin 1.3.8+ | completo | Carrito con líneas; el «grupo» del cliente son sus roles de WordPress, que es lo que leen los plugins de precios B2B. |
| Magento | módulo 1.3.6+ | completo | Carrito del quote y precio con las catalog price rules del grupo. La parte estática es FPC-safe: no tumba la caché de página. |
| Shopware | App 0.9.5+ | sin carrito | La App no ejecuta PHP en la tienda y el hook del endpoint no expone el carrito, así que cart queda como DESCONOCIDO (no como vacío). Sí llegan el grupo del cliente y si está logueado. |

## El crudo de la plataforma: `native`

Lo normalizado cubre lo común a las cuatro plataformas; `ctx.native` lleva lo específico — los ids nativos que manda el módulo (`id_shop`, `store_id`, `salesChannelId`…) y una referencia a los globals que la tienda ya publica:

```js
ctx.native.prestashop?.page.page_name          // PrestaShop
ctx.native.wc_add_to_cart_params?.ajax_url     // WooCommerce
ctx.native.mageCacheStorage?.cart?.summary_count // Magento (localStorage del core)
ctx.native.activeNavigationId                  // Shopware
```

## Módulos anteriores

La variable existe igual, con `source: "fallback"`: el widget la reconstruye desde su configuración y sus heurísticas. Menos campos, misma forma.

## Garantía

Los nombres de campo son contrato estable: se añaden, nunca se renombran ni se eliminan. Un cambio de forma sube `v`.
