@whodo/app-sdk
v0.4.1
Published
SDK oficial para apps construidos na plataforma Whodo. Acesso a entidades, autenticacao e funcoes serverless via cliente unico.
Maintainers
Readme
@whodo/app-sdk
SDK oficial dos apps construidos na plataforma Whodo — plataforma onde aplicacoes React sao geradas via prompt de IA.
Status: 0.3.x — funcional + backend integrado + URL canonica RESTful. Cliente HTTP, entidades, autenticacao, funcoes, telemetria, retry e suporte a
import.meta.env.VITE_WHODO_*implementados (144 testes). URL pattern comappIdno path (/api/apps/<appId>/entities/<Name>) — RESTful, debugavel, compativel com cache/CDN. Sistema de permissoes por entidade (public, authenticated, owner, admin). Templates declaram permissoes no clone viagetSeedPermissions(). Vejabackend/skills/SDK_INTEGRATION.mdpro guia completo de integracao backend.Breaking change vs 0.2.x: URL agora inclui
appIdno path. SDK 0.2.x chamava/api/apps/entities/XcomX-App-Idheader; 0.3.x chama/api/apps/<appId>/entities/X. O backend mantem bridge transicional pra aceitar 0.2.x temporariamente, mas apps novos devem usar 0.3.x.
O que este SDK faz
Cada app gerado pela Whodo precisa de quatro coisas no runtime:
- Entidades persistentes — coleções de dados que sobrevivem a reload e sao compartilhadas entre usuarios
- Autenticacao de usuarios finais — login/logout/sessao com JWT
- Funcoes serverless — codigo que precisa rodar com privilegios server-side (validar pagamento, chamar API com chave secreta, etc.)
- Observabilidade — telemetria de requests para debug no painel da plataforma
O SDK expoe tudo isso por uma unica instancia configurada com appId + token. Apps escrevem o minimo de codigo possivel: a IA da plataforma copia padroes diretamente do README e dos tipos.
Instalacao
npm install @whodo/app-sdkCompativel com browsers modernos (ES2022+) e Node 18+. ESM-only — sem build CJS.
Inicializacao
A forma canonica usada pelo scaffold da Whodo:
// lib/whodoClient.ts
import { createClient } from '@whodo/app-sdk';
export const whodo = createClient({
appId: import.meta.env.VITE_WHODO_APP_ID,
appBaseUrl: 'https://app.whodo.com.br',
});createClient resolve appId e token em ordem de prioridade:
- Override explicito na config
- Query params da URL (
?app_id=...&access_token=...) — tokens vindos por URL sao removidos viahistory.replaceState - localStorage (
whodo_app_id,whodo_access_token) - Defaults (geralmente
import.meta.env.VITE_WHODO_*)
Esse encadeamento permite o mesmo bundle servir cenarios diferentes (preview no dashboard, app publicado, embed em terceiros) sem reconfiguracao.
API completa
whodo.entities
Acesso CRUD a colecoes do banco do app. Toda propriedade do entities e
resolvida via Proxy para uma EntityCollection — nao ha lista finita,
qualquer nome funciona. As mesmas colecoes retornam a mesma instancia
(whodo.entities.Order === whodo.entities.Order).
// Listar (com sort opcional, formato '-campo' para desc)
const all = await whodo.entities.Order.list();
const recent = await whodo.entities.Order.list('-_created', 50);
// Filtrar (igualdade direta na v0.1; operadores avancados na v0.3)
const pending = await whodo.entities.Order.filter({ status: 'pending' });
// Buscar por ID
const order = await whodo.entities.Order.get('abc-123');
// Criar
const created = await whodo.entities.Order.create({ total: 100, status: 'pending' });
// Atualizar parcialmente (PATCH)
const updated = await whodo.entities.Order.update(created._id, { status: 'paid' });
// Deletar
await whodo.entities.Order.delete(created._id);| Metodo | HTTP | Path |
|---|---|---|
| list(sort?, limit?, skip?) | GET | /entities/:name |
| filter(query, sort?, limit?, skip?) | GET | /entities/:name?q=... |
| get(id) | GET | /entities/:name/:id |
| create(data) | POST | /entities/:name |
| update(id, data) | PATCH | /entities/:name/:id |
| delete(id) | DELETE | /entities/:name/:id |
Todo registro tem campos auto-preenchidos pelo backend: _id, _created, _updated.
whodo.auth
Autenticacao baseada em JWT. Tokens viajam em Authorization: Bearer ...
e sao persistidos em localStorage (chave whodo_access_token).
// Login — JWT e auto-persistido; a proxima request ja vai autenticada
const session = await whodo.auth.login('[email protected]', 'senha');
console.log(session.user, session.token);
// Usuario logado atualmente
const me = await whodo.auth.me();
// Boolean rapido (faz roundtrip ao /auth/me)
const isLogged = await whodo.auth.isAuthenticated();
// Logout — sempre limpa storage local, mesmo se servidor falhar
await whodo.auth.logout();| Metodo | HTTP | Path |
|---|---|---|
| me() | GET | /auth/me |
| login(email, password) | POST | /auth/login |
| logout() | POST | /auth/logout |
| isAuthenticated() | GET | /auth/me (try/catch) |
whodo.functions
Invocacao de codigo serverless. Suporta payload JSON ou FormData (uploads).
const result = await whodo.functions.invoke<{ paymentId: string }>(
'processPayment',
{ amount: 100, customerId: 'cust_1' },
);
// Upload via FormData (sem JSON.stringify automatico)
const formData = new FormData();
formData.append('file', fileFromInput);
const upload = await whodo.functions.invoke<{ url: string }>('uploadFile', formData);| Metodo | HTTP | Path |
|---|---|---|
| invoke<T>(name, payload?) | POST | /functions/:name |
Lifecycle
// Sobrescrever ou limpar token em runtime
whodo.setToken('new-jwt');
whodo.setToken(null); // tambem remove de localStorage
// Inspecionar config atual (frozen)
const config = whodo.getConfig();
console.log(config.appId, config.serverUrl, config.token);
// Cleanup ao desmontar (idempotente)
whodo.cleanup();Configuracao completa
createClient({
appId: 'meu-app', // obrigatorio
token: 'jwt-inicial', // opcional; lido tambem de URL/storage
appBaseUrl: 'https://app.whodo.com.br', // default
serverUrl: 'https://app.whodo.com.br', // override do path da API; default eh `${appBaseUrl}/api`
requiresAuth: false, // se true, redireciona pra login quando sem token
headers: { 'X-Custom': 'value' }, // headers extras em toda request
fetch: customFetch, // override do fetch global (SSR, testing)
options: {
onError: (err) => report(err), // hook de observabilidade
telemetry: {
enabled: true, // default true em iframe, no-op fora
targetOrigin: 'https://app.whodo.com.br', // origem permitida; '*' apenas em dev
},
},
});Tratamento de erros
Todo erro lancado pelo SDK e instancia de WhodoError:
import { WhodoError } from '@whodo/app-sdk';
try {
await whodo.entities.Order.get('nao-existe');
} catch (err) {
if (err instanceof WhodoError) {
console.error(err.code, err.status, err.message, err.requestId);
if (err.code === 'unauthorized') {
// redireciona pra login
}
}
throw err;
}| Campo | Tipo | Descricao |
|---|---|---|
| message | string | Mensagem PT-BR pronta pra UI |
| status | number | HTTP status; 0 para erros nao-HTTP |
| code | string | Codigo curto e estavel |
| data | unknown | Payload original do servidor |
| requestId | string | UUID gerado pelo cliente, correlaciona com logs |
| cause | unknown | Erro original (rede/parse) quando aplicavel |
Codigos comuns
| code | status | Quando |
|---|---|---|
| invalid_config | 0 | appId ausente ou invalido em createClient |
| unauthorized | 401 | Token ausente, expirado ou revogado |
| forbidden | 403 | Sem permissao no app/entidade |
| not_found | 404 | Recurso inexistente |
| validation_error | 422 | Payload invalido para a entidade/funcao |
| rate_limited | 429 | Limite de requests atingido |
| server_error | 5xx | Falha do backend (com retry automatico) |
| network_error | 0 | Falha de rede (TypeError do fetch) |
| aborted | 0 | AbortSignal disparado |
| invalid_json_response | 2xx | 200 com body nao-JSON |
| not_implemented | 501 | Endpoint nao implementado no backend ainda |
WhodoError.toJSON() produz uma representacao log-safe sem cause/stack.
Retry e telemetria
Retry automatico ocorre em respostas HTTP 5xx e erros de rede (TypeError). Politica default: 3 tentativas, exponential backoff (200ms, 400ms, 800ms) com jitter de 50-100% para evitar thundering herd. NUNCA retenta 4xx — bug do cliente nao se resolve repetindo.
Telemetria emite eventos whodo:request:start e whodo:request:end via postMessage para window.parent quando o SDK roda em iframe (caso do preview do dashboard Whodo). Cada evento inclui requestId, url, method, status, durationMs. Fora de iframe, e no-op.
Em producao do dashboard, configure options.telemetry.targetOrigin para 'https://app.whodo.com.br' (origem especifica). O default '*' e apenas para desenvolvimento.
Storage e seguranca
Token e armazenado em localStorage:
whodo_access_token— JWT correntewhodo_app_id— ID do app (resolvido apenas uma vez por origin)whodo_app_base_url— URL da plataformawhodo_from_url— URL de origem (para redirect pos-login)
Decisao consciente: localStorage em vez de cookie HttpOnly. Vantagem: funciona em qualquer cenario de embed, qualquer origem. Tradeoff: vulneravel a XSS no app gerado. Mitigacoes server-side: TTL curto no JWT (24h), revoke list em Redis, rate limit por token, sessoes ativas inspecionaveis. Para apps com dados sensiveis (financeiro, saude), revisitar essa decisao.
?clear_access_token=true na URL forca remocao do token (util para fluxo de logout iniciado por servico externo).
Tokens vindos por URL (?access_token=...) sao removidos via history.replaceState apos serem persistidos — evita aparecer em Referer headers, screenshots e logs do browser.
TypeScript
Tipagem completa via generics em entities:
interface Order {
customer_name: string;
total: number;
status: 'pending' | 'paid' | 'cancelled';
}
const orders = await whodo.entities.Order.list<Order>();
// ^? Array<Order & BaseRecord>
const newOrder = await whodo.entities.Order.create<Order>({
customer_name: 'Maria',
total: 49.9,
status: 'pending',
});BaseRecord adiciona _id, _created, _updated automaticamente.
Browser support
- ESM-only (sem CJS bundle)
- ES2022 target — Chrome 94+, Firefox 93+, Safari 16.4+, Edge 94+
- Node 18+ (com
fetchnativo) - Compativel com SSR (Next.js, Remix, Astro) via injecao de
fetchcustomizado
Sem dependencias runtime — bundle final do app inclui apenas o SDK (~19 KB ESM minified pre-gzip; ~7 KB pos-gzip).
Desenvolvimento local
git clone <repo-url>
cd packages/whodo-app-sdk
npm install
npm run typecheck # tsc --noEmit
npm test # vitest run (135 testes)
npm run test:watch # vitest watch mode
npm run build # tsup -> dist/
npm run dev # tsup --watchRoadmap
| Versao | Conteudo | Status |
|---|---|---|
| 0.0.1-alpha | Surface publica + tipos estaveis | concluido |
| 0.1.x | HTTP transport, entities CRUD, auth, functions, telemetry, retry | concluido |
| 0.2.x | Backend integration: rotas REST, permission system, JWT site (TTL 24h), import.meta.env.VITE_WHODO_*, prefix /api/apps/ | concluido |
| 0.3.x | URL canonica /api/apps/<appId>/... (appId no path, RESTful, debugavel). Templates declaram permissoes via getSeedPermissions(). Validacao de cross-app token replay no path | atual |
| 0.4.x | Paginacao cursor-based + filter operators ($gt, $in, $regex) | depois |
| 0.5.x | Functions com sandbox real + secrets management | depois |
| 0.5.x | Realtime subscriptions via WebSocket | futuro |
| 0.6.x | Custom domains + SSL automatico | futuro |
| 0.7.x | Hardening: revoke list, audit log, refresh tokens, rate limit Redis | futuro |
| 1.0.0 | API congelada, security audit, production-ready | TBD |
Manutencao via IA
Esse pacote tem documentacao orientada para agentes de IA / Claude Code em
SKILL.md — cobre arquitetura, decisoes de design, gotchas, e
guias do tipo "como adicionar X". Leia antes de modificar codigo aqui.
Licenca
MIT (c) 2026 Whodo.
