secure-role-guard 1.0.2 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/core/permission-engine.ts","../../src/adapters/nextjs.ts"],"names":[],"mappings":"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","file":"nextjs.mjs","sourcesContent":["/**\r\n * Secure Role Guard - Permission Engine\r\n *\r\n * Pure functions for permission checking.\r\n * Zero side effects, zero dependencies, zero mutations.\r\n */\r\n\r\nimport type { UserContext, RoleRegistry, PermissionCheckResult } from \"./types\";\r\n\r\n/** Wildcard permission that grants all access */\r\nconst WILDCARD_PERMISSION = \"*\";\r\n\r\n/**\r\n * Collects all permissions for a user from their roles and direct permissions.\r\n *\r\n * @param user - The user context\r\n * @param registry - The role registry to resolve role permissions\r\n * @returns Set of all permissions (for efficient lookup)\r\n */\r\nfunction collectUserPermissions(\r\n user: UserContext,\r\n registry: RoleRegistry\r\n): ReadonlySet<string> {\r\n const permissions = new Set<string>();\r\n\r\n // Add direct user permissions\r\n if (user.permissions !== undefined) {\r\n for (const permission of user.permissions) {\r\n permissions.add(permission);\r\n }\r\n }\r\n\r\n // Add permissions from all user roles\r\n if (user.roles !== undefined) {\r\n for (const role of user.roles) {\r\n const rolePermissions = registry.getPermissions(role);\r\n for (const permission of rolePermissions) {\r\n permissions.add(permission);\r\n }\r\n }\r\n }\r\n\r\n return permissions;\r\n}\r\n\r\n/**\r\n * Checks if a permission set contains a wildcard that grants the requested permission.\r\n *\r\n * Supports:\r\n * - Exact wildcard (*) - grants everything\r\n * - Namespace wildcards (user.*) - grants all under namespace\r\n *\r\n * @param permissions - Set of permissions to check\r\n * @param requested - The permission being requested\r\n * @returns True if wildcard grants access\r\n */\r\nfunction hasWildcardAccess(\r\n permissions: ReadonlySet<string>,\r\n requested: string\r\n): boolean {\r\n // Check for global wildcard\r\n if (permissions.has(WILDCARD_PERMISSION)) {\r\n return true;\r\n }\r\n\r\n // Check for namespace wildcards (e.g., user.* grants user.read)\r\n const parts = requested.split(\".\");\r\n let namespace = \"\";\r\n\r\n for (let i = 0; i < parts.length - 1; i++) {\r\n const part = parts[i];\r\n if (part !== undefined) {\r\n namespace = namespace === \"\" ? part : `${namespace}.${part}`;\r\n if (permissions.has(`${namespace}.*`)) {\r\n return true;\r\n }\r\n }\r\n }\r\n\r\n return false;\r\n}\r\n\r\n/**\r\n * Checks if a user has a specific permission.\r\n *\r\n * SECURITY GUARANTEES:\r\n * - Deny by default: undefined/null/empty always returns false\r\n * - Pure function: no side effects, no mutations\r\n * - Deterministic: same inputs always produce same output\r\n *\r\n * @param user - The user context to check\r\n * @param permission - The permission required\r\n * @param registry - The role registry for resolving roles\r\n * @returns True if user has the permission, false otherwise\r\n *\r\n * @example\r\n * const canEdit = canUser(currentUser, 'user.update', roleRegistry);\r\n */\r\nexport function canUser(\r\n user: UserContext | null | undefined,\r\n permission: string,\r\n registry: RoleRegistry\r\n): boolean {\r\n // DENY BY DEFAULT: No user context means no access\r\n if (user === null || user === undefined) {\r\n return false;\r\n }\r\n\r\n // DENY BY DEFAULT: Empty permission request is invalid\r\n if (permission === \"\" || permission.trim() === \"\") {\r\n return false;\r\n }\r\n\r\n const userPermissions = collectUserPermissions(user, registry);\r\n\r\n // Check for exact permission match\r\n if (userPermissions.has(permission)) {\r\n return true;\r\n }\r\n\r\n // Check for wildcard access\r\n if (hasWildcardAccess(userPermissions, permission)) {\r\n return true;\r\n }\r\n\r\n // DENY BY DEFAULT\r\n return false;\r\n}\r\n\r\n/**\r\n * Checks if a user has ALL of the specified permissions.\r\n *\r\n * @param user - The user context to check\r\n * @param permissions - Array of permissions required\r\n * @param registry - The role registry\r\n * @returns True if user has ALL permissions\r\n *\r\n * @example\r\n * const canManage = canUserAll(user, ['user.read', 'user.update'], registry);\r\n */\r\nexport function canUserAll(\r\n user: UserContext | null | undefined,\r\n permissions: readonly string[],\r\n registry: RoleRegistry\r\n): boolean {\r\n // DENY BY DEFAULT: Empty permissions array\r\n if (permissions.length === 0) {\r\n return false;\r\n }\r\n\r\n for (const permission of permissions) {\r\n if (!canUser(user, permission, registry)) {\r\n return false;\r\n }\r\n }\r\n\r\n return true;\r\n}\r\n\r\n/**\r\n * Checks if a user has ANY of the specified permissions.\r\n *\r\n * @param user - The user context to check\r\n * @param permissions - Array of permissions to check\r\n * @param registry - The role registry\r\n * @returns True if user has at least one permission\r\n *\r\n * @example\r\n * const canView = canUserAny(user, ['report.view', 'report.admin'], registry);\r\n */\r\nexport function canUserAny(\r\n user: UserContext | null | undefined,\r\n permissions: readonly string[],\r\n registry: RoleRegistry\r\n): boolean {\r\n // DENY BY DEFAULT: Empty permissions array\r\n if (permissions.length === 0) {\r\n return false;\r\n }\r\n\r\n for (const permission of permissions) {\r\n if (canUser(user, permission, registry)) {\r\n return true;\r\n }\r\n }\r\n\r\n return false;\r\n}\r\n\r\n/**\r\n * Extended permission check with detailed result.\r\n * Useful for debugging and logging.\r\n *\r\n * @param user - The user context\r\n * @param permission - Permission to check\r\n * @param registry - The role registry\r\n * @returns PermissionCheckResult with allowed status and reason\r\n */\r\nexport function checkPermission(\r\n user: UserContext | null | undefined,\r\n permission: string,\r\n registry: RoleRegistry\r\n): PermissionCheckResult {\r\n if (user === null || user === undefined) {\r\n return Object.freeze({\r\n allowed: false,\r\n reason: \"No user context provided\",\r\n });\r\n }\r\n\r\n if (permission === \"\" || permission.trim() === \"\") {\r\n return Object.freeze({\r\n allowed: false,\r\n reason: \"Empty permission requested\",\r\n });\r\n }\r\n\r\n const allowed = canUser(user, permission, registry);\r\n\r\n return Object.freeze({\r\n allowed,\r\n reason: allowed\r\n ? `Permission \"${permission}\" granted`\r\n : `Permission \"${permission}\" denied`,\r\n });\r\n}\r\n","/**\r\n * Secure Role Guard - Next.js Adapter\r\n *\r\n * Thin wrapper for Next.js API routes and Route Handlers.\r\n * Delegates all authorization logic to the core permission engine.\r\n *\r\n * IMPORTANT: This adapter does NOT handle authentication.\r\n * You must provide the user context from your auth system.\r\n */\r\n\r\nimport type { UserContext, RoleRegistry } from \"../core/types\";\r\nimport { canUser, canUserAll, canUserAny } from \"../core/permission-engine\";\r\n\r\n/**\r\n * Next.js Request type (minimal interface for App Router).\r\n * Using minimal interface to avoid requiring next as a dependency.\r\n */\r\nexport interface NextRequest {\r\n /** Headers from the request */\r\n headers: {\r\n get(name: string): string | null;\r\n };\r\n}\r\n\r\n/**\r\n * Next.js Response helper for App Router.\r\n */\r\nexport interface NextResponse {\r\n json(body: unknown, init?: { status?: number }): Response;\r\n}\r\n\r\n/**\r\n * Result of a permission check in Next.js context.\r\n */\r\nexport type PermissionResult = {\r\n readonly allowed: boolean;\r\n readonly user: UserContext | null;\r\n};\r\n\r\n/**\r\n * Options for Next.js permission wrappers.\r\n */\r\nexport type NextPermissionOptions = {\r\n /** Function to extract user context from request */\r\n readonly getUser: (\r\n request: NextRequest\r\n ) => UserContext | null | Promise<UserContext | null>;\r\n /** HTTP status code on denial (default: 403) */\r\n readonly statusCode?: number;\r\n /** Error message on denial (default: 'Forbidden') */\r\n readonly message?: string;\r\n};\r\n\r\n/**\r\n * Checks if a user has permission in a Next.js API context.\r\n *\r\n * This is a pure check function - you handle the response.\r\n *\r\n * @param user - User context\r\n * @param permission - Required permission\r\n * @param registry - Role registry\r\n * @returns PermissionResult\r\n *\r\n * @example\r\n * ```ts\r\n * // app/api/users/route.ts\r\n * import { defineRoles } from 'secure-role-guard/core';\r\n * import { checkNextPermission } from 'secure-role-guard/adapters/nextjs';\r\n *\r\n * const registry = defineRoles({ admin: ['user.update'] });\r\n *\r\n * export async function PUT(request: NextRequest) {\r\n * const user = await getUser(request); // Your auth function\r\n * const result = checkNextPermission(user, 'user.update', registry);\r\n *\r\n * if (!result.allowed) {\r\n * return NextResponse.json({ error: 'Forbidden' }, { status: 403 });\r\n * }\r\n *\r\n * // Handle the request...\r\n * }\r\n * ```\r\n */\r\nexport function checkNextPermission(\r\n user: UserContext | null | undefined,\r\n permission: string,\r\n registry: RoleRegistry\r\n): PermissionResult {\r\n return {\r\n allowed: canUser(user, permission, registry),\r\n user: user ?? null,\r\n };\r\n}\r\n\r\n/**\r\n * Checks if a user has ALL specified permissions.\r\n *\r\n * @param user - User context\r\n * @param permissions - Required permissions\r\n * @param registry - Role registry\r\n * @returns PermissionResult\r\n */\r\nexport function checkNextPermissionAll(\r\n user: UserContext | null | undefined,\r\n permissions: readonly string[],\r\n registry: RoleRegistry\r\n): PermissionResult {\r\n return {\r\n allowed: canUserAll(user, permissions, registry),\r\n user: user ?? null,\r\n };\r\n}\r\n\r\n/**\r\n * Checks if a user has ANY of the specified permissions.\r\n *\r\n * @param user - User context\r\n * @param permissions - Permissions to check\r\n * @param registry - Role registry\r\n * @returns PermissionResult\r\n */\r\nexport function checkNextPermissionAny(\r\n user: UserContext | null | undefined,\r\n permissions: readonly string[],\r\n registry: RoleRegistry\r\n): PermissionResult {\r\n return {\r\n allowed: canUserAny(user, permissions, registry),\r\n user: user ?? null,\r\n };\r\n}\r\n\r\n/**\r\n * Higher-order function that wraps a Next.js Route Handler with permission check.\r\n *\r\n * @param permission - Required permission\r\n * @param registry - Role registry\r\n * @param options - Configuration options\r\n * @param handler - The route handler to wrap\r\n * @returns Wrapped route handler\r\n *\r\n * @example\r\n * ```ts\r\n * // app/api/admin/route.ts\r\n * import { defineRoles } from 'secure-role-guard/core';\r\n * import { withPermission } from 'secure-role-guard/adapters/nextjs';\r\n *\r\n * const registry = defineRoles({ admin: ['admin.access'] });\r\n *\r\n * async function getUser(req: NextRequest) {\r\n * // Your auth logic to extract user from session/token\r\n * return { roles: ['admin'] };\r\n * }\r\n *\r\n * export const GET = withPermission(\r\n * 'admin.access',\r\n * registry,\r\n * { getUser },\r\n * async (request, user) => {\r\n * return Response.json({ message: 'Welcome, admin!' });\r\n * }\r\n * );\r\n * ```\r\n */\r\nexport function withPermission<T extends NextRequest>(\r\n permission: string,\r\n registry: RoleRegistry,\r\n options: NextPermissionOptions,\r\n handler: (request: T, user: UserContext) => Response | Promise<Response>\r\n): (request: T) => Promise<Response> {\r\n const statusCode = options.statusCode ?? 403;\r\n const message = options.message ?? \"Forbidden\";\r\n\r\n return async (request: T): Promise<Response> => {\r\n const user = await options.getUser(request);\r\n\r\n if (user === null || !canUser(user, permission, registry)) {\r\n return Response.json({ error: message }, { status: statusCode });\r\n }\r\n\r\n return handler(request, user);\r\n };\r\n}\r\n\r\n/**\r\n * Higher-order function that wraps a handler requiring ALL permissions.\r\n */\r\nexport function withAllPermissions<T extends NextRequest>(\r\n permissions: readonly string[],\r\n registry: RoleRegistry,\r\n options: NextPermissionOptions,\r\n handler: (request: T, user: UserContext) => Response | Promise<Response>\r\n): (request: T) => Promise<Response> {\r\n const statusCode = options.statusCode ?? 403;\r\n const message = options.message ?? \"Forbidden\";\r\n\r\n return async (request: T): Promise<Response> => {\r\n const user = await options.getUser(request);\r\n\r\n if (user === null || !canUserAll(user, permissions, registry)) {\r\n return Response.json({ error: message }, { status: statusCode });\r\n }\r\n\r\n return handler(request, user);\r\n };\r\n}\r\n\r\n/**\r\n * Higher-order function that wraps a handler requiring ANY permission.\r\n */\r\nexport function withAnyPermission<T extends NextRequest>(\r\n permissions: readonly string[],\r\n registry: RoleRegistry,\r\n options: NextPermissionOptions,\r\n handler: (request: T, user: UserContext) => Response | Promise<Response>\r\n): (request: T) => Promise<Response> {\r\n const statusCode = options.statusCode ?? 403;\r\n const message = options.message ?? \"Forbidden\";\r\n\r\n return async (request: T): Promise<Response> => {\r\n const user = await options.getUser(request);\r\n\r\n if (user === null || !canUserAny(user, permissions, registry)) {\r\n return Response.json({ error: message }, { status: statusCode });\r\n }\r\n\r\n return handler(request, user);\r\n };\r\n}\r\n"]}
1
+ {"version":3,"sources":["../../src/core/permission-engine.ts","../../src/adapters/nextjs.ts"],"names":[],"mappings":"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","file":"nextjs.mjs","sourcesContent":["/**\n * Secure Role Guard - Permission Engine\n *\n * Pure functions for permission checking.\n * Zero side effects, zero dependencies, zero mutations.\n */\n\nimport type { UserContext, RoleRegistry, PermissionCheckResult } from \"./types\";\n\n/** Wildcard permission that grants all access */\nconst WILDCARD_PERMISSION = \"*\";\n\n/**\n * Collects all permissions for a user from their roles and direct permissions.\n *\n * @param user - The user context\n * @param registry - The role registry to resolve role permissions\n * @returns Set of all permissions (for efficient lookup)\n */\nfunction collectUserPermissions(\n user: UserContext,\n registry: RoleRegistry,\n): ReadonlySet<string> {\n const permissions = new Set<string>();\n\n // Add direct user permissions\n if (user.permissions !== undefined) {\n for (const permission of user.permissions) {\n permissions.add(permission);\n }\n }\n\n // Add permissions from all user roles\n if (user.roles !== undefined) {\n for (const role of user.roles) {\n const rolePermissions = registry.getPermissions(role);\n for (const permission of rolePermissions) {\n permissions.add(permission);\n }\n }\n }\n\n return permissions;\n}\n\n/**\n * Checks if a permission set contains a wildcard that grants the requested permission.\n *\n * Supports:\n * - Exact wildcard (*) - grants everything\n * - Namespace wildcards (user.*) - grants all under namespace\n *\n * @param permissions - Set of permissions to check\n * @param requested - The permission being requested\n * @returns True if wildcard grants access\n */\nfunction hasWildcardAccess(\n permissions: ReadonlySet<string>,\n requested: string,\n): boolean {\n // Check for global wildcard\n if (permissions.has(WILDCARD_PERMISSION)) {\n return true;\n }\n\n // Check for namespace wildcards (e.g., user.* grants user.read)\n const parts = requested.split(\".\");\n let namespace = \"\";\n\n for (let i = 0; i < parts.length - 1; i++) {\n const part = parts[i];\n if (part !== undefined) {\n namespace = namespace === \"\" ? part : `${namespace}.${part}`;\n if (permissions.has(`${namespace}.*`)) {\n return true;\n }\n }\n }\n\n return false;\n}\n\n/**\n * Checks if a user has a specific permission.\n *\n * SECURITY GUARANTEES:\n * - Deny by default: undefined/null/empty always returns false\n * - Pure function: no side effects, no mutations\n * - Deterministic: same inputs always produce same output\n *\n * @param user - The user context to check\n * @param permission - The permission required\n * @param registry - The role registry for resolving roles\n * @returns True if user has the permission, false otherwise\n *\n * @example\n * const canEdit = canUser(currentUser, 'user.update', roleRegistry);\n */\nexport function canUser(\n user: UserContext | null | undefined,\n permission: string,\n registry: RoleRegistry,\n): boolean {\n // DENY BY DEFAULT: No user context means no access\n if (user === null || user === undefined) {\n return false;\n }\n\n // DENY BY DEFAULT: Empty permission request is invalid\n if (permission === \"\" || permission.trim() === \"\") {\n return false;\n }\n\n const userPermissions = collectUserPermissions(user, registry);\n\n // Check for exact permission match\n if (userPermissions.has(permission)) {\n return true;\n }\n\n // Check for wildcard access\n if (hasWildcardAccess(userPermissions, permission)) {\n return true;\n }\n\n // DENY BY DEFAULT\n return false;\n}\n\n/**\n * Checks if a user has ALL of the specified permissions.\n *\n * @param user - The user context to check\n * @param permissions - Array of permissions required\n * @param registry - The role registry\n * @returns True if user has ALL permissions\n *\n * @example\n * const canManage = canUserAll(user, ['user.read', 'user.update'], registry);\n */\nexport function canUserAll(\n user: UserContext | null | undefined,\n permissions: readonly string[],\n registry: RoleRegistry,\n): boolean {\n // DENY BY DEFAULT: Empty permissions array\n if (permissions.length === 0) {\n return false;\n }\n\n for (const permission of permissions) {\n if (!canUser(user, permission, registry)) {\n return false;\n }\n }\n\n return true;\n}\n\n/**\n * Checks if a user has ANY of the specified permissions.\n *\n * @param user - The user context to check\n * @param permissions - Array of permissions to check\n * @param registry - The role registry\n * @returns True if user has at least one permission\n *\n * @example\n * const canView = canUserAny(user, ['report.view', 'report.admin'], registry);\n */\nexport function canUserAny(\n user: UserContext | null | undefined,\n permissions: readonly string[],\n registry: RoleRegistry,\n): boolean {\n // DENY BY DEFAULT: Empty permissions array\n if (permissions.length === 0) {\n return false;\n }\n\n for (const permission of permissions) {\n if (canUser(user, permission, registry)) {\n return true;\n }\n }\n\n return false;\n}\n\n/**\n * Extended permission check with detailed result.\n * Useful for debugging and logging.\n *\n * @param user - The user context\n * @param permission - Permission to check\n * @param registry - The role registry\n * @returns PermissionCheckResult with allowed status and reason\n */\nexport function checkPermission(\n user: UserContext | null | undefined,\n permission: string,\n registry: RoleRegistry,\n): PermissionCheckResult {\n if (user === null || user === undefined) {\n return Object.freeze({\n allowed: false,\n reason: \"No user context provided\",\n });\n }\n\n if (permission === \"\" || permission.trim() === \"\") {\n return Object.freeze({\n allowed: false,\n reason: \"Empty permission requested\",\n });\n }\n\n const allowed = canUser(user, permission, registry);\n\n return Object.freeze({\n allowed,\n reason: allowed\n ? `Permission \"${permission}\" granted`\n : `Permission \"${permission}\" denied`,\n });\n}\n","/**\n * Secure Role Guard - Next.js Adapter\n *\n * Thin wrapper for Next.js API routes and Route Handlers.\n * Delegates all authorization logic to the core permission engine.\n *\n * IMPORTANT: This adapter does NOT handle authentication.\n * You must provide the user context from your auth system.\n */\n\nimport type { UserContext, RoleRegistry } from \"../core/types\";\nimport { canUser, canUserAll, canUserAny } from \"../core/permission-engine\";\n\n/**\n * Next.js Request type (minimal interface for App Router).\n * Using minimal interface to avoid requiring next as a dependency.\n */\nexport interface NextRequest {\n /** Headers from the request */\n headers: {\n get(name: string): string | null;\n };\n}\n\n/**\n * Next.js Response helper for App Router.\n */\nexport interface NextResponse {\n json(body: unknown, init?: { status?: number }): Response;\n}\n\n/**\n * Result of a permission check in Next.js context.\n */\nexport type PermissionResult = {\n readonly allowed: boolean;\n readonly user: UserContext | null;\n};\n\n/**\n * Options for Next.js permission wrappers.\n */\nexport type NextPermissionOptions = {\n /** Function to extract user context from request */\n readonly getUser: (\n request: NextRequest,\n ) => UserContext | null | Promise<UserContext | null>;\n /** HTTP status code on denial (default: 403) */\n readonly statusCode?: number;\n /** Error message on denial (default: 'Forbidden') */\n readonly message?: string;\n};\n\n/**\n * Checks if a user has permission in a Next.js API context.\n *\n * This is a pure check function - you handle the response.\n *\n * @param user - User context\n * @param permission - Required permission\n * @param registry - Role registry\n * @returns PermissionResult\n *\n * @example\n * ```ts\n * // app/api/users/route.ts\n * import { defineRoles } from 'secure-role-guard/core';\n * import { checkNextPermission } from 'secure-role-guard/adapters/nextjs';\n *\n * const registry = defineRoles({ admin: ['user.update'] });\n *\n * export async function PUT(request: NextRequest) {\n * const user = await getUser(request); // Your auth function\n * const result = checkNextPermission(user, 'user.update', registry);\n *\n * if (!result.allowed) {\n * return NextResponse.json({ error: 'Forbidden' }, { status: 403 });\n * }\n *\n * // Handle the request...\n * }\n * ```\n */\nexport function checkNextPermission(\n user: UserContext | null | undefined,\n permission: string,\n registry: RoleRegistry,\n): PermissionResult {\n return {\n allowed: canUser(user, permission, registry),\n user: user ?? null,\n };\n}\n\n/**\n * Checks if a user has ALL specified permissions.\n *\n * @param user - User context\n * @param permissions - Required permissions\n * @param registry - Role registry\n * @returns PermissionResult\n */\nexport function checkNextPermissionAll(\n user: UserContext | null | undefined,\n permissions: readonly string[],\n registry: RoleRegistry,\n): PermissionResult {\n return {\n allowed: canUserAll(user, permissions, registry),\n user: user ?? null,\n };\n}\n\n/**\n * Checks if a user has ANY of the specified permissions.\n *\n * @param user - User context\n * @param permissions - Permissions to check\n * @param registry - Role registry\n * @returns PermissionResult\n */\nexport function checkNextPermissionAny(\n user: UserContext | null | undefined,\n permissions: readonly string[],\n registry: RoleRegistry,\n): PermissionResult {\n return {\n allowed: canUserAny(user, permissions, registry),\n user: user ?? null,\n };\n}\n\n/**\n * Higher-order function that wraps a Next.js Route Handler with permission check.\n *\n * @param permission - Required permission\n * @param registry - Role registry\n * @param options - Configuration options\n * @param handler - The route handler to wrap\n * @returns Wrapped route handler\n *\n * @example\n * ```ts\n * // app/api/admin/route.ts\n * import { defineRoles } from 'secure-role-guard/core';\n * import { withPermission } from 'secure-role-guard/adapters/nextjs';\n *\n * const registry = defineRoles({ admin: ['admin.access'] });\n *\n * async function getUser(req: NextRequest) {\n * // Your auth logic to extract user from session/token\n * return { roles: ['admin'] };\n * }\n *\n * export const GET = withPermission(\n * 'admin.access',\n * registry,\n * { getUser },\n * async (request, user) => {\n * return Response.json({ message: 'Welcome, admin!' });\n * }\n * );\n * ```\n */\nexport function withPermission<T extends NextRequest>(\n permission: string,\n registry: RoleRegistry,\n options: NextPermissionOptions,\n handler: (request: T, user: UserContext) => Response | Promise<Response>,\n): (request: T) => Promise<Response> {\n const statusCode = options.statusCode ?? 403;\n const message = options.message ?? \"Forbidden\";\n\n return async (request: T): Promise<Response> => {\n const user = await options.getUser(request);\n\n if (user === null || !canUser(user, permission, registry)) {\n return Response.json({ error: message }, { status: statusCode });\n }\n\n return handler(request, user);\n };\n}\n\n/**\n * Higher-order function that wraps a handler requiring ALL permissions.\n */\nexport function withAllPermissions<T extends NextRequest>(\n permissions: readonly string[],\n registry: RoleRegistry,\n options: NextPermissionOptions,\n handler: (request: T, user: UserContext) => Response | Promise<Response>,\n): (request: T) => Promise<Response> {\n const statusCode = options.statusCode ?? 403;\n const message = options.message ?? \"Forbidden\";\n\n return async (request: T): Promise<Response> => {\n const user = await options.getUser(request);\n\n if (user === null || !canUserAll(user, permissions, registry)) {\n return Response.json({ error: message }, { status: statusCode });\n }\n\n return handler(request, user);\n };\n}\n\n/**\n * Higher-order function that wraps a handler requiring ANY permission.\n */\nexport function withAnyPermission<T extends NextRequest>(\n permissions: readonly string[],\n registry: RoleRegistry,\n options: NextPermissionOptions,\n handler: (request: T, user: UserContext) => Response | Promise<Response>,\n): (request: T) => Promise<Response> {\n const statusCode = options.statusCode ?? 403;\n const message = options.message ?? \"Forbidden\";\n\n return async (request: T): Promise<Response> => {\n const user = await options.getUser(request);\n\n if (user === null || !canUserAny(user, permissions, registry)) {\n return Response.json({ error: message }, { status: statusCode });\n }\n\n return handler(request, user);\n };\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/core/role-registry.ts","../../src/core/permission-engine.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\r\n * Secure Role Guard - Role Registry\r\n *\r\n * Creates an immutable registry of role definitions.\r\n * Pure functions only, zero side effects.\r\n */\r\n\r\nimport type { RoleDefinition, RoleRegistry } from \"./types\";\r\n\r\n/**\r\n * Creates an immutable role registry from role definitions.\r\n *\r\n * Security guarantees:\r\n * - Returns a frozen, immutable object\r\n * - No mutations possible after creation\r\n * - Unknown roles return empty permissions (deny by default)\r\n *\r\n * @param definitions - Object mapping role names to permission arrays\r\n * @returns Frozen RoleRegistry object\r\n *\r\n * @example\r\n * const registry = defineRoles({\r\n * superadmin: ['*'],\r\n * admin: ['user.read', 'user.update'],\r\n * support: ['ticket.read', 'ticket.reply']\r\n * });\r\n *\r\n * registry.getPermissions('admin'); // ['user.read', 'user.update']\r\n * registry.getPermissions('unknown'); // []\r\n */\r\nexport function defineRoles(definitions: RoleDefinition): RoleRegistry {\r\n // Create a deep-frozen copy to prevent external mutations\r\n const frozenDefinitions: Record<string, readonly string[]> = {};\r\n\r\n for (const role of Object.keys(definitions)) {\r\n const permissions = definitions[role];\r\n if (permissions !== undefined) {\r\n // Freeze the permissions array\r\n frozenDefinitions[role] = Object.freeze([...permissions]);\r\n }\r\n }\r\n\r\n // Freeze the definitions object itself\r\n Object.freeze(frozenDefinitions);\r\n\r\n const registry: RoleRegistry = {\r\n getPermissions(role: string): readonly string[] {\r\n const permissions = frozenDefinitions[role];\r\n // Deny by default: unknown roles get empty permissions\r\n return permissions !== undefined ? permissions : Object.freeze([]);\r\n },\r\n\r\n hasRole(role: string): boolean {\r\n return Object.prototype.hasOwnProperty.call(frozenDefinitions, role);\r\n },\r\n\r\n getRoleNames(): readonly string[] {\r\n return Object.freeze(Object.keys(frozenDefinitions));\r\n },\r\n };\r\n\r\n // Freeze the registry object to prevent modifications\r\n return Object.freeze(registry);\r\n}\r\n\r\n/**\r\n * Creates an empty role registry.\r\n * Useful for testing or when no roles are defined.\r\n *\r\n * @returns Empty frozen RoleRegistry\r\n */\r\nexport function createEmptyRegistry(): RoleRegistry {\r\n return defineRoles({});\r\n}\r\n","/**\r\n * Secure Role Guard - Permission Engine\r\n *\r\n * Pure functions for permission checking.\r\n * Zero side effects, zero dependencies, zero mutations.\r\n */\r\n\r\nimport type { UserContext, RoleRegistry, PermissionCheckResult } from \"./types\";\r\n\r\n/** Wildcard permission that grants all access */\r\nconst WILDCARD_PERMISSION = \"*\";\r\n\r\n/**\r\n * Collects all permissions for a user from their roles and direct permissions.\r\n *\r\n * @param user - The user context\r\n * @param registry - The role registry to resolve role permissions\r\n * @returns Set of all permissions (for efficient lookup)\r\n */\r\nfunction collectUserPermissions(\r\n user: UserContext,\r\n registry: RoleRegistry\r\n): ReadonlySet<string> {\r\n const permissions = new Set<string>();\r\n\r\n // Add direct user permissions\r\n if (user.permissions !== undefined) {\r\n for (const permission of user.permissions) {\r\n permissions.add(permission);\r\n }\r\n }\r\n\r\n // Add permissions from all user roles\r\n if (user.roles !== undefined) {\r\n for (const role of user.roles) {\r\n const rolePermissions = registry.getPermissions(role);\r\n for (const permission of rolePermissions) {\r\n permissions.add(permission);\r\n }\r\n }\r\n }\r\n\r\n return permissions;\r\n}\r\n\r\n/**\r\n * Checks if a permission set contains a wildcard that grants the requested permission.\r\n *\r\n * Supports:\r\n * - Exact wildcard (*) - grants everything\r\n * - Namespace wildcards (user.*) - grants all under namespace\r\n *\r\n * @param permissions - Set of permissions to check\r\n * @param requested - The permission being requested\r\n * @returns True if wildcard grants access\r\n */\r\nfunction hasWildcardAccess(\r\n permissions: ReadonlySet<string>,\r\n requested: string\r\n): boolean {\r\n // Check for global wildcard\r\n if (permissions.has(WILDCARD_PERMISSION)) {\r\n return true;\r\n }\r\n\r\n // Check for namespace wildcards (e.g., user.* grants user.read)\r\n const parts = requested.split(\".\");\r\n let namespace = \"\";\r\n\r\n for (let i = 0; i < parts.length - 1; i++) {\r\n const part = parts[i];\r\n if (part !== undefined) {\r\n namespace = namespace === \"\" ? part : `${namespace}.${part}`;\r\n if (permissions.has(`${namespace}.*`)) {\r\n return true;\r\n }\r\n }\r\n }\r\n\r\n return false;\r\n}\r\n\r\n/**\r\n * Checks if a user has a specific permission.\r\n *\r\n * SECURITY GUARANTEES:\r\n * - Deny by default: undefined/null/empty always returns false\r\n * - Pure function: no side effects, no mutations\r\n * - Deterministic: same inputs always produce same output\r\n *\r\n * @param user - The user context to check\r\n * @param permission - The permission required\r\n * @param registry - The role registry for resolving roles\r\n * @returns True if user has the permission, false otherwise\r\n *\r\n * @example\r\n * const canEdit = canUser(currentUser, 'user.update', roleRegistry);\r\n */\r\nexport function canUser(\r\n user: UserContext | null | undefined,\r\n permission: string,\r\n registry: RoleRegistry\r\n): boolean {\r\n // DENY BY DEFAULT: No user context means no access\r\n if (user === null || user === undefined) {\r\n return false;\r\n }\r\n\r\n // DENY BY DEFAULT: Empty permission request is invalid\r\n if (permission === \"\" || permission.trim() === \"\") {\r\n return false;\r\n }\r\n\r\n const userPermissions = collectUserPermissions(user, registry);\r\n\r\n // Check for exact permission match\r\n if (userPermissions.has(permission)) {\r\n return true;\r\n }\r\n\r\n // Check for wildcard access\r\n if (hasWildcardAccess(userPermissions, permission)) {\r\n return true;\r\n }\r\n\r\n // DENY BY DEFAULT\r\n return false;\r\n}\r\n\r\n/**\r\n * Checks if a user has ALL of the specified permissions.\r\n *\r\n * @param user - The user context to check\r\n * @param permissions - Array of permissions required\r\n * @param registry - The role registry\r\n * @returns True if user has ALL permissions\r\n *\r\n * @example\r\n * const canManage = canUserAll(user, ['user.read', 'user.update'], registry);\r\n */\r\nexport function canUserAll(\r\n user: UserContext | null | undefined,\r\n permissions: readonly string[],\r\n registry: RoleRegistry\r\n): boolean {\r\n // DENY BY DEFAULT: Empty permissions array\r\n if (permissions.length === 0) {\r\n return false;\r\n }\r\n\r\n for (const permission of permissions) {\r\n if (!canUser(user, permission, registry)) {\r\n return false;\r\n }\r\n }\r\n\r\n return true;\r\n}\r\n\r\n/**\r\n * Checks if a user has ANY of the specified permissions.\r\n *\r\n * @param user - The user context to check\r\n * @param permissions - Array of permissions to check\r\n * @param registry - The role registry\r\n * @returns True if user has at least one permission\r\n *\r\n * @example\r\n * const canView = canUserAny(user, ['report.view', 'report.admin'], registry);\r\n */\r\nexport function canUserAny(\r\n user: UserContext | null | undefined,\r\n permissions: readonly string[],\r\n registry: RoleRegistry\r\n): boolean {\r\n // DENY BY DEFAULT: Empty permissions array\r\n if (permissions.length === 0) {\r\n return false;\r\n }\r\n\r\n for (const permission of permissions) {\r\n if (canUser(user, permission, registry)) {\r\n return true;\r\n }\r\n }\r\n\r\n return false;\r\n}\r\n\r\n/**\r\n * Extended permission check with detailed result.\r\n * Useful for debugging and logging.\r\n *\r\n * @param user - The user context\r\n * @param permission - Permission to check\r\n * @param registry - The role registry\r\n * @returns PermissionCheckResult with allowed status and reason\r\n */\r\nexport function checkPermission(\r\n user: UserContext | null | undefined,\r\n permission: string,\r\n registry: RoleRegistry\r\n): PermissionCheckResult {\r\n if (user === null || user === undefined) {\r\n return Object.freeze({\r\n allowed: false,\r\n reason: \"No user context provided\",\r\n });\r\n }\r\n\r\n if (permission === \"\" || permission.trim() === \"\") {\r\n return Object.freeze({\r\n allowed: false,\r\n reason: \"Empty permission requested\",\r\n });\r\n }\r\n\r\n const allowed = canUser(user, permission, registry);\r\n\r\n return Object.freeze({\r\n allowed,\r\n reason: allowed\r\n ? `Permission \"${permission}\" granted`\r\n : `Permission \"${permission}\" denied`,\r\n });\r\n}\r\n"]}
1
+ {"version":3,"sources":["../../src/core/role-registry.ts","../../src/core/permission-engine.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Secure Role Guard - Role Registry\n *\n * Creates an immutable registry of role definitions.\n * Pure functions only, zero side effects.\n */\n\nimport type { RoleDefinition, RoleRegistry } from \"./types\";\n\n/**\n * Creates an immutable role registry from role definitions.\n *\n * Security guarantees:\n * - Returns a frozen, immutable object\n * - No mutations possible after creation\n * - Unknown roles return empty permissions (deny by default)\n *\n * @param definitions - Object mapping role names to permission arrays\n * @returns Frozen RoleRegistry object\n *\n * @example\n * const registry = defineRoles({\n * superadmin: ['*'],\n * admin: ['user.read', 'user.update'],\n * support: ['ticket.read', 'ticket.reply']\n * });\n *\n * registry.getPermissions('admin'); // ['user.read', 'user.update']\n * registry.getPermissions('unknown'); // []\n */\nexport function defineRoles(definitions: RoleDefinition): RoleRegistry {\n // Create a deep-frozen copy to prevent external mutations\n const frozenDefinitions: Record<string, readonly string[]> = {};\n\n for (const role of Object.keys(definitions)) {\n const permissions = definitions[role];\n if (permissions !== undefined) {\n // Freeze the permissions array\n frozenDefinitions[role] = Object.freeze([...permissions]);\n }\n }\n\n // Freeze the definitions object itself\n Object.freeze(frozenDefinitions);\n\n const registry: RoleRegistry = {\n getPermissions(role: string): readonly string[] {\n const permissions = frozenDefinitions[role];\n // Deny by default: unknown roles get empty permissions\n return permissions !== undefined ? permissions : Object.freeze([]);\n },\n\n hasRole(role: string): boolean {\n return Object.prototype.hasOwnProperty.call(frozenDefinitions, role);\n },\n\n getRoleNames(): readonly string[] {\n return Object.freeze(Object.keys(frozenDefinitions));\n },\n };\n\n // Freeze the registry object to prevent modifications\n return Object.freeze(registry);\n}\n\n/**\n * Creates an empty role registry.\n * Useful for testing or when no roles are defined.\n *\n * @returns Empty frozen RoleRegistry\n */\nexport function createEmptyRegistry(): RoleRegistry {\n return defineRoles({});\n}\n","/**\n * Secure Role Guard - Permission Engine\n *\n * Pure functions for permission checking.\n * Zero side effects, zero dependencies, zero mutations.\n */\n\nimport type { UserContext, RoleRegistry, PermissionCheckResult } from \"./types\";\n\n/** Wildcard permission that grants all access */\nconst WILDCARD_PERMISSION = \"*\";\n\n/**\n * Collects all permissions for a user from their roles and direct permissions.\n *\n * @param user - The user context\n * @param registry - The role registry to resolve role permissions\n * @returns Set of all permissions (for efficient lookup)\n */\nfunction collectUserPermissions(\n user: UserContext,\n registry: RoleRegistry,\n): ReadonlySet<string> {\n const permissions = new Set<string>();\n\n // Add direct user permissions\n if (user.permissions !== undefined) {\n for (const permission of user.permissions) {\n permissions.add(permission);\n }\n }\n\n // Add permissions from all user roles\n if (user.roles !== undefined) {\n for (const role of user.roles) {\n const rolePermissions = registry.getPermissions(role);\n for (const permission of rolePermissions) {\n permissions.add(permission);\n }\n }\n }\n\n return permissions;\n}\n\n/**\n * Checks if a permission set contains a wildcard that grants the requested permission.\n *\n * Supports:\n * - Exact wildcard (*) - grants everything\n * - Namespace wildcards (user.*) - grants all under namespace\n *\n * @param permissions - Set of permissions to check\n * @param requested - The permission being requested\n * @returns True if wildcard grants access\n */\nfunction hasWildcardAccess(\n permissions: ReadonlySet<string>,\n requested: string,\n): boolean {\n // Check for global wildcard\n if (permissions.has(WILDCARD_PERMISSION)) {\n return true;\n }\n\n // Check for namespace wildcards (e.g., user.* grants user.read)\n const parts = requested.split(\".\");\n let namespace = \"\";\n\n for (let i = 0; i < parts.length - 1; i++) {\n const part = parts[i];\n if (part !== undefined) {\n namespace = namespace === \"\" ? part : `${namespace}.${part}`;\n if (permissions.has(`${namespace}.*`)) {\n return true;\n }\n }\n }\n\n return false;\n}\n\n/**\n * Checks if a user has a specific permission.\n *\n * SECURITY GUARANTEES:\n * - Deny by default: undefined/null/empty always returns false\n * - Pure function: no side effects, no mutations\n * - Deterministic: same inputs always produce same output\n *\n * @param user - The user context to check\n * @param permission - The permission required\n * @param registry - The role registry for resolving roles\n * @returns True if user has the permission, false otherwise\n *\n * @example\n * const canEdit = canUser(currentUser, 'user.update', roleRegistry);\n */\nexport function canUser(\n user: UserContext | null | undefined,\n permission: string,\n registry: RoleRegistry,\n): boolean {\n // DENY BY DEFAULT: No user context means no access\n if (user === null || user === undefined) {\n return false;\n }\n\n // DENY BY DEFAULT: Empty permission request is invalid\n if (permission === \"\" || permission.trim() === \"\") {\n return false;\n }\n\n const userPermissions = collectUserPermissions(user, registry);\n\n // Check for exact permission match\n if (userPermissions.has(permission)) {\n return true;\n }\n\n // Check for wildcard access\n if (hasWildcardAccess(userPermissions, permission)) {\n return true;\n }\n\n // DENY BY DEFAULT\n return false;\n}\n\n/**\n * Checks if a user has ALL of the specified permissions.\n *\n * @param user - The user context to check\n * @param permissions - Array of permissions required\n * @param registry - The role registry\n * @returns True if user has ALL permissions\n *\n * @example\n * const canManage = canUserAll(user, ['user.read', 'user.update'], registry);\n */\nexport function canUserAll(\n user: UserContext | null | undefined,\n permissions: readonly string[],\n registry: RoleRegistry,\n): boolean {\n // DENY BY DEFAULT: Empty permissions array\n if (permissions.length === 0) {\n return false;\n }\n\n for (const permission of permissions) {\n if (!canUser(user, permission, registry)) {\n return false;\n }\n }\n\n return true;\n}\n\n/**\n * Checks if a user has ANY of the specified permissions.\n *\n * @param user - The user context to check\n * @param permissions - Array of permissions to check\n * @param registry - The role registry\n * @returns True if user has at least one permission\n *\n * @example\n * const canView = canUserAny(user, ['report.view', 'report.admin'], registry);\n */\nexport function canUserAny(\n user: UserContext | null | undefined,\n permissions: readonly string[],\n registry: RoleRegistry,\n): boolean {\n // DENY BY DEFAULT: Empty permissions array\n if (permissions.length === 0) {\n return false;\n }\n\n for (const permission of permissions) {\n if (canUser(user, permission, registry)) {\n return true;\n }\n }\n\n return false;\n}\n\n/**\n * Extended permission check with detailed result.\n * Useful for debugging and logging.\n *\n * @param user - The user context\n * @param permission - Permission to check\n * @param registry - The role registry\n * @returns PermissionCheckResult with allowed status and reason\n */\nexport function checkPermission(\n user: UserContext | null | undefined,\n permission: string,\n registry: RoleRegistry,\n): PermissionCheckResult {\n if (user === null || user === undefined) {\n return Object.freeze({\n allowed: false,\n reason: \"No user context provided\",\n });\n }\n\n if (permission === \"\" || permission.trim() === \"\") {\n return Object.freeze({\n allowed: false,\n reason: \"Empty permission requested\",\n });\n }\n\n const allowed = canUser(user, permission, registry);\n\n return Object.freeze({\n allowed,\n reason: allowed\n ? `Permission \"${permission}\" granted`\n : `Permission \"${permission}\" denied`,\n });\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/core/role-registry.ts","../../src/core/permission-engine.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["/**\r\n * Secure Role Guard - Role Registry\r\n *\r\n * Creates an immutable registry of role definitions.\r\n * Pure functions only, zero side effects.\r\n */\r\n\r\nimport type { RoleDefinition, RoleRegistry } from \"./types\";\r\n\r\n/**\r\n * Creates an immutable role registry from role definitions.\r\n *\r\n * Security guarantees:\r\n * - Returns a frozen, immutable object\r\n * - No mutations possible after creation\r\n * - Unknown roles return empty permissions (deny by default)\r\n *\r\n * @param definitions - Object mapping role names to permission arrays\r\n * @returns Frozen RoleRegistry object\r\n *\r\n * @example\r\n * const registry = defineRoles({\r\n * superadmin: ['*'],\r\n * admin: ['user.read', 'user.update'],\r\n * support: ['ticket.read', 'ticket.reply']\r\n * });\r\n *\r\n * registry.getPermissions('admin'); // ['user.read', 'user.update']\r\n * registry.getPermissions('unknown'); // []\r\n */\r\nexport function defineRoles(definitions: RoleDefinition): RoleRegistry {\r\n // Create a deep-frozen copy to prevent external mutations\r\n const frozenDefinitions: Record<string, readonly string[]> = {};\r\n\r\n for (const role of Object.keys(definitions)) {\r\n const permissions = definitions[role];\r\n if (permissions !== undefined) {\r\n // Freeze the permissions array\r\n frozenDefinitions[role] = Object.freeze([...permissions]);\r\n }\r\n }\r\n\r\n // Freeze the definitions object itself\r\n Object.freeze(frozenDefinitions);\r\n\r\n const registry: RoleRegistry = {\r\n getPermissions(role: string): readonly string[] {\r\n const permissions = frozenDefinitions[role];\r\n // Deny by default: unknown roles get empty permissions\r\n return permissions !== undefined ? permissions : Object.freeze([]);\r\n },\r\n\r\n hasRole(role: string): boolean {\r\n return Object.prototype.hasOwnProperty.call(frozenDefinitions, role);\r\n },\r\n\r\n getRoleNames(): readonly string[] {\r\n return Object.freeze(Object.keys(frozenDefinitions));\r\n },\r\n };\r\n\r\n // Freeze the registry object to prevent modifications\r\n return Object.freeze(registry);\r\n}\r\n\r\n/**\r\n * Creates an empty role registry.\r\n * Useful for testing or when no roles are defined.\r\n *\r\n * @returns Empty frozen RoleRegistry\r\n */\r\nexport function createEmptyRegistry(): RoleRegistry {\r\n return defineRoles({});\r\n}\r\n","/**\r\n * Secure Role Guard - Permission Engine\r\n *\r\n * Pure functions for permission checking.\r\n * Zero side effects, zero dependencies, zero mutations.\r\n */\r\n\r\nimport type { UserContext, RoleRegistry, PermissionCheckResult } from \"./types\";\r\n\r\n/** Wildcard permission that grants all access */\r\nconst WILDCARD_PERMISSION = \"*\";\r\n\r\n/**\r\n * Collects all permissions for a user from their roles and direct permissions.\r\n *\r\n * @param user - The user context\r\n * @param registry - The role registry to resolve role permissions\r\n * @returns Set of all permissions (for efficient lookup)\r\n */\r\nfunction collectUserPermissions(\r\n user: UserContext,\r\n registry: RoleRegistry\r\n): ReadonlySet<string> {\r\n const permissions = new Set<string>();\r\n\r\n // Add direct user permissions\r\n if (user.permissions !== undefined) {\r\n for (const permission of user.permissions) {\r\n permissions.add(permission);\r\n }\r\n }\r\n\r\n // Add permissions from all user roles\r\n if (user.roles !== undefined) {\r\n for (const role of user.roles) {\r\n const rolePermissions = registry.getPermissions(role);\r\n for (const permission of rolePermissions) {\r\n permissions.add(permission);\r\n }\r\n }\r\n }\r\n\r\n return permissions;\r\n}\r\n\r\n/**\r\n * Checks if a permission set contains a wildcard that grants the requested permission.\r\n *\r\n * Supports:\r\n * - Exact wildcard (*) - grants everything\r\n * - Namespace wildcards (user.*) - grants all under namespace\r\n *\r\n * @param permissions - Set of permissions to check\r\n * @param requested - The permission being requested\r\n * @returns True if wildcard grants access\r\n */\r\nfunction hasWildcardAccess(\r\n permissions: ReadonlySet<string>,\r\n requested: string\r\n): boolean {\r\n // Check for global wildcard\r\n if (permissions.has(WILDCARD_PERMISSION)) {\r\n return true;\r\n }\r\n\r\n // Check for namespace wildcards (e.g., user.* grants user.read)\r\n const parts = requested.split(\".\");\r\n let namespace = \"\";\r\n\r\n for (let i = 0; i < parts.length - 1; i++) {\r\n const part = parts[i];\r\n if (part !== undefined) {\r\n namespace = namespace === \"\" ? part : `${namespace}.${part}`;\r\n if (permissions.has(`${namespace}.*`)) {\r\n return true;\r\n }\r\n }\r\n }\r\n\r\n return false;\r\n}\r\n\r\n/**\r\n * Checks if a user has a specific permission.\r\n *\r\n * SECURITY GUARANTEES:\r\n * - Deny by default: undefined/null/empty always returns false\r\n * - Pure function: no side effects, no mutations\r\n * - Deterministic: same inputs always produce same output\r\n *\r\n * @param user - The user context to check\r\n * @param permission - The permission required\r\n * @param registry - The role registry for resolving roles\r\n * @returns True if user has the permission, false otherwise\r\n *\r\n * @example\r\n * const canEdit = canUser(currentUser, 'user.update', roleRegistry);\r\n */\r\nexport function canUser(\r\n user: UserContext | null | undefined,\r\n permission: string,\r\n registry: RoleRegistry\r\n): boolean {\r\n // DENY BY DEFAULT: No user context means no access\r\n if (user === null || user === undefined) {\r\n return false;\r\n }\r\n\r\n // DENY BY DEFAULT: Empty permission request is invalid\r\n if (permission === \"\" || permission.trim() === \"\") {\r\n return false;\r\n }\r\n\r\n const userPermissions = collectUserPermissions(user, registry);\r\n\r\n // Check for exact permission match\r\n if (userPermissions.has(permission)) {\r\n return true;\r\n }\r\n\r\n // Check for wildcard access\r\n if (hasWildcardAccess(userPermissions, permission)) {\r\n return true;\r\n }\r\n\r\n // DENY BY DEFAULT\r\n return false;\r\n}\r\n\r\n/**\r\n * Checks if a user has ALL of the specified permissions.\r\n *\r\n * @param user - The user context to check\r\n * @param permissions - Array of permissions required\r\n * @param registry - The role registry\r\n * @returns True if user has ALL permissions\r\n *\r\n * @example\r\n * const canManage = canUserAll(user, ['user.read', 'user.update'], registry);\r\n */\r\nexport function canUserAll(\r\n user: UserContext | null | undefined,\r\n permissions: readonly string[],\r\n registry: RoleRegistry\r\n): boolean {\r\n // DENY BY DEFAULT: Empty permissions array\r\n if (permissions.length === 0) {\r\n return false;\r\n }\r\n\r\n for (const permission of permissions) {\r\n if (!canUser(user, permission, registry)) {\r\n return false;\r\n }\r\n }\r\n\r\n return true;\r\n}\r\n\r\n/**\r\n * Checks if a user has ANY of the specified permissions.\r\n *\r\n * @param user - The user context to check\r\n * @param permissions - Array of permissions to check\r\n * @param registry - The role registry\r\n * @returns True if user has at least one permission\r\n *\r\n * @example\r\n * const canView = canUserAny(user, ['report.view', 'report.admin'], registry);\r\n */\r\nexport function canUserAny(\r\n user: UserContext | null | undefined,\r\n permissions: readonly string[],\r\n registry: RoleRegistry\r\n): boolean {\r\n // DENY BY DEFAULT: Empty permissions array\r\n if (permissions.length === 0) {\r\n return false;\r\n }\r\n\r\n for (const permission of permissions) {\r\n if (canUser(user, permission, registry)) {\r\n return true;\r\n }\r\n }\r\n\r\n return false;\r\n}\r\n\r\n/**\r\n * Extended permission check with detailed result.\r\n * Useful for debugging and logging.\r\n *\r\n * @param user - The user context\r\n * @param permission - Permission to check\r\n * @param registry - The role registry\r\n * @returns PermissionCheckResult with allowed status and reason\r\n */\r\nexport function checkPermission(\r\n user: UserContext | null | undefined,\r\n permission: string,\r\n registry: RoleRegistry\r\n): PermissionCheckResult {\r\n if (user === null || user === undefined) {\r\n return Object.freeze({\r\n allowed: false,\r\n reason: \"No user context provided\",\r\n });\r\n }\r\n\r\n if (permission === \"\" || permission.trim() === \"\") {\r\n return Object.freeze({\r\n allowed: false,\r\n reason: \"Empty permission requested\",\r\n });\r\n }\r\n\r\n const allowed = canUser(user, permission, registry);\r\n\r\n return Object.freeze({\r\n allowed,\r\n reason: allowed\r\n ? `Permission \"${permission}\" granted`\r\n : `Permission \"${permission}\" denied`,\r\n });\r\n}\r\n"]}
1
+ {"version":3,"sources":["../../src/core/role-registry.ts","../../src/core/permission-engine.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * Secure Role Guard - Role Registry\n *\n * Creates an immutable registry of role definitions.\n * Pure functions only, zero side effects.\n */\n\nimport type { RoleDefinition, RoleRegistry } from \"./types\";\n\n/**\n * Creates an immutable role registry from role definitions.\n *\n * Security guarantees:\n * - Returns a frozen, immutable object\n * - No mutations possible after creation\n * - Unknown roles return empty permissions (deny by default)\n *\n * @param definitions - Object mapping role names to permission arrays\n * @returns Frozen RoleRegistry object\n *\n * @example\n * const registry = defineRoles({\n * superadmin: ['*'],\n * admin: ['user.read', 'user.update'],\n * support: ['ticket.read', 'ticket.reply']\n * });\n *\n * registry.getPermissions('admin'); // ['user.read', 'user.update']\n * registry.getPermissions('unknown'); // []\n */\nexport function defineRoles(definitions: RoleDefinition): RoleRegistry {\n // Create a deep-frozen copy to prevent external mutations\n const frozenDefinitions: Record<string, readonly string[]> = {};\n\n for (const role of Object.keys(definitions)) {\n const permissions = definitions[role];\n if (permissions !== undefined) {\n // Freeze the permissions array\n frozenDefinitions[role] = Object.freeze([...permissions]);\n }\n }\n\n // Freeze the definitions object itself\n Object.freeze(frozenDefinitions);\n\n const registry: RoleRegistry = {\n getPermissions(role: string): readonly string[] {\n const permissions = frozenDefinitions[role];\n // Deny by default: unknown roles get empty permissions\n return permissions !== undefined ? permissions : Object.freeze([]);\n },\n\n hasRole(role: string): boolean {\n return Object.prototype.hasOwnProperty.call(frozenDefinitions, role);\n },\n\n getRoleNames(): readonly string[] {\n return Object.freeze(Object.keys(frozenDefinitions));\n },\n };\n\n // Freeze the registry object to prevent modifications\n return Object.freeze(registry);\n}\n\n/**\n * Creates an empty role registry.\n * Useful for testing or when no roles are defined.\n *\n * @returns Empty frozen RoleRegistry\n */\nexport function createEmptyRegistry(): RoleRegistry {\n return defineRoles({});\n}\n","/**\n * Secure Role Guard - Permission Engine\n *\n * Pure functions for permission checking.\n * Zero side effects, zero dependencies, zero mutations.\n */\n\nimport type { UserContext, RoleRegistry, PermissionCheckResult } from \"./types\";\n\n/** Wildcard permission that grants all access */\nconst WILDCARD_PERMISSION = \"*\";\n\n/**\n * Collects all permissions for a user from their roles and direct permissions.\n *\n * @param user - The user context\n * @param registry - The role registry to resolve role permissions\n * @returns Set of all permissions (for efficient lookup)\n */\nfunction collectUserPermissions(\n user: UserContext,\n registry: RoleRegistry,\n): ReadonlySet<string> {\n const permissions = new Set<string>();\n\n // Add direct user permissions\n if (user.permissions !== undefined) {\n for (const permission of user.permissions) {\n permissions.add(permission);\n }\n }\n\n // Add permissions from all user roles\n if (user.roles !== undefined) {\n for (const role of user.roles) {\n const rolePermissions = registry.getPermissions(role);\n for (const permission of rolePermissions) {\n permissions.add(permission);\n }\n }\n }\n\n return permissions;\n}\n\n/**\n * Checks if a permission set contains a wildcard that grants the requested permission.\n *\n * Supports:\n * - Exact wildcard (*) - grants everything\n * - Namespace wildcards (user.*) - grants all under namespace\n *\n * @param permissions - Set of permissions to check\n * @param requested - The permission being requested\n * @returns True if wildcard grants access\n */\nfunction hasWildcardAccess(\n permissions: ReadonlySet<string>,\n requested: string,\n): boolean {\n // Check for global wildcard\n if (permissions.has(WILDCARD_PERMISSION)) {\n return true;\n }\n\n // Check for namespace wildcards (e.g., user.* grants user.read)\n const parts = requested.split(\".\");\n let namespace = \"\";\n\n for (let i = 0; i < parts.length - 1; i++) {\n const part = parts[i];\n if (part !== undefined) {\n namespace = namespace === \"\" ? part : `${namespace}.${part}`;\n if (permissions.has(`${namespace}.*`)) {\n return true;\n }\n }\n }\n\n return false;\n}\n\n/**\n * Checks if a user has a specific permission.\n *\n * SECURITY GUARANTEES:\n * - Deny by default: undefined/null/empty always returns false\n * - Pure function: no side effects, no mutations\n * - Deterministic: same inputs always produce same output\n *\n * @param user - The user context to check\n * @param permission - The permission required\n * @param registry - The role registry for resolving roles\n * @returns True if user has the permission, false otherwise\n *\n * @example\n * const canEdit = canUser(currentUser, 'user.update', roleRegistry);\n */\nexport function canUser(\n user: UserContext | null | undefined,\n permission: string,\n registry: RoleRegistry,\n): boolean {\n // DENY BY DEFAULT: No user context means no access\n if (user === null || user === undefined) {\n return false;\n }\n\n // DENY BY DEFAULT: Empty permission request is invalid\n if (permission === \"\" || permission.trim() === \"\") {\n return false;\n }\n\n const userPermissions = collectUserPermissions(user, registry);\n\n // Check for exact permission match\n if (userPermissions.has(permission)) {\n return true;\n }\n\n // Check for wildcard access\n if (hasWildcardAccess(userPermissions, permission)) {\n return true;\n }\n\n // DENY BY DEFAULT\n return false;\n}\n\n/**\n * Checks if a user has ALL of the specified permissions.\n *\n * @param user - The user context to check\n * @param permissions - Array of permissions required\n * @param registry - The role registry\n * @returns True if user has ALL permissions\n *\n * @example\n * const canManage = canUserAll(user, ['user.read', 'user.update'], registry);\n */\nexport function canUserAll(\n user: UserContext | null | undefined,\n permissions: readonly string[],\n registry: RoleRegistry,\n): boolean {\n // DENY BY DEFAULT: Empty permissions array\n if (permissions.length === 0) {\n return false;\n }\n\n for (const permission of permissions) {\n if (!canUser(user, permission, registry)) {\n return false;\n }\n }\n\n return true;\n}\n\n/**\n * Checks if a user has ANY of the specified permissions.\n *\n * @param user - The user context to check\n * @param permissions - Array of permissions to check\n * @param registry - The role registry\n * @returns True if user has at least one permission\n *\n * @example\n * const canView = canUserAny(user, ['report.view', 'report.admin'], registry);\n */\nexport function canUserAny(\n user: UserContext | null | undefined,\n permissions: readonly string[],\n registry: RoleRegistry,\n): boolean {\n // DENY BY DEFAULT: Empty permissions array\n if (permissions.length === 0) {\n return false;\n }\n\n for (const permission of permissions) {\n if (canUser(user, permission, registry)) {\n return true;\n }\n }\n\n return false;\n}\n\n/**\n * Extended permission check with detailed result.\n * Useful for debugging and logging.\n *\n * @param user - The user context\n * @param permission - Permission to check\n * @param registry - The role registry\n * @returns PermissionCheckResult with allowed status and reason\n */\nexport function checkPermission(\n user: UserContext | null | undefined,\n permission: string,\n registry: RoleRegistry,\n): PermissionCheckResult {\n if (user === null || user === undefined) {\n return Object.freeze({\n allowed: false,\n reason: \"No user context provided\",\n });\n }\n\n if (permission === \"\" || permission.trim() === \"\") {\n return Object.freeze({\n allowed: false,\n reason: \"Empty permission requested\",\n });\n }\n\n const allowed = canUser(user, permission, registry);\n\n return Object.freeze({\n allowed,\n reason: allowed\n ? `Permission \"${permission}\" granted`\n : `Permission \"${permission}\" denied`,\n });\n}\n"]}
package/dist/index.js CHANGED
@@ -173,8 +173,6 @@ function useUser() {
173
173
  const { user } = react.useContext(PermissionContext);
174
174
  return user;
175
175
  }
