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

@hemia/vue-core

v0.0.5

Published

Vue 3 utility library with declarative form validation using decorators, formatters, and custom validators. Build type-safe forms faster with automatic validation.

Downloads

65

Readme

@hemia/vue-core

npm version License: ISC

Librería de utilidades Vue.js 3 que proporciona un sistema robusto de formularios con validación automática basada en decoradores, funciones de formateo y validadores personalizados.


✨ Características

  • 🎯 Formularios automáticos con decoradores TypeScript
  • Validación declarativa síncrona y asíncrona
  • 🎨 Funciones de formateo (texto, fechas, moneda, números)
  • 🔍 Validadores personalizados (URLs, slugs, SemVer, contraseñas)
  • 🚀 Type-safe con TypeScript
  • ⚡️ Optimizado con debounce para validaciones asíncronas
  • 🔥 Compatible con Vue 3 y Composition API

📦 Instalación

npm install @hemia/vue-core @vuelidate/core @vuelidate/validators vue

Peer Dependencies

  • vue: ^3.0.0
  • @vuelidate/core: ^2.0.0
  • @vuelidate/validators: ^2.0.0

🚀 Uso Rápido

1. Formularios con Decoradores

Define un modelo de datos con validaciones usando decoradores:

import { field } from '@hemia/vue-core';

class LoginDTO {
  @field({
    required: true,
    type: 'email',
    label: 'Correo electrónico',
    messages: {
      required: 'El email es obligatorio',
      pattern: 'Ingresa un email válido'
    }
  })
  email: string = '';

  @field({
    required: true,
    min: 8,
    label: 'Contraseña',
    messages: {
      required: 'La contraseña es obligatoria',
      min: 'Mínimo 8 caracteres'
    }
  })
  password: string = '';
}

Usa el composable useAutoForm en tu componente:

<script setup lang="ts">
import { useAutoForm } from '@hemia/vue-core';

const { form, validate, getError, hasError, isPending, reset } = useAutoForm(LoginDTO, {
  $autoDirty: true
});

const handleSubmit = async () => {
  const isValid = await validate();
  if (isValid) {
    console.log('Datos válidos:', form);
    // Enviar datos al servidor
  }
};
</script>

<template>
  <form @submit.prevent="handleSubmit">
    <div>
      <input 
        v-model="form.email" 
        type="email" 
        placeholder="Correo electrónico"
        :class="{ error: hasError('email') }"
      />
      <span v-if="hasError('email')" class="error-message">
        {{ getError('email') }}
      </span>
    </div>

    <div>
      <input 
        v-model="form.password" 
        type="password" 
        placeholder="Contraseña"
        :class="{ error: hasError('password') }"
      />
      <span v-if="hasError('password')" class="error-message">
        {{ getError('password') }}
      </span>
    </div>

    <button type="submit" :disabled="isPending()">
      {{ isPending() ? 'Validando...' : 'Iniciar sesión' }}
    </button>
    <button type="button" @click="reset">Limpiar</button>
  </form>
</template>

2. Validación Asíncrona

import { field } from '@hemia/vue-core';

class RegistroDTO {
  @field({
    required: true,
    min: 3,
    label: 'Nombre de usuario',
    async: {
      validator: async (value: string) => {
        // Simula llamada a API para verificar disponibilidad
        const response = await fetch(`/api/check-username/${value}`);
        return response.ok;
      },
      message: 'Este nombre de usuario ya está en uso',
      debounce: 500 // Espera 500ms antes de validar
    }
  })
  username: string = '';
}

3. Funciones de Formateo

import { 
  capitalize, 
  formatCurrency, 
  formatDateTime,
  getRelativeTime,
  truncate,
  getInitials 
} from '@hemia/vue-core';

// Texto
capitalize('hOLA MUNDO'); // "Hola mundo"
truncate('Texto muy largo...', 10); // "Texto muy ..."
getInitials('Cristian Mendez'); // "CM"

// Moneda
formatCurrency(1250.50, 'USD', 'es-MX'); // "$1,250.50"
formatCurrency(1250.50, 'MXN', 'es-MX'); // "$1,250.50"

// Fechas
formatDateTime(new Date()); // "18 feb 2026, 10:30 a.m."
getRelativeTime(new Date(Date.now() - 3600000)); // "hace 1 hora"
getRelativeTime(new Date(Date.now() - 172800000), 'en'); // "2 days ago"

