npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

thrive_utils

v0.0.1

Published

Componentes React 18 personalizados: formularios dinámicos, servicios API y autenticación JWT

Readme

thrive_utils

Librería React con componentes de formulario dinámico, servicios HTTP y autenticación JWT.

Instalación

npm install thrive_utils

Peer Dependencies

npm install react react-dom @mui/material @emotion/react @emotion/styled axios jwt-decode qs

Módulos


📋 FieldDef — Definición de campos

Clase builder para definir campos de formulario de forma declarativa.

Tipos

type FieldType = 'input' | 'select' | 'checkbox' | 'textarea' | 'date';

interface FieldOption {
    label: string;
    value: string | number;
}

Atributos

| Atributo | Tipo | Default | Descripción | |---|---|---|---| | type | FieldType | — | Tipo de campo | | key | string | — | Identificador único del campo | | label | string? | undefined | Etiqueta visible | | placeholder | string? | undefined | Texto placeholder | | typeFormat | string? | 'text' | Subtipo HTML (email, password, etc.) | | required | boolean | false | Si es obligatorio | | options | FieldOption[] | [] | Opciones para campos select | | width | string \| number? | undefined | Ancho personalizado | | rows | number | 4 | Filas para campos textarea | | defaultChecked | boolean | false | Estado inicial para checkbox |

Métodos builder

Todos retornan this para encadenamiento.

| Método | Firma | Descripción | |---|---|---| | setRequired | (value?: boolean): this | Marca como obligatorio | | setOptions | (items: Record[], labelKey?: string, valueKey?: string): this | Carga opciones de un array de objetos | | setWidth | (value: string \| number): this | Establece ancho | | setRows | (value: number): this | Establece filas del textarea | | setDefaultChecked | (value?: boolean): this | Establece checked por defecto |

Ejemplo

import { FieldDef } from 'thrive_utils';

const campos = [
    new FieldDef('input', 'nombre', 'Nombre completo', 'Ingresá tu nombre')
        .setRequired(),

    new FieldDef('input', 'email', 'Email', '', 'email')
        .setRequired()
        .setWidth('300px'),

    new FieldDef('select', 'pais', 'País', 'Seleccioná...')
        .setRequired()
        .setOptions(paises, 'nombre', 'id'),

    new FieldDef('date', 'fechaNac', 'Fecha de nacimiento'),

    new FieldDef('textarea', 'observaciones', 'Observaciones', 'Notas...')
        .setRows(6),

    new FieldDef('checkbox', 'acepta', 'Acepto los términos')
        .setDefaultChecked(false),
];

🧩 Formulario — Componente de formulario dinámico

Renderiza una lista de FieldDef como campos MUI interactivos.

Props (FormularioProps)

interface FormularioProps {
    fields: FieldDef[];
    valores: Record<string, any>;
    onInputChange: (campo: string, valor: any) => void;
    tieneError: (campo: string) => boolean;
    obtenerError: (campo: string) => string;
    className?: string;
    slots?: Record<string, React.ReactNode>;
}

| Prop | Tipo | Requerido | Descripción | |---|---|---|---| | fields | FieldDef[] | ✅ | Definiciones de campos a renderizar | | valores | Record<string, any> | ✅ | Valores actuales del formulario | | onInputChange | (campo, valor) => void | ✅ | Callback cuando cambia un campo | | tieneError | (campo) => boolean | ✅ | Retorna si un campo tiene error | | obtenerError | (campo) => string | ✅ | Retorna el mensaje de error | | className | string? | ❌ | Clase CSS adicional para el contenedor | | slots | Record<string, ReactNode>? | ❌ | Contenido personalizado por campo |

Ejemplo

import { Formulario, FieldDef, useForm } from 'thrive_utils';

function MiFormulario() {
    const campos = [
        new FieldDef('input', 'nombre', 'Nombre').setRequired(),
        new FieldDef('select', 'rol', 'Rol').setOptions([
            { id: 1, name: 'Admin' },
            { id: 2, name: 'Editor' },
        ]),
    ];

    const { valores, handleChange, tieneError, obtenerError } = useForm({
        nombre: '',
        rol: '',
    });

    return (
        <Formulario
            fields={campos}
            valores={valores}
            onInputChange={handleChange}
            tieneError={tieneError}
            obtenerError={obtenerError}
            className="grid grid-cols-2"
        />
    );
}

