snipe-auth-rbac 0.1.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/cli.mjs +140 -0
- package/dist/admin/index.cjs +30 -12
- package/dist/admin/index.cjs.map +1 -1
- package/dist/admin/index.d.cts +37 -6
- package/dist/admin/index.d.ts +37 -6
- package/dist/admin/index.js +30 -13
- package/dist/admin/index.js.map +1 -1
- package/dist/{chunk-4WTV6J44.js → chunk-C76JHCKM.js} +1 -1
- package/dist/chunk-C76JHCKM.js.map +1 -0
- package/dist/{chunk-BRCJUCDG.js → chunk-NRDW233A.js} +17 -3
- package/dist/chunk-NRDW233A.js.map +1 -0
- package/dist/index.cjs +17 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +35 -7
- package/dist/index.d.ts +35 -7
- package/dist/index.js +5 -3
- package/dist/react/index.cjs +17 -2
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +3 -3
- package/dist/react/index.d.ts +3 -3
- package/dist/react/index.js +5 -3
- package/dist/react/index.js.map +1 -1
- package/dist/{types-BEc5SCIo.d.cts → types-DxvFudPF.d.cts} +1 -1
- package/dist/{types-BEc5SCIo.d.ts → types-DxvFudPF.d.ts} +1 -1
- package/package.json +8 -2
- package/sql/0001_initial.sql +512 -0
- package/sql/0002_seed_defaults.sql +57 -0
- package/dist/chunk-4WTV6J44.js.map +0 -1
- package/dist/chunk-BRCJUCDG.js.map +0 -1
package/bin/cli.mjs
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* snipe-auth-rbac CLI
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* npx snipe-auth-rbac install [--dir supabase/migrations] [--skip-seed]
|
|
7
|
+
* npx snipe-auth-rbac --help
|
|
8
|
+
*
|
|
9
|
+
* The `install` command copies the package's SQL files into the
|
|
10
|
+
* adopter's migration directory with timestamped filenames so they
|
|
11
|
+
* apply via `supabase db push` (or any migration tool that scans a
|
|
12
|
+
* directory in alphabetical order).
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { mkdirSync, copyFileSync, existsSync, readdirSync } from "node:fs";
|
|
16
|
+
import { dirname, join, resolve } from "node:path";
|
|
17
|
+
import { fileURLToPath } from "node:url";
|
|
18
|
+
|
|
19
|
+
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
20
|
+
const PKG_SQL = resolve(HERE, "..", "sql");
|
|
21
|
+
|
|
22
|
+
function help() {
|
|
23
|
+
process.stdout.write(`snipe-auth-rbac — install helper
|
|
24
|
+
|
|
25
|
+
Commands
|
|
26
|
+
install [--dir <path>] [--skip-seed]
|
|
27
|
+
Copy 0001_initial.sql and (optionally) 0002_seed_defaults.sql
|
|
28
|
+
into the target migration directory with timestamped names.
|
|
29
|
+
|
|
30
|
+
Options
|
|
31
|
+
--dir <path> Target directory. Default: supabase/migrations
|
|
32
|
+
--skip-seed Don't copy 0002_seed_defaults.sql
|
|
33
|
+
--force Overwrite existing rbac migrations in the target
|
|
34
|
+
|
|
35
|
+
After running install:
|
|
36
|
+
1. supabase db push # apply migrations
|
|
37
|
+
2. Studio → Settings → API → Exposed schemas: # add 'rbac'
|
|
38
|
+
|
|
39
|
+
`);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function parseArgs(argv) {
|
|
43
|
+
const args = { command: null, dir: "supabase/migrations", skipSeed: false, force: false };
|
|
44
|
+
let i = 0;
|
|
45
|
+
while (i < argv.length) {
|
|
46
|
+
const a = argv[i];
|
|
47
|
+
if (a === "--help" || a === "-h") return { ...args, command: "help" };
|
|
48
|
+
if (a === "install") args.command = "install";
|
|
49
|
+
else if (a === "--dir") args.dir = argv[++i];
|
|
50
|
+
else if (a === "--skip-seed") args.skipSeed = true;
|
|
51
|
+
else if (a === "--force") args.force = true;
|
|
52
|
+
else {
|
|
53
|
+
process.stderr.write(`Unknown argument: ${a}\n`);
|
|
54
|
+
return { ...args, command: "help" };
|
|
55
|
+
}
|
|
56
|
+
i++;
|
|
57
|
+
}
|
|
58
|
+
return args;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function timestampSeq(seqFromMs) {
|
|
62
|
+
// 14-digit YYYYMMDDHHMMSS used by Supabase migration naming
|
|
63
|
+
const d = new Date(seqFromMs);
|
|
64
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
65
|
+
return (
|
|
66
|
+
d.getUTCFullYear().toString() +
|
|
67
|
+
pad(d.getUTCMonth() + 1) +
|
|
68
|
+
pad(d.getUTCDate()) +
|
|
69
|
+
pad(d.getUTCHours()) +
|
|
70
|
+
pad(d.getUTCMinutes()) +
|
|
71
|
+
pad(d.getUTCSeconds())
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function findExistingRbacMigrations(dir) {
|
|
76
|
+
if (!existsSync(dir)) return [];
|
|
77
|
+
return readdirSync(dir).filter((f) => /_rbac_(init|seed)\.sql$/.test(f));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function install(opts) {
|
|
81
|
+
const target = resolve(process.cwd(), opts.dir);
|
|
82
|
+
if (!existsSync(target)) {
|
|
83
|
+
process.stdout.write(`Creating ${target}\n`);
|
|
84
|
+
mkdirSync(target, { recursive: true });
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const existing = findExistingRbacMigrations(target);
|
|
88
|
+
if (existing.length > 0 && !opts.force) {
|
|
89
|
+
process.stderr.write(
|
|
90
|
+
`Refusing to install: existing rbac migrations in ${target}\n` +
|
|
91
|
+
existing.map((f) => ` - ${f}`).join("\n") +
|
|
92
|
+
"\nPass --force to overwrite, or --dir to pick a different directory.\n",
|
|
93
|
+
);
|
|
94
|
+
process.exit(2);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const initSrc = join(PKG_SQL, "0001_initial.sql");
|
|
98
|
+
const seedSrc = join(PKG_SQL, "0002_seed_defaults.sql");
|
|
99
|
+
|
|
100
|
+
if (!existsSync(initSrc)) {
|
|
101
|
+
process.stderr.write(
|
|
102
|
+
`error: package SQL files missing at ${PKG_SQL}.\n` +
|
|
103
|
+
"This is a packaging bug — please file an issue.\n",
|
|
104
|
+
);
|
|
105
|
+
process.exit(3);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Use two consecutive seconds so init sorts before seed.
|
|
109
|
+
const now = Date.now();
|
|
110
|
+
const initName = `${timestampSeq(now)}_rbac_init.sql`;
|
|
111
|
+
const seedName = `${timestampSeq(now + 1000)}_rbac_seed.sql`;
|
|
112
|
+
|
|
113
|
+
copyFileSync(initSrc, join(target, initName));
|
|
114
|
+
process.stdout.write(`✓ ${target}/${initName}\n`);
|
|
115
|
+
|
|
116
|
+
if (!opts.skipSeed) {
|
|
117
|
+
copyFileSync(seedSrc, join(target, seedName));
|
|
118
|
+
process.stdout.write(`✓ ${target}/${seedName}\n`);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
process.stdout.write(`
|
|
122
|
+
Next steps:
|
|
123
|
+
supabase db push
|
|
124
|
+
# then: Studio → Settings → API → Exposed schemas → add 'rbac'
|
|
125
|
+
|
|
126
|
+
After applying, your codebase can call:
|
|
127
|
+
supabase.schema('rbac').rpc('user_can', { … })
|
|
128
|
+
`);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const args = parseArgs(process.argv.slice(2));
|
|
132
|
+
if (args.command === null || args.command === "help") {
|
|
133
|
+
help();
|
|
134
|
+
process.exit(args.command === null ? 1 : 0);
|
|
135
|
+
} else if (args.command === "install") {
|
|
136
|
+
install(args);
|
|
137
|
+
} else {
|
|
138
|
+
help();
|
|
139
|
+
process.exit(1);
|
|
140
|
+
}
|
package/dist/admin/index.cjs
CHANGED
|
@@ -29,6 +29,7 @@ __export(admin_exports, {
|
|
|
29
29
|
useAdminCompanyMembers: () => useAdminCompanyMembers,
|
|
30
30
|
useAdminRolePermissions: () => useAdminRolePermissions,
|
|
31
31
|
useAdminRoles: () => useAdminRoles,
|
|
32
|
+
useApplyTemplateDefaults: () => useApplyTemplateDefaults,
|
|
32
33
|
useCreateCompany: () => useCreateCompany,
|
|
33
34
|
useCreateRole: () => useCreateRole,
|
|
34
35
|
useDeleteRole: () => useDeleteRole,
|
|
@@ -48,6 +49,7 @@ var ACTION_COLUMN = {
|
|
|
48
49
|
};
|
|
49
50
|
function createSupabaseAdminClient(opts) {
|
|
50
51
|
const sb = opts.supabase;
|
|
52
|
+
const rbac = sb.schema("rbac");
|
|
51
53
|
return {
|
|
52
54
|
async syncResources(resources) {
|
|
53
55
|
if (resources.length === 0) {
|
|
@@ -60,14 +62,14 @@ function createSupabaseAdminClient(opts) {
|
|
|
60
62
|
description: r.description ?? null,
|
|
61
63
|
group_label: r.group ?? null
|
|
62
64
|
}));
|
|
63
|
-
const { error } = await
|
|
65
|
+
const { error } = await rbac.from("resources").upsert(payload, { onConflict: "resource" });
|
|
64
66
|
if (error) {
|
|
65
67
|
throw new Error(`syncResources: ${error.message}`);
|
|
66
68
|
}
|
|
67
69
|
return resources.length;
|
|
68
70
|
},
|
|
69
71
|
async listRoles({ scope, companyId, templatesOnly }) {
|
|
70
|
-
let q =
|
|
72
|
+
let q = rbac.from("roles").select("*").eq("scope", scope);
|
|
71
73
|
if (templatesOnly) {
|
|
72
74
|
q = q.is("company_id", null);
|
|
73
75
|
} else if (companyId !== void 0) {
|
|
@@ -80,7 +82,7 @@ function createSupabaseAdminClient(opts) {
|
|
|
80
82
|
return data ?? [];
|
|
81
83
|
},
|
|
82
84
|
async listRolePermissions(roleId) {
|
|
83
|
-
const { data, error } = await
|
|
85
|
+
const { data, error } = await rbac.from("role_permissions").select("*").eq("role_id", roleId);
|
|
84
86
|
if (error) {
|
|
85
87
|
throw new Error(`listRolePermissions: ${error.message}`);
|
|
86
88
|
}
|
|
@@ -94,21 +96,21 @@ function createSupabaseAdminClient(opts) {
|
|
|
94
96
|
description: input.description ?? null,
|
|
95
97
|
frontend_config: input.frontend_config ?? {}
|
|
96
98
|
};
|
|
97
|
-
const { data, error } = await
|
|
99
|
+
const { data, error } = await rbac.from("roles").insert(row).select("*").single();
|
|
98
100
|
if (error) {
|
|
99
101
|
throw new Error(`createRole: ${error.message}`);
|
|
100
102
|
}
|
|
101
103
|
return data;
|
|
102
104
|
},
|
|
103
105
|
async updateRole(id, patch) {
|
|
104
|
-
const { data, error } = await
|
|
106
|
+
const { data, error } = await rbac.from("roles").update(patch).eq("id", id).select("*").single();
|
|
105
107
|
if (error) {
|
|
106
108
|
throw new Error(`updateRole: ${error.message}`);
|
|
107
109
|
}
|
|
108
110
|
return data;
|
|
109
111
|
},
|
|
110
112
|
async deleteRole(id) {
|
|
111
|
-
const { error } = await
|
|
113
|
+
const { error } = await rbac.from("roles").delete().eq("id", id);
|
|
112
114
|
if (error) {
|
|
113
115
|
throw new Error(`deleteRole: ${error.message}`);
|
|
114
116
|
}
|
|
@@ -120,20 +122,31 @@ function createSupabaseAdminClient(opts) {
|
|
|
120
122
|
resource,
|
|
121
123
|
[column]: value
|
|
122
124
|
};
|
|
123
|
-
const { error } = await
|
|
125
|
+
const { error } = await rbac.from("role_permissions").upsert(row, { onConflict: "role_id,resource" });
|
|
124
126
|
if (error) {
|
|
125
127
|
throw new Error(`setRolePermissionCell: ${error.message}`);
|
|
126
128
|
}
|
|
127
129
|
},
|
|
130
|
+
async applyTemplateDefaults({ role_id, only_missing = true }) {
|
|
131
|
+
const { data, error } = await rbac.rpc("apply_template_defaults", {
|
|
132
|
+
p_role_id: role_id,
|
|
133
|
+
p_only_missing: only_missing
|
|
134
|
+
});
|
|
135
|
+
if (error) {
|
|
136
|
+
throw new Error(`applyTemplateDefaults: ${error.message}`);
|
|
137
|
+
}
|
|
138
|
+
if (typeof data === "number") return data;
|
|
139
|
+
return Number(data ?? 0);
|
|
140
|
+
},
|
|
128
141
|
async listCompanies() {
|
|
129
|
-
const { data, error } = await
|
|
142
|
+
const { data, error } = await rbac.from("companies").select("*").order("name", { ascending: true });
|
|
130
143
|
if (error) {
|
|
131
144
|
throw new Error(`listCompanies: ${error.message}`);
|
|
132
145
|
}
|
|
133
146
|
return data ?? [];
|
|
134
147
|
},
|
|
135
148
|
async createCompany(input) {
|
|
136
|
-
const { data, error } = await
|
|
149
|
+
const { data, error } = await rbac.from("companies").insert({
|
|
137
150
|
name: input.name,
|
|
138
151
|
slug: input.slug ?? null,
|
|
139
152
|
type: input.type ?? null
|
|
@@ -144,7 +157,7 @@ function createSupabaseAdminClient(opts) {
|
|
|
144
157
|
return data;
|
|
145
158
|
},
|
|
146
159
|
async listCompanyMembers(companyId) {
|
|
147
|
-
const { data, error } = await
|
|
160
|
+
const { data, error } = await rbac.from("user_company_roles").select("user_id, role_id, assigned_at").eq("company_id", companyId);
|
|
148
161
|
if (error) {
|
|
149
162
|
throw new Error(`listCompanyMembers: ${error.message}`);
|
|
150
163
|
}
|
|
@@ -169,8 +182,8 @@ function createSupabaseAdminClient(opts) {
|
|
|
169
182
|
async inviteCompanyMember({ companyId, email, roleIds }) {
|
|
170
183
|
const { error } = await sb.auth.admin.inviteUserByEmail(email, {
|
|
171
184
|
data: {
|
|
172
|
-
|
|
173
|
-
|
|
185
|
+
rbac_company_id: companyId,
|
|
186
|
+
rbac_role_ids: roleIds
|
|
174
187
|
},
|
|
175
188
|
redirectTo: opts.inviteRedirectUrl
|
|
176
189
|
});
|
|
@@ -286,6 +299,10 @@ function useSetRolePermissionCell() {
|
|
|
286
299
|
const transport = useAdminTransport();
|
|
287
300
|
return useMutation(transport.setRolePermissionCell);
|
|
288
301
|
}
|
|
302
|
+
function useApplyTemplateDefaults() {
|
|
303
|
+
const transport = useAdminTransport();
|
|
304
|
+
return useMutation(transport.applyTemplateDefaults);
|
|
305
|
+
}
|
|
289
306
|
function useCreateCompany() {
|
|
290
307
|
const transport = useAdminTransport();
|
|
291
308
|
return useMutation(transport.createCompany);
|
|
@@ -504,6 +521,7 @@ function InviteMemberForm(props) {
|
|
|
504
521
|
useAdminCompanyMembers,
|
|
505
522
|
useAdminRolePermissions,
|
|
506
523
|
useAdminRoles,
|
|
524
|
+
useApplyTemplateDefaults,
|
|
507
525
|
useCreateCompany,
|
|
508
526
|
useCreateRole,
|
|
509
527
|
useDeleteRole,
|
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 AdminRole,\n AdminRolePermission,\n AdminTransport,\n} from \"./types.js\";\n\nexport {\n createSupabaseAdminClient,\n type SupabaseAdminClientOptions,\n} from \"./transport.js\";\n\nexport {\n AdminTransportProvider,\n type AdminTransportProviderProps,\n useAdminRoles,\n useAdminRolePermissions,\n useAdminCompanies,\n useAdminCompanyMembers,\n useCreateRole,\n useUpdateRole,\n useDeleteRole,\n useSetRolePermissionCell,\n useCreateCompany,\n useInviteCompanyMember,\n useRolePermissionGrid,\n type RolePermissionGrid,\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 directly via `from(...)` and the auth admin\n * endpoint for invites.\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 AdminRole,\n AdminRolePermission,\n AdminTransport,\n} from \"./types.js\";\n\ninterface SupabaseAdmin {\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 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\nexport function createSupabaseAdminClient(\n opts: SupabaseAdminClientOptions,\n): AdminTransport {\n const sb = opts.supabase;\n\n return {\n async syncResources(resources) {\n if (resources.length === 0) {\n return 0;\n }\n const payload = resources.map((r) => ({\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 sb\n .from(\"auth_rbac_resources\")\n .upsert(payload, { onConflict: \"resource\" });\n if (error) {\n throw new Error(`syncResources: ${error.message}`);\n }\n return resources.length;\n },\n\n async listRoles({ scope, companyId, templatesOnly }) {\n let q = sb\n .from(\"auth_rbac_roles\")\n .select(\"*\")\n .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 sb\n .from(\"auth_rbac_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 sb\n .from(\"auth_rbac_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 sb\n .from(\"auth_rbac_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 sb\n .from(\"auth_rbac_roles\")\n .delete()\n .eq(\"id\", id);\n if (error) {\n throw new Error(`deleteRole: ${error.message}`);\n }\n },\n\n async setRolePermissionCell({ role_id, resource, action, value }) {\n const column = ACTION_COLUMN[action];\n const row: Record<string, unknown> = {\n role_id,\n resource,\n [column]: value,\n };\n const { error } = await sb\n .from(\"auth_rbac_role_permissions\")\n .upsert(row, { onConflict: \"role_id,resource\" });\n if (error) {\n throw new Error(`setRolePermissionCell: ${error.message}`);\n }\n },\n\n async listCompanies() {\n const { data, error } = await sb\n .from(\"auth_rbac_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 sb\n .from(\"auth_rbac_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 sb\n .from(\"auth_rbac_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 auth_rbac_company_id: companyId,\n auth_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 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 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\nexport function useRolePermissionGrid(roleId: string | null) {\n const { data, isLoading, error, refresh } = useAdminRolePermissions(roleId);\n const setCell = useSetRolePermissionCell();\n\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 const updateCell = useCallback(\n async (resource: string, action: Action, value: boolean) => {\n if (!roleId) {\n return;\n }\n await setCell.mutate({ role_id: roleId, resource, action, value });\n void refresh();\n },\n [roleId, setCell, refresh],\n );\n\n return {\n grid,\n isLoading,\n error,\n refresh,\n updateCell,\n isUpdating: setCell.isPending,\n updateError: setCell.error,\n };\n}\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 /** Write a single cell. Optimistic in the local cache + writes through. */\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, isLoading, error, updateCell, isUpdating } =\n useRolePermissionGrid(props.roleId);\n\n const groups = useMemo<MatrixGroup[]>(\n () => groupResources(props.resources),\n [props.resources],\n );\n\n const isCellEnabled = (resource: string, action: Action): boolean => {\n return grid[resource]?.[action] ?? false;\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 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 * `auth_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 return {\n can,\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 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","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 AdminRole,\n AdminRolePermission,\n AdminTransport,\n} from \"./types.js\";\n\nexport {\n createSupabaseAdminClient,\n type SupabaseAdminClientOptions,\n} from \"./transport.js\";\n\nexport {\n AdminTransportProvider,\n type AdminTransportProviderProps,\n useAdminRoles,\n useAdminRolePermissions,\n useAdminCompanies,\n useAdminCompanyMembers,\n useCreateRole,\n useUpdateRole,\n useDeleteRole,\n useSetRolePermissionCell,\n useApplyTemplateDefaults,\n useCreateCompany,\n useInviteCompanyMember,\n useRolePermissionGrid,\n type RolePermissionGrid,\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 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\nexport function createSupabaseAdminClient(\n opts: SupabaseAdminClientOptions,\n): AdminTransport {\n const sb = opts.supabase;\n const rbac = sb.schema(\"rbac\");\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 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 }) {\n const column = ACTION_COLUMN[action];\n const row: Record<string, unknown> = {\n role_id,\n resource,\n [column]: value,\n };\n const { error } = await rbac\n .from(\"role_permissions\")\n .upsert(row, { onConflict: \"role_id,resource\" });\n if (error) {\n throw new Error(`setRolePermissionCell: ${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 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\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\nexport function useRolePermissionGrid(roleId: string | null) {\n const { data, isLoading, error, refresh } = useAdminRolePermissions(roleId);\n const setCell = useSetRolePermissionCell();\n\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 const updateCell = useCallback(\n async (resource: string, action: Action, value: boolean) => {\n if (!roleId) {\n return;\n }\n await setCell.mutate({ role_id: roleId, resource, action, value });\n void refresh();\n },\n [roleId, setCell, refresh],\n );\n\n return {\n grid,\n isLoading,\n error,\n refresh,\n updateCell,\n isUpdating: setCell.isPending,\n updateError: setCell.error,\n };\n}\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 /** Write a single cell. Optimistic in the local cache + writes through. */\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, isLoading, error, updateCell, isUpdating } =\n useRolePermissionGrid(props.roleId);\n\n const groups = useMemo<MatrixGroup[]>(\n () => groupResources(props.resources),\n [props.resources],\n );\n\n const isCellEnabled = (resource: string, action: Action): boolean => {\n return grid[resource]?.[action] ?? false;\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 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 return {\n can,\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 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","import_react","import_jsx_runtime","import_react","import_jsx_runtime"]}
|
package/dist/admin/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { R as ResourceScope, F as FrontendConfig, a as ResourceDescriptor, A as Action } from '../types-
|
|
1
|
+
import { R as ResourceScope, F as FrontendConfig, a as ResourceDescriptor, A as Action } from '../types-DxvFudPF.cjs';
|
|
2
2
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -54,7 +54,7 @@ interface AdminMember {
|
|
|
54
54
|
*/
|
|
55
55
|
interface AdminTransport {
|
|
56
56
|
/**
|
|
57
|
-
* Upsert resource descriptors into `
|
|
57
|
+
* Upsert resource descriptors into `rbac.resources`. Call
|
|
58
58
|
* once at app boot (or behind a SuperAdmin button) so the
|
|
59
59
|
* permission matrix UI mirrors the host's typed resource
|
|
60
60
|
* registry. Returns the number of rows upserted.
|
|
@@ -82,6 +82,16 @@ interface AdminTransport {
|
|
|
82
82
|
action: Action;
|
|
83
83
|
value: boolean;
|
|
84
84
|
}): Promise<void>;
|
|
85
|
+
/**
|
|
86
|
+
* Materialise `rbac.role_permissions` rows from a template role's
|
|
87
|
+
* `default_permissions` JSONB pattern. Calls the SQL function
|
|
88
|
+
* `rbac.apply_template_defaults(role_id, only_missing)`. Returns
|
|
89
|
+
* the number of rows the function reports as upserted.
|
|
90
|
+
*/
|
|
91
|
+
applyTemplateDefaults(args: {
|
|
92
|
+
role_id: string;
|
|
93
|
+
only_missing?: boolean;
|
|
94
|
+
}): Promise<number>;
|
|
85
95
|
listCompanies(): Promise<AdminCompany[]>;
|
|
86
96
|
createCompany(input: {
|
|
87
97
|
name: string;
|
|
@@ -105,15 +115,19 @@ interface AdminTransport {
|
|
|
105
115
|
|
|
106
116
|
/**
|
|
107
117
|
* Default Supabase implementation of the admin transport. Hits the
|
|
108
|
-
* package's tables
|
|
109
|
-
* endpoint for invites.
|
|
118
|
+
* package's tables in the `rbac` schema directly via `.schema('rbac').
|
|
119
|
+
* from(...)` and the auth admin endpoint for invites.
|
|
120
|
+
*
|
|
121
|
+
* Adopters must add `rbac` to their PostgREST exposed-schemas list
|
|
122
|
+
* (Supabase Studio → Settings → API → Exposed schemas) for these
|
|
123
|
+
* calls to reach the tables.
|
|
110
124
|
*
|
|
111
125
|
* Projects that route admin writes through their own backend
|
|
112
126
|
* (e.g. for audit logging or extra validation) skip this and
|
|
113
127
|
* implement `AdminTransport` themselves.
|
|
114
128
|
*/
|
|
115
129
|
|
|
116
|
-
interface
|
|
130
|
+
interface RbacSchemaClient {
|
|
117
131
|
from(table: string): {
|
|
118
132
|
select: (cols: string) => {
|
|
119
133
|
eq: (col: string, value: unknown) => any;
|
|
@@ -145,6 +159,15 @@ interface SupabaseAdmin {
|
|
|
145
159
|
eq: (col: string, value: unknown) => any;
|
|
146
160
|
};
|
|
147
161
|
};
|
|
162
|
+
rpc(fn: string, args: Record<string, unknown>): Promise<{
|
|
163
|
+
data: unknown;
|
|
164
|
+
error: {
|
|
165
|
+
message: string;
|
|
166
|
+
} | null;
|
|
167
|
+
}>;
|
|
168
|
+
}
|
|
169
|
+
interface SupabaseAdmin {
|
|
170
|
+
schema(name: string): RbacSchemaClient;
|
|
148
171
|
auth: {
|
|
149
172
|
admin: {
|
|
150
173
|
inviteUserByEmail: (email: string, opts?: {
|
|
@@ -230,6 +253,14 @@ declare function useSetRolePermissionCell(): {
|
|
|
230
253
|
value: boolean;
|
|
231
254
|
}) => Promise<void>;
|
|
232
255
|
};
|
|
256
|
+
declare function useApplyTemplateDefaults(): {
|
|
257
|
+
isPending: boolean;
|
|
258
|
+
error: Error | null;
|
|
259
|
+
mutate: (args: {
|
|
260
|
+
role_id: string;
|
|
261
|
+
only_missing?: boolean;
|
|
262
|
+
}) => Promise<number>;
|
|
263
|
+
};
|
|
233
264
|
declare function useCreateCompany(): {
|
|
234
265
|
isPending: boolean;
|
|
235
266
|
error: Error | null;
|
|
@@ -343,4 +374,4 @@ interface InviteMemberFormProps {
|
|
|
343
374
|
}
|
|
344
375
|
declare function InviteMemberForm(props: InviteMemberFormProps): react_jsx_runtime.JSX.Element;
|
|
345
376
|
|
|
346
|
-
export { type AdminCompany, type AdminMember, type AdminRole, type AdminRolePermission, type AdminTransport, AdminTransportProvider, type AdminTransportProviderProps, InviteMemberForm, type InviteMemberFormProps, type InviteMemberFormRenderArgs, type MatrixGroup, type MatrixRenderArgs, PermissionsMatrix, type PermissionsMatrixProps, type RolePermissionGrid, RolesList, type RolesListProps, type RolesListRenderArgs, type SupabaseAdminClientOptions, createSupabaseAdminClient, useAdminCompanies, useAdminCompanyMembers, useAdminRolePermissions, useAdminRoles, useCreateCompany, useCreateRole, useDeleteRole, useInviteCompanyMember, useRolePermissionGrid, useSetRolePermissionCell, useUpdateRole };
|
|
377
|
+
export { type AdminCompany, type AdminMember, type AdminRole, type AdminRolePermission, type AdminTransport, AdminTransportProvider, type AdminTransportProviderProps, InviteMemberForm, type InviteMemberFormProps, type InviteMemberFormRenderArgs, type MatrixGroup, type MatrixRenderArgs, PermissionsMatrix, type PermissionsMatrixProps, type RolePermissionGrid, RolesList, type RolesListProps, type RolesListRenderArgs, type SupabaseAdminClientOptions, createSupabaseAdminClient, useAdminCompanies, useAdminCompanyMembers, useAdminRolePermissions, useAdminRoles, useApplyTemplateDefaults, useCreateCompany, useCreateRole, useDeleteRole, useInviteCompanyMember, useRolePermissionGrid, useSetRolePermissionCell, useUpdateRole };
|