📚 API Reference

Decorador @field(config)

Define reglas de validación para una propiedad de clase.

Configuración

| Propiedad | Tipo | Descripción | |-----------|------|-------------| | required | boolean | Campo obligatorio | | requiredIf | string \| function | Campo requerido si condición se cumple | | requiredUnless | string \| function | Campo requerido a menos que condición se cumple | | type | FieldType | Tipo de campo: 'string', 'email', 'url', 'slug', 'semver', 'number', 'boolean', 'date', 'password', 'phone', 'rfc', 'curp' | | label | string | Etiqueta descriptiva del campo | | minLength | number | Longitud mínima (para strings) | | maxLength | number | Longitud máxima (para strings) | | min | number | Valor mínimo (para números) | | max | number | Valor máximo (para números) | | between | [number, number] | Rango de valores permitidos | | greaterThan | string | Campo debe ser mayor que otro campo | | lessThan | string | Campo debe ser menor que otro campo | | greaterThanOrEqual | string | Campo debe ser mayor o igual que otro campo | | lessThanOrEqual | string | Campo debe ser menor o igual que otro campo | | notEqualTo | string | Campo no puede ser igual a otro campo | | sameAs | string | Campo debe coincidir con otro campo (útil para confirmación) | | minDate | Date \| string | Fecha mínima permitida | | maxDate | Date \| string | Fecha máxima permitida | | strictPassword | boolean | Valida contraseña fuerte (min 8 chars, mayús, minús, número, símbolo) | | transform | TransformType \| function | Transforma el valor automáticamente: 'trim', 'lowercase', 'uppercase', 'capitalize' | | enum | string[] | Valores permitidos | | async | AsyncValidationConfig | Configuración de validación asíncrona | | regex | RegExp | Expresión regular custom | | messages | object | Mensajes de error personalizados |

AsyncValidationConfig

interface AsyncValidationConfig {
  validator: (value: any) => Promise<boolean>;
  message?: string;
  debounce?: number; // En milisegundos (default: 500)
}

Composable useAutoForm<T>

Crea un formulario reactivo con validación automática.

Parámetros

  • SchemaClass: Constructor de la clase con decoradores @field
  • options: Configuración opcional de Vuelidate

Retorna

{
  form: T;                          // Estado reactivo del formulario
  v$: Ref<Validation>;              // Instancia de Vuelidate
  validate: () => Promise<boolean>; // Valida todo el formulario
  validateField: (field: keyof T) => Promise<boolean>; // Valida un campo específico
  validateFields: (fields: (keyof T)[]) => Promise<boolean>; // Valida múltiples campos
  reset: () => Promise<void>;       // Resetea formulario y validación
  getError: (field: keyof T) => string; // Obtiene el primer error de un campo
  getAllErrors: () => Record<string, string[]>; // Obtiene todos los errores
  hasError: (field: keyof T) => boolean; // Verifica si un campo tiene error
  hasErrors: Ref<boolean>;          // Verifica si el formulario tiene errores
  isValid: Ref<boolean>;            // Formulario es válido
  isDirty: Ref<boolean>;            // Formulario fue modificado
  isPending: (field?: keyof T) => boolean; // Verifica si hay validación pendiente
  setFieldValue: (field: keyof T, value: any) => void; // Establece valor de un campo
  setFieldTouched: (field: keyof T, touched?: boolean) => void; // Marca campo como tocado
  touch: (field: keyof T) => void; // Marca un campo como "tocado"
  refs: ToRefs<T>;                  // Referencias reactivas individuales
}

Funciones de Formateo

Texto

capitalize(text: string): string
titleCase(text: string): string
sentenceCase(text: string): string
snakeCase(text: string): string
camelCase(text: string): string
kebabCase(text: string): string
truncate(text: string, length?: number, suffix?: string): string
getInitials(name: string): string

Números y Moneda

formatCurrency(amount: number | string, currency?: string, locale?: string): string
formatCompactNumber(num: number): string // "1.5K", "2M"
formatBytes(bytes: number, decimals?: number): string // "1.5 MB"

Fechas

formatDateShort(date: Date | string | number, locale?: string): string
formatDateTime(date: Date | string | number, locale?: string): string
formatTime(date: Date | string | number, locale?: string): string
getRelativeTime(date: Date | string | number, locale?: 'es' | 'en'): string

Validadores

