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

@codeswayam/access

v1.0.3

Published

Universal entitlements, usage meters, and feature gates for CodeSwayam apps

Readme

@codeswayam/access

Universal entitlements, usage meters, and feature gates for all CodeSwayam apps.

Version TypeScript License

@codeswayam/access is the single source of truth for access control across the CodeSwayam platform. It provides a unified React context, hooks, and components that connect to the entitlements API and expose tier checks, feature gates, and usage metering — all with built-in SWR caching.


Table of Contents


Overview

Every CodeSwayam application needs to know:

  • Who the user is (role, tier)
  • What they can access (features, subscription plan)
  • How much they've used (usage counters vs limits)

@codeswayam/access answers all three questions through a single call to the entitlements API and exposes the result via composable React primitives. It is designed to be dropped into any CodeSwayam app with minimal configuration.

Key capabilities:

| Capability | Primitive | |---|---| | Load full entitlements for an app | useAppAccess(appId) | | Check if user meets a minimum tier | access.hasTier('pro') | | Check a specific feature flag | access.hasFeature('ai_chat') | | Check feature + credits together | access.canUseFeature('ai_chat') | | Declarative component-level gate | <FeatureGate minTier="pro" /> | | Render a visual usage progress bar | <UsageMeter counterKey="dms" /> | | Show the user's current tier | <TierBadge tier="pro" /> | | Increment a usage counter | useUsageTracker(appId) |


Installation

npm install @codeswayam/access
# or
yarn add @codeswayam/access
# or
pnpm add @codeswayam/access

Peer Dependencies

These packages must be present in your application. They are not bundled with @codeswayam/access.

| Package | Version | |---|---| | react | ^18.0.0 | | react-dom | ^18.0.0 | | swr | ^2.0.0 | | @codeswayam/api-client | ^1.3.0 |

npm install react react-dom swr @codeswayam/api-client

Quick Start

1. Wrap your app with AccessProvider

// app/layout.tsx (Next.js App Router)
import { AccessProvider } from '@codeswayam/access';

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        <AccessProvider>
          {children}
        </AccessProvider>
      </body>
    </html>
  );
}

2. Use useAppAccess in any component

import { useAppAccess } from '@codeswayam/access';

export function Dashboard() {
  const access = useAppAccess('auraflow');

  if (!access.isLoaded) return <Spinner />;

  return (
    <div>
      <h1>Welcome, {access.role}</h1>
      {access.hasTier('pro') && <ProFeaturePanel />}
    </div>
  );
}

3. Gate with a component

import { FeatureGate } from '@codeswayam/access';

export function AiChatPage() {
  return (
    <FeatureGate appId="auraflow" minTier="pro" fallback={<UpgradePrompt />}>
      <AiChatInterface />
    </FeatureGate>
  );
}

API Reference

AccessProvider

The root React context provider. Must wrap any component that uses access hooks or components. Typically placed at the top-most layout.

import { AccessProvider } from '@codeswayam/access';

<AccessProvider>
  {children}
</AccessProvider>

Props: None. The provider does not accept configuration — all per-app entitlement loading is done lazily on first useAppAccess(appId) call.


useAppAccess

The primary hook for loading and interacting with entitlements. Calls fetchMyEntitlements via SWR and returns a fully-typed AppAccess object.

function useAppAccess(appId: string): AppAccess

Parameters:

| Parameter | Type | Description | |---|---|---| | appId | string | The CodeSwayam app identifier (e.g. "auraflow", "dmflow") |

Returns: AppAccess

AppAccess properties and methods:

| Member | Type | Description | |---|---|---| | isLoaded | boolean | true once entitlements have been fetched | | appId | string | The app identifier passed to the hook | | role | string | User's role string (e.g. "user", "admin") | | tier | AppTier | Current tier object: { name, label, aiIncluded } | | subscription | AppSubscription \| null | Active subscription data or null | | credits | AppCredits | Credit balance and per-feature costs | | usage | Record<string, UsageCounter> | Map of usage counters keyed by counter name | | features | Record<string, boolean \| number> | Feature flags and numeric feature values | | hasTier(minTier) | (minTier: AppTierName) => boolean | Returns true if user's tier ≥ minTier | | hasFeature(key) | (key: string) => boolean | Returns true if the feature flag is enabled | | canUseFeature(key) | (key: string) => boolean | Returns true if feature is enabled and credits are sufficient | | refresh() | () => void | Manually revalidate the SWR cache |


