snipe-auth-rbac 0.5.0 → 0.6.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.
- package/dist/admin/index.cjs +113 -6
- package/dist/admin/index.cjs.map +1 -1
- package/dist/admin/index.d.cts +99 -20
- package/dist/admin/index.d.ts +99 -20
- package/dist/admin/index.js +112 -6
- package/dist/admin/index.js.map +1 -1
- package/package.json +1 -1
- package/sql/0001_initial.sql +258 -59
package/dist/admin/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/admin/transport.ts","../../src/admin/hooks.tsx","../../src/admin/PermissionsMatrix.tsx","../../src/admin/RolesList.tsx","../../src/admin/InviteMemberForm.tsx"],"sourcesContent":["/**\n * Default Supabase implementation of the admin transport. Hits the\n * package's tables in the `rbac` schema directly via `.schema('rbac').\n * from(...)` and the auth admin endpoint for invites.\n *\n * Adopters must add `rbac` to their PostgREST exposed-schemas list\n * (Supabase Studio → Settings → API → Exposed schemas) for these\n * calls to reach the tables.\n *\n * Projects that route admin writes through their own backend\n * (e.g. for audit logging or extra validation) skip this and\n * implement `AdminTransport` themselves.\n */\n\nimport type { Action, ResourceDescriptor } from \"../types.js\";\n\nimport type {\n AdminCompany,\n AdminMember,\n AdminResourceDependency,\n AdminRole,\n AdminRolePermission,\n AdminTransport,\n} from \"./types.js\";\n\ninterface RbacSchemaClient {\n from(table: string): {\n select: (cols: string) => {\n eq: (col: string, value: unknown) => any;\n is: (col: string, value: unknown) => any;\n order: (col: string, opts?: { ascending: boolean }) => any;\n };\n insert: (row: Record<string, unknown>) => {\n select: (cols: string) => { single: () => any };\n };\n update: (patch: Record<string, unknown>) => {\n eq: (col: string, value: unknown) => {\n select: (cols: string) => { single: () => any };\n };\n };\n upsert: (\n row: Record<string, unknown> | Array<Record<string, unknown>>,\n opts?: { onConflict: string },\n ) => Promise<{ error: { message: string } | null }>;\n delete: () => { eq: (col: string, value: unknown) => any };\n };\n rpc(\n fn: string,\n args: Record<string, unknown>,\n ): Promise<{ data: unknown; error: { message: string } | null }>;\n}\n\ninterface SupabaseAdmin {\n schema(name: string): RbacSchemaClient;\n auth: {\n admin: {\n inviteUserByEmail: (\n email: string,\n opts?: { data?: Record<string, unknown>; redirectTo?: string },\n ) => Promise<{ data: unknown; error: { message: string } | null }>;\n };\n };\n}\n\nexport interface SupabaseAdminClientOptions {\n supabase: SupabaseAdmin;\n /** Where the invitee should land after setting their password. */\n inviteRedirectUrl?: string;\n}\n\nconst ACTION_COLUMN: Record<Action, string> = {\n read: \"can_read\",\n write: \"can_write\",\n update: \"can_update\",\n delete: \"can_delete\",\n};\n\nconst GRANTED_VIA_COLUMN: Record<Action, string> = {\n read: \"read_granted_via\",\n write: \"write_granted_via\",\n update: \"update_granted_via\",\n delete: \"delete_granted_via\",\n};\n\n/**\n * Pull `dependsOn` edges out of a registry array and flatten them\n * into one row per (parent, child, action). Shared helper used by\n * `syncResources` and by adopters who want to sync dependencies\n * manually.\n */\nexport function extractResourceDependencies(\n resources: ReadonlyArray<ResourceDescriptor>,\n): AdminResourceDependency[] {\n const out: AdminResourceDependency[] = [];\n for (const r of resources) {\n for (const edge of r.dependsOn ?? []) {\n const child = typeof edge === \"string\" ? edge : edge.resource;\n const actions =\n typeof edge === \"string\" ? ([\"read\"] as const) : (edge.actions ?? [\"read\"]);\n for (const action of actions) {\n out.push({\n parent_resource: r.resource,\n child_resource: child,\n action,\n });\n }\n }\n }\n return out;\n}\n\nexport function createSupabaseAdminClient(\n opts: SupabaseAdminClientOptions,\n): AdminTransport {\n const sb = opts.supabase;\n const rbac = sb.schema(\"rbac\");\n\n const syncResourceDependencies = async (\n edges: ReadonlyArray<AdminResourceDependency>,\n ): Promise<number> => {\n // Atomic replace-all via the package's RPC. Sidesteps PostgREST's\n // refusal of wildcard deletes and gives a single round-trip.\n const payload = edges.map((e) => ({\n parent_resource: e.parent_resource,\n child_resource: e.child_resource,\n action: e.action,\n }));\n const { error } = await rbac.rpc(\"replace_resource_dependencies\", {\n p_edges: payload,\n });\n if (error) {\n throw new Error(`syncResourceDependencies: ${error.message}`);\n }\n return edges.length;\n };\n\n return {\n async syncResources(resources) {\n if (resources.length === 0) {\n return 0;\n }\n const payload = resources.map((r: ResourceDescriptor) => ({\n resource: r.resource,\n scope: r.scope,\n label: r.label,\n description: r.description ?? null,\n group_label: r.group ?? null,\n }));\n const { error } = await rbac\n .from(\"resources\")\n .upsert(payload, { onConflict: \"resource\" });\n if (error) {\n throw new Error(`syncResources: ${error.message}`);\n }\n // 0.4.0+: also sync dependency edges declared via `dependsOn`.\n // Pre-0.4.0 SQL won't have `rbac.resource_dependencies` yet —\n // syncResourceDependencies tolerates that case internally and\n // surfaces other errors normally.\n const edges = extractResourceDependencies(resources);\n try {\n await syncResourceDependencies(edges);\n } catch (err) {\n if (\n err instanceof Error &&\n /resource_dependencies/i.test(err.message) &&\n /(does not exist|relation .* does not exist)/i.test(err.message)\n ) {\n // Pre-0.4.0 SQL — silently skip.\n } else {\n throw err;\n }\n }\n return resources.length;\n },\n\n async listRoles({ scope, companyId, templatesOnly }) {\n let q = rbac.from(\"roles\").select(\"*\").eq(\"scope\", scope);\n if (templatesOnly) {\n q = q.is(\"company_id\", null);\n } else if (companyId !== undefined) {\n q = companyId === null ? q.is(\"company_id\", null) : q.eq(\"company_id\", companyId);\n }\n const { data, error } = await q.order(\"name\", { ascending: true });\n if (error) {\n throw new Error(`listRoles: ${error.message}`);\n }\n return (data ?? []) as AdminRole[];\n },\n\n async listRolePermissions(roleId) {\n const { data, error } = await rbac\n .from(\"role_permissions\")\n .select(\"*\")\n .eq(\"role_id\", roleId);\n if (error) {\n throw new Error(`listRolePermissions: ${error.message}`);\n }\n return (data ?? []) as AdminRolePermission[];\n },\n\n async createRole(input) {\n const row = {\n scope: input.scope,\n company_id: input.companyId ?? null,\n name: input.name,\n description: input.description ?? null,\n frontend_config: input.frontend_config ?? {},\n };\n const { data, error } = await rbac\n .from(\"roles\")\n .insert(row)\n .select(\"*\")\n .single();\n if (error) {\n throw new Error(`createRole: ${error.message}`);\n }\n return data as AdminRole;\n },\n\n async updateRole(id, patch) {\n const { data, error } = await rbac\n .from(\"roles\")\n .update(patch)\n .eq(\"id\", id)\n .select(\"*\")\n .single();\n if (error) {\n throw new Error(`updateRole: ${error.message}`);\n }\n return data as AdminRole;\n },\n\n async deleteRole(id) {\n const { error } = await rbac.from(\"roles\").delete().eq(\"id\", id);\n if (error) {\n throw new Error(`deleteRole: ${error.message}`);\n }\n },\n\n async setRolePermissionCell({ role_id, resource, action, value, grantedVia }) {\n const actionCol = ACTION_COLUMN[action];\n const originCol = GRANTED_VIA_COLUMN[action];\n // grantedVia semantics:\n // undefined → don't touch the origin column (legacy callers)\n // null → explicit \"this is a direct grant\" — set origin\n // to NULL even if a previous parent owned it\n // string → record the parent name\n const row: Record<string, unknown> = {\n role_id,\n resource,\n [actionCol]: value,\n };\n if (grantedVia !== undefined) {\n // When clearing the action (value=false) we always clear the\n // origin too. Otherwise we record whatever the caller passed.\n row[originCol] = value ? grantedVia : null;\n }\n const { error } = await rbac\n .from(\"role_permissions\")\n .upsert(row, { onConflict: \"role_id,resource\" });\n if (error) {\n // Tolerate pre-0.4.0 SQL that doesn't have the origin column\n // — retry without the origin field so existing adopters can\n // still toggle cells. The matrix UI's cascade simply won't\n // produce the implied badge until they migrate.\n if (\n grantedVia !== undefined &&\n /column .*granted_via.* does not exist/i.test(error.message)\n ) {\n const fallbackRow: Record<string, unknown> = {\n role_id,\n resource,\n [actionCol]: value,\n };\n const { error: retryErr } = await rbac\n .from(\"role_permissions\")\n .upsert(fallbackRow, { onConflict: \"role_id,resource\" });\n if (retryErr) {\n throw new Error(`setRolePermissionCell: ${retryErr.message}`);\n }\n return;\n }\n throw new Error(`setRolePermissionCell: ${error.message}`);\n }\n },\n\n async batchSetRolePermissionCells(writes) {\n if (writes.length === 0) {\n return;\n }\n // Group writes by (role_id, resource) so the upsert payload has\n // at most one row per matrix cell — otherwise the upsert would\n // need conflict resolution on its own input.\n const byKey = new Map<string, Record<string, unknown>>();\n for (const w of writes) {\n const key = `${w.role_id}::${w.resource}`;\n const existing = byKey.get(key) ?? {\n role_id: w.role_id,\n resource: w.resource,\n };\n existing[ACTION_COLUMN[w.action]] = w.value;\n if (w.grantedVia !== undefined) {\n existing[GRANTED_VIA_COLUMN[w.action]] = w.value ? w.grantedVia : null;\n }\n byKey.set(key, existing);\n }\n const payload = Array.from(byKey.values());\n const { error } = await rbac\n .from(\"role_permissions\")\n .upsert(payload, { onConflict: \"role_id,resource\" });\n if (error) {\n throw new Error(`batchSetRolePermissionCells: ${error.message}`);\n }\n },\n\n syncResourceDependencies,\n\n async listResourceDependencies() {\n // `.order(...)` returns an awaitable filter chain in the\n // Supabase JS runtime — using it here keeps the mock interface\n // in this file happy without widening it.\n const { data, error } = await rbac\n .from(\"resource_dependencies\")\n .select(\"parent_resource, child_resource, action\")\n .order(\"parent_resource\", { ascending: true });\n if (error) {\n // Pre-0.4.0 SQL: table doesn't exist. Treat as \"no\n // dependencies declared\" so legacy callers don't blow up.\n if (\n /resource_dependencies/i.test(error.message) &&\n /does not exist/i.test(error.message)\n ) {\n return [];\n }\n throw new Error(`listResourceDependencies: ${error.message}`);\n }\n return (data ?? []) as AdminResourceDependency[];\n },\n\n async applyTemplateDefaults({ role_id, only_missing = true }) {\n const { data, error } = await rbac.rpc(\"apply_template_defaults\", {\n p_role_id: role_id,\n p_only_missing: only_missing,\n });\n if (error) {\n throw new Error(`applyTemplateDefaults: ${error.message}`);\n }\n if (typeof data === \"number\") return data;\n return Number(data ?? 0);\n },\n\n async listCompanies() {\n const { data, error } = await rbac\n .from(\"companies\")\n .select(\"*\")\n .order(\"name\", { ascending: true });\n if (error) {\n throw new Error(`listCompanies: ${error.message}`);\n }\n return (data ?? []) as AdminCompany[];\n },\n\n async createCompany(input) {\n const { data, error } = await rbac\n .from(\"companies\")\n .insert({\n name: input.name,\n slug: input.slug ?? null,\n type: input.type ?? null,\n })\n .select(\"*\")\n .single();\n if (error) {\n throw new Error(`createCompany: ${error.message}`);\n }\n return data as AdminCompany;\n },\n\n async listCompanyMembers(companyId) {\n // The package doesn't ship a view that joins users + invitations\n // out of the box because the host's auth.users schema may differ.\n // Adopters that need a richer join replace this with their own\n // transport. Fallback: list raw assignments.\n const { data, error } = await rbac\n .from(\"user_company_roles\")\n .select(\"user_id, role_id, assigned_at\")\n .eq(\"company_id\", companyId);\n if (error) {\n throw new Error(`listCompanyMembers: ${error.message}`);\n }\n const grouped = new Map<string, AdminMember>();\n for (const row of (data ?? []) as Array<{\n user_id: string;\n role_id: string;\n assigned_at: string;\n }>) {\n const existing = grouped.get(row.user_id);\n if (existing) {\n existing.role_ids.push(row.role_id);\n } else {\n grouped.set(row.user_id, {\n user_id: row.user_id,\n email: null,\n full_name: null,\n role_ids: [row.role_id],\n invited_at: row.assigned_at,\n invitation_status: \"accepted\",\n });\n }\n }\n return Array.from(grouped.values());\n },\n\n async inviteCompanyMember({ companyId, email, roleIds }) {\n const { error } = await sb.auth.admin.inviteUserByEmail(email, {\n data: {\n rbac_company_id: companyId,\n rbac_role_ids: roleIds,\n },\n redirectTo: opts.inviteRedirectUrl,\n });\n if (error) {\n throw new Error(`inviteCompanyMember: ${error.message}`);\n }\n return { invited: true };\n },\n };\n}\n","/**\n * React hooks for the admin surface. UI-kit-agnostic — adopters\n * render whatever JSX they like with the data + mutations these\n * expose. A copy-paste reference page styled with Tailwind primitives\n * lives in `examples/react-admin/`.\n *\n * Pattern: each hook returns `{ data, isLoading, error, refresh }`\n * and where applicable `{ mutate }`. We deliberately avoid pulling in\n * react-query as a dependency so the package stays peer-light;\n * adopters that already use react-query can wrap these primitives\n * with an extra hook of their own (5 lines).\n */\n\nimport { createContext, useCallback, useContext, useEffect, useMemo, useState } from \"react\";\n\nimport type { Action, FrontendConfig, ResourceScope } from \"../types.js\";\n\nimport type {\n AdminCompany,\n AdminMember,\n AdminResourceDependency,\n AdminRole,\n AdminRolePermission,\n AdminTransport,\n} from \"./types.js\";\n\n// ─────────────────────────────────────────────────────────────────\n// Context — adopter mounts <AdminTransportProvider> once\n// ─────────────────────────────────────────────────────────────────\n\nconst AdminTransportContext = createContext<AdminTransport | null>(null);\n\nexport interface AdminTransportProviderProps {\n transport: AdminTransport;\n children: React.ReactNode;\n}\n\nexport function AdminTransportProvider(props: AdminTransportProviderProps) {\n return (\n <AdminTransportContext.Provider value={props.transport}>\n {props.children}\n </AdminTransportContext.Provider>\n );\n}\n\nfunction useAdminTransport(): AdminTransport {\n const t = useContext(AdminTransportContext);\n if (!t) {\n throw new Error(\n \"auth-rbac admin hooks require <AdminTransportProvider> — wrap your admin pages with one.\",\n );\n }\n return t;\n}\n\n// ─────────────────────────────────────────────────────────────────\n// Tiny generic async-state helper. Avoids reinventing react-query\n// while keeping the boilerplate per-hook to a single line.\n// ─────────────────────────────────────────────────────────────────\n\ninterface AsyncState<T> {\n data: T | null;\n isLoading: boolean;\n error: Error | null;\n}\n\nfunction useAsync<T>(loader: () => Promise<T>, deps: ReadonlyArray<unknown>) {\n const [state, setState] = useState<AsyncState<T>>({\n data: null,\n isLoading: true,\n error: null,\n });\n\n const refresh = useCallback(async () => {\n setState((s) => ({ ...s, isLoading: true, error: null }));\n try {\n const data = await loader();\n setState({ data, isLoading: false, error: null });\n } catch (e) {\n setState({\n data: null,\n isLoading: false,\n error: e instanceof Error ? e : new Error(String(e)),\n });\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, deps);\n\n useEffect(() => {\n void refresh();\n }, [refresh]);\n\n return { ...state, refresh };\n}\n\n// ─────────────────────────────────────────────────────────────────\n// Reads\n// ─────────────────────────────────────────────────────────────────\n\nexport function useAdminRoles(args: {\n scope: ResourceScope;\n companyId?: string | null;\n templatesOnly?: boolean;\n}) {\n const transport = useAdminTransport();\n return useAsync(\n () => transport.listRoles(args),\n [transport, args.scope, args.companyId, args.templatesOnly],\n );\n}\n\nexport function useAdminRolePermissions(roleId: string | null) {\n const transport = useAdminTransport();\n return useAsync(\n async () =>\n roleId == null ? [] : transport.listRolePermissions(roleId),\n [transport, roleId],\n );\n}\n\nexport function useAdminCompanies() {\n const transport = useAdminTransport();\n return useAsync(() => transport.listCompanies(), [transport]);\n}\n\nexport function useAdminCompanyMembers(companyId: string | null) {\n const transport = useAdminTransport();\n return useAsync(\n async () =>\n companyId == null ? [] : transport.listCompanyMembers(companyId),\n [transport, companyId],\n );\n}\n\n// ─────────────────────────────────────────────────────────────────\n// Mutations — return `{ mutate, isPending, error }`. Adopters wrap\n// these in their own toast / error-boundary as needed.\n// ─────────────────────────────────────────────────────────────────\n\ninterface MutationState {\n isPending: boolean;\n error: Error | null;\n}\n\nfunction useMutation<TArgs extends unknown[], TResult>(\n fn: (...args: TArgs) => Promise<TResult>,\n) {\n const [state, setState] = useState<MutationState>({\n isPending: false,\n error: null,\n });\n\n const mutate = useCallback(\n async (...args: TArgs): Promise<TResult> => {\n setState({ isPending: true, error: null });\n try {\n const result = await fn(...args);\n setState({ isPending: false, error: null });\n return result;\n } catch (e) {\n const err = e instanceof Error ? e : new Error(String(e));\n setState({ isPending: false, error: err });\n throw err;\n }\n },\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [fn],\n );\n\n return { mutate, ...state };\n}\n\nexport function useCreateRole() {\n const transport = useAdminTransport();\n return useMutation(transport.createRole);\n}\n\nexport function useUpdateRole() {\n const transport = useAdminTransport();\n return useMutation(transport.updateRole);\n}\n\nexport function useDeleteRole() {\n const transport = useAdminTransport();\n return useMutation(transport.deleteRole);\n}\n\nexport function useSetRolePermissionCell() {\n const transport = useAdminTransport();\n return useMutation(transport.setRolePermissionCell);\n}\n\nexport function useApplyTemplateDefaults() {\n const transport = useAdminTransport();\n return useMutation(transport.applyTemplateDefaults);\n}\n\n/**\n * 0.4.0+. Materialised dependency edges. Loaded once per admin\n * session — the underlying table mutates only on app boot (via\n * `syncResources` → `syncResourceDependencies`).\n */\nexport function useAdminResourceDependencies() {\n const transport = useAdminTransport();\n return useAsync(\n () => transport.listResourceDependencies(),\n [transport],\n );\n}\n\nexport function useCreateCompany() {\n const transport = useAdminTransport();\n return useMutation(transport.createCompany);\n}\n\nexport function useInviteCompanyMember() {\n const transport = useAdminTransport();\n return useMutation(transport.inviteCompanyMember);\n}\n\n// ─────────────────────────────────────────────────────────────────\n// Convenience: hold a role's full state (role + permission grid)\n// in one hook, with a `setCell` mutator that optimistically updates\n// the local cache and writes through to the transport.\n// ─────────────────────────────────────────────────────────────────\n\nexport interface RolePermissionGrid {\n // resource → action → boolean\n [resource: string]: { [A in Action]: boolean };\n}\n\n/**\n * 0.4.0+ shape (0.5.0 semantics): per-cell origin marker. `'direct'`\n * for an explicit admin grant, the name of the parent resource for\n * an implied grant (from rbac.resource_dependencies), or `null` /\n * absent when neither.\n *\n * In 0.4.x this was driven by the `<action>_granted_via` columns on\n * rbac.role_permissions. In 0.5.0 implied rows are no longer\n * materialised — origin is computed client-side from the dependency\n * graph + the role's direct grants.\n */\nexport interface RolePermissionOriginGrid {\n [resource: string]: { [A in Action]: string | null };\n}\n\nexport function useRolePermissionGrid(roleId: string | null) {\n const { data, isLoading, error, refresh } = useAdminRolePermissions(roleId);\n const dependencies = useAdminResourceDependencies();\n const setCell = useSetRolePermissionCell();\n const transport = useAdminTransport();\n\n // 0.5.0: grid reflects the role's DIRECT grants only (the data\n // returned from rbac.role_permissions). Implied state for matrix\n // rendering is computed by the consumer (see PermissionsMatrix —\n // it expands the grid against the dependency graph for both the\n // isCellEnabled and cellOrigin render props).\n const grid = useMemo<RolePermissionGrid>(() => {\n const out: RolePermissionGrid = {};\n for (const row of data ?? []) {\n out[row.resource] = {\n read: row.can_read,\n write: row.can_write,\n update: row.can_update,\n delete: row.can_delete,\n };\n }\n return out;\n }, [data]);\n\n // Reverse-indexed dependency map: child → list of (parent, action)\n // edges that confer implied access. Used by PermissionsMatrix to\n // resolve cellOrigin in O(1) per cell.\n const parentsByChild = useMemo(() => {\n const map = new Map<\n string,\n ReadonlyArray<{ parent: string; action: Action }>\n >();\n for (const edge of dependencies.data ?? []) {\n const list = map.get(edge.child_resource) ?? [];\n map.set(edge.child_resource, [\n ...list,\n { parent: edge.parent_resource, action: edge.action },\n ]);\n }\n return map;\n }, [dependencies.data]);\n\n const originGrid = useMemo<RolePermissionOriginGrid>(() => {\n const out: RolePermissionOriginGrid = {};\n const resources = new Set<string>(Object.keys(grid));\n for (const child of parentsByChild.keys()) {\n resources.add(child);\n }\n for (const resource of resources) {\n const directs = grid[resource];\n const cellOrigins: { [A in Action]: string | null } = {\n read: null,\n write: null,\n update: null,\n delete: null,\n };\n for (const action of ACTIONS) {\n if (directs?.[action]) {\n cellOrigins[action] = \"direct\";\n continue;\n }\n const parents = parentsByChild.get(resource) ?? [];\n const impliedFrom = parents.find(\n (p) => p.action === action && grid[p.parent]?.[action] === true,\n );\n cellOrigins[action] = impliedFrom ? impliedFrom.parent : null;\n }\n out[resource] = cellOrigins;\n }\n return out;\n }, [grid, parentsByChild]);\n\n const updateCell = useCallback(\n async (resource: string, action: Action, value: boolean) => {\n if (!roleId) {\n return;\n }\n // 0.5.0: every write is a DIRECT grant (or direct revoke). No\n // cascade — implied access is computed by the resolver at\n // query time, never materialised. Implied cells are\n // non-editable in the UI (PermissionsMatrix disables them),\n // so this code path only fires for direct cells.\n await setCell.mutate({\n role_id: roleId,\n resource,\n action,\n value,\n grantedVia: null,\n });\n void refresh();\n },\n [roleId, setCell, refresh],\n );\n\n // transport is intentionally part of the closure to keep\n // batchSetRolePermissionCells available to adopters extending the\n // hook (template applies, batch role copies). It's not invoked\n // here in 0.5.0 because per-cell writes are sufficient.\n void transport;\n\n return {\n grid,\n originGrid,\n parentsByChild,\n isLoading: isLoading || dependencies.isLoading,\n error: error ?? dependencies.error,\n refresh,\n updateCell,\n isUpdating: setCell.isPending,\n updateError: setCell.error,\n };\n}\n\nconst ACTIONS: ReadonlyArray<Action> = [\"read\", \"write\", \"update\", \"delete\"];\n","/**\n * Headless permissions matrix.\n *\n * Owns:\n * - reading the role's current permission grid\n * - debounced write-through on every cell toggle\n * - grouping resources by `group` for a sectioned UI\n *\n * Owns NOTHING about styling — the consumer renders all JSX via the\n * single `children` render-prop. A copy-paste reference styled with\n * Tailwind + Radix lives in `examples/react-admin/`.\n *\n * @example minimum viable adoption\n *\n * <PermissionsMatrix\n * roleId={role.id}\n * resources={resources.filter(r => r.scope === role.scope)}\n * >\n * {({ groups, isCellEnabled, setCell, isLoading }) =>\n * groups.map((g) => (\n * <section key={g.group}>\n * <h3>{g.group}</h3>\n * {g.resources.map((r) => (\n * <div key={r.resource}>\n * <span>{r.label}</span>\n * {([\"read\", \"write\", \"update\", \"delete\"] as const).map((a) => (\n * <input\n * key={a}\n * type=\"checkbox\"\n * checked={isCellEnabled(r.resource, a)}\n * disabled={isLoading}\n * onChange={(e) => setCell(r.resource, a, e.target.checked)}\n * />\n * ))}\n * </div>\n * ))}\n * </section>\n * ))\n * }\n * </PermissionsMatrix>\n */\n\nimport { useMemo } from \"react\";\n\nimport type {\n Action,\n ResourceDescriptor,\n} from \"../types.js\";\nimport { groupResources } from \"../client.js\";\n\nimport { useRolePermissionGrid } from \"./hooks.js\";\n\nexport interface MatrixGroup {\n group: string;\n resources: ResourceDescriptor[];\n}\n\nexport interface MatrixRenderArgs {\n /** Resources grouped by their `group` label, original insertion order. */\n groups: MatrixGroup[];\n /** Read a single cell from the current grid. */\n isCellEnabled: (resource: string, action: Action) => boolean;\n /**\n * Origin of a single cell — `'direct'` for a direct admin grant\n * (or off), or the name of the parent resource whose `dependsOn`\n * edge implied the row. The consumer renders an \"Implied by …\"\n * badge whenever this returns a non-`'direct'` value.\n *\n * Available since 0.4.0. With pre-0.4.0 SQL (no granted_via\n * columns) this always returns `'direct'`.\n */\n cellOrigin: (resource: string, action: Action) => \"direct\" | string;\n /**\n * Write a single cell. Optimistic in the local cache + writes\n * through. On toggle-on, also writes implied rows for every\n * `dependsOn` edge whose `actions` include the toggled action —\n * those rows carry the parent's name in\n * `<action>_granted_via`. Toggle-off never cascades.\n */\n setCell: (resource: string, action: Action, value: boolean) => Promise<void>;\n isLoading: boolean;\n isUpdating: boolean;\n error: Error | null;\n /** All four actions, exposed for the consumer to render headers. */\n actions: ReadonlyArray<Action>;\n}\n\nexport interface PermissionsMatrixProps {\n roleId: string | null;\n resources: ReadonlyArray<ResourceDescriptor>;\n children: (args: MatrixRenderArgs) => React.ReactNode;\n}\n\nconst ACTIONS = [\"read\", \"write\", \"update\", \"delete\"] as const;\n\nexport function PermissionsMatrix(props: PermissionsMatrixProps) {\n const { grid, originGrid, isLoading, error, updateCell, isUpdating } =\n useRolePermissionGrid(props.roleId);\n\n const groups = useMemo<MatrixGroup[]>(\n () => groupResources(props.resources),\n [props.resources],\n );\n\n // 0.5.0: a cell is \"enabled\" if it has a DIRECT grant (grid row\n // says true) OR an IMPLIED grant (originGrid says a parent\n // resource — non-null AND not 'direct'). originGrid stores\n // 'direct' for the direct case and the parent resource name for\n // the implied case, so a non-null origin means the cell is\n // enabled either way.\n const isCellEnabled = (resource: string, action: Action): boolean => {\n if (grid[resource]?.[action]) {\n return true;\n }\n return originGrid[resource]?.[action] != null;\n };\n\n const cellOrigin = (\n resource: string,\n action: Action,\n ): \"direct\" | string => {\n const origin = originGrid[resource]?.[action];\n // No grant of any kind → still \"direct\" for the UI (an empty\n // direct cell). Implied grants return the parent resource name.\n return origin == null ? \"direct\" : origin;\n };\n\n const setCell = async (resource: string, action: Action, value: boolean) => {\n await updateCell(resource, action, value);\n };\n\n return (\n <>\n {props.children({\n groups,\n isCellEnabled,\n cellOrigin,\n setCell,\n isLoading,\n isUpdating,\n error,\n actions: ACTIONS,\n })}\n </>\n );\n}\n","/**\n * Headless roles-list controller. Tracks selection + create/delete\n * mutations; consumer renders the list, the new-role dialog, and\n * the destructive-action confirmation.\n */\n\nimport { useCallback, useState } from \"react\";\n\nimport type { ResourceScope } from \"../types.js\";\n\nimport {\n useAdminRoles,\n useCreateRole,\n useDeleteRole,\n} from \"./hooks.js\";\nimport type { AdminRole } from \"./types.js\";\n\nexport interface RolesListRenderArgs {\n roles: AdminRole[];\n isLoading: boolean;\n error: Error | null;\n\n selectedRoleId: string | null;\n selectRole: (id: string | null) => void;\n\n createRole: (input: {\n name: string;\n description?: string;\n }) => Promise<AdminRole>;\n isCreating: boolean;\n createError: Error | null;\n\n deleteRole: (id: string) => Promise<void>;\n isDeleting: boolean;\n deleteError: Error | null;\n\n refresh: () => Promise<void>;\n}\n\nexport interface RolesListProps {\n scope: ResourceScope;\n /** Required for company-scope. Pass `null` for templates. */\n companyId?: string | null;\n /** Pre-select the first role on load. Default: true. */\n autoSelectFirst?: boolean;\n children: (args: RolesListRenderArgs) => React.ReactNode;\n}\n\nexport function RolesList(props: RolesListProps) {\n const { scope, companyId, autoSelectFirst = true } = props;\n\n const list = useAdminRoles({ scope, companyId });\n const create = useCreateRole();\n const remove = useDeleteRole();\n\n const [selectedRoleId, setSelectedRoleId] = useState<string | null>(null);\n\n // Auto-select first role on load.\n if (\n autoSelectFirst &&\n selectedRoleId == null &&\n list.data != null &&\n list.data.length > 0\n ) {\n setSelectedRoleId(list.data[0]!.id);\n }\n\n const createRole = useCallback(\n async (input: { name: string; description?: string }) => {\n const role = await create.mutate({\n scope,\n companyId: companyId ?? null,\n name: input.name,\n description: input.description,\n });\n await list.refresh();\n setSelectedRoleId(role.id);\n return role;\n },\n [create, scope, companyId, list],\n );\n\n const deleteRole = useCallback(\n async (id: string) => {\n await remove.mutate(id);\n if (selectedRoleId === id) {\n setSelectedRoleId(null);\n }\n await list.refresh();\n },\n [remove, list, selectedRoleId],\n );\n\n return (\n <>\n {props.children({\n roles: list.data ?? [],\n isLoading: list.isLoading,\n error: list.error,\n selectedRoleId,\n selectRole: setSelectedRoleId,\n createRole,\n isCreating: create.isPending,\n createError: create.error,\n deleteRole,\n isDeleting: remove.isPending,\n deleteError: remove.error,\n refresh: list.refresh,\n })}\n </>\n );\n}\n","/**\n * Headless invite-member form state. Tracks email + selected role\n * ids, runs basic local validation, and exposes a submit handler\n * that calls the configured transport (Supabase Auth invite by\n * default).\n */\n\nimport { useCallback, useState } from \"react\";\n\nimport { useAdminRoles, useInviteCompanyMember } from \"./hooks.js\";\nimport type { AdminRole } from \"./types.js\";\n\nexport interface InviteMemberFormRenderArgs {\n // form state\n email: string;\n setEmail: (v: string) => void;\n selectedRoleIds: Set<string>;\n toggleRole: (roleId: string) => void;\n resetForm: () => void;\n\n // catalog\n roles: AdminRole[];\n rolesLoading: boolean;\n rolesError: Error | null;\n\n // submission\n submit: () => Promise<void>;\n isSubmitting: boolean;\n submitError: Error | null;\n submittedSuccessfully: boolean;\n\n // validation\n isValid: boolean;\n errors: { email?: string; roles?: string };\n}\n\nexport interface InviteMemberFormProps {\n companyId: string;\n /** Called after a successful invite — typically clears a dialog. */\n onSuccess?: () => void;\n children: (args: InviteMemberFormRenderArgs) => React.ReactNode;\n}\n\nexport function InviteMemberForm(props: InviteMemberFormProps) {\n const rolesQuery = useAdminRoles({\n scope: \"company\",\n companyId: props.companyId,\n });\n const invite = useInviteCompanyMember();\n\n const [email, setEmail] = useState(\"\");\n const [selectedRoleIds, setSelectedRoleIds] = useState<Set<string>>(\n new Set(),\n );\n const [submittedSuccessfully, setSubmittedSuccessfully] = useState(false);\n\n const toggleRole = useCallback((roleId: string) => {\n setSelectedRoleIds((prev) => {\n const next = new Set(prev);\n if (next.has(roleId)) {\n next.delete(roleId);\n } else {\n next.add(roleId);\n }\n return next;\n });\n }, []);\n\n const resetForm = useCallback(() => {\n setEmail(\"\");\n setSelectedRoleIds(new Set());\n setSubmittedSuccessfully(false);\n }, []);\n\n const errors: InviteMemberFormRenderArgs[\"errors\"] = {};\n if (email.trim() && !/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(email.trim())) {\n errors.email = \"Bitte gib eine gültige E-Mail-Adresse ein.\";\n }\n if (selectedRoleIds.size === 0) {\n errors.roles = \"Bitte mindestens eine Rolle auswählen.\";\n }\n const isValid =\n email.trim().length > 0 &&\n Object.keys(errors).length === 0;\n\n const submit = useCallback(async () => {\n if (!isValid) {\n return;\n }\n await invite.mutate({\n companyId: props.companyId,\n email: email.trim(),\n roleIds: Array.from(selectedRoleIds),\n });\n setSubmittedSuccessfully(true);\n props.onSuccess?.();\n }, [invite, props, email, selectedRoleIds, isValid]);\n\n return (\n <>\n {props.children({\n email,\n setEmail,\n selectedRoleIds,\n toggleRole,\n resetForm,\n roles: rolesQuery.data ?? [],\n rolesLoading: rolesQuery.isLoading,\n rolesError: rolesQuery.error,\n submit,\n isSubmitting: invite.isPending,\n submitError: invite.error,\n submittedSuccessfully,\n isValid,\n errors,\n })}\n </>\n );\n}\n"],"mappings":"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","names":["useMemo","jsx","ACTIONS","useMemo","useCallback","useState","Fragment","jsx","useState","useCallback","useCallback","useState","Fragment","jsx","useState","useCallback"]}
|
|
1
|
+
{"version":3,"sources":["../../src/admin/transport.ts","../../src/admin/hooks.tsx","../../src/admin/PermissionsMatrix.tsx","../../src/admin/RolesList.tsx","../../src/admin/InviteMemberForm.tsx"],"sourcesContent":["/**\n * Default Supabase implementation of the admin transport. Hits the\n * package's tables in the `rbac` schema directly via `.schema('rbac').\n * from(...)` and the auth admin endpoint for invites.\n *\n * Adopters must add `rbac` to their PostgREST exposed-schemas list\n * (Supabase Studio → Settings → API → Exposed schemas) for these\n * calls to reach the tables.\n *\n * Projects that route admin writes through their own backend\n * (e.g. for audit logging or extra validation) skip this and\n * implement `AdminTransport` themselves.\n */\n\nimport type { Action, ResourceDescriptor } from \"../types.js\";\n\nimport type {\n AdminCompany,\n AdminMember,\n AdminResourceDependency,\n AdminRole,\n AdminRolePermission,\n AdminRolePermissionOverride,\n AdminTransport,\n} from \"./types.js\";\n\ninterface RbacSchemaClient {\n from(table: string): {\n select: (cols: string) => {\n eq: (col: string, value: unknown) => any;\n is: (col: string, value: unknown) => any;\n order: (col: string, opts?: { ascending: boolean }) => any;\n };\n insert: (row: Record<string, unknown>) => {\n select: (cols: string) => { single: () => any };\n };\n update: (patch: Record<string, unknown>) => {\n eq: (col: string, value: unknown) => {\n select: (cols: string) => { single: () => any };\n };\n };\n upsert: (\n row: Record<string, unknown> | Array<Record<string, unknown>>,\n opts?: { onConflict: string },\n ) => Promise<{ error: { message: string } | null }>;\n delete: () => { eq: (col: string, value: unknown) => any };\n };\n rpc(\n fn: string,\n args: Record<string, unknown>,\n ): Promise<{ data: unknown; error: { message: string } | null }>;\n}\n\ninterface SupabaseAdmin {\n schema(name: string): RbacSchemaClient;\n auth: {\n admin: {\n inviteUserByEmail: (\n email: string,\n opts?: { data?: Record<string, unknown>; redirectTo?: string },\n ) => Promise<{ data: unknown; error: { message: string } | null }>;\n };\n };\n}\n\nexport interface SupabaseAdminClientOptions {\n supabase: SupabaseAdmin;\n /** Where the invitee should land after setting their password. */\n inviteRedirectUrl?: string;\n}\n\nconst ACTION_COLUMN: Record<Action, string> = {\n read: \"can_read\",\n write: \"can_write\",\n update: \"can_update\",\n delete: \"can_delete\",\n};\n\nconst GRANTED_VIA_COLUMN: Record<Action, string> = {\n read: \"read_granted_via\",\n write: \"write_granted_via\",\n update: \"update_granted_via\",\n delete: \"delete_granted_via\",\n};\n\n/**\n * Pull `dependsOn` edges out of a registry array and flatten them\n * into one row per (parent, child, action). Shared helper used by\n * `syncResources` and by adopters who want to sync dependencies\n * manually.\n */\nexport function extractResourceDependencies(\n resources: ReadonlyArray<ResourceDescriptor>,\n): AdminResourceDependency[] {\n const out: AdminResourceDependency[] = [];\n for (const r of resources) {\n for (const edge of r.dependsOn ?? []) {\n const child = typeof edge === \"string\" ? edge : edge.resource;\n const actions =\n typeof edge === \"string\" ? ([\"read\"] as const) : (edge.actions ?? [\"read\"]);\n for (const action of actions) {\n out.push({\n parent_resource: r.resource,\n child_resource: child,\n action,\n });\n }\n }\n }\n return out;\n}\n\nexport function createSupabaseAdminClient(\n opts: SupabaseAdminClientOptions,\n): AdminTransport {\n const sb = opts.supabase;\n const rbac = sb.schema(\"rbac\");\n\n const syncResourceDependencies = async (\n edges: ReadonlyArray<AdminResourceDependency>,\n ): Promise<number> => {\n // Atomic replace-all via the package's RPC. Sidesteps PostgREST's\n // refusal of wildcard deletes and gives a single round-trip.\n const payload = edges.map((e) => ({\n parent_resource: e.parent_resource,\n child_resource: e.child_resource,\n action: e.action,\n }));\n const { error } = await rbac.rpc(\"replace_resource_dependencies\", {\n p_edges: payload,\n });\n if (error) {\n throw new Error(`syncResourceDependencies: ${error.message}`);\n }\n return edges.length;\n };\n\n return {\n async syncResources(resources) {\n if (resources.length === 0) {\n return 0;\n }\n const payload = resources.map((r: ResourceDescriptor) => ({\n resource: r.resource,\n scope: r.scope,\n label: r.label,\n description: r.description ?? null,\n group_label: r.group ?? null,\n }));\n const { error } = await rbac\n .from(\"resources\")\n .upsert(payload, { onConflict: \"resource\" });\n if (error) {\n throw new Error(`syncResources: ${error.message}`);\n }\n // 0.4.0+: also sync dependency edges declared via `dependsOn`.\n // Pre-0.4.0 SQL won't have `rbac.resource_dependencies` yet —\n // syncResourceDependencies tolerates that case internally and\n // surfaces other errors normally.\n const edges = extractResourceDependencies(resources);\n try {\n await syncResourceDependencies(edges);\n } catch (err) {\n if (\n err instanceof Error &&\n /resource_dependencies/i.test(err.message) &&\n /(does not exist|relation .* does not exist)/i.test(err.message)\n ) {\n // Pre-0.4.0 SQL — silently skip.\n } else {\n throw err;\n }\n }\n return resources.length;\n },\n\n async listRoles({ scope, companyId, templatesOnly }) {\n let q = rbac.from(\"roles\").select(\"*\").eq(\"scope\", scope);\n if (templatesOnly) {\n q = q.is(\"company_id\", null);\n } else if (companyId !== undefined) {\n q = companyId === null ? q.is(\"company_id\", null) : q.eq(\"company_id\", companyId);\n }\n const { data, error } = await q.order(\"name\", { ascending: true });\n if (error) {\n throw new Error(`listRoles: ${error.message}`);\n }\n return (data ?? []) as AdminRole[];\n },\n\n async listRolePermissions(roleId) {\n const { data, error } = await rbac\n .from(\"role_permissions\")\n .select(\"*\")\n .eq(\"role_id\", roleId);\n if (error) {\n throw new Error(`listRolePermissions: ${error.message}`);\n }\n return (data ?? []) as AdminRolePermission[];\n },\n\n async createRole(input) {\n const row = {\n scope: input.scope,\n company_id: input.companyId ?? null,\n name: input.name,\n description: input.description ?? null,\n frontend_config: input.frontend_config ?? {},\n };\n const { data, error } = await rbac\n .from(\"roles\")\n .insert(row)\n .select(\"*\")\n .single();\n if (error) {\n throw new Error(`createRole: ${error.message}`);\n }\n return data as AdminRole;\n },\n\n async updateRole(id, patch) {\n const { data, error } = await rbac\n .from(\"roles\")\n .update(patch)\n .eq(\"id\", id)\n .select(\"*\")\n .single();\n if (error) {\n throw new Error(`updateRole: ${error.message}`);\n }\n return data as AdminRole;\n },\n\n async deleteRole(id) {\n const { error } = await rbac.from(\"roles\").delete().eq(\"id\", id);\n if (error) {\n throw new Error(`deleteRole: ${error.message}`);\n }\n },\n\n async setRolePermissionCell({ role_id, resource, action, value, grantedVia }) {\n const actionCol = ACTION_COLUMN[action];\n const originCol = GRANTED_VIA_COLUMN[action];\n // grantedVia semantics:\n // undefined → don't touch the origin column (legacy callers)\n // null → explicit \"this is a direct grant\" — set origin\n // to NULL even if a previous parent owned it\n // string → record the parent name\n const row: Record<string, unknown> = {\n role_id,\n resource,\n [actionCol]: value,\n };\n if (grantedVia !== undefined) {\n // When clearing the action (value=false) we always clear the\n // origin too. Otherwise we record whatever the caller passed.\n row[originCol] = value ? grantedVia : null;\n }\n const { error } = await rbac\n .from(\"role_permissions\")\n .upsert(row, { onConflict: \"role_id,resource\" });\n if (error) {\n // Tolerate pre-0.4.0 SQL that doesn't have the origin column\n // — retry without the origin field so existing adopters can\n // still toggle cells. The matrix UI's cascade simply won't\n // produce the implied badge until they migrate.\n if (\n grantedVia !== undefined &&\n /column .*granted_via.* does not exist/i.test(error.message)\n ) {\n const fallbackRow: Record<string, unknown> = {\n role_id,\n resource,\n [actionCol]: value,\n };\n const { error: retryErr } = await rbac\n .from(\"role_permissions\")\n .upsert(fallbackRow, { onConflict: \"role_id,resource\" });\n if (retryErr) {\n throw new Error(`setRolePermissionCell: ${retryErr.message}`);\n }\n return;\n }\n throw new Error(`setRolePermissionCell: ${error.message}`);\n }\n },\n\n async batchSetRolePermissionCells(writes) {\n if (writes.length === 0) {\n return;\n }\n // Group writes by (role_id, resource) so the upsert payload has\n // at most one row per matrix cell — otherwise the upsert would\n // need conflict resolution on its own input.\n const byKey = new Map<string, Record<string, unknown>>();\n for (const w of writes) {\n const key = `${w.role_id}::${w.resource}`;\n const existing = byKey.get(key) ?? {\n role_id: w.role_id,\n resource: w.resource,\n };\n existing[ACTION_COLUMN[w.action]] = w.value;\n if (w.grantedVia !== undefined) {\n existing[GRANTED_VIA_COLUMN[w.action]] = w.value ? w.grantedVia : null;\n }\n byKey.set(key, existing);\n }\n const payload = Array.from(byKey.values());\n const { error } = await rbac\n .from(\"role_permissions\")\n .upsert(payload, { onConflict: \"role_id,resource\" });\n if (error) {\n throw new Error(`batchSetRolePermissionCells: ${error.message}`);\n }\n },\n\n syncResourceDependencies,\n\n async listResourceDependencies() {\n // `.order(...)` returns an awaitable filter chain in the\n // Supabase JS runtime — using it here keeps the mock interface\n // in this file happy without widening it.\n const { data, error } = await rbac\n .from(\"resource_dependencies\")\n .select(\"parent_resource, child_resource, action\")\n .order(\"parent_resource\", { ascending: true });\n if (error) {\n // Pre-0.4.0 SQL: table doesn't exist. Treat as \"no\n // dependencies declared\" so legacy callers don't blow up.\n if (\n /resource_dependencies/i.test(error.message) &&\n /does not exist/i.test(error.message)\n ) {\n return [];\n }\n throw new Error(`listResourceDependencies: ${error.message}`);\n }\n return (data ?? []) as AdminResourceDependency[];\n },\n\n async listRolePermissionOverrides(roleId: string) {\n const { data, error } = await rbac\n .from(\"role_permission_overrides\")\n .select(\"role_id, resource, action\")\n .eq(\"role_id\", roleId);\n if (error) {\n // Pre-0.6.0 SQL — table absent. Empty override set is the\n // right fallback (no rows = no suppressions).\n if (\n /role_permission_overrides/i.test(error.message) &&\n /does not exist/i.test(error.message)\n ) {\n return [];\n }\n throw new Error(`listRolePermissionOverrides: ${error.message}`);\n }\n return (data ?? []) as AdminRolePermissionOverride[];\n },\n\n async setRolePermissionOverride({ role_id, resource, action, suppress }) {\n if (suppress) {\n const { error } = await rbac\n .from(\"role_permission_overrides\")\n .upsert(\n { role_id, resource, action },\n { onConflict: \"role_id,resource,action\" },\n );\n if (error) {\n throw new Error(`setRolePermissionOverride(insert): ${error.message}`);\n }\n return;\n }\n const { error } = await rbac\n .from(\"role_permission_overrides\")\n .delete()\n .eq(\"role_id\", role_id)\n .eq(\"resource\", resource)\n .eq(\"action\", action);\n if (error) {\n throw new Error(`setRolePermissionOverride(delete): ${error.message}`);\n }\n },\n\n async applyTemplateDefaults({ role_id, only_missing = true }) {\n const { data, error } = await rbac.rpc(\"apply_template_defaults\", {\n p_role_id: role_id,\n p_only_missing: only_missing,\n });\n if (error) {\n throw new Error(`applyTemplateDefaults: ${error.message}`);\n }\n if (typeof data === \"number\") return data;\n return Number(data ?? 0);\n },\n\n async listCompanies() {\n const { data, error } = await rbac\n .from(\"companies\")\n .select(\"*\")\n .order(\"name\", { ascending: true });\n if (error) {\n throw new Error(`listCompanies: ${error.message}`);\n }\n return (data ?? []) as AdminCompany[];\n },\n\n async createCompany(input) {\n const { data, error } = await rbac\n .from(\"companies\")\n .insert({\n name: input.name,\n slug: input.slug ?? null,\n type: input.type ?? null,\n })\n .select(\"*\")\n .single();\n if (error) {\n throw new Error(`createCompany: ${error.message}`);\n }\n return data as AdminCompany;\n },\n\n async listCompanyMembers(companyId) {\n // The package doesn't ship a view that joins users + invitations\n // out of the box because the host's auth.users schema may differ.\n // Adopters that need a richer join replace this with their own\n // transport. Fallback: list raw assignments.\n const { data, error } = await rbac\n .from(\"user_company_roles\")\n .select(\"user_id, role_id, assigned_at\")\n .eq(\"company_id\", companyId);\n if (error) {\n throw new Error(`listCompanyMembers: ${error.message}`);\n }\n const grouped = new Map<string, AdminMember>();\n for (const row of (data ?? []) as Array<{\n user_id: string;\n role_id: string;\n assigned_at: string;\n }>) {\n const existing = grouped.get(row.user_id);\n if (existing) {\n existing.role_ids.push(row.role_id);\n } else {\n grouped.set(row.user_id, {\n user_id: row.user_id,\n email: null,\n full_name: null,\n role_ids: [row.role_id],\n invited_at: row.assigned_at,\n invitation_status: \"accepted\",\n });\n }\n }\n return Array.from(grouped.values());\n },\n\n async inviteCompanyMember({ companyId, email, roleIds }) {\n const { error } = await sb.auth.admin.inviteUserByEmail(email, {\n data: {\n rbac_company_id: companyId,\n rbac_role_ids: roleIds,\n },\n redirectTo: opts.inviteRedirectUrl,\n });\n if (error) {\n throw new Error(`inviteCompanyMember: ${error.message}`);\n }\n return { invited: true };\n },\n };\n}\n","/**\n * React hooks for the admin surface. UI-kit-agnostic — adopters\n * render whatever JSX they like with the data + mutations these\n * expose. A copy-paste reference page styled with Tailwind primitives\n * lives in `examples/react-admin/`.\n *\n * Pattern: each hook returns `{ data, isLoading, error, refresh }`\n * and where applicable `{ mutate }`. We deliberately avoid pulling in\n * react-query as a dependency so the package stays peer-light;\n * adopters that already use react-query can wrap these primitives\n * with an extra hook of their own (5 lines).\n */\n\nimport { createContext, useCallback, useContext, useEffect, useMemo, useState } from \"react\";\n\nimport type { Action, FrontendConfig, ResourceScope } from \"../types.js\";\n\nimport type {\n AdminCompany,\n AdminMember,\n AdminResourceDependency,\n AdminRole,\n AdminRolePermission,\n AdminRolePermissionOverride,\n AdminTransport,\n} from \"./types.js\";\n\n// ─────────────────────────────────────────────────────────────────\n// Context — adopter mounts <AdminTransportProvider> once\n// ─────────────────────────────────────────────────────────────────\n\nconst AdminTransportContext = createContext<AdminTransport | null>(null);\n\nexport interface AdminTransportProviderProps {\n transport: AdminTransport;\n children: React.ReactNode;\n}\n\nexport function AdminTransportProvider(props: AdminTransportProviderProps) {\n return (\n <AdminTransportContext.Provider value={props.transport}>\n {props.children}\n </AdminTransportContext.Provider>\n );\n}\n\nfunction useAdminTransport(): AdminTransport {\n const t = useContext(AdminTransportContext);\n if (!t) {\n throw new Error(\n \"auth-rbac admin hooks require <AdminTransportProvider> — wrap your admin pages with one.\",\n );\n }\n return t;\n}\n\n// ─────────────────────────────────────────────────────────────────\n// Tiny generic async-state helper. Avoids reinventing react-query\n// while keeping the boilerplate per-hook to a single line.\n// ─────────────────────────────────────────────────────────────────\n\ninterface AsyncState<T> {\n data: T | null;\n isLoading: boolean;\n error: Error | null;\n}\n\nfunction useAsync<T>(loader: () => Promise<T>, deps: ReadonlyArray<unknown>) {\n const [state, setState] = useState<AsyncState<T>>({\n data: null,\n isLoading: true,\n error: null,\n });\n\n const refresh = useCallback(async () => {\n setState((s) => ({ ...s, isLoading: true, error: null }));\n try {\n const data = await loader();\n setState({ data, isLoading: false, error: null });\n } catch (e) {\n setState({\n data: null,\n isLoading: false,\n error: e instanceof Error ? e : new Error(String(e)),\n });\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, deps);\n\n useEffect(() => {\n void refresh();\n }, [refresh]);\n\n return { ...state, refresh };\n}\n\n// ─────────────────────────────────────────────────────────────────\n// Reads\n// ─────────────────────────────────────────────────────────────────\n\nexport function useAdminRoles(args: {\n scope: ResourceScope;\n companyId?: string | null;\n templatesOnly?: boolean;\n}) {\n const transport = useAdminTransport();\n return useAsync(\n () => transport.listRoles(args),\n [transport, args.scope, args.companyId, args.templatesOnly],\n );\n}\n\nexport function useAdminRolePermissions(roleId: string | null) {\n const transport = useAdminTransport();\n return useAsync(\n async () =>\n roleId == null ? [] : transport.listRolePermissions(roleId),\n [transport, roleId],\n );\n}\n\nexport function useAdminCompanies() {\n const transport = useAdminTransport();\n return useAsync(() => transport.listCompanies(), [transport]);\n}\n\nexport function useAdminCompanyMembers(companyId: string | null) {\n const transport = useAdminTransport();\n return useAsync(\n async () =>\n companyId == null ? [] : transport.listCompanyMembers(companyId),\n [transport, companyId],\n );\n}\n\n// ─────────────────────────────────────────────────────────────────\n// Mutations — return `{ mutate, isPending, error }`. Adopters wrap\n// these in their own toast / error-boundary as needed.\n// ─────────────────────────────────────────────────────────────────\n\ninterface MutationState {\n isPending: boolean;\n error: Error | null;\n}\n\nfunction useMutation<TArgs extends unknown[], TResult>(\n fn: (...args: TArgs) => Promise<TResult>,\n) {\n const [state, setState] = useState<MutationState>({\n isPending: false,\n error: null,\n });\n\n const mutate = useCallback(\n async (...args: TArgs): Promise<TResult> => {\n setState({ isPending: true, error: null });\n try {\n const result = await fn(...args);\n setState({ isPending: false, error: null });\n return result;\n } catch (e) {\n const err = e instanceof Error ? e : new Error(String(e));\n setState({ isPending: false, error: err });\n throw err;\n }\n },\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [fn],\n );\n\n return { mutate, ...state };\n}\n\nexport function useCreateRole() {\n const transport = useAdminTransport();\n return useMutation(transport.createRole);\n}\n\nexport function useUpdateRole() {\n const transport = useAdminTransport();\n return useMutation(transport.updateRole);\n}\n\nexport function useDeleteRole() {\n const transport = useAdminTransport();\n return useMutation(transport.deleteRole);\n}\n\nexport function useSetRolePermissionCell() {\n const transport = useAdminTransport();\n return useMutation(transport.setRolePermissionCell);\n}\n\nexport function useApplyTemplateDefaults() {\n const transport = useAdminTransport();\n return useMutation(transport.applyTemplateDefaults);\n}\n\n/**\n * 0.4.0+. Materialised dependency edges. Loaded once per admin\n * session — the underlying table mutates only on app boot (via\n * `syncResources` → `syncResourceDependencies`).\n */\nexport function useAdminResourceDependencies() {\n const transport = useAdminTransport();\n return useAsync(\n () => transport.listResourceDependencies(),\n [transport],\n );\n}\n\n/**\n * 0.6.0+. Per-role override map. Returns a Set of\n * `\"<resource>:<action>\"` keys for the given role plus a\n * `setOverride(resource, action, suppress)` mutator. Optimistic —\n * the local set flips immediately, then a re-fetch reconciles.\n *\n * Use this in tandem with `useRolePermissionGrid` to render a\n * matrix UI that distinguishes:\n * * direct grants (the row is on rbac.role_permissions)\n * * implied grants (resource_dependencies expansion)\n * * overrides (this hook's set — admin clicked an implied cell\n * off to carve it out for this specific role)\n */\nexport function useRolePermissionOverrides(roleId: string | null) {\n const transport = useAdminTransport();\n const [overrides, setOverrides] = useState<Set<string>>(() => new Set<string>());\n const [error, setError] = useState<Error | null>(null);\n\n const fetchOverrides = useCallback(async () => {\n if (!roleId) {\n setOverrides(new Set<string>());\n return;\n }\n try {\n const rows = await transport.listRolePermissionOverrides(roleId);\n setOverrides(new Set(rows.map((r) => `${r.resource}:${r.action}`)));\n setError(null);\n } catch (e) {\n setError(e instanceof Error ? e : new Error(String(e)));\n }\n }, [transport, roleId]);\n\n useEffect(() => {\n void fetchOverrides();\n }, [fetchOverrides]);\n\n const setOverride = useCallback(\n async (resource: string, action: Action, suppress: boolean) => {\n if (!roleId) {\n return;\n }\n const key = `${resource}:${action}`;\n // Optimistic flip.\n setOverrides((prev) => {\n const next = new Set(prev);\n if (suppress) {\n next.add(key);\n } else {\n next.delete(key);\n }\n return next;\n });\n try {\n await transport.setRolePermissionOverride({\n role_id: roleId,\n resource,\n action,\n suppress,\n });\n } catch (e) {\n setError(e instanceof Error ? e : new Error(String(e)));\n }\n // Reconcile against the server in case the optimistic flip\n // was wrong (e.g. concurrent edit from another admin tab).\n void fetchOverrides();\n },\n [transport, roleId, fetchOverrides],\n );\n\n return { overrides, setOverride, error, refresh: fetchOverrides };\n}\n\nexport function useCreateCompany() {\n const transport = useAdminTransport();\n return useMutation(transport.createCompany);\n}\n\nexport function useInviteCompanyMember() {\n const transport = useAdminTransport();\n return useMutation(transport.inviteCompanyMember);\n}\n\n// ─────────────────────────────────────────────────────────────────\n// Convenience: hold a role's full state (role + permission grid)\n// in one hook, with a `setCell` mutator that optimistically updates\n// the local cache and writes through to the transport.\n// ─────────────────────────────────────────────────────────────────\n\nexport interface RolePermissionGrid {\n // resource → action → boolean\n [resource: string]: { [A in Action]: boolean };\n}\n\n/**\n * Per-cell origin marker:\n * * `'direct'` — explicit admin grant on rbac.role_permissions\n * * `'override'` — admin set an override on rbac.role_permission_overrides\n * (0.6.0+; cell is OFF even if a direct or\n * implied grant would otherwise apply)\n * * `<string>` — name of the parent resource that implies this\n * cell via rbac.resource_dependencies\n * * `null` — neither granted nor implied\n *\n * In 0.4.x this was driven by the `<action>_granted_via` columns on\n * rbac.role_permissions. In 0.5.0+ implied rows are no longer\n * materialised — origin is computed client-side from the dependency\n * graph + the role's direct grants + (0.6.0+) any overrides.\n */\nexport interface RolePermissionOriginGrid {\n [resource: string]: { [A in Action]: string | null };\n}\n\nexport function useRolePermissionGrid(roleId: string | null) {\n const { data, isLoading, error, refresh } = useAdminRolePermissions(roleId);\n const dependencies = useAdminResourceDependencies();\n const overridesHook = useRolePermissionOverrides(roleId);\n const setCell = useSetRolePermissionCell();\n const transport = useAdminTransport();\n\n // 0.5.0: grid reflects the role's DIRECT grants only (the data\n // returned from rbac.role_permissions). Implied state for matrix\n // rendering is computed by the consumer (see PermissionsMatrix —\n // it expands the grid against the dependency graph for both the\n // isCellEnabled and cellOrigin render props).\n const grid = useMemo<RolePermissionGrid>(() => {\n const out: RolePermissionGrid = {};\n for (const row of data ?? []) {\n out[row.resource] = {\n read: row.can_read,\n write: row.can_write,\n update: row.can_update,\n delete: row.can_delete,\n };\n }\n return out;\n }, [data]);\n\n // Reverse-indexed dependency map: child → list of (parent, action)\n // edges that confer implied access. Used by PermissionsMatrix to\n // resolve cellOrigin in O(1) per cell.\n const parentsByChild = useMemo(() => {\n const map = new Map<\n string,\n ReadonlyArray<{ parent: string; action: Action }>\n >();\n for (const edge of dependencies.data ?? []) {\n const list = map.get(edge.child_resource) ?? [];\n map.set(edge.child_resource, [\n ...list,\n { parent: edge.parent_resource, action: edge.action },\n ]);\n }\n return map;\n }, [dependencies.data]);\n\n const originGrid = useMemo<RolePermissionOriginGrid>(() => {\n const out: RolePermissionOriginGrid = {};\n const resources = new Set<string>(Object.keys(grid));\n for (const child of parentsByChild.keys()) {\n resources.add(child);\n }\n const overrides = overridesHook.overrides;\n for (const resource of resources) {\n const directs = grid[resource];\n const cellOrigins: { [A in Action]: string | null } = {\n read: null,\n write: null,\n update: null,\n delete: null,\n };\n for (const action of ACTIONS) {\n // 0.6.0+ override takes precedence over everything else.\n // Direct row + override = cell renders as \"override\" (off).\n if (overrides.has(`${resource}:${action}`)) {\n cellOrigins[action] = \"override\";\n continue;\n }\n if (directs?.[action]) {\n cellOrigins[action] = \"direct\";\n continue;\n }\n const parents = parentsByChild.get(resource) ?? [];\n const impliedFrom = parents.find(\n (p) => p.action === action && grid[p.parent]?.[action] === true,\n );\n cellOrigins[action] = impliedFrom ? impliedFrom.parent : null;\n }\n out[resource] = cellOrigins;\n }\n return out;\n }, [grid, parentsByChild, overridesHook.overrides]);\n\n const updateCell = useCallback(\n async (resource: string, action: Action, value: boolean) => {\n if (!roleId) {\n return;\n }\n // 0.5.0: every write is a DIRECT grant (or direct revoke). No\n // cascade — implied access is computed by the resolver at\n // query time, never materialised. Implied cells are\n // non-editable in the UI (PermissionsMatrix disables them),\n // so this code path only fires for direct cells.\n await setCell.mutate({\n role_id: roleId,\n resource,\n action,\n value,\n grantedVia: null,\n });\n void refresh();\n },\n [roleId, setCell, refresh],\n );\n\n // transport is intentionally part of the closure to keep\n // batchSetRolePermissionCells available to adopters extending the\n // hook (template applies, batch role copies). It's not invoked\n // here in 0.5.0 because per-cell writes are sufficient.\n void transport;\n\n return {\n grid,\n originGrid,\n parentsByChild,\n /** 0.6.0+. Set of `\"<resource>:<action>\"` for this role's overrides. */\n overrides: overridesHook.overrides,\n /**\n * 0.6.0+. Suppress (`suppress=true`) or restore (`suppress=false`)\n * an implied permission for this role. The grid + originGrid\n * re-render with `'override'` state on the cell as soon as the\n * optimistic flip lands.\n */\n setOverride: overridesHook.setOverride,\n isLoading: isLoading || dependencies.isLoading,\n error: error ?? dependencies.error ?? overridesHook.error,\n refresh,\n updateCell,\n isUpdating: setCell.isPending,\n updateError: setCell.error,\n };\n}\n\nconst ACTIONS: ReadonlyArray<Action> = [\"read\", \"write\", \"update\", \"delete\"];\n","/**\n * Headless permissions matrix.\n *\n * Owns:\n * - reading the role's current permission grid\n * - debounced write-through on every cell toggle\n * - grouping resources by `group` for a sectioned UI\n *\n * Owns NOTHING about styling — the consumer renders all JSX via the\n * single `children` render-prop. A copy-paste reference styled with\n * Tailwind + Radix lives in `examples/react-admin/`.\n *\n * @example minimum viable adoption\n *\n * <PermissionsMatrix\n * roleId={role.id}\n * resources={resources.filter(r => r.scope === role.scope)}\n * >\n * {({ groups, isCellEnabled, setCell, isLoading }) =>\n * groups.map((g) => (\n * <section key={g.group}>\n * <h3>{g.group}</h3>\n * {g.resources.map((r) => (\n * <div key={r.resource}>\n * <span>{r.label}</span>\n * {([\"read\", \"write\", \"update\", \"delete\"] as const).map((a) => (\n * <input\n * key={a}\n * type=\"checkbox\"\n * checked={isCellEnabled(r.resource, a)}\n * disabled={isLoading}\n * onChange={(e) => setCell(r.resource, a, e.target.checked)}\n * />\n * ))}\n * </div>\n * ))}\n * </section>\n * ))\n * }\n * </PermissionsMatrix>\n */\n\nimport { useMemo } from \"react\";\n\nimport type {\n Action,\n ResourceDescriptor,\n} from \"../types.js\";\nimport { groupResources } from \"../client.js\";\n\nimport { useRolePermissionGrid } from \"./hooks.js\";\n\nexport interface MatrixGroup {\n group: string;\n resources: ResourceDescriptor[];\n}\n\nexport interface MatrixRenderArgs {\n /** Resources grouped by their `group` label, original insertion order. */\n groups: MatrixGroup[];\n /**\n * Effective state of a cell after applying direct grants, the\n * resource-dependency expansion, and any per-role overrides.\n * What the resolver would answer for a user holding this role.\n */\n isCellEnabled: (resource: string, action: Action) => boolean;\n /**\n * Origin of a single cell:\n * * `'direct'` — explicit admin grant on rbac.role_permissions\n * * `'override'` — admin suppressed it via rbac.role_permission_overrides\n * (0.6.0+; cell is off even if a parent would imply)\n * * `<string>` — the name of the parent resource whose\n * `dependsOn` edge implies this cell\n *\n * Available since 0.4.0; the `'override'` value is 0.6.0+.\n */\n cellOrigin: (resource: string, action: Action) => \"direct\" | \"override\" | string;\n /**\n * Toggle a DIRECT grant on rbac.role_permissions. Use for cells\n * that the matrix UI shows as \"direct\" (no implied parent). For\n * cells that are implied, use `setOverride` instead — that's what\n * lets the admin opt a single role out of a cascade without\n * touching the parent grant or the registry.\n */\n setCell: (resource: string, action: Action, value: boolean) => Promise<void>;\n /**\n * 0.6.0+. Suppress (`suppress=true`) or restore (`suppress=false`)\n * an implied permission for this role via\n * rbac.role_permission_overrides. Writes are optimistic; the\n * `cellOrigin` reflects the new state immediately.\n */\n setOverride: (\n resource: string,\n action: Action,\n suppress: boolean,\n ) => Promise<void>;\n isLoading: boolean;\n isUpdating: boolean;\n error: Error | null;\n /** All four actions, exposed for the consumer to render headers. */\n actions: ReadonlyArray<Action>;\n}\n\nexport interface PermissionsMatrixProps {\n roleId: string | null;\n resources: ReadonlyArray<ResourceDescriptor>;\n children: (args: MatrixRenderArgs) => React.ReactNode;\n}\n\nconst ACTIONS = [\"read\", \"write\", \"update\", \"delete\"] as const;\n\nexport function PermissionsMatrix(props: PermissionsMatrixProps) {\n const {\n grid,\n originGrid,\n isLoading,\n error,\n updateCell,\n isUpdating,\n setOverride: gridSetOverride,\n } = useRolePermissionGrid(props.roleId);\n\n const groups = useMemo<MatrixGroup[]>(\n () => groupResources(props.resources),\n [props.resources],\n );\n\n // A cell is \"enabled\" when its origin is direct or implied via a\n // parent — anything except null/`override`/`undefined`. The\n // 0.6.0 override marker counts as \"not enabled\" because the\n // resolver subtracts it.\n const isCellEnabled = (resource: string, action: Action): boolean => {\n const origin = originGrid[resource]?.[action];\n if (origin == null || origin === \"override\") {\n return false;\n }\n return true;\n };\n\n const cellOrigin = (\n resource: string,\n action: Action,\n ): \"direct\" | \"override\" | string => {\n const origin = originGrid[resource]?.[action];\n // No grant of any kind → still \"direct\" for the UI (an empty\n // direct cell). Implied grants return the parent resource name.\n return origin == null ? \"direct\" : origin;\n };\n\n const setCell = async (resource: string, action: Action, value: boolean) => {\n await updateCell(resource, action, value);\n };\n\n const setOverride = async (\n resource: string,\n action: Action,\n suppress: boolean,\n ) => {\n await gridSetOverride(resource, action, suppress);\n };\n\n return (\n <>\n {props.children({\n groups,\n isCellEnabled,\n cellOrigin,\n setCell,\n setOverride,\n isLoading,\n isUpdating,\n error,\n actions: ACTIONS,\n })}\n </>\n );\n}\n","/**\n * Headless roles-list controller. Tracks selection + create/delete\n * mutations; consumer renders the list, the new-role dialog, and\n * the destructive-action confirmation.\n */\n\nimport { useCallback, useState } from \"react\";\n\nimport type { ResourceScope } from \"../types.js\";\n\nimport {\n useAdminRoles,\n useCreateRole,\n useDeleteRole,\n} from \"./hooks.js\";\nimport type { AdminRole } from \"./types.js\";\n\nexport interface RolesListRenderArgs {\n roles: AdminRole[];\n isLoading: boolean;\n error: Error | null;\n\n selectedRoleId: string | null;\n selectRole: (id: string | null) => void;\n\n createRole: (input: {\n name: string;\n description?: string;\n }) => Promise<AdminRole>;\n isCreating: boolean;\n createError: Error | null;\n\n deleteRole: (id: string) => Promise<void>;\n isDeleting: boolean;\n deleteError: Error | null;\n\n refresh: () => Promise<void>;\n}\n\nexport interface RolesListProps {\n scope: ResourceScope;\n /** Required for company-scope. Pass `null` for templates. */\n companyId?: string | null;\n /** Pre-select the first role on load. Default: true. */\n autoSelectFirst?: boolean;\n children: (args: RolesListRenderArgs) => React.ReactNode;\n}\n\nexport function RolesList(props: RolesListProps) {\n const { scope, companyId, autoSelectFirst = true } = props;\n\n const list = useAdminRoles({ scope, companyId });\n const create = useCreateRole();\n const remove = useDeleteRole();\n\n const [selectedRoleId, setSelectedRoleId] = useState<string | null>(null);\n\n // Auto-select first role on load.\n if (\n autoSelectFirst &&\n selectedRoleId == null &&\n list.data != null &&\n list.data.length > 0\n ) {\n setSelectedRoleId(list.data[0]!.id);\n }\n\n const createRole = useCallback(\n async (input: { name: string; description?: string }) => {\n const role = await create.mutate({\n scope,\n companyId: companyId ?? null,\n name: input.name,\n description: input.description,\n });\n await list.refresh();\n setSelectedRoleId(role.id);\n return role;\n },\n [create, scope, companyId, list],\n );\n\n const deleteRole = useCallback(\n async (id: string) => {\n await remove.mutate(id);\n if (selectedRoleId === id) {\n setSelectedRoleId(null);\n }\n await list.refresh();\n },\n [remove, list, selectedRoleId],\n );\n\n return (\n <>\n {props.children({\n roles: list.data ?? [],\n isLoading: list.isLoading,\n error: list.error,\n selectedRoleId,\n selectRole: setSelectedRoleId,\n createRole,\n isCreating: create.isPending,\n createError: create.error,\n deleteRole,\n isDeleting: remove.isPending,\n deleteError: remove.error,\n refresh: list.refresh,\n })}\n </>\n );\n}\n","/**\n * Headless invite-member form state. Tracks email + selected role\n * ids, runs basic local validation, and exposes a submit handler\n * that calls the configured transport (Supabase Auth invite by\n * default).\n */\n\nimport { useCallback, useState } from \"react\";\n\nimport { useAdminRoles, useInviteCompanyMember } from \"./hooks.js\";\nimport type { AdminRole } from \"./types.js\";\n\nexport interface InviteMemberFormRenderArgs {\n // form state\n email: string;\n setEmail: (v: string) => void;\n selectedRoleIds: Set<string>;\n toggleRole: (roleId: string) => void;\n resetForm: () => void;\n\n // catalog\n roles: AdminRole[];\n rolesLoading: boolean;\n rolesError: Error | null;\n\n // submission\n submit: () => Promise<void>;\n isSubmitting: boolean;\n submitError: Error | null;\n submittedSuccessfully: boolean;\n\n // validation\n isValid: boolean;\n errors: { email?: string; roles?: string };\n}\n\nexport interface InviteMemberFormProps {\n companyId: string;\n /** Called after a successful invite — typically clears a dialog. */\n onSuccess?: () => void;\n children: (args: InviteMemberFormRenderArgs) => React.ReactNode;\n}\n\nexport function InviteMemberForm(props: InviteMemberFormProps) {\n const rolesQuery = useAdminRoles({\n scope: \"company\",\n companyId: props.companyId,\n });\n const invite = useInviteCompanyMember();\n\n const [email, setEmail] = useState(\"\");\n const [selectedRoleIds, setSelectedRoleIds] = useState<Set<string>>(\n new Set(),\n );\n const [submittedSuccessfully, setSubmittedSuccessfully] = useState(false);\n\n const toggleRole = useCallback((roleId: string) => {\n setSelectedRoleIds((prev) => {\n const next = new Set(prev);\n if (next.has(roleId)) {\n next.delete(roleId);\n } else {\n next.add(roleId);\n }\n return next;\n });\n }, []);\n\n const resetForm = useCallback(() => {\n setEmail(\"\");\n setSelectedRoleIds(new Set());\n setSubmittedSuccessfully(false);\n }, []);\n\n const errors: InviteMemberFormRenderArgs[\"errors\"] = {};\n if (email.trim() && !/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(email.trim())) {\n errors.email = \"Bitte gib eine gültige E-Mail-Adresse ein.\";\n }\n if (selectedRoleIds.size === 0) {\n errors.roles = \"Bitte mindestens eine Rolle auswählen.\";\n }\n const isValid =\n email.trim().length > 0 &&\n Object.keys(errors).length === 0;\n\n const submit = useCallback(async () => {\n if (!isValid) {\n return;\n }\n await invite.mutate({\n companyId: props.companyId,\n email: email.trim(),\n roleIds: Array.from(selectedRoleIds),\n });\n setSubmittedSuccessfully(true);\n props.onSuccess?.();\n }, [invite, props, email, selectedRoleIds, isValid]);\n\n return (\n <>\n {props.children({\n email,\n setEmail,\n selectedRoleIds,\n toggleRole,\n resetForm,\n roles: rolesQuery.data ?? [],\n rolesLoading: rolesQuery.isLoading,\n rolesError: rolesQuery.error,\n submit,\n isSubmitting: invite.isPending,\n submitError: invite.error,\n submittedSuccessfully,\n isValid,\n errors,\n })}\n </>\n );\n}\n"],"mappings":";;;;;AAuEA,IAAM,gBAAwC;AAAA,EAC5C,MAAM;AAAA,EACN,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,QAAQ;AACV;AAEA,IAAM,qBAA6C;AAAA,EACjD,MAAM;AAAA,EACN,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,QAAQ;AACV;AAQO,SAAS,4BACd,WAC2B;AAC3B,QAAM,MAAiC,CAAC;AACxC,aAAW,KAAK,WAAW;AACzB,eAAW,QAAQ,EAAE,aAAa,CAAC,GAAG;AACpC,YAAM,QAAQ,OAAO,SAAS,WAAW,OAAO,KAAK;AACrD,YAAM,UACJ,OAAO,SAAS,WAAY,CAAC,MAAM,IAAe,KAAK,WAAW,CAAC,MAAM;AAC3E,iBAAW,UAAU,SAAS;AAC5B,YAAI,KAAK;AAAA,UACP,iBAAiB,EAAE;AAAA,UACnB,gBAAgB;AAAA,UAChB;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAEO,SAAS,0BACd,MACgB;AAChB,QAAM,KAAK,KAAK;AAChB,QAAM,OAAO,GAAG,OAAO,MAAM;AAE7B,QAAM,2BAA2B,OAC/B,UACoB;AAGpB,UAAM,UAAU,MAAM,IAAI,CAAC,OAAO;AAAA,MAChC,iBAAiB,EAAE;AAAA,MACnB,gBAAgB,EAAE;AAAA,MAClB,QAAQ,EAAE;AAAA,IACZ,EAAE;AACF,UAAM,EAAE,MAAM,IAAI,MAAM,KAAK,IAAI,iCAAiC;AAAA,MAChE,SAAS;AAAA,IACX,CAAC;AACD,QAAI,OAAO;AACT,YAAM,IAAI,MAAM,6BAA6B,MAAM,OAAO,EAAE;AAAA,IAC9D;AACA,WAAO,MAAM;AAAA,EACf;AAEA,SAAO;AAAA,IACL,MAAM,cAAc,WAAW;AAC7B,UAAI,UAAU,WAAW,GAAG;AAC1B,eAAO;AAAA,MACT;AACA,YAAM,UAAU,UAAU,IAAI,CAAC,OAA2B;AAAA,QACxD,UAAU,EAAE;AAAA,QACZ,OAAO,EAAE;AAAA,QACT,OAAO,EAAE;AAAA,QACT,aAAa,EAAE,eAAe;AAAA,QAC9B,aAAa,EAAE,SAAS;AAAA,MAC1B,EAAE;AACF,YAAM,EAAE,MAAM,IAAI,MAAM,KACrB,KAAK,WAAW,EAChB,OAAO,SAAS,EAAE,YAAY,WAAW,CAAC;AAC7C,UAAI,OAAO;AACT,cAAM,IAAI,MAAM,kBAAkB,MAAM,OAAO,EAAE;AAAA,MACnD;AAKA,YAAM,QAAQ,4BAA4B,SAAS;AACnD,UAAI;AACF,cAAM,yBAAyB,KAAK;AAAA,MACtC,SAAS,KAAK;AACZ,YACE,eAAe,SACf,yBAAyB,KAAK,IAAI,OAAO,KACzC,+CAA+C,KAAK,IAAI,OAAO,GAC/D;AAAA,QAEF,OAAO;AACL,gBAAM;AAAA,QACR;AAAA,MACF;AACA,aAAO,UAAU;AAAA,IACnB;AAAA,IAEA,MAAM,UAAU,EAAE,OAAO,WAAW,cAAc,GAAG;AACnD,UAAI,IAAI,KAAK,KAAK,OAAO,EAAE,OAAO,GAAG,EAAE,GAAG,SAAS,KAAK;AACxD,UAAI,eAAe;AACjB,YAAI,EAAE,GAAG,cAAc,IAAI;AAAA,MAC7B,WAAW,cAAc,QAAW;AAClC,YAAI,cAAc,OAAO,EAAE,GAAG,cAAc,IAAI,IAAI,EAAE,GAAG,cAAc,SAAS;AAAA,MAClF;AACA,YAAM,EAAE,MAAM,MAAM,IAAI,MAAM,EAAE,MAAM,QAAQ,EAAE,WAAW,KAAK,CAAC;AACjE,UAAI,OAAO;AACT,cAAM,IAAI,MAAM,cAAc,MAAM,OAAO,EAAE;AAAA,MAC/C;AACA,aAAQ,QAAQ,CAAC;AAAA,IACnB;AAAA,IAEA,MAAM,oBAAoB,QAAQ;AAChC,YAAM,EAAE,MAAM,MAAM,IAAI,MAAM,KAC3B,KAAK,kBAAkB,EACvB,OAAO,GAAG,EACV,GAAG,WAAW,MAAM;AACvB,UAAI,OAAO;AACT,cAAM,IAAI,MAAM,wBAAwB,MAAM,OAAO,EAAE;AAAA,MACzD;AACA,aAAQ,QAAQ,CAAC;AAAA,IACnB;AAAA,IAEA,MAAM,WAAW,OAAO;AACtB,YAAM,MAAM;AAAA,QACV,OAAO,MAAM;AAAA,QACb,YAAY,MAAM,aAAa;AAAA,QAC/B,MAAM,MAAM;AAAA,QACZ,aAAa,MAAM,eAAe;AAAA,QAClC,iBAAiB,MAAM,mBAAmB,CAAC;AAAA,MAC7C;AACA,YAAM,EAAE,MAAM,MAAM,IAAI,MAAM,KAC3B,KAAK,OAAO,EACZ,OAAO,GAAG,EACV,OAAO,GAAG,EACV,OAAO;AACV,UAAI,OAAO;AACT,cAAM,IAAI,MAAM,eAAe,MAAM,OAAO,EAAE;AAAA,MAChD;AACA,aAAO;AAAA,IACT;AAAA,IAEA,MAAM,WAAW,IAAI,OAAO;AAC1B,YAAM,EAAE,MAAM,MAAM,IAAI,MAAM,KAC3B,KAAK,OAAO,EACZ,OAAO,KAAK,EACZ,GAAG,MAAM,EAAE,EACX,OAAO,GAAG,EACV,OAAO;AACV,UAAI,OAAO;AACT,cAAM,IAAI,MAAM,eAAe,MAAM,OAAO,EAAE;AAAA,MAChD;AACA,aAAO;AAAA,IACT;AAAA,IAEA,MAAM,WAAW,IAAI;AACnB,YAAM,EAAE,MAAM,IAAI,MAAM,KAAK,KAAK,OAAO,EAAE,OAAO,EAAE,GAAG,MAAM,EAAE;AAC/D,UAAI,OAAO;AACT,cAAM,IAAI,MAAM,eAAe,MAAM,OAAO,EAAE;AAAA,MAChD;AAAA,IACF;AAAA,IAEA,MAAM,sBAAsB,EAAE,SAAS,UAAU,QAAQ,OAAO,WAAW,GAAG;AAC5E,YAAM,YAAY,cAAc,MAAM;AACtC,YAAM,YAAY,mBAAmB,MAAM;AAM3C,YAAM,MAA+B;AAAA,QACnC;AAAA,QACA;AAAA,QACA,CAAC,SAAS,GAAG;AAAA,MACf;AACA,UAAI,eAAe,QAAW;AAG5B,YAAI,SAAS,IAAI,QAAQ,aAAa;AAAA,MACxC;AACA,YAAM,EAAE,MAAM,IAAI,MAAM,KACrB,KAAK,kBAAkB,EACvB,OAAO,KAAK,EAAE,YAAY,mBAAmB,CAAC;AACjD,UAAI,OAAO;AAKT,YACE,eAAe,UACf,yCAAyC,KAAK,MAAM,OAAO,GAC3D;AACA,gBAAM,cAAuC;AAAA,YAC3C;AAAA,YACA;AAAA,YACA,CAAC,SAAS,GAAG;AAAA,UACf;AACA,gBAAM,EAAE,OAAO,SAAS,IAAI,MAAM,KAC/B,KAAK,kBAAkB,EACvB,OAAO,aAAa,EAAE,YAAY,mBAAmB,CAAC;AACzD,cAAI,UAAU;AACZ,kBAAM,IAAI,MAAM,0BAA0B,SAAS,OAAO,EAAE;AAAA,UAC9D;AACA;AAAA,QACF;AACA,cAAM,IAAI,MAAM,0BAA0B,MAAM,OAAO,EAAE;AAAA,MAC3D;AAAA,IACF;AAAA,IAEA,MAAM,4BAA4B,QAAQ;AACxC,UAAI,OAAO,WAAW,GAAG;AACvB;AAAA,MACF;AAIA,YAAM,QAAQ,oBAAI,IAAqC;AACvD,iBAAW,KAAK,QAAQ;AACtB,cAAM,MAAM,GAAG,EAAE,OAAO,KAAK,EAAE,QAAQ;AACvC,cAAM,WAAW,MAAM,IAAI,GAAG,KAAK;AAAA,UACjC,SAAS,EAAE;AAAA,UACX,UAAU,EAAE;AAAA,QACd;AACA,iBAAS,cAAc,EAAE,MAAM,CAAC,IAAI,EAAE;AACtC,YAAI,EAAE,eAAe,QAAW;AAC9B,mBAAS,mBAAmB,EAAE,MAAM,CAAC,IAAI,EAAE,QAAQ,EAAE,aAAa;AAAA,QACpE;AACA,cAAM,IAAI,KAAK,QAAQ;AAAA,MACzB;AACA,YAAM,UAAU,MAAM,KAAK,MAAM,OAAO,CAAC;AACzC,YAAM,EAAE,MAAM,IAAI,MAAM,KACrB,KAAK,kBAAkB,EACvB,OAAO,SAAS,EAAE,YAAY,mBAAmB,CAAC;AACrD,UAAI,OAAO;AACT,cAAM,IAAI,MAAM,gCAAgC,MAAM,OAAO,EAAE;AAAA,MACjE;AAAA,IACF;AAAA,IAEA;AAAA,IAEA,MAAM,2BAA2B;AAI/B,YAAM,EAAE,MAAM,MAAM,IAAI,MAAM,KAC3B,KAAK,uBAAuB,EAC5B,OAAO,yCAAyC,EAChD,MAAM,mBAAmB,EAAE,WAAW,KAAK,CAAC;AAC/C,UAAI,OAAO;AAGT,YACE,yBAAyB,KAAK,MAAM,OAAO,KAC3C,kBAAkB,KAAK,MAAM,OAAO,GACpC;AACA,iBAAO,CAAC;AAAA,QACV;AACA,cAAM,IAAI,MAAM,6BAA6B,MAAM,OAAO,EAAE;AAAA,MAC9D;AACA,aAAQ,QAAQ,CAAC;AAAA,IACnB;AAAA,IAEA,MAAM,4BAA4B,QAAgB;AAChD,YAAM,EAAE,MAAM,MAAM,IAAI,MAAM,KAC3B,KAAK,2BAA2B,EAChC,OAAO,2BAA2B,EAClC,GAAG,WAAW,MAAM;AACvB,UAAI,OAAO;AAGT,YACE,6BAA6B,KAAK,MAAM,OAAO,KAC/C,kBAAkB,KAAK,MAAM,OAAO,GACpC;AACA,iBAAO,CAAC;AAAA,QACV;AACA,cAAM,IAAI,MAAM,gCAAgC,MAAM,OAAO,EAAE;AAAA,MACjE;AACA,aAAQ,QAAQ,CAAC;AAAA,IACnB;AAAA,IAEA,MAAM,0BAA0B,EAAE,SAAS,UAAU,QAAQ,SAAS,GAAG;AACvE,UAAI,UAAU;AACZ,cAAM,EAAE,OAAAA,OAAM,IAAI,MAAM,KACrB,KAAK,2BAA2B,EAChC;AAAA,UACC,EAAE,SAAS,UAAU,OAAO;AAAA,UAC5B,EAAE,YAAY,0BAA0B;AAAA,QAC1C;AACF,YAAIA,QAAO;AACT,gBAAM,IAAI,MAAM,sCAAsCA,OAAM,OAAO,EAAE;AAAA,QACvE;AACA;AAAA,MACF;AACA,YAAM,EAAE,MAAM,IAAI,MAAM,KACrB,KAAK,2BAA2B,EAChC,OAAO,EACP,GAAG,WAAW,OAAO,EACrB,GAAG,YAAY,QAAQ,EACvB,GAAG,UAAU,MAAM;AACtB,UAAI,OAAO;AACT,cAAM,IAAI,MAAM,sCAAsC,MAAM,OAAO,EAAE;AAAA,MACvE;AAAA,IACF;AAAA,IAEA,MAAM,sBAAsB,EAAE,SAAS,eAAe,KAAK,GAAG;AAC5D,YAAM,EAAE,MAAM,MAAM,IAAI,MAAM,KAAK,IAAI,2BAA2B;AAAA,QAChE,WAAW;AAAA,QACX,gBAAgB;AAAA,MAClB,CAAC;AACD,UAAI,OAAO;AACT,cAAM,IAAI,MAAM,0BAA0B,MAAM,OAAO,EAAE;AAAA,MAC3D;AACA,UAAI,OAAO,SAAS,SAAU,QAAO;AACrC,aAAO,OAAO,QAAQ,CAAC;AAAA,IACzB;AAAA,IAEA,MAAM,gBAAgB;AACpB,YAAM,EAAE,MAAM,MAAM,IAAI,MAAM,KAC3B,KAAK,WAAW,EAChB,OAAO,GAAG,EACV,MAAM,QAAQ,EAAE,WAAW,KAAK,CAAC;AACpC,UAAI,OAAO;AACT,cAAM,IAAI,MAAM,kBAAkB,MAAM,OAAO,EAAE;AAAA,MACnD;AACA,aAAQ,QAAQ,CAAC;AAAA,IACnB;AAAA,IAEA,MAAM,cAAc,OAAO;AACzB,YAAM,EAAE,MAAM,MAAM,IAAI,MAAM,KAC3B,KAAK,WAAW,EAChB,OAAO;AAAA,QACN,MAAM,MAAM;AAAA,QACZ,MAAM,MAAM,QAAQ;AAAA,QACpB,MAAM,MAAM,QAAQ;AAAA,MACtB,CAAC,EACA,OAAO,GAAG,EACV,OAAO;AACV,UAAI,OAAO;AACT,cAAM,IAAI,MAAM,kBAAkB,MAAM,OAAO,EAAE;AAAA,MACnD;AACA,aAAO;AAAA,IACT;AAAA,IAEA,MAAM,mBAAmB,WAAW;AAKlC,YAAM,EAAE,MAAM,MAAM,IAAI,MAAM,KAC3B,KAAK,oBAAoB,EACzB,OAAO,+BAA+B,EACtC,GAAG,cAAc,SAAS;AAC7B,UAAI,OAAO;AACT,cAAM,IAAI,MAAM,uBAAuB,MAAM,OAAO,EAAE;AAAA,MACxD;AACA,YAAM,UAAU,oBAAI,IAAyB;AAC7C,iBAAW,OAAQ,QAAQ,CAAC,GAIxB;AACF,cAAM,WAAW,QAAQ,IAAI,IAAI,OAAO;AACxC,YAAI,UAAU;AACZ,mBAAS,SAAS,KAAK,IAAI,OAAO;AAAA,QACpC,OAAO;AACL,kBAAQ,IAAI,IAAI,SAAS;AAAA,YACvB,SAAS,IAAI;AAAA,YACb,OAAO;AAAA,YACP,WAAW;AAAA,YACX,UAAU,CAAC,IAAI,OAAO;AAAA,YACtB,YAAY,IAAI;AAAA,YAChB,mBAAmB;AAAA,UACrB,CAAC;AAAA,QACH;AAAA,MACF;AACA,aAAO,MAAM,KAAK,QAAQ,OAAO,CAAC;AAAA,IACpC;AAAA,IAEA,MAAM,oBAAoB,EAAE,WAAW,OAAO,QAAQ,GAAG;AACvD,YAAM,EAAE,MAAM,IAAI,MAAM,GAAG,KAAK,MAAM,kBAAkB,OAAO;AAAA,QAC7D,MAAM;AAAA,UACJ,iBAAiB;AAAA,UACjB,eAAe;AAAA,QACjB;AAAA,QACA,YAAY,KAAK;AAAA,MACnB,CAAC;AACD,UAAI,OAAO;AACT,cAAM,IAAI,MAAM,wBAAwB,MAAM,OAAO,EAAE;AAAA,MACzD;AACA,aAAO,EAAE,SAAS,KAAK;AAAA,IACzB;AAAA,EACF;AACF;;;AC1cA,SAAS,eAAe,aAAa,YAAY,WAAW,SAAS,gBAAgB;AA2BjF;AATJ,IAAM,wBAAwB,cAAqC,IAAI;AAOhE,SAAS,uBAAuB,OAAoC;AACzE,SACE,oBAAC,sBAAsB,UAAtB,EAA+B,OAAO,MAAM,WAC1C,gBAAM,UACT;AAEJ;AAEA,SAAS,oBAAoC;AAC3C,QAAM,IAAI,WAAW,qBAAqB;AAC1C,MAAI,CAAC,GAAG;AACN,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAaA,SAAS,SAAY,QAA0B,MAA8B;AAC3E,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAwB;AAAA,IAChD,MAAM;AAAA,IACN,WAAW;AAAA,IACX,OAAO;AAAA,EACT,CAAC;AAED,QAAM,UAAU,YAAY,YAAY;AACtC,aAAS,CAAC,OAAO,EAAE,GAAG,GAAG,WAAW,MAAM,OAAO,KAAK,EAAE;AACxD,QAAI;AACF,YAAM,OAAO,MAAM,OAAO;AAC1B,eAAS,EAAE,MAAM,WAAW,OAAO,OAAO,KAAK,CAAC;AAAA,IAClD,SAAS,GAAG;AACV,eAAS;AAAA,QACP,MAAM;AAAA,QACN,WAAW;AAAA,QACX,OAAO,aAAa,QAAQ,IAAI,IAAI,MAAM,OAAO,CAAC,CAAC;AAAA,MACrD,CAAC;AAAA,IACH;AAAA,EAEF,GAAG,IAAI;AAEP,YAAU,MAAM;AACd,SAAK,QAAQ;AAAA,EACf,GAAG,CAAC,OAAO,CAAC;AAEZ,SAAO,EAAE,GAAG,OAAO,QAAQ;AAC7B;AAMO,SAAS,cAAc,MAI3B;AACD,QAAM,YAAY,kBAAkB;AACpC,SAAO;AAAA,IACL,MAAM,UAAU,UAAU,IAAI;AAAA,IAC9B,CAAC,WAAW,KAAK,OAAO,KAAK,WAAW,KAAK,aAAa;AAAA,EAC5D;AACF;AAEO,SAAS,wBAAwB,QAAuB;AAC7D,QAAM,YAAY,kBAAkB;AACpC,SAAO;AAAA,IACL,YACE,UAAU,OAAO,CAAC,IAAI,UAAU,oBAAoB,MAAM;AAAA,IAC5D,CAAC,WAAW,MAAM;AAAA,EACpB;AACF;AAEO,SAAS,oBAAoB;AAClC,QAAM,YAAY,kBAAkB;AACpC,SAAO,SAAS,MAAM,UAAU,cAAc,GAAG,CAAC,SAAS,CAAC;AAC9D;AAEO,SAAS,uBAAuB,WAA0B;AAC/D,QAAM,YAAY,kBAAkB;AACpC,SAAO;AAAA,IACL,YACE,aAAa,OAAO,CAAC,IAAI,UAAU,mBAAmB,SAAS;AAAA,IACjE,CAAC,WAAW,SAAS;AAAA,EACvB;AACF;AAYA,SAAS,YACP,IACA;AACA,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAwB;AAAA,IAChD,WAAW;AAAA,IACX,OAAO;AAAA,EACT,CAAC;AAED,QAAM,SAAS;AAAA,IACb,UAAU,SAAkC;AAC1C,eAAS,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AACzC,UAAI;AACF,cAAM,SAAS,MAAM,GAAG,GAAG,IAAI;AAC/B,iBAAS,EAAE,WAAW,OAAO,OAAO,KAAK,CAAC;AAC1C,eAAO;AAAA,MACT,SAAS,GAAG;AACV,cAAM,MAAM,aAAa,QAAQ,IAAI,IAAI,MAAM,OAAO,CAAC,CAAC;AACxD,iBAAS,EAAE,WAAW,OAAO,OAAO,IAAI,CAAC;AACzC,cAAM;AAAA,MACR;AAAA,IACF;AAAA;AAAA,IAEA,CAAC,EAAE;AAAA,EACL;AAEA,SAAO,EAAE,QAAQ,GAAG,MAAM;AAC5B;AAEO,SAAS,gBAAgB;AAC9B,QAAM,YAAY,kBAAkB;AACpC,SAAO,YAAY,UAAU,UAAU;AACzC;AAEO,SAAS,gBAAgB;AAC9B,QAAM,YAAY,kBAAkB;AACpC,SAAO,YAAY,UAAU,UAAU;AACzC;AAEO,SAAS,gBAAgB;AAC9B,QAAM,YAAY,kBAAkB;AACpC,SAAO,YAAY,UAAU,UAAU;AACzC;AAEO,SAAS,2BAA2B;AACzC,QAAM,YAAY,kBAAkB;AACpC,SAAO,YAAY,UAAU,qBAAqB;AACpD;AAEO,SAAS,2BAA2B;AACzC,QAAM,YAAY,kBAAkB;AACpC,SAAO,YAAY,UAAU,qBAAqB;AACpD;AAOO,SAAS,+BAA+B;AAC7C,QAAM,YAAY,kBAAkB;AACpC,SAAO;AAAA,IACL,MAAM,UAAU,yBAAyB;AAAA,IACzC,CAAC,SAAS;AAAA,EACZ;AACF;AAeO,SAAS,2BAA2B,QAAuB;AAChE,QAAM,YAAY,kBAAkB;AACpC,QAAM,CAAC,WAAW,YAAY,IAAI,SAAsB,MAAM,oBAAI,IAAY,CAAC;AAC/E,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAuB,IAAI;AAErD,QAAM,iBAAiB,YAAY,YAAY;AAC7C,QAAI,CAAC,QAAQ;AACX,mBAAa,oBAAI,IAAY,CAAC;AAC9B;AAAA,IACF;AACA,QAAI;AACF,YAAM,OAAO,MAAM,UAAU,4BAA4B,MAAM;AAC/D,mBAAa,IAAI,IAAI,KAAK,IAAI,CAAC,MAAM,GAAG,EAAE,QAAQ,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;AAClE,eAAS,IAAI;AAAA,IACf,SAAS,GAAG;AACV,eAAS,aAAa,QAAQ,IAAI,IAAI,MAAM,OAAO,CAAC,CAAC,CAAC;AAAA,IACxD;AAAA,EACF,GAAG,CAAC,WAAW,MAAM,CAAC;AAEtB,YAAU,MAAM;AACd,SAAK,eAAe;AAAA,EACtB,GAAG,CAAC,cAAc,CAAC;AAEnB,QAAM,cAAc;AAAA,IAClB,OAAO,UAAkB,QAAgB,aAAsB;AAC7D,UAAI,CAAC,QAAQ;AACX;AAAA,MACF;AACA,YAAM,MAAM,GAAG,QAAQ,IAAI,MAAM;AAEjC,mBAAa,CAAC,SAAS;AACrB,cAAM,OAAO,IAAI,IAAI,IAAI;AACzB,YAAI,UAAU;AACZ,eAAK,IAAI,GAAG;AAAA,QACd,OAAO;AACL,eAAK,OAAO,GAAG;AAAA,QACjB;AACA,eAAO;AAAA,MACT,CAAC;AACD,UAAI;AACF,cAAM,UAAU,0BAA0B;AAAA,UACxC,SAAS;AAAA,UACT;AAAA,UACA;AAAA,UACA;AAAA,QACF,CAAC;AAAA,MACH,SAAS,GAAG;AACV,iBAAS,aAAa,QAAQ,IAAI,IAAI,MAAM,OAAO,CAAC,CAAC,CAAC;AAAA,MACxD;AAGA,WAAK,eAAe;AAAA,IACtB;AAAA,IACA,CAAC,WAAW,QAAQ,cAAc;AAAA,EACpC;AAEA,SAAO,EAAE,WAAW,aAAa,OAAO,SAAS,eAAe;AAClE;AAEO,SAAS,mBAAmB;AACjC,QAAM,YAAY,kBAAkB;AACpC,SAAO,YAAY,UAAU,aAAa;AAC5C;AAEO,SAAS,yBAAyB;AACvC,QAAM,YAAY,kBAAkB;AACpC,SAAO,YAAY,UAAU,mBAAmB;AAClD;AAgCO,SAAS,sBAAsB,QAAuB;AAC3D,QAAM,EAAE,MAAM,WAAW,OAAO,QAAQ,IAAI,wBAAwB,MAAM;AAC1E,QAAM,eAAe,6BAA6B;AAClD,QAAM,gBAAgB,2BAA2B,MAAM;AACvD,QAAM,UAAU,yBAAyB;AACzC,QAAM,YAAY,kBAAkB;AAOpC,QAAM,OAAO,QAA4B,MAAM;AAC7C,UAAM,MAA0B,CAAC;AACjC,eAAW,OAAO,QAAQ,CAAC,GAAG;AAC5B,UAAI,IAAI,QAAQ,IAAI;AAAA,QAClB,MAAM,IAAI;AAAA,QACV,OAAO,IAAI;AAAA,QACX,QAAQ,IAAI;AAAA,QACZ,QAAQ,IAAI;AAAA,MACd;AAAA,IACF;AACA,WAAO;AAAA,EACT,GAAG,CAAC,IAAI,CAAC;AAKT,QAAM,iBAAiB,QAAQ,MAAM;AACnC,UAAM,MAAM,oBAAI,IAGd;AACF,eAAW,QAAQ,aAAa,QAAQ,CAAC,GAAG;AAC1C,YAAM,OAAO,IAAI,IAAI,KAAK,cAAc,KAAK,CAAC;AAC9C,UAAI,IAAI,KAAK,gBAAgB;AAAA,QAC3B,GAAG;AAAA,QACH,EAAE,QAAQ,KAAK,iBAAiB,QAAQ,KAAK,OAAO;AAAA,MACtD,CAAC;AAAA,IACH;AACA,WAAO;AAAA,EACT,GAAG,CAAC,aAAa,IAAI,CAAC;AAEtB,QAAM,aAAa,QAAkC,MAAM;AACzD,UAAM,MAAgC,CAAC;AACvC,UAAM,YAAY,IAAI,IAAY,OAAO,KAAK,IAAI,CAAC;AACnD,eAAW,SAAS,eAAe,KAAK,GAAG;AACzC,gBAAU,IAAI,KAAK;AAAA,IACrB;AACA,UAAM,YAAY,cAAc;AAChC,eAAW,YAAY,WAAW;AAChC,YAAM,UAAU,KAAK,QAAQ;AAC7B,YAAM,cAAgD;AAAA,QACpD,MAAM;AAAA,QACN,OAAO;AAAA,QACP,QAAQ;AAAA,QACR,QAAQ;AAAA,MACV;AACA,iBAAW,UAAU,SAAS;AAG5B,YAAI,UAAU,IAAI,GAAG,QAAQ,IAAI,MAAM,EAAE,GAAG;AAC1C,sBAAY,MAAM,IAAI;AACtB;AAAA,QACF;AACA,YAAI,UAAU,MAAM,GAAG;AACrB,sBAAY,MAAM,IAAI;AACtB;AAAA,QACF;AACA,cAAM,UAAU,eAAe,IAAI,QAAQ,KAAK,CAAC;AACjD,cAAM,cAAc,QAAQ;AAAA,UAC1B,CAAC,MAAM,EAAE,WAAW,UAAU,KAAK,EAAE,MAAM,IAAI,MAAM,MAAM;AAAA,QAC7D;AACA,oBAAY,MAAM,IAAI,cAAc,YAAY,SAAS;AAAA,MAC3D;AACA,UAAI,QAAQ,IAAI;AAAA,IAClB;AACA,WAAO;AAAA,EACT,GAAG,CAAC,MAAM,gBAAgB,cAAc,SAAS,CAAC;AAElD,QAAM,aAAa;AAAA,IACjB,OAAO,UAAkB,QAAgB,UAAmB;AAC1D,UAAI,CAAC,QAAQ;AACX;AAAA,MACF;AAMA,YAAM,QAAQ,OAAO;AAAA,QACnB,SAAS;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,QACA,YAAY;AAAA,MACd,CAAC;AACD,WAAK,QAAQ;AAAA,IACf;AAAA,IACA,CAAC,QAAQ,SAAS,OAAO;AAAA,EAC3B;AAMA,OAAK;AAEL,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA;AAAA,IAEA,WAAW,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOzB,aAAa,cAAc;AAAA,IAC3B,WAAW,aAAa,aAAa;AAAA,IACrC,OAAO,SAAS,aAAa,SAAS,cAAc;AAAA,IACpD;AAAA,IACA;AAAA,IACA,YAAY,QAAQ;AAAA,IACpB,aAAa,QAAQ;AAAA,EACvB;AACF;AAEA,IAAM,UAAiC,CAAC,QAAQ,SAAS,UAAU,QAAQ;;;AC3Z3E,SAAS,WAAAC,gBAAe;AAwHpB,0BAAAC,YAAA;AArDJ,IAAMC,WAAU,CAAC,QAAQ,SAAS,UAAU,QAAQ;AAE7C,SAAS,kBAAkB,OAA+B;AAC/D,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,aAAa;AAAA,EACf,IAAI,sBAAsB,MAAM,MAAM;AAEtC,QAAM,SAASC;AAAA,IACb,MAAM,eAAe,MAAM,SAAS;AAAA,IACpC,CAAC,MAAM,SAAS;AAAA,EAClB;AAMA,QAAM,gBAAgB,CAAC,UAAkB,WAA4B;AACnE,UAAM,SAAS,WAAW,QAAQ,IAAI,MAAM;AAC5C,QAAI,UAAU,QAAQ,WAAW,YAAY;AAC3C,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT;AAEA,QAAM,aAAa,CACjB,UACA,WACmC;AACnC,UAAM,SAAS,WAAW,QAAQ,IAAI,MAAM;AAG5C,WAAO,UAAU,OAAO,WAAW;AAAA,EACrC;AAEA,QAAM,UAAU,OAAO,UAAkB,QAAgB,UAAmB;AAC1E,UAAM,WAAW,UAAU,QAAQ,KAAK;AAAA,EAC1C;AAEA,QAAM,cAAc,OAClB,UACA,QACA,aACG;AACH,UAAM,gBAAgB,UAAU,QAAQ,QAAQ;AAAA,EAClD;AAEA,SACE,gBAAAF,KAAA,YACG,gBAAM,SAAS;AAAA,IACd;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,SAASC;AAAA,EACX,CAAC,GACH;AAEJ;;;AC1KA,SAAS,eAAAE,cAAa,YAAAC,iBAAgB;AAwFlC,qBAAAC,WAAA,OAAAC,YAAA;AA9CG,SAAS,UAAU,OAAuB;AAC/C,QAAM,EAAE,OAAO,WAAW,kBAAkB,KAAK,IAAI;AAErD,QAAM,OAAO,cAAc,EAAE,OAAO,UAAU,CAAC;AAC/C,QAAM,SAAS,cAAc;AAC7B,QAAM,SAAS,cAAc;AAE7B,QAAM,CAAC,gBAAgB,iBAAiB,IAAIC,UAAwB,IAAI;AAGxE,MACE,mBACA,kBAAkB,QAClB,KAAK,QAAQ,QACb,KAAK,KAAK,SAAS,GACnB;AACA,sBAAkB,KAAK,KAAK,CAAC,EAAG,EAAE;AAAA,EACpC;AAEA,QAAM,aAAaC;AAAA,IACjB,OAAO,UAAkD;AACvD,YAAM,OAAO,MAAM,OAAO,OAAO;AAAA,QAC/B;AAAA,QACA,WAAW,aAAa;AAAA,QACxB,MAAM,MAAM;AAAA,QACZ,aAAa,MAAM;AAAA,MACrB,CAAC;AACD,YAAM,KAAK,QAAQ;AACnB,wBAAkB,KAAK,EAAE;AACzB,aAAO;AAAA,IACT;AAAA,IACA,CAAC,QAAQ,OAAO,WAAW,IAAI;AAAA,EACjC;AAEA,QAAM,aAAaA;AAAA,IACjB,OAAO,OAAe;AACpB,YAAM,OAAO,OAAO,EAAE;AACtB,UAAI,mBAAmB,IAAI;AACzB,0BAAkB,IAAI;AAAA,MACxB;AACA,YAAM,KAAK,QAAQ;AAAA,IACrB;AAAA,IACA,CAAC,QAAQ,MAAM,cAAc;AAAA,EAC/B;AAEA,SACE,gBAAAF,KAAAD,WAAA,EACG,gBAAM,SAAS;AAAA,IACd,OAAO,KAAK,QAAQ,CAAC;AAAA,IACrB,WAAW,KAAK;AAAA,IAChB,OAAO,KAAK;AAAA,IACZ;AAAA,IACA,YAAY;AAAA,IACZ;AAAA,IACA,YAAY,OAAO;AAAA,IACnB,aAAa,OAAO;AAAA,IACpB;AAAA,IACA,YAAY,OAAO;AAAA,IACnB,aAAa,OAAO;AAAA,IACpB,SAAS,KAAK;AAAA,EAChB,CAAC,GACH;AAEJ;;;ACxGA,SAAS,eAAAI,cAAa,YAAAC,iBAAgB;AA4FlC,qBAAAC,WAAA,OAAAC,YAAA;AAxDG,SAAS,iBAAiB,OAA8B;AAC7D,QAAM,aAAa,cAAc;AAAA,IAC/B,OAAO;AAAA,IACP,WAAW,MAAM;AAAA,EACnB,CAAC;AACD,QAAM,SAAS,uBAAuB;AAEtC,QAAM,CAAC,OAAO,QAAQ,IAAIC,UAAS,EAAE;AACrC,QAAM,CAAC,iBAAiB,kBAAkB,IAAIA;AAAA,IAC5C,oBAAI,IAAI;AAAA,EACV;AACA,QAAM,CAAC,uBAAuB,wBAAwB,IAAIA,UAAS,KAAK;AAExE,QAAM,aAAaC,aAAY,CAAC,WAAmB;AACjD,uBAAmB,CAAC,SAAS;AAC3B,YAAM,OAAO,IAAI,IAAI,IAAI;AACzB,UAAI,KAAK,IAAI,MAAM,GAAG;AACpB,aAAK,OAAO,MAAM;AAAA,MACpB,OAAO;AACL,aAAK,IAAI,MAAM;AAAA,MACjB;AACA,aAAO;AAAA,IACT,CAAC;AAAA,EACH,GAAG,CAAC,CAAC;AAEL,QAAM,YAAYA,aAAY,MAAM;AAClC,aAAS,EAAE;AACX,uBAAmB,oBAAI,IAAI,CAAC;AAC5B,6BAAyB,KAAK;AAAA,EAChC,GAAG,CAAC,CAAC;AAEL,QAAM,SAA+C,CAAC;AACtD,MAAI,MAAM,KAAK,KAAK,CAAC,6BAA6B,KAAK,MAAM,KAAK,CAAC,GAAG;AACpE,WAAO,QAAQ;AAAA,EACjB;AACA,MAAI,gBAAgB,SAAS,GAAG;AAC9B,WAAO,QAAQ;AAAA,EACjB;AACA,QAAM,UACJ,MAAM,KAAK,EAAE,SAAS,KACtB,OAAO,KAAK,MAAM,EAAE,WAAW;AAEjC,QAAM,SAASA,aAAY,YAAY;AACrC,QAAI,CAAC,SAAS;AACZ;AAAA,IACF;AACA,UAAM,OAAO,OAAO;AAAA,MAClB,WAAW,MAAM;AAAA,MACjB,OAAO,MAAM,KAAK;AAAA,MAClB,SAAS,MAAM,KAAK,eAAe;AAAA,IACrC,CAAC;AACD,6BAAyB,IAAI;AAC7B,UAAM,YAAY;AAAA,EACpB,GAAG,CAAC,QAAQ,OAAO,OAAO,iBAAiB,OAAO,CAAC;AAEnD,SACE,gBAAAF,KAAAD,WAAA,EACG,gBAAM,SAAS;AAAA,IACd;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,OAAO,WAAW,QAAQ,CAAC;AAAA,IAC3B,cAAc,WAAW;AAAA,IACzB,YAAY,WAAW;AAAA,IACvB;AAAA,IACA,cAAc,OAAO;AAAA,IACrB,aAAa,OAAO;AAAA,IACpB;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC,GACH;AAEJ;","names":["error","useMemo","jsx","ACTIONS","useMemo","useCallback","useState","Fragment","jsx","useState","useCallback","useCallback","useState","Fragment","jsx","useState","useCallback"]}
|
package/package.json
CHANGED