matchesValue(compareValue): Validator    // Valida que dos valores coincidan

// Validadores numéricos
isBetween(min, max): Validator           // Valor entre rango
minValue(min): Validator                 // Valor mínimo
maxValue(max): Validator                 // Valor máximo
isGreaterThan(compareValue): Validator   // Mayor que otro campo
isLessThan(compareValue): Validator      // Menor que otro campo
isGreaterThanOrEqual(compareValue): Validator // Mayor o igual
isLessThanOrEqual(compareValue): Validator    // Menor o igual
isNotEqualTo(compareValue): Validator    // Diferente a otro campo

// Validadores de fecha
isAfterDate(minDate): Validator          // Fecha posterior a mínima
isBeforeDate(maxDate): Validator         // Fecha anterior a máxima
isFutureDate(value): boolean             // Fecha en el futuro
isPastDate(value): boolean               // Fecha en el pasado
isMinAge(minAge): Validator              // Edad mínima
isMaxAge(maxAge): Validator              // Edad máxima

// Validadores locales (México)
isRFC(value): boolean                    // RFC mexicano
isCURP(value): boolean                   // CURP mexicana
isPhoneMX(value): boolean                // Teléfono mexicano
isPostalCodeMX(value): boolean           // Código postal MX

// Validadores internacionales
isCreditCard(value): boolean             // Tarjeta de crédito (Luhn)
isSSN(value): boolean                    // SSN estadounidense
isPhoneInternational(value): boolean     // Teléfono formato E.164

// Validadores asíncronos
createDebouncedValidator(fn: AsyncValidatorFn, delay?: number): AsyncValidatorFn

🎯 Ejemplos Avanzados

1. Validación de Contraseñas Coincidentes

import { field } from '@hemia/vue-core';

export class RegisterForm {
  @field({
    required: true,
    type: 'password',
    strictPassword: true,
    minLength: 8,
    messages: {
      required: 'La contraseña es obligatoria',
    }
  })
  password!: string;

  @field({
    required: true,
    type: 'password',
    sameAs: 'password', // 👈 Valida que coincida con password
    messages: {
      required: 'Confirma tu contraseña',
      sameAs: 'Las contraseñas no coinciden',
    }
  })
  confirmPassword!: string;
}

2. Validaciones Condicionales

export class OrderForm {
  @field({ required: true })
  orderType!: string; // 'pickup' | 'delivery'

  @field({
    requiredIf: 'orderType', // Requerido si orderType tiene valor
    // o con función:
    // requiredIf: (instance) => instance.orderType === 'delivery',
    messages: {
      requiredIf: 'La dirección es requerida para envío',
    }
  })
  address!: string;

  @field({
    requiredUnless: (instance) => instance.orderType === 'pickup',
    messages: {
      requiredUnless: 'El código postal es necesario',
    }
  })
  zipCode!: string;
}

3. Validaciones Numéricas y Comparaciones

export class ProductForm {
  @field({
    required: true,
    type: 'number',
    min: 0,
    max: 999999,
    messages: {
      min: 'El precio debe ser mayor o igual a 0',
      max: 'El precio no puede exceder 999,999',
    }
  })
  price!: number;

  @field({
    type: 'number',
    between: [1, 100],
    messages: {
      between: 'El descuento debe estar entre 1% y 100%',
    }
  })
  discount!: number;

  @field({
    type: 'number',
    greaterThan: 'price', // Debe ser mayor que el campo 'price'
    messages: {
      greaterThan: 'El precio final debe ser mayor al precio base',
    }
  })
  finalPrice!: number;
}

4. Validaciones de Fecha

export class EventForm {
  @field({
    required: true,
    type: 'date',
    minDate: new Date(), // No puede ser en el pasado
    messages: {
      minDate: 'La fecha del evento debe ser futura',
    }
  })
  eventDate!: Date;

  @field({
    type: 'date',
    maxDate: new Date('2026-12-31'),
    messages: {
      maxDate: 'La fecha no puede ser posterior a 2026',
    }
  })
  expirationDate!: Date;
}

5. Validadores Locales (México)

export class PersonForm {
  @field({
    required: true,
    type: 'phone', // Valida teléfono mexicano
    messages: {
      pattern: 'Ingresa un teléfono válido (10 dígitos)',
    }
  })
  phone!: string;

  @field({
    required: true,
    type: 'rfc', // Valida RFC mexicano
    messages: {
      pattern: 'RFC inválido',
    }
  })
  rfc!: string;