Slots personalizados

Reemplazá cualquier campo con contenido propio:

<Formulario
    fields={campos}
    valores={valores}
    onInputChange={handleChange}
    tieneError={tieneError}
    obtenerError={obtenerError}
    slots={{
        avatar: <MiComponenteAvatar />,
    }}
/>

🪝 useForm<T> — Hook de gestión de formularios

Hook React para manejar estado, errores de backend y notificaciones de formularios.

Tipos

type TipoNotificacion = 'success' | 'error' | 'info' | 'warning';

interface NotificacionState {
    abierta: boolean;
    mensaje: string;
    tipo: TipoNotificacion;
}

interface UseFormReturn<T> {
    valores: T;
    enviando: boolean;
    setEnviando: (value: boolean) => void;
    handleChange: (campo: string, valor: unknown) => void;
    capturarErrores: (error: BackendError) => void;
    tieneError: (campo: string) => boolean;
    obtenerError: (campo: string) => string;
    reset: () => void;
    notificacion: NotificacionState;
    abrirNotificacion: (mensaje: string, tipo?: TipoNotificacion) => void;
    cerrarNotificacion: () => void;
}

Retorno

| Propiedad | Tipo | Descripción | |---|---|---| | valores | T | Estado actual del formulario | | enviando | boolean | Flag de envío en progreso | | setEnviando | (boolean) => void | Controla el estado de envío | | handleChange | (campo, valor) => void | Actualiza un campo (y limpia su error) | | capturarErrores | (error) => void | Parsea errores de backend { response.data.errors } | | tieneError | (campo) => boolean | Verifica si un campo tiene error | | obtenerError | (campo) => string | Obtiene el mensaje de error | | reset | () => void | Restaura valores y errores al estado inicial | | notificacion | NotificacionState | Estado de la notificación | | abrirNotificacion | (mensaje, tipo?) => void | Muestra notificación (default: 'success') | | cerrarNotificacion | () => void | Cierra la notificación |

Ejemplo

import { useForm } from 'thrive_utils';

function CrearUsuario() {
    const { valores, handleChange, capturarErrores, tieneError, obtenerError,
            enviando, setEnviando, abrirNotificacion, reset } = useForm({
        nombre: '',
        email: '',
    });

    const guardar = async () => {
        setEnviando(true);
        try {
            await api.post('/usuarios', valores);
            abrirNotificacion('Usuario creado', 'success');
            reset();
        } catch (error) {
            capturarErrores(error);
        } finally {
            setEnviando(false);
        }
    };
}

🌐 ApiService — Cliente HTTP configurable

Wrapper sobre Axios con interceptores de autenticación, manejo de errores y soporte para uploads con FormData.

Configuración (ApiServiceConfig)

interface ApiServiceConfig {
    baseURL: string;
    getToken?: () => string | null;
    onUnauthorized?: () => void;
    onForbidden?: () => void;
}

| Propiedad | Tipo | Requerido | Descripción | |---|---|---|---| | baseURL | string | ✅ | URL base de la API | | getToken | () => string \| null | ❌ | Función que retorna el JWT actual | | onUnauthorized | () => void | ❌ | Callback en respuesta HTTP 401 | | onForbidden | () => void | ❌ | Callback en respuesta HTTP 403 |

Atributos

| Atributo | Tipo | Default | Descripción | |---|---|---|---| | mostrarMensajes | boolean | true | Habilita logs de errores 404/422 en consola |

Métodos

| Método | Firma | Retorna | Descripción | |---|---|---|---| | get | <T>(uri, params?) | Promise<AxiosResponse<T>> | GET con respuesta Axios completa | | getData | <T>(uri, params?) | Promise<T> | GET → response.data.data | | getRaw | <T>(uri, params?) | Promise<T> | GET → response.data | | getRawData | (uri, responseType, params?) | Promise<AxiosResponse> | GET con responseType (blob, arraybuffer) | | getAll | <T>(uri, params?) | Promise<AxiosResponse<T>> | GET con limit=0 | | getAllData | <T>(uri, params?) | Promise<T> | GET con limit=0response.data.data | | post | <T>(uri, body, options?) | Promise<T> | POST (auto-detecta FileFormData) | | put | <T>(uri, body, options?) | Promise<T> | PUT (auto-detecta FileFormData) | | patch | <T>(uri, body, options?) | Promise<AxiosResponse<T>> | PATCH | | delete | <T>(uri) | Promise<T> | DELETE |

