@auth-gate/rbac 0.9.3 → 0.11.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/README.md ADDED
@@ -0,0 +1,117 @@
1
+ <p align="center">
2
+ <img src="icon.png" alt="AuthGate" width="120" height="120" />
3
+ </p>
4
+
5
+ # @auth-gate/rbac
6
+
7
+ RBAC as code for [AuthGate](https://www.authgate.dev) — define resources, roles, and permissions in TypeScript and sync them with a single CLI command.
8
+
9
+ ## Installation
10
+
11
+ ```bash
12
+ npm install @auth-gate/rbac
13
+ ```
14
+
15
+ ## Quick Start
16
+
17
+ ### 1. Generate a starter config
18
+
19
+ ```bash
20
+ npx @auth-gate/rbac init
21
+ ```
22
+
23
+ ### 2. Define your RBAC config
24
+
25
+ ```ts
26
+ // authgate.rbac.ts
27
+ import { defineRbac } from "@auth-gate/rbac";
28
+
29
+ export const rbac = defineRbac({
30
+ resources: {
31
+ documents: { actions: ["read", "write", "delete"] },
32
+ billing: { actions: ["read", "manage"] },
33
+ },
34
+ roles: {
35
+ admin: {
36
+ name: "Admin",
37
+ grants: {
38
+ documents: { read: true, write: true, delete: true },
39
+ billing: { read: true, manage: true },
40
+ },
41
+ },
42
+ member: {
43
+ name: "Member",
44
+ isDefault: true,
45
+ grants: {
46
+ documents: { read: true, write: true },
47
+ },
48
+ },
49
+ viewer: {
50
+ name: "Viewer",
51
+ grants: {
52
+ documents: { read: true },
53
+ },
54
+ },
55
+ },
56
+ });
57
+ ```
58
+
59
+ ### 3. Sync to AuthGate
60
+
61
+ ```bash
62
+ export AUTHGATE_API_KEY=ag_...
63
+ export AUTHGATE_BASE_URL=https://www.authgate.dev
64
+
65
+ npx @auth-gate/rbac diff # Preview changes
66
+ npx @auth-gate/rbac sync # Apply changes
67
+ ```
68
+
69
+ ## Type Safety
70
+
71
+ `defineRbac()` validates grants at compile time. Referencing an undeclared resource or action is a TypeScript error.
72
+
73
+ ```ts
74
+ rbac.resources.documents.key // "documents"
75
+ rbac.resources.documents.actions.read // "documents:read"
76
+ rbac.roles.admin.key // "admin"
77
+ rbac.permissions.documents.write // "documents:write"
78
+ ```
79
+
80
+ ## CLI Commands
81
+
82
+ | Command | Description |
83
+ |---------|-------------|
84
+ | `npx @auth-gate/rbac init` | Generate a starter config file |
85
+ | `npx @auth-gate/rbac diff` | Preview changes without applying |
86
+ | `npx @auth-gate/rbac sync` | Apply changes to AuthGate |
87
+ | `npx @auth-gate/rbac pull` | Pull server state into a local config |
88
+
89
+ ## Features
90
+
91
+ - **Type-safe grants** — compile-time validation of resources, actions, and roles
92
+ - **Role inheritance** — roles can inherit permissions from other roles via `inherits`
93
+ - **Rename migrations** — rename roles without losing assignments via `renamedFrom`
94
+ - **Conditional grants** — attach condition functions to grant values
95
+ - **Default role** — mark one role as `isDefault: true` for new org members
96
+ - **Diff before sync** — preview all changes before they're applied
97
+ - **Coexists with dashboard** — roles created in the dashboard are preserved
98
+
99
+ ## Runtime Role Management
100
+
101
+ ```ts
102
+ import { createRoleManagement } from "@auth-gate/rbac";
103
+
104
+ const roles = createRoleManagement({
105
+ apiKey: process.env.AUTHGATE_API_KEY!,
106
+ baseUrl: process.env.AUTHGATE_BASE_URL!,
107
+ });
108
+
109
+ await roles.list();
110
+ await roles.create({ key: "editor", name: "Editor", permissions: ["documents:read", "documents:write"] });
111
+ await roles.update("editor", { name: "Content Editor" });
112
+ await roles.delete("editor");
113
+ ```
114
+
115
+ ## License
116
+
117
+ MIT
package/dist/index.d.cts CHANGED
@@ -94,30 +94,28 @@ type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
94
94
  type IsUnion<T> = [T] extends [UnionToIntersection<T>] ? false : true;
95
95
  /**
96
96
  * Compile-time constraint: if more than one role has `isDefault: true`,
97
- * intersect those roles' `isDefault` with an error string so TypeScript
98
- * produces a descriptive message.
97
+ * produce a descriptive error at the `roles` level naming the conflicting roles.
99
98
  *
100
- * Error reads: Type 'true' is not assignable to type '"ERROR: ..."'.
99
+ * The error string includes the names of all roles that have `isDefault: true`
100
+ * so the developer knows exactly which roles to fix.
101
101
  */
102
102
  type ValidateSingleDefault<T> = T extends {
103
103
  roles: infer Roles;
104
104
  } ? IsUnion<DefaultRoleKeys<Roles>> extends true ? {
105
- roles: {
106
- [K in DefaultRoleKeys<Roles> & keyof Roles]: {
107
- isDefault: "ERROR: Only one role may have isDefault: true";
108
- };
109
- };
105
+ roles: `ERROR: multiple roles have isDefault: true ("${DefaultRoleKeys<Roles> & string}"). Only one role may be the default`;
110
106
  } : {} : {};
111
107
  /** Extract valid action string literals from a resource config. */
112
108
  type ValidActions<R> = R extends {
113
109
  actions: readonly (infer U)[];
114
110
  } ? U & string : never;
111
+ /** Extract action keys that are NOT declared in the resource config. */
112
+ type InvalidActions<Actions, Res extends ResourceConfig> = Exclude<keyof Actions & string, ValidActions<Res>>;
115
113
  /**
116
114
  * Compile-time constraint: validates grant resource/action keys against
117
115
  * declared resources at the type level.
118
116
  *
119
117
  * - **Invalid resource key** → descriptive error naming the resource.
120
- * - **Invalid action key** → descriptive error listing valid actions.
118
+ * - **Invalid action key** → descriptive error naming the specific invalid key(s).
121
119
  * - **Valid grants** → passes through original types transparently.
122
120
  *
123
121
  * Used as `T & ValidateConfig<T>` in the `defineRbac()` parameter type.
@@ -136,7 +134,7 @@ type ValidateConfig<T> = T extends {
136
134
  grants: {
137
135
  [ResK in keyof G]: ResK extends keyof R ? keyof G[ResK] extends ValidActions<R[ResK & keyof R]> ? {
138
136
  [ActK in keyof G[ResK]]: G[ResK][ActK];
139
- } : `ERROR: invalid action(s) in "${ResK & string}" grants. Valid actions: ${ValidActions<R[ResK & keyof R]>}` : `ERROR: resource "${ResK & string}" is not declared in resources`;
137
+ } : `ERROR: unknown action(s) "${InvalidActions<G[ResK], R[ResK & keyof R]>}" in "${ResK & string}" grants. Valid actions: ${ValidActions<R[ResK & keyof R]>}` : `ERROR: resource "${ResK & string}" is not declared in resources`;
140
138
  };
141
139
  } : Roles[RK];
142
140
  };
package/dist/index.d.ts CHANGED
@@ -94,30 +94,28 @@ type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
94
94
  type IsUnion<T> = [T] extends [UnionToIntersection<T>] ? false : true;
95
95
  /**
96
96
  * Compile-time constraint: if more than one role has `isDefault: true`,
97
- * intersect those roles' `isDefault` with an error string so TypeScript
98
- * produces a descriptive message.
97
+ * produce a descriptive error at the `roles` level naming the conflicting roles.
99
98
  *
100
- * Error reads: Type 'true' is not assignable to type '"ERROR: ..."'.
99
+ * The error string includes the names of all roles that have `isDefault: true`
100
+ * so the developer knows exactly which roles to fix.
101
101
  */
102
102
  type ValidateSingleDefault<T> = T extends {
103
103
  roles: infer Roles;
104
104
  } ? IsUnion<DefaultRoleKeys<Roles>> extends true ? {
105
- roles: {
106
- [K in DefaultRoleKeys<Roles> & keyof Roles]: {
107
- isDefault: "ERROR: Only one role may have isDefault: true";
108
- };
109
- };
105
+ roles: `ERROR: multiple roles have isDefault: true ("${DefaultRoleKeys<Roles> & string}"). Only one role may be the default`;
110
106
  } : {} : {};
111
107
  /** Extract valid action string literals from a resource config. */
112
108
  type ValidActions<R> = R extends {
113
109
  actions: readonly (infer U)[];
114
110
  } ? U & string : never;
111
+ /** Extract action keys that are NOT declared in the resource config. */
112
+ type InvalidActions<Actions, Res extends ResourceConfig> = Exclude<keyof Actions & string, ValidActions<Res>>;
115
113
  /**
116
114
  * Compile-time constraint: validates grant resource/action keys against
117
115
  * declared resources at the type level.
118
116
  *
119
117
  * - **Invalid resource key** → descriptive error naming the resource.
120
- * - **Invalid action key** → descriptive error listing valid actions.
118
+ * - **Invalid action key** → descriptive error naming the specific invalid key(s).
121
119
  * - **Valid grants** → passes through original types transparently.
122
120
  *
123
121
  * Used as `T & ValidateConfig<T>` in the `defineRbac()` parameter type.
@@ -136,7 +134,7 @@ type ValidateConfig<T> = T extends {
136
134
  grants: {
137
135
  [ResK in keyof G]: ResK extends keyof R ? keyof G[ResK] extends ValidActions<R[ResK & keyof R]> ? {
138
136
  [ActK in keyof G[ResK]]: G[ResK][ActK];
139
- } : `ERROR: invalid action(s) in "${ResK & string}" grants. Valid actions: ${ValidActions<R[ResK & keyof R]>}` : `ERROR: resource "${ResK & string}" is not declared in resources`;
137
+ } : `ERROR: unknown action(s) "${InvalidActions<G[ResK], R[ResK & keyof R]>}" in "${ResK & string}" grants. Valid actions: ${ValidActions<R[ResK & keyof R]>}` : `ERROR: resource "${ResK & string}" is not declared in resources`;
140
138
  };
141
139
  } : Roles[RK];
142
140
  };
package/icon.png ADDED
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@auth-gate/rbac",
3
- "version": "0.9.3",
3
+ "version": "0.11.0",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -16,13 +16,14 @@
16
16
  "module": "./dist/index.mjs",
17
17
  "types": "./dist/index.d.ts",
18
18
  "files": [
19
- "dist"
19
+ "dist",
20
+ "icon.png"
20
21
  ],
21
22
  "dependencies": {
22
23
  "chalk": "^5.4.0",
23
24
  "dotenv": "^17.2.4",
24
25
  "jiti": "^2.4.0",
25
- "@auth-gate/core": "0.9.3"
26
+ "@auth-gate/core": "0.11.0"
26
27
  },
27
28
  "devDependencies": {
28
29
  "tsup": "^8.0.0",