  @field({
    type: 'curp', // Valida CURP mexicana
    messages: {
      pattern: 'CURP inválida',
    }
  })
  curp!: string;
}

6. Transformadores de Valores

export class UserForm {
  @field({
    required: true,
    type: 'email',
    transform: ['trim', 'lowercase'], // 👈 Aplica automáticamente
  })
  email!: string;

  @field({
    required: true,
    transform: 'capitalize', // Capitaliza cada palabra
  })
  fullName!: string;

  @field({
    transform: (value) => value?.toUpperCase().trim(), // Custom
  })
  code!: string;
}

7. Mensajes Globales

import { setGlobalMessages } from '@hemia/vue-core';

// En main.ts o app setup
setGlobalMessages({
  required: 'Campo obligatorio',
  email: 'Email inválido',
  minLength: 'Mínimo {0} caracteres',
  maxLength: 'Máximo {0} caracteres',
});

8. useAutoForm Extendido

import { useAutoForm } from '@hemia/vue-core';

const {
  form,
  v$,
  
  // Validación
  validate,           // Valida todo el formulario
  validateField,      // Valida un campo específico
  validateFields,     // Valida múltiples campos
  
  // Estado
  hasError,          // Campo tiene error
  hasErrors,         // Formulario tiene errores
  isValid,           // Formulario válido
  isDirty,           // Formulario modificado
  
  // Manipulación
  setFieldValue,     // Establece valor
  setFieldTouched,   // Marca como tocado
  reset,             // Resetea formulario
  
  // Errores
  getError,          // Primer error de campo
  getAllErrors,      // Todos los errores
} = useAutoForm(RegisterForm);

// Validar solo campos específicos
await validateFields(['email', 'password']);

// Cambiar valor programáticamente
setFieldValue('email', '[email protected]');

// Obtener todos los errores
const errors = getAllErrors();
console.log(errors); // { email: ['Email inválido'], password: [...] }
isUrl(value: string): boolean            // URLs con http/https
isStrictUrl(value: string): boolean      // Validación más estricta
isEmail(value: string): boolean
isStrongPassword(value: string): boolean // Min 8 chars, mayús, minús, número, especial
isOneOf(options: string[]): Validator    // Valida contra enum

// Validadores asíncronos
createDebouncedValidator(fn: AsyncValidatorFn, delay?: number): AsyncValidatorFn

🎯 Casos de Uso

Formulario de Registro Completo

class RegisterDTO {
  @field({
    required: true,
    min: 3,
    max: 20,
    type: 'slug',
    label: 'Usuario',
    async: {
      validator: checkUsernameAvailability,
      message: 'Este usuario ya existe',
      debounce: 500
    }
  })
  username: string = '';

  @field({
    required: true,
    type: 'email',
    label: 'Email',
    async: {
      validator: checkEmailAvailability,
      message: 'Este email ya está registrado'
    }
  })
  email: string = '';

  @field({
    required: true,
    min: 8,
    label: 'Contraseña'
  })
  password: string = '';

  @field({
    required: true,
    label: 'Confirmación'
  })
  confirmPassword: string = '';
}

Formateo en Templates

<template>
  <div>
    <h2>{{ capitalize(user.name) }}</h2>
    <p>Miembro desde {{ getRelativeTime(user.createdAt) }}</p>
    <span>{{ formatCurrency(product.price, 'MXN') }}</span>
    <small>Tamaño: {{ formatBytes(file.size) }}</small>
  </div>
</template>

<script setup lang="ts">
import { capitalize, getRelativeTime, formatCurrency, formatBytes } from '@hemia/vue-core';
</script>

📝 Ejemplos Adicionales

Ver el archivo EXAMPLES.md para más ejemplos detallados y casos de uso avanzados.

También puedes revisar la carpeta src/__tests__/ para ver tests y ejemplos de implementación.


🤝 Contribución

Las contribuciones son bienvenidas. Por favor:

  1. Fork el proyecto
  2. Crea una rama para tu feature (git checkout -b feature/AmazingFeature)
  3. Commit tus cambios (git commit -m 'Add some AmazingFeature')
  4. Push a la rama (git push origin feature/AmazingFeature)
  5. Abre un Pull Request

📄 Licencia

ISC © Hemia Team


🔗 Enlaces


Generado con Hemia CLI