Ejemplo

import { createApiService } from 'thrive_utils';

const api = createApiService({
    baseURL: 'https://mi-api.com/api',
    getToken: () => sessionStorage.getItem('accessToken'),
    onUnauthorized: () => window.location.href = '/login',
    onForbidden: () => alert('Sin permisos'),
});

// GET simple
const usuarios = await api.getData<Usuario[]>('/usuarios');

// POST con archivo
const file = new File([blob], 'foto.jpg');
await api.post('/documentos', { titulo: 'Mi doc', archivo: file });

// GET con parámetros
const resultado = await api.getData('/buscar', { q: 'juan', page: 1 });

🔐 AuthService — Servicio de autenticación JWT

Gestiona sesión de usuario, tokens JWT y verificación de roles.

Tipos

interface RolUsuario {
    id: number;
    nombre: string;
}

interface User {
    id: number;
    nombreCompleto: string;
    email?: string;
    roles: RolUsuario[];
}

Métodos

| Método | Firma | Retorna | Descripción | |---|---|---|---| | setAccessToken | (token: string) | JWTPayload | Decodifica JWT, guarda token y usuario | | setRefreshToken | (token: string) | void | Guarda refresh token | | getAccessToken | () | string \| null | Retorna access token | | getRefreshToken | () | string \| null | Retorna refresh token | | getToken | () | string \| null | Alias de getAccessToken | | getUser | () | User \| null | Retorna usuario actual | | isAuthenticated | () | boolean | Verifica si hay sesión activa | | estaLogueado | () | boolean | Alias de isAuthenticated | | removeSessionData | () | void | Limpia todos los datos de sesión | | setState | (response) | void | Establece sesión desde respuesta del backend | | getRolUsuario | () | RolUsuario \| null | Retorna el primer rol del usuario | | getRolUsuarios | () | RolUsuario[] | Retorna todos los roles | | hasRole | (nombre: string) | boolean | Verifica si el usuario tiene un rol | | setSessionData | (authData) | void | Establece sesión desde objeto { user, token } |

Ejemplo

import { AuthService } from 'thrive_utils';

const auth = new AuthService();

// Después del login
auth.setState({
    access_token: tokenDelBackend,
    refresh_token: refreshTokenDelBackend,
});

// Verificar sesión
if (auth.isAuthenticated()) {
    const user = auth.getUser();
    console.log(`Hola, ${user.nombreCompleto}`);
}

// Verificar roles
if (auth.hasRole('admin')) {
    // mostrar panel de administración
}

// Logout
auth.removeSessionData();

También se exporta una instancia singleton:

import { authService } from 'thrive_utils';

// Uso directo sin instanciar
authService.isAuthenticated();

Scripts

npm test               # Ejecutar tests
npm run test:coverage  # Tests + reporte de cobertura
npm run build:lib      # Build del paquete (ESM + CJS + tipos)

Exports

// Componentes
export { Formulario, FieldDef, useForm }

// Servicios
export { ApiService, createApiService, AuthService, authService }

// Tipos
export type {
    FormularioProps,    // Props del componente Formulario
    FieldType,          // 'input' | 'select' | 'checkbox' | 'textarea' | 'date'
    FieldOption,        // { label, value }
    TipoNotificacion,  // 'success' | 'error' | 'info' | 'warning'
    NotificacionState,  // Estado de notificación
    UseFormReturn,      // Retorno del hook useForm
    ApiServiceConfig,   // Configuración de ApiService
    RolUsuario,         // { id, nombre }
    User,               // Usuario autenticado
}

Requisitos

  • Node.js ≥ 20.x
  • npm ≥ 10.x
  • React ≥ 18
  • MUI ≥ 7

Cobertura de Tests

| Módulo          | Stmts | Branch | Funcs | Lines |
|-----------------|-------|--------|-------|-------|
| FieldDef.ts     | 100%  | 100%   | 100%  | 100%  |
| Formulario.tsx  | 100%  | 100%   | 100%  | 100%  |
| useForm.ts      | 100%  | 100%   | 100%  | 100%  |
| api-service.ts  | 100%  | 100%   | 100%  | 100%  |
| auth-service.ts | 100%  | 100%   | 100%  | 100%  |