snipe-auth-rbac 0.5.0 → 0.6.0
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 +236 -59
package/dist/admin/index.cjs
CHANGED
|
@@ -37,6 +37,7 @@ __export(admin_exports, {
|
|
|
37
37
|
useDeleteRole: () => useDeleteRole,
|
|
38
38
|
useInviteCompanyMember: () => useInviteCompanyMember,
|
|
39
39
|
useRolePermissionGrid: () => useRolePermissionGrid,
|
|
40
|
+
useRolePermissionOverrides: () => useRolePermissionOverrides,
|
|
40
41
|
useSetRolePermissionCell: () => useSetRolePermissionCell,
|
|
41
42
|
useUpdateRole: () => useUpdateRole
|
|
42
43
|
});
|
|
@@ -225,6 +226,32 @@ function createSupabaseAdminClient(opts) {
|
|
|
225
226
|
}
|
|
226
227
|
return data ?? [];
|
|
227
228
|
},
|
|
229
|
+
async listRolePermissionOverrides(roleId) {
|
|
230
|
+
const { data, error } = await rbac.from("role_permission_overrides").select("role_id, resource, action").eq("role_id", roleId);
|
|
231
|
+
if (error) {
|
|
232
|
+
if (/role_permission_overrides/i.test(error.message) && /does not exist/i.test(error.message)) {
|
|
233
|
+
return [];
|
|
234
|
+
}
|
|
235
|
+
throw new Error(`listRolePermissionOverrides: ${error.message}`);
|
|
236
|
+
}
|
|
237
|
+
return data ?? [];
|
|
238
|
+
},
|
|
239
|
+
async setRolePermissionOverride({ role_id, resource, action, suppress }) {
|
|
240
|
+
if (suppress) {
|
|
241
|
+
const { error: error2 } = await rbac.from("role_permission_overrides").upsert(
|
|
242
|
+
{ role_id, resource, action },
|
|
243
|
+
{ onConflict: "role_id,resource,action" }
|
|
244
|
+
);
|
|
245
|
+
if (error2) {
|
|
246
|
+
throw new Error(`setRolePermissionOverride(insert): ${error2.message}`);
|
|
247
|
+
}
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
const { error } = await rbac.from("role_permission_overrides").delete().eq("role_id", role_id).eq("resource", resource).eq("action", action);
|
|
251
|
+
if (error) {
|
|
252
|
+
throw new Error(`setRolePermissionOverride(delete): ${error.message}`);
|
|
253
|
+
}
|
|
254
|
+
},
|
|
228
255
|
async applyTemplateDefaults({ role_id, only_missing = true }) {
|
|
229
256
|
const { data, error } = await rbac.rpc("apply_template_defaults", {
|
|
230
257
|
p_role_id: role_id,
|
|
@@ -408,6 +435,57 @@ function useAdminResourceDependencies() {
|
|
|
408
435
|
[transport]
|
|
409
436
|
);
|
|
410
437
|
}
|
|
438
|
+
function useRolePermissionOverrides(roleId) {
|
|
439
|
+
const transport = useAdminTransport();
|
|
440
|
+
const [overrides, setOverrides] = (0, import_react.useState)(() => /* @__PURE__ */ new Set());
|
|
441
|
+
const [error, setError] = (0, import_react.useState)(null);
|
|
442
|
+
const fetchOverrides = (0, import_react.useCallback)(async () => {
|
|
443
|
+
if (!roleId) {
|
|
444
|
+
setOverrides(/* @__PURE__ */ new Set());
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
try {
|
|
448
|
+
const rows = await transport.listRolePermissionOverrides(roleId);
|
|
449
|
+
setOverrides(new Set(rows.map((r) => `${r.resource}:${r.action}`)));
|
|
450
|
+
setError(null);
|
|
451
|
+
} catch (e) {
|
|
452
|
+
setError(e instanceof Error ? e : new Error(String(e)));
|
|
453
|
+
}
|
|
454
|
+
}, [transport, roleId]);
|
|
455
|
+
(0, import_react.useEffect)(() => {
|
|
456
|
+
void fetchOverrides();
|
|
457
|
+
}, [fetchOverrides]);
|
|
458
|
+
const setOverride = (0, import_react.useCallback)(
|
|
459
|
+
async (resource, action, suppress) => {
|
|
460
|
+
if (!roleId) {
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
const key = `${resource}:${action}`;
|
|
464
|
+
setOverrides((prev) => {
|
|
465
|
+
const next = new Set(prev);
|
|
466
|
+
if (suppress) {
|
|
467
|
+
next.add(key);
|
|
468
|
+
} else {
|
|
469
|
+
next.delete(key);
|
|
470
|
+
}
|
|
471
|
+
return next;
|
|
472
|
+
});
|
|
473
|
+
try {
|
|
474
|
+
await transport.setRolePermissionOverride({
|
|
475
|
+
role_id: roleId,
|
|
476
|
+
resource,
|
|
477
|
+
action,
|
|
478
|
+
suppress
|
|
479
|
+
});
|
|
480
|
+
} catch (e) {
|
|
481
|
+
setError(e instanceof Error ? e : new Error(String(e)));
|
|
482
|
+
}
|
|
483
|
+
void fetchOverrides();
|
|
484
|
+
},
|
|
485
|
+
[transport, roleId, fetchOverrides]
|
|
486
|
+
);
|
|
487
|
+
return { overrides, setOverride, error, refresh: fetchOverrides };
|
|
488
|
+
}
|
|
411
489
|
function useCreateCompany() {
|
|
412
490
|
const transport = useAdminTransport();
|
|
413
491
|
return useMutation(transport.createCompany);
|
|
@@ -419,6 +497,7 @@ function useInviteCompanyMember() {
|
|
|
419
497
|
function useRolePermissionGrid(roleId) {
|
|
420
498
|
const { data, isLoading, error, refresh } = useAdminRolePermissions(roleId);
|
|
421
499
|
const dependencies = useAdminResourceDependencies();
|
|
500
|
+
const overridesHook = useRolePermissionOverrides(roleId);
|
|
422
501
|
const setCell = useSetRolePermissionCell();
|
|
423
502
|
const transport = useAdminTransport();
|
|
424
503
|
const grid = (0, import_react.useMemo)(() => {
|
|
@@ -450,6 +529,7 @@ function useRolePermissionGrid(roleId) {
|
|
|
450
529
|
for (const child of parentsByChild.keys()) {
|
|
451
530
|
resources.add(child);
|
|
452
531
|
}
|
|
532
|
+
const overrides = overridesHook.overrides;
|
|
453
533
|
for (const resource of resources) {
|
|
454
534
|
const directs = grid[resource];
|
|
455
535
|
const cellOrigins = {
|
|
@@ -459,6 +539,10 @@ function useRolePermissionGrid(roleId) {
|
|
|
459
539
|
delete: null
|
|
460
540
|
};
|
|
461
541
|
for (const action of ACTIONS) {
|
|
542
|
+
if (overrides.has(`${resource}:${action}`)) {
|
|
543
|
+
cellOrigins[action] = "override";
|
|
544
|
+
continue;
|
|
545
|
+
}
|
|
462
546
|
if (directs?.[action]) {
|
|
463
547
|
cellOrigins[action] = "direct";
|
|
464
548
|
continue;
|
|
@@ -472,7 +556,7 @@ function useRolePermissionGrid(roleId) {
|
|
|
472
556
|
out[resource] = cellOrigins;
|
|
473
557
|
}
|
|
474
558
|
return out;
|
|
475
|
-
}, [grid, parentsByChild]);
|
|
559
|
+
}, [grid, parentsByChild, overridesHook.overrides]);
|
|
476
560
|
const updateCell = (0, import_react.useCallback)(
|
|
477
561
|
async (resource, action, value) => {
|
|
478
562
|
if (!roleId) {
|
|
@@ -494,8 +578,17 @@ function useRolePermissionGrid(roleId) {
|
|
|
494
578
|
grid,
|
|
495
579
|
originGrid,
|
|
496
580
|
parentsByChild,
|
|
581
|
+
/** 0.6.0+. Set of `"<resource>:<action>"` for this role's overrides. */
|
|
582
|
+
overrides: overridesHook.overrides,
|
|
583
|
+
/**
|
|
584
|
+
* 0.6.0+. Suppress (`suppress=true`) or restore (`suppress=false`)
|
|
585
|
+
* an implied permission for this role. The grid + originGrid
|
|
586
|
+
* re-render with `'override'` state on the cell as soon as the
|
|
587
|
+
* optimistic flip lands.
|
|
588
|
+
*/
|
|
589
|
+
setOverride: overridesHook.setOverride,
|
|
497
590
|
isLoading: isLoading || dependencies.isLoading,
|
|
498
|
-
error: error ?? dependencies.error,
|
|
591
|
+
error: error ?? dependencies.error ?? overridesHook.error,
|
|
499
592
|
refresh,
|
|
500
593
|
updateCell,
|
|
501
594
|
isUpdating: setCell.isPending,
|
|
@@ -526,16 +619,25 @@ function groupResources(registry) {
|
|
|
526
619
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
527
620
|
var ACTIONS2 = ["read", "write", "update", "delete"];
|
|
528
621
|
function PermissionsMatrix(props) {
|
|
529
|
-
const {
|
|
622
|
+
const {
|
|
623
|
+
grid,
|
|
624
|
+
originGrid,
|
|
625
|
+
isLoading,
|
|
626
|
+
error,
|
|
627
|
+
updateCell,
|
|
628
|
+
isUpdating,
|
|
629
|
+
setOverride: gridSetOverride
|
|
630
|
+
} = useRolePermissionGrid(props.roleId);
|
|
530
631
|
const groups = (0, import_react2.useMemo)(
|
|
531
632
|
() => groupResources(props.resources),
|
|
532
633
|
[props.resources]
|
|
533
634
|
);
|
|
534
635
|
const isCellEnabled = (resource, action) => {
|
|
535
|
-
|
|
536
|
-
|
|
636
|
+
const origin = originGrid[resource]?.[action];
|
|
637
|
+
if (origin == null || origin === "override") {
|
|
638
|
+
return false;
|
|
537
639
|
}
|
|
538
|
-
return
|
|
640
|
+
return true;
|
|
539
641
|
};
|
|
540
642
|
const cellOrigin = (resource, action) => {
|
|
541
643
|
const origin = originGrid[resource]?.[action];
|
|
@@ -544,11 +646,15 @@ function PermissionsMatrix(props) {
|
|
|
544
646
|
const setCell = async (resource, action, value) => {
|
|
545
647
|
await updateCell(resource, action, value);
|
|
546
648
|
};
|
|
649
|
+
const setOverride = async (resource, action, suppress) => {
|
|
650
|
+
await gridSetOverride(resource, action, suppress);
|
|
651
|
+
};
|
|
547
652
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: props.children({
|
|
548
653
|
groups,
|
|
549
654
|
isCellEnabled,
|
|
550
655
|
cellOrigin,
|
|
551
656
|
setCell,
|
|
657
|
+
setOverride,
|
|
552
658
|
isLoading,
|
|
553
659
|
isUpdating,
|
|
554
660
|
error,
|
|
@@ -694,6 +800,7 @@ function InviteMemberForm(props) {
|
|
|
694
800
|
useDeleteRole,
|
|
695
801
|
useInviteCompanyMember,
|
|
696
802
|
useRolePermissionGrid,
|
|
803
|
+
useRolePermissionOverrides,
|
|
697
804
|
useSetRolePermissionCell,
|
|
698
805
|
useUpdateRole
|
|
699
806
|
});
|
package/dist/admin/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/admin/index.ts","../../src/admin/transport.ts","../../src/admin/hooks.tsx","../../src/admin/PermissionsMatrix.tsx","../../src/client.ts","../../src/admin/RolesList.tsx","../../src/admin/InviteMemberForm.tsx"],"sourcesContent":["/**\n * Admin entry — import from `snipe-auth-rbac/admin`.\n *\n * Two layers shipped here, all UI-kit-agnostic:\n *\n * 1. **Transport + hooks.** Pick `createSupabaseAdminClient(...)`\n * or implement `AdminTransport` yourself, then mount\n * `<AdminTransportProvider>` and pull data with the hooks.\n * 2. **Headless render-prop components.** `<PermissionsMatrix>`,\n * `<RolesList>`, `<InviteMemberForm>` own state + mutations\n * and hand the consumer a render-prop with everything needed\n * to draw the UI in any design system.\n *\n * For a styled reference (Tailwind + Radix shadcn primitives) see\n * `examples/react-admin/` — copy the page into your project, swap\n * the imports for your local UI kit, ship.\n */\n\nexport type {\n AdminCompany,\n AdminMember,\n AdminResourceDependency,\n AdminRole,\n AdminRolePermission,\n AdminTransport,\n} from \"./types.js\";\n\nexport {\n createSupabaseAdminClient,\n extractResourceDependencies,\n type SupabaseAdminClientOptions,\n} from \"./transport.js\";\n\nexport {\n AdminTransportProvider,\n type AdminTransportProviderProps,\n useAdminRoles,\n useAdminRolePermissions,\n useAdminCompanies,\n useAdminCompanyMembers,\n useAdminResourceDependencies,\n useCreateRole,\n useUpdateRole,\n useDeleteRole,\n useSetRolePermissionCell,\n useApplyTemplateDefaults,\n useCreateCompany,\n useInviteCompanyMember,\n useRolePermissionGrid,\n type RolePermissionGrid,\n type RolePermissionOriginGrid,\n} from \"./hooks.js\";\n\nexport {\n PermissionsMatrix,\n type PermissionsMatrixProps,\n type MatrixGroup,\n type MatrixRenderArgs,\n} from \"./PermissionsMatrix.js\";\n\nexport {\n RolesList,\n type RolesListProps,\n type RolesListRenderArgs,\n} from \"./RolesList.js\";\n\nexport {\n InviteMemberForm,\n type InviteMemberFormProps,\n type InviteMemberFormRenderArgs,\n} from \"./InviteMemberForm.js\";\n","/**\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 * Transport-agnostic client: turns an adopter-supplied\n * `AuthRbacFetcher` into a permission resolver. The React provider\n * wraps this; non-React consumers (Node scripts, edge functions)\n * can use it directly.\n */\n\nimport type {\n Action,\n AuthRbacFetcher,\n PermissionMap,\n ResourceDescriptor,\n ResourceRegistry,\n ResourceScope,\n UserProfile,\n} from \"./types.js\";\n\nexport interface AuthRbacClientOptions {\n fetcher: AuthRbacFetcher;\n /**\n * The host project's full resource list. Required so the resolver\n * can look up a resource's scope without a DB round-trip per call.\n * Re-using the same array the host syncs into the\n * `rbac.resources` table at boot keeps everything in lockstep.\n */\n resources: ResourceRegistry;\n}\n\nexport interface CanOptions {\n /**\n * Override the active company. Omit to use the company the\n * caller has currently activated (the React provider tracks\n * this; for direct client use you must pass it).\n */\n companyId?: string | null;\n}\n\n/**\n * Pure resolver. Given a hydrated profile it answers boolean\n * questions instantly — no I/O. The `resourceMap` is built once at\n * construction so per-call work is two map lookups.\n */\nexport function buildPermissionResolver(\n resources: ResourceRegistry,\n profile: UserProfile,\n defaultCompanyId: string | null,\n) {\n const scopeByResource = new Map<string, ResourceScope>(\n resources.map((r) => [r.resource, r.scope]),\n );\n\n const can = (\n resource: string,\n action: Action,\n options?: CanOptions,\n ): boolean => {\n if (profile.is_super_admin) {\n return true;\n }\n const scope = scopeByResource.get(resource);\n if (!scope) {\n // Unknown resource — fail closed.\n return false;\n }\n if (scope === \"system\") {\n return readGrid(profile.system_permissions, resource, action);\n }\n const companyId = options?.companyId ?? defaultCompanyId;\n if (!companyId) {\n return false;\n }\n const membership = profile.memberships.find(\n (m) => m.company_id === companyId,\n );\n if (!membership) {\n return false;\n }\n return readGrid(membership.permissions, resource, action);\n };\n\n /**\n * Direct-grant lookup: returns true only if the user has the\n * action granted on the resource as a direct admin grant —\n * `<action>_granted_via IS NULL` in `rbac.role_permissions`.\n * Implied rows (granted as a side-effect of a parent resource's\n * `dependsOn` cascade) return false here.\n *\n * Use for top-level navigation / list-page gating: a Verwalter\n * with only `leases:read` direct gets the Leases sidebar item but\n * not Tenants / Units / Properties, even though `can(...)` returns\n * true for those (because the implied rows let the lease detail\n * page render its joined data).\n *\n * Available since 0.4.0. For older SQL that doesn't return\n * `direct_*` maps, every cell answers false — equivalent to\n * \"no direct grants known\". Adopters running pre-0.4.0 SQL should\n * keep using `can(...)`.\n */\n const canAccessSection = (\n resource: string,\n action: Action = \"read\",\n options?: CanOptions,\n ): boolean => {\n if (profile.is_super_admin) {\n return true;\n }\n const scope = scopeByResource.get(resource);\n if (!scope) {\n return false;\n }\n if (scope === \"system\") {\n return readDirect(profile, action, resource);\n }\n const companyId = options?.companyId ?? defaultCompanyId;\n if (!companyId) {\n return false;\n }\n const membership = profile.memberships.find(\n (m) => m.company_id === companyId,\n );\n if (!membership) {\n return false;\n }\n return readDirectMembership(membership, action, resource);\n };\n\n return {\n can,\n canAccessSection,\n /** Permission map for the active (or specified) company. */\n activePermissions: (companyId?: string | null): PermissionMap => {\n const id = companyId ?? defaultCompanyId;\n if (!id) {\n return {};\n }\n return (\n profile.memberships.find((m) => m.company_id === id)?.permissions ?? {}\n );\n },\n systemPermissions: (): PermissionMap => profile.system_permissions,\n };\n}\n\nfunction readDirect(\n profile: UserProfile,\n action: Action,\n resource: string,\n): boolean {\n const map =\n action === \"read\"\n ? profile.system_direct_reads\n : action === \"write\"\n ? profile.system_direct_writes\n : action === \"update\"\n ? profile.system_direct_updates\n : profile.system_direct_deletes;\n return map?.[resource] === true;\n}\n\nfunction readDirectMembership(\n membership: { direct_reads?: Readonly<Record<string, boolean>>;\n direct_writes?: Readonly<Record<string, boolean>>;\n direct_updates?: Readonly<Record<string, boolean>>;\n direct_deletes?: Readonly<Record<string, boolean>>; },\n action: Action,\n resource: string,\n): boolean {\n const map =\n action === \"read\"\n ? membership.direct_reads\n : action === \"write\"\n ? membership.direct_writes\n : action === \"update\"\n ? membership.direct_updates\n : membership.direct_deletes;\n return map?.[resource] === true;\n}\n\nfunction readGrid(\n map: PermissionMap,\n resource: string,\n action: Action,\n): boolean {\n const grid = map[resource];\n if (!grid) {\n return false;\n }\n return grid[action];\n}\n\n/**\n * Helper: groups a resource registry by `group` for the matrix UI.\n * Returns groups in insertion order with their resources.\n */\nexport function groupResources(\n registry: ResourceRegistry,\n): Array<{ group: string; resources: ResourceDescriptor[] }> {\n const order: string[] = [];\n const buckets = new Map<string, ResourceDescriptor[]>();\n for (const r of registry) {\n const key = r.group ?? \"Sonstige\";\n if (!buckets.has(key)) {\n buckets.set(key, []);\n order.push(key);\n }\n buckets.get(key)!.push(r);\n }\n return order.map((g) => ({ group: g, resources: buckets.get(g)! }));\n}\n\nexport type AuthRbacClient = ReturnType<typeof buildPermissionResolver>;\nexport type { AuthRbacClientOptions as ClientOptions };\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":["import_react","import_jsx_runtime","ACTIONS","import_react","import_jsx_runtime","import_react","import_jsx_runtime"]}
|
|
1
|
+
{"version":3,"sources":["../../src/admin/index.ts","../../src/admin/transport.ts","../../src/admin/hooks.tsx","../../src/admin/PermissionsMatrix.tsx","../../src/client.ts","../../src/admin/RolesList.tsx","../../src/admin/InviteMemberForm.tsx"],"sourcesContent":["/**\n * Admin entry — import from `snipe-auth-rbac/admin`.\n *\n * Two layers shipped here, all UI-kit-agnostic:\n *\n * 1. **Transport + hooks.** Pick `createSupabaseAdminClient(...)`\n * or implement `AdminTransport` yourself, then mount\n * `<AdminTransportProvider>` and pull data with the hooks.\n * 2. **Headless render-prop components.** `<PermissionsMatrix>`,\n * `<RolesList>`, `<InviteMemberForm>` own state + mutations\n * and hand the consumer a render-prop with everything needed\n * to draw the UI in any design system.\n *\n * For a styled reference (Tailwind + Radix shadcn primitives) see\n * `examples/react-admin/` — copy the page into your project, swap\n * the imports for your local UI kit, ship.\n */\n\nexport type {\n AdminCompany,\n AdminMember,\n AdminResourceDependency,\n AdminRole,\n AdminRolePermission,\n AdminRolePermissionOverride,\n AdminTransport,\n} from \"./types.js\";\n\nexport {\n createSupabaseAdminClient,\n extractResourceDependencies,\n type SupabaseAdminClientOptions,\n} from \"./transport.js\";\n\nexport {\n AdminTransportProvider,\n type AdminTransportProviderProps,\n useAdminRoles,\n useAdminRolePermissions,\n useAdminCompanies,\n useAdminCompanyMembers,\n useAdminResourceDependencies,\n useRolePermissionOverrides,\n useCreateRole,\n useUpdateRole,\n useDeleteRole,\n useSetRolePermissionCell,\n useApplyTemplateDefaults,\n useCreateCompany,\n useInviteCompanyMember,\n useRolePermissionGrid,\n type RolePermissionGrid,\n type RolePermissionOriginGrid,\n} from \"./hooks.js\";\n\nexport {\n PermissionsMatrix,\n type PermissionsMatrixProps,\n type MatrixGroup,\n type MatrixRenderArgs,\n} from \"./PermissionsMatrix.js\";\n\nexport {\n RolesList,\n type RolesListProps,\n type RolesListRenderArgs,\n} from \"./RolesList.js\";\n\nexport {\n InviteMemberForm,\n type InviteMemberFormProps,\n type InviteMemberFormRenderArgs,\n} from \"./InviteMemberForm.js\";\n","/**\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 * Transport-agnostic client: turns an adopter-supplied\n * `AuthRbacFetcher` into a permission resolver. The React provider\n * wraps this; non-React consumers (Node scripts, edge functions)\n * can use it directly.\n */\n\nimport type {\n Action,\n AuthRbacFetcher,\n PermissionMap,\n ResourceDescriptor,\n ResourceRegistry,\n ResourceScope,\n UserProfile,\n} from \"./types.js\";\n\nexport interface AuthRbacClientOptions {\n fetcher: AuthRbacFetcher;\n /**\n * The host project's full resource list. Required so the resolver\n * can look up a resource's scope without a DB round-trip per call.\n * Re-using the same array the host syncs into the\n * `rbac.resources` table at boot keeps everything in lockstep.\n */\n resources: ResourceRegistry;\n}\n\nexport interface CanOptions {\n /**\n * Override the active company. Omit to use the company the\n * caller has currently activated (the React provider tracks\n * this; for direct client use you must pass it).\n */\n companyId?: string | null;\n}\n\n/**\n * Pure resolver. Given a hydrated profile it answers boolean\n * questions instantly — no I/O. The `resourceMap` is built once at\n * construction so per-call work is two map lookups.\n */\nexport function buildPermissionResolver(\n resources: ResourceRegistry,\n profile: UserProfile,\n defaultCompanyId: string | null,\n) {\n const scopeByResource = new Map<string, ResourceScope>(\n resources.map((r) => [r.resource, r.scope]),\n );\n\n const can = (\n resource: string,\n action: Action,\n options?: CanOptions,\n ): boolean => {\n if (profile.is_super_admin) {\n return true;\n }\n const scope = scopeByResource.get(resource);\n if (!scope) {\n // Unknown resource — fail closed.\n return false;\n }\n if (scope === \"system\") {\n return readGrid(profile.system_permissions, resource, action);\n }\n const companyId = options?.companyId ?? defaultCompanyId;\n if (!companyId) {\n return false;\n }\n const membership = profile.memberships.find(\n (m) => m.company_id === companyId,\n );\n if (!membership) {\n return false;\n }\n return readGrid(membership.permissions, resource, action);\n };\n\n /**\n * Direct-grant lookup: returns true only if the user has the\n * action granted on the resource as a direct admin grant —\n * `<action>_granted_via IS NULL` in `rbac.role_permissions`.\n * Implied rows (granted as a side-effect of a parent resource's\n * `dependsOn` cascade) return false here.\n *\n * Use for top-level navigation / list-page gating: a Verwalter\n * with only `leases:read` direct gets the Leases sidebar item but\n * not Tenants / Units / Properties, even though `can(...)` returns\n * true for those (because the implied rows let the lease detail\n * page render its joined data).\n *\n * Available since 0.4.0. For older SQL that doesn't return\n * `direct_*` maps, every cell answers false — equivalent to\n * \"no direct grants known\". Adopters running pre-0.4.0 SQL should\n * keep using `can(...)`.\n */\n const canAccessSection = (\n resource: string,\n action: Action = \"read\",\n options?: CanOptions,\n ): boolean => {\n if (profile.is_super_admin) {\n return true;\n }\n const scope = scopeByResource.get(resource);\n if (!scope) {\n return false;\n }\n if (scope === \"system\") {\n return readDirect(profile, action, resource);\n }\n const companyId = options?.companyId ?? defaultCompanyId;\n if (!companyId) {\n return false;\n }\n const membership = profile.memberships.find(\n (m) => m.company_id === companyId,\n );\n if (!membership) {\n return false;\n }\n return readDirectMembership(membership, action, resource);\n };\n\n return {\n can,\n canAccessSection,\n /** Permission map for the active (or specified) company. */\n activePermissions: (companyId?: string | null): PermissionMap => {\n const id = companyId ?? defaultCompanyId;\n if (!id) {\n return {};\n }\n return (\n profile.memberships.find((m) => m.company_id === id)?.permissions ?? {}\n );\n },\n systemPermissions: (): PermissionMap => profile.system_permissions,\n };\n}\n\nfunction readDirect(\n profile: UserProfile,\n action: Action,\n resource: string,\n): boolean {\n const map =\n action === \"read\"\n ? profile.system_direct_reads\n : action === \"write\"\n ? profile.system_direct_writes\n : action === \"update\"\n ? profile.system_direct_updates\n : profile.system_direct_deletes;\n return map?.[resource] === true;\n}\n\nfunction readDirectMembership(\n membership: { direct_reads?: Readonly<Record<string, boolean>>;\n direct_writes?: Readonly<Record<string, boolean>>;\n direct_updates?: Readonly<Record<string, boolean>>;\n direct_deletes?: Readonly<Record<string, boolean>>; },\n action: Action,\n resource: string,\n): boolean {\n const map =\n action === \"read\"\n ? membership.direct_reads\n : action === \"write\"\n ? membership.direct_writes\n : action === \"update\"\n ? membership.direct_updates\n : membership.direct_deletes;\n return map?.[resource] === true;\n}\n\nfunction readGrid(\n map: PermissionMap,\n resource: string,\n action: Action,\n): boolean {\n const grid = map[resource];\n if (!grid) {\n return false;\n }\n return grid[action];\n}\n\n/**\n * Helper: groups a resource registry by `group` for the matrix UI.\n * Returns groups in insertion order with their resources.\n */\nexport function groupResources(\n registry: ResourceRegistry,\n): Array<{ group: string; resources: ResourceDescriptor[] }> {\n const order: string[] = [];\n const buckets = new Map<string, ResourceDescriptor[]>();\n for (const r of registry) {\n const key = r.group ?? \"Sonstige\";\n if (!buckets.has(key)) {\n buckets.set(key, []);\n order.push(key);\n }\n buckets.get(key)!.push(r);\n }\n return order.map((g) => ({ group: g, resources: buckets.get(g)! }));\n}\n\nexport type AuthRbacClient = ReturnType<typeof buildPermissionResolver>;\nexport type { AuthRbacClientOptions as ClientOptions };\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":["error","import_react","import_jsx_runtime","ACTIONS","import_react","import_jsx_runtime","import_react","import_jsx_runtime"]}
|