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

@widergy/web-utils

v2.31.0

Published

Utility GO! Web utils

Readme

web-utils

Utility GO! Web Utils

Analytics

Se utiliza desde Frontend Web, mediante Google Analytics 4, para recoger datos de los sitios web y comprender mejor el recorrido del cliente.

Funciones

- initializeGA(analyticsTrackingId, options)

Se encarga de inicializar analytics.

  • analyticsTrackingId: Google Analytics Tracking ID.
  • options: Objeto de configuración custom para Analytics.
    • En caso de querer un tracker, se debe indicar acá:
      { gaOptions: { name: ANALYTICS_TRACKER }, alwaysSendToDefaultTracker: false }
    • En caso de no recibir options, se usará como default
    { titleCase: false }

- sendGAEvent(category, action, label, value)

Se utiliza para enviar eventos.

  • category: Categoría del evento.
  • action: Acción del evento.
  • label: Label del evento.
  • value: Valor del evento.

Por ejemplo, si se quiere enviar un evento sobre una recarga prepaga exitosa:

sendGAEvent('Cobranzas', 'Generación de carga prepago', 'WDRG | Valor agregado | -OK- |', '1850')

- sendGAPageView(pathname)

Se utiliza para trackear cambios de página.

  • pathname: URL de la página a trackear.

Ejemplo:

sendGAPageView(ROUTES.BALANCE_TO_PAY);

- setGAUserId(userId)

Se utiliza para setear el id de usuario.

  • userId: ID del usuario

Ejemplo:

setGAUserId(getState().user.currentUser.id);

- createMiddleware(eventDataDefinition)

Se utiliza para crear un middleware que intercepte acciones de redux y envíe eventos de analytics.

  • eventDataDefinition debe ser un diccionario de tipo de acción - función.
const eventDataDefinition = {
  actionType: function
}

Por ejemplo:

const eventDataDefinition = {
  [paymentActions.PREPAID_PAYMENT_REQUEST_FAILURE]: prepaidPaymentRequestFailure
}

Cada uno de estas funciones, al ejecutarse, debe devolver los valores a usar en el trackeo de eventos.

const function = action => ({
  category,
  action,
  label,
  value
});

Por ejemplo:

const prepaidPaymentRequestFailure = action => ({
  category: categories.COBRANZAS,
  action: actions.GENERACION_DE_CARGA_PREPAGO_ERRONEA,
  label: `${labels.GENERACION_DE_PAGO_ERROR} ${action.payload}`,
  value: Math.round(action.totalAmount)
});

Fechas con formato configurable

Hasta ahora las utilidades de fecha trabajaban fijas en DD/MM/YYYY. Los validadores, el normalizador y los límites de rango aceptan ahora un formato opcional: quien no lo pasa se sigue comportando igual que siempre, porque el default es el histórico de la librería.

Formatos aceptados — DATE_FORMAT_UTILS

Un formato se escribe con los tokens DD, MM, AAAA (o YYYY) y AA (o YY), separados por cualquier caracter que no sea una letra. No distingue mayúsculas. Se acepta tanto el token de dayjs (YYYY) como el que se lee en pantalla (AAAA), porque el formato lo configura quien define el campo y es lo que espera escribir.

Un formato que no aporte ningún tramo de dígitos, o que deje letras sueltas fuera de los tokens reconocidos ('DD de MM', 'MMM', 'DD/MM/AAA'), no es usable: la máscara quedaría vacía o produciría texto incoherente y el campo sería imposible de completar. En ese caso se vuelve al default DD/MM/YYYY en lugar de romper el campo.

Lo mismo con un formato que no sea un string. Estas utilidades se consumen desde JavaScript, donde el tipo no protege, y el formato se sumó como último parámetro de funciones que ya existían: un llamador viejo puede estar pasando ahí un argumento que hasta ahora no tenía ningún efecto. Antes que romper en runtime por eso, se ignora y se usa el default.

- normalizeDateFormat(format)

Devuelve el formato en tokens de dayjs (AAAA → YYYY), o DD/MM/YYYY si el recibido no es usable. Es el punto por el que pasa todo lo demás.

- applyDateMask(value, format)

Máscara de entrada: descarta los separadores, se queda con los dígitos y rearma el texto colocando cada separador solo cuando hay un dígito que lo siga. Al derivarse del formato en lugar de tener las posiciones fijas, sirve igual para DD/MM/YYYY, YYYY-MM-DD o MM/YYYY.

applyDateMask('090', 'DD/MM/AAAA'); // '09/0'
applyDateMask('09092026', 'DD/MM/AAAA'); // '09/09/2026'
applyDateMask('20260909', 'AAAA-MM-DD'); // '2026-09-09'

