snipe-auth-rbac 0.1.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 +515 -0
- package/dist/admin/index.cjs.map +1 -0
- package/dist/admin/index.d.cts +346 -0
- package/dist/admin/index.d.ts +346 -0
- package/dist/admin/index.js +460 -0
- package/dist/admin/index.js.map +1 -0
- package/dist/chunk-4WTV6J44.js +67 -0
- package/dist/chunk-4WTV6J44.js.map +1 -0
- package/dist/chunk-BRCJUCDG.js +55 -0
- package/dist/chunk-BRCJUCDG.js.map +1 -0
- package/dist/index.cjs +148 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +90 -0
- package/dist/index.d.ts +90 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/react/index.cjs +349 -0
- package/dist/react/index.cjs.map +1 -0
- package/dist/react/index.d.cts +221 -0
- package/dist/react/index.d.ts +221 -0
- package/dist/react/index.js +227 -0
- package/dist/react/index.js.map +1 -0
- package/dist/types-BEc5SCIo.d.cts +69 -0
- package/dist/types-BEc5SCIo.d.ts +69 -0
- package/package.json +68 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/fetchers.ts"],"sourcesContent":["/**\n * Built-in fetchers — adopters can use these or pass their own\n * implementation of `AuthRbacFetcher`.\n */\n\nimport type { AuthRbacFetcher, UserProfile } from \"./types.js\";\n\n/**\n * Calls the package's SQL function `auth_rbac_user_profile(uuid)` via\n * a Supabase JS client. Easiest path when the host project already\n * uses Supabase.\n *\n * @example\n * createSupabaseFetcher({ supabase, userId: session.user.id })\n */\nexport function createSupabaseFetcher(opts: {\n supabase: {\n rpc: (\n fn: string,\n args: Record<string, unknown>,\n ) => Promise<{ data: unknown; error: { message: string } | null }>;\n };\n userId: string;\n}): AuthRbacFetcher {\n return {\n async fetchProfile(): Promise<UserProfile> {\n const { data, error } = await opts.supabase.rpc(\n \"auth_rbac_user_profile\",\n { p_user_id: opts.userId },\n );\n if (error) {\n throw new Error(\n `auth-rbac: failed to load user profile via Supabase RPC: ${error.message}`,\n );\n }\n return normalizeProfile(data);\n },\n };\n}\n\n/**\n * Calls a regular HTTP endpoint that returns a `UserProfile` JSON\n * payload. Use this when the host project has its own backend that\n * wraps the package's Python helpers (or any equivalent).\n *\n * @example\n * createHttpFetcher({ url: \"/api/users/me/profile\" })\n */\nexport function createHttpFetcher(opts: {\n url: string;\n /** Forwarded as-is to `fetch`. Use this to attach auth headers. */\n init?: RequestInit;\n /** Override the global `fetch` if you're in a non-browser env. */\n fetch?: typeof fetch;\n}): AuthRbacFetcher {\n const fetchImpl = opts.fetch ?? globalThis.fetch;\n return {\n async fetchProfile(): Promise<UserProfile> {\n const res = await fetchImpl(opts.url, opts.init);\n if (!res.ok) {\n throw new Error(\n `auth-rbac: profile endpoint ${opts.url} returned ${res.status}`,\n );\n }\n const json = (await res.json()) as unknown;\n return normalizeProfile(json);\n },\n };\n}\n\n/**\n * Defensive normalisation: the Supabase RPC returns whatever the SQL\n * function emitted. We coerce missing fields into the empty defaults\n * so consumers can iterate without null checks. Throws if the shape\n * is unrecognisable.\n */\nfunction normalizeProfile(raw: unknown): UserProfile {\n if (!raw || typeof raw !== \"object\") {\n throw new Error(\"auth-rbac: profile payload is not an object\");\n }\n const p = raw as Partial<UserProfile> & Record<string, unknown>;\n if (typeof p.user_id !== \"string\") {\n throw new Error(\"auth-rbac: profile payload missing user_id\");\n }\n return {\n user_id: p.user_id,\n is_super_admin: !!p.is_super_admin,\n system_roles: Array.isArray(p.system_roles) ? p.system_roles : [],\n system_permissions:\n p.system_permissions && typeof p.system_permissions === \"object\"\n ? (p.system_permissions as UserProfile[\"system_permissions\"])\n : {},\n system_frontend_config:\n p.system_frontend_config && typeof p.system_frontend_config === \"object\"\n ? (p.system_frontend_config as UserProfile[\"system_frontend_config\"])\n : {},\n memberships: Array.isArray(p.memberships)\n ? (p.memberships as UserProfile[\"memberships\"])\n : [],\n };\n}\n"],"mappings":";AAeO,SAAS,sBAAsB,MAQlB;AAClB,SAAO;AAAA,IACL,MAAM,eAAqC;AACzC,YAAM,EAAE,MAAM,MAAM,IAAI,MAAM,KAAK,SAAS;AAAA,QAC1C;AAAA,QACA,EAAE,WAAW,KAAK,OAAO;AAAA,MAC3B;AACA,UAAI,OAAO;AACT,cAAM,IAAI;AAAA,UACR,4DAA4D,MAAM,OAAO;AAAA,QAC3E;AAAA,MACF;AACA,aAAO,iBAAiB,IAAI;AAAA,IAC9B;AAAA,EACF;AACF;AAUO,SAAS,kBAAkB,MAMd;AAClB,QAAM,YAAY,KAAK,SAAS,WAAW;AAC3C,SAAO;AAAA,IACL,MAAM,eAAqC;AACzC,YAAM,MAAM,MAAM,UAAU,KAAK,KAAK,KAAK,IAAI;AAC/C,UAAI,CAAC,IAAI,IAAI;AACX,cAAM,IAAI;AAAA,UACR,+BAA+B,KAAK,GAAG,aAAa,IAAI,MAAM;AAAA,QAChE;AAAA,MACF;AACA,YAAM,OAAQ,MAAM,IAAI,KAAK;AAC7B,aAAO,iBAAiB,IAAI;AAAA,IAC9B;AAAA,EACF;AACF;AAQA,SAAS,iBAAiB,KAA2B;AACnD,MAAI,CAAC,OAAO,OAAO,QAAQ,UAAU;AACnC,UAAM,IAAI,MAAM,6CAA6C;AAAA,EAC/D;AACA,QAAM,IAAI;AACV,MAAI,OAAO,EAAE,YAAY,UAAU;AACjC,UAAM,IAAI,MAAM,4CAA4C;AAAA,EAC9D;AACA,SAAO;AAAA,IACL,SAAS,EAAE;AAAA,IACX,gBAAgB,CAAC,CAAC,EAAE;AAAA,IACpB,cAAc,MAAM,QAAQ,EAAE,YAAY,IAAI,EAAE,eAAe,CAAC;AAAA,IAChE,oBACE,EAAE,sBAAsB,OAAO,EAAE,uBAAuB,WACnD,EAAE,qBACH,CAAC;AAAA,IACP,wBACE,EAAE,0BAA0B,OAAO,EAAE,2BAA2B,WAC3D,EAAE,yBACH,CAAC;AAAA,IACP,aAAa,MAAM,QAAQ,EAAE,WAAW,IACnC,EAAE,cACH,CAAC;AAAA,EACP;AACF;","names":[]}
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var src_exports = {};
|
|
22
|
+
__export(src_exports, {
|
|
23
|
+
buildPermissionResolver: () => buildPermissionResolver,
|
|
24
|
+
createHttpFetcher: () => createHttpFetcher,
|
|
25
|
+
createSupabaseFetcher: () => createSupabaseFetcher,
|
|
26
|
+
groupResources: () => groupResources
|
|
27
|
+
});
|
|
28
|
+
module.exports = __toCommonJS(src_exports);
|
|
29
|
+
|
|
30
|
+
// src/client.ts
|
|
31
|
+
function buildPermissionResolver(resources, profile, defaultCompanyId) {
|
|
32
|
+
const scopeByResource = new Map(
|
|
33
|
+
resources.map((r) => [r.resource, r.scope])
|
|
34
|
+
);
|
|
35
|
+
const can = (resource, action, options) => {
|
|
36
|
+
if (profile.is_super_admin) {
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
const scope = scopeByResource.get(resource);
|
|
40
|
+
if (!scope) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
if (scope === "system") {
|
|
44
|
+
return readGrid(profile.system_permissions, resource, action);
|
|
45
|
+
}
|
|
46
|
+
const companyId = options?.companyId ?? defaultCompanyId;
|
|
47
|
+
if (!companyId) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
const membership = profile.memberships.find(
|
|
51
|
+
(m) => m.company_id === companyId
|
|
52
|
+
);
|
|
53
|
+
if (!membership) {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
return readGrid(membership.permissions, resource, action);
|
|
57
|
+
};
|
|
58
|
+
return {
|
|
59
|
+
can,
|
|
60
|
+
/** Permission map for the active (or specified) company. */
|
|
61
|
+
activePermissions: (companyId) => {
|
|
62
|
+
const id = companyId ?? defaultCompanyId;
|
|
63
|
+
if (!id) {
|
|
64
|
+
return {};
|
|
65
|
+
}
|
|
66
|
+
return profile.memberships.find((m) => m.company_id === id)?.permissions ?? {};
|
|
67
|
+
},
|
|
68
|
+
systemPermissions: () => profile.system_permissions
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
function readGrid(map, resource, action) {
|
|
72
|
+
const grid = map[resource];
|
|
73
|
+
if (!grid) {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
return grid[action];
|
|
77
|
+
}
|
|
78
|
+
function groupResources(registry) {
|
|
79
|
+
const order = [];
|
|
80
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
81
|
+
for (const r of registry) {
|
|
82
|
+
const key = r.group ?? "Sonstige";
|
|
83
|
+
if (!buckets.has(key)) {
|
|
84
|
+
buckets.set(key, []);
|
|
85
|
+
order.push(key);
|
|
86
|
+
}
|
|
87
|
+
buckets.get(key).push(r);
|
|
88
|
+
}
|
|
89
|
+
return order.map((g) => ({ group: g, resources: buckets.get(g) }));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// src/fetchers.ts
|
|
93
|
+
function createSupabaseFetcher(opts) {
|
|
94
|
+
return {
|
|
95
|
+
async fetchProfile() {
|
|
96
|
+
const { data, error } = await opts.supabase.rpc(
|
|
97
|
+
"auth_rbac_user_profile",
|
|
98
|
+
{ p_user_id: opts.userId }
|
|
99
|
+
);
|
|
100
|
+
if (error) {
|
|
101
|
+
throw new Error(
|
|
102
|
+
`auth-rbac: failed to load user profile via Supabase RPC: ${error.message}`
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
return normalizeProfile(data);
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
function createHttpFetcher(opts) {
|
|
110
|
+
const fetchImpl = opts.fetch ?? globalThis.fetch;
|
|
111
|
+
return {
|
|
112
|
+
async fetchProfile() {
|
|
113
|
+
const res = await fetchImpl(opts.url, opts.init);
|
|
114
|
+
if (!res.ok) {
|
|
115
|
+
throw new Error(
|
|
116
|
+
`auth-rbac: profile endpoint ${opts.url} returned ${res.status}`
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
const json = await res.json();
|
|
120
|
+
return normalizeProfile(json);
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
function normalizeProfile(raw) {
|
|
125
|
+
if (!raw || typeof raw !== "object") {
|
|
126
|
+
throw new Error("auth-rbac: profile payload is not an object");
|
|
127
|
+
}
|
|
128
|
+
const p = raw;
|
|
129
|
+
if (typeof p.user_id !== "string") {
|
|
130
|
+
throw new Error("auth-rbac: profile payload missing user_id");
|
|
131
|
+
}
|
|
132
|
+
return {
|
|
133
|
+
user_id: p.user_id,
|
|
134
|
+
is_super_admin: !!p.is_super_admin,
|
|
135
|
+
system_roles: Array.isArray(p.system_roles) ? p.system_roles : [],
|
|
136
|
+
system_permissions: p.system_permissions && typeof p.system_permissions === "object" ? p.system_permissions : {},
|
|
137
|
+
system_frontend_config: p.system_frontend_config && typeof p.system_frontend_config === "object" ? p.system_frontend_config : {},
|
|
138
|
+
memberships: Array.isArray(p.memberships) ? p.memberships : []
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
142
|
+
0 && (module.exports = {
|
|
143
|
+
buildPermissionResolver,
|
|
144
|
+
createHttpFetcher,
|
|
145
|
+
createSupabaseFetcher,
|
|
146
|
+
groupResources
|
|
147
|
+
});
|
|
148
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/client.ts","../src/fetchers.ts"],"sourcesContent":["/**\n * Public entry — transport-agnostic. Use this in non-React code\n * (Node, scripts, edge workers). React hosts should import from\n * `snipe-auth-rbac/react`.\n */\n\nexport type {\n Action,\n AuthRbacFetcher,\n CompanyMembership,\n FrontendConfig,\n PermissionGrid,\n PermissionMap,\n ResourceDescriptor,\n ResourceRegistry,\n ResourceScope,\n RoleSummary,\n UserProfile,\n} from \"./types.js\";\n\nexport {\n buildPermissionResolver,\n groupResources,\n type AuthRbacClient,\n type CanOptions,\n type ClientOptions,\n} from \"./client.js\";\n\nexport {\n createSupabaseFetcher,\n createHttpFetcher,\n} from \"./fetchers.js\";\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 * Built-in fetchers — adopters can use these or pass their own\n * implementation of `AuthRbacFetcher`.\n */\n\nimport type { AuthRbacFetcher, UserProfile } from \"./types.js\";\n\n/**\n * Calls the package's SQL function `auth_rbac_user_profile(uuid)` via\n * a Supabase JS client. Easiest path when the host project already\n * uses Supabase.\n *\n * @example\n * createSupabaseFetcher({ supabase, userId: session.user.id })\n */\nexport function createSupabaseFetcher(opts: {\n supabase: {\n rpc: (\n fn: string,\n args: Record<string, unknown>,\n ) => Promise<{ data: unknown; error: { message: string } | null }>;\n };\n userId: string;\n}): AuthRbacFetcher {\n return {\n async fetchProfile(): Promise<UserProfile> {\n const { data, error } = await opts.supabase.rpc(\n \"auth_rbac_user_profile\",\n { p_user_id: opts.userId },\n );\n if (error) {\n throw new Error(\n `auth-rbac: failed to load user profile via Supabase RPC: ${error.message}`,\n );\n }\n return normalizeProfile(data);\n },\n };\n}\n\n/**\n * Calls a regular HTTP endpoint that returns a `UserProfile` JSON\n * payload. Use this when the host project has its own backend that\n * wraps the package's Python helpers (or any equivalent).\n *\n * @example\n * createHttpFetcher({ url: \"/api/users/me/profile\" })\n */\nexport function createHttpFetcher(opts: {\n url: string;\n /** Forwarded as-is to `fetch`. Use this to attach auth headers. */\n init?: RequestInit;\n /** Override the global `fetch` if you're in a non-browser env. */\n fetch?: typeof fetch;\n}): AuthRbacFetcher {\n const fetchImpl = opts.fetch ?? globalThis.fetch;\n return {\n async fetchProfile(): Promise<UserProfile> {\n const res = await fetchImpl(opts.url, opts.init);\n if (!res.ok) {\n throw new Error(\n `auth-rbac: profile endpoint ${opts.url} returned ${res.status}`,\n );\n }\n const json = (await res.json()) as unknown;\n return normalizeProfile(json);\n },\n };\n}\n\n/**\n * Defensive normalisation: the Supabase RPC returns whatever the SQL\n * function emitted. We coerce missing fields into the empty defaults\n * so consumers can iterate without null checks. Throws if the shape\n * is unrecognisable.\n */\nfunction normalizeProfile(raw: unknown): UserProfile {\n if (!raw || typeof raw !== \"object\") {\n throw new Error(\"auth-rbac: profile payload is not an object\");\n }\n const p = raw as Partial<UserProfile> & Record<string, unknown>;\n if (typeof p.user_id !== \"string\") {\n throw new Error(\"auth-rbac: profile payload missing user_id\");\n }\n return {\n user_id: p.user_id,\n is_super_admin: !!p.is_super_admin,\n system_roles: Array.isArray(p.system_roles) ? p.system_roles : [],\n system_permissions:\n p.system_permissions && typeof p.system_permissions === \"object\"\n ? (p.system_permissions as UserProfile[\"system_permissions\"])\n : {},\n system_frontend_config:\n p.system_frontend_config && typeof p.system_frontend_config === \"object\"\n ? (p.system_frontend_config as UserProfile[\"system_frontend_config\"])\n : {},\n memberships: Array.isArray(p.memberships)\n ? (p.memberships as UserProfile[\"memberships\"])\n : [],\n };\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { c as ResourceRegistry, U as UserProfile, A as Action, d as PermissionMap, b as AuthRbacFetcher, a as ResourceDescriptor } from './types-BEc5SCIo.cjs';
|
|
2
|
+
export { C as CompanyMembership, F as FrontendConfig, P as PermissionGrid, R as ResourceScope, e as RoleSummary } from './types-BEc5SCIo.cjs';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Transport-agnostic client: turns an adopter-supplied
|
|
6
|
+
* `AuthRbacFetcher` into a permission resolver. The React provider
|
|
7
|
+
* wraps this; non-React consumers (Node scripts, edge functions)
|
|
8
|
+
* can use it directly.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
interface AuthRbacClientOptions {
|
|
12
|
+
fetcher: AuthRbacFetcher;
|
|
13
|
+
/**
|
|
14
|
+
* The host project's full resource list. Required so the resolver
|
|
15
|
+
* can look up a resource's scope without a DB round-trip per call.
|
|
16
|
+
* Re-using the same array the host syncs into the
|
|
17
|
+
* `auth_rbac_resources` table at boot keeps everything in lockstep.
|
|
18
|
+
*/
|
|
19
|
+
resources: ResourceRegistry;
|
|
20
|
+
}
|
|
21
|
+
interface CanOptions {
|
|
22
|
+
/**
|
|
23
|
+
* Override the active company. Omit to use the company the
|
|
24
|
+
* caller has currently activated (the React provider tracks
|
|
25
|
+
* this; for direct client use you must pass it).
|
|
26
|
+
*/
|
|
27
|
+
companyId?: string | null;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Pure resolver. Given a hydrated profile it answers boolean
|
|
31
|
+
* questions instantly — no I/O. The `resourceMap` is built once at
|
|
32
|
+
* construction so per-call work is two map lookups.
|
|
33
|
+
*/
|
|
34
|
+
declare function buildPermissionResolver(resources: ResourceRegistry, profile: UserProfile, defaultCompanyId: string | null): {
|
|
35
|
+
can: (resource: string, action: Action, options?: CanOptions) => boolean;
|
|
36
|
+
/** Permission map for the active (or specified) company. */
|
|
37
|
+
activePermissions: (companyId?: string | null) => PermissionMap;
|
|
38
|
+
systemPermissions: () => PermissionMap;
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Helper: groups a resource registry by `group` for the matrix UI.
|
|
42
|
+
* Returns groups in insertion order with their resources.
|
|
43
|
+
*/
|
|
44
|
+
declare function groupResources(registry: ResourceRegistry): Array<{
|
|
45
|
+
group: string;
|
|
46
|
+
resources: ResourceDescriptor[];
|
|
47
|
+
}>;
|
|
48
|
+
type AuthRbacClient = ReturnType<typeof buildPermissionResolver>;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Built-in fetchers — adopters can use these or pass their own
|
|
52
|
+
* implementation of `AuthRbacFetcher`.
|
|
53
|
+
*/
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Calls the package's SQL function `auth_rbac_user_profile(uuid)` via
|
|
57
|
+
* a Supabase JS client. Easiest path when the host project already
|
|
58
|
+
* uses Supabase.
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* createSupabaseFetcher({ supabase, userId: session.user.id })
|
|
62
|
+
*/
|
|
63
|
+
declare function createSupabaseFetcher(opts: {
|
|
64
|
+
supabase: {
|
|
65
|
+
rpc: (fn: string, args: Record<string, unknown>) => Promise<{
|
|
66
|
+
data: unknown;
|
|
67
|
+
error: {
|
|
68
|
+
message: string;
|
|
69
|
+
} | null;
|
|
70
|
+
}>;
|
|
71
|
+
};
|
|
72
|
+
userId: string;
|
|
73
|
+
}): AuthRbacFetcher;
|
|
74
|
+
/**
|
|
75
|
+
* Calls a regular HTTP endpoint that returns a `UserProfile` JSON
|
|
76
|
+
* payload. Use this when the host project has its own backend that
|
|
77
|
+
* wraps the package's Python helpers (or any equivalent).
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* createHttpFetcher({ url: "/api/users/me/profile" })
|
|
81
|
+
*/
|
|
82
|
+
declare function createHttpFetcher(opts: {
|
|
83
|
+
url: string;
|
|
84
|
+
/** Forwarded as-is to `fetch`. Use this to attach auth headers. */
|
|
85
|
+
init?: RequestInit;
|
|
86
|
+
/** Override the global `fetch` if you're in a non-browser env. */
|
|
87
|
+
fetch?: typeof fetch;
|
|
88
|
+
}): AuthRbacFetcher;
|
|
89
|
+
|
|
90
|
+
export { Action, type AuthRbacClient, AuthRbacFetcher, type CanOptions, type AuthRbacClientOptions as ClientOptions, PermissionMap, ResourceDescriptor, ResourceRegistry, UserProfile, buildPermissionResolver, createHttpFetcher, createSupabaseFetcher, groupResources };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { c as ResourceRegistry, U as UserProfile, A as Action, d as PermissionMap, b as AuthRbacFetcher, a as ResourceDescriptor } from './types-BEc5SCIo.js';
|
|
2
|
+
export { C as CompanyMembership, F as FrontendConfig, P as PermissionGrid, R as ResourceScope, e as RoleSummary } from './types-BEc5SCIo.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Transport-agnostic client: turns an adopter-supplied
|
|
6
|
+
* `AuthRbacFetcher` into a permission resolver. The React provider
|
|
7
|
+
* wraps this; non-React consumers (Node scripts, edge functions)
|
|
8
|
+
* can use it directly.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
interface AuthRbacClientOptions {
|
|
12
|
+
fetcher: AuthRbacFetcher;
|
|
13
|
+
/**
|
|
14
|
+
* The host project's full resource list. Required so the resolver
|
|
15
|
+
* can look up a resource's scope without a DB round-trip per call.
|
|
16
|
+
* Re-using the same array the host syncs into the
|
|
17
|
+
* `auth_rbac_resources` table at boot keeps everything in lockstep.
|
|
18
|
+
*/
|
|
19
|
+
resources: ResourceRegistry;
|
|
20
|
+
}
|
|
21
|
+
interface CanOptions {
|
|
22
|
+
/**
|
|
23
|
+
* Override the active company. Omit to use the company the
|
|
24
|
+
* caller has currently activated (the React provider tracks
|
|
25
|
+
* this; for direct client use you must pass it).
|
|
26
|
+
*/
|
|
27
|
+
companyId?: string | null;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Pure resolver. Given a hydrated profile it answers boolean
|
|
31
|
+
* questions instantly — no I/O. The `resourceMap` is built once at
|
|
32
|
+
* construction so per-call work is two map lookups.
|
|
33
|
+
*/
|
|
34
|
+
declare function buildPermissionResolver(resources: ResourceRegistry, profile: UserProfile, defaultCompanyId: string | null): {
|
|
35
|
+
can: (resource: string, action: Action, options?: CanOptions) => boolean;
|
|
36
|
+
/** Permission map for the active (or specified) company. */
|
|
37
|
+
activePermissions: (companyId?: string | null) => PermissionMap;
|
|
38
|
+
systemPermissions: () => PermissionMap;
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Helper: groups a resource registry by `group` for the matrix UI.
|
|
42
|
+
* Returns groups in insertion order with their resources.
|
|
43
|
+
*/
|
|
44
|
+
declare function groupResources(registry: ResourceRegistry): Array<{
|
|
45
|
+
group: string;
|
|
46
|
+
resources: ResourceDescriptor[];
|
|
47
|
+
}>;
|
|
48
|
+
type AuthRbacClient = ReturnType<typeof buildPermissionResolver>;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Built-in fetchers — adopters can use these or pass their own
|
|
52
|
+
* implementation of `AuthRbacFetcher`.
|
|
53
|
+
*/
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Calls the package's SQL function `auth_rbac_user_profile(uuid)` via
|
|
57
|
+
* a Supabase JS client. Easiest path when the host project already
|
|
58
|
+
* uses Supabase.
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* createSupabaseFetcher({ supabase, userId: session.user.id })
|
|
62
|
+
*/
|
|
63
|
+
declare function createSupabaseFetcher(opts: {
|
|
64
|
+
supabase: {
|
|
65
|
+
rpc: (fn: string, args: Record<string, unknown>) => Promise<{
|
|
66
|
+
data: unknown;
|
|
67
|
+
error: {
|
|
68
|
+
message: string;
|
|
69
|
+
} | null;
|
|
70
|
+
}>;
|
|
71
|
+
};
|
|
72
|
+
userId: string;
|
|
73
|
+
}): AuthRbacFetcher;
|
|
74
|
+
/**
|
|
75
|
+
* Calls a regular HTTP endpoint that returns a `UserProfile` JSON
|
|
76
|
+
* payload. Use this when the host project has its own backend that
|
|
77
|
+
* wraps the package's Python helpers (or any equivalent).
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* createHttpFetcher({ url: "/api/users/me/profile" })
|
|
81
|
+
*/
|
|
82
|
+
declare function createHttpFetcher(opts: {
|
|
83
|
+
url: string;
|
|
84
|
+
/** Forwarded as-is to `fetch`. Use this to attach auth headers. */
|
|
85
|
+
init?: RequestInit;
|
|
86
|
+
/** Override the global `fetch` if you're in a non-browser env. */
|
|
87
|
+
fetch?: typeof fetch;
|
|
88
|
+
}): AuthRbacFetcher;
|
|
89
|
+
|
|
90
|
+
export { Action, type AuthRbacClient, AuthRbacFetcher, type CanOptions, type AuthRbacClientOptions as ClientOptions, PermissionMap, ResourceDescriptor, ResourceRegistry, UserProfile, buildPermissionResolver, createHttpFetcher, createSupabaseFetcher, groupResources };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createHttpFetcher,
|
|
3
|
+
createSupabaseFetcher
|
|
4
|
+
} from "./chunk-BRCJUCDG.js";
|
|
5
|
+
import {
|
|
6
|
+
buildPermissionResolver,
|
|
7
|
+
groupResources
|
|
8
|
+
} from "./chunk-4WTV6J44.js";
|
|
9
|
+
export {
|
|
10
|
+
buildPermissionResolver,
|
|
11
|
+
createHttpFetcher,
|
|
12
|
+
createSupabaseFetcher,
|
|
13
|
+
groupResources
|
|
14
|
+
};
|
|
15
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|