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

exguard-client

v2.1.9

Published

ExGuard RBAC client with cache-first Redis support for maximum performance in EmpowerX applications

Readme

exguard-client

High-performance RBAC (Role-Based Access Control) client for React.
Uses a Cache-First Redis strategy to ensure permission checks are instant (0ms) and API calls are minimized.


🚀 Quick Start (Recommended)

The fastest way to integrate ExGuard into your React project.

1. Install

pnpm add exguard-client

2. Auto-Configure

Run the setup tool to automatically scaffold files and update your code:

npx exguard-setup

3. Add Environment Variable

Add your backend URL to your .env file:

VITE_GUARD_API_URL=http://localhost:3000

🛠️ What npx exguard-setup does

The setup script automates the following integration steps:

✅ Files Generated

It creates a dedicated feature folder at src/features/exguard/:

  • index.ts: Central export point for all ExGuard hooks and components.
  • components/system-admin-permission-guard.tsx: A pre-built guard for Elevated/Admin privileges.
  • config/exguard.config.ts: Placeholder for custom app-specific RBAC logic.

✅ Code Modified

  • main.tsx: Injects setExGuardConfig, initializeCacheFirstRedisClient(), and wraps your app in ExGuardRealtimeProviderUserAccessProvider. (The script intelligently detects if these are already present to avoid duplicates).
  • protected-route.tsx: Wraps your <Outlet /> with the Realtime provider to enable instant RBAC sync.
  • auth-utils.ts: Adds window.dispatchEvent(new CustomEvent('exguard:token-updated')) to trigger immediate permission refreshes on login.

Expected main.tsx after setup:

import { UserAccessProvider, ExGuardRealtimeProvider, setExGuardConfig, initializeCacheFirstRedisClient } from 'exguard-client';

// Auto-injected Configuration
setExGuardConfig({
  apiUrl: import.meta.env.VITE_GUARD_API_URL,
  withCredentials: true,
});

initializeCacheFirstRedisClient();

createRoot(document.getElementById('root')!).render(
  <ExGuardRealtimeProvider>
    <UserAccessProvider>
      <App />
    </UserAccessProvider>
  </ExGuardRealtimeProvider>
);

📖 Usage Examples

1. Conditional Navigation (Sidebar/Menu)

Hide or show menu items based on user permissions.

import { useUserAccessCacheFirst } from '@/features/exguard';
import { Link } from 'react-router-dom';

function Sidebar() {
  const { hasPermission, hasModulePermission } = useUserAccessCacheFirst();

  return (
    <nav>
      <Link to="/dashboard">Dashboard</Link>
      
      {/* Show based on specific permission */}
      {hasPermission('users:view') && (
        <Link to="/users">Manage Users</Link>
      )}

      {/* Show entire section only if user has access to the module */}
      {hasModulePermission('inventory') && (
        <div className="section">
          <h3>Inventory</h3>
          <Link to="/stock">Stock Levels</Link>
          {hasPermission('inventory:admin') && <Link to="/settings">Inventory Settings</Link>}
        </div>
      )}
    </nav>
  );
}

2. Displaying User Details from Cached Data

Extract and display the authenticated user's information.

import { useUserAccessCacheFirst } from '@/features/exguard';

function UserProfile() {
  const { userAccess, isLoading } = useUserAccessCacheFirst();

  if (isLoading) return <div>Loading user...</div>;
  if (!userAccess) return null;

  const fullName = `${userAccess.user.givenName} ${userAccess.user.familyName}`;
  const fieldOffice = userAccess.user.fieldOffice?.name ?? 'N/A';

  return (
    <div className="user-profile">
      <p><strong>Name:</strong> {fullName}</p>
      <p><strong>Field Office:</strong> {fieldOffice}</p>
    </div>
  );
}

Or using useUserAccessSingleton:

import { useUserAccessSingleton } from '@/features/exguard';

function SidebarHeader() {
  const { userAccess } = useUserAccessSingleton();

  if (!userAccess) return null;

  return (
    <div className="sidebar-header">
      <span className="user-name">
        {userAccess.user.givenName} {userAccess.user.familyName}
      </span>
      <span className="user-office">
        {userAccess.user.fieldOffice?.name ?? 'No Field Office'}
      </span>
    </div>
  );
}

3. Button & Action Protection

Disable or hide buttons for unauthorized actions.

function UserRow({ user }) {
  const { hasPermission } = useUserAccessCacheFirst();

  return (
    <tr>
      <td>{user.name}</td>
      <td>
        <button disabled={!hasPermission('users:edit')}>
          Edit
        </button>
        
        {hasPermission('users:delete') && (
          <button className="delete-btn">Delete</button>
        )}
      </td>
    </tr>
  );
}

4. Protecting Entire Pages (Routes)

Use the PermissionGuard to wrap routes in your main router configuration.

import { PermissionGuard } from '@/features/exguard';

// In your routes file:
const router = createBrowserRouter([
  {
    path: "/admin",
    element: (
      <PermissionGuard module="system" permission="admin:access">
        <AdminLayout />
      </PermissionGuard>
    ),
    children: [
      { path: "settings", element: <SettingsPage /> }
    ]
  }
]);

⚡ How Real-time Updates Work

ExGuard ensures your frontend permissions stay in sync with the backend without requiring a page refresh.

  1. Backend Change: An admin updates a user's role in the dashboard.
  2. WebSocket Signal: The backend sends a targeted "RBAC Update" signal to the user's browser.
  3. Instant Invalidation: The ExGuardRealtimeProvider receives the signal and instantly invalidates the local cache.
  4. Auto-Refresh: All active useUserAccessCacheFirst hooks detect the change and re-sync with the latest permissions from Redis (0.5ms - 2ms).
  5. UI Update: Your sidebar, buttons, and guards update immediately to reflect the new permissions.

💡 Key Concepts

  • Redis-First: The client always tries to fetch permissions from Redis (via your backend proxy) before falling back to an API call.
  • Real-time: When a user's role changes on the backend, the frontend cache is invalidated instantly via WebSockets.
  • Zero Latency: Once the cache is loaded, hasPermission() calls do not trigger network requests.

❓ Troubleshooting

| Issue | Solution | | :--- | :--- | | Permissions returning false | Ensure your .env URL is correct and the user has a valid token. | | Changes not reflecting | Call invalidateCache() from the hook or refresh the page. | | 401 Unauthorized | Check if access_token is present in LocalStorage. |


Built for speed. Designed for scale.