0889ee9117
feat(hooks): add useLiveStream generic WebSocket hook - supports websocket/sse/polling transports - exponential backoff reconnect with jitter - circular buffer with configurable size - typed filter callback per use case - manual disconnect + reconnect + error state feat(hooks): add useLiveMetrics derived hook - sliding time-window cut - moving average (configurable window) - current / avg / min / max / ratePerSecond - zero allocations per tick (memoized) feat(charts): add LiveMetricChart molecule (Recharts) - line + area variants, grid + tooltip - moving-average overlay (dashed) - ConnectionStatus atom in header - status bar + compact mode - 100% responsive, GPU via SVG ViewBox feat(atoms): add ConnectionStatus indicator - 5 states: disconnected/connecting/connected/reconnecting/error - animated pulse, JetBrains Mono, pill style - exported helpers: formatLatency / formatBytes docs(pkg): bump v0.1.0 → v0.2.0, add recharts peerDep
155 lines
7.3 KiB
JavaScript
Executable File
155 lines
7.3 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
import { createRequire } from 'module';
|
|
import express from 'express';
|
|
import path from 'path';
|
|
import { fileURLToPath } from 'url';
|
|
|
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
const require = createRequire(import.meta.url);
|
|
|
|
const app = express();
|
|
app.use(express.json());
|
|
|
|
// Health check
|
|
app.get('/health', (req, res) => {
|
|
res.json({ status: 'ok', lib: '@pulse-libs/core', version: '1.0.0-beta.1', uptime: process.uptime().toFixed(1) + 's' });
|
|
});
|
|
|
|
// Demo date endpoint
|
|
app.get('/api/now', (req, res) => {
|
|
res.json({ iso: new Date().toISOString(), locale: new Date().toLocaleString('pt-BR') });
|
|
});
|
|
|
|
// Demo validators list
|
|
app.get('/api/validators', (req, res) => {
|
|
res.json({
|
|
emailSchema: 'RFC-compliant email validation',
|
|
passwordSchema:'8+ chars, uppercase, lowercase, number, special',
|
|
uuidSchema: 'UUID v4/v5 validation',
|
|
phoneSchema: 'BR phone (8-15 digits)',
|
|
cpfSchema: 'CPF with check digits',
|
|
cnpjSchema: 'CNPJ with check digits',
|
|
urlSchema: 'http/https URL validation',
|
|
sanitizedStr: 'XSS-safe string stripping HTML tags',
|
|
safeParse: 'Zod safeParse wrapper → {ok, data, error}',
|
|
});
|
|
});
|
|
|
|
// Demo utils
|
|
app.get('/api/utils', (req, res) => {
|
|
res.json({
|
|
cn: 'tailwind-merge classNames builder',
|
|
debounce:'fn debounce = debounce(fn, ms)',
|
|
throttle:'fn throttle = throttle(fn, ms)',
|
|
storage: { ls: 'get/set/remove localStorage', ss: 'get/set/remove sessionStorage' },
|
|
date: { format: 'DD/MM/YYYY', iso, relative: 'diff ago/from now' },
|
|
str: { capitalize, truncate, toSlug },
|
|
num: { format: '1,000', percent: '85.0%' },
|
|
arr: { chunk: 'chunk([], n)', unique: 'unique([])', flatten: 'flatten([])' },
|
|
obj: { pick, omit, merge, isEmpty },
|
|
});
|
|
});
|
|
|
|
// Static files (dist includes DTS/DTSM)
|
|
app.use('/dist', express.static(path.join(__dirname, 'dist')));
|
|
|
|
// Main page — docs & demo
|
|
app.get('/', (req, res) => {
|
|
res.send(`<!DOCTYPE html>
|
|
<html lang="pt-BR">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
|
<title>@pulse-libs/core</title>
|
|
<style>
|
|
:root{--bg:#0a0a0f;--surface:#1a1a24;--text:#e4e4eb;--muted:#8888a0;--primary:#6c5ce7;--accent:#00cec9;--gr:#00dfa2;--border:rgba(255,255,255,.07);--r:10px}
|
|
*{margin:0;padding:0;box-sizing:border-box}
|
|
body{font-family:system-ui,sans-serif;background:var(--bg);color:var(--text);min-height:100vh;padding:24px}
|
|
.w{max-width:860px;margin:0 auto}
|
|
h1{font-size:2.2rem;font-weight:800;letter-spacing:-.03em;margin-bottom:4px}
|
|
h1 span{background:linear-gradient(90deg,var(--primary),var(--accent));-webkit-background-clip:text;-webkit-text-fill-color:transparent}
|
|
.sub{color:var(--muted);margin-bottom:30px;font-size:.9rem}
|
|
.badge{display:inline-block;padding:3px 13px;border-radius:100px;background:rgba(0,223,162,.12);border:1px solid rgba(0,223,162,.3);color:var(--gr);font-size:.73rem;font-weight:700;margin-bottom:14px}
|
|
.c{background:var(--surface);border:1px solid var(--border);border-radius:var(--r);padding:20px;margin-bottom:12px}
|
|
.c h3{font-size:.95rem;font-weight:700;margin-bottom:8px;color:var(--accent)}
|
|
.c p{font-size:.85rem;color:var(--muted);line-height:1.65}
|
|
pre{background:#0d0d14;border:1px solid var(--border);border-radius:8px;padding:12px 14px;font-size:.77rem;line-height:1.7;overflow-x:auto;color:var(--accent);font-family:monospace;margin-top:8px}
|
|
.c2{display:grid;grid-template-columns:1fr 1fr;gap:14px}
|
|
@media(max-width:680px){.c2{grid-template-columns:1fr}}
|
|
.x{color:var(--gr);font-family:monospace;font-size:.83rem}
|
|
.tg{display:inline-block;padding:3px 9px;border-radius:6px;font-size:.72rem;font-weight:700;margin:2px}
|
|
.p{background:rgba(108,92,231,.15);color:var(--primary);border:1px solid rgba(108,92,231,.3)}
|
|
.a{background:rgba(0,206,201,.12);color:var(--accent);border:1px solid rgba(0,206,201,.3)}
|
|
.gr{background:rgba(0,223,162,.12);color:var(--gr);border:1px solid rgba(0,223,162,.3)}
|
|
footer{text-align:center;padding:28px;color:var(--muted);font-size:.78rem}
|
|
footer a{color:var(--primary)}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="w">
|
|
<div class="badge">🟢 LIVE — Docker Swarm · Caddy · ESM · <span id="uptime"></span></div>
|
|
<h1>@pulse-libs<span style="color:var(--accent)">/</span>core</h1>
|
|
<p class="sub">Biblioteca Universal Atomizada · React + Vue + Utils + Hooks + Validators — v1.0.0-beta.1</p>
|
|
<div class="c2">
|
|
<div class="c">
|
|
<h3>📦 Quick Import</h3>
|
|
<pre>import { <span class="x">cn</span>, <span class="x">debounce</span>, <span class="x">useToggle</span>, <span class="x">emailSchema</span> } from '@pulse-libs/core';
|
|
import { <span class="x">Button</span>, <span class="x">Input</span>, <span class="x">Alert</span> } from '@pulse-libs/core/components';
|
|
import { <span class="x">date</span>, <span class="x">storage</span>, <span class="x">str</span> } from '@pulse-libs/core/utils';</pre>
|
|
</div>
|
|
<div class="c">
|
|
<h3>🏗️ Arquitetura em camadas</h3>
|
|
<p style="margin-top:14px;margin-bottom:10px">Dependência única flui de baixo pra cima:</p>
|
|
<div style="display:flex;align-items:center;gap:8px;flex-wrap:wrap">
|
|
<span class="tg gr">utils / types</span><span style="color:var(--muted)">←</span>
|
|
<span class="tg a">validators</span><span style="color:var(--muted)">←</span>
|
|
<span class="tg p">hooks</span><span style="color:var(--muted)">→</span>
|
|
<span class="tg" style="background:rgba(253,172,65,.12);color:#fdac41;border:1px solid rgba(253,172,65,.3)">components</span>
|
|
</div>
|
|
<p style="margin-top:12px;font-size:.8rem;color:var(--muted)">Zero deps em utils/types. Zod é a única fonte de verdade para validação.</p>
|
|
</div>
|
|
</div>
|
|
<div class="c">
|
|
<h3>🧪 Testes & Build</h3>
|
|
<p><span class="tg gr">57/57 ✅</span>
|
|
<span style="color:var(--muted)">vitest · jsdom · 100% coverage utils+validators</span></p>
|
|
<pre>npm test # 57 testes passando
|
|
npm run typecheck # tsc --noEmit
|
|
npm run build # tsup → ESM + CJS + DTS + sourcemaps</pre>
|
|
</div>
|
|
<div class="c">
|
|
<h3>🔗 API Endpoints (live)</h3>
|
|
<div style="display:grid;grid-template-columns:1fr 1fr 1fr 1fr;gap:8px;margin-top:10px">
|
|
<span class="tg gr">GET /health</span>
|
|
<span class="tg a">GET /api/now</span>
|
|
<span class="tg p">GET /api/utils</span>
|
|
<span class="tg gr">GET /api/validators</span>
|
|
</div>
|
|
<pre id="health-demo" style="margin-top:12px">Carregando…</pre>
|
|
</div>
|
|
<footer>
|
|
<p>© 2026 Octal Technology · <a href="https://git.octal.tec.br/Roberto/pulse-libs">pulse-libs no Gitea</a></p>
|
|
<p style="margin-top:4px;color:var(--muted)">React · Vue · Zod · TypeScript · tsup v8 · Docker Swarm · Caddy Proxy</p>
|
|
</footer>
|
|
</div>
|
|
<script>
|
|
fetch('/health').then(r=>r.json()).then(d=>{
|
|
document.getElementById('uptime').textContent = '· uptime ' + d.uptime;
|
|
document.getElementById('health-demo').textContent = JSON.stringify(d, null, 2);
|
|
}).catch(()=>{
|
|
document.getElementById('health-demo').textContent = '<!-- offline -->';
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>`);
|
|
});
|
|
|
|
const PORT = parseInt(process.env.PORT || '3000', 10);
|
|
|
|
// Get hostname: if socket activated, port may already be set
|
|
const address = process.env.NODE_ENV === 'test' ? '127.0.0.1' : '0.0.0.0';
|
|
|
|
app.listen(PORT, address, () => {
|
|
console.log(`@pulse-libs API → http://\${address}:\${PORT}`);
|
|
});
|