{"id":51116194,"url":"https://github.com/abhishekayu/react-access-engine","last_synced_at":"2026-06-24T22:01:09.703Z","repository":{"id":344406817,"uuid":"1177542470","full_name":"abhishekayu/react-access-engine","owner":"abhishekayu","description":"Unified access control engine for React with RBAC, ABAC, feature flags, experiments, plan gating, and policy-based authorization.","archived":false,"fork":false,"pushed_at":"2026-03-14T14:26:31.000Z","size":1896,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-15T01:24:02.162Z","etag":null,"topics":["a-b-testing","abac","access-control","authorization","experiments","feature-flags","feature-toggles","nextjs","permission-guard","permissions","plan-gating","policy-engine","rbac","react","react-hooks","remote-config","ssr-safe","tree-shakeable","typescript"],"latest_commit_sha":null,"homepage":"https://react-access-engine.dev","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/abhishekayu.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-10T05:56:29.000Z","updated_at":"2026-03-14T14:39:47.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/abhishekayu/react-access-engine","commit_stats":null,"previous_names":["abhishekayu/react-access-engine"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/abhishekayu/react-access-engine","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/abhishekayu%2Freact-access-engine","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/abhishekayu%2Freact-access-engine/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/abhishekayu%2Freact-access-engine/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/abhishekayu%2Freact-access-engine/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/abhishekayu","download_url":"https://codeload.github.com/abhishekayu/react-access-engine/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/abhishekayu%2Freact-access-engine/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34750953,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-24T02:00:07.484Z","response_time":106,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["a-b-testing","abac","access-control","authorization","experiments","feature-flags","feature-toggles","nextjs","permission-guard","permissions","plan-gating","policy-engine","rbac","react","react-hooks","remote-config","ssr-safe","tree-shakeable","typescript"],"created_at":"2026-06-24T22:01:08.524Z","updated_at":"2026-06-24T22:01:09.695Z","avatar_url":"https://github.com/abhishekayu.png","language":"TypeScript","funding_links":["https://github.com/sponsors/abhishekayu"],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n  \n\u003cimg width=\"400\" height=\"400\" alt=\"React Access Engine — unified RBAC, ABAC, feature flags, and policy engine for React and Node.js\" src=\"https://github.com/user-attachments/assets/6593ecec-b349-44f6-96d5-a3ff599dcd6b\" /\u003e\n  \n# React Access Engine\n\n**Unified access control, RBAC, ABAC, feature flags, experiments, and policy engine for React.js, Next.js \u0026 Node.js.**\n\nOne config. Frontend hooks + backend engine functions. Same logic everywhere.\n\nReact.js, Next.js, Node.js, Express, RBAC, ABAC, authorization, permissions, feature flags, A/B testing, plan gating, remote config, SSR-safe, TypeScript, middleware, subscription\n\n[![npm version](https://img.shields.io/npm/v/react-access-engine?color=blue\u0026label=npm)](https://www.npmjs.com/package/react-access-engine)\n[![minzipped size](https://img.shields.io/badge/minzipped-5.7_kB-blue)](https://bundlephobia.com/package/react-access-engine)\n[![tests](https://img.shields.io/badge/tests-620%20passing-brightgreen)](https://github.com/abhishekayu/react-access-engine)\n[![TypeScript](https://img.shields.io/badge/TypeScript-5.7+-3178C6?logo=typescript\u0026logoColor=white)](https://www.typescriptlang.org/)\n[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](CONTRIBUTING.md)\n\n[Documentation](https://react-access-engine.dev/docs) · [Playground](https://react-access-engine.dev/playground) · [Examples](https://github.com/abhishekayu/react-access-engine/tree/main/examples)\n\n\u003c/div\u003e\n\n---\n\n## Table of Contents\n\n- [Why?](#why)\n- [Features](#features)\n- [Installation](#installation)\n- [Quick Start](#quick-start)\n- [API Reference](#api-reference)\n  - [useAccess() Hook](#useaccess--the-only-hook-you-need)\n  - [Allow Component](#allow--the-only-component-you-need)\n  - [Specialized Components](#specialized-components)\n  - [Specialized Hooks](#specialized-hooks)\n- [Advanced Usage](#advanced-usage)\n  - [Policy Engine (ABAC)](#policy-engine-abac)\n  - [Percentage Rollouts](#percentage-rollouts)\n  - [Feature Dependencies](#feature-dependencies)\n  - [Plan Gating](#subscriptionplan-gating)\n  - [Plugin System](#plugin-system)\n  - [Experiments / A/B Testing](#experiments--ab-testing)\n  - [Debug Mode](#debug-mode)\n  - [Remote Config](#remote-config)\n  - [Condition Engine](#condition-engine-declarative-abac)\n- [Full E-commerce Example](#full-e-commerce-example)\n- [SSR / Next.js](#ssr--nextjs)\n- [TypeScript](#typescript)\n- [Backend / Node.js Usage](#backend--nodejs-usage)\n- [Architecture](#architecture)\n- [Testing](#testing)\n- [Comparison](#comparison)\n- [Contributing](#contributing)\n- [Security](#security)\n- [License](#license)\n\n---\n\n## Why?\n\nReact apps cobble together homegrown RBAC, a feature flag service, ad-hoc plan gating, and manual A/B test wiring — each with its own provider, API, and blind spots.\n\n**react-access-engine** replaces all of them with one system — and keeps it simple:\n\n```tsx\nconst { can, is, has, tier } = useAccess();\n\ncan('edit'); // check permission\nis('admin'); // check role\nhas('dark-mode'); // check feature flag\ntier('pro'); // check plan\n```\n\n## Features\n\n- **RBAC** — Multi-role users, role → permission mapping, wildcard permissions (`*`, `namespace:*`)\n- **ABAC** — Attribute-based policies with composable allow/deny rules and priority ordering\n- **Feature Flags** — Boolean toggles, percentage rollouts, role/plan/environment targeting, dependencies\n- **A/B Experiments** — Deterministic variant assignment, SSR-safe hashing, allocation control\n- **Plan Gating** — Hierarchical subscription tiers with automatic comparison\n- **Remote Config** — Fetch config from API with stale-while-revalidate, polling, signature verification\n- **Plugin System** — Lifecycle hooks for audit logging, analytics, custom condition operators\n- **DevTools** — Optional overlay for inspecting access decisions and policy traces\n- **Type Safety** — Full TypeScript inference — `InferRoles`, `InferPermissions`, `InferFeatures`\n- **SSR-Ready** — Deterministic evaluation, works with Next.js App Router\n- **Tree-Shakeable** — Import only what you use — unused engines are eliminated at build time\n- **Zero Dependencies** — No runtime dependencies beyond React\n\n## Installation\n\n```bash\nnpm install react-access-engine\n# or\npnpm add react-access-engine\n# or\nyarn add react-access-engine\n```\n\n\u003e Requires `react \u003e= 18` as a peer dependency.\n\n## Quick Start\n\n### 1. Define your config\n\n```typescript\nimport { defineAccess } from 'react-access-engine';\n\nconst config = defineAccess({\n  roles: ['customer', 'seller', 'admin', 'support'],\n  permissions: {\n    customer: ['products:browse', 'cart:manage', 'orders:own', 'reviews:write', 'wishlist:manage'],\n    seller: [\n      'products:browse',\n      'products:create',\n      'products:edit-own',\n      'inventory:manage',\n      'analytics:own-store',\n      'coupons:create',\n    ],\n    admin: ['*'],\n    support: ['orders:view-all', 'orders:refund', 'reviews:moderate', 'tickets:manage'],\n  },\n  plans: ['free', 'plus', 'premium'],\n  features: {\n    'quick-buy': true,\n    wishlist: true,\n    'ai-recommendations': { enabled: true, allowedPlans: ['premium'] },\n    'live-chat': { enabled: true, allowedPlans: ['plus', 'premium'] },\n    'loyalty-points': { enabled: true, allowedPlans: ['plus', 'premium'] },\n    'bulk-discount': { enabled: true, allowedRoles: ['seller'] },\n  },\n  experiments: {\n    'checkout-layout': {\n      id: 'checkout-layout',\n      variants: ['classic', 'one-page', 'step-wizard'],\n      defaultVariant: 'classic',\n      active: true,\n      allocation: { classic: 34, 'one-page': 33, 'step-wizard': 33 },\n    },\n  },\n  policies: [\n    {\n      id: 'seller-own-products',\n      effect: 'allow',\n      permissions: ['products:edit', 'products:delete'],\n      condition: ({ user, resource }) =\u003e user.id === resource.sellerId,\n      description: 'Sellers can only edit/delete their own products',\n    },\n    {\n      id: 'refund-time-limit',\n      effect: 'deny',\n      permissions: ['orders:refund'],\n      condition: ({ resource }) =\u003e {\n        const daysSinceOrder = (Date.now() - resource.orderedAt) / 86400000;\n        return daysSinceOrder \u003e 30;\n      },\n      priority: 100,\n      description: 'Block refunds after 30 days',\n    },\n  ],\n  plugins: [\n    {\n      name: 'store-analytics',\n      onAccessCheck: (event) =\u003e {\n        console.log(`[STORE] ${event.permission}: ${event.granted ? '✅' : '❌'}`);\n      },\n      onFeatureEvaluate: (event) =\u003e {\n        console.log(`[FLAG] ${event.feature}: ${event.enabled ? 'ON' : 'OFF'}`);\n      },\n    },\n  ],\n  debug: true,\n});\n```\n\n### 2. Wrap your app\n\n```tsx\nimport { AccessProvider } from 'react-access-engine';\n\nfunction App() {\n  const user = useCurrentUser(); // your auth hook\n  return (\n    \u003cAccessProvider config={config} user={{ id: user.id, roles: user.roles, plan: user.plan }}\u003e\n      \u003cStore /\u003e\n    \u003c/AccessProvider\u003e\n  );\n}\n```\n\n### 3. Use it\n\n```tsx\nimport { useAccess } from 'react-access-engine';\n\nfunction StorePage() {\n  const { can, is, has, tier } = useAccess();\n\n  return (\n    \u003cdiv\u003e\n      {can('cart:manage') \u0026\u0026 \u003cbutton\u003e🛒 Add to Cart\u003c/button\u003e}\n      {is('seller') \u0026\u0026 \u003cSellerDashboard /\u003e}\n      {has('quick-buy') \u0026\u0026 \u003cbutton\u003e⚡ Buy Now\u003c/button\u003e}\n      {tier('premium') \u0026\u0026 \u003cAIRecommendations /\u003e}\n    \u003c/div\u003e\n  );\n}\n```\n\n**Or use `\u003cAllow\u003e` — one component for all access control:**\n\n```tsx\nimport { Allow } from 'react-access-engine';\n\nfunction StorePage() {\n  return (\n    \u003cdiv\u003e\n      \u003cAllow permission=\"cart:manage\"\u003e\n        \u003cbutton\u003e🛒 Add to Cart\u003c/button\u003e\n      \u003c/Allow\u003e\n\n      \u003cAllow role=\"seller\"\u003e\n        \u003cSellerDashboard /\u003e\n      \u003c/Allow\u003e\n\n      \u003cAllow feature=\"ai-recommendations\" fallback={\u003cUpgradePrompt plan=\"premium\" /\u003e}\u003e\n        \u003cAIRecommendations /\u003e\n      \u003c/Allow\u003e\n\n      \u003cAllow plan=\"plus\" fallback={\u003cUpgradePrompt /\u003e}\u003e\n        \u003cLiveChat /\u003e\n      \u003c/Allow\u003e\n\n      {/* Combine conditions — all must pass */}\n      \u003cAllow permission=\"analytics:own-store\" role=\"seller\" plan=\"plus\"\u003e\n        \u003cStoreAnalytics /\u003e\n      \u003c/Allow\u003e\n    \u003c/div\u003e\n  );\n}\n```\n\nThat's it. One hook, one component. No need to memorize anything else.\n\n## API Reference\n\n### `useAccess()` — The Only Hook You Need\n\n```tsx\nconst { can, is, has, tier, user, roles, permissions } = useAccess();\n```\n\n| Method                 | Returns   | Purpose                                            |\n| ---------------------- | --------- | -------------------------------------------------- |\n| `can(perm, resource?)` | `boolean` | Check a permission (with optional ABAC resource)   |\n| `is(role)`             | `boolean` | Check if user has a role                           |\n| `has(feature)`         | `boolean` | Check if a feature flag is enabled                 |\n| `tier(plan)`           | `boolean` | Check if user's plan meets or exceeds the required |\n\nAlso exposes `user`, `roles`, `permissions`, `checkPermission`, `checkFeature`, `getExperiment` for advanced use.\n\n### `\u003cAllow\u003e` — The Only Component You Need\n\n```tsx\n\u003cAllow\n  permission=\"edit\" // permission check (optional)\n  role=\"admin\" // role check (optional)\n  feature=\"dark-mode\" // feature flag check (optional)\n  plan=\"pro\" // plan tier check (optional)\n  on={{ ownerId: id }} // resource for ABAC (optional)\n  match=\"all\" // \"all\" (default) or \"any\"\n  fallback={\u003cUpgrade /\u003e} // shown when denied (optional)\n\u003e\n  \u003cProtectedContent /\u003e\n\u003c/Allow\u003e\n```\n\nEvery prop is optional. Use only what you need. Combine freely.\n\n### Specialized Components\n\nFor specific use cases, these focused components are also available:\n\n#### `\u003cCan\u003e`\n\nPermission gate with roles, policy, and multi-permission support.\n\n```tsx\n\u003cCan perform=\"products:edit\" on={{ sellerId: product.sellerId }} fallback={\u003cReadOnly /\u003e}\u003e\n  \u003cProductEditor /\u003e\n\u003c/Can\u003e\n```\n\n#### `\u003cFeature\u003e`\n\nFeature flag gate.\n\n```tsx\n\u003cFeature name=\"live-chat\" fallback={\u003cEmailSupport /\u003e}\u003e\n  \u003cLiveChat /\u003e\n\u003c/Feature\u003e\n```\n\n#### `\u003cExperiment\u003e`\n\nA/B testing component.\n\n```tsx\n\u003cExperiment\n  id=\"checkout-redesign\"\n  variants={{\n    control: \u003cCheckoutA /\u003e,\n    'variant-b': \u003cCheckoutB /\u003e,\n  }}\n  fallback={\u003cCheckoutA /\u003e}\n/\u003e\n```\n\n#### `\u003cAccessGate\u003e`\n\nMulti-condition gate (like `\u003cAllow\u003e` but with `mode` instead of `match`).\n\n#### `\u003cPermissionGuard\u003e`\n\nRoute-level guard requiring ALL permissions to pass.\n\n#### `\u003cFeatureToggle\u003e`\n\nRender-prop variant of `\u003cFeature\u003e` — use when you need the `enabled` value.\n\n### Specialized Hooks\n\nWhen you need more detail than `useAccess()` provides:\n\n| Hook                             | Returns                                       | Purpose                             |\n| -------------------------------- | --------------------------------------------- | ----------------------------------- |\n| `usePermission(perm, resource?)` | `boolean`                                     | Check a single permission           |\n| `useRole()`                      | `{ roles, hasRole, hasAnyRole, hasAllRoles }` | Role checking utilities             |\n| `useFeature(name)`               | `{ enabled, reason }`                         | Check a feature flag with reason    |\n| `usePolicy(perm, resource?)`     | `{ allowed, matchedRule, reason }`            | Evaluate policy rules               |\n| `useExperiment(id)`              | `{ variant, active, experimentId }`           | Get experiment assignment           |\n| `usePlan()`                      | `{ plan, hasPlanAccess }`                     | Subscription plan checks            |\n| `useAccessDebug()`               | `AccessDebugInfo`                             | Debug metadata (when `debug: true`) |\n| `useRemoteConfig(base, loader)`  | `{ config, loading, error, stale, refresh }`  | Remote config with SWR pattern      |\n\n## Advanced Usage\n\n### Policy Engine (ABAC)\n\nDefine composable allow/deny rules for fine-grained access control:\n\n```typescript\nconst config = defineAccess({\n  roles: ['customer', 'seller', 'admin', 'support'],\n  permissions: {\n    customer: ['products:browse', 'cart:manage', 'orders:own', 'reviews:write'],\n    seller: ['products:browse', 'products:create', 'inventory:manage'],\n    admin: ['*'],\n    support: ['orders:view-all', 'orders:refund', 'reviews:moderate'],\n  },\n  policies: [\n    {\n      id: 'seller-own-products',\n      effect: 'allow',\n      permissions: ['products:edit', 'products:delete'],\n      condition: ({ user, resource }) =\u003e user.id === resource.sellerId,\n      description: 'Sellers can only edit/delete their own products',\n    },\n    {\n      id: 'refund-time-limit',\n      effect: 'deny',\n      permissions: ['orders:refund'],\n      condition: ({ resource }) =\u003e {\n        const daysSinceOrder = (Date.now() - resource.orderedAt) / 86400000;\n        return daysSinceOrder \u003e 30;\n      },\n      priority: 100,\n      description: 'Block refunds after 30 days',\n    },\n  ],\n});\n```\n\nUse policies in components with the `on` prop:\n\n```tsx\n// Seller can only edit their own products\n\u003cCan perform=\"products:edit\" on={{ sellerId: product.sellerId }}\u003e\n  \u003cbutton\u003eEdit Product\u003c/button\u003e\n\u003c/Can\u003e\n\n// Support can only refund recent orders\n\u003cCan perform=\"orders:refund\" on={{ orderedAt: order.createdAt }}\u003e\n  \u003cbutton\u003eProcess Refund\u003c/button\u003e\n\u003c/Can\u003e\n```\n\n### Percentage Rollouts\n\n```typescript\nfeatures: {\n  'new-ui': { rolloutPercentage: 25 },         // 25% of users\n  'beta-editor': {\n    rolloutPercentage: 50,\n    allowedRoles: ['editor', 'admin'],          // 50% of editors/admins only\n  },\n}\n```\n\nRollouts are deterministic — the same user always sees the same result (based on user ID hash).\n\n### Feature Dependencies\n\n```typescript\nfeatures: {\n  'base-analytics': { enabled: true },\n  'advanced-charts': {\n    enabled: true,\n    dependencies: ['base-analytics'],           // Only active if base-analytics is enabled\n  },\n}\n```\n\n### Subscription/Plan Gating\n\n```typescript\nconst config = defineAccess({\n  // ...\n  plans: ['free', 'starter', 'pro', 'enterprise'] as const,\n  features: {\n    'advanced-export': { enabled: true, allowedPlans: ['pro', 'enterprise'] },\n  },\n});\n\n// In components:\nconst { hasPlanAccess } = usePlan();\nif (hasPlanAccess('pro')) {\n  /* show pro features */\n}\n```\n\n### Plugin System\n\nHook into every access decision for logging, analytics, or custom behavior:\n\n```typescript\nimport {\n  createAuditLoggerPlugin,\n  createAnalyticsPlugin,\n  createOperatorPlugin,\n} from 'react-access-engine';\n\n// Built-in plugins — drop-in audit logging and analytics\nconst auditPlugin = createAuditLoggerPlugin({\n  logger: (entry) =\u003e fetch('/api/audit', { method: 'POST', body: JSON.stringify(entry) }),\n});\n\n// Custom condition operators for the ABAC condition engine\nconst operatorPlugin = createOperatorPlugin([\n  { name: 'regex', evaluate: (value, pattern) =\u003e new RegExp(pattern).test(String(value)) },\n  { name: 'lengthGt', evaluate: (value, min) =\u003e String(value).length \u003e Number(min) },\n]);\n\n// Custom plugin — e-commerce analytics\nconst storePlugin = {\n  name: 'store-analytics',\n  onAccessCheck: (event) =\u003e {\n    // Track when customers are blocked from actions (e.g. upgrade prompt triggers)\n    if (!event.granted) {\n      analytics.track('access_denied', { permission: event.permission, userId: event.userId });\n    }\n  },\n  onFeatureEvaluate: (event) =\u003e {\n    // Track feature flag exposure for product analytics\n    analytics.track('feature_exposure', { feature: event.feature, enabled: event.enabled });\n  },\n  onExperimentAssign: (event) =\u003e {\n    // Track A/B test variant assignments\n    analytics.track('experiment_assigned', {\n      experiment: event.experimentId,\n      variant: event.variant,\n    });\n  },\n};\n\nconst config = defineAccess({\n  // ...roles, permissions, features, experiments, policies...\n  plugins: [auditPlugin, storePlugin],\n});\n```\n\nPlugins fire on every `can()`, `has()`, and `useExperiment()` call — zero component changes needed.\n\n### Experiments / A/B Testing\n\n```typescript\nconst config = defineAccess({\n  // ...\n  experiments: {\n    'checkout-flow': {\n      id: 'checkout-flow',\n      variants: ['control', 'streamlined', 'one-page'] as const,\n      defaultVariant: 'control',\n      active: true,\n      allocation: { control: 34, streamlined: 33, 'one-page': 33 },\n    },\n  },\n});\n\n// In component:\nconst { variant } = useExperiment('checkout-flow');\n```\n\n### Debug Mode\n\n```typescript\nconst config = defineAccess({\n  // ...\n  debug: true,\n});\n\n// In a dev overlay:\nconst debugInfo = useAccessDebug();\nconsole.log(debugInfo.lastChecks); // Recent permission checks\nconsole.log(debugInfo.lastFeatureEvals); // Recent feature evaluations\n```\n\n### Remote Config\n\nFetch access configuration from an API with stale-while-revalidate, polling, and optional signature verification:\n\n```tsx\nimport { useRemoteConfig, AccessProvider, defineAccess } from 'react-access-engine';\n\nconst baseConfig = defineAccess({\n  roles: ['viewer', 'editor', 'admin'],\n  permissions: { viewer: ['read'], editor: ['read', 'write'], admin: ['*'] },\n});\n\nfunction App() {\n  const { config, loading, error, stale, refresh } = useRemoteConfig(baseConfig, {\n    load: () =\u003e fetch('/api/access-config').then((r) =\u003e r.json()),\n    pollInterval: 60_000, // Re-fetch every 60s\n    signatureHeader: 'x-config-signature',\n    verifySignature: (payload, sig) =\u003e verify(payload, sig),\n  });\n\n  if (loading) return \u003cSpinner /\u003e;\n\n  return (\n    \u003cAccessProvider config={config} user={user}\u003e\n      \u003cYourApp /\u003e\n    \u003c/AccessProvider\u003e\n  );\n}\n```\n\nFor advanced use, the `RemoteConfigEngine` class is also exported for direct programmatic control.\n\n### Merging Configs\n\nUse `mergeConfigs` to combine a base config with overrides (e.g., remote patches, environment-specific tweaks):\n\n```typescript\nimport { defineAccess, mergeConfigs } from 'react-access-engine';\n\nconst base = defineAccess({\n  roles: ['viewer', 'admin'],\n  permissions: { viewer: ['read'], admin: ['*'] },\n});\nconst overrides = { features: { 'new-ui': true } };\n\nconst merged = mergeConfigs(base, overrides);\n// merged has all base config + new-ui feature added\n```\n\n### Condition Engine (Declarative ABAC)\n\nFor declarative attribute-based conditions without writing callback functions, use the condition engine:\n\n```typescript\nimport { evaluateConditions, buildConditionContext } from 'react-access-engine';\n\nconst conditions = [\n  { field: 'user.role', operator: 'in' as const, value: ['admin', 'manager'] },\n  { field: 'resource.status', operator: 'equals' as const, value: 'draft' },\n];\n\nconst context = buildConditionContext(\n  { role: 'admin', id: 'u1' },\n  { status: 'draft', ownerId: 'u1' },\n);\n\nconst allowed = evaluateConditions(conditions, context); // true\n```\n\nBuilt-in operators: `equals`, `notEquals`, `in`, `notIn`, `includes`, `greaterThan`, `lessThan`, `greaterThanOrEqual`, `lessThanOrEqual`, `exists`.\n\nAdd custom operators via the `createOperatorPlugin` plugin factory (see Plugin System above).\n\n## Full E-commerce Example\n\nA complete e-commerce store using **every feature** — RBAC, ABAC policies, feature flags, A/B experiments, plan gating, plugins, and devtools.\n\n### Config — One Source of Truth\n\n```typescript\nimport { defineAccess } from 'react-access-engine';\n\nconst storeConfig = defineAccess({\n  // ── Roles \u0026 Permissions ──────────────────────────────────────────────\n  roles: ['customer', 'seller', 'admin', 'support'],\n  permissions: {\n    customer: ['products:browse', 'cart:manage', 'orders:own', 'reviews:write', 'wishlist:manage'],\n    seller: [\n      'products:browse',\n      'products:create',\n      'products:edit-own',\n      'inventory:manage',\n      'analytics:own-store',\n      'orders:seller-view',\n      'coupons:create',\n    ],\n    admin: ['*'],\n    support: ['orders:view-all', 'orders:refund', 'reviews:moderate', 'tickets:manage'],\n  },\n\n  // ── Plans ────────────────────────────────────────────────────────────\n  plans: ['free', 'plus', 'premium'],\n\n  // ── Feature Flags ───────────────────────────────────────────────────\n  features: {\n    'quick-buy': true, // Simple toggle\n    wishlist: true,\n    'reviews-v2': { rolloutPercentage: 50 }, // 50% rollout\n    'ai-recommendations': { enabled: true, allowedPlans: ['premium'] }, // Plan-gated\n    'live-chat': { enabled: true, allowedPlans: ['plus', 'premium'] },\n    'loyalty-points': { enabled: true, allowedPlans: ['plus', 'premium'] },\n    'bulk-discount': { enabled: true, allowedRoles: ['seller'] }, // Role-gated\n  },\n\n  // ── A/B Experiments ─────────────────────────────────────────────────\n  experiments: {\n    'checkout-layout': {\n      id: 'checkout-layout',\n      variants: ['classic', 'one-page', 'step-wizard'],\n      defaultVariant: 'classic',\n      active: true,\n      allocation: { classic: 34, 'one-page': 33, 'step-wizard': 33 },\n    },\n    'promo-banner': {\n      id: 'promo-banner',\n      variants: ['seasonal', 'loyalty', 'referral'],\n      defaultVariant: 'seasonal',\n      active: true,\n      allocation: { seasonal: 34, loyalty: 33, referral: 33 },\n    },\n  },\n\n  // ── ABAC Policies ───────────────────────────────────────────────────\n  policies: [\n    {\n      id: 'seller-own-products',\n      effect: 'allow',\n      permissions: ['products:edit', 'products:delete'],\n      condition: ({ user, resource }) =\u003e user.id === resource.sellerId,\n      description: 'Sellers can only edit/delete their own products',\n    },\n    {\n      id: 'refund-time-limit',\n      effect: 'deny',\n      permissions: ['orders:refund'],\n      condition: ({ resource }) =\u003e {\n        const daysSinceOrder = (Date.now() - resource.orderedAt) / 86400000;\n        return daysSinceOrder \u003e 30;\n      },\n      priority: 100,\n      description: 'Block refunds after 30 days',\n    },\n    {\n      id: 'premium-bulk-orders',\n      effect: 'allow',\n      permissions: ['orders:bulk-create'],\n      condition: ({ user }) =\u003e user.plan === 'premium',\n      description: 'Only premium users can place bulk orders',\n    },\n  ],\n\n  // ── Plugins ─────────────────────────────────────────────────────────\n  plugins: [\n    {\n      name: 'store-audit',\n      onAccessCheck: (event) =\u003e {\n        // Log every access decision for compliance\n        console.log(`[AUDIT] ${event.permission}: ${event.granted ? '✅' : '❌'}`);\n      },\n      onFeatureEvaluate: (event) =\u003e {\n        // Track feature flag exposure\n        analytics.track('feature_exposure', { feature: event.feature, enabled: event.enabled });\n      },\n      onExperimentAssign: (event) =\u003e {\n        // Track A/B test assignments\n        analytics.track('experiment_assigned', { id: event.experimentId, variant: event.variant });\n      },\n    },\n  ],\n\n  debug: true, // Enable DevTools overlay\n});\n```\n\n### Components — Protect Everything\n\n```tsx\nimport {\n  AccessProvider,\n  Allow,\n  Can,\n  Feature,\n  Experiment,\n  useAccess,\n  useExperiment,\n} from 'react-access-engine';\nimport { AccessDevtools } from 'react-access-engine-devtools';\n\n// ── Product Card ───────────────────────────────────────────────────────\nfunction ProductCard({ product }) {\n  const { can, has } = useAccess();\n\n  return (\n    \u003cdiv\u003e\n      \u003ch3\u003e\n        {product.name} — ${product.price}\n      \u003c/h3\u003e\n      \u003cAllow permission=\"cart:manage\"\u003e\n        \u003cbutton\u003e🛒 Add to Cart\u003c/button\u003e\n      \u003c/Allow\u003e\n      {has('quick-buy') \u0026\u0026 can('cart:manage') \u0026\u0026 \u003cbutton\u003e⚡ Buy Now\u003c/button\u003e}\n      \u003cAllow feature=\"wishlist\"\u003e\n        \u003cbutton\u003e❤️ Wishlist\u003c/button\u003e\n      \u003c/Allow\u003e\n    \u003c/div\u003e\n  );\n}\n\n// ── ABAC: Seller can only edit own products ────────────────────────────\nfunction ProductActions({ product }) {\n  return (\n    \u003cCan perform=\"products:edit\" on={{ sellerId: product.sellerId }}\u003e\n      \u003cbutton\u003e✏️ Edit\u003c/button\u003e\n    \u003c/Can\u003e\n  );\n}\n\n// ── ABAC: Refund blocked after 30 days ─────────────────────────────────\nfunction RefundButton({ order }) {\n  return (\n    \u003cCan\n      perform=\"orders:refund\"\n      on={{ orderedAt: order.createdAt }}\n      fallback={\u003cspan\u003eRefund window expired\u003c/span\u003e}\n    \u003e\n      \u003cbutton\u003e💸 Process Refund\u003c/button\u003e\n    \u003c/Can\u003e\n  );\n}\n\n// ── Plan-gated feature with upgrade prompt ─────────────────────────────\nfunction AIRecommendations() {\n  return (\n    \u003cAllow feature=\"ai-recommendations\" fallback={\u003cUpgradePrompt plan=\"premium\" /\u003e}\u003e\n      \u003cdiv\u003e🤖 AI Picks: Earbuds, Phone Case, USB-C Cable\u003c/div\u003e\n    \u003c/Allow\u003e\n  );\n}\n\n// ── A/B Test: Declarative variant rendering ────────────────────────────\nfunction PromoBanner() {\n  return (\n    \u003cExperiment\n      id=\"promo-banner\"\n      variants={{\n        seasonal: \u003cdiv\u003e🎄 Winter Sale — 50% off!\u003c/div\u003e,\n        loyalty: \u003cdiv\u003e⭐ Double Points Week!\u003c/div\u003e,\n        referral: \u003cdiv\u003e👥 Refer \u0026 Save — Give $10, Get $10\u003c/div\u003e,\n      }}\n      fallback={\u003cdiv\u003eLoading promotion...\u003c/div\u003e}\n    /\u003e\n  );\n}\n\n// ── A/B Test: Hook-based variant ───────────────────────────────────────\nfunction CheckoutSection() {\n  const { variant } = useExperiment('checkout-layout');\n  return (\n    \u003cdiv\u003e\n      {variant === 'classic' \u0026\u0026 \u003cClassicCheckout /\u003e}\n      {variant === 'one-page' \u0026\u0026 \u003cOnePageCheckout /\u003e}\n      {variant === 'step-wizard' \u0026\u0026 \u003cWizardCheckout /\u003e}\n    \u003c/div\u003e\n  );\n}\n\n// ── Seller Dashboard ───────────────────────────────────────────────────\nfunction SellerDashboard() {\n  return (\n    \u003cAllow role=\"seller\"\u003e\n      \u003ch3\u003e📦 Seller Dashboard\u003c/h3\u003e\n      \u003cCan perform=\"inventory:manage\"\u003e\n        \u003cp\u003eInventory Manager\u003c/p\u003e\n      \u003c/Can\u003e\n      \u003cCan perform=\"analytics:own-store\"\u003e\n        \u003cp\u003eStore Analytics\u003c/p\u003e\n      \u003c/Can\u003e\n      \u003cCan perform=\"coupons:create\"\u003e\n        \u003cp\u003eCreate Coupons\u003c/p\u003e\n      \u003c/Can\u003e\n      \u003cFeature name=\"bulk-discount\"\u003e\n        \u003cp\u003eBulk Discount Pricing\u003c/p\u003e\n      \u003c/Feature\u003e\n    \u003c/Allow\u003e\n  );\n}\n\n// ── Support Tools ──────────────────────────────────────────────────────\nfunction SupportTools() {\n  return (\n    \u003cAllow role=\"support\"\u003e\n      \u003cCan perform=\"orders:view-all\"\u003e\n        \u003cp\u003eView All Orders\u003c/p\u003e\n      \u003c/Can\u003e\n      \u003cCan perform=\"orders:refund\"\u003e\n        \u003cbutton\u003eProcess Refund\u003c/button\u003e\n      \u003c/Can\u003e\n      \u003cCan perform=\"reviews:moderate\"\u003e\n        \u003cbutton\u003eModerate Reviews\u003c/button\u003e\n      \u003c/Can\u003e\n      \u003cCan perform=\"tickets:manage\"\u003e\n        \u003cbutton\u003eManage Tickets\u003c/button\u003e\n      \u003c/Can\u003e\n    \u003c/Allow\u003e\n  );\n}\n\n// ── App — Everything together ──────────────────────────────────────────\nfunction App() {\n  const user = useCurrentUser();\n  return (\n    \u003cAccessProvider config={storeConfig} user={{ id: user.id, roles: user.roles, plan: user.plan }}\u003e\n      \u003cProductCard product={product} /\u003e\n      \u003cProductActions product={product} /\u003e\n      \u003cAIRecommendations /\u003e\n      \u003cPromoBanner /\u003e\n      \u003cCheckoutSection /\u003e\n      \u003cSellerDashboard /\u003e\n      \u003cSupportTools /\u003e\n      \u003cAccessDevtools position=\"bottom-right\" /\u003e\n    \u003c/AccessProvider\u003e\n  );\n}\n```\n\n\u003e 📂 Full runnable example: [`examples/ecommerce`](examples/ecommerce) — switch between Customer, Seller, Admin, and Support users to see everything change in real time.\n\n## SSR / Next.js\n\nreact-access-engine is SSR-safe by design:\n\n- All hooks return deterministic values (no `useEffect` for initial state)\n- No `window`/`document`/`localStorage` access in core evaluation\n- Rollouts use user ID hashing, not `Math.random()`\n- All components use `'use client'` directive for App Router compatibility\n\n```tsx\n// app/layout.tsx (Next.js App Router)\nimport { AccessProvider } from 'react-access-engine';\n\nexport default function RootLayout({ children }) {\n  const user = await getServerUser(); // Your server-side user fetching\n  return (\n    \u003cAccessProvider config={config} user={user}\u003e\n      {children}\n    \u003c/AccessProvider\u003e\n  );\n}\n```\n\n## TypeScript\n\n`defineAccess` works without `as const`. Add it when you want literal-type inference:\n\n```typescript\nconst config = defineAccess({\n  roles: ['customer', 'seller', 'admin'] as const,\n  permissions: {\n    customer: ['products:browse', 'cart:manage'] as const,\n    seller: ['products:create', 'inventory:manage'] as const,\n    admin: ['*'] as const,\n  },\n});\n\n// InferRoles\u003ctypeof config\u003e = 'customer' | 'seller' | 'admin'\n// InferPermissions\u003ctypeof config\u003e = 'products:browse' | 'cart:manage' | 'products:create' | 'inventory:manage' | '*'\n```\n\nWithout `as const`, everything still works — you just get `string` instead of literal union types.\n\n## Backend / Node.js Usage\n\nThe same `react-access-engine` package works on the backend. All engine functions are **pure logic** — no React dependency — so you can import them directly in Node.js, Express, Fastify, Deno, or any JavaScript runtime.\n\n```bash\nnpm install react-access-engine\n```\n\n### Shared Config Pattern\n\nDefine your access config **once** and share it between frontend and backend:\n\n```typescript\n// shared/access-config.ts — single source of truth\nimport { defineAccess } from 'react-access-engine';\n\nexport const accessConfig = defineAccess({\n  roles: ['viewer', 'editor', 'admin', 'super_admin'] as const,\n  permissions: {\n    viewer: ['articles:read', 'comments:read', 'dashboard:view'],\n    editor: ['articles:read', 'articles:write', 'articles:publish', 'comments:*', 'media:upload'],\n    admin: ['articles:*', 'comments:*', 'media:*', 'users:*', 'settings:*', 'analytics:*'],\n    super_admin: ['*'],\n  },\n  plans: ['free', 'starter', 'professional', 'enterprise'] as const,\n  features: {\n    dark_mode: true,\n    ai_assistant: { enabled: true, allowedRoles: ['admin', 'super_admin'] },\n    api_access: { enabled: true, allowedPlans: ['professional', 'enterprise'] },\n    beta_features: { enabled: true, allowedEnvironments: ['development', 'staging'] },\n  },\n  experiments: {\n    checkout_flow: {\n      id: 'checkout_flow',\n      variants: ['control', 'single_page', 'multi_step'] as const,\n      defaultVariant: 'control',\n      active: true,\n      allocation: { control: 34, single_page: 33, multi_step: 33 },\n    },\n  },\n  policies: [\n    {\n      id: 'owner-can-edit',\n      effect: 'allow',\n      permissions: ['articles:write'],\n      priority: 100,\n      condition: ({ user, resource }) =\u003e resource?.authorId === user.id,\n    },\n    {\n      id: 'deny-delete-published',\n      effect: 'deny',\n      permissions: ['articles:delete'],\n      priority: 90,\n      condition: ({ resource }) =\u003e resource?.status === 'published',\n    },\n  ],\n  environment: { name: 'production' },\n});\n\nexport type AppRoles = (typeof accessConfig.roles)[number];\n```\n\n### Express API Example\n\nImport engine functions and use them in your route handlers — no middleware package needed:\n\n```typescript\n// server.ts\nimport express from 'express';\nimport {\n  hasPermission,\n  hasRole,\n  evaluateFeature,\n  evaluatePolicy,\n  assignExperiment,\n  hasPlanAccess,\n  getPlanTier,\n  getPermissionsForUser,\n  evaluateAllFeatures,\n} from 'react-access-engine';\nimport type { UserContext } from 'react-access-engine';\nimport { accessConfig } from './shared/access-config';\n\nconst app = express();\napp.use(express.json());\n\n// Your auth middleware sets req.user\napp.use(async (req: any, _res, next) =\u003e {\n  const token = req.headers.authorization?.replace('Bearer ', '');\n  req.user = await getUserFromToken(token); // { id, roles, plan, attributes }\n  next();\n});\n```\n\n### Permission Guards (Middleware)\n\n```typescript\n// Reusable permission guard\nfunction requirePermission(...permissions: string[]) {\n  return (req: any, res: express.Response, next: express.NextFunction) =\u003e {\n    const user: UserContext = req.user;\n    if (!user) return res.status(401).json({ error: 'Not authenticated' });\n\n    for (const perm of permissions) {\n      if (!hasPermission(user, perm, accessConfig)) {\n        return res.status(403).json({ error: `Permission denied: ${perm}` });\n      }\n    }\n    next();\n  };\n}\n\n// Usage\napp.get('/api/articles', requirePermission('articles:read'), (req: any, res) =\u003e {\n  res.json({ articles: getAllArticles() });\n});\n\napp.post('/api/articles', requirePermission('articles:write'), (req: any, res) =\u003e {\n  res.status(201).json({ article: createArticle(req.body, req.user) });\n});\n\napp.delete('/api/articles/:id', requirePermission('articles:delete'), (req: any, res) =\u003e {\n  // Additional ABAC policy check\n  const article = getArticleById(req.params.id);\n  const policy = evaluatePolicy('articles:delete', req.user, accessConfig, { resource: article });\n\n  if (policy.effect === 'deny') {\n    return res.status(403).json({\n      error: 'Policy denied',\n      rule: policy.matchedRule,\n      reason: policy.reason,\n    });\n  }\n\n  deleteArticle(req.params.id);\n  res.json({ message: 'Deleted' });\n});\n```\n\n### Feature-Gated Endpoints\n\n```typescript\napp.get('/api/ai/suggest', (req: any, res) =\u003e {\n  const user: UserContext = req.user;\n  const feature = evaluateFeature('ai_assistant', user, accessConfig, accessConfig.environment);\n\n  if (!feature.enabled) {\n    return res.status(403).json({\n      error: 'AI Assistant not available for your account',\n      reason: feature.reason, // 'role', 'plan', 'rollout', etc.\n    });\n  }\n\n  res.json({ suggestions: generateAISuggestions(req.body) });\n});\n\n// Get all feature flags for current user (useful for frontend hydration)\napp.get('/api/features', (req: any, res) =\u003e {\n  const allFeatures = evaluateAllFeatures(req.user, accessConfig, accessConfig.environment);\n  res.json({\n    features: Array.from(allFeatures.entries()).map(([name, result]) =\u003e ({\n      name,\n      enabled: result.enabled,\n      reason: result.reason,\n    })),\n  });\n});\n```\n\n### Plan-Gated Endpoints\n\n```typescript\napp.get('/api/integrations', (req: any, res) =\u003e {\n  const user: UserContext = req.user;\n\n  if (!hasPlanAccess(user, 'professional', accessConfig)) {\n    return res.status(403).json({\n      error: 'Integrations require Professional plan or higher',\n      currentPlan: user.plan,\n      currentTier: getPlanTier(user, accessConfig),\n    });\n  }\n\n  res.json({ integrations: getAvailableIntegrations() });\n});\n```\n\n### ABAC Policy Evaluation in API\n\n```typescript\napp.post('/api/check/policy', (req: any, res) =\u003e {\n  const { permission, resource } = req.body;\n\n  const result = evaluatePolicy(permission, req.user, accessConfig, {\n    resource,\n    environment: accessConfig.environment,\n  });\n\n  res.json({\n    permission,\n    effect: result.effect, // 'allow' or 'deny'\n    matchedRule: result.matchedRule, // e.g. 'owner-can-edit'\n    reason: result.reason,\n  });\n});\n```\n\n### A/B Experiments on Backend\n\n```typescript\napp.get('/api/experiment/:id', (req: any, res) =\u003e {\n  const experiment = accessConfig.experiments?.[req.params.id];\n  if (!experiment) return res.status(404).json({ error: 'Experiment not found' });\n\n  const assignment = assignExperiment(experiment, req.user);\n  // assignment = { experimentId, variant, active }\n\n  res.json(assignment);\n});\n\n// Use experiments to vary API behavior\napp.get('/api/recommendations', (req: any, res) =\u003e {\n  const { variant } = assignExperiment(accessConfig.experiments.checkout_flow, req.user);\n\n  if (variant === 'single_page') {\n    return res.json({ layout: 'single', items: getCompactRecommendations() });\n  }\n  res.json({ layout: 'default', items: getFullRecommendations() });\n});\n```\n\n### Full User Access Snapshot\n\n```typescript\n// Return everything a frontend needs in one call\napp.get('/api/me/access', (req: any, res) =\u003e {\n  const user: UserContext = req.user;\n\n  res.json({\n    user: { id: user.id, roles: user.roles, plan: user.plan },\n    permissions: [...getPermissionsForUser(user, accessConfig)],\n    planTier: getPlanTier(user, accessConfig),\n    features: Array.from(evaluateAllFeatures(user, accessConfig).entries()).map(\n      ([name, result]) =\u003e ({ name, ...result }),\n    ),\n    experiments: Object.values(accessConfig.experiments ?? {}).map((exp) =\u003e\n      assignExperiment(exp, user),\n    ),\n  });\n});\n```\n\n### Using Plugins on the Backend\n\nPlugins work identically on Node.js:\n\n```typescript\nimport {\n  PluginEngine,\n  DebugEngine,\n  createAuditLoggerPlugin,\n  createAnalyticsPlugin,\n} from 'react-access-engine';\n\n// Initialize plugin engine\nconst pluginEngine = new PluginEngine();\npluginEngine.registerAll([\n  createAuditLoggerPlugin({\n    log: (entry) =\u003e auditService.write(entry),\n    deniedOnly: true,\n  }),\n  createAnalyticsPlugin({\n    adapter: { track: (name, props) =\u003e mixpanel.track(name, props) },\n  }),\n]);\n\n// Emit events from your route handlers\napp.get('/api/articles', (req: any, res) =\u003e {\n  const granted = hasPermission(req.user, 'articles:read', config);\n  pluginEngine.emitAccessCheck({\n    permission: 'articles:read',\n    granted,\n    roles: [...req.user.roles],\n    timestamp: Date.now(),\n  });\n  // ...\n});\n\n// Debug engine for audit trails\nconst debugEngine = new DebugEngine();\ndebugEngine.setConfig(config);\ndebugEngine.recordAccessCheck({\n  permission: 'articles:read',\n  granted: true,\n  roles: ['admin'],\n  timestamp: Date.now(),\n});\nconsole.log(debugEngine.getDebugInfo()); // { lastChecks, lastFeatureEvals, lastPolicyEvals, configSnapshot, timestamp }\n```\n\n## Architecture\n\n```\n┌──────────────────────────────────────────────────────────────┐\n│                      AccessProvider                          │\n│                                                              │\n│  ┌───────────┐ ┌────────────┐ ┌───────────────┐             │\n│  │ Role      │ │ Permission │ │ Policy Engine │             │\n│  │ Engine    │ │ Engine     │ │ (ABAC rules)  │             │\n│  └───────────┘ └────────────┘ └───────────────┘             │\n│                                                              │\n│  ┌───────────┐ ┌────────────┐ ┌───────────────┐             │\n│  │ Feature   │ │ Experiment │ │ Plan Engine   │             │\n│  │ Engine    │ │ Engine     │ │               │             │\n│  └───────────┘ └────────────┘ └───────────────┘             │\n│                                                              │\n│  ┌───────────┐ ┌────────────┐ ┌───────────────────────────┐ │\n│  │ Plugin    │ │ Debug      │ │ Condition Engine          │ │\n│  │ Engine    │ │ Engine     │ │ (declarative ABAC)        │ │\n│  └───────────┘ └────────────┘ └───────────────────────────┘ │\n│                                                              │\n│  ┌───────────────────────────┐                               │\n│  │ Remote Config Engine      │                               │\n│  │ (SWR + polling)           │                               │\n│  └───────────────────────────┘                               │\n└──────────────────────────────────────────────────────────────┘\n```\n\nEach engine is a pure function module. Unused engines are tree-shaken from bundles.\n\nAll engine functions exported for **backend / Node.js** use (no React dependency):\n\n- **Roles** — `hasRole`, `hasAnyRole`, `hasAllRoles`\n- **Permissions** — `hasPermission`, `hasAnyPermission`, `hasAllPermissions`, `getPermissionsForUser`\n- **Features** — `evaluateFeature`, `evaluateAllFeatures`\n- **Policies** — `evaluatePolicy`\n- **Experiments** — `assignExperiment`\n- **Plans** — `hasPlanAccess`, `getPlanTier`\n- **Conditions** — `evaluateCondition`, `evaluateConditions`, `buildConditionContext`\n- **Classes** — `PluginEngine`, `DebugEngine`\n\n## Testing\n\n**620 tests passing** across 18 test files covering every engine, hook, and component.\n\n```bash\npnpm test\n```\n\n- **Engines (344)** — Role (33), Permission (43), Feature (54), Plan (27), Policy (44), Experiment (29), Condition (41), Plugin (25), Debug (14), Remote Config (56)\n- **React (181)** — Hooks (74), Components (107)\n- **Integration \u0026 Utils (95)** — Integration (12), Utilities (13), Plugin Integration (17), plus additional edge-case coverage\n\n## Monorepo Structure\n\n```\nreact-access-engine/\n├── packages/\n│   ├── react-access-engine/  # Core library (published to npm)\n│   ├── devtools/              # Dev overlay for inspecting access decisions\n│   └── shared/                # Internal shared utilities\n├── apps/\n│   ├── docs/                  # Next.js documentation site\n│   └── playground/            # Interactive playground\n├── examples/\n│   ├── basic/                 # Simple RBAC example\n│   ├── ecommerce/             # E-commerce store (roles, plans, flags, A/B)\n│   ├── nextjs/                # Next.js App Router integration\n│   ├── saas-dashboard/        # SaaS with plan gating \u0026 multi-role\n│   ├── feature-rollout/       # Percentage rollouts \u0026 feature gates\n│   ├── experiments/           # A/B testing with Experiment component\n│   └── abac-policies/         # ABAC policy engine patterns\n├── .github/workflows/         # CI, release, and docs pipelines\n├── .changeset/                # Changesets for version management\n└── turbo.json                 # Turborepo task config\n```\n\n### Development\n\n```bash\n# Install dependencies\npnpm install\n\n# Build all packages\npnpm build\n\n# Run tests\npnpm test\n\n# Type-check\npnpm typecheck\n\n# Start playground\npnpm --filter playground dev\n\n# Start docs\npnpm --filter docs dev\n```\n\n### Quality \u0026 Release Readiness\n\nSee [QUALITY.md](QUALITY.md) for test coverage details, rerender strategy, bundle size analysis, SSR safety, and the full QA checklist.\n\n## Comparison\n\n**vs. RBAC libraries** — adds ABAC policies, feature flags, A/B experiments, plan gating, remote config, plugins, and DevTools. Most RBAC libs only cover roles and permissions.\n\n**vs. Feature flag services** (LaunchDarkly, Unleash) — adds RBAC, ABAC, plan gating, and experiments in one zero-dependency package. No vendor lock-in, no external service, fully self-hosted.\n\n**vs. DIY** — type-safe, tested (620 tests), tree-shakeable, SSR-safe, with plugin system and DevTools. Replaces months of custom code with one `npm install`.\n\n## Community\n\n- [Documentation](https://react-access-engine.dev/docs)\n- [Playground](https://react-access-engine.dev/playground) — Try it live\n- [GitHub Discussions](https://github.com/abhishekayu/react-access-engine/discussions) — Questions \u0026 ideas\n- [Issue Tracker](https://github.com/abhishekayu/react-access-engine/issues) — Bug reports \u0026 feature requests\n- [Changelog](CHANGELOG.md)\n\n## Contributing\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.\n\n## Security\n\nTo report a vulnerability, please see [SECURITY.md](SECURITY.md).\n\n## Sponsors\n\nIf react-access-engine saves you time, consider [sponsoring the project](https://github.com/sponsors/abhishekayu).\n\n## License\n\n[MIT](LICENSE) © [Abhishek Verma](https://github.com/abhishekayu)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fabhishekayu%2Freact-access-engine","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fabhishekayu%2Freact-access-engine","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fabhishekayu%2Freact-access-engine/lists"}