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.
@@ -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":[]}
@@ -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 };
@@ -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":[]}