Changelog - 12/07/2026
Semana de 06/07 a 12/07, na branch main-7k do front-web-base. É a semana em que o programa de performance/streaming da plataforma sai do papel: 93 commits filtrados, a maioria concentrada num único eixo.
Semana 06–12/07 — streaming SSR e clientLoader em toda a aplicação
- App-shell streaming SSR, começando pela home (
90d1dcfb4): oentry.serverpassa a transmitir o stream no shell-ready viaTransformStream— head processado no primeiro flush (tailwind inline, strip demodulepreloadcom keep-list derivada do manifest, strip de preloads de imagem não-LCP) e body em passthrough com tail-carry. Antes o stream era bufferizado até o fim pra rodar regex no HTML completo, ou seja, o primeiro byte esperava todo o conteúdo. Junto vem o piloto de deferred data na home:catalog,gameStatsCache,topWinselastWinssaem do caminho crítico e streamam viaSuspense/<Await>no documento SSR e via turbo-stream nas navegações.data, comSectionSkeletonno lugar. Continuam awaitedhomeRows,brand,authe a page description (meta/SEO/LCP). Cada perna deferida ganhou.catch(() => vazio), então rejeição degrada pra row vazia em vez de 500 na home inteira.streamTimeoutde 10s com abort combinado. - Estendido pra game page, hubs e navegação (
85a108c2d): o loader da página de jogo cai de 3 gates sequenciais pra 2 awaits (gameebrand, o mínimo pro meta/SEO), deferindostats,winners,related,providers,voteStatee as seções showcase. OgetStatsRaw, um endpoint do BFF não cacheado que só alimentava o DevPanel, foi movido pro gate de dev — produção deixa de pagar por ele. Nos hubs de cassino e ao-vivo,gameStatsCachee os wins passam a deferidos no caminho de rows. E em_layout.shouldRevalidate, troca de pathname deixa de revalidar o layout, o que remove a chamada de auth por usuário — transatlântica — do caminho crítico de toda navegação SPA logada; login e logout seguem revalidando viarevalidate()explícito. Skeletons CLS-zero novos:WinsCombinedSkeleton(o fallback anterior causava ~137px de shift no desktop),WinsMarqueeRowSkeleton,PaidTodaySkeleton,ListWinsSkeletoneGameRowSkeletonderivado dogameCardConfig, com widths corretos por breakpoint e por brand. clientLoaderdeixa a navegação game → game instantânea (1812d8b07): a navegação SPA pra/cassino/jogar/:provider/:gameesperava o.datainicial, que aguarda ogetDetail(round-trip de 0,5-1,5s em cache miss), travando a troca de rota inteira. OclientLoaderdevolve oloaderDatana hora —serverLoader()disparado semawait, cada campo encadeado como promise — então a URL troca instantaneamente e o conteúdo preenche por streaming.game,frontDescription,seoefaqEntryforam combinados num único boundary deAwaitValuecujo fallback reserva a geometria exata do conteúdo real, eslug/showcaseficam síncronos porque são deriváveis deparams/featuresConfigsem round-trip.5c24a1354acrescenta o skeleton pixel-perfect e o seed otimista do card clicado.- Padrão propagado pro resto das rotas —
b3e8c151d(hubs cassino e ao-vivo),bb0b2c012(categorias e provedores),f241a9145(home, incluindo seed otimista a partir dohomeRowsjá cacheado na store e o bundle de favoritos/recentes virando um únicofavoritesBundlestreamado),761088aa6(VIP e gamificação) ee6fe562f1(conteúdo WP, legal e busca, comlegalTermsdeferido).cc237adfccentraliza o preview de dev-skeleton num módulo único e removível;49ef0d808ed8401122bcorrigem imports.serverque vazaram pro caminho client. - Dois defeitos reais achados e corrigidos no próprio programa.
885f0e92c: o encoder de single-fetch do React Router corre toda promise deferida contra ostreamTimeout, e perna pendente virava frameE:["Unexpected Server Error"]dentro de um body 200, por fora do.catchdo loader; pior,errorElement={null}é falsy, então o<Await>re-lançava proErrorBoundaryda rota em vez de suprimir (a tela renderizava e depois virava erro); e o worker cacheava o.data200 com o frame de erro embutido por 60s, servindo o mesmo erro em HITs subsequentes. Fix:errorElementtruthy (fragment vazio) emAwaitValue/AwaitWinse na home.afc20a73d, resultado de revisão adversarial: obuildSpaShellbufferizava o stream inteiro pro usuário logado (TTFB = render completo, ~11s no pior caso) e devolvia um shell morto;Set-Cookiede tracking bloqueava o cache compartilhado pra todo visitante anônimo frio; o cache autenticado não tinha guard contra payload envenenado ou documento truncado; e a home resuspendia pra skeleton genérico em todo login/logout. ObuildSpaShellfoi removido e o autenticado em rota pública passa a receber a mesma stream real do anônimo. - Navegação e stats estáveis na página de jogo (
dfad863b1):getStatsegetVoteCountganham timeout fail-fast de 1200ms, com a promise original registrada nowaitUntil— assim o warming completa em background mesmo em cold-miss, o que o timeout sozinho mataria. As duas ondas sequenciais dePromise.alldo loader foram fundidas, removendo um round-trip serial.
Zero recursos render-blocking
- Tailwind inline no HTML em produção (
7cc1f51e2): o<link>do Tailwind (32 KB gzip, 88% não usado na home por ser SPA multi-rota) era o último recurso render-blocking do site. Em produção o CSS inteiro vai inline no fim do<head>viaprocessHtml— cerca de +25 KB brotli num HTML que já é edge-cached e que a navegação SPA nunca repaga. Medido no preview: HTML gzip de 210 pra 199 KB já incluindo o CSS. Em dev oroot.tsxmantém o<link>pra preservar o hot-swap de CSS do Vite. - Fontes de brand inline (
3718b9bb6): orootlinkava~/styles/brand-fonts.css?urlpra toda brand, mas o arquivo do base é vazio de propósito — resultado: um request render-blocking de 1 byte em todas as brands, que o PSI flagava como bloqueio de renderização num CSS vazio. Agora o CSS é importado com?inlinee vai num<style>só quando tem conteúdo. Brands sem fonte própria não pagam nada; adonald-bet-brinlina ~1 KB de@font-facee também deixa de pagar o request. - Hidratação direta e
modulepreloadde boot-critical (82c94bfd1): o boot-loader via Blob (gateload→requestIdleCallbackde 5s) estava morto em produção — o regex exigia começar comimport * as, mas o React Router 7 emite o import do manifest primeiro, então nunca casava. Removido com nota histórica. O gate real era orequestIdleCallbackde 2s doentry.client, e agora hidrata direto. O strip demodulepreloadpassa a preservar os ~5 chunks que o script inline de hidratação importa, então a rede corre durante o parse em vez de só depois de ~880 KB, e preloads de imagem commedia=sobrevivem ao strip. - Banner de cookies SSR-first com guard pré-paint (
a2bd19eae): o<p>do banner era o elemento LCP da home mobile no lab (render delay de 4,7s no PSI). Sai olazy()+ client-only +setTimeout(2000); entra render no HTML SSR com script inline síncrono no<head>que setadata-consent-givenno<html>antes do primeiro paint — CSS esconde pra quem já consentiu, sem flash, sem tocar o cache do edge e sem hydration mismatch. - Decorações como SVG inline e ligas de cor fixa como
<img>lazy —e24a5b172: odecoration.svgdo corner-diagonal era o elemento LCP de campo dominante da home 7k (~300 mil pageviews a ~5,7s no RUM) e oemblem.svgdos CTAs também aparecia como LCP;<svg>inline não é candidato a LCP por spec, então os dois viraram um kind novoinline-svgcom asset por brand importado em build-time (bônus: −2 requests por página).8c839f6b2ataca o outro lado: os SVGs inline eram 44% do gzip do HTML da home (232 svgs, 311 KB raw, 46% dos nós do DOM). As 7 ilustrações de liga de cor fixa (~90 KB inline, −151 nós) saem do HTML SSR e viram<img width=52 height=52 loading=lazy>de assets estáticos otimizados por svgo (91 → 43 KB); as 22 ilustrações temáticas, que usamcurrentColor, seguem inline. CampoiconSrcaditivo e opcional, então outras brands ficam intactas.
INP, CLS, TBT e payload
- INP (
27562d6a8): o CTA "Jogar" era o elemento INP dominante do RUM (~1,4s) — passa a pintar o feedback barato, ceder o main thread com double-rAF e só então montar o overlay fullscreen, e obackdrop-blursaiu da top bar. Chunks de auth ganham prefetch no idle, matando o "tap morto" no primeiro toque em Entrar/Cadastrar. No registro,started_registeré deferido pra fora do task do keystroke (o#register-emailtinha INP de ~600ms no RUM). - CLS (
96e1f1b9b): no sheet de auth (CLS 0,345 no RUM), oavoidKeyboardfoi desligado — o padding do teclado virtual re-layoutava o form a cada focus/blur — e o Turnstile ganhou espaço reservado (300×65) antes do mount assíncrono.bodye shells trocammin-h-dvhporsvh, porque o collapse da URL bar re-layoutava a página inteira (CLS 1,018 no body da CL). Em/esportes, o spinner passa a ter a mesma geometria do iframe final. Em/usuario/*, skeleton com geometria real substitui o spinner (CLS 0,124). - TBT (
71857c25e): os 4 publishers de CSS vars (--header-h,--topbar-visible-h,--mobile-nav-content-he o offset do consent) entrelaçavam leitura de geometria com escrita de estilo no flush pós-hidratação — cada escrita invalida layout global, porque as vars alimentamcalc()em toda a árvore, e a leitura seguinte força reflow full-document (~205ms somados no PSI). Olayout-vars-schedulernovo agenda tudo numrAF: todas as leituras primeiro, depois todas as escritas, cerca de 1 reflow. - Imagens sem dimensão e animação não-composited (
619f0ba57): 15 selos do footer semwidth/height(auditoria "unsized images" do PSI) ganham dimensões reais edecoding=async; oautorizado.pngda 7k cai de 500×142/7,2 KB pra 400×114 paletado/3,2 KB; e opulseRed, que animavabox-shadowem loop acima da dobra (paint na main thread a cada frame), vira um ping composited comtransform+opacityemotion-reduce.77d3032eecompleta com dimensões no logo e nos sponsors do footer e variantes sized nos ícones de CDN;b5569c007corrige quality e variante mobile do preload do banner. - Payload da home cortado 43% (
5a02521b4): o loader do_layoutembarcava os 5 documentos legais completos (~170 KB raw, só o T&C são 94 KB de HTML) em toda página, mas o footer só usarouteetitle— passa a ir a versão slim, com o conteúdo integral ficando na rota/pagina/:slug. Obrand.settings.terms(~39 KB, zero consumidores no app) é zerado com clone raso, porque o objeto de brand é cacheado server-side e não pode ser mutado.topWins/lastWinsvêm com 100 do BFF e são truncados em 30. Turbo-stream da home medido: 345,6 KB → 195,3 KB. - Modal base fora do grafo eager (
5d6534ef6):CheckinTriggereFtdOfferProviderimportavam o Modal base estaticamente (react-modal-sheet→framer-motion, chunk de 156 KB), então ele entrava no grafo eager de_layout/_indexem toda página — o "Modal 71% unused" do PSI. VirouReact.lazygateado na primeira abertura, com prefetch nopointerenter/focus.3cbe3c674fecha a última cadeia eager pro Modal base, gateando oMatchXRankingModal, e ainda para de buscar as feature flags de check-in pra visitante anônimo (2 dos 3 requests de flag no PSI eram RTT puro desperdiçado).2194c5294adiciona prefetch no idle doDepositModal(29,7 KB gz) e doValidationStepsModal(20,5 KB gz), os dois hot paths que pagavam RTT + download + parse no clique. - SDK da rogue fora do caminho crítico de toda página (
0ab3c67c0): o matcherincludes("@cactus-agents/sports")casava por substring com@cactus-agents/sports-rogue(SDK completo, dist de 844 KB com side-effects top-level) e fundia tudo emvendor-cts-sports, que o layout importa eager só pra usar 12 KB de helpers. Resultado: 770 KB / 213 KB gz avaliados em toda página — inclusive em brands que nem usam a rogue. Osports-roguesai do grupo nomeado e o rolldown code-splitta pelo grafo natural; como é lazy-only, cai num chunk async. Verificado no build: SDK de 558 KB fora dos imports do_layout, React novendor-framework, zero betslip em chunk eager. - Acessibilidade (
78807398d): o PSI deu 82 em acessibilidade no fluxo de registro, e o commit fecha os apontamentos —aria-labeldinâmico nos 6 toggles de mostrar/ocultar senha com hit area de 32px,aria-labelno backdrop doreact-modal-sheet(a lib renderiza<button>quando háonTap) e dois ajustes de contraste token-safe (3,75:1 → 6,9:1 e ~2,6:1 → 5,0:1).
Cache, worker e TTFB
- Documento e
.datana mesma janela de frescor (2fac84d4d): três relógios divergentes serviam snapshots de idades diferentes pro mesmo usuário entre o primeiro paint (documento) e a volta por navegação SPA (.data) — a causa-raiz do ranking "Pagou muito" trocando valores. Quatro correções: a resposta.datasaía semCache-Control(só a cópia interna levava o header, porque o handler de single-fetch não passa peloentry.server), então a Cloudflare nunca cacheava.dataenquanto cacheava o doc;DEFAULT_TTLSde.dataera 30/30 contra 60/120 do doc, agora alinhados;resolveSSRCacheTTLsrecalculavadataSWR = dataSMaxAge, sem o ×2, o que dava 60/60 efetivo mesmo com o default corrigido — a regra passa a serSWR = 2 × s-maxage; eKV_SSR_TTLcai de 3600 pra 180, porque o fallback de KV repopula o Cache API sem re-renderizar, então um POP frio podia servir um snapshot de até 1 hora.6eb02f314fecha o resíduo com um session-pin:getSessionPinnedGamescongela a primeira lista não-vazia da sessão SPA, porque ototalPaidé janela móvel de 24h e oscila nos dois sentidos entre fotos — dentro da sessão o ranking fica estável, e full reload pega foto nova.1dd77e927é o hotfix de um 500 causado por esse session-pin ter nascido.client(undefined no SSR). - Camada
api:removida do worker do front (3af6997b5): o cache de resposta crua do BFF pra/api/*(API_CACHE_RULES,getApiCacheRule,handleApiCache,kvApiKey) foi removido por completo, sem código morto. Motivo: todo/api/*proxia pro BFF via service binding e ofront-service-apijá é a única fonte de cache raw-BFF, com TTLs emfront-ops/config/cache/service-api-defaults.yml. Cachear no front era redundante e causava staleness por conflito de TTL — a camada fixava 3600s enquanto o service-api usa TTLs menores e intencionais por endpoint. O único path que não passava pelo binding,/api/cactus-sportbook/*, agora também vai por ele. O worker do front deixa de emitirX-Cacheem/api/*. - Cache de shell morto removido — gravava dado de usuário em cache compartilhado (
22ceda50c): obuildSpaShelltinha um cache cujas leituras estavam desligadas desde 05/05, mas cujas escritas seguiam vivas: cada request de usuário logado gravava ~335 KB com oloaderDatadele (auth, token,smarticoHash, favoritos) numa chave sem dimensão de usuário, compartilhada entre todos os logados, sem ninguém ler. Era munição carregada porque o sanitizer que deveria zerar auth é no-op no React Router 7.14 — oloaderDatanão vive no objeto que o regex casa, e sim no turbo-stream, que passa intacto. Religar as leituras serviria o perfil de um usuário a outro. Como as leituras já estavam off, remover não muda comportamento: só elimina o dado at-rest e umcaches.match+put+kvPutde ~335 KB por request logado. - Warmer de cache via cron (
5af3d7649): kill-switchCACHE_WARMER_ENABLED(default off, sobe inerte). Quando ligado, o handlerscheduled()re-renderiza o SSR anônimo dos paths quentes em modo force-fresh e regrava o snapshot KV global, rodando in-process (chamawithSSRCachee o request handler direto, sem fetch externo) — então não passa por Cloudflare Access nem pelo geo-block regulatório. Cron a cada 2 minutos, 12 testes novos. Opsf821bbamove a config do warmer do dashboard da Cloudflare profront-ops(cache_warmer.{enabled,paths,origin}injetados no deploy como oBUILD_IDe oCACHE_GENERATION), ligado na stage do 7k e comentado em produção;e8a3761ajusta paths.f4ab0f017corrige uma deriva de semântica introduzida pelo merge do streaming: o labelX-Cache: WARM-STOREDvinha do nível de header, então um warm-render truncado ainda era 200 e era contado como aquecido mesmo quando o guard de completude dropava o body — agorabufferAndCacheIfSafedevolve boolean e o label reflete se gravou de fato. - Fase 1 e fase 2 do doc público compartilhado pro logado —
590253fec(fase 1) torna o auth 100% reidratável no client:/api/auth/profilepassa a devolversmarticoHash, entra uma storeapp/store/smartico.ts(zustand puro, SSR-safe) e oSmarticoInitializerusaprop ?? store. Na mesma leva,fetchProfileFromApivira resultado discriminado (ok/unauthorized/error) — antes colapsava 401/440 e falha de rede/502 no mesmonull, o que deslogaria o usuário num soluço de rede — e, no casounauthorized, além de limparis_authenticatedpassa a apagar ojwt_tokenHttpOnly, porque antes oisCookieAuthdo worker seguia classificando como logado e toda navegação re-disparava chamadas 401 em loop.24ecb4ad7(fase 2) adiciona o kill-switchSHARED_PUBLIC_DOC_FOR_AUTH, default off: quando ligado, um GET de usuário logado em rota pública cacheável tem os cookies de auth removidos no entry do worker, caindo no bucket compartilhado e renderizando neutro. O strip reusa o conjunto canônico e completo de cookies de~/utils/cookie.server— como oApiClientreencaminha o headerCookieinteiro pro BFF, remover só um subconjunto vazaria dado per-user pro cache compartilhado (postmortem de vazamento de saldo de 29/04). 31 testes novos. Server-Timingno caminho do primeiro byte (1dbe2233c): novo utilwithServerTimingacumula spans noloadContexte oentry.serveremite o header, visível no DevTools e legível por RUM (spans debrand/auth/legalno_layoutehome/gbase/topwins/lastwinsno_index). Junto, o_layoutdeixa de esperarbrand→auth→legalTermsem sequência: com o BFF emeu-central-1e o worker na edge, cada perna sequencial custava até um RTT transatlântico, e as três são independentes, então viraramPromise.all. Amostra de cache frio: auth 3ms, legal 58ms, brand 62ms contra 1,1-2,3s dos catálogos de jogos — o header já aponta onde o TTFB de cache-miss realmente mora.5ee474a0epropaga o header pro shell de usuário logado, que antes o descartava.- Scripts de marketing na estratégia
engagement(4abdac92c): lane opt-in por brand (marketingScriptsStrategy: 'engagement') que move o download dos scripts conversion-critical (gtm.js, fbevents, Taboola, Kwai, Webtrends, Floodlight) pra primeira interação do visitante ou um deadline configurável (default 5s), o que vier primeiro — sem trigger de idle, porquerequestIdleCallbackdispara quase imediato em página ociosa. O que separa isto do gating de junho, que foi removido por quebrar o marketing, é que as filas nascem síncronas no mount:primeGTMQueue(comdataLayere Consent Mode v2 default à frente da fila) eprimeFBPixelStub(stub dofbq+fbq('init'), sem o qualtrackFBEventno-opa); Kwai e Taboola já são queue-by-design. Todo evento disparado antes do download é enfileirado e processado em ordem, com zero descarte — a perda residual é o visitante que sai antes do deadline sem interagir. Sem branch de bot: o CrUX mede a mesma página que o Lighthouse. Racional: os scripts do container somam ~700ms de long tasks na janela de load. 7k opta no teste com 5000ms; o default global segueeager.
Segurança
API_BASE_URLnunca mais chega ao client (c7dd1513f): o host final do BFF estava exposto ao browser e usado em requests cross-origin diretas. A auditoria confirmou três vetores alcançáveis pelo client —API_BASE_URLfazia parte doClientEnve portanto era serializado noloaderData/HTML;user.clienteSocialAccountsSectionbatiam cross-origin no BFF do próprio browser; eregister/validate-phonedevolvia a URL absoluta no body da response (debug:true, com camposurl/requestInfo/raw), que foi o vazamento observado em produção. Correções:API_BASE_URLremovido doClientEnvcom aviso pra nunca reintroduzir e os loaders deixando de serializá-lo; rotas same-origin novasGET /api/user/social-accountseDELETE /api/user/social-accounts/:id;SocialAccountsSectionmigrado pra fetch same-origin; envelope devalidate-phonereduzido a{ ok, message }; e o interceptor de 401 restrito a same-origin, comconfigureApiFetchInterceptoretrustedOriginsremovidos.- Login social gateado enquanto desativado (
223e7f183): o loader de/api/auth/social/:providerfaziaredirect(302)pro BFF, e o headerLocationexpunha o host final ao browser.disableSocialAuthétrueem todas as brands, então o caminho é inalcançável pela UI hoje, mas o vazamento era latente — loader e action passam a devolver 404 (social_auth_disabled). Registrado no commit que, antes de religar social, o start do OAuth precisa ser brokerado same-origin. - Complemento no core (
b343bca): oApiClientfaziaconsole.logincondicional de método, URL e payload emsend()— no client isso imprimia a URL absoluta do BFF e o payload, que em auth e registro inclui e-mail, senha, telefone e documento. Os logs foram removidos (server-side userequestInfo, já gated porconfig.debuge com headers redigidos). No mesmo commit,proxyRaw()deixa de fazerfetch()global direto no BFF e passa pelo service bindingAPI_SERVICE, como osend()— antes o tráfego de proxy cru escapava do cache raw-BFF dofront-service-api.
Ondas 1 a 3 do refino de UX do 7k
- Onda 1 — limpeza de UX (
5a033ffdf): primeira onda do backlog de refino da 7k, com todas as mudanças gateadas por flags novas ligadas só no override da 7k, então as outras brands ficam byte-idênticas. Flags:showGameCardStats(7kfalse— oculta a pill "online agora" e o balão de estatística nos cards comuns, preservando o valor nos cards compayoutAmount),clientStatsReorder(7kfalse— desliga o reordenamento client-side que causava "cards pulando"),gameCtaGuestPlaysOpensLogin,gameVotesTotalOnly,hideUserSummaryActionseshowHeaderPromotionsGift. - Onda 2 — página de jogo no mobile (
b6fbf47db): diferente da onda 1, aqui são correções de responsividade universais, desenhadas pra zero regressão em larguras comuns. O breadcrumb do item atual truncava com elipse no mobile, ficando ilegível — agora a trilha rola na horizontal com o scroll fixado no fim (item atual visível primeiro), etruncatevirouwhitespace-nowrap. O componente é compartilhado por cerca de 40 rotas. - Onda 3 — atividade recente na carteira (
5364ec3a8): novoWalletRecentActivityreusandouseTransactionseTransactionCardpra mostrar os 10 lançamentos mais recentes direto na página da carteira, com link pro extrato completo, gateado porshowWalletRecentActivity(só 7k). Quando o bloco aparece, o card "procura o extrato?" é ocultado pra não duplicar o link. Suportado pelo core9308848, que publicawallet.pending_notice_titleepending_notice_descnos 7 locales.
Core / SDK
@cactus-agents/sweepstakesnasce (cf89ca9): camada de core dual-currency, loja e resgate pra o produto 7wins — aditiva, gateada e default-OFF em todos os packages, sem mudança de comportamento pras brands existentes. O package novo (0.2.0) cobre balances, wallet-preference, currency-switch, elegibilidade, resgates e pacotes/compra na loja;accountsganha tipos de coins, slice reativo deactiveCoinId/coinBalances,formatCoinsem símbolo euseFormatMoneycoin-aware;gamespassa a levarStartGameParams.currencyprostart-game-v2;country-configadiciona os EUA (USD, en-US, sem CPF); e o i18n em inglês ganha as strings de coins, resgate e compra. Seguemd15a7af(escala de valores de coin pra unidades de wire),182b9f9e0a4b4fb. É a primeira aparição do 7wins em qualquer registro do changelog.@cactus-agents/payments(ab76ab8):buildDepositPayloadpassa a emitircard_tokenjunto comkushki_token, com o mesmo valor, porque o backend está migrando a integração Kushki (CHL) pro contrato de one-step payments — o legado segue lendokushki_token, o novo lêcard_token. Aditivo, sem mudança de tipos.@cactus-agents/i18n(d2acf99): chaves dearia-labeldo toggle de senha (register.password_show/hide), consumidas pelo pass de acessibilidade do base.
Brand Overrides
- betpontobet-bet-br ganha bottom nav estilo Betano (
4515f0cba): variantemobile-nav-betanovertical-aware (conjunto CASSINO fora de/esportes, conjunto ESPORTES dentro), comNavTile/dispatcher/resolve estendidos prarotatingLabels,liveDot,vertical-toggleeopen-sports-menu. O botão "Menu" da barra de esportes passa a alternar o menu do SDK viainstance.toggleSportsMenu(); a barra fica sem "Ao Vivo" e ganha um item "Bônus". Consomesports-rogue ^0.25.0. Iterações de FAB e polish em274dba966,207e018f9,570caca87,abc2ac887,ba9c19477e7c0185a09.5ff3471b2desativa o GTM em produção na brand e alinha a home aos ajustes de 7k e vera. - 7k-bet-br: GTM reativado num container novo (
f0f37b214), fechando o desligamento da semana anterior.315f989c7migra ~157 assets que a 7k carregava do CDN de outra marca pro próprio CDN da brand (álbum e sticker-catalog, campaign widget, sidebar e escudos de posição de torneio) — os mesmos image IDs resolvem em qualquer hostname do CF Images da plataforma, e as 157 URLs foram verificadas com 200; oRankBadgetinha os escudos hardcoded no componente e virou brand-overrideable por um campo opcional emgamification-ui.ts, com o default preservado pra não exigir propagação. Também: selo Google Play restaurado no footer (f9b3afe0b) e virando link direto pra loja quando a brand tem a URL (0473b0810), logo do header fechando o jogo em tela cheia (cc6f71acf), botão "Baixar App" da sidebar levando pra LP interna (4d2160bb6) e banners da campanha 3M desligados (e1de6107d). - Widgets flutuantes com comportamento nativo (
655f3332b): entrada com atraso, auto-recolhimento numa peek tab colada na lateral e swipe pra dispensar (hook novouseFloatingCard), com orquestração unificada do stack (cards e tabs recolhidos dividem uma coluna sem sobreposição, back-to-top empilha acima). Config novafloatingWidgetscom override por brand: 7k e variações desativam tudo, betpontobet fecha de vez, donald e pb.bet recolhem sozinhos, demais em manual. --mobile-nav-hcomenv(safe-area)vivo (e19446b89): a var era um snapshot em px que congelava oenv(safe-area-inset-bottom), que muda em runtime quando a URL bar recolhe ou expande no scroll, e oResizeObserver(content-box) nunca re-dispara quando só o padding muda. Consequências reproduzíveis só em aparelho real: gap de ~17px entre a sidebar aberta e o nav, e widgets flutuantes grudando ou afundando no nav ao rolar. OBottomNavShellpassa a publicar só a altura de conteúdo (--mobile-nav-content-h) e--mobile-nav-hviracalc(content + env()*0.5)no CSS, resolvido pelo browser ao vivo.- Referral "Indique e Ganhe" v2 portado (
a4be35e01): varianteindicator-v2com fluxo de resgate por indicação, proxies/api/user/referral-indicator-v2e/api/user/referral-claim, botão "Resgatar" gateado por 100% de progresso e kill-switch de feature flag. Exclusivo davera-bet-brno momento do port.8dd1182ece0fc9c4c2badicionam ligas e ícones de esporte na mesma brand;6cd95da74ajusta as rows da home e do cassino ao vivo.
SEO
- Categoria canônica
todos-os-jogoscom 301 do alias (ededcc06b): sidebar e header de todas as brands BR apontavam pra/cassino/categoria/all, alias legado que renderiza o mesmo conteúdo do slug canônico, dividindo sinal de SEO, CrUX e PSI em duas URLs. Os configs foram trocados pro canônico em 9 pontos de 8 brands e o loader da categoria passa a responder 301 pro canônico quando recebe qualquer alias, preservando a query string. - Texto long-form da home 7k reescrito (
fd87c47f1): substitui ofront_descriptionportado do Nuxt legado, que fazia keyword stuffing e citava um fato desatualizado sobre licenciamento pré-regulamentação, por conteúdo no modelo das casas regulamentadas — heading e disclaimer de jogo responsável sempre visíveis (oPageDescriptioncolapsa em ~2 linhas), seções H2/H3 e FAQ de 8 perguntas em<details>/<summary>, indexável porque o conteúdo está no HTML e abre/fecha sem JS. Dados de operadora, CNPJ e portaria espelham o texto legal do footer.
Infra / CI/CD
- Auditoria de paridade skeleton × resolvido nos testes E2E (
b39f9a4): spec declarativo (ROUTE_CASES) que congela oclientLoadervia?__skeleton=1, medegetBoundingClientRectdos blocos-chave e falha se o delta passar de 4px — 6 rotas cobertas (home, hub, ao-vivo, categoria, provedor, jogo), estável em 12 execuções. É o guard anti-drift do trabalho de streaming, e o único trabalho substantivo de E2E do período.78d82d0adiciona as rotas de VIP/gamificação eb5d38b9as de conteúdo e legal. - Environments novos — ops
dd5ed7ecriastage-spaestage-cachedo 7k, espelhando a stage existente, pras duas linhas de experimento (app-shell SPA e cache);952f488cria a stage da brand de sweepstakes 7wins; base393c6ebf3,bc4543ba7,7d679da57e42ee10b60sincronizam os dropdowns de dispatch.5f4e09cbumpa a geraçãoapcsrasj-v13→v14, e ops9ce77f7,95285f9,8552ee1,66b3c8f,34247cde7e995bamovemdefault_branchdecl-bet7k-come de stages entremain-b,main-clestage-workers-cache, com557cedaapontando a stage do 7k pramain-7k. d5e538480adiciona triggers temporários de URL pra teste de performance (ferramenta de medição, não comportamento de produção);7ee2df289reabre o modal de depósito em navegação repetida com?deposit=.
Documentação
6cc78cfeedocumenta em código o dual-write dekushki_tokenecard_tokenno fluxo Kushki, acompanhando o coreab76ab8.front-service-api418708acorrige um comentário que confundia pin de região com Smart Placement.