{"version":3,"file":"index.cjs","names":["iamBuildPermissionKey"],"sources":["../../../src/client/vue/index.ts"],"sourcesContent":["/**\n * Vue 3 integration for duck-iam.\n *\n * Usage:\n *\n *   // Plugin setup (main.ts):\n *   import { createAccessPlugin } from \"duck-iam/client/vue\";\n *   app.use(createAccessPlugin(permissionMap));\n *\n *   // In components:\n *   import { useAccess } from \"duck-iam/client/vue\";\n *\n *   const { can, cannot } = useAccess();\n *   const canDelete = can(\"delete\", \"post\");\n *   const canManage = can(\"manage\", \"user\", undefined, \"admin\");\n *\n *   // Template directive:\n *   <button v-if=\"can('delete', 'post')\">Delete</button>\n */\n\nimport type { IamClient } from '../../core/types'\nimport { iamBuildPermissionKey } from '../../shared/keys'\n\n/** Vue injection key for the access control state. */\nexport const IAM_ACCESS_INJECTION_KEY = Symbol('duck-iam')\n\n// Vue is a peer dep; consumers inject their own Vue via createIamVueAccess(vue).\n\n/** Minimal Vue ref type. */\ninterface VueRef<T> {\n  value: T\n}\n\n/** Minimal Vue virtual node type. */\ninterface VNode {\n  [key: string]: unknown\n}\n\n/** Minimal Vue API surface for dependency injection. */\ninterface VueLike {\n  ref<T>(value: T): VueRef<T>\n  computed<T>(getter: () => T): Readonly<VueRef<T>>\n  inject<T>(key: symbol | string): T | undefined\n  provide(key: symbol | string, value: unknown): void\n  defineComponent(options: Record<string, unknown>): unknown\n  h(type: unknown, props?: Record<string, unknown> | null, children?: unknown): VNode\n}\n\n/** Minimal Vue application instance type. */\ninterface VueApp {\n  provide(key: symbol | string, value: unknown): void\n  config: { globalProperties: Record<string, unknown> }\n}\n\n/**\n * Builds the Vue 3 access control surface (composable, plugin, components).\n *\n * Pass Vue's reactive utilities to avoid a hard dependency on the framework.\n *\n * @template TAction - Constrains valid action strings.\n * @template TResource - Constrains valid resource strings.\n * @template TScope - Constrains valid scope strings.\n * @param vue - Provides the host Vue module so we never bundle our own copy.\n * @returns `{ createAccessState, provideAccess, useAccess, createAccessPlugin, Can, Cannot, IAM_ACCESS_INJECTION_KEY }`.\n * @example\n * ```ts\n * import { ref, computed, inject, provide, defineComponent, h } from 'vue'\n * import { createIamVueAccess } from 'duck-iam/client/vue'\n *\n * export const { useAccess, createAccessPlugin } = createIamVueAccess({\n *   ref, computed, inject, provide, defineComponent, h,\n * })\n * ```\n */\nexport function createIamVueAccess<\n  TAction extends string = string,\n  TResource extends string = string,\n  TScope extends string = string,\n>(vue: VueLike) {\n  const { ref, inject, provide, defineComponent } = vue\n\n  /** Create reactive access control state with can/cannot helpers. */\n  function createAccessState(initialPermissions: IamClient.PermissionMap<TAction, TResource, TScope>) {\n    const permissions = ref(initialPermissions)\n\n    const can = (action: TAction, resource: TResource, resourceId?: string, scope?: TScope): boolean => {\n      const key = iamBuildPermissionKey(action, resource, resourceId, scope)\n      return (permissions.value as Record<string, boolean>)[key] ?? false\n    }\n\n    const cannot = (action: TAction, resource: TResource, resourceId?: string, scope?: TScope): boolean => {\n      return !can(action, resource, resourceId, scope)\n    }\n\n    const update = (newPerms: IamClient.PermissionMap<TAction, TResource, TScope>) => {\n      permissions.value = newPerms\n    }\n\n    return { permissions, can, cannot, update }\n  }\n\n  /** Provide access control state to child components via Vue's provide/inject. */\n  function provideAccess(permissions: IamClient.PermissionMap<TAction, TResource, TScope>) {\n    const state = createAccessState(permissions)\n    provide(IAM_ACCESS_INJECTION_KEY, state)\n    return state\n  }\n\n  /** Composable to access the permission state from a parent provider. */\n  function useAccess() {\n    const state = inject(IAM_ACCESS_INJECTION_KEY)\n    if (!state) {\n      throw new Error(\n        '[@gentleduck/iam:vue] useAccess() called without provideAccess(). ' +\n          'Use provideAccess() in a parent component or install the plugin.',\n      )\n    }\n    return state as ReturnType<typeof createAccessState>\n  }\n\n  /** Create a Vue plugin that installs access control globally. */\n  function createAccessPlugin(permissions: IamClient.PermissionMap<TAction, TResource, TScope>) {\n    return {\n      install(app: VueApp) {\n        const state = createAccessState(permissions)\n        app.provide(IAM_ACCESS_INJECTION_KEY, state)\n\n        app.config.globalProperties.$can = state.can\n        app.config.globalProperties.$cannot = state.cannot\n      },\n    }\n  }\n\n  /**\n   * Declarative component that renders slot content only when the permission is granted.\n   *\n   *   <Can action=\"delete\" resource=\"post\">\n   *     <button>Delete</button>\n   *   </Can>\n   *\n   *   <Can action=\"read\" resource=\"analytics\">\n   *     <template #default>Analytics</template>\n   *     <template #fallback>Upgrade to Pro</template>\n   *   </Can>\n   */\n  const Can = defineComponent({\n    name: 'Can',\n    props: {\n      action: { type: String, required: true },\n      resource: { type: String, required: true },\n      resourceId: { type: String, default: undefined },\n      scope: { type: String, default: undefined },\n    },\n    setup(\n      props: { action: string; resource: string; resourceId?: string; scope?: string },\n      { slots }: { slots: Record<string, (() => VNode[]) | undefined> },\n    ) {\n      // biome-ignore lint/correctness/useHookAtTopLevel: this is a declarative component\n      const { can } = useAccess()\n      return () => {\n        if (can(props.action as TAction, props.resource as TResource, props.resourceId, props.scope as TScope)) {\n          return slots.default?.()\n        }\n        return slots.fallback?.()\n      }\n    },\n  })\n\n  /**\n   * Declarative component that renders slot content only when the permission is denied.\n   *\n   *   <Cannot action=\"read\" resource=\"analytics\">\n   *     <div>Upgrade to access this feature</div>\n   *   </Cannot>\n   */\n  const Cannot = defineComponent({\n    name: 'Cannot',\n    props: {\n      action: { type: String, required: true },\n      resource: { type: String, required: true },\n      resourceId: { type: String, default: undefined },\n      scope: { type: String, default: undefined },\n    },\n    setup(\n      props: { action: string; resource: string; resourceId?: string; scope?: string },\n      { slots }: { slots: Record<string, (() => VNode[]) | undefined> },\n    ) {\n      // biome-ignore lint/correctness/useHookAtTopLevel: this is a declarative component\n      const { cannot } = useAccess()\n      return () => {\n        if (cannot(props.action as TAction, props.resource as TResource, props.resourceId, props.scope as TScope)) {\n          return slots.default?.()\n        }\n        return null\n      }\n    },\n  })\n\n  return {\n    createAccessState,\n    provideAccess,\n    useAccess,\n    createAccessPlugin,\n    Can,\n    Cannot,\n    IAM_ACCESS_INJECTION_KEY,\n  }\n}\n"],"mappings":";;;;;AAwBA,MAAa,2BAA2B,OAAO,UAAU;;;;;;;;;;;;;;;;;;;;;AAkDzD,SAAgB,mBAId,KAAc;CACd,MAAM,EAAE,KAAK,QAAQ,SAAS,oBAAoB;;CAGlD,SAAS,kBAAkB,oBAAyE;EAClG,MAAM,cAAc,IAAI,kBAAkB;EAE1C,MAAM,OAAO,QAAiB,UAAqB,YAAqB,UAA4B;GAClG,MAAM,MAAMA,mCAAsB,QAAQ,UAAU,YAAY,KAAK;GACrE,OAAQ,YAAY,MAAkC,QAAQ;EAChE;EAEA,MAAM,UAAU,QAAiB,UAAqB,YAAqB,UAA4B;GACrG,OAAO,CAAC,IAAI,QAAQ,UAAU,YAAY,KAAK;EACjD;EAEA,MAAM,UAAU,aAAkE;GAChF,YAAY,QAAQ;EACtB;EAEA,OAAO;GAAE;GAAa;GAAK;GAAQ;EAAO;CAC5C;;CAGA,SAAS,cAAc,aAAkE;EACvF,MAAM,QAAQ,kBAAkB,WAAW;EAC3C,QAAQ,0BAA0B,KAAK;EACvC,OAAO;CACT;;CAGA,SAAS,YAAY;EACnB,MAAM,QAAQ,OAAO,wBAAwB;EAC7C,IAAI,CAAC,OACH,MAAM,IAAI,MACR,oIAEF;EAEF,OAAO;CACT;;CAGA,SAAS,mBAAmB,aAAkE;EAC5F,OAAO,EACL,QAAQ,KAAa;GACnB,MAAM,QAAQ,kBAAkB,WAAW;GAC3C,IAAI,QAAQ,0BAA0B,KAAK;GAE3C,IAAI,OAAO,iBAAiB,OAAO,MAAM;GACzC,IAAI,OAAO,iBAAiB,UAAU,MAAM;EAC9C,EACF;CACF;CAmEA,OAAO;EACL;EACA;EACA;EACA;EACA,KA1DU,gBAAgB;GAC1B,MAAM;GACN,OAAO;IACL,QAAQ;KAAE,MAAM;KAAQ,UAAU;IAAK;IACvC,UAAU;KAAE,MAAM;KAAQ,UAAU;IAAK;IACzC,YAAY;KAAE,MAAM;KAAQ,SAAS;IAAU;IAC/C,OAAO;KAAE,MAAM;KAAQ,SAAS;IAAU;GAC5C;GACA,MACE,OACA,EAAE,SACF;IAEA,MAAM,EAAE,QAAQ,UAAU;IAC1B,aAAa;KACX,IAAI,IAAI,MAAM,QAAmB,MAAM,UAAuB,MAAM,YAAY,MAAM,KAAe,GACnG,OAAO,MAAM,UAAU;KAEzB,OAAO,MAAM,WAAW;IAC1B;GACF;EACF,CAqCI;EACF,QA7Ba,gBAAgB;GAC7B,MAAM;GACN,OAAO;IACL,QAAQ;KAAE,MAAM;KAAQ,UAAU;IAAK;IACvC,UAAU;KAAE,MAAM;KAAQ,UAAU;IAAK;IACzC,YAAY;KAAE,MAAM;KAAQ,SAAS;IAAU;IAC/C,OAAO;KAAE,MAAM;KAAQ,SAAS;IAAU;GAC5C;GACA,MACE,OACA,EAAE,SACF;IAEA,MAAM,EAAE,WAAW,UAAU;IAC7B,aAAa;KACX,IAAI,OAAO,MAAM,QAAmB,MAAM,UAAuB,MAAM,YAAY,MAAM,KAAe,GACtG,OAAO,MAAM,UAAU;KAEzB,OAAO;IACT;GACF;EACF,CAQO;EACL;CACF;AACF"}