{"version":3,"file":"index.cjs","names":["iamBuildPermissionKey"],"sources":["../../../src/client/react/index.ts"],"sourcesContent":["/**\n * React integration for duck-iam.\n *\n * Two patterns:\n *   1. Server-driven (recommended): generate IamClient.PermissionMap on server, pass to client.\n *   2. IamClient-evaluated: load Engine on client with HttpAdapter or MemoryAdapter.\n *\n * Usage (server-driven):\n *\n *   // Server (Next.js layout, RSC, or API):\n *   const perms = await engine.permissions(userId, [\n *     { action: \"create\", resource: \"post\" },\n *     { action: \"delete\", resource: \"post\" },\n *     { action: \"manage\", resource: \"team\" },\n *   ]);\n *\n *   // IamClient:\n *   <AccessProvider permissions={perms}>\n *     <App />\n *   </AccessProvider>\n *\n *   // In any component:\n *   const { can } = useAccess();\n *   if (can(\"delete\", \"post\")) { ... }\n *   if (can(\"manage\", \"user\", undefined, \"admin\")) { ... }\n *\n *   // Or declaratively:\n *   <Can action=\"manage\" resource=\"team\">\n *     <AdminPanel />\n *   </Can>\n */\n\nimport type { ReactNode } from 'react'\nimport type { IamClient } from '../../core/types'\nimport { iamBuildPermissionKey } from '../../shared/keys'\n\n// React is a peer dep; consumers inject their own React via createIamAccessControl(React).\n\n/** Minimal React context type. */\ninterface ReactContext<_T> {\n  Provider: unknown\n}\n\n/** Minimal React API surface for dependency injection. */\ninterface ReactLike {\n  createContext<T>(defaultValue: T): ReactContext<T>\n  useContext<T>(context: ReactContext<T>): T\n  useMemo<T>(factory: () => T, deps: readonly unknown[]): T\n  // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type -- matches React's own useCallback<T extends Function>\n  useCallback<T extends Function>(callback: T, deps: readonly unknown[]): T\n  createElement(type: unknown, props: Record<string, unknown> | null, ...children: ReactNode[]): ReactNode\n  useState<T>(initialState: T | (() => T)): [T, (value: T | ((prev: T) => T)) => void]\n  useEffect(effect: () => undefined | (() => void), deps?: readonly unknown[]): void\n}\n\n/**\n * React client integration types. Type-only namespace - zero bundle cost.\n *\n * Named `IamReactClient` (rather than `React`) to avoid clashing with the React\n * package namespace when consumers import this module alongside React.\n */\nexport namespace IamReactClient {\n  /**\n   * Describes the value exposed by the {@link createIamAccessControl} React context.\n   *\n   * @template TAction - Constrains valid action strings.\n   * @template TResource - Constrains valid resource strings.\n   * @template TScope - Constrains valid scope strings.\n   */\n  export interface IContextValue<\n    TAction extends string = string,\n    TResource extends string = string,\n    TScope extends string = string,\n  > {\n    /** The resolved permission map. */\n    permissions: IamClient.PermissionMap<TAction, TResource, TScope>\n    /** Returns `true` if the action/resource combination is allowed. */\n    can: (action: TAction, resource: TResource, resourceId?: string, scope?: TScope) => boolean\n    /** Returns `true` if the action/resource combination is denied. */\n    cannot: (action: TAction, resource: TResource, resourceId?: string, scope?: TScope) => boolean\n  }\n}\n\n/**\n * Builds the React access control surface (Provider, hook, components).\n *\n * Call once at app init and export the result so the entire app shares a\n * single context.\n *\n * @template TAction - Constrains valid action strings.\n * @template TResource - Constrains valid resource strings.\n * @template TScope - Constrains valid scope strings.\n * @param React - Provides the host React module so we never bundle our own copy.\n * @returns `{ AccessContext, AccessProvider, useAccess, usePermissions, Can, Cannot }`.\n * @example\n * ```ts\n * import React from 'react'\n * import { createIamAccessControl } from 'duck-iam/client/react'\n *\n * export const { AccessProvider, useAccess, Can, Cannot } = createIamAccessControl(React)\n * ```\n */\nexport function createIamAccessControl<\n  TAction extends string = string,\n  TResource extends string = string,\n  TScope extends string = string,\n>(React: ReactLike) {\n  const { createContext, useContext, useMemo, useCallback } = React\n\n  const AccessContext = createContext<IamReactClient.IContextValue<TAction, TResource, TScope>>({\n    permissions: {} as IamClient.PermissionMap<TAction, TResource, TScope>,\n    can: () => false,\n    cannot: () => true,\n  })\n\n  /** Context provider component that supplies permission data to the tree. */\n  function AccessProvider({\n    permissions,\n    children,\n  }: {\n    permissions: IamClient.PermissionMap<TAction, TResource, TScope>\n    children: ReactNode\n  }): ReactNode {\n    const value = useMemo(() => {\n      const can = (action: TAction, resource: TResource, resourceId?: string, scope?: TScope): boolean => {\n        const key = iamBuildPermissionKey(action, resource, resourceId, scope)\n        return (permissions as Record<string, boolean>)[key] ?? false\n      }\n\n      return {\n        permissions,\n        can,\n        cannot: (a: TAction, r: TResource, id?: string, s?: TScope) => !can(a, r, id, s),\n      }\n    }, [permissions])\n\n    return React.createElement(AccessContext.Provider, { value }, children)\n  }\n\n  /** Hook to access the permission context. */\n  function useAccess(): IamReactClient.IContextValue<TAction, TResource, TScope> {\n    return useContext(AccessContext)\n  }\n\n  /** Declarative component that renders children only when the permission is granted. */\n  function Can({\n    action,\n    resource,\n    resourceId,\n    scope,\n    children,\n    fallback = null,\n  }: {\n    action: TAction\n    resource: TResource\n    resourceId?: string\n    scope?: TScope\n    children: ReactNode\n    fallback?: ReactNode\n  }): ReactNode {\n    const { can } = useAccess()\n    return can(action, resource, resourceId, scope) ? children : fallback\n  }\n\n  /** Declarative component that renders children only when the permission is denied. */\n  function Cannot({\n    action,\n    resource,\n    resourceId,\n    scope,\n    children,\n  }: {\n    action: TAction\n    resource: TResource\n    resourceId?: string\n    scope?: TScope\n    children: ReactNode\n  }): ReactNode {\n    const { cannot } = useAccess()\n    return cannot(action, resource, resourceId, scope) ? children : null\n  }\n\n  /** Hook to asynchronously fetch permissions from a server endpoint. */\n  function usePermissions(\n    fetchFn: () => Promise<IamClient.PermissionMap<TAction, TResource, TScope>>,\n    deps: readonly unknown[] = [],\n  ) {\n    const [permissions, setPermissions] = React.useState({} as IamClient.PermissionMap<TAction, TResource, TScope>)\n    const [loading, setLoading] = React.useState(true)\n    const [error, setError] = React.useState<Error | null>(null)\n\n    React.useEffect(() => {\n      let cancelled = false\n      setLoading(true)\n      fetchFn()\n        .then((perms: IamClient.PermissionMap<TAction, TResource, TScope>) => {\n          if (!cancelled) {\n            setPermissions(perms)\n            setLoading(false)\n          }\n        })\n        .catch((err: Error) => {\n          if (!cancelled) {\n            setError(err)\n            setLoading(false)\n          }\n        })\n      return () => {\n        cancelled = true\n      }\n    }, deps)\n\n    const can = useCallback(\n      (action: TAction, resource: TResource, resourceId?: string, scope?: TScope) => {\n        const key = iamBuildPermissionKey(action, resource, resourceId, scope)\n        return (permissions as Record<string, boolean>)[key] ?? false\n      },\n      [permissions],\n    )\n\n    return { permissions, can, loading, error }\n  }\n\n  return {\n    AccessContext,\n    AccessProvider,\n    useAccess,\n    usePermissions,\n    Can,\n    Cannot,\n  }\n}\n\n/**\n * Builds a standalone permission checker that does not require React.\n *\n * Useful for one-off checks, hooks outside the provider, or non-React paths.\n *\n * @template TAction - Constrains valid action strings.\n * @template TResource - Constrains valid resource strings.\n * @template TScope - Constrains valid scope strings.\n * @param permissions - Provides the permission map (typically from `engine.permissions(...)`).\n * @returns `{ can, cannot, permissions }`.\n */\nexport function createIamPermissionChecker<\n  TAction extends string = string,\n  TResource extends string = string,\n  TScope extends string = string,\n>(permissions: IamClient.PermissionMap<TAction, TResource, TScope>) {\n  const can = (action: TAction, resource: TResource, resourceId?: string, scope?: TScope): boolean => {\n    const key = iamBuildPermissionKey(action, resource, resourceId, scope)\n    return (permissions as Record<string, boolean>)[key] ?? false\n  }\n\n  return {\n    can,\n    cannot: (action: TAction, resource: TResource, resourceId?: string, scope?: TScope): boolean => {\n      return !can(action, resource, resourceId, scope)\n    },\n    permissions,\n  }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAsGA,SAAgB,uBAId,OAAkB;CAClB,MAAM,EAAE,eAAe,YAAY,SAAS,gBAAgB;CAE5D,MAAM,gBAAgB,cAAwE;EAC5F,aAAa,CAAC;EACd,WAAW;EACX,cAAc;CAChB,CAAC;;CAGD,SAAS,eAAe,EACtB,aACA,YAIY;EACZ,MAAM,QAAQ,cAAc;GAC1B,MAAM,OAAO,QAAiB,UAAqB,YAAqB,UAA4B;IAElG,OAAQ,YADIA,mCAAsB,QAAQ,UAAU,YAAY,KACd,MAAM;GAC1D;GAEA,OAAO;IACL;IACA;IACA,SAAS,GAAY,GAAc,IAAa,MAAe,CAAC,IAAI,GAAG,GAAG,IAAI,CAAC;GACjF;EACF,GAAG,CAAC,WAAW,CAAC;EAEhB,OAAO,MAAM,cAAc,cAAc,UAAU,EAAE,MAAM,GAAG,QAAQ;CACxE;;CAGA,SAAS,YAAsE;EAC7E,OAAO,WAAW,aAAa;CACjC;;CAGA,SAAS,IAAI,EACX,QACA,UACA,YACA,OACA,UACA,WAAW,QAQC;EACZ,MAAM,EAAE,QAAQ,UAAU;EAC1B,OAAO,IAAI,QAAQ,UAAU,YAAY,KAAK,IAAI,WAAW;CAC/D;;CAGA,SAAS,OAAO,EACd,QACA,UACA,YACA,OACA,YAOY;EACZ,MAAM,EAAE,WAAW,UAAU;EAC7B,OAAO,OAAO,QAAQ,UAAU,YAAY,KAAK,IAAI,WAAW;CAClE;;CAGA,SAAS,eACP,SACA,OAA2B,CAAC,GAC5B;EACA,MAAM,CAAC,aAAa,kBAAkB,MAAM,SAAS,CAAC,CAAwD;EAC9G,MAAM,CAAC,SAAS,cAAc,MAAM,SAAS,IAAI;EACjD,MAAM,CAAC,OAAO,YAAY,MAAM,SAAuB,IAAI;EAE3D,MAAM,gBAAgB;GACpB,IAAI,YAAY;GAChB,WAAW,IAAI;GACf,QAAQ,CAAC,CACN,MAAM,UAA+D;IACpE,IAAI,CAAC,WAAW;KACd,eAAe,KAAK;KACpB,WAAW,KAAK;IAClB;GACF,CAAC,CAAC,CACD,OAAO,QAAe;IACrB,IAAI,CAAC,WAAW;KACd,SAAS,GAAG;KACZ,WAAW,KAAK;IAClB;GACF,CAAC;GACH,aAAa;IACX,YAAY;GACd;EACF,GAAG,IAAI;EAUP,OAAO;GAAE;GAAa,KARV,aACT,QAAiB,UAAqB,YAAqB,UAAmB;IAE7E,OAAQ,YADIA,mCAAsB,QAAQ,UAAU,YAAY,KACd,MAAM;GAC1D,GACA,CAAC,WAAW,CAGU;GAAG;GAAS;EAAM;CAC5C;CAEA,OAAO;EACL;EACA;EACA;EACA;EACA;EACA;CACF;AACF;;;;;;;;;;;;AAaA,SAAgB,2BAId,aAAkE;CAClE,MAAM,OAAO,QAAiB,UAAqB,YAAqB,UAA4B;EAElG,OAAQ,YADIA,mCAAsB,QAAQ,UAAU,YAAY,KACd,MAAM;CAC1D;CAEA,OAAO;EACL;EACA,SAAS,QAAiB,UAAqB,YAAqB,UAA4B;GAC9F,OAAO,CAAC,IAAI,QAAQ,UAAU,YAAY,KAAK;EACjD;EACA;CACF;AACF"}