Pular para o conteúdo principal

Changelog - 10/05/2026

Remarketing first-party (rmkvera)

  • Sistema de remarketing first-party com UUID v4 estável + audience tags do funil, brand-aware via feature flag (app/config/features/remarketing.ts). Vera ativa com cookie rmkvera (365 dias sliding TTL, Domain=.vera.bet.br, SameSite=None, Secure); demais brands fazem opt-in declarando seu próprio cookieName no override.
  • Cookie rmkvera_aud armazena tags do funil em JSON (90 dias sliding TTL, cap de 40 tags com eviction de oldest, set-once semantics) — pra ser usado como external_id em Meta CAPI, Google Enhanced Conversions e TikTok Events API. Funciona mesmo sem cookies de terceiros (iOS 14+, ITP, Brave).
  • Tags emitidas automaticamente em três eixos: path-based via useLocation brand-aware no Route Registry (viewed_casino, viewed_live_casino, viewed_game_detail, viewed_sports, viewed_promotions), modal-based via Zustand (clicked_register, registered, abandoned_register, viewed_deposit_modal, abandoned_deposit) e FTD-based em useAnalytics.trackDepositConfirmed (ftd_completed, multi_deposit).
  • Captura server-side no root loader (captureRemarketingIdFromRequest) — sobrevive a redirects via Set-Cookie na própria response do 302 (mesmo padrão do tracking de UTMs). Client-side RemarketingCapture faz refresh de TTL, publica em window.__rmk e dispara rmk_ready + rmk_audience_tag no dataLayer.
  • Regras duras: nome do cookie rejeita termos internos da plataforma (cactus, bluetec) em runtime, bot traffic é skipado no server, validador strict UUID v4 RFC 4122 contra cookies adulterados. 39 unit tests + 16 server-side tests cobrindo capture, Set-Cookie, regenerate em tamper, eviction.

Changelog - 09/05/2026

Geo-SEO multi-país da rede 7k (BR / CL / NG / FI)

  • Cluster recíproco de hreflang sem x-default: cada um dos quatro domínios da família 7k declara as outras três variantes como alternates explícitas — 7k.bet.br (pt-BR, self) + cl.bet7k.com (es-CL) + ng.7k.bet (en-NG) + fi.7k.bet (fi-FI). Os três overrides novos (overrides/{cl-bet7k-com,fi-7k-bet,ng-7k-bet}/app/config/seo/hreflang.ts) e a atualização do 7k-bet-br removem o x-default que apontava pro Brasil — cada brand é geo-exclusiva (KYC, regulação e moeda locais), então não existe "world fallback" legítimo. Resultado: o Google só serve a variante quando o idioma/região do usuário matcha um alternate explícito (ex: russo ou alemão não vê nenhum domínio, em vez de cair no 7k.bet.br).
  • Schema.org/Organization enriquecida com sinais de país: buildOrganizationJsonLd() (app/utils/seo.ts) agora aceita addressCountry (ISO-3166 alpha-2) + areaServedName (nome do país em inglês) e emite address.addressCountry + areaServed.@type=Country. O _layout.tsx resolve ambos via getCountryByAlpha3 de @cactus-agents/country-config — mesmo catálogo usado pelo api-client pra derivar geo headers, evita duplicar mapping alpha-3 → alpha-2 + nome do país.
  • Novo JSON-LD WebSite com inLanguage: segundo blob de Schema.org reforçando o idioma da página de forma independente do <html lang>. Google parseia inLanguage separadamente, então emitir os dois dá ao algoritmo dois sinais redundantes de locale.
  • Header HTTP Content-Language injetado em toda response HTML pelo app/entry.server.tsx (text/html only — JSON/JS unaffected). Sourced do define __BRAND_LANGUAGE__ do Vite e mapeado pra forma BCP 47. Adiciona uma terceira camada de sinal de locale (header HTTP + JSON-LD + <html lang>).
  • Helper compartilhado app/utils/locale.ts consolida os mapas BRAND_LANGUAGE → BCP 47 (pra <html lang>, Content-Language, inLanguage, hreflang) e BRAND_LANGUAGE → og:locale (pra OpenGraph). Antes os dois mapas ficavam inline em root.tsx e _layout.tsx — havia risco de drift entre <html lang="pt-BR"> e Content-Language: pt. Cobertura: oito códigos ativos (pt-br, pt, en, es, es-mx, es-cl, en-ng, fi-fi) com fallback pra pt-BR.
  • useCasinoNomenclature agora é guiado por i18n, não mais por regex country-aware. O hook colapsa qualquer string vinda do BFF (campo casinoNomenclature) num enum de 2 valores ("casino" | "games") e resolve o substantivo traduzido via t("layout:sidebar.casino") / t("layout:sidebar.nomenclature_games"). Adicionar país novo virou uma linha em cada layout.json do core — zero mudança no front. Dependência de useBrand().features.country?.code removida.
  • Doc operacional fora-do-código: o sinal mais forte de geo-targeting pra gccTLDs (.com, .bet) continua sendo o Google Search Console → International Targeting → Country, que é manual. Walkthrough escrito em docs/superpowers/specs/2026-05-09-google-search-console-geo-targeting.md pro time de SEO/marketing.

