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

shared-lib-react

v1.0.8

Published

React/Next.js Librería Framework para autenticación, autorización y componentes de UI

Readme

@mh-dinafi-frmk/shared-lib-react

Librería React / Next.js 15 del Framework institucional del Ministerio de Hacienda de El Salvador (DINAFI).
Provee autenticación OAuth 2 / OIDC (Keycloak), autorización basada en roles y grupos, componentes de Login y Dashboard, configuración dinámica de 3 capas y design tokens institucionales.

📦 Equivalente funcional de @mh-dinafi-frmk/shared-lib-angular v2.x


Tabla de Contenidos

  1. Requisitos
  2. Instalación
  3. Configuración rápida
  4. AuthProvider
  5. Componentes
  6. Guard de rutas — withAuth
  7. Hooks
  8. Servicios
  9. Zustand Config Store
  10. Estilos y Design Tokens
  11. Arquitectura de configuración 3 capas
  12. Migración desde lib-angular
  13. Scripts
  14. Contribución

Requisitos

| Dependencia | Versión mínima | | -------------- | -------------- | | React | ≥ 18 | | Next.js | ≥ 15 (App Router) | | Node.js | ≥ 18 | | Sass | ≥ 1.80 |


Instalación

npm install @mh-dinafi-frmk/shared-lib-react

Peer dependencies que ya deben existir en su proyecto:

npm install react react-dom next oidc-client-ts

Configuración rápida

1. Envolver la aplicación con AuthProvider

// app/layout.tsx (Next.js 15 App Router)
import { AuthProvider } from '@mh-dinafi-frmk/shared-lib-react';
import '@mh-dinafi-frmk/shared-lib-react/dist/index.css';

const frmkConfig = {
  configServerHost: 'config-server.ejemplo.gob.sv',
  frontendId: 'mi-aplicacion',
  realm: 'mi-realm',
  requireHttps: true,
  loginConfig: {
    title: 'Mi Aplicación',
    subtitle: 'Ministerio de Hacienda',
    brandingTitle: 'Nombre del Sistema',
    brandingSubtitle: 'Descripción breve del sistema',
  },
  dashboardConfig: {
    title: 'Mi Aplicación',
    institutionName: 'Ministerio de Hacienda',
  },
  // Opcional: OAuth directo (sin Config Server)
  oauth: {
    clientId: 'mi-client-id',
    keycloakHost: 'keycloak.ejemplo.gob.sv',
    authServerHost: 'auth-proxy.ejemplo.gob.sv',
    scope: 'openid email profile',
  },
};

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="es">
      <body>
        <AuthProvider config={frmkConfig}>
          {children}
        </AuthProvider>
      </body>
    </html>
  );
}

2. Página de Login

// app/login/page.tsx
import { Login } from '@mh-dinafi-frmk/shared-lib-react';

export default function LoginPage() {
  return <Login dashboardUrl="/dashboard" />;
}

3. Dashboard con protección

// app/dashboard/layout.tsx
'use client';
import { Dashboard, withAuth } from '@mh-dinafi-frmk/shared-lib-react';

function DashboardLayout({ children }: { children: React.ReactNode }) {
  return <Dashboard>{children}</Dashboard>;
}

export default withAuth(DashboardLayout);

AuthProvider

El AuthProvider es el componente raíz que inicializa toda la librería:

<AuthProvider config={frmkConfig}>
  {children}
</AuthProvider>

Props:

| Prop | Tipo | Descripción | | -------- | ------------------ | ----------- | | config | FrmkLibraryConfig | Configuración de la librería (ver interfaz completa abajo) | | children | ReactNode | Contenido de la aplicación |

Flujo de inicialización:

  1. Inicializa el Zustand store con los defaults + config
  2. Solicita configuración al Config Server (configService.loadConfig())
  3. Inicializa oidc-client-ts UserManager con la configuración resultante
  4. Ejecuta runInitialLoginSequence() (callback handling, silent renew, etc.)

Componentes

Login

Componente de login con diseño institucional de panel dividido (branding panel + formulario).

import { Login } from '@mh-dinafi-frmk/shared-lib-react';

<Login
  dashboardUrl="/dashboard"
  title="Mi Sistema"
  subtitle="Bienvenido al portal"
  brandingTitle="Sistema de Gestión"
  brandingSubtitle="Plataforma institucional"
  showForgotLinks={true}
  forgotPasswordUrl="https://keycloak.example.com/forgot-password"
  forgotPasswordText="¿Olvidó su contraseña?"
  forgotUsernameUrl="https://keycloak.example.com/forgot-username"
  forgotUsernameText="¿Olvidó su usuario?"
  showLogoutButton={true}
/>

Props opcionales: Todos sobreescriben la configuración del store.

