Agenda pessoal que não perde o que você escreveu.
Atividades, calendário, notas e compras em um espaço privado, no celular e no desktop.
- A proposta
- Funcionalidades
- Arquitetura
- Decisões de engenharia
- Como executar
- Variáveis de ambiente
- Testes e verificação
- Estrutura do repositório
- Segurança e operação
- Privacidade e acessibilidade
- Status do projeto
- Autor
- Licença
Aplicativos de agenda costumam falhar exatamente quando você mais precisa deles: no metrô sem sinal, com duas abas abertas, no meio de uma edição. O resultado é sempre o mesmo — a alteração some, ou uma versão sobrescreve a outra em silêncio.
O Leve foi construído a partir de uma premissa oposta: perder dado do usuário é o pior defeito possível, e todo o resto da arquitetura se subordina a isso.
Na prática, isso significa três compromissos:
1. Nenhuma escrita acontece sem o servidor validar. O cliente nunca grava direto no banco. Toda mutação vira um comando com identificador próprio, entidade estável e revisão esperada. Se o comando chegar duas vezes — retry de rede, clique duplo, reconexão — ele só produz efeito uma vez.
2. Conflito não é resolvido por sorte. Quando duas sessões editam a mesma nota, o sistema não aplica "a última vence". Ele detecta a divergência de revisão e preserva o conteúdo concorrente para que você decida.
3. Ficar offline não pode custar trabalho. Alterações feitas sem conexão vão para uma outbox local e sincronizam quando a rede volta. Nada é descartado no caminho, e a interface diz o que está pendente em vez de fingir que salvou.
Somado a isso, é um sistema de uso pessoal: os dados são isolados por conta, exportáveis em JSON versionado e apagáveis de verdade — incluindo a exclusão completa da conta.
| Área | O que faz |
|---|---|
| Agenda | Tarefas e compromissos (com horário ou dia inteiro), pendências atrasadas e visão de calendário |
| Recorrência | Diária, semanal e mensal, editando uma ocorrência ou todas as futuras |
| Notas | Editor limitado e seguro, vínculos, fixação e busca |
| Compras | Listas, quantidades, modelos e ciclos |
| Organização | Categorias com criação, edição, arquivamento e lixeira |
| Recuperação | Lixeira, expurgo e exclusão de conta retomável |
| Portabilidade | Exportação JSON versionada; importação entra como cópia, sem sobrescrever |
| PWA | Instalável, com notificações configuráveis por aparelho |
| Offline | Cache privado e outbox local, ativados manualmente |
Desativado por padrão, por ser um dado sensível guardado no aparelho. A escolha vale para esta conta neste aparelho. Ative em Perfil e preferências → Uso offline enquanto estiver conectado e abra as áreas que quiser consultar depois. A partir daí, a sessão e os dados já carregados ficam salvos neste aparelho; novas alterações elegíveis entram na outbox e sincronizam na reconexão. Ao sair, trocar de conta ou remover esses dados, o Leve avisa se houver alterações ou rascunhos que ainda não foram enviados.
O primeiro acesso e a ativação do modo offline precisam acontecer online. Com o modo desligado, rascunhos novos ficam apenas nesta aba até ela ser fechada; rascunhos e alterações de versões anteriores continuam disponíveis para recuperação.
Navegador (React 19 + TS)
│
├── Firebase Auth ──────────── e-mail/senha ou Google
│
└── POST /api/commands ─────── Express 5 (token Firebase obrigatório)
│
├── validação com schemas de packages/domain (Zod)
├── autorização por UID + membership
└── Firestore ◄── Rules negam escrita direta do cliente
Worker (cron) ── tick assinado por HMAC ──► /api (recorrência, avisos, limpeza)
| Camada | Escolha | Papel |
|---|---|---|
| Web | React 19 + TypeScript + Vite | Aplicação responsiva e PWA |
| Identidade | Firebase Authentication | E-mail/senha e Google, sessão persistente |
| Dados | Firestore | Coleções privadas por UID e membership |
| API | Express 5 | Único ponto de escrita, via POST /api/commands |
| Domínio | packages/domain (Zod) |
Schemas e regras compartilhados entre cliente e servidor |
| Agendamento | Worker com tick HMAC | Recorrência, invalidação de avisos e limpeza paginada |
Um único caminho de escrita, em vez de CRUD por endpoint.
Toda mutação passa por POST /api/commands. Validação, autorização, idempotência e registro ficam concentrados em um lugar só. O custo é uma API menos "REST", com discriminador de tipo no payload e menos legibilidade para quem espera rotas por recurso.
Schemas compartilhados em vez de validação duplicada. Cliente e servidor importam o mesmo pacote de domínio. Uma regra de negócio muda em um arquivo, não em dois — e o TypeScript acusa a divergência em build.
Revisão esperada em vez de last-write-wins. Cada comando declara a revisão que espera encontrar. Se não bater, o servidor recusa e devolve o estado concorrente. É mais trabalho na interface, mas elimina a classe inteira de bugs de sobrescrita silenciosa.
Offline como opt-in, não como padrão. Cache local significa dados pessoais gravados no aparelho. A escolha é do usuário, feita de forma explícita e reversível.
Custos dependem da configuração do provedor. As escolhas iniciais priorizaram planos sem custo. Preços, limites, franquias e condições variam conforme o plano, a região e o consumo; confirme as condições atuais de Vercel, Firebase e demais provedores antes de ativar serviços ou publicar. O repositório não certifica custo zero.
Requer Node.js 24.x e npm 11.19.1. O campo packageManager fixa a versão e o CI verifica divergências.
corepack enable
npm ci
npm run devnpm run dev sobe Auth Emulator, Firestore Emulator, API e Vite juntos. Em outro terminal:
npm run seed:localDepois abra http://localhost:5174/entrar e use as credenciais fictícias impressas pelo seed.
| Script | O que faz |
|---|---|
npm run dev |
Emuladores + API + Vite (ambiente completo) |
npm run dev:web |
Só o Vite; exige uma API separada em localhost:8788 |
npm run seed:local |
Popula os emuladores com dados fictícios |
npm run preview |
Serve o dist já buildado |
npm run screenshots |
Regera as imagens de docs/screenshots/ |
O Auth Emulator não envia e-mails reais. No cadastro local, a tela de verificação oferece Confirmar neste ambiente, que consome apenas o código de teste do emulador. Fora do modo local, o Firebase envia o link normalmente.
Copie .env.example para .env.local e preencha antes de usar o Firebase fora dos emuladores. Arquivos .env* não são versionados.
| Variável | Onde vive | Observação |
|---|---|---|
VITE_FIREBASE_* |
Cliente | Vai para o bundle e é pública — só configuração do app Web |
| Credenciais de servidor | Servidor | Nunca use o prefixo VITE_, ou o segredo vaza no build |
CORS_ORIGINS |
Servidor | Origens aceitas pela API. Sem valor, só o mesmo domínio |
LOG_LEVEL |
Servidor | debug em desenvolvimento, error em produção |
npx playwright install chromium
npm run lint
npm run typecheck
npm run build
npm test
npm run test:integration
npm run test:e2e:localnpm run check encadeia build, testes unitários e E2E.
| Camada | O que cobre |
|---|---|
| Unitários | Regras de domínio, recorrência e utilitários |
| Integração | Emuladores reais — retomada de importação, leases e contas concorrentes |
| E2E (Playwright) | Duas abas, reconexão da outbox, conflito de notas, reflow, Axe e exportação/importação |
Relatório E2E em playwright-report/index.html; traces de falha em test-results/.
Fora do automatizado: push com o app fechado, leitor de tela externo e instalação em aparelho físico exigem validação manual.
| Caminho | Responsabilidade |
|---|---|
apps/web/src/app |
Rotas, shell e providers |
apps/web/src/features |
Identidade, agenda, notas, compras e preferências |
apps/web/src/platform |
Firebase, API, tema, outbox e PWA |
api/ |
Entrypoint HTTP da API |
server/ |
Comandos, autorização, repositórios e jobs |
packages/domain/ |
Schemas e regras compartilhadas |
workers/scheduler/ |
Tick HMAC |
scripts/ |
Dev local, seed, screenshots e migrações |
tests/ |
Unitários, integração e navegador |
docs/ |
ADRs, runbooks e evidências |
- Dados isolados por UID e membership; escrita direta do cliente é negada pelas Rules.
- Comandos idempotentes por
operationId; conflitos preservam o conteúdo concorrente. - Logs estruturados com redação de segredos — nunca devem conter tokens, cookies, senhas ou conteúdo privado.
- CSP permite recursos próprios e restringe scripts externos do login Google a
apis.google.comeaccounts.google.com. vercel.jsonfaz build na raiz (não emapps/web), saída emdist, com rewrites apenas das rotas previstas —/api/*e/assets/*não têm fallback global.- Nenhum recurso pago é ativado.
O resumo público está em /privacidade. A conta permite exportar e excluir dados, revogar notificações, reduzir movimento, reduzir transparência e aumentar contraste. Estados de carregamento e de perda de conexão explicam o que está acontecendo e oferecem recuperação sem apagar alterações locais.
Release final homologada e aceita: validações em aparelho, instalação e atualização do PWA, notificações, acessibilidade manual, capacidade e rollback foram executadas. A base está coberta por testes automatizados, emuladores e homologação manual.
Hospedagem autorizada: https://leve-agenda.vercel.app. Histórico operacional em CONTINUAR.md.
Kauan Kelvin — desenvolvedor fullstack
MIT — ver LICENSE.