176
-
177
- // src/react/components.tsx
178
176
  function Can({
179
177
  permission,
180
178
  permissions,
@@ -184,13 +182,13 @@ function Can({
184
182
  }) {
185
183
  if (permission !== void 0) {
186
184
  const allowed = useCan(permission);
187
- return allowed ? children : fallback;
185
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: allowed ? children : fallback });
188
186
  }
189
187
  if (permissions !== void 0 && permissions.length > 0) {
190
188
  const allowed = anyOf ? useCanAny(permissions) : useCanAll(permissions);
191
- return allowed ? children : fallback;
189
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: allowed ? children : fallback });
192
190
  }
193
- return fallback;
191
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: fallback });
194
192
  }
195
193
  function Cannot({
196
194
  permission,
@@ -200,13 +198,13 @@ function Cannot({
200
198
  }) {
201
199
  if (permission !== void 0) {
202
200
  const allowed = useCan(permission);
203
- return allowed ? null : children;
201
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: allowed ? null : children });
204
202
  }
205
203
  if (permissions !== void 0 && permissions.length > 0) {
206
204
  const allowed = anyOf ? useCanAny(permissions) : useCanAll(permissions);
207
- return allowed ? null : children;
205
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: allowed ? null : children });
208
206
  }
209
- return children;
207
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children });
210
208
  }