- getFormatPlaceholder(format)

El formato tal como se lee en pantalla, para usar de placeholder: 'YYYY-MM-DD' → 'AAAA-MM-DD'.

- parseDateFormat(format) · getFormatDigits(format) · formatHasToken(format, token) · formatHasYear(format)

Descomposición del formato en sus partes, cantidad de dígitos que pide, y presencia de un token. formatHasYear contempla que el año pueda estar como YYYY o como YY, así que no alcanza con buscar un token puntual.

Límites de rango — DATE_BOUNDS_UTILS

Un límite (DateBound) puede ser una fecha fija en string, o un objeto que se resuelve contra la fecha de hoy:

| Clave | Qué hace | |---|---| | set | Componentes fijos: { date, month, year }. El mes va de 1 a 12, que es como se lee, y no de 0 a 11 como lo maneja dayjs | | days / months / years | Desplazamientos respecto de hoy. Se aceptan en plural y en singular (month y months son lo mismo): el componente fijo vive dentro de set, así que a este nivel no hay ambigüedad que resolver obligando a recordar una forma u otra | | startOf / endOf | Anclaje a day, month o year, también en plural |

El orden de aplicación es hoy → componentes fijos → desplazamientos → anclaje, de modo que { set: { date: 15 }, months: 1 } sea "el 15, un mes después" y { months: 1, endOf: 'month' } sea "el último día del mes que viene".

Una clave que no se reconozca se ignora, pero se avisa por consola: el límite se resolvería a algo distinto de lo configurado y sin esa señal no habría forma de notarlo.

- resolveDateBound(bound, format)

Resuelve un límite dinámico contra hoy y lo devuelve en el formato indicado. Un string se devuelve tal cual, que es el comportamiento de siempre para las fechas fijas.

resolveDateBound('01/01/2026'); // '01/01/2026'
resolveDateBound({ months: 6 }); // seis meses después de hoy
resolveDateBound({ years: -18, endOf: 'year' }); // 31/12 del año en que se cumplen 18

- resolveDateRange(range, format)

Resuelve los dos límites de un rango. Los devuelve siempre como fecha completa (DD/MM/YYYY, expuesto como FULL_DATE_FORMAT) y no en el formato del campo: el rango acota qué se puede elegir, y eso no depende de cómo se muestre o se guarde el valor. Recortarlos al formato dejaba un rango sin año atado al año en que se resolvió, y el calendario no podía pasar de ahí aunque el rango configurado abarcara varios años. Un límite fijo escrito sin año se entiende del año en curso, que es el único al que puede referirse.

Si el mínimo queda después del máximo y el formato lleva año, el rango no deja ninguna fecha elegible y volvería el campo imposible de completar, así que se descarta y se avisa por consola: descartarlo en silencio deja un campo sin las restricciones que alguien configuró, sin ninguna pista de por qué.

- isCyclicRange(minDate, maxDate, format) · isWithinCyclicRange(value, minDate, maxDate, format)

Un formato sin año no ubica una fecha en la línea del tiempo, ubica una posición en el círculo del año. Ahí un mínimo posterior al máximo no es un error: describe un tramo que cruza el fin de año ('del 01/12 al 28/02'), y valen las fechas desde el mínimo en adelante o hasta el máximo. Comparado como rango lineal no quedaría ninguna fecha válida.

Validaciones — VALIDATE_UTILS

Todas reciben el formato como último parámetro y es opcional:

  • validDate(errorMessage, format)
  • minDate(bound, errorMessage, format) · maxDate(bound, errorMessage, format)
  • dateIsInRange(lowBound, topBound, errorMessage, format)
  • checkCustomDateValidations(dateValidations, format, fieldFormat), donde fieldFormat es el formato del campo y format sigue siendo el alternativo que ya se aceptaba.

minDate, maxDate y dateIsInRange aceptan límites dinámicos, y los resuelven en cada ejecución y no al construir el validador: un límite relativo depende de la fecha de hoy, y el validador se construye una vez pero corre en cada cambio del campo. dateIsInRange contempla además los rangos que cruzan el fin de año.

Normalizador — NORMALIZE_UTILS

- dateWithFormat(format)(value)

Máscara derivada del formato, para los campos que configuran uno. Se recibe curried porque los normalizadores de redux-form se invocan con (value, previousValue) y el formato no puede ocupar el segundo lugar.

normalizeDate se mantiene tal cual, con su máscara fija, para no cambiarle el comportamiento a quienes ya lo usan.