useFeatureGate

Lightweight hook for a single boolean feature check. Prefer this over useAppAccess when you only need one flag.

function useFeatureGate(appId: string, feature: string): boolean

Parameters:

| Parameter | Type | Description | |---|---|---| | appId | string | The CodeSwayam app identifier | | feature | string | Feature key to check (e.g. "ai_chat", "export_pdf") |

Returns: boolean — true if the feature is accessible for this user.


useUsageTracker

Returns a trackUsage function that records usage increments against the entitlements API.

function useUsageTracker(appId: string): {
  trackUsage: (counterKey: string, increment?: number) => Promise<void>
}

Parameters:

| Parameter | Type | Description | |---|---|---| | appId | string | The CodeSwayam app identifier |

Returns:

| Property | Type | Description | |---|---|---| | trackUsage | (counterKey: string, increment?: number) => Promise<void> | Posts a usage increment. increment defaults to 1. |


FeatureGate Component

Declarative component wrapper that conditionally renders children based on tier, feature flags, or both.

import { FeatureGate } from '@codeswayam/access';

<FeatureGate
  appId="auraflow"
  minTier="pro"
  feature="ai_chat"
  fallback={<UpgradePrompt />}
>
  <AiChatInterface />
</FeatureGate>

Props (FeatureGateProps):

| Prop | Type | Required | Description | |---|---|---|---| | appId | string | ✅ | The CodeSwayam app identifier | | minTier | AppTierName | — | Minimum tier required to render children | | feature | string | — | Feature key that must be enabled | | fallback | React.ReactNode | — | Content to render when access is denied | | children | React.ReactNode | ✅ | Content to render when access is granted |

When both minTier and feature are provided, both conditions must pass.


TierBadge Component

Renders a styled badge showing the given tier name.

import { TierBadge } from '@codeswayam/access';

<TierBadge tier="pro" />

Props:

| Prop | Type | Required | Description | |---|---|---|---| | tier | AppTierName | ✅ | Tier to display: 'free', 'standard', 'pro', 'enterprise' | | className | string | — | Additional CSS class names |


UsageMeter Component

Renders a visual progress bar showing used vs limit for a specific usage counter.

import { UsageMeter } from '@codeswayam/access';

<UsageMeter appId="auraflow" counterKey="dms" />

Props:

| Prop | Type | Required | Description | |---|---|---|---| | appId | string | ✅ | The CodeSwayam app identifier | | counterKey | string | ✅ | The usage counter to display (e.g. "dms", "ai_requests") | | label | string | — | Override the display label | | className | string | — | Additional CSS class names |


TypeScript Interfaces

// Tier names in ascending order
type AppTierName = 'free' | 'standard' | 'pro' | 'enterprise';

// Tier order array — useful for comparisons
const TIER_ORDER: AppTierName[] = ['free', 'standard', 'pro', 'enterprise'];

interface AppTier {
  name: AppTierName;
  label: string;        // Human-readable: "Free", "Pro", etc.
  aiIncluded: boolean;  // Whether AI features are bundled in this tier
}

interface AppSubscription {
  id: string;
  status: string;           // e.g. "active", "cancelled", "expired"
  planType: string;         // e.g. "pro_monthly"
  expiresAt: string | null; // ISO date string
  billingCycle: string;     // "monthly" | "annual"
}

interface AppCredits {
  balance: number;
  featureCosts: Record<string, number>; // { ai_chat: 5, export_pdf: 2 }
}

interface UsageCounter {
  used: number;
  limit: number;
  remaining: number;
  percentage: number; // 0–100
}

