@cfast/permissions 0.0.1 → 0.2.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/llms.txt ADDED
@@ -0,0 +1,233 @@
1
+ # @cfast/permissions
2
+
3
+ > Isomorphic, Drizzle-native permission system. Define permissions once, enforce as SQL WHERE clauses.
4
+
5
+ ## When to use
6
+ Use this package to define role-based permissions that compile down to Drizzle `where` clauses for Cloudflare D1. Permissions defined here are consumed by `@cfast/db` for server enforcement and `@cfast/actions` for client-side UI introspection.
7
+
8
+ ## Key concepts
9
+ - **Permissions are WHERE clauses**: A read permission becomes a SQL filter. You don't write a check then repeat the logic in your query.
10
+ - **Two layers**: (1) Structural -- "does this role have any grant for update on posts?" (2) Row-level -- "does the grant's WHERE clause include this specific row?" Structural checks are cheap and work client-side. Row-level checks happen at execution time.
11
+ - **`"manage"` = all CRUD**: A shorthand for granting `read`, `create`, `update`, `delete`.
12
+ - **`"all"` = every table**: `grant("manage", "all")` grants full access to everything.
13
+ - **Role hierarchy**: Roles can inherit from parent roles. Multiple WHERE clauses on the same action+table are OR'd (more permissive grant wins).
14
+
15
+ ## API Reference
16
+
17
+ ### `definePermissions(config): Permissions`
18
+ ```typescript
19
+ import { definePermissions, grant } from "@cfast/permissions";
20
+
21
+ const permissions = definePermissions({
22
+ roles: ["anonymous", "user", "admin"] as const,
23
+ grants: {
24
+ anonymous: [ grant("read", posts, { where: (post) => eq(post.published, true) }) ],
25
+ user: [ grant("read", posts), grant("create", posts) ],
26
+ admin: [ grant("manage", "all") ],
27
+ },
28
+ hierarchy: { // optional
29
+ user: ["anonymous"], // user inherits anonymous grants
30
+ admin: ["user"], // admin inherits user grants
31
+ },
32
+ });
33
+ ```
34
+
35
+ **Curried form** for typed user in WHERE clauses:
36
+ ```typescript
37
+ const permissions = definePermissions<MyUser>()({
38
+ roles: [...] as const,
39
+ grants: (grant) => ({
40
+ user: [ grant("update", posts, { where: (post, user) => eq(post.authorId, user.id) }) ],
41
+ // ...
42
+ }),
43
+ });
44
+ ```
45
+
46
+ **Curried form with schema** to constrain string subjects to known table names at compile time:
47
+ ```typescript
48
+ import * as schema from "./schema";
49
+
50
+ const permissions = definePermissions<MyUser, typeof schema>()({
51
+ roles: ["member", "admin"] as const,
52
+ grants: (grant) => ({
53
+ member: [
54
+ grant("read", "projects"), // ✓ string form, type-checked
55
+ grant("read", schema.projects), // ✓ object form still works
56
+ // grant("read", "unknownTable"), // ✗ TypeScript error
57
+ ],
58
+ admin: [grant("manage", "all")],
59
+ }),
60
+ });
61
+ ```
62
+
63
+ **Returns** `Permissions<TRoles>`:
64
+ ```typescript
65
+ type Permissions<TRoles> = {
66
+ roles: TRoles;
67
+ grants: Record<TRoles[number], Grant[]>;
68
+ resolvedGrants: Record<TRoles[number], Grant[]>; // hierarchy-expanded
69
+ };
70
+ ```
71
+
72
+ ### `grant(action, subject, options?): Grant`
73
+ ```typescript
74
+ grant(action: PermissionAction, subject: DrizzleTable | string, options?: { where?: WhereClause }): Grant
75
+ ```
76
+ - `PermissionAction`: `"read" | "create" | "update" | "delete" | "manage"`
77
+ - `WhereClause`: `(columns, user) => DrizzleSQL | undefined`
78
+ - `subject` may be a Drizzle table object, a string table name (e.g. `"projects"`), or `"all"`. String and object forms are interchangeable at runtime — both are normalized to the same key by `getTableName()`. To get compile-time validation that a string subject is a known table, use the `definePermissions<User, typeof schema>()` curried form.
79
+
80
+ ### `checkPermissions(role, permissions, descriptors): PermissionCheckResult`
81
+ ```typescript
82
+ import { checkPermissions } from "@cfast/permissions";
83
+
84
+ const result = checkPermissions("user", permissions, [
85
+ { action: "update", table: posts }, // object form
86
+ { action: "create", table: "comments" }, // string form also accepted
87
+ ]);
88
+ result.permitted; // boolean
89
+ result.denied; // PermissionDescriptor[]
90
+ result.reasons; // string[]
91
+ ```
92
+
93
+ ### `resolveGrants(permissions, userOrRoles): Grant[]`
94
+ Merges grants from multiple roles into a flat array. Deduplicates by action+subject and OR-merges WHERE clauses.
95
+
96
+ Two calling styles are supported (both equivalent):
97
+
98
+ ```typescript
99
+ // Preferred: pass the user directly — the function extracts user.roles
100
+ const grants = resolveGrants(permissions, user);
101
+
102
+ // Legacy: pass the roles array yourself (still supported)
103
+ const grants = resolveGrants(permissions, user.roles);
104
+ ```
105
+
106
+ The user-object form accepts any value with a `roles: readonly string[]` field, so it works directly with the user shape from `@cfast/auth` without manual extraction.
107
+
108
+ ### `ForbiddenError`
109
+ ```typescript
110
+ import { ForbiddenError } from "@cfast/permissions";
111
+
112
+ class ForbiddenError extends Error {
113
+ readonly action: PermissionAction;
114
+ readonly table: DrizzleTable;
115
+ readonly role: string | undefined;
116
+ readonly descriptors: PermissionDescriptor[];
117
+ toJSON(): { name, message, action, table, role };
118
+ }
119
+ ```
120
+
121
+ ### `can(grants, action, table): boolean`
122
+ ```typescript
123
+ import { can } from "@cfast/permissions";
124
+ function can<TTables extends Record<string, DrizzleTable> = Record<string, DrizzleTable>>(
125
+ grants: Grant[],
126
+ action: PermissionAction,
127
+ table: DrizzleTable | string,
128
+ ): boolean
129
+ ```
130
+ Quick grant-level permission check that works directly with resolved grants. Unlike `checkPermissions` (which takes a role string + full permissions object), `can` works with the user's resolved grants -- the same grants available in loaders, actions, and client-side. Returns `true` if any grant permits the action on the table (including `manage`/`all` wildcards).
131
+
132
+ Accepts either a Drizzle table object or a string table name. To get compile-time validation that a string is an actual table, supply the schema map as a generic argument.
133
+
134
+ ```typescript
135
+ import * as schema from "../db/schema";
136
+
137
+ can(grants, "create", schema.posts) // ✓ object form
138
+ can(grants, "create", "posts") // ✓ string form (untyped)
139
+ can<typeof schema>(grants, "create", "posts") // ✓ string form, type-checked
140
+ // can<typeof schema>(grants, "create", "unknownTable") // ✗ TypeScript error
141
+ ```
142
+
143
+ ### `CRUD_ACTIONS`
144
+ ```typescript
145
+ const CRUD_ACTIONS: readonly CrudAction[] = ["read", "create", "update", "delete"];
146
+ ```
147
+
148
+ ### Client entrypoint
149
+ ```typescript
150
+ import { ForbiddenError, can } from "@cfast/permissions/client";
151
+ import type { PermissionAction, CrudAction, PermissionDescriptor, PermissionCheckResult } from "@cfast/permissions/client";
152
+ ```
153
+ Use this in client bundles to avoid importing server-only code. `can` is available from both the main and client entrypoints.
154
+
155
+ ## Usage Examples
156
+
157
+ ### Ownership-based permissions with hierarchy
158
+ ```typescript
159
+ import { definePermissions, grant } from "@cfast/permissions";
160
+ import { eq } from "drizzle-orm";
161
+ import { posts, comments } from "./schema";
162
+
163
+ export const permissions = definePermissions({
164
+ roles: ["anonymous", "user", "editor", "admin"] as const,
165
+ hierarchy: {
166
+ user: ["anonymous"],
167
+ editor: ["user"],
168
+ admin: ["editor"],
169
+ },
170
+ grants: {
171
+ anonymous: [
172
+ grant("read", posts, { where: (p) => eq(p.published, true) }),
173
+ ],
174
+ user: [
175
+ grant("create", posts),
176
+ grant("update", posts, { where: (p, u) => eq(p.authorId, u.id) }),
177
+ grant("delete", posts, { where: (p, u) => eq(p.authorId, u.id) }),
178
+ ],
179
+ editor: [
180
+ grant("read", posts), // unrestricted overrides inherited "published only"
181
+ grant("update", posts),
182
+ grant("delete", posts),
183
+ ],
184
+ admin: [
185
+ grant("manage", "all"),
186
+ ],
187
+ },
188
+ });
189
+ ```
190
+
191
+ ### Quick permission check with `can()`
192
+ ```typescript
193
+ import { can } from "@cfast/permissions";
194
+
195
+ // In a loader/action -- grants come from auth.requireUser()
196
+ const { grants } = await auth.requireUser(request);
197
+ if (can(grants, "update", posts)) {
198
+ // user has structural permission to update posts
199
+ }
200
+
201
+ // In a React component -- grants come from loader data
202
+ function PostCard({ post, grants }) {
203
+ return (
204
+ <div>
205
+ <h2>{post.title}</h2>
206
+ {can(grants, "update", posts) && <Button>Edit</Button>}
207
+ {can(grants, "delete", posts) && <Button color="danger">Delete</Button>}
208
+ </div>
209
+ );
210
+ }
211
+ ```
212
+
213
+ ### Checking permissions manually
214
+ ```typescript
215
+ const result = checkPermissions("user", permissions, [
216
+ { action: "delete", table: posts },
217
+ ]);
218
+ if (!result.permitted) {
219
+ console.log(result.reasons); // ["Role 'user' cannot delete on 'posts'"] -- if no grant exists
220
+ }
221
+ ```
222
+
223
+ ## Integration
224
+ - **`@cfast/db`**: Pass `permissions` to `createDb()`. Operations auto-enforce permissions.
225
+ - **`@cfast/actions`**: Actions extract `PermissionDescriptor[]` from operations for client-side `permitted` booleans.
226
+ - **`@cfast/admin`**: Admin CRUD goes through the same permission system.
227
+
228
+ ## Common Mistakes
229
+ - **Forgetting `as const` on roles array** -- without it, TypeScript infers `string[]` and you lose type checking on grant keys.
230
+ - **Expecting WHERE on `"create"` grants** -- create is boolean (can/can't). There's no existing row to filter.
231
+ - **Circular hierarchy** -- detected at runtime and throws `Error`. E.g., A inherits B inherits A.
232
+ - **Using `checkPermissions` for row-level security** -- it only does structural checks (action + table). Row-level WHERE enforcement happens at execution time in `@cfast/db`.
233
+ - **Importing `definePermissions` in client bundles** -- use `@cfast/permissions/client` instead to keep the bundle small.
package/package.json CHANGED
@@ -1,7 +1,14 @@
1
1
  {
2
2
  "name": "@cfast/permissions",
3
- "version": "0.0.1",
3
+ "version": "0.2.0",
4
4
  "description": "Isomorphic, composable permission system with Drizzle-native row-level access control",
5
+ "keywords": [
6
+ "cfast",
7
+ "cloudflare-workers",
8
+ "permissions",
9
+ "rbac",
10
+ "access-control"
11
+ ],
5
12
  "license": "MIT",
6
13
  "repository": {
7
14
  "type": "git",
@@ -22,7 +29,8 @@
22
29
  }
23
30
  },
24
31
  "files": [
25
- "dist"
32
+ "dist",
33
+ "llms.txt"
26
34
  ],
27
35
  "sideEffects": false,
28
36
  "publishConfig": {
@@ -1,32 +0,0 @@
1
- // src/errors.ts
2
- function getTableName(table) {
3
- return table._?.name ?? "unknown";
4
- }
5
- var ForbiddenError = class extends Error {
6
- action;
7
- table;
8
- role;
9
- descriptors;
10
- constructor(options) {
11
- const tableName = getTableName(options.table);
12
- super(`Role '${options.role}' cannot ${options.action} on '${tableName}'`);
13
- this.name = "ForbiddenError";
14
- this.action = options.action;
15
- this.table = options.table;
16
- this.role = options.role;
17
- this.descriptors = options.descriptors ?? [];
18
- }
19
- toJSON() {
20
- return {
21
- name: this.name,
22
- message: this.message,
23
- action: this.action,
24
- table: getTableName(this.table),
25
- role: this.role
26
- };
27
- }
28
- };
29
-
30
- export {
31
- ForbiddenError
32
- };
@@ -1,53 +0,0 @@
1
- // src/types.ts
2
- var DRIZZLE_NAME_SYMBOL = /* @__PURE__ */ Symbol.for("drizzle:Name");
3
- function getTableName(table) {
4
- return table[DRIZZLE_NAME_SYMBOL] ?? "unknown";
5
- }
6
- var CRUD_ACTIONS = ["read", "create", "update", "delete"];
7
-
8
- // src/errors.ts
9
- var ForbiddenError = class extends Error {
10
- /** The action that was denied (e.g., `"delete"`). */
11
- action;
12
- /** The Drizzle table the action targeted. */
13
- table;
14
- /** The role that lacked the permission, or `undefined` if not specified. */
15
- role;
16
- /** The full list of permission descriptors that were checked. */
17
- descriptors;
18
- /**
19
- * Creates a new `ForbiddenError`.
20
- *
21
- * @param options - The action, table, and optional role/descriptors for the error.
22
- */
23
- constructor(options) {
24
- const tableName = getTableName(options.table);
25
- const msg = options.role ? `Role '${options.role}' cannot ${options.action} on '${tableName}'` : `Cannot ${options.action} on '${tableName}'`;
26
- super(msg);
27
- this.name = "ForbiddenError";
28
- this.action = options.action;
29
- this.table = options.table;
30
- this.role = options.role;
31
- this.descriptors = options.descriptors ?? [];
32
- }
33
- /**
34
- * Serializes the error to a JSON-safe object for server-to-client transfer.
35
- *
36
- * @returns A plain object with `name`, `message`, `action`, `table`, and `role` fields.
37
- */
38
- toJSON() {
39
- return {
40
- name: this.name,
41
- message: this.message,
42
- action: this.action,
43
- table: getTableName(this.table),
44
- role: this.role
45
- };
46
- }
47
- };
48
-
49
- export {
50
- getTableName,
51
- CRUD_ACTIONS,
52
- ForbiddenError
53
- };
@@ -1,39 +0,0 @@
1
- // src/types.ts
2
- var DRIZZLE_NAME_SYMBOL = /* @__PURE__ */ Symbol.for("drizzle:Name");
3
- function getTableName(table) {
4
- return table[DRIZZLE_NAME_SYMBOL] ?? "unknown";
5
- }
6
- var CRUD_ACTIONS = ["read", "create", "update", "delete"];
7
-
8
- // src/errors.ts
9
- var ForbiddenError = class extends Error {
10
- action;
11
- table;
12
- role;
13
- descriptors;
14
- constructor(options) {
15
- const tableName = getTableName(options.table);
16
- const msg = options.role ? `Role '${options.role}' cannot ${options.action} on '${tableName}'` : `Cannot ${options.action} on '${tableName}'`;
17
- super(msg);
18
- this.name = "ForbiddenError";
19
- this.action = options.action;
20
- this.table = options.table;
21
- this.role = options.role;
22
- this.descriptors = options.descriptors ?? [];
23
- }
24
- toJSON() {
25
- return {
26
- name: this.name,
27
- message: this.message,
28
- action: this.action,
29
- table: getTableName(this.table),
30
- role: this.role
31
- };
32
- }
33
- };
34
-
35
- export {
36
- getTableName,
37
- CRUD_ACTIONS,
38
- ForbiddenError
39
- };
@@ -1,33 +0,0 @@
1
- // src/errors.ts
2
- function getTableName(table) {
3
- return table._?.name ?? "unknown";
4
- }
5
- var ForbiddenError = class extends Error {
6
- action;
7
- table;
8
- role;
9
- descriptors;
10
- constructor(options) {
11
- const tableName = getTableName(options.table);
12
- const msg = options.role ? `Role '${options.role}' cannot ${options.action} on '${tableName}'` : `Cannot ${options.action} on '${tableName}'`;
13
- super(msg);
14
- this.name = "ForbiddenError";
15
- this.action = options.action;
16
- this.table = options.table;
17
- this.role = options.role;
18
- this.descriptors = options.descriptors ?? [];
19
- }
20
- toJSON() {
21
- return {
22
- name: this.name,
23
- message: this.message,
24
- action: this.action,
25
- table: getTableName(this.table),
26
- role: this.role
27
- };
28
- }
29
- };
30
-
31
- export {
32
- ForbiddenError
33
- };
@@ -1,38 +0,0 @@
1
- // src/types.ts
2
- function getTableName(table) {
3
- return table._?.name ?? "unknown";
4
- }
5
- var CRUD_ACTIONS = ["read", "create", "update", "delete"];
6
-
7
- // src/errors.ts
8
- var ForbiddenError = class extends Error {
9
- action;
10
- table;
11
- role;
12
- descriptors;
13
- constructor(options) {
14
- const tableName = getTableName(options.table);
15
- const msg = options.role ? `Role '${options.role}' cannot ${options.action} on '${tableName}'` : `Cannot ${options.action} on '${tableName}'`;
16
- super(msg);
17
- this.name = "ForbiddenError";
18
- this.action = options.action;
19
- this.table = options.table;
20
- this.role = options.role;
21
- this.descriptors = options.descriptors ?? [];
22
- }
23
- toJSON() {
24
- return {
25
- name: this.name,
26
- message: this.message,
27
- action: this.action,
28
- table: getTableName(this.table),
29
- role: this.role
30
- };
31
- }
32
- };
33
-
34
- export {
35
- getTableName,
36
- CRUD_ACTIONS,
37
- ForbiddenError
38
- };
@@ -1,62 +0,0 @@
1
- type DrizzleTable = {
2
- _: {
3
- name: string;
4
- };
5
- };
6
- type DrizzleSQL = {
7
- getSQL(): unknown;
8
- };
9
- type PermissionAction = "read" | "create" | "update" | "delete" | "manage";
10
- type CrudAction = Exclude<PermissionAction, "manage">;
11
- declare const CRUD_ACTIONS: readonly CrudAction[];
12
- type WhereClause = (columns: Record<string, unknown>, user: any) => DrizzleSQL | undefined;
13
- type Grant = {
14
- action: PermissionAction;
15
- subject: DrizzleTable | "all";
16
- where?: WhereClause;
17
- };
18
- type GrantFn<TUser> = (action: PermissionAction, subject: DrizzleTable | "all", options?: {
19
- where?: (columns: Record<string, unknown>, user: TUser) => DrizzleSQL | undefined;
20
- }) => Grant;
21
- type PermissionDescriptor = {
22
- action: PermissionAction;
23
- table: DrizzleTable;
24
- };
25
- type PermissionCheckResult = {
26
- permitted: boolean;
27
- denied: PermissionDescriptor[];
28
- reasons: string[];
29
- };
30
- type PermissionsConfig<TRoles extends readonly string[], TUser = unknown> = {
31
- roles: TRoles;
32
- grants: Record<TRoles[number], Grant[]> | ((grant: GrantFn<TUser>) => Record<TRoles[number], Grant[]>);
33
- hierarchy?: Partial<Record<TRoles[number], TRoles[number][]>>;
34
- };
35
- type Permissions<TRoles extends readonly string[] = readonly string[]> = {
36
- roles: TRoles;
37
- grants: Record<TRoles[number], Grant[]>;
38
- resolvedGrants: Record<TRoles[number], Grant[]>;
39
- };
40
-
41
- type ForbiddenErrorOptions = {
42
- action: PermissionAction;
43
- table: DrizzleTable;
44
- role?: string;
45
- descriptors?: PermissionDescriptor[];
46
- };
47
- declare class ForbiddenError extends Error {
48
- readonly action: PermissionAction;
49
- readonly table: DrizzleTable;
50
- readonly role: string | undefined;
51
- readonly descriptors: PermissionDescriptor[];
52
- constructor(options: ForbiddenErrorOptions);
53
- toJSON(): {
54
- name: string;
55
- message: string;
56
- action: PermissionAction;
57
- table: string;
58
- role: string | undefined;
59
- };
60
- }
61
-
62
- export { CRUD_ACTIONS as C, type DrizzleTable as D, ForbiddenError as F, type Grant as G, type PermissionsConfig as P, type WhereClause as W, type Permissions as a, type PermissionAction as b, type PermissionDescriptor as c, type PermissionCheckResult as d, type CrudAction as e, type GrantFn as f };
@@ -1,61 +0,0 @@
1
- import { SQL } from 'drizzle-orm';
2
-
3
- type DrizzleTable = {
4
- _: {
5
- name: string;
6
- };
7
- };
8
- type PermissionAction = "read" | "create" | "update" | "delete" | "manage";
9
- type CrudAction = Exclude<PermissionAction, "manage">;
10
- declare const CRUD_ACTIONS: readonly CrudAction[];
11
- type WhereClause = (columns: Record<string, unknown>, user: any) => SQL | undefined;
12
- type Grant = {
13
- action: PermissionAction;
14
- subject: DrizzleTable | "all";
15
- where?: WhereClause;
16
- };
17
- type GrantFn<TUser> = (action: PermissionAction, subject: DrizzleTable | "all", options?: {
18
- where?: (columns: Record<string, unknown>, user: TUser) => SQL | undefined;
19
- }) => Grant;
20
- type PermissionDescriptor = {
21
- action: PermissionAction;
22
- table: DrizzleTable;
23
- };
24
- type PermissionCheckResult = {
25
- permitted: boolean;
26
- denied: PermissionDescriptor[];
27
- reasons: string[];
28
- };
29
- type PermissionsConfig<TRoles extends readonly string[], TUser = unknown> = {
30
- roles: TRoles;
31
- grants: Record<TRoles[number], Grant[]> | ((grant: GrantFn<TUser>) => Record<TRoles[number], Grant[]>);
32
- hierarchy?: Partial<Record<TRoles[number], TRoles[number][]>>;
33
- };
34
- type Permissions<TRoles extends readonly string[] = readonly string[]> = {
35
- roles: TRoles;
36
- grants: Record<TRoles[number], Grant[]>;
37
- resolvedGrants: Record<TRoles[number], Grant[]>;
38
- };
39
-
40
- type ForbiddenErrorOptions = {
41
- action: PermissionAction;
42
- table: DrizzleTable;
43
- role: string;
44
- descriptors?: PermissionDescriptor[];
45
- };
46
- declare class ForbiddenError extends Error {
47
- readonly action: PermissionAction;
48
- readonly table: DrizzleTable;
49
- readonly role: string;
50
- readonly descriptors: PermissionDescriptor[];
51
- constructor(options: ForbiddenErrorOptions);
52
- toJSON(): {
53
- name: string;
54
- message: string;
55
- action: PermissionAction;
56
- table: string;
57
- role: string;
58
- };
59
- }
60
-
61
- export { CRUD_ACTIONS as C, type DrizzleTable as D, ForbiddenError as F, type Grant as G, type PermissionsConfig as P, type WhereClause as W, type Permissions as a, type PermissionAction as b, type PermissionDescriptor as c, type PermissionCheckResult as d, type CrudAction as e, type GrantFn as f };
@@ -1,56 +0,0 @@
1
- import { Table, SQL } from 'drizzle-orm';
2
-
3
- type PermissionAction = "read" | "create" | "update" | "delete" | "manage";
4
- type CrudAction = Exclude<PermissionAction, "manage">;
5
- declare const CRUD_ACTIONS: readonly CrudAction[];
6
- type WhereClause = (columns: Record<string, unknown>, user: any) => SQL | undefined;
7
- type Grant = {
8
- action: PermissionAction;
9
- subject: Table | "all";
10
- where?: WhereClause;
11
- };
12
- type GrantFn<TUser> = (action: PermissionAction, subject: Table | "all", options?: {
13
- where?: (columns: Record<string, unknown>, user: TUser) => SQL | undefined;
14
- }) => Grant;
15
- type PermissionDescriptor = {
16
- action: PermissionAction;
17
- table: Table;
18
- };
19
- type PermissionCheckResult = {
20
- permitted: boolean;
21
- denied: PermissionDescriptor[];
22
- reasons: string[];
23
- };
24
- type PermissionsConfig<TRoles extends readonly string[], TUser = unknown> = {
25
- roles: TRoles;
26
- grants: Record<TRoles[number], Grant[]> | ((grant: GrantFn<TUser>) => Record<TRoles[number], Grant[]>);
27
- hierarchy?: Partial<Record<TRoles[number], TRoles[number][]>>;
28
- };
29
- type Permissions<TRoles extends readonly string[] = readonly string[]> = {
30
- roles: TRoles;
31
- grants: Record<TRoles[number], Grant[]>;
32
- resolvedGrants: Record<TRoles[number], Grant[]>;
33
- };
34
-
35
- type ForbiddenErrorOptions = {
36
- action: PermissionAction;
37
- table: Table;
38
- role: string;
39
- descriptors?: PermissionDescriptor[];
40
- };
41
- declare class ForbiddenError extends Error {
42
- readonly action: PermissionAction;
43
- readonly table: Table;
44
- readonly role: string;
45
- readonly descriptors: PermissionDescriptor[];
46
- constructor(options: ForbiddenErrorOptions);
47
- toJSON(): {
48
- name: string;
49
- message: string;
50
- action: PermissionAction;
51
- table: string;
52
- role: string;
53
- };
54
- }
55
-
56
- export { CRUD_ACTIONS as C, ForbiddenError as F, type Grant as G, type PermissionsConfig as P, type WhereClause as W, type Permissions as a, type PermissionAction as b, type PermissionDescriptor as c, type PermissionCheckResult as d, type CrudAction as e, type GrantFn as f };