| Prop | Tipo | Default | | -------------------- | -------- | ------- | | dashboardUrl | string | '/dashboard' | | title | string | Config del store | | subtitle | string | Config del store | | brandingTitle | string | Config del store | | brandingSubtitle | string | Config del store | | showForgotLinks | boolean | false | | forgotPasswordUrl | string | '#' | | forgotPasswordText | string | '¿Olvidó su contraseña?' | | forgotUsernameUrl | string | '#' | | forgotUsernameText | string | '¿Olvidó su usuario?' | | showLogoutButton | boolean | false |

Dashboard

Layout principal de la aplicación con header, menú de navegación jerárquico, área de contenido y footer.

import { Dashboard } from '@mh-dinafi-frmk/shared-lib-react';

<Dashboard
  title="Mi Sistema"
  loginUrl="/login"
>
  {/* Contenido de la página */}
  <h1>Bienvenido</h1>
</Dashboard>

Características:

  • Header responsivo (mobile top/bottom + desktop)
  • Navegación horizontal (desktop) / sidebar (mobile) con menú hamburguesa
  • Menú jerárquico dinámico basado en permisos del usuario
  • Panel de perfil de usuario con cerrar sesión
  • Footer institucional

Guard de rutas — withAuth

HOC (Higher-Order Component) para proteger rutas que requieren autenticación.

import { withAuth } from '@mh-dinafi-frmk/shared-lib-react';

// Protección básica
export default withAuth(MiComponente);

// Con roles específicos
export default withAuth(MiComponente, {
  roles: ['ADMIN', 'SUPERVISOR'],
  loginUrl: '/login',
});

// Con componentes personalizados de loading y acceso denegado
export default withAuth(MiComponente, {
  roles: ['ADMIN'],
  loading: <MiSpinner />,
  forbidden: <AccesoDenegado />,
});

Opciones:

| Opción | Tipo | Default | Descripción | | ----------- | ------------- | ------- | ----------- | | loginUrl | string | '/login' | URL de redirección si no está autenticado | | roles | string[] | undefined | Roles requeridos (any match) | | loading | ReactNode | Spinner default | Componente mientras verifica autenticación | | forbidden | ReactNode | Mensaje default | Componente si no tiene permisos |


Hooks

useAuth()

Accede al estado de autenticación reactivo.

import { useAuth } from '@mh-dinafi-frmk/shared-lib-react';

function MiComponente() {
  const {
    isAuthenticated,
    isDoneLoading,
    user,
    identityClaims,
    login,
    logout,
    getUserRoles,
    getUserGroups,
    hasRole,
    hasAnyRole,
  } = useAuth();

  return (
    <div>
      {isAuthenticated ? (
        <button onClick={logout}>Cerrar sesión</button>
      ) : (
        <button onClick={login}>Iniciar sesión</button>
      )}
    </div>
  );
}

useAuthorization()

Verificación de permisos y menú jerárquico.

import { useAuthorization } from '@mh-dinafi-frmk/shared-lib-react';

function MiComponente() {
  const {
    verifyPermission,
    checkAuthorization,
    getMenuHierarchy,
    getRootMenuItems,
    getChildMenuItems,
    hasChildren,
  } = useAuthorization();

  useEffect(() => {
    verifyPermission('/ruta/protegida').then(allowed => {
      if (!allowed) router.push('/sin-acceso');
    });
  }, []);
}

useConfig()

Acceso a la configuración de la aplicación.

import { useConfig } from '@mh-dinafi-frmk/shared-lib-react';

function MiComponente() {
  const {
    appConfig,
    loginConfig,
    dashboardConfig,
    libraryConfig,
    isConfigured,
  } = useConfig();

  return <span>{appConfig.auth.issuer}</span>;
}

Servicios

Los servicios se usan internamente pero están expuestos para uso avanzado:

| Servicio | Descripción | | ---------------------- | ----------- | | AuthService | Manejo de OAuth2/OIDC con oidc-client-ts (login, logout, tokens, roles) | | AuthorizationService | Verificación de permisos contra el backend de autorización | | ConfigService | Carga de configuración del Config Server | | TokenStorageService | Gestión de tokens en localStorage/sessionStorage |


Zustand Config Store

La librería usa un store Zustand (useFrmkStore) para la gestión de estado:

import { useFrmkStore } from '@mh-dinafi-frmk/shared-lib-react';

// Acceso directo (fuera de React)
const appConfig = useFrmkStore.getState().appConfig();
const authConfig = useFrmkStore.getState().authConfig();

// Dentro de React (reactivo)
function MiComponente() {
  const isConfigured = useFrmkStore(state => state.isConfigured());
}

Estilos y Design Tokens

La librería incluye CSS compilado y SCSS source tokens para personalización:

CSS compilado (recomendado)

