@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/dist/{chunk-I35WLWUH.js → chunk-NCLN5YBQ.js} +4 -1
- package/dist/{client-CJBFS0IS.d.ts → client-C7xdwHTk.d.ts} +76 -19
- package/dist/client.d.ts +1 -1
- package/dist/client.js +1 -1
- package/dist/index.d.ts +105 -13
- package/dist/index.js +26 -7
- package/llms.txt +233 -0
- package/package.json +10 -2
- package/dist/chunk-FVAAEIAE.js +0 -32
- package/dist/chunk-IPYPD2CZ.js +0 -53
- package/dist/chunk-PNHVTXCZ.js +0 -39
- package/dist/chunk-YYYHMPTS.js +0 -33
- package/dist/chunk-ZTQJZJFW.js +0 -38
- package/dist/client-8HHp1rPO.d.ts +0 -62
- package/dist/client-BBcryLDZ.d.ts +0 -61
- package/dist/client-BKD8jH5P.d.ts +0 -56
- package/dist/client-ChpyEV1r.d.ts +0 -62
- package/dist/client-CuB6igvw.d.ts +0 -195
- package/dist/client-DduKFZmk.d.ts +0 -59
- package/dist/client-FbortuK3.d.ts +0 -53
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
// src/types.ts
|
|
2
2
|
var DRIZZLE_NAME_SYMBOL = /* @__PURE__ */ Symbol.for("drizzle:Name");
|
|
3
3
|
function getTableName(table) {
|
|
4
|
+
if (typeof table === "string") {
|
|
5
|
+
return table;
|
|
6
|
+
}
|
|
4
7
|
const name = Reflect.get(table, DRIZZLE_NAME_SYMBOL);
|
|
5
8
|
return typeof name === "string" ? name : "unknown";
|
|
6
9
|
}
|
|
@@ -10,7 +13,7 @@ var CRUD_ACTIONS = ["read", "create", "update", "delete"];
|
|
|
10
13
|
var ForbiddenError = class extends Error {
|
|
11
14
|
/** The action that was denied (e.g., `"delete"`). */
|
|
12
15
|
action;
|
|
13
|
-
/** The Drizzle table the action targeted. */
|
|
16
|
+
/** The Drizzle table or string table name the action targeted. */
|
|
14
17
|
table;
|
|
15
18
|
/** The role that lacked the permission, or `undefined` if not specified. */
|
|
16
19
|
role;
|
|
@@ -17,12 +17,46 @@ type DrizzleSQL = {
|
|
|
17
17
|
getSQL(): unknown;
|
|
18
18
|
};
|
|
19
19
|
/**
|
|
20
|
-
*
|
|
20
|
+
* A schema map: an object mapping table names to Drizzle table references.
|
|
21
21
|
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
22
|
+
* Typically the result of `import * as schema from "./schema"`. Used as the
|
|
23
|
+
* `TTables` generic parameter for {@link definePermissions}, {@link can}, and
|
|
24
|
+
* the curried {@link grant} callback so that string subjects (e.g.
|
|
25
|
+
* `"projects"`) are constrained to known table names at compile time.
|
|
24
26
|
*/
|
|
25
|
-
|
|
27
|
+
type SchemaMap = Record<string, DrizzleTable>;
|
|
28
|
+
/**
|
|
29
|
+
* Extracts the string-literal union of valid table names from a {@link SchemaMap}.
|
|
30
|
+
*
|
|
31
|
+
* Given `typeof schema` (where `schema` exports tables as named bindings),
|
|
32
|
+
* `TableName<typeof schema>` is the union of all exported table-key strings.
|
|
33
|
+
*/
|
|
34
|
+
type TableName<TTables extends SchemaMap> = Extract<keyof TTables, string>;
|
|
35
|
+
/**
|
|
36
|
+
* The set of acceptable subject inputs for a grant or `can()` check.
|
|
37
|
+
*
|
|
38
|
+
* - A {@link DrizzleTable} object reference (the original form, always allowed).
|
|
39
|
+
* - A string-literal table name from {@link TableName}, constrained to the
|
|
40
|
+
* provided `TTables` schema map at compile time when one is supplied.
|
|
41
|
+
* - The literal `"all"` for grants that apply to every table.
|
|
42
|
+
*/
|
|
43
|
+
type SubjectInput<TTables extends SchemaMap = SchemaMap> = DrizzleTable | TableName<TTables> | "all";
|
|
44
|
+
/**
|
|
45
|
+
* Extracts the table name string from a Drizzle table reference, or returns
|
|
46
|
+
* a string subject as-is.
|
|
47
|
+
*
|
|
48
|
+
* Accepts both:
|
|
49
|
+
* - A Drizzle table object containing the `drizzle:Name` symbol — the symbol
|
|
50
|
+
* is read via `Reflect.get`.
|
|
51
|
+
* - A bare string (e.g., a table-name literal like `"projects"`) — returned
|
|
52
|
+
* unchanged so callers can use the same key for grant matching whether the
|
|
53
|
+
* subject was declared as an object or a string.
|
|
54
|
+
*
|
|
55
|
+
* @param table - A Drizzle table object or a string subject key.
|
|
56
|
+
* @returns The table name, the input string, or `"unknown"` if a symbol-less
|
|
57
|
+
* object was provided.
|
|
58
|
+
*/
|
|
59
|
+
declare function getTableName(table: DrizzleTable | string): string;
|
|
26
60
|
/**
|
|
27
61
|
* A permission action: one of the four CRUD operations, or `"manage"` for all.
|
|
28
62
|
*
|
|
@@ -63,24 +97,37 @@ declare const CRUD_ACTIONS: readonly CrudAction[];
|
|
|
63
97
|
type WhereClause = (columns: Record<string, unknown>, user: unknown) => DrizzleSQL | undefined;
|
|
64
98
|
/**
|
|
65
99
|
* A single permission grant: an action on a subject, optionally restricted by a `where` clause.
|
|
100
|
+
*
|
|
101
|
+
* `subject` may be a Drizzle table object, a string table name (e.g.
|
|
102
|
+
* `"projects"`), or the literal `"all"` for grants that apply to every table.
|
|
103
|
+
* String and object subjects are normalized to the same key by
|
|
104
|
+
* {@link getTableName} during matching, so the two forms are interchangeable
|
|
105
|
+
* at runtime.
|
|
66
106
|
*/
|
|
67
107
|
type Grant = {
|
|
68
108
|
/** The permitted operation. `"manage"` is shorthand for all four CRUD actions. */
|
|
69
109
|
action: PermissionAction;
|
|
70
|
-
/**
|
|
71
|
-
|
|
110
|
+
/**
|
|
111
|
+
* The subject of the grant: a Drizzle table object, a string table name,
|
|
112
|
+
* or `"all"` for every table.
|
|
113
|
+
*/
|
|
114
|
+
subject: DrizzleTable | string;
|
|
72
115
|
/** Optional row-level filter that restricts which rows this grant covers. */
|
|
73
116
|
where?: WhereClause;
|
|
74
117
|
};
|
|
75
118
|
/**
|
|
76
|
-
* Type-safe grant builder function, parameterized by the user type
|
|
119
|
+
* Type-safe grant builder function, parameterized by the user type and an
|
|
120
|
+
* optional schema map.
|
|
77
121
|
*
|
|
78
122
|
* Used when `grants` is provided as a callback in {@link PermissionsConfig}
|
|
79
|
-
* so that `where` clauses receive a correctly typed `user` parameter
|
|
123
|
+
* so that `where` clauses receive a correctly typed `user` parameter and
|
|
124
|
+
* string subjects are constrained to known table names from `TTables`.
|
|
80
125
|
*
|
|
81
126
|
* @typeParam TUser - The user type passed to `where` clause callbacks.
|
|
127
|
+
* @typeParam TTables - Optional schema map (e.g. `typeof schema`) used to
|
|
128
|
+
* constrain string subjects to known table-name literals.
|
|
82
129
|
*/
|
|
83
|
-
type GrantFn<TUser> = (action: PermissionAction, subject:
|
|
130
|
+
type GrantFn<TUser, TTables extends SchemaMap = SchemaMap> = (action: PermissionAction, subject: SubjectInput<TTables>, options?: {
|
|
84
131
|
where?: (columns: Record<string, unknown>, user: TUser) => DrizzleSQL | undefined;
|
|
85
132
|
}) => Grant;
|
|
86
133
|
/**
|
|
@@ -90,19 +137,27 @@ type GrantFn<TUser> = (action: PermissionAction, subject: DrizzleTable | "all",
|
|
|
90
137
|
* This is what makes client-side permission introspection possible: you can check whether a
|
|
91
138
|
* role has the right grants without knowing the specific row being accessed.
|
|
92
139
|
*
|
|
140
|
+
* The `table` field accepts either a Drizzle table object or a string table
|
|
141
|
+
* name; both forms are normalized to the same key by {@link getTableName}.
|
|
142
|
+
*
|
|
93
143
|
* @example
|
|
94
144
|
* ```typescript
|
|
95
145
|
* const descriptor: PermissionDescriptor = {
|
|
96
146
|
* action: "update",
|
|
97
|
-
* table: posts,
|
|
147
|
+
* table: posts, // object form
|
|
148
|
+
* };
|
|
149
|
+
*
|
|
150
|
+
* const descriptor2: PermissionDescriptor = {
|
|
151
|
+
* action: "create",
|
|
152
|
+
* table: "posts", // string form
|
|
98
153
|
* };
|
|
99
154
|
* ```
|
|
100
155
|
*/
|
|
101
156
|
type PermissionDescriptor = {
|
|
102
157
|
/** The operation being checked. */
|
|
103
158
|
action: PermissionAction;
|
|
104
|
-
/** The Drizzle table the operation targets. */
|
|
105
|
-
table: DrizzleTable;
|
|
159
|
+
/** The Drizzle table or string table name the operation targets. */
|
|
160
|
+
table: DrizzleTable | string;
|
|
106
161
|
};
|
|
107
162
|
/**
|
|
108
163
|
* Result of a permission check via {@link checkPermissions}.
|
|
@@ -120,12 +175,14 @@ type PermissionCheckResult = {
|
|
|
120
175
|
*
|
|
121
176
|
* @typeParam TRoles - Tuple of role name string literals (use `as const`).
|
|
122
177
|
* @typeParam TUser - The user type for typed `where` clauses (defaults to `unknown`).
|
|
178
|
+
* @typeParam TTables - Optional schema map used to constrain string subjects
|
|
179
|
+
* inside the `grants` callback to known table names.
|
|
123
180
|
*/
|
|
124
|
-
type PermissionsConfig<TRoles extends readonly string[], TUser = unknown> = {
|
|
181
|
+
type PermissionsConfig<TRoles extends readonly string[], TUser = unknown, TTables extends SchemaMap = SchemaMap> = {
|
|
125
182
|
/** All roles in the application, declared with `as const` for type inference. */
|
|
126
183
|
roles: TRoles;
|
|
127
184
|
/** A map from role to grant arrays, or a callback that receives a typed `grant` function. */
|
|
128
|
-
grants: Record<TRoles[number], Grant[]> | ((grant: GrantFn<TUser>) => Record<TRoles[number], Grant[]>);
|
|
185
|
+
grants: Record<TRoles[number], Grant[]> | ((grant: GrantFn<TUser, TTables>) => Record<TRoles[number], Grant[]>);
|
|
129
186
|
/** Optional role hierarchy declaring which roles inherit from which. */
|
|
130
187
|
hierarchy?: Partial<Record<TRoles[number], TRoles[number][]>>;
|
|
131
188
|
};
|
|
@@ -151,8 +208,8 @@ type Permissions<TRoles extends readonly string[] = readonly string[]> = {
|
|
|
151
208
|
type ForbiddenErrorOptions = {
|
|
152
209
|
/** The action that was denied. */
|
|
153
210
|
action: PermissionAction;
|
|
154
|
-
/** The Drizzle table the action targeted. */
|
|
155
|
-
table: DrizzleTable;
|
|
211
|
+
/** The Drizzle table or string table name the action targeted. */
|
|
212
|
+
table: DrizzleTable | string;
|
|
156
213
|
/** The role that lacked the permission, if known. */
|
|
157
214
|
role?: string;
|
|
158
215
|
/** The full list of permission descriptors that were checked. */
|
|
@@ -168,8 +225,8 @@ type ForbiddenErrorOptions = {
|
|
|
168
225
|
declare class ForbiddenError extends Error {
|
|
169
226
|
/** The action that was denied (e.g., `"delete"`). */
|
|
170
227
|
readonly action: PermissionAction;
|
|
171
|
-
/** The Drizzle table the action targeted. */
|
|
172
|
-
readonly table: DrizzleTable;
|
|
228
|
+
/** The Drizzle table or string table name the action targeted. */
|
|
229
|
+
readonly table: DrizzleTable | string;
|
|
173
230
|
/** The role that lacked the permission, or `undefined` if not specified. */
|
|
174
231
|
readonly role: string | undefined;
|
|
175
232
|
/** The full list of permission descriptors that were checked. */
|
|
@@ -194,4 +251,4 @@ declare class ForbiddenError extends Error {
|
|
|
194
251
|
};
|
|
195
252
|
}
|
|
196
253
|
|
|
197
|
-
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
|
|
254
|
+
export { CRUD_ACTIONS as C, type DrizzleTable as D, ForbiddenError as F, type Grant as G, type PermissionsConfig as P, type SchemaMap as S, type TableName as T, type WhereClause as W, type Permissions as a, type PermissionAction as b, type SubjectInput as c, type PermissionDescriptor as d, type PermissionCheckResult as e, type CrudAction as f, type GrantFn as g, getTableName as h };
|
package/dist/client.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { f as CrudAction, F as ForbiddenError, b as PermissionAction, e as PermissionCheckResult, d as PermissionDescriptor } from './client-C7xdwHTk.js';
|
package/dist/client.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
|
-
import { P as PermissionsConfig, a as Permissions, b as PermissionAction,
|
|
2
|
-
export { C as CRUD_ACTIONS,
|
|
1
|
+
import { P as PermissionsConfig, a as Permissions, S as SchemaMap, b as PermissionAction, c as SubjectInput, W as WhereClause, G as Grant, d as PermissionDescriptor, e as PermissionCheckResult } from './client-C7xdwHTk.js';
|
|
2
|
+
export { C as CRUD_ACTIONS, f as CrudAction, D as DrizzleTable, F as ForbiddenError, g as GrantFn, T as TableName, h as getTableName } from './client-C7xdwHTk.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Creates a permission configuration that can be shared between server-side
|
|
6
6
|
* enforcement (`@cfast/db`) and client-side introspection (`@cfast/actions`).
|
|
7
7
|
*
|
|
8
|
-
* Supports
|
|
8
|
+
* Supports three calling styles:
|
|
9
9
|
* - **Direct:** `definePermissions(config)` when no custom user type is needed.
|
|
10
|
-
* - **Curried:** `definePermissions<MyUser>()(config)`
|
|
10
|
+
* - **Curried (user only):** `definePermissions<MyUser>()(config)` for typed
|
|
11
|
+
* `where` clause user parameters.
|
|
12
|
+
* - **Curried (user + tables):** `definePermissions<MyUser, typeof schema>()(config)`
|
|
13
|
+
* to additionally constrain string subjects passed to the `grant` callback
|
|
14
|
+
* to known table names from the schema map.
|
|
11
15
|
*
|
|
12
16
|
* @param config - The permissions configuration with roles, grants, and optional hierarchy.
|
|
13
17
|
* @returns A {@link Permissions} object containing roles, raw grants, and hierarchy-expanded `resolvedGrants`.
|
|
@@ -16,8 +20,10 @@ export { C as CRUD_ACTIONS, e as CrudAction, F as ForbiddenError, f as GrantFn,
|
|
|
16
20
|
* ```typescript
|
|
17
21
|
* import { definePermissions, grant } from "@cfast/permissions";
|
|
18
22
|
* import { eq } from "drizzle-orm";
|
|
19
|
-
* import
|
|
23
|
+
* import * as schema from "./schema";
|
|
24
|
+
* const { posts, comments } = schema;
|
|
20
25
|
*
|
|
26
|
+
* // Direct form — accepts table objects
|
|
21
27
|
* const permissions = definePermissions({
|
|
22
28
|
* roles: ["anonymous", "user", "admin"] as const,
|
|
23
29
|
* grants: {
|
|
@@ -27,15 +33,29 @@ export { C as CRUD_ACTIONS, e as CrudAction, F as ForbiddenError, f as GrantFn,
|
|
|
27
33
|
* user: [
|
|
28
34
|
* grant("read", posts),
|
|
29
35
|
* grant("create", posts),
|
|
30
|
-
* grant("update", posts, { where: (p, u) => eq(p.authorId, u.id) }),
|
|
31
36
|
* ],
|
|
32
37
|
* admin: [grant("manage", "all")],
|
|
33
38
|
* },
|
|
34
39
|
* });
|
|
40
|
+
*
|
|
41
|
+
* // Curried form — string subjects constrained to known tables
|
|
42
|
+
* type AuthUser = { id: string };
|
|
43
|
+
* const perms = definePermissions<AuthUser, typeof schema>()({
|
|
44
|
+
* roles: ["user", "admin"] as const,
|
|
45
|
+
* grants: (grant) => ({
|
|
46
|
+
* user: [
|
|
47
|
+
* grant("read", "posts"), // string form
|
|
48
|
+
* grant("update", posts, { // object form still works
|
|
49
|
+
* where: (p, u) => eq(p.authorId, u.id),
|
|
50
|
+
* }),
|
|
51
|
+
* ],
|
|
52
|
+
* admin: [grant("manage", "all")],
|
|
53
|
+
* }),
|
|
54
|
+
* });
|
|
35
55
|
* ```
|
|
36
56
|
*/
|
|
37
57
|
declare function definePermissions<TRoles extends readonly string[]>(config: PermissionsConfig<TRoles>): Permissions<TRoles>;
|
|
38
|
-
declare function definePermissions<TUser>(): <TRoles extends readonly string[]>(config: PermissionsConfig<TRoles, TUser>) => Permissions<TRoles>;
|
|
58
|
+
declare function definePermissions<TUser, TTables extends SchemaMap = SchemaMap>(): <TRoles extends readonly string[]>(config: PermissionsConfig<TRoles, TUser, TTables>) => Permissions<TRoles>;
|
|
39
59
|
|
|
40
60
|
/**
|
|
41
61
|
* Declares that a role can perform an action on a subject, optionally restricted
|
|
@@ -44,8 +64,15 @@ declare function definePermissions<TUser>(): <TRoles extends readonly string[]>(
|
|
|
44
64
|
* Used inside the `grants` map of {@link definePermissions} to build permission rules.
|
|
45
65
|
* A grant without a `where` clause applies to all rows.
|
|
46
66
|
*
|
|
67
|
+
* Subjects accept three forms (all interchangeable at runtime):
|
|
68
|
+
* - A {@link DrizzleTable} object reference (the original form).
|
|
69
|
+
* - A string table name (e.g. `"projects"`) — when called via the `grant`
|
|
70
|
+
* callback inside `definePermissions<User, typeof schema>()`, strings are
|
|
71
|
+
* constrained to known table names by TypeScript.
|
|
72
|
+
* - The literal `"all"` for grants that apply to every table.
|
|
73
|
+
*
|
|
47
74
|
* @param action - The operation being permitted (`"read"`, `"create"`, `"update"`, `"delete"`, or `"manage"` for all four).
|
|
48
|
-
* @param subject - A Drizzle table reference, or `"all"` to apply to every table.
|
|
75
|
+
* @param subject - A Drizzle table reference, a string table name, or `"all"` to apply to every table.
|
|
49
76
|
* @param options - Optional configuration.
|
|
50
77
|
* @param options.where - A Drizzle filter function `(columns, user) => SQL` that restricts which rows this grant covers.
|
|
51
78
|
* @returns A {@link Grant} object for use in a permissions configuration.
|
|
@@ -56,9 +83,13 @@ declare function definePermissions<TUser>(): <TRoles extends readonly string[]>(
|
|
|
56
83
|
* import { eq } from "drizzle-orm";
|
|
57
84
|
* import { posts } from "./schema";
|
|
58
85
|
*
|
|
59
|
-
* //
|
|
86
|
+
* // Object form (always works)
|
|
60
87
|
* grant("read", posts);
|
|
61
88
|
*
|
|
89
|
+
* // String form (works standalone; type-checked when called via the
|
|
90
|
+
* // `grant` callback inside `definePermissions<User, typeof schema>()`)
|
|
91
|
+
* grant("read", "posts");
|
|
92
|
+
*
|
|
62
93
|
* // Only allow updating own posts
|
|
63
94
|
* grant("update", posts, {
|
|
64
95
|
* where: (post, user) => eq(post.authorId, user.id),
|
|
@@ -68,10 +99,46 @@ declare function definePermissions<TUser>(): <TRoles extends readonly string[]>(
|
|
|
68
99
|
* grant("manage", "all");
|
|
69
100
|
* ```
|
|
70
101
|
*/
|
|
71
|
-
declare function grant(action: PermissionAction, subject:
|
|
102
|
+
declare function grant<TTables extends SchemaMap = SchemaMap>(action: PermissionAction, subject: SubjectInput<TTables>, options?: {
|
|
72
103
|
where?: WhereClause;
|
|
73
104
|
}): Grant;
|
|
74
105
|
|
|
106
|
+
/**
|
|
107
|
+
* Checks whether a set of resolved grants permits a specific action on a table.
|
|
108
|
+
*
|
|
109
|
+
* Unlike {@link checkPermissions} (which takes a role string and the full permissions
|
|
110
|
+
* object), `can` works directly with the user's resolved grants — the same grants
|
|
111
|
+
* available in loaders, actions, and client-side via `useActions`.
|
|
112
|
+
*
|
|
113
|
+
* Accepts either a Drizzle table object or a string table name. The two forms
|
|
114
|
+
* are interchangeable and resolve to the same underlying key. To get
|
|
115
|
+
* compile-time validation that a string table name actually exists in your
|
|
116
|
+
* schema, supply the schema map as a generic argument: `can<typeof schema>(...)`.
|
|
117
|
+
*
|
|
118
|
+
* @typeParam TTables - Optional schema map (e.g. `typeof schema`) used to
|
|
119
|
+
* constrain string subjects to known table-name literals.
|
|
120
|
+
* @param grants - The user's resolved permission grants.
|
|
121
|
+
* @param action - The permission action to check (e.g., `"create"`, `"read"`).
|
|
122
|
+
* @param table - The Drizzle table object or string table name to check against.
|
|
123
|
+
* @returns `true` if any grant permits the action on the table.
|
|
124
|
+
*
|
|
125
|
+
* @example
|
|
126
|
+
* ```ts
|
|
127
|
+
* import { can } from "@cfast/permissions";
|
|
128
|
+
* import * as schema from "../db/schema";
|
|
129
|
+
*
|
|
130
|
+
* // Object form (always works)
|
|
131
|
+
* if (!can(ctx.auth.grants, "create", schema.posts)) throw redirect("/");
|
|
132
|
+
*
|
|
133
|
+
* // String form (always allowed; type-checked when a schema generic is supplied)
|
|
134
|
+
* if (!can<typeof schema>(ctx.auth.grants, "create", "posts")) throw redirect("/");
|
|
135
|
+
*
|
|
136
|
+
* // In a component
|
|
137
|
+
* {can(grants, "update", schema.posts) && <Button>Edit</Button>}
|
|
138
|
+
* ```
|
|
139
|
+
*/
|
|
140
|
+
declare function can<TTables extends SchemaMap = SchemaMap>(grants: Grant[], action: PermissionAction, table: SubjectInput<TTables>): boolean;
|
|
141
|
+
|
|
75
142
|
/**
|
|
76
143
|
* Checks whether a role satisfies a set of permission descriptors.
|
|
77
144
|
*
|
|
@@ -106,6 +173,14 @@ declare function grant(action: PermissionAction, subject: DrizzleTable | "all",
|
|
|
106
173
|
*/
|
|
107
174
|
declare function checkPermissions(role: string, permissions: Permissions, descriptors: PermissionDescriptor[]): PermissionCheckResult;
|
|
108
175
|
|
|
176
|
+
/**
|
|
177
|
+
* Minimal user shape accepted by {@link resolveGrants}: any object with a
|
|
178
|
+
* `roles` array. The function reads `.roles` and forwards them to the
|
|
179
|
+
* underlying merge logic.
|
|
180
|
+
*/
|
|
181
|
+
type UserWithRoles = {
|
|
182
|
+
roles: readonly string[];
|
|
183
|
+
};
|
|
109
184
|
/**
|
|
110
185
|
* Resolves and merges grants for multiple roles into a single flat array.
|
|
111
186
|
*
|
|
@@ -117,10 +192,27 @@ declare function checkPermissions(role: string, permissions: Permissions, descri
|
|
|
117
192
|
*
|
|
118
193
|
* This is used when a user has multiple roles and their grants need to be combined.
|
|
119
194
|
*
|
|
195
|
+
* Two calling styles are supported (both equivalent):
|
|
196
|
+
* - **User object (preferred):** `resolveGrants(permissions, user)` where
|
|
197
|
+
* `user` has a `roles` field. The function extracts roles internally.
|
|
198
|
+
* - **Roles array (legacy):** `resolveGrants(permissions, roles)` where
|
|
199
|
+
* `roles` is a string array. Still supported for backwards compatibility.
|
|
200
|
+
*
|
|
120
201
|
* @param permissions - The permissions object from {@link definePermissions}.
|
|
121
|
-
* @param
|
|
202
|
+
* @param userOrRoles - Either a user object with a `roles` field, or an
|
|
203
|
+
* array of role names whose grants should be merged.
|
|
122
204
|
* @returns A deduplicated array of {@link Grant} objects with merged `where` clauses.
|
|
205
|
+
*
|
|
206
|
+
* @example
|
|
207
|
+
* ```ts
|
|
208
|
+
* // Preferred: pass the user directly
|
|
209
|
+
* const grants = resolveGrants(permissions, user);
|
|
210
|
+
*
|
|
211
|
+
* // Legacy: pass roles array (still works)
|
|
212
|
+
* const grants = resolveGrants(permissions, user.roles);
|
|
213
|
+
* ```
|
|
123
214
|
*/
|
|
124
|
-
declare function resolveGrants(permissions: Permissions,
|
|
215
|
+
declare function resolveGrants(permissions: Permissions, user: UserWithRoles): Grant[];
|
|
216
|
+
declare function resolveGrants(permissions: Permissions, roles: readonly string[]): Grant[];
|
|
125
217
|
|
|
126
|
-
export {
|
|
218
|
+
export { Grant, PermissionAction, PermissionCheckResult, PermissionDescriptor, Permissions, PermissionsConfig, SchemaMap, SubjectInput, type UserWithRoles, WhereClause, can, checkPermissions, definePermissions, grant, resolveGrants };
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
CRUD_ACTIONS,
|
|
3
3
|
ForbiddenError,
|
|
4
4
|
getTableName
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-NCLN5YBQ.js";
|
|
6
6
|
|
|
7
7
|
// src/grant.ts
|
|
8
8
|
function grant(action, subject, options) {
|
|
@@ -58,11 +58,23 @@ function resolveHierarchy(roles, grants, hierarchy) {
|
|
|
58
58
|
return resolved;
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
+
// src/can.ts
|
|
62
|
+
function can(grants, action, table) {
|
|
63
|
+
const targetKey = getTableName(table);
|
|
64
|
+
return grants.some((g) => {
|
|
65
|
+
const actionOk = g.action === action || g.action === "manage";
|
|
66
|
+
if (!actionOk) return false;
|
|
67
|
+
if (g.subject === "all") return true;
|
|
68
|
+
return getTableName(g.subject) === targetKey;
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
61
72
|
// src/check.ts
|
|
62
73
|
function grantMatches(g, action, table) {
|
|
63
74
|
const actionOk = g.action === action || g.action === "manage";
|
|
64
|
-
|
|
65
|
-
|
|
75
|
+
if (!actionOk) return false;
|
|
76
|
+
if (g.subject === "all") return true;
|
|
77
|
+
return getTableName(g.subject) === getTableName(table);
|
|
66
78
|
}
|
|
67
79
|
function hasGrantFor(grants, action, table) {
|
|
68
80
|
return grants.some((g) => grantMatches(g, action, table));
|
|
@@ -101,7 +113,11 @@ import { or } from "drizzle-orm";
|
|
|
101
113
|
function toSQLWrapper(value) {
|
|
102
114
|
return value;
|
|
103
115
|
}
|
|
104
|
-
function
|
|
116
|
+
function isUserWithRoles(value) {
|
|
117
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) && Array.isArray(value.roles);
|
|
118
|
+
}
|
|
119
|
+
function resolveGrants(permissions, userOrRoles) {
|
|
120
|
+
const roles = isUserWithRoles(userOrRoles) ? userOrRoles.roles : userOrRoles;
|
|
105
121
|
const allGrants = [];
|
|
106
122
|
for (const role of roles) {
|
|
107
123
|
const roleGrants = permissions.resolvedGrants[role];
|
|
@@ -113,16 +129,18 @@ function resolveGrants(permissions, roles) {
|
|
|
113
129
|
const groups = /* @__PURE__ */ new Map();
|
|
114
130
|
const subjectIds = /* @__PURE__ */ new Map();
|
|
115
131
|
let nextId = 0;
|
|
116
|
-
function
|
|
132
|
+
function getSubjectKey(subject) {
|
|
133
|
+
if (subject === "all") return "all";
|
|
134
|
+
if (typeof subject === "string") return `s:${subject}`;
|
|
117
135
|
let id = subjectIds.get(subject);
|
|
118
136
|
if (id === void 0) {
|
|
119
137
|
id = nextId++;
|
|
120
138
|
subjectIds.set(subject, id);
|
|
121
139
|
}
|
|
122
|
-
return id
|
|
140
|
+
return `o:${id}`;
|
|
123
141
|
}
|
|
124
142
|
for (const g of allGrants) {
|
|
125
|
-
const key = `${g.action}:${
|
|
143
|
+
const key = `${g.action}:${getSubjectKey(g.subject)}`;
|
|
126
144
|
let group = groups.get(key);
|
|
127
145
|
if (!group) {
|
|
128
146
|
group = { action: g.action, subject: g.subject, wheres: [] };
|
|
@@ -162,6 +180,7 @@ function resolveGrants(permissions, roles) {
|
|
|
162
180
|
export {
|
|
163
181
|
CRUD_ACTIONS,
|
|
164
182
|
ForbiddenError,
|
|
183
|
+
can,
|
|
165
184
|
checkPermissions,
|
|
166
185
|
definePermissions,
|
|
167
186
|
getTableName,
|