Changelog — 08/05/2026

Cadastro — validações client-side mais rigorosas

  • Limite etário no cadastro (PR #654) — novo app/utils/age.ts calcula idade contra now e bloqueia data de nascimento fora da faixa 18–99 anos direto no RegisterModal antes do POST. Antes o front aceitava qualquer data válida e só o BFF reclamava (round-trip + UX ruim). Erros aparecem inline com chaves i18n já existentes (auth.errors.too_young / too_old), e o parseRegisterFormData em app/modules/register/flow.ts ganhou o mesmo guard pra blindar o pipeline FormData → payload.
  • Validação de telefone contra máscaras do país (PR #683) — o RegisterModal agora deriva os digit lengths válidos a partir de availableMasks (ex: BR → [10, 11]) e marca o campo como INVALID_PHONE no blur quando a contagem não bate. Isso afeta automaticamente 7k-bet-br, cassino-bet-br, vera-bet-br (e qualquer brand futuro) sem mudança no override — a validação é mask-derived. Erro limpa assim que o user atinge um length válido; não dispara shake (esse fica reservado pra erros de submit), só border + mensagem inline.

Changelog - 07/05/2026

Segurança — remoção total de debug do bundle de produção

  • Toggle ?EnableDebug=1 removido em prod. O parâmetro de URL e a chave sessionStorage["debug_enabled"] foram completamente eliminados — não existe mais runtime gate que possa ser ligado por query param, cookie ou storage. O motivador foi um leak no /api/games/start: o endpoint passava debug:true ao ApiClient e ecoava _debug.requestInfo na response, expondo o header cf-worker-key que o Worker usa pra autenticar com o BFF — qualquer um clicando em "Play" em qualquer brand de prod conseguia ler do DevTools. Endpoint corrigido com 6 testes de regressão que travam o contrato (debug:true proibido, _debug ausente em sucesso e erro).
  • Painéis DevApiDebug/DevApiExplorer agora gated por import.meta.env.DEV. Novo wrapper DevPanel com React.lazy condicional — em prod, o dynamic import('./DevApiDebug') vira inalcançável e o Vite remove o chunk inteiro do build. Consumers (home, favorites, game detail, wallet, history) trocaram <DevApiDebug> por <DevPanel> envolto em {import.meta.env.DEV && (...)}, garantindo que loader _debug data nunca serializa no SSR stream de prod.
  • Rotas /api/dev/*, /dev/ftd-cashback, /debug e /debug/analytics só existem em dev build. O registro em app/router/routes.ts é gated por process.env.NODE_ENV !== 'production' — em prod build esses arquivos são tree-shaken por completo (server e client). Eliminados também app/utils/debug.server.ts (com isDebugRequest/?EnableDebug) e a doc docs/enable-debug/overview.md.
  • CI guard pnpm bundle:guard falha se strings proibidas vazarem para o bundle de prod. Novo script scripts/check-prod-bundle.mjs faz grep no build/ após pnpm build e detecta DevApiDebug, DevApiExplorer, activateDebugFromUrl, isDebugActive, EnableDebug, debug_enabled, validation_debug, /api/dev/, /dev/ftd-cashback, debug/analytics e cf-worker-key (no client bundle apenas). Wired via pnpm build:check (= build + bundle:guard). Adicionar nova string secret-adjacent exige atualizar FORBIDDEN_STRINGS no script.
  • Bumps no core que sustentam a remoção: @cactus-agents/api-client 0.12.0 (redação regex-based de headers *-key/*-token/*-secret/cf-*/x-api-* no requestInfo em modo debug) e @cactus-agents/utils 1.0.0 major (remove os exports activateDebugFromUrl e isDebugActive — não existe mais API pública pra ligar o toggle em runtime). Defense-in-depth: console.* em ftd-cashback.client.ts, smartico-checkin.client.ts e useOnFirstScrollIntoView.ts agora também passam por import.meta.env.DEV, então mesmo se um caller acidentalmente passar debug:true em prod o output some no build.

Changelog - 06/05/2026

Pós-registro & Auth — fluxo regulatório e dedup de bursts

  • Novo hook useAfterRegisterFlow (app/hooks/useAfterRegisterFlow.ts) orquestra o pós-registro: abre o modal de depósito uma vez, logo após o cadastro, bypassando o throttle de 12h do useAutoDepositModal. Replica o handleOpenRegisterStrategy → openModal('deposit') do handleSuccessRegister legado em Vue. Suporta o ramo regulatório (espera o LimitsStep do ValidationBlockerOverlay resolver antes de abrir o depósito) e suprime quando há ?deposit=<campaign> na URL, conta restrita ou featuresConfig.autoDepositAfterRegister === false.
  • Store app/store/onboarding.ts (Zustand, não persistida) carrega o sinal justRegistered + lastPrudentialLimitActive entre o sucesso do RegisterModal e o próximo render do DefaultLayout. Reset automático no logout.
  • Forward de user_prudential_limit_active no payload de signup (/bff/register-simplified, /auth/register): sinaliza ao BFF qual botão regulatório o usuário clicou ("Quero definir meus limites agora" vs "Continuar sem limites"). Nova feature flag autoDepositAfterRegister propagada nos 13 overrides de brand.
  • Dedup cross-request de /auth/user-profile em app/services/auth.server.ts: cache token-scoped com TTL de 10s + cap de 100 entradas (chave = SHA-256 hex do token, evita expor token cru em dumps de memória). Resolve o burst pós-login onde POST /api/auth/login, useAuthProfileSync → GET /api/auth/profile e revalidator.revalidate() → _layout loader pegavam três Requests distintos com o mesmo token — o WeakMap<Request> antigo não conseguia deduplicar.
  • /api/auth/profile agora consome getAuthForRequest em vez de chamar createAuthFromClient direto, herdando automaticamente o cache acima. PRs touch-up em 20+ componentes (UserSummaryHeader, ValidationContextGate, GameIframe, payments, protection, validation steps) que liam useProfile() ad-hoc.

Changelog - 05/05/2026

Analytics — Mixpanel Deposit + AppsFlyer S2S

  • Novo evento Mixpanel Deposit Requested (useAnalytics.trackDepositRequested) disparado quando /wallet/add-credit retorna 200 OK. Espelha o legado DefaultLayout.vue das brands 7k/cassino/vera (props Amount em unidades inteiras + DepositMethod). Continua complementar ao evento de funil já existente (pix_generated no GTM e PixGenerated no Facebook).
  • Bugfix crítico nos eventos S2S do AppsFlyer (sendRegister, sendFTD, sendRebill). Os três estavam dentro do guard if (!shouldLoadTrackers) return do useAnalytics, mas o gate é o inverso do gate do AppsFlyer (shouldLoadTrackers = !isAppMode && !isBot, useAppsFlyer.ready = enabled && isAppMode) — resultado: nenhuma das chamadas server-to-server disparava nem na web (gate externo bloqueia) nem dentro do APK (early return do gate externo cortava o caminho). Agora as três chamadas são disparadas antes do guard e o S2S funciona em produção. Coberto por useAppsFlyer.test.ts (202 linhas) e useAppMode.test.ts (72 linhas).
  • useAppMode agora reconhece ?is_twa=true além de ?app=true. Os APKs em produção da família 7k/cassino/vera (publicados antes do rename do query param) continuam injetando o legado em cada landing — sem isso isAppMode ficava false pra users do app e desativava todo o pipeline de tracking S2S silenciosamente.
  • Valor do deposit GTM corrigido pra unidades inteiras (amountCents / 100) ao invés de cents puros — paridade com o legado e com a expectativa dos dashboards de BI.

Changelog - 04/05/2026

Jornada FTD — Refatoração Completa e Novas Brands

  • D0 e D1 desacoplados — antes, ativar o template vera-legacy do D0 (cashback) ligava implicitamente o modal de anúncio do D1 (check-in). Agora cada brand habilita as três etapas independentemente: ftdCashback.enabled, ftdCheckin.enabled e a flag nova ftdCheckin.announcement.enabled. Quatro combinações possíveis — só D0, só D1, ambos ou nenhum.
  • Copy e assets configuráveis por brand no template vera-legacyFtdCashbackConfig ganhou copy.{firstBonusToastTitle,cashbackModalTitle,cashbackModalDescriptionHtml,cashbackModalCtaText} + templateAssets.{firstBonusToastImage,cashbackModalImage}. FtdCheckinConfig ganhou announcement.copy.{title,descriptionHtml,ctaText} + templateAssets.image. Todos opcionais — fallback é o copy PT-BR + CDN da Vera (zero regressão).
  • 16 tokens de tema novos para o template e o toastftd-offer.template-{shell-bg,title-text,title-shadow,image-bg-from,image-bg-to,highlight-bg,highlight-border,highlight-text,cta-bg,cta-text} e ftd-offer.toast-{bg-from,bg-to,border,icon-bg-from,icon-bg-to,icon-border}. FtdOfferModalTemplate e FtdOfferInGameToast agora leem essas cores via CSS custom properties inline (Tailwind JIT não consegue gerar classes a partir de tokens dinâmicos).
  • 7k-bet-br ativa a jornada completa — D0 cashback, D1 check-in (com modal de anúncio "Garanta sua diversão!") e oferta-relâmpago pré-FTD, todos via template unificado. Quatro assets brand-specific da CDN do 7k substituem os fallbacks da Vera (toast, modal cashback, anúncio D1 e modal de oferta).
  • cassino-bet-br ativa a jornada completa — paleta + assets próprios, brand_id: 2 no Dark Verifier/Freedom (paridade com useFtdCashback.ts:52 do legado), kill-switches feFtdD0Cassino / feFtdD1Cassino, tabela bonusTiers de 35 níveis portada verbatim do legado (R$ 5 → R$ 3000+, cap em R$ 800), autoDepositModal abre o drawer de depósito automaticamente no login quando saldo ≤ R$ 0,10. Inclui lista de jogos elegíveis em cashback/eligible-games.ts.
  • cl-bet7k-com ativa D0 + D1 com identidade chilenabrandId: 4, kill-switch feFtdD07KCl (compartilhado D0/D1), tabela bonusTiers de 11 níveis em CLP (CLP 850 → CLP 510 000, cap CLP 136 000), 6995 IDs elegíveis portados 1:1 do legado, copy em espanhol chileno ("¡Sigue jugando para ganar un cashback!"). Moeda renderiza sem decimais ($50.000) via useFormatMoney() + CountryConfig.displayDecimalDigits. D1 começa desabilitado no primeiro rollout.
  • STT 2 (saldo bônus) com kill-switch remotosaldoBonus.featureFlags?: { legacy?, configcat? } permite desligar a STT 2 instantaneamente via ConfigCat ou FF legado sem deploy. O hook useFtdCashbackFlow computa saldoBonusRemoteKillSwitchPass (closed-by-default enquanto a API está em voo, mesma semântica do D0/D1). Cassino declara feFtdSaldoBonus + feFtdSaldoBonusCassino.

Changelog — 03/05/2026

CASSINO_MODE — front_custom removido (breaking change)

  • @cactus-agents/games 2.0.0 — CASSINO_MODE=front_custom deixou de existir. Antes desta data havia 3 modes (legacy, api_new, front_custom); agora são apenas 2 (legacy, api_new). O front_custom era usado só pelo sandbox interno stage1 — stage1 migrou pra legacy e nenhuma brand de cliente consumia o mode. Em vez de manter código morto como surface area pública, o package removeu de vez.
  • Exports públicos removidos: createCustomCategoriesGamesFromClient, createCustomCategoriesGamesService, CustomCategoriesGamesOptions, CustomCategoryDef, CustomCategoriesConfig, CustomCategorySource, filterExplicitCategory, resolveCategoryGames, customCategoryDefToRaw. Tipo LegacyHomeRowConfig.type deixou de aceitar "custom-category".
  • Sobrevive intacto: HomeRow.type runtime ainda inclui "custom-category" / "custom-section" — esses são emitidos pelo BFF page pipeline em legacy e api_new e continuam funcionando. Curadoria por overlay (categories.personalize.ts) também continua válida nos dois modes.
  • Refactor interno: src/custom-categories.ts virou src/stats-ordering.ts; orderByStats, CustomCategoryOrderBy, DisplayPriority, StatDisplayKey continuam exportados do root do package (API pública preservada).
  • Documentação atualizada em conjunto: docs-internal/template/env-vars.md, docs-internal/template/games.md, docs-internal/forking/override-files.md, docs-external/customization/games.md e docs-external/deployment/environment.md reescritos para descrever apenas os 2 modes restantes. CASSINO_FRONT_CUSTOM_BASE removido das tabelas de env vars; regra de "paridade obrigatória" pra .new.ts/.custom.ts foi droppada (não existem mais). Guia de personalização aponta agora pra categories.personalize.ts em vez de categories.custom.ts.

Changelog - 02/05/2026

Dia pesado: 30 PRs no front-web-base + 4 PRs no front-cactus-core. Destaques: revamp completo da gamificação 7k (cards de missão/torneio + modal), portabilidade massiva da personalização da vera-bet-br (paridade visual com legado), fluxo FTD completo da Vera (oferta, cashback, autoDeposit, check-in), polimentos visuais da cassino-bet-br e correções de Proteção de Conta.

Gamificação — Revamp 7k

  • Cards de missão e torneio completamente revisados nas variantes classic e stacked para 7k-bet-br/fi-7k-bet/ng-7k-bet, com novos visuais, badges de status e progresso refinado.
  • Modal de detalhe da missão (MissionDetailModal) ganhou layout novo com seção de prêmios, regras de elegibilidade e CTA de inscrição/reivindicação.
  • Tela de torneio (tournaments.$id.tsx) reescrita: novo TournamentHero, RegistrationCTA com estados expandidos, TournamentStatsCarousel e tournament-internals consolidados.
  • LevelsSection ajustada e página vip/levels.tsx reorganizada para refletir a hierarquia de níveis VIP do 7k.
  • Smartico Initializer atualizado para integrar com o novo fluxo de claim e pending_claim exposto pelo SDK.
  • Configs de brand (gamification.ts, widgets/mission-card.ts, widgets/tournament-card.ts) propagadas nos três overrides 7k para refletir o novo visual.

Changelog - 01/05/2026

FTD Onboarding — três fluxos novos consolidados (stage-ftd)

Maior entregável do dia. A branch stage-ftd aterrissou três fluxos completos de retenção/conversão D0, todos brand-configuráveis e cobertos por testes:

  • FTD Offer ("Oferta Relâmpago") — modal + floating widget + story thumb com Quick Deposit embutido. Componentes em app/components/ftd-offer/ (Provider, Modal, FloatingWidget, StoryThumb), storage isolado por marca em ftd-offer-storage.ts e analytics em ftd-offer-analytics.ts.
  • FTD Cashback — fluxo D0 com modal de oferta inicial (FtdCashbackFirstModal), modal de prêmio (FtdCashbackPrizeModal), Provider, dev panel, scaffolding de tiers (ftd-cashback-tiers.ts) e persistência local (ftd-cashback-storage.ts). Testes cobrem storage e cálculo de tiers.
  • FTD Check-in — daily check-in com mock fixture, logs diagnósticos, special offers, label "done today" e kill switch via feature flag remota da brand 7k (feat/ftd-checkin-7k-feature-flags). Componentes Checkin, CheckinTrigger, CheckinStoreOffers.
  • Loop de reabertura corrigidouseFtdCashbackFlow ganha guard pra não reabrir o first-modal logo depois do close (PR #485, fix/ftd-cashback-first-modal-loop).