// En su layout raíz
import '@mh-dinafi-frmk/shared-lib-react/dist/index.css';

SCSS tokens para personalización

// Importar tokens institucionales
@use '@mh-dinafi-frmk/shared-lib-react/styles' as frmk;

// Los tokens incluyen:
// - Colores institucionales (--frmk-primary, --frmk-secondary, etc.)
// - Tipografías (Bembo, MuseoSans)
// - Espaciado, bordes, sombras
// - Variables para login y dashboard

Assets incluidos

  • Fuentes: Bembo, MuseoSans
  • Iconos: Material Symbols Outlined
  • Logo: logo-mh.png (Ministerio de Hacienda)

Arquitectura de configuración 3 capas

┌─────────────────────────────────────────────┐
│ Capa 3: Config Server (runtime)             │
│  → Sobreescribe todo en tiempo de ejecución │
├─────────────────────────────────────────────┤
│ Capa 2: FrmkLibraryConfig (compile-time)    │
│  → Valores específicos de la aplicación     │
├─────────────────────────────────────────────┤
│ Capa 1: Defaults de la librería             │
│  → DEFAULT_LOGIN_CONFIG, DEFAULT_DASHBOARD_ │
│    CONFIG, DEFAULT_LIBRARY_CONFIG            │
└─────────────────────────────────────────────┘

Flujo:
Defaults → se mezclan con → FrmkLibraryConfig → se sobreescriben por → ConfigServerResponse

Claves del Config Server

| Clave del servidor | Campo de configuración | | ----------------------------- | ---------------------- | | security.url.auth | hosts.authServer | | security.url.keycloak | hosts.keycloak | | security.url.callback | hosts.localApp | | security.realm | paths.realm | | security.scope | auth.scope | | security.url.authz | authorization.baseUrl | | security.url.https | environment.requireHttps | | quarkus.oidc.client-id | auth.clientId |


Migración desde lib-angular

| Concepto Angular | Equivalente React | | ----------------------------------- | ----------------- | | FrmkModule.forRoot(config) | <AuthProvider config={config}> | | FrmkAuthGuard | withAuth(Component, options) | | FrmkOauthService (DI) | useAuth() hook | | FrmkAuthzService (DI) | useAuthorization() hook | | FrmkConfigService (DI) | useConfig() hook | | FrmkLoginComponent | <Login /> | | FrmkDashboardComponent | <Dashboard>{children}</Dashboard> | | Angular Signals (computed()) | useMemo() / Zustand selectors | | BehaviorSubject / RxJS | Zustand store + useSyncExternalStore | | <router-outlet> | {children} prop | | routerLink | next/link | | *ngTemplateOutlet recursivo | Función recursiva React | | @Input() | Props | | @HostListener | useEffect + addEventListener |


Scripts

# Build (ESM + CJS + .d.ts + CSS)
npm run build

# Tests (Jest + RTL)
npm test

# Sincronizar versión package.json → version.ts
npm run sync-version

# Lint
npm run lint

Estructura del proyecto

lib-react/
├── src/
│   ├── index.ts                 # Entry point (exports todo)
│   ├── version.ts               # VERSION constante auto-sincronizada
│   ├── types.d.ts               # Declaraciones de módulos (.scss, .png, etc.)
│   ├── models/                  # Interfaces TypeScript
│   │   ├── app-config.interface.ts
│   │   ├── authorization.interface.ts
│   │   ├── dashboard-config.interface.ts
│   │   ├── library-config.interface.ts
│   │   └── login-config.interface.ts
│   ├── stores/
│   │   └── config-store.ts      # Zustand store principal
│   ├── services/
│   │   ├── auth.service.ts      # OAuth2/OIDC (oidc-client-ts)
│   │   ├── authorization.service.ts
│   │   ├── config.service.ts    # Carga Config Server
│   │   └── token-storage.service.ts
│   ├── hooks/
│   │   └── index.ts             # useAuth, useAuthorization, useConfig
│   ├── components/
│   │   ├── AuthProvider.tsx
│   │   ├── Login/
│   │   │   ├── Login.tsx
│   │   │   └── Login.module.scss
│   │   └── Dashboard/
│   │       ├── Dashboard.tsx
│   │       └── Dashboard.module.scss
│   ├── guards/
│   │   └── withAuth.tsx         # HOC de protección de rutas
│   ├── styles/                  # Design tokens SCSS
│   │   ├── _tokens.scss
│   │   ├── _auth.scss
│   │   ├── _dashboard.scss
│   │   └── _index.scss
│   └── assets/                  # Fuentes, iconos, logos
├── scripts/
│   ├── sync-version.js
│   └── postbuild.js
├── jest.config.js
├── tsconfig.json
├── tsup.config.ts
└── package.json

Licencia

Uso interno — Ministerio de Hacienda de El Salvador, DINAFI.