211
209
  /**
212
210
  * Secure Role Guard
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/core/role-registry.ts","../src/core/permission-engine.ts","../src/react/context.tsx","../src/react/provider.tsx","../src/react/hooks.ts","../src/react/components.tsx"],"names":["createContext","useMemo","useContext"],"mappings":"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","file":"index.js","sourcesContent":["/**\r\n * Secure Role Guard - Role Registry\r\n *\r\n * Creates an immutable registry of role definitions.\r\n * Pure functions only, zero side effects.\r\n */\r\n\r\nimport type { RoleDefinition, RoleRegistry } from \"./types\";\r\n\r\n/**\r\n * Creates an immutable role registry from role definitions.\r\n *\r\n * Security guarantees:\r\n * - Returns a frozen, immutable object\r\n * - No mutations possible after creation\r\n * - Unknown roles return empty permissions (deny by default)\r\n *\r\n * @param definitions - Object mapping role names to permission arrays\r\n * @returns Frozen RoleRegistry object\r\n *\r\n * @example\r\n * const registry = defineRoles({\r\n * superadmin: ['*'],\r\n * admin: ['user.read', 'user.update'],\r\n * support: ['ticket.read', 'ticket.reply']\r\n * });\r\n *\r\n * registry.getPermissions('admin'); // ['user.read', 'user.update']\r\n * registry.getPermissions('unknown'); // []\r\n */\r\nexport function defineRoles(definitions: RoleDefinition): RoleRegistry {\r\n // Create a deep-frozen copy to prevent external mutations\r\n const frozenDefinitions: Record<string, readonly string[]> = {};\r\n\r\n for (const role of Object.keys(definitions)) {\r\n const permissions = definitions[role];\r\n if (permissions !== undefined) {\r\n // Freeze the permissions array\r\n frozenDefinitions[role] = Object.freeze([...permissions]);\r\n }\r\n }\r\n\r\n // Freeze the definitions object itself\r\n Object.freeze(frozenDefinitions);\r\n\r\n const registry: RoleRegistry = {\r\n getPermissions(role: string): readonly string[] {\r\n const permissions = frozenDefinitions[role];\r\n // Deny by default: unknown roles get empty permissions\r\n return permissions !== undefined ? permissions : Object.freeze([]);\r\n },\r\n\r\n hasRole(role: string): boolean {\r\n return Object.prototype.hasOwnProperty.call(frozenDefinitions, role);\r\n },\r\n\r\n getRoleNames(): readonly string[] {\r\n return Object.freeze(Object.keys(frozenDefinitions));\r\n },\r\n };\r\n\r\n // Freeze the registry object to prevent modifications\r\n return Object.freeze(registry);\r\n}\r\n\r\n/**\r\n * Creates an empty role registry.\r\n * Useful for testing or when no roles are defined.\r\n *\r\n * @returns Empty frozen RoleRegistry\r\n */\r\nexport function createEmptyRegistry(): RoleRegistry {\r\n return defineRoles({});\r\n}\r\n","/**\r\n * Secure Role Guard - Permission Engine\r\n *\r\n * Pure functions for permission checking.\r\n * Zero side effects, zero dependencies, zero mutations.\r\n */\r\n\r\nimport type { UserContext, RoleRegistry, PermissionCheckResult } from \"./types\";\r\n\r\n/** Wildcard permission that grants all access */\r\nconst WILDCARD_PERMISSION = \"*\";\r\n\r\n/**\r\n * Collects all permissions for a user from their roles and direct permissions.\r\n *\r\n * @param user - The user context\r\n * @param registry - The role registry to resolve role permissions\r\n * @returns Set of all permissions (for efficient lookup)\r\n */\r\nfunction collectUserPermissions(\r\n user: UserContext,\r\n registry: RoleRegistry\r\n): ReadonlySet<string> {\r\n const permissions = new Set<string>();\r\n\r\n // Add direct user permissions\r\n if (user.permissions !== undefined) {\r\n for (const permission of user.permissions) {\r\n permissions.add(permission);\r\n }\r\n }\r\n\r\n // Add permissions from all user roles\r\n if (user.roles !== undefined) {\r\n for (const role of user.roles) {\r\n const rolePermissions = registry.getPermissions(role);\r\n for (const permission of rolePermissions) {\r\n permissions.add(permission);\r\n }\r\n }\r\n }\r\n\r\n return permissions;\r\n}\r\n\r\n/**\r\n * Checks if a permission set contains a wildcard that grants the requested permission.\r\n *\r\n * Supports:\r\n * - Exact wildcard (*) - grants everything\r\n * - Namespace wildcards (user.*) - grants all under namespace\r\n *\r\n * @param permissions - Set of permissions to check\r\n * @param requested - The permission being requested\r\n * @returns True if wildcard grants access\r\n */\r\nfunction hasWildcardAccess(\r\n permissions: ReadonlySet<string>,\r\n requested: string\r\n): boolean {\r\n // Check for global wildcard\r\n if (permissions.has(WILDCARD_PERMISSION)) {\r\n return true;\r\n }\r\n\r\n // Check for namespace wildcards (e.g., user.* grants user.read)\r\n const parts = requested.split(\".\");\r\n let namespace = \"\";\r\n\r\n for (let i = 0; i < parts.length - 1; i++) {\r\n const part = parts[i];\r\n if (part !== undefined) {\r\n namespace = namespace === \"\" ? part : `${namespace}.${part}`;\r\n if (permissions.has(`${namespace}.*`)) {\r\n return true;\r\n }\r\n }\r\n }\r\n\r\n return false;\r\n}\r\n\r\n/**\r\n * Checks if a user has a specific permission.\r\n *\r\n * SECURITY GUARANTEES:\r\n * - Deny by default: undefined/null/empty always returns false\r\n * - Pure function: no side effects, no mutations\r\n * - Deterministic: same inputs always produce same output\r\n *\r\n * @param user - The user context to check\r\n * @param permission - The permission required\r\n * @param registry - The role registry for resolving roles\r\n * @returns True if user has the permission, false otherwise\r\n *\r\n * @example\r\n * const canEdit = canUser(currentUser, 'user.update', roleRegistry);\r\n */\r\nexport function canUser(\r\n user: UserContext | null | undefined,\r\n permission: string,\r\n registry: RoleRegistry\r\n): boolean {\r\n // DENY BY DEFAULT: No user context means no access\r\n if (user === null || user === undefined) {\r\n return false;\r\n }\r\n\r\n // DENY BY DEFAULT: Empty permission request is invalid\r\n if (permission === \"\" || permission.trim() === \"\") {\r\n return false;\r\n }\r\n\r\n const userPermissions = collectUserPermissions(user, registry);\r\n\r\n // Check for exact permission match\r\n if (userPermissions.has(permission)) {\r\n return true;\r\n }\r\n\r\n // Check for wildcard access\r\n if (hasWildcardAccess(userPermissions, permission)) {\r\n return true;\r\n }\r\n\r\n // DENY BY DEFAULT\r\n return false;\r\n}\r\n\r\n/**\r\n * Checks if a user has ALL of the specified permissions.\r\n *\r\n * @param user - The user context to check\r\n * @param permissions - Array of permissions required\r\n * @param registry - The role registry\r\n * @returns True if user has ALL permissions\r\n *\r\n * @example\r\n * const canManage = canUserAll(user, ['user.read', 'user.update'], registry);\r\n */\r\nexport function canUserAll(\r\n user: UserContext | null | undefined,\r\n permissions: readonly string[],\r\n registry: RoleRegistry\r\n): boolean {\r\n // DENY BY DEFAULT: Empty permissions array\r\n if (permissions.length === 0) {\r\n return false;\r\n }\r\n\r\n for (const permission of permissions) {\r\n if (!canUser(user, permission, registry)) {\r\n return false;\r\n }\r\n }\r\n\r\n return true;\r\n}\r\n\r\n/**\r\n * Checks if a user has ANY of the specified permissions.\r\n *\r\n * @param user - The user context to check\r\n * @param permissions - Array of permissions to check\r\n * @param registry - The role registry\r\n * @returns True if user has at least one permission\r\n *\r\n * @example\r\n * const canView = canUserAny(user, ['report.view', 'report.admin'], registry);\r\n */\r\nexport function canUserAny(\r\n user: UserContext | null | undefined,\r\n permissions: readonly string[],\r\n registry: RoleRegistry\r\n): boolean {\r\n // DENY BY DEFAULT: Empty permissions array\r\n if (permissions.length === 0) {\r\n return false;\r\n }\r\n\r\n for (const permission of permissions) {\r\n if (canUser(user, permission, registry)) {\r\n return true;\r\n }\r\n }\r\n\r\n return false;\r\n}\r\n\r\n/**\r\n * Extended permission check with detailed result.\r\n * Useful for debugging and logging.\r\n *\r\n * @param user - The user context\r\n * @param permission - Permission to check\r\n * @param registry - The role registry\r\n * @returns PermissionCheckResult with allowed status and reason\r\n */\r\nexport function checkPermission(\r\n user: UserContext | null | undefined,\r\n permission: string,\r\n registry: RoleRegistry\r\n): PermissionCheckResult {\r\n if (user === null || user === undefined) {\r\n return Object.freeze({\r\n allowed: false,\r\n reason: \"No user context provided\",\r\n });\r\n }\r\n\r\n if (permission === \"\" || permission.trim() === \"\") {\r\n return Object.freeze({\r\n allowed: false,\r\n reason: \"Empty permission requested\",\r\n });\r\n }\r\n\r\n const allowed = canUser(user, permission, registry);\r\n\r\n return Object.freeze({\r\n allowed,\r\n reason: allowed\r\n ? `Permission \"${permission}\" granted`\r\n : `Permission \"${permission}\" denied`,\r\n });\r\n}\r\n","/**\r\n * Secure Role Guard - React Context\r\n *\r\n * Framework-agnostic React context for permission management.\r\n * Works with Next.js, Remix, Gatsby, CRA, Vite, and any React framework.\r\n */\r\n\r\nimport { createContext } from \"react\";\r\nimport type { UserContext, RoleRegistry } from \"../core/types\";\r\n\r\n/**\r\n * Shape of the permission context value.\r\n */\r\nexport type PermissionContextValue = {\r\n /** Current user context */\r\n readonly user: UserContext | null;\r\n /** Role registry for permission lookups */\r\n readonly registry: RoleRegistry;\r\n /** Check if user has a specific permission */\r\n readonly can: (permission: string) => boolean;\r\n /** Check if user has ALL permissions */\r\n readonly canAll: (permissions: readonly string[]) => boolean;\r\n /** Check if user has ANY permission */\r\n readonly canAny: (permissions: readonly string[]) => boolean;\r\n};\r\n\r\n/**\r\n * Default context value (deny all).\r\n * Used when no provider is present.\r\n */\r\nconst defaultContextValue: PermissionContextValue = {\r\n user: null,\r\n registry: {\r\n getPermissions: () => Object.freeze([]),\r\n hasRole: () => false,\r\n getRoleNames: () => Object.freeze([]),\r\n },\r\n can: () => false,\r\n canAll: () => false,\r\n canAny: () => false,\r\n};\r\n\r\n/**\r\n * React context for permissions.\r\n * Default value denies all permissions (deny by default).\r\n */\r\nexport const PermissionContext =\r\n createContext<PermissionContextValue>(defaultContextValue);\r\n\r\n// Set display name for React DevTools\r\nPermissionContext.displayName = \"PermissionContext\";\r\n","/**\r\n * Secure Role Guard - React Provider\r\n *\r\n * Main provider component for permission management.\r\n * Framework-agnostic: works with Next.js, Remix, Gatsby, CRA, Vite.\r\n */\r\n\r\nimport { useMemo, type ReactNode } from \"react\";\r\nimport type { UserContext, RoleRegistry } from \"../core/types\";\r\nimport { canUser, canUserAll, canUserAny } from \"../core/permission-engine\";\r\nimport { PermissionContext, type PermissionContextValue } from \"./context\";\r\n\r\n/**\r\n * Props for the PermissionProvider component.\r\n */\r\nexport type PermissionProviderProps = {\r\n /** Current user context (from your auth system) */\r\n readonly user: UserContext | null;\r\n /** Role registry created by defineRoles() */\r\n readonly registry: RoleRegistry;\r\n /** Child components */\r\n readonly children: ReactNode;\r\n};\r\n\r\n/**\r\n * Provides permission context to all child components.\r\n *\r\n * SECURITY: If user is null/undefined, all permission checks will deny by default.\r\n *\r\n * @example\r\n * ```tsx\r\n * import { defineRoles, PermissionProvider } from 'secure-role-guard';\r\n *\r\n * const roleRegistry = defineRoles({\r\n * admin: ['user.read', 'user.update'],\r\n * viewer: ['user.read']\r\n * });\r\n *\r\n * function App() {\r\n * const user = { roles: ['admin'] }; // From your auth system\r\n *\r\n * return (\r\n * <PermissionProvider user={user} registry={roleRegistry}>\r\n * <YourApp />\r\n * </PermissionProvider>\r\n * );\r\n * }\r\n * ```\r\n */\r\nexport function PermissionProvider({\r\n user,\r\n registry,\r\n children,\r\n}: PermissionProviderProps): ReactNode {\r\n // Memoize context value to prevent unnecessary re-renders\r\n const contextValue = useMemo<PermissionContextValue>(\r\n () => ({\r\n user,\r\n registry,\r\n can: (permission: string) => canUser(user, permission, registry),\r\n canAll: (permissions: readonly string[]) =>\r\n canUserAll(user, permissions, registry),\r\n canAny: (permissions: readonly string[]) =>\r\n canUserAny(user, permissions, registry),\r\n }),\r\n [user, registry]\r\n );\r\n\r\n return (\r\n <PermissionContext.Provider value={contextValue}>\r\n {children}\r\n </PermissionContext.Provider>\r\n );\r\n}\r\n","/**\r\n * Secure Role Guard - React Hooks\r\n *\r\n * Hooks for permission checking in React components.\r\n * Framework-agnostic: works with any React framework.\r\n */\r\n\r\nimport { useContext } from \"react\";\r\nimport { PermissionContext, type PermissionContextValue } from \"./context\";\r\n\r\n/**\r\n * Hook to access the full permission context.\r\n *\r\n * @returns Full PermissionContextValue\r\n *\r\n * @example\r\n * ```tsx\r\n * function UserProfile() {\r\n * const { user, can, canAll } = usePermissions();\r\n *\r\n * if (can('user.read')) {\r\n * return <UserDetails />;\r\n * }\r\n * return <AccessDenied />;\r\n * }\r\n * ```\r\n */\r\nexport function usePermissions(): PermissionContextValue {\r\n return useContext(PermissionContext);\r\n}\r\n\r\n/**\r\n * Hook to check a single permission.\r\n *\r\n * @param permission - The permission to check\r\n * @returns True if user has the permission\r\n *\r\n * @example\r\n * ```tsx\r\n * function EditButton() {\r\n * const canEdit = useCan('user.update');\r\n *\r\n * if (!canEdit) return null;\r\n * return <button>Edit User</button>;\r\n * }\r\n * ```\r\n */\r\nexport function useCan(permission: string): boolean {\r\n const { can } = useContext(PermissionContext);\r\n return can(permission);\r\n}\r\n\r\n/**\r\n * Hook to check if user has ALL specified permissions.\r\n *\r\n * @param permissions - Array of permissions to check\r\n * @returns True if user has all permissions\r\n *\r\n * @example\r\n * ```tsx\r\n * function AdminPanel() {\r\n * const canManage = useCanAll(['user.read', 'user.update', 'user.delete']);\r\n *\r\n * if (!canManage) return <AccessDenied />;\r\n * return <AdminDashboard />;\r\n * }\r\n * ```\r\n */\r\nexport function useCanAll(permissions: readonly string[]): boolean {\r\n const { canAll } = useContext(PermissionContext);\r\n return canAll(permissions);\r\n}\r\n\r\n/**\r\n * Hook to check if user has ANY of the specified permissions.\r\n *\r\n * @param permissions - Array of permissions to check\r\n * @returns True if user has at least one permission\r\n *\r\n * @example\r\n * ```tsx\r\n * function ViewReports() {\r\n * const canView = useCanAny(['report.admin', 'report.viewer']);\r\n *\r\n * if (!canView) return null;\r\n * return <ReportList />;\r\n * }\r\n * ```\r\n */\r\nexport function useCanAny(permissions: readonly string[]): boolean {\r\n const { canAny } = useContext(PermissionContext);\r\n return canAny(permissions);\r\n}\r\n\r\n/**\r\n * Hook to get current user context.\r\n *\r\n * @returns Current UserContext or null\r\n *\r\n * @example\r\n * ```tsx\r\n * function UserInfo() {\r\n * const user = useUser();\r\n * return <span>User ID: {user?.userId ?? 'Anonymous'}</span>;\r\n * }\r\n * ```\r\n */\r\nexport function useUser() {\r\n const { user } = useContext(PermissionContext);\r\n return user;\r\n}\r\n","/**\r\n * Secure Role Guard - React Components\r\n *\r\n * Declarative components for conditional rendering based on permissions.\r\n * Framework-agnostic: works with any React framework.\r\n */\r\n\r\nimport type { ReactNode } from \"react\";\r\nimport { useCan, useCanAll, useCanAny } from \"./hooks\";\r\n\r\n/**\r\n * Props for the Can component.\r\n */\r\nexport type CanProps = {\r\n /** Single permission to check */\r\n readonly permission?: string;\r\n /** Multiple permissions - user must have ALL (default behavior) */\r\n readonly permissions?: readonly string[];\r\n /** If true, user needs only ANY of the permissions */\r\n readonly anyOf?: boolean;\r\n /** Content to render if permission granted */\r\n readonly children: ReactNode;\r\n /** Optional fallback content if permission denied */\r\n readonly fallback?: ReactNode;\r\n};\r\n\r\n/**\r\n * Conditionally renders children based on user permissions.\r\n *\r\n * SECURITY: If no permission matches, nothing is rendered (deny by default).\r\n *\r\n * @example Single permission\r\n * ```tsx\r\n * <Can permission=\"user.update\">\r\n * <EditButton />\r\n * </Can>\r\n * ```\r\n *\r\n * @example Multiple permissions (ALL required)\r\n * ```tsx\r\n * <Can permissions={['user.read', 'user.update']}>\r\n * <UserEditor />\r\n * </Can>\r\n * ```\r\n *\r\n * @example Multiple permissions (ANY required)\r\n * ```tsx\r\n * <Can permissions={['admin', 'moderator']} anyOf>\r\n * <ModPanel />\r\n * </Can>\r\n * ```\r\n *\r\n * @example With fallback\r\n * ```tsx\r\n * <Can permission=\"premium.access\" fallback={<UpgradePrompt />}>\r\n * <PremiumContent />\r\n * </Can>\r\n * ```\r\n */\r\nexport function Can({\r\n permission,\r\n permissions,\r\n anyOf = false,\r\n children,\r\n fallback = null,\r\n}: CanProps): ReactNode {\r\n // Single permission check\r\n if (permission !== undefined) {\r\n const allowed = useCan(permission);\r\n return allowed ? children : fallback;\r\n }\r\n\r\n // Multiple permissions check\r\n if (permissions !== undefined && permissions.length > 0) {\r\n const allowed = anyOf ? useCanAny(permissions) : useCanAll(permissions);\r\n return allowed ? children : fallback;\r\n }\r\n\r\n // No permissions specified - deny by default\r\n return fallback;\r\n}\r\n\r\n/**\r\n * Props for the Cannot component.\r\n */\r\nexport type CannotProps = {\r\n /** Single permission to check */\r\n readonly permission?: string;\r\n /** Multiple permissions to check */\r\n readonly permissions?: readonly string[];\r\n /** If true, blocked when user has ANY permission */\r\n readonly anyOf?: boolean;\r\n /** Content to render if permission is NOT granted */\r\n readonly children: ReactNode;\r\n};\r\n\r\n/**\r\n * Inverse of Can - renders children when user does NOT have the permission.\r\n * Useful for showing upgrade prompts, locked features, etc.\r\n *\r\n * @example\r\n * ```tsx\r\n * <Cannot permission=\"premium.access\">\r\n * <UpgradePrompt />\r\n * </Cannot>\r\n * ```\r\n */\r\nexport function Cannot({\r\n permission,\r\n permissions,\r\n anyOf = false,\r\n children,\r\n}: CannotProps): ReactNode {\r\n // Single permission check\r\n if (permission !== undefined) {\r\n const allowed = useCan(permission);\r\n return allowed ? null : children;\r\n }\r\n\r\n // Multiple permissions check\r\n if (permissions !== undefined && permissions.length > 0) {\r\n const allowed = anyOf ? useCanAny(permissions) : useCanAll(permissions);\r\n return allowed ? null : children;\r\n }\r\n\r\n // No permissions specified - show children (inverse of deny by default)\r\n return children;\r\n}\r\n"]}
1
+ {"version":3,"sources":["../src/core/role-registry.ts","../src/core/permission-engine.ts","../src/react/context.tsx","../src/react/provider.tsx","../src/react/hooks.ts","../src/react/components.tsx"],"names":["createContext","useMemo","useContext","jsx","Fragment"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Secure Role Guard - Role Registry\n *\n * Creates an immutable registry of role definitions.\n * Pure functions only, zero side effects.\n */\n\nimport type { RoleDefinition, RoleRegistry } from \"./types\";\n\n/**\n * Creates an immutable role registry from role definitions.\n *\n * Security guarantees:\n * - Returns a frozen, immutable object\n * - No mutations possible after creation\n * - Unknown roles return empty permissions (deny by default)\n *\n * @param definitions - Object mapping role names to permission arrays\n * @returns Frozen RoleRegistry object\n *\n * @example\n * const registry = defineRoles({\n * superadmin: ['*'],\n * admin: ['user.read', 'user.update'],\n * support: ['ticket.read', 'ticket.reply']\n * });\n *\n * registry.getPermissions('admin'); // ['user.read', 'user.update']\n * registry.getPermissions('unknown'); // []\n */\nexport function defineRoles(definitions: RoleDefinition): RoleRegistry {\n // Create a deep-frozen copy to prevent external mutations\n const frozenDefinitions: Record<string, readonly string[]> = {};\n\n for (const role of Object.keys(definitions)) {\n const permissions = definitions[role];\n if (permissions !== undefined) {\n // Freeze the permissions array\n frozenDefinitions[role] = Object.freeze([...permissions]);\n }\n }\n\n // Freeze the definitions object itself\n Object.freeze(frozenDefinitions);\n\n const registry: RoleRegistry = {\n getPermissions(role: string): readonly string[] {\n const permissions = frozenDefinitions[role];\n // Deny by default: unknown roles get empty permissions\n return permissions !== undefined ? permissions : Object.freeze([]);\n },\n\n hasRole(role: string): boolean {\n return Object.prototype.hasOwnProperty.call(frozenDefinitions, role);\n },\n\n getRoleNames(): readonly string[] {\n return Object.freeze(Object.keys(frozenDefinitions));\n },\n };\n\n // Freeze the registry object to prevent modifications\n return Object.freeze(registry);\n}\n\n/**\n * Creates an empty role registry.\n * Useful for testing or when no roles are defined.\n *\n * @returns Empty frozen RoleRegistry\n */\nexport function createEmptyRegistry(): RoleRegistry {\n return defineRoles({});\n}\n","/**\n * Secure Role Guard - Permission Engine\n *\n * Pure functions for permission checking.\n * Zero side effects, zero dependencies, zero mutations.\n */\n\nimport type { UserContext, RoleRegistry, PermissionCheckResult } from \"./types\";\n\n/** Wildcard permission that grants all access */\nconst WILDCARD_PERMISSION = \"*\";\n\n/**\n * Collects all permissions for a user from their roles and direct permissions.\n *\n * @param user - The user context\n * @param registry - The role registry to resolve role permissions\n * @returns Set of all permissions (for efficient lookup)\n */\nfunction collectUserPermissions(\n user: UserContext,\n registry: RoleRegistry,\n): ReadonlySet<string> {\n const permissions = new Set<string>();\n\n // Add direct user permissions\n if (user.permissions !== undefined) {\n for (const permission of user.permissions) {\n permissions.add(permission);\n }\n }\n\n // Add permissions from all user roles\n if (user.roles !== undefined) {\n for (const role of user.roles) {\n const rolePermissions = registry.getPermissions(role);\n for (const permission of rolePermissions) {\n permissions.add(permission);\n }\n }\n }\n\n return permissions;\n}\n\n/**\n * Checks if a permission set contains a wildcard that grants the requested permission.\n *\n * Supports:\n * - Exact wildcard (*) - grants everything\n * - Namespace wildcards (user.*) - grants all under namespace\n *\n * @param permissions - Set of permissions to check\n * @param requested - The permission being requested\n * @returns True if wildcard grants access\n */\nfunction hasWildcardAccess(\n permissions: ReadonlySet<string>,\n requested: string,\n): boolean {\n // Check for global wildcard\n if (permissions.has(WILDCARD_PERMISSION)) {\n return true;\n }\n\n // Check for namespace wildcards (e.g., user.* grants user.read)\n const parts = requested.split(\".\");\n let namespace = \"\";\n\n for (let i = 0; i < parts.length - 1; i++) {\n const part = parts[i];\n if (part !== undefined) {\n namespace = namespace === \"\" ? part : `${namespace}.${part}`;\n if (permissions.has(`${namespace}.*`)) {\n return true;\n }\n }\n }\n\n return false;\n}\n\n/**\n * Checks if a user has a specific permission.\n *\n * SECURITY GUARANTEES:\n * - Deny by default: undefined/null/empty always returns false\n * - Pure function: no side effects, no mutations\n * - Deterministic: same inputs always produce same output\n *\n * @param user - The user context to check\n * @param permission - The permission required\n * @param registry - The role registry for resolving roles\n * @returns True if user has the permission, false otherwise\n *\n * @example\n * const canEdit = canUser(currentUser, 'user.update', roleRegistry);\n */\nexport function canUser(\n user: UserContext | null | undefined,\n permission: string,\n registry: RoleRegistry,\n): boolean {\n // DENY BY DEFAULT: No user context means no access\n if (user === null || user === undefined) {\n return false;\n }\n\n // DENY BY DEFAULT: Empty permission request is invalid\n if (permission === \"\" || permission.trim() === \"\") {\n return false;\n }\n\n const userPermissions = collectUserPermissions(user, registry);\n\n // Check for exact permission match\n if (userPermissions.has(permission)) {\n return true;\n }\n\n // Check for wildcard access\n if (hasWildcardAccess(userPermissions, permission)) {\n return true;\n }\n\n // DENY BY DEFAULT\n return false;\n}\n\n/**\n * Checks if a user has ALL of the specified permissions.\n *\n * @param user - The user context to check\n * @param permissions - Array of permissions required\n * @param registry - The role registry\n * @returns True if user has ALL permissions\n *\n * @example\n * const canManage = canUserAll(user, ['user.read', 'user.update'], registry);\n */\nexport function canUserAll(\n user: UserContext | null | undefined,\n permissions: readonly string[],\n registry: RoleRegistry,\n): boolean {\n // DENY BY DEFAULT: Empty permissions array\n if (permissions.length === 0) {\n return false;\n }\n\n for (const permission of permissions) {\n if (!canUser(user, permission, registry)) {\n return false;\n }\n }\n\n return true;\n}\n\n/**\n * Checks if a user has ANY of the specified permissions.\n *\n * @param user - The user context to check\n * @param permissions - Array of permissions to check\n * @param registry - The role registry\n * @returns True if user has at least one permission\n *\n * @example\n * const canView = canUserAny(user, ['report.view', 'report.admin'], registry);\n */\nexport function canUserAny(\n user: UserContext | null | undefined,\n permissions: readonly string[],\n registry: RoleRegistry,\n): boolean {\n // DENY BY DEFAULT: Empty permissions array\n if (permissions.length === 0) {\n return false;\n }\n\n for (const permission of permissions) {\n if (canUser(user, permission, registry)) {\n return true;\n }\n }\n\n return false;\n}\n\n/**\n * Extended permission check with detailed result.\n * Useful for debugging and logging.\n *\n * @param user - The user context\n * @param permission - Permission to check\n * @param registry - The role registry\n * @returns PermissionCheckResult with allowed status and reason\n */\nexport function checkPermission(\n user: UserContext | null | undefined,\n permission: string,\n registry: RoleRegistry,\n): PermissionCheckResult {\n if (user === null || user === undefined) {\n return Object.freeze({\n allowed: false,\n reason: \"No user context provided\",\n });\n }\n\n if (permission === \"\" || permission.trim() === \"\") {\n return Object.freeze({\n allowed: false,\n reason: \"Empty permission requested\",\n });\n }\n\n const allowed = canUser(user, permission, registry);\n\n return Object.freeze({\n allowed,\n reason: allowed\n ? `Permission \"${permission}\" granted`\n : `Permission \"${permission}\" denied`,\n });\n}\n","/**\n * Secure Role Guard - React Context\n *\n * Framework-agnostic React context for permission management.\n * Works with Next.js, Remix, Gatsby, CRA, Vite, and any React framework.\n */\n\nimport { createContext } from \"react\";\nimport type { UserContext, RoleRegistry } from \"../core/types\";\n\n/**\n * Shape of the permission context value.\n */\nexport type PermissionContextValue = {\n /** Current user context */\n readonly user: UserContext | null;\n /** Role registry for permission lookups */\n readonly registry: RoleRegistry;\n /** Check if user has a specific permission */\n readonly can: (permission: string) => boolean;\n /** Check if user has ALL permissions */\n readonly canAll: (permissions: readonly string[]) => boolean;\n /** Check if user has ANY permission */\n readonly canAny: (permissions: readonly string[]) => boolean;\n};\n\n/**\n * Default context value (deny all).\n * Used when no provider is present.\n */\nconst defaultContextValue: PermissionContextValue = {\n user: null,\n registry: {\n getPermissions: () => Object.freeze([]),\n hasRole: () => false,\n getRoleNames: () => Object.freeze([]),\n },\n can: () => false,\n canAll: () => false,\n canAny: () => false,\n};\n\n/**\n * React context for permissions.\n * Default value denies all permissions (deny by default).\n */\nexport const PermissionContext =\n createContext<PermissionContextValue>(defaultContextValue);\n\n// Set display name for React DevTools\nPermissionContext.displayName = \"PermissionContext\";\n","/**\n * Secure Role Guard - React Provider\n *\n * Main provider component for permission management.\n * Framework-agnostic: works with Next.js, Remix, Gatsby, CRA, Vite.\n */\n\nimport { useMemo, type ReactNode } from \"react\";\nimport type { UserContext, RoleRegistry } from \"../core/types\";\nimport { canUser, canUserAll, canUserAny } from \"../core/permission-engine\";\nimport { PermissionContext, type PermissionContextValue } from \"./context\";\n\n/**\n * Props for the PermissionProvider component.\n */\nexport type PermissionProviderProps = {\n /** Current user context (from your auth system) */\n readonly user: UserContext | null;\n /** Role registry created by defineRoles() */\n readonly registry: RoleRegistry;\n /** Child components */\n readonly children: ReactNode;\n};\n\n/**\n * Provides permission context to all child components.\n *\n * SECURITY: If user is null/undefined, all permission checks will deny by default.\n *\n * @example\n * ```tsx\n * import { defineRoles, PermissionProvider } from 'secure-role-guard';\n *\n * const roleRegistry = defineRoles({\n * admin: ['user.read', 'user.update'],\n * viewer: ['user.read']\n * });\n *\n * function App() {\n * const user = { roles: ['admin'] }; // From your auth system\n *\n * return (\n * <PermissionProvider user={user} registry={roleRegistry}>\n * <YourApp />\n * </PermissionProvider>\n * );\n * }\n * ```\n */\nexport function PermissionProvider({\n user,\n registry,\n children,\n}: PermissionProviderProps) {\n // Memoize context value to prevent unnecessary re-renders\n const contextValue = useMemo<PermissionContextValue>(\n () => ({\n user,\n registry,\n can: (permission: string) => canUser(user, permission, registry),\n canAll: (permissions: readonly string[]) =>\n canUserAll(user, permissions, registry),\n canAny: (permissions: readonly string[]) =>\n canUserAny(user, permissions, registry),\n }),\n [user, registry],\n );\n\n return (\n <PermissionContext.Provider value={contextValue}>\n {children}\n </PermissionContext.Provider>\n );\n}\n","/**\n * Secure Role Guard - React Hooks\n *\n * Hooks for permission checking in React components.\n * Framework-agnostic: works with any React framework.\n */\n\nimport { useContext } from \"react\";\nimport { PermissionContext, type PermissionContextValue } from \"./context\";\n\n/**\n * Hook to access the full permission context.\n *\n * @returns Full PermissionContextValue\n *\n * @example\n * ```tsx\n * function UserProfile() {\n * const { user, can, canAll } = usePermissions();\n *\n * if (can('user.read')) {\n * return <UserDetails />;\n * }\n * return <AccessDenied />;\n * }\n * ```\n */\nexport function usePermissions(): PermissionContextValue {\n return useContext(PermissionContext);\n}\n\n/**\n * Hook to check a single permission.\n *\n * @param permission - The permission to check\n * @returns True if user has the permission\n *\n * @example\n * ```tsx\n * function EditButton() {\n * const canEdit = useCan('user.update');\n *\n * if (!canEdit) return null;\n * return <button>Edit User</button>;\n * }\n * ```\n */\nexport function useCan(permission: string): boolean {\n const { can } = useContext(PermissionContext);\n return can(permission);\n}\n\n/**\n * Hook to check if user has ALL specified permissions.\n *\n * @param permissions - Array of permissions to check\n * @returns True if user has all permissions\n *\n * @example\n * ```tsx\n * function AdminPanel() {\n * const canManage = useCanAll(['user.read', 'user.update', 'user.delete']);\n *\n * if (!canManage) return <AccessDenied />;\n * return <AdminDashboard />;\n * }\n * ```\n */\nexport function useCanAll(permissions: readonly string[]): boolean {\n const { canAll } = useContext(PermissionContext);\n return canAll(permissions);\n}\n\n/**\n * Hook to check if user has ANY of the specified permissions.\n *\n * @param permissions - Array of permissions to check\n * @returns True if user has at least one permission\n *\n * @example\n * ```tsx\n * function ViewReports() {\n * const canView = useCanAny(['report.admin', 'report.viewer']);\n *\n * if (!canView) return null;\n * return <ReportList />;\n * }\n * ```\n */\nexport function useCanAny(permissions: readonly string[]): boolean {\n const { canAny } = useContext(PermissionContext);\n return canAny(permissions);\n}\n\n/**\n * Hook to get current user context.\n *\n * @returns Current UserContext or null\n *\n * @example\n * ```tsx\n * function UserInfo() {\n * const user = useUser();\n * return <span>User ID: {user?.userId ?? 'Anonymous'}</span>;\n * }\n * ```\n */\nexport function useUser() {\n const { user } = useContext(PermissionContext);\n return user;\n}\n","/**\n * Secure Role Guard - React Components\n *\n * Declarative components for conditional rendering based on permissions.\n * Framework-agnostic: works with any React framework.\n */\n\nimport type { ReactNode } from \"react\";\nimport { useCan, useCanAll, useCanAny } from \"./hooks\";\n\n/**\n * Props for the Can component.\n */\nexport type CanProps = {\n /** Single permission to check */\n readonly permission?: string;\n /** Multiple permissions - user must have ALL (default behavior) */\n readonly permissions?: readonly string[];\n /** If true, user needs only ANY of the permissions */\n readonly anyOf?: boolean;\n /** Content to render if permission granted */\n readonly children: ReactNode;\n /** Optional fallback content if permission denied */\n readonly fallback?: ReactNode;\n};\n\n/**\n * Conditionally renders children based on user permissions.\n *\n * SECURITY: If no permission matches, nothing is rendered (deny by default).\n *\n * @example Single permission\n * ```tsx\n * <Can permission=\"user.update\">\n * <EditButton />\n * </Can>\n * ```\n *\n * @example Multiple permissions (ALL required)\n * ```tsx\n * <Can permissions={['user.read', 'user.update']}>\n * <UserEditor />\n * </Can>\n * ```\n *\n * @example Multiple permissions (ANY required)\n * ```tsx\n * <Can permissions={['admin', 'moderator']} anyOf>\n * <ModPanel />\n * </Can>\n * ```\n *\n * @example With fallback\n * ```tsx\n * <Can permission=\"premium.access\" fallback={<UpgradePrompt />}>\n * <PremiumContent />\n * </Can>\n * ```\n */\nexport function Can({\n permission,\n permissions,\n anyOf = false,\n children,\n fallback = null,\n}: CanProps) {\n // Single permission check\n if (permission !== undefined) {\n const allowed = useCan(permission);\n return <>{allowed ? children : fallback}</>;\n }\n\n // Multiple permissions check\n if (permissions !== undefined && permissions.length > 0) {\n const allowed = anyOf ? useCanAny(permissions) : useCanAll(permissions);\n return <>{allowed ? children : fallback}</>;\n }\n\n // No permissions specified - deny by default\n return <>{fallback}</>;\n}\n\n/**\n * Props for the Cannot component.\n */\nexport type CannotProps = {\n /** Single permission to check */\n readonly permission?: string;\n /** Multiple permissions to check */\n readonly permissions?: readonly string[];\n /** If true, blocked when user has ANY permission */\n readonly anyOf?: boolean;\n /** Content to render if permission is NOT granted */\n readonly children: ReactNode;\n};\n\n/**\n * Inverse of Can - renders children when user does NOT have the permission.\n * Useful for showing upgrade prompts, locked features, etc.\n *\n * @example\n * ```tsx\n * <Cannot permission=\"premium.access\">\n * <UpgradePrompt />\n * </Cannot>\n * ```\n */\nexport function Cannot({\n permission,\n permissions,\n anyOf = false,\n children,\n}: CannotProps) {\n // Single permission check\n if (permission !== undefined) {\n const allowed = useCan(permission);\n return <>{allowed ? null : children}</>;\n }\n\n // Multiple permissions check\n if (permissions !== undefined && permissions.length > 0) {\n const allowed = anyOf ? useCanAny(permissions) : useCanAll(permissions);\n return <>{allowed ? null : children}</>;\n }\n\n // No permissions specified - show children (inverse of deny by default)\n return <>{children}</>;\n}\n"]}
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { createContext, useMemo, useContext } from 'react';
2
- import { jsx } from 'react/jsx-runtime';
2
+ import { jsx, Fragment } from 'react/jsx-runtime';
3
3
 