interface AppAccess {
  isLoaded: boolean;
  appId: string;
  role: string;
  tier: AppTier;
  subscription: AppSubscription | null;
  credits: AppCredits;
  usage: Record<string, UsageCounter>;
  features: Record<string, boolean | number>;
  refresh(): void;
  hasTier(minTier: AppTierName): boolean;
  hasFeature(key: string): boolean;
  canUseFeature(featureKey: string): boolean;
}

interface FeatureGateProps {
  appId: string;
  minTier?: AppTierName;
  feature?: string;
  fallback?: React.ReactNode;
  children: React.ReactNode;
}

type DenialReason = 'tier' | 'feature' | 'credits' | 'usage_limit' | 'not_loaded';

Re-exported types from @codeswayam/api-client:

// These are available directly from @codeswayam/access
import type {
  EntitlementResult,
  AppEntitlementSummary,
  TrackUsagePayload,
  TrackUsageResult,
  UsageSummary,
} from '@codeswayam/access';

Code Examples

1. Installation and AccessProvider Setup

// app/layout.tsx
import { AccessProvider } from '@codeswayam/access';

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        {/* AccessProvider must be an ancestor of any component using access hooks */}
        <AccessProvider>
          {children}
        </AccessProvider>
      </body>
    </html>
  );
}

2. useAppAccess — Full Example with Conditional Rendering

import { useAppAccess } from '@codeswayam/access';

export function AuraflowDashboard() {
  const access = useAppAccess('auraflow');

  // Show skeleton while entitlements are loading
  if (!access.isLoaded) {
    return <DashboardSkeleton />;
  }

  const dmUsage = access.usage['dms'];

  return (
    <div className="dashboard">
      {/* Tier info */}
      <p>Plan: {access.tier.label}</p>
      <p>Role: {access.role}</p>

      {/* Subscription details */}
      {access.subscription ? (
        <p>Subscription active until: {access.subscription.expiresAt}</p>
      ) : (
        <p>No active subscription</p>
      )}

      {/* Credit balance */}
      <p>Credits: {access.credits.balance}</p>

      {/* Usage counter */}
      {dmUsage && (
        <p>DMs used: {dmUsage.used} / {dmUsage.limit} ({dmUsage.percentage}%)</p>
      )}

      {/* Feature-gated section */}
      {access.hasTier('pro') && <ProAnalyticsPanel />}

      {/* Feature flag check */}
      {access.hasFeature('ai_suggestions') && <AiSuggestionsWidget />}

      {/* Credits + feature check combined */}
      {access.canUseFeature('ai_chat') ? (
        <AiChatButton />
      ) : (
        <button onClick={() => {/* open buy credits modal */}}>
          Buy Credits to Use AI Chat
        </button>
      )}

      {/* Manual refresh */}
      <button onClick={access.refresh}>Refresh Entitlements</button>
    </div>
  );
}

3. FeatureGate Component — Declarative Gate

import { FeatureGate } from '@codeswayam/access';

function UpgradePrompt() {
  return (
    <div className="upgrade-banner">
      <p>This feature requires a Pro plan.</p>
      <a href="/upgrade">Upgrade Now →</a>
    </div>
  );
}

export function ExportPage() {
  return (
    <div>
      <h1>Export Your Data</h1>

      {/* Tier gate only */}
      <FeatureGate appId="auraflow" minTier="standard" fallback={<UpgradePrompt />}>
        <CsvExportButton />
      </FeatureGate>

      {/* Feature flag gate only */}
      <FeatureGate appId="auraflow" feature="pdf_export" fallback={<ComingSoonBadge />}>
        <PdfExportButton />
      </FeatureGate>

      {/* Both tier AND feature must pass */}
      <FeatureGate
        appId="auraflow"
        minTier="pro"
        feature="bulk_export"
        fallback={<UpgradePrompt />}
      >
        <BulkExportPanel />
      </FeatureGate>
    </div>
  );
}

4. Hard Subscription Gate for AI & Premium Features

In applications offering exclusive AI capabilities (such as AuraFlow Smart AI Agents), follow this deterministic entitlement check:

import { useAppAccess } from '@codeswayam/access';

