@codedartdev/coinsentry-sdk
v0.1.3
Published
Headless CoinSentry SDK for browser-safe Auth, Session, Entity, KYC, Brand, HTTP and Realtime contracts.
Readme
@codedartdev/coinsentry-sdk
Core headless do SDK CoinSentry. Este pacote define contratos publicos para frontends de clientes consumirem Auth, Session, Entity, KYC, Brand, HTTP, erros e Realtime sem depender de React, Next.js ou componentes da whitelabel.
Responsabilidade
- Cliente HTTP same-origin com
fetch, cookies, CSRF retry, FormData seguro e corposURLSearchParamspara endpoints form-urlencoded. - Servicos headless:
auth,session,entity,kyc,brande modulos de produto. - Tipos publicos, guards e codigos de erro estaveis.
- Resolucao pura do KYC universal via
@codedartdev/coinsentry-sdk/kyc. - Interface de realtime desacoplada do client WebSocket interno.
Fora Do Escopo
- Segredos de assinatura do BFF.
- Headers upstream
X-Frontend-*. - React providers, hooks, modais, toasts ou redirects.
- Chamada browser para
/api/v1/kyc/requirements. - Decisao local de
client_flow, jurisdicao, customer type ou proxima tela.
createCoinSentryClient() e browser-safe e nunca aceita segredo de assinatura.
O segredo do proxy deve existir apenas em APIs server-only de
@codedartdev/coinsentry-next.
Uso Basico
import { createCoinSentryClient } from '@codedartdev/coinsentry-sdk';
const cs = createCoinSentryClient({
baseUrl: '/',
locale: 'pt-BR',
onError(error) {
console.warn(error.code, error.status, error.reason);
},
});
await cs.auth.login({ email, password, slideCaptchaToken });
const session = await cs.session.refresh();
const entities = await cs.entity.list();
const selected = await cs.entity.select({ entityUuid: entities[0].uuid });
const overview = await cs.kyc.getOverview();O browser deve usar baseUrl: '/' para chamar somente o BFF same-origin.
Nao passe WHITELABEL_PROXY_SIGNATURE_SECRET,
FRONTEND_PROXY_SIGNATURE_SECRET ou qualquer segredo equivalente para este
cliente.
Locale
O adapter fetch envia Accept-Language automaticamente.
cs.locale.set('pt-BR');
cs.locale.setKyc('es-AR');
await cs.kyc.getOverview();
cs.locale.clearKyc();locale.setKyc() afeta apenas requests para /api/v1/kyc*. Isso preserva o
comportamento da whitelabel atual sem colocar fallback de fluxo no browser.
Brand
client.brand.getConfig() consome o contrato publico GET /api/brand/theme.
Ele aceita brandKey e etag, normaliza o payload canonico e retorna metadata
de cache quando disponivel.
const response = await cs.brand.getConfig({
brandKey: 'default',
etag: previousEtag,
});
if (!response.notModified && response.config) {
renderBrand(response.config);
}O subpath @codedartdev/coinsentry-sdk/brand contem helpers puros para aplicar
o contrato em qualquer framework:
import {
buildBrandCss,
getBrandCssVariables,
normalizeBrandConfig,
} from '@codedartdev/coinsentry-sdk/brand';
const brand = normalizeBrandConfig(response.config);
const css = buildBrandCss(brand);
const darkVars = getBrandCssVariables(brand, 'dark');Os tipos cobrem fonte base, tipografia, logo textual/imagem, favicon claro e
escuro, tokens de mercado (rising/downing) e tokens opcionais de superficie,
texto e estado enviados pelo backend.
Transporte HTTP
client.request<T>() e o transporte headless oficial. Use-o para endpoints
same-origin do BFF em vez de fetch manual no browser. O adapter preserva
FormData sem definir Content-Type e aceita URLSearchParams para casos
form-urlencoded como autorizacao de canais realtime.
Para compatibilidade com Sanctum/Fortify, requests mutaveis same-origin leem o
ultimo cookie XSRF-TOKEN disponivel e enviam X-XSRF-TOKEN decodificado
automaticamente. O token nao e anexado a requests cross-origin.
O retry automatico de 419 continua ativo por padrao em client.request<T>().
Para endpoints sensiveis que ja inicializam CSRF antes do POST, use
retryOnCsrf: false para evitar repetir a mesma acao:
await cs.request({
method: 'POST',
path: '/auth/login',
retryOnCsrf: false,
body: payload,
});csrf.init() e deduplicado: chamadas concorrentes compartilham o mesmo request
e chamadas recentes reaproveitam uma janela curta de inicializacao. Metodos de
auth, como login, register e 2FA, continuam forçando CSRF fresco antes do POST
para preservar a seguranca do fluxo Fortify/Sanctum.
Auth
auth.login() e auth.twoFactorChallenge() preservam os envelopes reais do
backend:
CoinSentryLoginTwoFactor:{ two_factor: true };CoinSentryLoginIncomplete:{ success: true, two_factor: false, needs_refresh: true };CoinSentryLoginMultipleEntities: login autenticado sem entidade ativa;CoinSentryLoginSingleEntity: login com entidade selecionada;CoinSentryLoginSuccess: sucesso simples.
Os metodos de auth que chamam csrf.init() antes do POST nao repetem
automaticamente o POST em 419. Isso evita duplicar a mesma tentativa de login,
registro ou desafio 2FA, especialmente quando ha CAPTCHA envolvido.
Use guards publicos para reduzir branching inseguro:
import {
isCoinSentryLoginIncomplete,
isCoinSentryLoginMultipleEntities,
isCoinSentryLoginTwoFactor,
} from '@codedartdev/coinsentry-sdk';
const response = await cs.auth.login({ email, password, slideCaptchaToken });
if (isCoinSentryLoginTwoFactor(response)) {
showTwoFactorChallenge();
}
if (isCoinSentryLoginIncomplete(response)) {
await cs.session.refresh();
}
if (isCoinSentryLoginMultipleEntities(response)) {
openEntitySelector(response.entities);
}Session E Entity
session.refresh() retorna a sessao autenticada. entity.select() retorna um
contrato tipado com usuario, balances, entidades e kyc_required quando o BFF
responde com esse envelope.
Guards uteis:
isCoinSentrySession(value);hasCoinSentryActiveEntity(session);isCoinSentryEntityMinimal(value);requiresCoinSentryEntitySelection(value).
Modulos De Produto
Alem de Auth, Session, Entity e KYC, o client expoe dominios usados pela
whitelabel atual. Todos usam client.request<T>(), retornam payload de dominio
normalizado e aceitam { signal } quando aplicavel.
account.twoFactor: enable, QR code, confirmacao, recovery codes, regeneracao, disable e status.contacts: contatos bancarios, bancos e contatos de wallet.deposits: dados de deposito e intent de deposito.withdrawals: dados de saque, saque fiat, saque crypto e taxa de rede.payments: moedas, historico, PIX receive/status/lookup/pay.affiliate: codigo, status, referrals, payouts e rankings.market: market data, orderbook, criar/cancelar ordens e cancelar todas.otc: pares, quote/execute/history, ativar/desativar quote e planos recorrentes.
const depositData = await cs.deposits.get('BRL');
const fee = await cs.withdrawals.networkFee({ currency: 'BTC', network: 'BTC' });
const quote = await cs.otc.createQuote({
symbol: 'USDT_BRL',
from_currency: 'BRL',
to_currency: 'USDT',
from_amount: '100',
});Esses modulos continuam headless: nao exibem toast, nao redirecionam e nao conhecem componentes da whitelabel.
Market E Orderbook
O modulo market tambem expoe helpers puros para consumers que precisam de
orderbook realtime sem conhecer o formato interno de canais:
import {
buildCreateOrderPayload,
getOrderbookChannel,
normalizeMarketSymbol,
normalizeOrderbookSnapshot,
} from '@codedartdev/coinsentry-sdk/market';
const symbol = normalizeMarketSymbol('btc-brl'); // BTC_BRL
const markets = await cs.market.listMarkets();
const ticker = await cs.market.getTicker(symbol);
const details = await cs.market.getMarketDetails(symbol);
const snapshot = await cs.market.getOrderbookSnapshot('BTC_USDT', 50);
const trades = await cs.market.getTrades('BTC_USDT', { limit: 50 });
const channel = getOrderbookChannel('BTC_USDT'); // orderbook.btc-usdt
await cs.market.createOrder(buildCreateOrderPayload({
symbol: 'BTC_USDT',
side: 'BUY',
type: 'LIMIT',
price: '67000',
quantity: '0.01',
}));O book publico e tratado como snapshot-only: o client deve carregar
GET /api/v1/orderbook/{symbol} e substituir o estado local em
.orderbook.updated. O SDK nao expoe diffs incrementais publicos porque o
backend atual nao publica sequencia nem delta canonico.
Endpoints publicos de mercado:
market.listMarkets()->GET /api/v1/market;market.getTicker(symbol)->GET /api/v1/ticker/{symbol};market.getOrderbookSnapshot(symbol, depth?)->GET /api/v1/orderbook/{symbol};market.getTrades(symbol, { limit? })->GET /api/v1/trades/{symbol}.
market.getMarketDetails(symbol) e o agregado autenticado
GET /api/v1/market/{symbol} para saldos, ordens do usuario e enriquecimento
privado. market.getMarket(symbol) continua como alias por compatibilidade,
mas orderbook e trades publicos nao dependem desse agregado.
KYC Headless
import {
hasKycClientFlow,
isKycFlowSnapshotConfigured,
resolveKycFlowSnapshot,
} from '@codedartdev/coinsentry-sdk/kyc';
const overview = await cs.kyc.getOverview();
const flow = resolveKycFlowSnapshot({ overview });
if (!isKycFlowSnapshotConfigured(flow)) {
throw new Error('KYC flow is not configured.');
}
if (flow.activeStep?.screen === 'document_upload') {
await cs.kyc.upload({ step: flow.activeStep, formData });
}O core falha fechado quando nao existe client_flow. Ele nao cria fallback local
para fluxo legado, jurisdicao, customer type ou proxima tela.
Guards KYC publicos:
hasKycClientFlow(requirements);hasResolvedKycRequirements(requirements);isKycFlowSnapshotConfigured(snapshot);isKycOverviewFlowConfigured(overview).
Erros Publicos
O SDK normaliza falhas em CoinSentryApiError:
import {
isCoinSentryAbortError,
normalizeCoinSentryError,
} from '@codedartdev/coinsentry-sdk';
try {
await cs.kyc.getOverview();
} catch (error) {
if (isCoinSentryAbortError(error)) return;
const normalized = normalizeCoinSentryError(error);
if (normalized.code === 'kyc_required') {
openKyc();
}
}Codigos estaveis:
unauthenticatedcsrf_expiredkyc_requiredentity_requiredinvalid_signatureorigin_not_allowedmissing_signature_headersvalidation_failedrate_limitedforbiddennetwork_errorserver_errorunknown_error
Cancelamentos iniciados pelo caller via request.signal sao repropagados como
abort e nao chamam onError. Use isCoinSentryAbortError(error) para ignorar
esse caso. Timeout interno continua sendo falha de rede normalizada.
Realtime
createCoinSentryRealtime(adapter) define a interface publica. A implementacao
concreta pode ser o Reverb/Pusher client interno da whitelabel ou outro adapter
compativel.
const realtime = createCoinSentryRealtime(adapter);
realtime.connect();
realtime.subscribeToEntity(entityUuid, 'balance.updated', onBalance);
realtime.subscribeStatus?.((status) => {
if (status === 'connected') {
// reancore snapshots locais se necessario
}
});O payload recomendado para saldo e CoinSentryBalanceUpdate:
type CoinSentryBalanceUpdate = {
currency: string;
available: string | number;
locked: string | number;
total?: string | number;
};Estado Atual
Beta npm (0.1.2, dist-tag latest). O pacote gera dist, .d.ts,
.d.ts.map e sourcemaps. O pacote permanece UNLICENSED enquanto os contratos
publicos seguem em beta.