4
4
  // src/core/role-registry.ts
5
5
  function defineRoles(definitions) {
@@ -171,8 +171,6 @@ function useUser() {
171
171
  const { user } = useContext(PermissionContext);
172
172
  return user;
173
173
  }
174
-
175
- // src/react/components.tsx
176
174
  function Can({
177
175
  permission,
178
176
  permissions,
@@ -182,13 +180,13 @@ function Can({
182
180
  }) {
183
181
  if (permission !== void 0) {
184
182
  const allowed = useCan(permission);
185
- return allowed ? children : fallback;
183
+ return /* @__PURE__ */ jsx(Fragment, { children: allowed ? children : fallback });
186
184
  }
187
185
  if (permissions !== void 0 && permissions.length > 0) {
188
186
  const allowed = anyOf ? useCanAny(permissions) : useCanAll(permissions);
189
- return allowed ? children : fallback;
187
+ return /* @__PURE__ */ jsx(Fragment, { children: allowed ? children : fallback });
190
188
  }
191
- return fallback;
189
+ return /* @__PURE__ */ jsx(Fragment, { children: fallback });
192
190
  }
193
191
  function Cannot({
194
192
  permission,
@@ -198,13 +196,13 @@ function Cannot({
198
196
  }) {
199
197
  if (permission !== void 0) {
200
198
  const allowed = useCan(permission);
201
- return allowed ? null : children;
199
+ return /* @__PURE__ */ jsx(Fragment, { children: allowed ? null : children });
202
200
  }
203
201
  if (permissions !== void 0 && permissions.length > 0) {
204
202
  const allowed = anyOf ? useCanAny(permissions) : useCanAll(permissions);
205
- return allowed ? null : children;
203
+ return /* @__PURE__ */ jsx(Fragment, { children: allowed ? null : children });
206
204
  }
207
- return children;
205
+ return /* @__PURE__ */ jsx(Fragment, { children });
208
206
  }
209
207
  /**
210
208
  * Secure Role Guard
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/core/role-registry.ts","../src/core/permission-engine.ts","../src/react/context.tsx","../src/react/provider.tsx","../src/react/hooks.ts","../src/react/components.tsx"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["/**\r\n * Secure Role Guard - Role Registry\r\n *\r\n * Creates an immutable registry of role definitions.\r\n * Pure functions only, zero side effects.\r\n */\r\n\r\nimport type { RoleDefinition, RoleRegistry } from \"./types\";\r\n\r\n/**\r\n * Creates an immutable role registry from role definitions.\r\n *\r\n * Security guarantees:\r\n * - Returns a frozen, immutable object\r\n * - No mutations possible after creation\r\n * - Unknown roles return empty permissions (deny by default)\r\n *\r\n * @param definitions - Object mapping role names to permission arrays\r\n * @returns Frozen RoleRegistry object\r\n *\r\n * @example\r\n * const registry = defineRoles({\r\n * superadmin: ['*'],\r\n * admin: ['user.read', 'user.update'],\r\n * support: ['ticket.read', 'ticket.reply']\r\n * });\r\n *\r\n * registry.getPermissions('admin'); // ['user.read', 'user.update']\r\n * registry.getPermissions('unknown'); // []\r\n */\r\nexport function defineRoles(definitions: RoleDefinition): RoleRegistry {\r\n // Create a deep-frozen copy to prevent external mutations\r\n const frozenDefinitions: Record<string, readonly string[]> = {};\r\n\r\n for (const role of Object.keys(definitions)) {\r\n const permissions = definitions[role];\r\n if (permissions !== undefined) {\r\n // Freeze the permissions array\r\n frozenDefinitions[role] = Object.freeze([...permissions]);\r\n }\r\n }\r\n\r\n // Freeze the definitions object itself\r\n Object.freeze(frozenDefinitions);\r\n\r\n const registry: RoleRegistry = {\r\n getPermissions(role: string): readonly string[] {\r\n const permissions = frozenDefinitions[role];\r\n // Deny by default: unknown roles get empty permissions\r\n return permissions !== undefined ? permissions : Object.freeze([]);\r\n },\r\n\r\n hasRole(role: string): boolean {\r\n return Object.prototype.hasOwnProperty.call(frozenDefinitions, role);\r\n },\r\n\r\n getRoleNames(): readonly string[] {\r\n return Object.freeze(Object.keys(frozenDefinitions));\r\n },\r\n };\r\n\r\n // Freeze the registry object to prevent modifications\r\n return Object.freeze(registry);\r\n}\r\n\r\n/**\r\n * Creates an empty role registry.\r\n * Useful for testing or when no roles are defined.\r\n *\r\n * @returns Empty frozen RoleRegistry\r\n */\r\nexport function createEmptyRegistry(): RoleRegistry {\r\n return defineRoles({});\r\n}\r\n","/**\r\n * Secure Role Guard - Permission Engine\r\n *\r\n * Pure functions for permission checking.\r\n * Zero side effects, zero dependencies, zero mutations.\r\n */\r\n\r\nimport type { UserContext, RoleRegistry, PermissionCheckResult } from \"./types\";\r\n\r\n/** Wildcard permission that grants all access */\r\nconst WILDCARD_PERMISSION = \"*\";\r\n\r\n/**\r\n * Collects all permissions for a user from their roles and direct permissions.\r\n *\r\n * @param user - The user context\r\n * @param registry - The role registry to resolve role permissions\r\n * @returns Set of all permissions (for efficient lookup)\r\n */\r\nfunction collectUserPermissions(\r\n user: UserContext,\r\n registry: RoleRegistry\r\n): ReadonlySet<string> {\r\n const permissions = new Set<string>();\r\n\r\n // Add direct user permissions\r\n if (user.permissions !== undefined) {\r\n for (const permission of user.permissions) {\r\n permissions.add(permission);\r\n }\r\n }\r\n\r\n // Add permissions from all user roles\r\n if (user.roles !== undefined) {\r\n for (const role of user.roles) {\r\n const rolePermissions = registry.getPermissions(role);\r\n for (const permission of rolePermissions) {\r\n permissions.add(permission);\r\n }\r\n }\r\n }\r\n\r\n return permissions;\r\n}\r\n\r\n/**\r\n * Checks if a permission set contains a wildcard that grants the requested permission.\r\n *\r\n * Supports:\r\n * - Exact wildcard (*) - grants everything\r\n * - Namespace wildcards (user.*) - grants all under namespace\r\n *\r\n * @param permissions - Set of permissions to check\r\n * @param requested - The permission being requested\r\n * @returns True if wildcard grants access\r\n */\r\nfunction hasWildcardAccess(\r\n permissions: ReadonlySet<string>,\r\n requested: string\r\n): boolean {\r\n // Check for global wildcard\r\n if (permissions.has(WILDCARD_PERMISSION)) {\r\n return true;\r\n }\r\n\r\n // Check for namespace wildcards (e.g., user.* grants user.read)\r\n const parts = requested.split(\".\");\r\n let namespace = \"\";\r\n\r\n for (let i = 0; i < parts.length - 1; i++) {\r\n const part = parts[i];\r\n if (part !== undefined) {\r\n namespace = namespace === \"\" ? part : `${namespace}.${part}`;\r\n if (permissions.has(`${namespace}.*`)) {\r\n return true;\r\n }\r\n }\r\n }\r\n\r\n return false;\r\n}\r\n\r\n/**\r\n * Checks if a user has a specific permission.\r\n *\r\n * SECURITY GUARANTEES:\r\n * - Deny by default: undefined/null/empty always returns false\r\n * - Pure function: no side effects, no mutations\r\n * - Deterministic: same inputs always produce same output\r\n *\r\n * @param user - The user context to check\r\n * @param permission - The permission required\r\n * @param registry - The role registry for resolving roles\r\n * @returns True if user has the permission, false otherwise\r\n *\r\n * @example\r\n * const canEdit = canUser(currentUser, 'user.update', roleRegistry);\r\n */\r\nexport function canUser(\r\n user: UserContext | null | undefined,\r\n permission: string,\r\n registry: RoleRegistry\r\n): boolean {\r\n // DENY BY DEFAULT: No user context means no access\r\n if (user === null || user === undefined) {\r\n return false;\r\n }\r\n\r\n // DENY BY DEFAULT: Empty permission request is invalid\r\n if (permission === \"\" || permission.trim() === \"\") {\r\n return false;\r\n }\r\n\r\n const userPermissions = collectUserPermissions(user, registry);\r\n\r\n // Check for exact permission match\r\n if (userPermissions.has(permission)) {\r\n return true;\r\n }\r\n\r\n // Check for wildcard access\r\n if (hasWildcardAccess(userPermissions, permission)) {\r\n return true;\r\n }\r\n\r\n // DENY BY DEFAULT\r\n return false;\r\n}\r\n\r\n/**\r\n * Checks if a user has ALL of the specified permissions.\r\n *\r\n * @param user - The user context to check\r\n * @param permissions - Array of permissions required\r\n * @param registry - The role registry\r\n * @returns True if user has ALL permissions\r\n *\r\n * @example\r\n * const canManage = canUserAll(user, ['user.read', 'user.update'], registry);\r\n */\r\nexport function canUserAll(\r\n user: UserContext | null | undefined,\r\n permissions: readonly string[],\r\n registry: RoleRegistry\r\n): boolean {\r\n // DENY BY DEFAULT: Empty permissions array\r\n if (permissions.length === 0) {\r\n return false;\r\n }\r\n\r\n for (const permission of permissions) {\r\n if (!canUser(user, permission, registry)) {\r\n return false;\r\n }\r\n }\r\n\r\n return true;\r\n}\r\n\r\n/**\r\n * Checks if a user has ANY of the specified permissions.\r\n *\r\n * @param user - The user context to check\r\n * @param permissions - Array of permissions to check\r\n * @param registry - The role registry\r\n * @returns True if user has at least one permission\r\n *\r\n * @example\r\n * const canView = canUserAny(user, ['report.view', 'report.admin'], registry);\r\n */\r\nexport function canUserAny(\r\n user: UserContext | null | undefined,\r\n permissions: readonly string[],\r\n registry: RoleRegistry\r\n): boolean {\r\n // DENY BY DEFAULT: Empty permissions array\r\n if (permissions.length === 0) {\r\n return false;\r\n }\r\n\r\n for (const permission of permissions) {\r\n if (canUser(user, permission, registry)) {\r\n return true;\r\n }\r\n }\r\n\r\n return false;\r\n}\r\n\r\n/**\r\n * Extended permission check with detailed result.\r\n * Useful for debugging and logging.\r\n *\r\n * @param user - The user context\r\n * @param permission - Permission to check\r\n * @param registry - The role registry\r\n * @returns PermissionCheckResult with allowed status and reason\r\n */\r\nexport function checkPermission(\r\n user: UserContext | null | undefined,\r\n permission: string,\r\n registry: RoleRegistry\r\n): PermissionCheckResult {\r\n if (user === null || user === undefined) {\r\n return Object.freeze({\r\n allowed: false,\r\n reason: \"No user context provided\",\r\n });\r\n }\r\n\r\n if (permission === \"\" || permission.trim() === \"\") {\r\n return Object.freeze({\r\n allowed: false,\r\n reason: \"Empty permission requested\",\r\n });\r\n }\r\n\r\n const allowed = canUser(user, permission, registry);\r\n\r\n return Object.freeze({\r\n allowed,\r\n reason: allowed\r\n ? `Permission \"${permission}\" granted`\r\n : `Permission \"${permission}\" denied`,\r\n });\r\n}\r\n","/**\r\n * Secure Role Guard - React Context\r\n *\r\n * Framework-agnostic React context for permission management.\r\n * Works with Next.js, Remix, Gatsby, CRA, Vite, and any React framework.\r\n */\r\n\r\nimport { createContext } from \"react\";\r\nimport type { UserContext, RoleRegistry } from \"../core/types\";\r\n\r\n/**\r\n * Shape of the permission context value.\r\n */\r\nexport type PermissionContextValue = {\r\n /** Current user context */\r\n readonly user: UserContext | null;\r\n /** Role registry for permission lookups */\r\n readonly registry: RoleRegistry;\r\n /** Check if user has a specific permission */\r\n readonly can: (permission: string) => boolean;\r\n /** Check if user has ALL permissions */\r\n readonly canAll: (permissions: readonly string[]) => boolean;\r\n /** Check if user has ANY permission */\r\n readonly canAny: (permissions: readonly string[]) => boolean;\r\n};\r\n\r\n/**\r\n * Default context value (deny all).\r\n * Used when no provider is present.\r\n */\r\nconst defaultContextValue: PermissionContextValue = {\r\n user: null,\r\n registry: {\r\n getPermissions: () => Object.freeze([]),\r\n hasRole: () => false,\r\n getRoleNames: () => Object.freeze([]),\r\n },\r\n can: () => false,\r\n canAll: () => false,\r\n canAny: () => false,\r\n};\r\n\r\n/**\r\n * React context for permissions.\r\n * Default value denies all permissions (deny by default).\r\n */\r\nexport const PermissionContext =\r\n createContext<PermissionContextValue>(defaultContextValue);\r\n\r\n// Set display name for React DevTools\r\nPermissionContext.displayName = \"PermissionContext\";\r\n","/**\r\n * Secure Role Guard - React Provider\r\n *\r\n * Main provider component for permission management.\r\n * Framework-agnostic: works with Next.js, Remix, Gatsby, CRA, Vite.\r\n */\r\n\r\nimport { useMemo, type ReactNode } from \"react\";\r\nimport type { UserContext, RoleRegistry } from \"../core/types\";\r\nimport { canUser, canUserAll, canUserAny } from \"../core/permission-engine\";\r\nimport { PermissionContext, type PermissionContextValue } from \"./context\";\r\n\r\n/**\r\n * Props for the PermissionProvider component.\r\n */\r\nexport type PermissionProviderProps = {\r\n /** Current user context (from your auth system) */\r\n readonly user: UserContext | null;\r\n /** Role registry created by defineRoles() */\r\n readonly registry: RoleRegistry;\r\n /** Child components */\r\n readonly children: ReactNode;\r\n};\r\n\r\n/**\r\n * Provides permission context to all child components.\r\n *\r\n * SECURITY: If user is null/undefined, all permission checks will deny by default.\r\n *\r\n * @example\r\n * ```tsx\r\n * import { defineRoles, PermissionProvider } from 'secure-role-guard';\r\n *\r\n * const roleRegistry = defineRoles({\r\n * admin: ['user.read', 'user.update'],\r\n * viewer: ['user.read']\r\n * });\r\n *\r\n * function App() {\r\n * const user = { roles: ['admin'] }; // From your auth system\r\n *\r\n * return (\r\n * <PermissionProvider user={user} registry={roleRegistry}>\r\n * <YourApp />\r\n * </PermissionProvider>\r\n * );\r\n * }\r\n * ```\r\n */\r\nexport function PermissionProvider({\r\n user,\r\n registry,\r\n children,\r\n}: PermissionProviderProps): ReactNode {\r\n // Memoize context value to prevent unnecessary re-renders\r\n const contextValue = useMemo<PermissionContextValue>(\r\n () => ({\r\n user,\r\n registry,\r\n can: (permission: string) => canUser(user, permission, registry),\r\n canAll: (permissions: readonly string[]) =>\r\n canUserAll(user, permissions, registry),\r\n canAny: (permissions: readonly string[]) =>\r\n canUserAny(user, permissions, registry),\r\n }),\r\n [user, registry]\r\n );\r\n\r\n return (\r\n <PermissionContext.Provider value={contextValue}>\r\n {children}\r\n </PermissionContext.Provider>\r\n );\r\n}\r\n","/**\r\n * Secure Role Guard - React Hooks\r\n *\r\n * Hooks for permission checking in React components.\r\n * Framework-agnostic: works with any React framework.\r\n */\r\n\r\nimport { useContext } from \"react\";\r\nimport { PermissionContext, type PermissionContextValue } from \"./context\";\r\n\r\n/**\r\n * Hook to access the full permission context.\r\n *\r\n * @returns Full PermissionContextValue\r\n *\r\n * @example\r\n * ```tsx\r\n * function UserProfile() {\r\n * const { user, can, canAll } = usePermissions();\r\n *\r\n * if (can('user.read')) {\r\n * return <UserDetails />;\r\n * }\r\n * return <AccessDenied />;\r\n * }\r\n * ```\r\n */\r\nexport function usePermissions(): PermissionContextValue {\r\n return useContext(PermissionContext);\r\n}\r\n\r\n/**\r\n * Hook to check a single permission.\r\n *\r\n * @param permission - The permission to check\r\n * @returns True if user has the permission\r\n *\r\n * @example\r\n * ```tsx\r\n * function EditButton() {\r\n * const canEdit = useCan('user.update');\r\n *\r\n * if (!canEdit) return null;\r\n * return <button>Edit User</button>;\r\n * }\r\n * ```\r\n */\r\nexport function useCan(permission: string): boolean {\r\n const { can } = useContext(PermissionContext);\r\n return can(permission);\r\n}\r\n\r\n/**\r\n * Hook to check if user has ALL specified permissions.\r\n *\r\n * @param permissions - Array of permissions to check\r\n * @returns True if user has all permissions\r\n *\r\n * @example\r\n * ```tsx\r\n * function AdminPanel() {\r\n * const canManage = useCanAll(['user.read', 'user.update', 'user.delete']);\r\n *\r\n * if (!canManage) return <AccessDenied />;\r\n * return <AdminDashboard />;\r\n * }\r\n * ```\r\n */\r\nexport function useCanAll(permissions: readonly string[]): boolean {\r\n const { canAll } = useContext(PermissionContext);\r\n return canAll(permissions);\r\n}\r\n\r\n/**\r\n * Hook to check if user has ANY of the specified permissions.\r\n *\r\n * @param permissions - Array of permissions to check\r\n * @returns True if user has at least one permission\r\n *\r\n * @example\r\n * ```tsx\r\n * function ViewReports() {\r\n * const canView = useCanAny(['report.admin', 'report.viewer']);\r\n *\r\n * if (!canView) return null;\r\n * return <ReportList />;\r\n * }\r\n * ```\r\n */\r\nexport function useCanAny(permissions: readonly string[]): boolean {\r\n const { canAny } = useContext(PermissionContext);\r\n return canAny(permissions);\r\n}\r\n\r\n/**\r\n * Hook to get current user context.\r\n *\r\n * @returns Current UserContext or null\r\n *\r\n * @example\r\n * ```tsx\r\n * function UserInfo() {\r\n * const user = useUser();\r\n * return <span>User ID: {user?.userId ?? 'Anonymous'}</span>;\r\n * }\r\n * ```\r\n */\r\nexport function useUser() {\r\n const { user } = useContext(PermissionContext);\r\n return user;\r\n}\r\n","/**\r\n * Secure Role Guard - React Components\r\n *\r\n * Declarative components for conditional rendering based on permissions.\r\n * Framework-agnostic: works with any React framework.\r\n */\r\n\r\nimport type { ReactNode } from \"react\";\r\nimport { useCan, useCanAll, useCanAny } from \"./hooks\";\r\n\r\n/**\r\n * Props for the Can component.\r\n */\r\nexport type CanProps = {\r\n /** Single permission to check */\r\n readonly permission?: string;\r\n /** Multiple permissions - user must have ALL (default behavior) */\r\n readonly permissions?: readonly string[];\r\n /** If true, user needs only ANY of the permissions */\r\n readonly anyOf?: boolean;\r\n /** Content to render if permission granted */\r\n readonly children: ReactNode;\r\n /** Optional fallback content if permission denied */\r\n readonly fallback?: ReactNode;\r\n};\r\n\r\n/**\r\n * Conditionally renders children based on user permissions.\r\n *\r\n * SECURITY: If no permission matches, nothing is rendered (deny by default).\r\n *\r\n * @example Single permission\r\n * ```tsx\r\n * <Can permission=\"user.update\">\r\n * <EditButton />\r\n * </Can>\r\n * ```\r\n *\r\n * @example Multiple permissions (ALL required)\r\n * ```tsx\r\n * <Can permissions={['user.read', 'user.update']}>\r\n * <UserEditor />\r\n * </Can>\r\n * ```\r\n *\r\n * @example Multiple permissions (ANY required)\r\n * ```tsx\r\n * <Can permissions={['admin', 'moderator']} anyOf>\r\n * <ModPanel />\r\n * </Can>\r\n * ```\r\n *\r\n * @example With fallback\r\n * ```tsx\r\n * <Can permission=\"premium.access\" fallback={<UpgradePrompt />}>\r\n * <PremiumContent />\r\n * </Can>\r\n * ```\r\n */\r\nexport function Can({\r\n permission,\r\n permissions,\r\n anyOf = false,\r\n children,\r\n fallback = null,\r\n}: CanProps): ReactNode {\r\n // Single permission check\r\n if (permission !== undefined) {\r\n const allowed = useCan(permission);\r\n return allowed ? children : fallback;\r\n }\r\n\r\n // Multiple permissions check\r\n if (permissions !== undefined && permissions.length > 0) {\r\n const allowed = anyOf ? useCanAny(permissions) : useCanAll(permissions);\r\n return allowed ? children : fallback;\r\n }\r\n\r\n // No permissions specified - deny by default\r\n return fallback;\r\n}\r\n\r\n/**\r\n * Props for the Cannot component.\r\n */\r\nexport type CannotProps = {\r\n /** Single permission to check */\r\n readonly permission?: string;\r\n /** Multiple permissions to check */\r\n readonly permissions?: readonly string[];\r\n /** If true, blocked when user has ANY permission */\r\n readonly anyOf?: boolean;\r\n /** Content to render if permission is NOT granted */\r\n readonly children: ReactNode;\r\n};\r\n\r\n/**\r\n * Inverse of Can - renders children when user does NOT have the permission.\r\n * Useful for showing upgrade prompts, locked features, etc.\r\n *\r\n * @example\r\n * ```tsx\r\n * <Cannot permission=\"premium.access\">\r\n * <UpgradePrompt />\r\n * </Cannot>\r\n * ```\r\n */\r\nexport function Cannot({\r\n permission,\r\n permissions,\r\n anyOf = false,\r\n children,\r\n}: CannotProps): ReactNode {\r\n // Single permission check\r\n if (permission !== undefined) {\r\n const allowed = useCan(permission);\r\n return allowed ? null : children;\r\n }\r\n\r\n // Multiple permissions check\r\n if (permissions !== undefined && permissions.length > 0) {\r\n const allowed = anyOf ? useCanAny(permissions) : useCanAll(permissions);\r\n return allowed ? null : children;\r\n }\r\n\r\n // No permissions specified - show children (inverse of deny by default)\r\n return children;\r\n}\r\n"]}
1
+ {"version":3,"sources":["../src/core/role-registry.ts","../src/core/permission-engine.ts","../src/react/context.tsx","../src/react/provider.tsx","../src/react/hooks.ts","../src/react/components.tsx"],"names":["jsx"],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * Secure Role Guard - Role Registry\n *\n * Creates an immutable registry of role definitions.\n * Pure functions only, zero side effects.\n */\n\nimport type { RoleDefinition, RoleRegistry } from \"./types\";\n\n/**\n * Creates an immutable role registry from role definitions.\n *\n * Security guarantees:\n * - Returns a frozen, immutable object\n * - No mutations possible after creation\n * - Unknown roles return empty permissions (deny by default)\n *\n * @param definitions - Object mapping role names to permission arrays\n * @returns Frozen RoleRegistry object\n *\n * @example\n * const registry = defineRoles({\n * superadmin: ['*'],\n * admin: ['user.read', 'user.update'],\n * support: ['ticket.read', 'ticket.reply']\n * });\n *\n * registry.getPermissions('admin'); // ['user.read', 'user.update']\n * registry.getPermissions('unknown'); // []\n */\nexport function defineRoles(definitions: RoleDefinition): RoleRegistry {\n // Create a deep-frozen copy to prevent external mutations\n const frozenDefinitions: Record<string, readonly string[]> = {};\n\n for (const role of Object.keys(definitions)) {\n const permissions = definitions[role];\n if (permissions !== undefined) {\n // Freeze the permissions array\n frozenDefinitions[role] = Object.freeze([...permissions]);\n }\n }\n\n // Freeze the definitions object itself\n Object.freeze(frozenDefinitions);\n\n const registry: RoleRegistry = {\n getPermissions(role: string): readonly string[] {\n const permissions = frozenDefinitions[role];\n // Deny by default: unknown roles get empty permissions\n return permissions !== undefined ? permissions : Object.freeze([]);\n },\n\n hasRole(role: string): boolean {\n return Object.prototype.hasOwnProperty.call(frozenDefinitions, role);\n },\n\n getRoleNames(): readonly string[] {\n return Object.freeze(Object.keys(frozenDefinitions));\n },\n };\n\n // Freeze the registry object to prevent modifications\n return Object.freeze(registry);\n}\n\n/**\n * Creates an empty role registry.\n * Useful for testing or when no roles are defined.\n *\n * @returns Empty frozen RoleRegistry\n */\nexport function createEmptyRegistry(): RoleRegistry {\n return defineRoles({});\n}\n","/**\n * Secure Role Guard - Permission Engine\n *\n * Pure functions for permission checking.\n * Zero side effects, zero dependencies, zero mutations.\n */\n\nimport type { UserContext, RoleRegistry, PermissionCheckResult } from \"./types\";\n\n/** Wildcard permission that grants all access */\nconst WILDCARD_PERMISSION = \"*\";\n\n/**\n * Collects all permissions for a user from their roles and direct permissions.\n *\n * @param user - The user context\n * @param registry - The role registry to resolve role permissions\n * @returns Set of all permissions (for efficient lookup)\n */\nfunction collectUserPermissions(\n user: UserContext,\n registry: RoleRegistry,\n): ReadonlySet<string> {\n const permissions = new Set<string>();\n\n // Add direct user permissions\n if (user.permissions !== undefined) {\n for (const permission of user.permissions) {\n permissions.add(permission);\n }\n }\n\n // Add permissions from all user roles\n if (user.roles !== undefined) {\n for (const role of user.roles) {\n const rolePermissions = registry.getPermissions(role);\n for (const permission of rolePermissions) {\n permissions.add(permission);\n }\n }\n }\n\n return permissions;\n}\n\n/**\n * Checks if a permission set contains a wildcard that grants the requested permission.\n *\n * Supports:\n * - Exact wildcard (*) - grants everything\n * - Namespace wildcards (user.*) - grants all under namespace\n *\n * @param permissions - Set of permissions to check\n * @param requested - The permission being requested\n * @returns True if wildcard grants access\n */\nfunction hasWildcardAccess(\n permissions: ReadonlySet<string>,\n requested: string,\n): boolean {\n // Check for global wildcard\n if (permissions.has(WILDCARD_PERMISSION)) {\n return true;\n }\n\n // Check for namespace wildcards (e.g., user.* grants user.read)\n const parts = requested.split(\".\");\n let namespace = \"\";\n\n for (let i = 0; i < parts.length - 1; i++) {\n const part = parts[i];\n if (part !== undefined) {\n namespace = namespace === \"\" ? part : `${namespace}.${part}`;\n if (permissions.has(`${namespace}.*`)) {\n return true;\n }\n }\n }\n\n return false;\n}\n\n/**\n * Checks if a user has a specific permission.\n *\n * SECURITY GUARANTEES:\n * - Deny by default: undefined/null/empty always returns false\n * - Pure function: no side effects, no mutations\n * - Deterministic: same inputs always produce same output\n *\n * @param user - The user context to check\n * @param permission - The permission required\n * @param registry - The role registry for resolving roles\n * @returns True if user has the permission, false otherwise\n *\n * @example\n * const canEdit = canUser(currentUser, 'user.update', roleRegistry);\n */\nexport function canUser(\n user: UserContext | null | undefined,\n permission: string,\n registry: RoleRegistry,\n): boolean {\n // DENY BY DEFAULT: No user context means no access\n if (user === null || user === undefined) {\n return false;\n }\n\n // DENY BY DEFAULT: Empty permission request is invalid\n if (permission === \"\" || permission.trim() === \"\") {\n return false;\n }\n\n const userPermissions = collectUserPermissions(user, registry);\n\n // Check for exact permission match\n if (userPermissions.has(permission)) {\n return true;\n }\n\n // Check for wildcard access\n if (hasWildcardAccess(userPermissions, permission)) {\n return true;\n }\n\n // DENY BY DEFAULT\n return false;\n}\n\n/**\n * Checks if a user has ALL of the specified permissions.\n *\n * @param user - The user context to check\n * @param permissions - Array of permissions required\n * @param registry - The role registry\n * @returns True if user has ALL permissions\n *\n * @example\n * const canManage = canUserAll(user, ['user.read', 'user.update'], registry);\n */\nexport function canUserAll(\n user: UserContext | null | undefined,\n permissions: readonly string[],\n registry: RoleRegistry,\n): boolean {\n // DENY BY DEFAULT: Empty permissions array\n if (permissions.length === 0) {\n return false;\n }\n\n for (const permission of permissions) {\n if (!canUser(user, permission, registry)) {\n return false;\n }\n }\n\n return true;\n}\n\n/**\n * Checks if a user has ANY of the specified permissions.\n *\n * @param user - The user context to check\n * @param permissions - Array of permissions to check\n * @param registry - The role registry\n * @returns True if user has at least one permission\n *\n * @example\n * const canView = canUserAny(user, ['report.view', 'report.admin'], registry);\n */\nexport function canUserAny(\n user: UserContext | null | undefined,\n permissions: readonly string[],\n registry: RoleRegistry,\n): boolean {\n // DENY BY DEFAULT: Empty permissions array\n if (permissions.length === 0) {\n return false;\n }\n\n for (const permission of permissions) {\n if (canUser(user, permission, registry)) {\n return true;\n }\n }\n\n return false;\n}\n\n/**\n * Extended permission check with detailed result.\n * Useful for debugging and logging.\n *\n * @param user - The user context\n * @param permission - Permission to check\n * @param registry - The role registry\n * @returns PermissionCheckResult with allowed status and reason\n */\nexport function checkPermission(\n user: UserContext | null | undefined,\n permission: string,\n registry: RoleRegistry,\n): PermissionCheckResult {\n if (user === null || user === undefined) {\n return Object.freeze({\n allowed: false,\n reason: \"No user context provided\",\n });\n }\n\n if (permission === \"\" || permission.trim() === \"\") {\n return Object.freeze({\n allowed: false,\n reason: \"Empty permission requested\",\n });\n }\n\n const allowed = canUser(user, permission, registry);\n\n return Object.freeze({\n allowed,\n reason: allowed\n ? `Permission \"${permission}\" granted`\n : `Permission \"${permission}\" denied`,\n });\n}\n","/**\n * Secure Role Guard - React Context\n *\n * Framework-agnostic React context for permission management.\n * Works with Next.js, Remix, Gatsby, CRA, Vite, and any React framework.\n */\n\nimport { createContext } from \"react\";\nimport type { UserContext, RoleRegistry } from \"../core/types\";\n\n/**\n * Shape of the permission context value.\n */\nexport type PermissionContextValue = {\n /** Current user context */\n readonly user: UserContext | null;\n /** Role registry for permission lookups */\n readonly registry: RoleRegistry;\n /** Check if user has a specific permission */\n readonly can: (permission: string) => boolean;\n /** Check if user has ALL permissions */\n readonly canAll: (permissions: readonly string[]) => boolean;\n /** Check if user has ANY permission */\n readonly canAny: (permissions: readonly string[]) => boolean;\n};\n\n/**\n * Default context value (deny all).\n * Used when no provider is present.\n */\nconst defaultContextValue: PermissionContextValue = {\n user: null,\n registry: {\n getPermissions: () => Object.freeze([]),\n hasRole: () => false,\n getRoleNames: () => Object.freeze([]),\n },\n can: () => false,\n canAll: () => false,\n canAny: () => false,\n};\n\n/**\n * React context for permissions.\n * Default value denies all permissions (deny by default).\n */\nexport const PermissionContext =\n createContext<PermissionContextValue>(defaultContextValue);\n\n// Set display name for React DevTools\nPermissionContext.displayName = \"PermissionContext\";\n","/**\n * Secure Role Guard - React Provider\n *\n * Main provider component for permission management.\n * Framework-agnostic: works with Next.js, Remix, Gatsby, CRA, Vite.\n */\n\nimport { useMemo, type ReactNode } from \"react\";\nimport type { UserContext, RoleRegistry } from \"../core/types\";\nimport { canUser, canUserAll, canUserAny } from \"../core/permission-engine\";\nimport { PermissionContext, type PermissionContextValue } from \"./context\";\n\n/**\n * Props for the PermissionProvider component.\n */\nexport type PermissionProviderProps = {\n /** Current user context (from your auth system) */\n readonly user: UserContext | null;\n /** Role registry created by defineRoles() */\n readonly registry: RoleRegistry;\n /** Child components */\n readonly children: ReactNode;\n};\n\n/**\n * Provides permission context to all child components.\n *\n * SECURITY: If user is null/undefined, all permission checks will deny by default.\n *\n * @example\n * ```tsx\n * import { defineRoles, PermissionProvider } from 'secure-role-guard';\n *\n * const roleRegistry = defineRoles({\n * admin: ['user.read', 'user.update'],\n * viewer: ['user.read']\n * });\n *\n * function App() {\n * const user = { roles: ['admin'] }; // From your auth system\n *\n * return (\n * <PermissionProvider user={user} registry={roleRegistry}>\n * <YourApp />\n * </PermissionProvider>\n * );\n * }\n * ```\n */\nexport function PermissionProvider({\n user,\n registry,\n children,\n}: PermissionProviderProps) {\n // Memoize context value to prevent unnecessary re-renders\n const contextValue = useMemo<PermissionContextValue>(\n () => ({\n user,\n registry,\n can: (permission: string) => canUser(user, permission, registry),\n canAll: (permissions: readonly string[]) =>\n canUserAll(user, permissions, registry),\n canAny: (permissions: readonly string[]) =>\n canUserAny(user, permissions, registry),\n }),\n [user, registry],\n );\n\n return (\n <PermissionContext.Provider value={contextValue}>\n {children}\n </PermissionContext.Provider>\n );\n}\n","/**\n * Secure Role Guard - React Hooks\n *\n * Hooks for permission checking in React components.\n * Framework-agnostic: works with any React framework.\n */\n\nimport { useContext } from \"react\";\nimport { PermissionContext, type PermissionContextValue } from \"./context\";\n\n/**\n * Hook to access the full permission context.\n *\n * @returns Full PermissionContextValue\n *\n * @example\n * ```tsx\n * function UserProfile() {\n * const { user, can, canAll } = usePermissions();\n *\n * if (can('user.read')) {\n * return <UserDetails />;\n * }\n * return <AccessDenied />;\n * }\n * ```\n */\nexport function usePermissions(): PermissionContextValue {\n return useContext(PermissionContext);\n}\n\n/**\n * Hook to check a single permission.\n *\n * @param permission - The permission to check\n * @returns True if user has the permission\n *\n * @example\n * ```tsx\n * function EditButton() {\n * const canEdit = useCan('user.update');\n *\n * if (!canEdit) return null;\n * return <button>Edit User</button>;\n * }\n * ```\n */\nexport function useCan(permission: string): boolean {\n const { can } = useContext(PermissionContext);\n return can(permission);\n}\n\n/**\n * Hook to check if user has ALL specified permissions.\n *\n * @param permissions - Array of permissions to check\n * @returns True if user has all permissions\n *\n * @example\n * ```tsx\n * function AdminPanel() {\n * const canManage = useCanAll(['user.read', 'user.update', 'user.delete']);\n *\n * if (!canManage) return <AccessDenied />;\n * return <AdminDashboard />;\n * }\n * ```\n */\nexport function useCanAll(permissions: readonly string[]): boolean {\n const { canAll } = useContext(PermissionContext);\n return canAll(permissions);\n}\n\n/**\n * Hook to check if user has ANY of the specified permissions.\n *\n * @param permissions - Array of permissions to check\n * @returns True if user has at least one permission\n *\n * @example\n * ```tsx\n * function ViewReports() {\n * const canView = useCanAny(['report.admin', 'report.viewer']);\n *\n * if (!canView) return null;\n * return <ReportList />;\n * }\n * ```\n */\nexport function useCanAny(permissions: readonly string[]): boolean {\n const { canAny } = useContext(PermissionContext);\n return canAny(permissions);\n}\n\n/**\n * Hook to get current user context.\n *\n * @returns Current UserContext or null\n *\n * @example\n * ```tsx\n * function UserInfo() {\n * const user = useUser();\n * return <span>User ID: {user?.userId ?? 'Anonymous'}</span>;\n * }\n * ```\n */\nexport function useUser() {\n const { user } = useContext(PermissionContext);\n return user;\n}\n","/**\n * Secure Role Guard - React Components\n *\n * Declarative components for conditional rendering based on permissions.\n * Framework-agnostic: works with any React framework.\n */\n\nimport type { ReactNode } from \"react\";\nimport { useCan, useCanAll, useCanAny } from \"./hooks\";\n\n/**\n * Props for the Can component.\n */\nexport type CanProps = {\n /** Single permission to check */\n readonly permission?: string;\n /** Multiple permissions - user must have ALL (default behavior) */\n readonly permissions?: readonly string[];\n /** If true, user needs only ANY of the permissions */\n readonly anyOf?: boolean;\n /** Content to render if permission granted */\n readonly children: ReactNode;\n /** Optional fallback content if permission denied */\n readonly fallback?: ReactNode;\n};\n\n/**\n * Conditionally renders children based on user permissions.\n *\n * SECURITY: If no permission matches, nothing is rendered (deny by default).\n *\n * @example Single permission\n * ```tsx\n * <Can permission=\"user.update\">\n * <EditButton />\n * </Can>\n * ```\n *\n * @example Multiple permissions (ALL required)\n * ```tsx\n * <Can permissions={['user.read', 'user.update']}>\n * <UserEditor />\n * </Can>\n * ```\n *\n * @example Multiple permissions (ANY required)\n * ```tsx\n * <Can permissions={['admin', 'moderator']} anyOf>\n * <ModPanel />\n * </Can>\n * ```\n *\n * @example With fallback\n * ```tsx\n * <Can permission=\"premium.access\" fallback={<UpgradePrompt />}>\n * <PremiumContent />\n * </Can>\n * ```\n */\nexport function Can({\n permission,\n permissions,\n anyOf = false,\n children,\n fallback = null,\n}: CanProps) {\n // Single permission check\n if (permission !== undefined) {\n const allowed = useCan(permission);\n return <>{allowed ? children : fallback}</>;\n }\n\n // Multiple permissions check\n if (permissions !== undefined && permissions.length > 0) {\n const allowed = anyOf ? useCanAny(permissions) : useCanAll(permissions);\n return <>{allowed ? children : fallback}</>;\n }\n\n // No permissions specified - deny by default\n return <>{fallback}</>;\n}\n\n/**\n * Props for the Cannot component.\n */\nexport type CannotProps = {\n /** Single permission to check */\n readonly permission?: string;\n /** Multiple permissions to check */\n readonly permissions?: readonly string[];\n /** If true, blocked when user has ANY permission */\n readonly anyOf?: boolean;\n /** Content to render if permission is NOT granted */\n readonly children: ReactNode;\n};\n\n/**\n * Inverse of Can - renders children when user does NOT have the permission.\n * Useful for showing upgrade prompts, locked features, etc.\n *\n * @example\n * ```tsx\n * <Cannot permission=\"premium.access\">\n * <UpgradePrompt />\n * </Cannot>\n * ```\n */\nexport function Cannot({\n permission,\n permissions,\n anyOf = false,\n children,\n}: CannotProps) {\n // Single permission check\n if (permission !== undefined) {\n const allowed = useCan(permission);\n return <>{allowed ? null : children}</>;\n }\n\n // Multiple permissions check\n if (permissions !== undefined && permissions.length > 0) {\n const allowed = anyOf ? useCanAny(permissions) : useCanAll(permissions);\n return <>{allowed ? null : children}</>;\n }\n\n // No permissions specified - show children (inverse of deny by default)\n return <>{children}</>;\n}\n"]}
@@ -66,7 +66,7 @@ type PermissionProviderProps = {
66
66
  * }
67
67
  * ```
68
68
  */
69
- declare function PermissionProvider({ user, registry, children, }: PermissionProviderProps): ReactNode;
69
+ declare function PermissionProvider({ user, registry, children, }: PermissionProviderProps): JSX.Element;
70
70
 
71
71
  /**
72
72
  * Hook to access the full permission context.
@@ -207,7 +207,7 @@ type CanProps = {
207
207
  * </Can>
208
208
  * ```
209
209
  */
210
- declare function Can({ permission, permissions, anyOf, children, fallback, }: CanProps): ReactNode;
210
+ declare function Can({ permission, permissions, anyOf, children, fallback, }: CanProps): JSX.Element;
211
211
  /**
212
212
  * Props for the Cannot component.
213
213
  */
@@ -232,6 +232,6 @@ type CannotProps = {
232
232
  * </Cannot>
233
233
  * ```
234
234
  */
235
- declare function Cannot({ permission, permissions, anyOf, children, }: CannotProps): ReactNode;
235
+ declare function Cannot({ permission, permissions, anyOf, children, }: CannotProps): JSX.Element;
236
236
 
237
237
  export { Can, type CanProps, Cannot, type CannotProps, PermissionContext, type PermissionContextValue, PermissionProvider, type PermissionProviderProps, useCan, useCanAll, useCanAny, usePermissions, useUser };