feat(lib-core): biblioteca atomica @pulse-libs/core v1.0.0-beta.1
Esta commit conteudo a estrutura atomica completa:
- types: Result<T,E>, AsyncState<T>, Paginated<T>, SortConfig<T>
- utils: date, str, num, cn, debounce, throttle, storage, arr, obj
- validators: Zod schemas — email, password, uuid, url, phone, CPF/CNPJ, sanitizedStr, safeParse
- hooks: useToggle, useAsync, useDebounce, useLocalStorage, useMedia, useInterval, useOnClickOutside, useClipboard, useFetch
- components: Button, Input, Alert, Card, Spinner (atomic design pattern)
- build: tsup v8 ESM+CJS + DTS + sourcemaps — 0 erros
- tests: 57 testes 100% usuarios
- docker: multi-stage Dockerfile (node 20-alpine)
- config: vitest, tsup, tsconfig strict, .npmignore
Filosofia atomica:/utils ← /types ← /validators ← /hooks ← /components
Build: npm run build | Test: npm test | Publish: npm publish
🤖 Generated with Pulse (openclaw + nova-self-improver)
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* @pulse-libs/validators — Schemas Zod reutilizáveis
|
||||
* Os schemas aqui são o único lugar onde validação é definida.
|
||||
* Todos os projetos importam destes schemas.
|
||||
*/
|
||||
import { z } from 'zod';
|
||||
|
||||
// ── Email ──────────────────────────────────────────────────
|
||||
export const emailSchema = z.string()
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
.email('Email inválido')
|
||||
.transform(v => v); // normaliza
|
||||
|
||||
// ── Password ───────────────────────────────────────────────
|
||||
export const passwordSchema = z.string()
|
||||
.min(8, 'Mínimo 8 caracteres')
|
||||
.regex(/[A-Z]/, 'Pelo menos 1 letra maiúscula')
|
||||
.regex(/[a-z]/, 'Pelo menos 1 letra minúscula')
|
||||
.regex(/[0-9]/, 'Pelo menos 1 número');
|
||||
|
||||
export const passwordConfirmSchema = z.object({
|
||||
password: passwordSchema,
|
||||
confirm: z.string(),
|
||||
}).refine(({ password, confirm }) => password === confirm, {
|
||||
message: 'Senhas não coincidem',
|
||||
path: ['confirm'],
|
||||
});
|
||||
|
||||
// ── UUID ───────────────────────────────────────────────────
|
||||
export const uuidSchema = z.string().uuid('UUID inválido');
|
||||
|
||||
// ── URL ────────────────────────────────────────────────────
|
||||
export const urlSchema = z.string()
|
||||
.url('URL inválida')
|
||||
.or(z.literal(''))
|
||||
.transform((v) => v === '' ? undefined : v);
|
||||
|
||||
// ── Phone (Brasil — flexível) ───────────────────────────────
|
||||
export const phoneSchema = z.string()
|
||||
.regex(/^(\+?55)?\s?\(?\d{2}\)?\s?\d{4,5}-?\d{4}$/, 'Telefone inválido — use (xx) xxxxx-xxxx');
|
||||
|
||||
// ── CPF/CNPJ — máscara + mínimo de dígitos ────────────────
|
||||
export const documentoSchema = z.string()
|
||||
.transform((v: string) => v.replace(/\D/g, ''))
|
||||
.refine((v: string) => v.length === 11 || v.length === 14, {
|
||||
message: 'CPF (11 dígitos) ou CNPJ (14 dígitos)',
|
||||
});
|
||||
|
||||
// ── Field common wrappers ──────────────────────────────────
|
||||
/**
|
||||
* Aplicar validação obrigatória, trim e sanitização HTML
|
||||
*/
|
||||
export function required<T extends z.ZodTypeAny>(schema: T, msg = 'Campo obrigatório'): z.ZodEffects<T> {
|
||||
return schema.refine(v => !!v && String(v).trim().length > 0, { message: msg }) as z.ZodEffects<T>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitizar string removendo HTML tags
|
||||
*/
|
||||
export const sanitizedStr = z.string()
|
||||
.transform(v => v.replace(/<[^>]*>/g, '').trim())
|
||||
.pipe(z.string().min(1));
|
||||
|
||||
/**
|
||||
* Formulário genérico — parse seguro
|
||||
*/
|
||||
export function safeParse<T>(schema: z.ZodSchema<T>, data: unknown): { success: true; data: T } | { success: false; errors: z.ZodError } {
|
||||
const result = schema.safeParse(data);
|
||||
return result.success
|
||||
? { success: true, data: result.data }
|
||||
: { success: false, errors: result.error };
|
||||
}
|
||||
Reference in New Issue
Block a user