@cactus-agents/brand
Transforma as respostas raw da API em BrandConfig limpo e tipado. Faz 3 requests em paralelo e aplica transformações.
Instalação
pnpm add @cactus-agents/brand
Uso básico
A forma recomendada é usar createBrandFromClient, que aceita um ApiClient diretamente:
import { createApiClient } from '@cactus-agents/api-client';
import { createBrandFromClient } from '@cactus-agents/brand';
const client = createApiClient({ baseUrl, tenant, language });
const brand = await createBrandFromClient(client, { country: 'BRA', language: 'pt-br' });
// brand.appearance.logo
// brand.features.maintenanceMode
// brand.settings.name
Uso de baixo nível (fetcher manual)
Se precisar de controle total sobre o adapter HTTP, use createBrandConfig:
import { createBrandConfig } from '@cactus-agents/brand';
const brand = await createBrandConfig({
fetcher: {
get: (path) => myHttpClient.get(path),
post: (path, body) => myHttpClient.post(path, body),
},
country: 'BRA',
language: 'pt-br',
});
API
createBrandConfig(options)
function createBrandConfig(options: CreateBrandConfigOptions): Promise<BrandConfig>;
interface CreateBrandConfigOptions {
fetcher: BrandFetcher;
country: string;
language: string;
}
interface BrandFetcher {
get(path: string): Promise<unknown>;
post(path: string, body: unknown): Promise<unknown>; // body é OBRIGATÓRIO
}
:::caution post exige body
No BrandFetcher, o segundo parâmetro de post não é opcional. Um adapter escrito como post: (path) => ... não satisfaz a interface. Passe {} quando não há corpo.
:::
Faz 3 requests em paralelo:
GET /appearanceGET /bff/featuresPOST /bookmaker-settings
selectByCountry(items, country)
Seleciona item de um array multi-país:
selectByCountry(items, 'BRA')
// 1. Exact match: item.country.code === 'BRA'
// 2. Default: item.is_default === 1
// 3. Fallback: items[0]
safeParseJson(raw, fallback)
Parse seguro de JSON strings (muitos campos da API vêm como JSON string):
safeParseJson('{"key": "value"}', {}) // { key: "value" }
safeParseJson('invalid', {}) // {}
safeParseJson(null, []) // []
bool(v)
Coerce 0 | 1 | null → boolean:
bool(1) // true
bool(0) // false
bool(null) // false
Transformações aplicadas
| Raw | Transformado |
|---|---|
Banners {"1":{...}} ou [] | Banner[] sorted by order |
Links duplicados (LINK_TO_X) | Último entry vence |
| JSON strings (auth_configs, contacts, etc.) | Objetos parseados |
Flags 0 | 1 | boolean |
| Arrays multi-país | Filtrado por selectByCountry |
| 26 campos sem consumidor | Podados — não aparecem no BrandConfig |
Campos podados (slim 2026-07-15)
O commit 116c234 removeu 26 campos dos três transforms (transform/appearance.ts, transform/features.ts, transform/settings.ts) porque não tinham consumidor e viajavam no turbo-stream de toda página + em todos os tiers de cache.
O raw do BFF continua enviando esses campos — a poda é só do lado transformado. Reativar exige descomentar em packages/types/src/brand.ts e no transform correspondente. Lista completa dos 26 e detalhes em types.
Isso é a assimetria mais fácil de tropeçar neste pacote: ver o campo no payload da API não significa que ele existe no BrandConfig.
Raw Types
O pacote exporta raw types para referência (RawAppearance, RawFeatures, RawFeaturesResponse, RawSettings). Representam as shapes verbatim da API e nunca devem ser usados na UI — use apenas BrandConfig e seus sub-tipos.