export function AiFeatureWrapper() {
  const access = useAppAccess('auraflow');

  // Verify Pro subscription, AI bundle, or active credit balance
  const canUseAi = access.isLoaded && (
    access.isSubscribed ||
    access.tier.aiIncluded ||
    access.hasTier('pro') ||
    access.hasFeature('canUseSmartAi') ||
    (access.credits.balance >= (access.credits.featureCosts['ai_reply'] ?? 5) && access.credits.balance > 0)
  );

  if (!access.isLoaded) return <Skeleton />;

  if (!canUseAi) {
    return (
      <div className="locked-feature-card">
        <span className="badge-pro">PRO ONLY</span>
        <p>AI Agents require an active Pro subscription or credit balance.</p>
        <a href="/subscription">Upgrade to Pro</a>
      </div>
    );
  }

  return <AutonomousAgentEngine />;
}

5. UsageMeter Component

import { UsageMeter } from '@codeswayam/access';

export function UsageDashboard() {
  return (
    <div className="usage-panel">
      <h2>Your Usage This Month</h2>

      <UsageMeter
        appId="auraflow"
        counterKey="dms"
        label="Direct Messages"
      />

      <UsageMeter
        appId="auraflow"
        counterKey="ai_requests"
        label="AI Requests"
      />

      <UsageMeter
        appId="auraflow"
        counterKey="storage_gb"
        label="Storage"
      />
    </div>
  );
}

5. TierBadge Component

import { TierBadge } from '@codeswayam/access';
import { useAppAccess } from '@codeswayam/access';

export function UserProfileHeader() {
  const access = useAppAccess('auraflow');

  if (!access.isLoaded) return null;

  return (
    <div className="profile-header">
      <Avatar />
      <div>
        <span className="username">John Doe</span>
        {/* Renders a styled badge — "Free", "Pro", etc. */}
        <TierBadge tier={access.tier.name} />
      </div>
    </div>
  );
}

// Or render a static badge anywhere
export function PricingCard() {
  return (
    <div className="pricing-card">
      <TierBadge tier="pro" />
      <h3>Pro Plan</h3>
      <p>Everything in Standard, plus AI features.</p>
    </div>
  );
}

6. useUsageTracker — Tracking Usage

import { useUsageTracker } from '@codeswayam/access';

export function SendDmButton({ recipientId }: { recipientId: string }) {
  const { trackUsage } = useUsageTracker('auraflow');

  async function handleSend() {
    // Send DM via your API
    await sendDm(recipientId);

    // Track the usage increment (defaults to +1)
    await trackUsage('dms');
  }

  async function handleBulkSend(count: number) {
    await bulkSendDms(recipientId, count);

    // Track a custom increment
    await trackUsage('dms', count);
  }

  return <button onClick={handleSend}>Send DM</button>;
}

7. Tier-Based Feature Gating Pattern

import { useAppAccess, TIER_ORDER } from '@codeswayam/access';

// Define which features are available per tier
const FEATURE_TIERS: Record<string, 'free' | 'standard' | 'pro' | 'enterprise'> = {
  basic_analytics:   'free',
  csv_export:        'standard',
  ai_suggestions:    'pro',
  custom_branding:   'enterprise',
};

export function FeatureList() {
  const access = useAppAccess('auraflow');

  if (!access.isLoaded) return <Skeleton />;

  return (
    <ul>
      {Object.entries(FEATURE_TIERS).map(([feature, requiredTier]) => {
        const isUnlocked = access.hasTier(requiredTier);
        return (
          <li key={feature} className={isUnlocked ? 'unlocked' : 'locked'}>
            <span>{feature}</span>
            {isUnlocked ? (
              <CheckIcon className="text-green-500" />
            ) : (
              <span className="text-gray-400">Requires {requiredTier}</span>
            )}
          </li>
        );
      })}
    </ul>
  );
}

Integration Guide (Next.js)

App Router Setup

src/
├── app/
│   ├── layout.tsx           ← Add AccessProvider here
│   ├── auth/
│   │   └── callback/
│   │       └── page.tsx
│   └── dashboard/
│       └── page.tsx         ← Use useAppAccess here
└── components/
    └── ProFeature.tsx       ← Use FeatureGate here

app/layout.tsx

import { AccessProvider } from '@codeswayam/access';

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        <AccessProvider>
          {children}
        </AccessProvider>
      </body>
    </html>
  );
}

app/dashboard/page.tsx

'use client';
import { useAppAccess, UsageMeter, TierBadge, FeatureGate } from '@codeswayam/access';

export default function DashboardPage() {
  const access = useAppAccess('auraflow');

  if (!access.isLoaded) return <p>Loading...</p>;

  return (
    <main>
      <div className="flex items-center gap-2">
        <h1>Dashboard</h1>
        <TierBadge tier={access.tier.name} />
      </div>

      <UsageMeter appId="auraflow" counterKey="dms" label="Messages" />

      <FeatureGate appId="auraflow" minTier="pro" fallback={<p>Upgrade to Pro</p>}>
        <AdvancedAnalytics />
      </FeatureGate>
    </main>
  );
}

Pages Router Setup

pages/_app.tsx

import { AccessProvider } from '@codeswayam/access';
import type { AppProps } from 'next/app';

export default function App({ Component, pageProps }: AppProps) {
  return (
    <AccessProvider>
      <Component {...pageProps} />
    </AccessProvider>
  );
}

Caching Behaviour

@codeswayam/access uses SWR under the hood with the following configuration:

| Setting | Value | Reason | |---|---|---| | dedupingInterval | 30,000ms | Prevents duplicate requests within 30 seconds | | revalidateOnFocus | false | Avoids refetching when the tab regains focus | | Manual revalidation | access.refresh() | Trigger a fresh fetch on demand |

To force a fresh entitlement check (e.g. after a plan upgrade), call:

const access = useAppAccess('auraflow');

// After user upgrades their plan:
await upgradePlan();
access.refresh(); // triggers immediate revalidation

Tier System

Tiers are ordered from lowest to highest access level:

free → standard → pro → enterprise

The TIER_ORDER constant reflects this:

import { TIER_ORDER } from '@codeswayam/access';
// ['free', 'standard', 'pro', 'enterprise']

hasTier('pro') returns true for users on pro or enterprise. It always checks "at least this tier".

| User Tier | hasTier('free') | hasTier('standard') | hasTier('pro') | hasTier('enterprise') | |---|---|---|---|---| | free | ✅ | ❌ | ❌ | ❌ | | standard | ✅ | ✅ | ❌ | ❌ | | pro | ✅ | ✅ | ✅ | ❌ | | enterprise | ✅ | ✅ | ✅ | ✅ |


Changelog

v1.0.3

  • Added Cross-App Entitlement Grants support (crossAppGrants, hasGrantedCategory)
  • Dual-rail access verification: Subscription Tier + Credit Wallet balance fallback
  • Updated dependency to @codeswayam/api-client@^1.3.4

v1.0.0 — Initial Release

  • AccessProvider React context provider
  • useAppAccess(appId) — Universal entitlements hook backed by SWR
  • useFeatureGate(appId, feature) — Single feature gate hook
  • useUsageTracker(appId) — Usage increment tracking
  • FeatureGate — Declarative component gate with tier and feature support
  • TierBadge — Tier display badge component
  • UsageMeter — Visual usage progress bar
  • Full TypeScript support with exported interfaces
  • Re-exports from @codeswayam/api-client: EntitlementResult, AppEntitlementSummary, TrackUsagePayload, TrackUsageResult, UsageSummary

Source Structure

packages/access/src/
├── index.ts                  — Barrel exports
├── AccessProvider.tsx        — React context provider
├── types.ts                  — All TypeScript types and interfaces
├── hooks/
│   ├── useAppAccess.ts       — Primary entitlements hook (SWR)
│   ├── useFeatureGate.ts     — Single feature gate hook
│   └── useUsageTracker.ts    — Usage tracking hook
└── components/
    ├── FeatureGate.tsx       — Declarative gate component
    ├── TierBadge.tsx         — Tier badge component
    └── UsageMeter.tsx        — Usage progress bar component

Part of the CodeSwayam platform. For support, open an issue in the monorepo.