payload-rbac-plugin 1.0.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.
Files changed (43) hide show
  1. package/README.md +172 -0
  2. package/dist/collections/Permissions.d.ts +3 -0
  3. package/dist/collections/Permissions.js +28 -0
  4. package/dist/collections/Permissions.js.map +1 -0
  5. package/dist/collections/Roles.d.ts +3 -0
  6. package/dist/collections/Roles.js +38 -0
  7. package/dist/collections/Roles.js.map +1 -0
  8. package/dist/components/BeforeDashboardClient.d.ts +1 -0
  9. package/dist/components/BeforeDashboardClient.js +40 -0
  10. package/dist/components/BeforeDashboardClient.js.map +1 -0
  11. package/dist/components/BeforeDashboardServer.d.ts +2 -0
  12. package/dist/components/BeforeDashboardServer.js +22 -0
  13. package/dist/components/BeforeDashboardServer.js.map +1 -0
  14. package/dist/components/BeforeDashboardServer.module.css +5 -0
  15. package/dist/endpoints/customEndpointHandler.d.ts +2 -0
  16. package/dist/endpoints/customEndpointHandler.js +7 -0
  17. package/dist/endpoints/customEndpointHandler.js.map +1 -0
  18. package/dist/exports/client.d.ts +1 -0
  19. package/dist/exports/client.js +3 -0
  20. package/dist/exports/client.js.map +1 -0
  21. package/dist/exports/rsc.d.ts +1 -0
  22. package/dist/exports/rsc.js +3 -0
  23. package/dist/exports/rsc.js.map +1 -0
  24. package/dist/hooks/beforePermissionChange.d.ts +2 -0
  25. package/dist/index.d.ts +5 -0
  26. package/dist/index.js +38 -0
  27. package/dist/index.js.map +1 -0
  28. package/dist/types.d.ts +31 -0
  29. package/dist/types.js +3 -0
  30. package/dist/types.js.map +1 -0
  31. package/dist/utilities/checkPermission.d.ts +8 -0
  32. package/dist/utilities/checkPermission.js +16 -0
  33. package/dist/utilities/checkPermission.js.map +1 -0
  34. package/dist/utilities/hasPermission.d.ts +10 -0
  35. package/dist/utilities/hasPermission.js +37 -0
  36. package/dist/utilities/hasPermission.js.map +1 -0
  37. package/dist/utilities/hasPermission.spec.d.ts +1 -0
  38. package/dist/utilities/hasPermission.spec.js +120 -0
  39. package/dist/utilities/hasPermission.spec.js.map +1 -0
  40. package/dist/utilities/index.d.ts +2 -0
  41. package/dist/utilities/index.js +4 -0
  42. package/dist/utilities/index.js.map +1 -0
  43. package/package.json +126 -0
package/README.md ADDED
@@ -0,0 +1,172 @@
1
+ # Payload CMS Dynamic RBAC Plugin
2
+
3
+ A professional, database-backed Role-Based Access Control (RBAC) system for [Payload CMS](https://payloadcms.com) (v3).
4
+
5
+ This plugin emphasizes checking **permissions instead of roles** to avoid hard-coded authorization logic in your source code, making your access control highly scalable, modular, and dynamic.
6
+
7
+ ---
8
+
9
+ ## Features
10
+
11
+ - **Dynamic Collections**: Automatically injects database-backed `Roles` and `Permissions` collections.
12
+ - **Auth Collection Extension**: Injects a `roles` relationship field into your target auth collection (e.g., `users`) with `saveToJWT: true` for zero-cost runtime checks.
13
+ - **Bulk Permission Generator**: In the Admin UI, easily switch between creating a single permission or using the Bulk CRUD Generator to automatically generate separate permissions (e.g., `posts:create`, `posts:read`, `posts:update`, `posts:delete`) at once.
14
+ - **UI-Only Helpers**: Bulk generator fields (`type`, `collectionName`, CRUD checkboxes) are purely for the UI and are never saved to your database, keeping your collections clean.
15
+ - **Customizable Schemas**: Allow developers to inject additional custom fields into both `Roles` and `Permissions` schemas.
16
+ - **Granular Control**: Set visibility permissions (`hideRoles`, `hidePermissions`) dynamically or statically for the RBAC admin interface.
17
+ - **Helper Utilities**: Simple and robust functions to check permissions (`hasPermission`) and access control wrappers (`checkPermission`).
18
+ - **Fully Tested**: Powered by a robust integration and unit test suite built with Vitest.
19
+
20
+ ## Screenshots
21
+
22
+ ### 1. Permissions List View
23
+ Shows flat generated permissions (e.g. `posts:create`, `posts:read`) and legacy system permissions.
24
+ ![Permissions List View](images/permissions_list.png)
25
+
26
+ ### 2. Single Permission Form
27
+ Create individual custom permissions.
28
+ ![Single Permission Form](images/permissions_single.png)
29
+
30
+ ### 3. Bulk CRUD Generator Form
31
+ Select a collection and check CRUD actions to instantly generate multiple permissions.
32
+ ![Bulk CRUD Generator Form](images/permissions_bulk.png)
33
+
34
+ ### 4. Roles List View
35
+ Displays roles and all permissions associated with them.
36
+ ![Roles List View](images/roles_list.png)
37
+
38
+ ### 5. Role Configuration Form
39
+ Assign permissions directly to roles in the admin UI.
40
+ ![Role Configuration Form](images/role_edit.png)
41
+
42
+ ---
43
+
44
+ ## Installation
45
+
46
+ Add the plugin to your project:
47
+
48
+ ```bash
49
+ pnpm add payload-rbac-plugin
50
+ # or
51
+ npm install payload-rbac-plugin
52
+ # or
53
+ yarn add payload-rbac-plugin
54
+ ```
55
+
56
+ ---
57
+
58
+ ## Setup & Configuration
59
+
60
+ Import and configure the plugin in your `payload.config.ts`:
61
+
62
+ ```typescript
63
+ import { buildConfig } from 'payload'
64
+ import { rbac, hasPermission } from 'payload-rbac-plugin'
65
+
66
+ export default buildConfig({
67
+ collections: [
68
+ {
69
+ slug: 'posts',
70
+ fields: [],
71
+ },
72
+ ],
73
+ plugins: [
74
+ rbac({
75
+ enabled: true,
76
+ // Optional configurations:
77
+ authCollectionSlug: 'users', // Default: 'users'
78
+ rolesCollectionSlug: 'roles', // Default: 'roles'
79
+ permissionsCollectionSlug: 'permissions', // Default: 'permissions'
80
+
81
+ // Control access/visibility of the RBAC panel:
82
+ hidePermissions: ({ user }) => !hasPermission(user, 'access:permissions'),
83
+ hideRoles: ({ user }) => !hasPermission(user, 'access:roles'),
84
+ }),
85
+ ],
86
+ })
87
+ ```
88
+
89
+ ### Configuration Options
90
+
91
+ | Option | Type | Default | Description |
92
+ | :--- | :--- | :--- | :--- |
93
+ | `enabled` | `boolean` | `true` | Enable or disable the plugin. |
94
+ | `authCollectionSlug` | `string` | `'users'` | The collection slug representing the users collection. |
95
+ | `rolesCollectionSlug` | `string` | `'roles'` | The slug for the automatically injected Roles collection. |
96
+ | `permissionsCollectionSlug` | `string` | `'permissions'` | The slug for the automatically injected Permissions collection. |
97
+ | `rolesFields` | `Field[]` | `[]` | Extra custom fields to inject into the Roles collection. |
98
+ | `permissionsFields` | `Field[]` | `[]` | Extra custom fields to inject into the Permissions collection. |
99
+ | `hideRoles` | `boolean \| ((args: { user: any }) => boolean)` | `false` | Hide the Roles collection from the sidebar navigation. |
100
+ | `hidePermissions` | `boolean \| ((args: { user: any }) => boolean)` | `false` | Hide the Permissions collection from the sidebar navigation. |
101
+
102
+ ---
103
+
104
+ ## Usage
105
+
106
+ ### 1. Protecting Collections (Access Control)
107
+
108
+ You can protect collections using the `checkPermission` Higher-Order Function. It returns a standard Payload `Access` control resolver.
109
+
110
+ ```typescript
111
+ import { checkPermission } from 'payload-rbac-plugin'
112
+
113
+ export const PostsCollection = {
114
+ slug: 'posts',
115
+ access: {
116
+ create: checkPermission('posts:create'),
117
+ read: checkPermission('posts:read'),
118
+ update: checkPermission('posts:update'),
119
+ delete: checkPermission('posts:delete'),
120
+ },
121
+ fields: [],
122
+ }
123
+ ```
124
+
125
+ ### 2. Manual Permission Verification (`hasPermission`)
126
+
127
+ For custom endpoints, hooks, or conditionally rendering logic, use the `hasPermission` utility:
128
+
129
+ ```typescript
130
+ import { hasPermission } from 'payload-rbac-plugin'
131
+
132
+ const myCustomEndpoint = (req, res) => {
133
+ const user = req.user
134
+
135
+ if (hasPermission(user, 'export:reports')) {
136
+ // allow operation
137
+ } else {
138
+ res.status(403).send('Forbidden')
139
+ }
140
+ }
141
+ ```
142
+
143
+ ---
144
+
145
+ ## Bulk Permission Generator UI
146
+
147
+ When navigating to the **Permissions** collection in your Payload Admin panel:
148
+ 1. Select **Bulk CRUD Generator** in the `Type` select box (visible only during document creation).
149
+ 2. Enter the target collection slug (e.g. `posts`) in the `Collection Name` field.
150
+ 3. Check the CRUD operations you wish to generate (e.g., `create`, `read`).
151
+ 4. Save the document.
152
+ 5. The plugin runs a `beforeChange` hook that creates individual flat records (`posts:create`, `posts:read`) in the database, and avoids saving any temporary helper fields to the database.
153
+
154
+ ---
155
+
156
+ ## Development & Testing
157
+
158
+ If you are developing this plugin locally, you can run the test suite:
159
+
160
+ ```bash
161
+ # Run unit and integration tests
162
+ pnpm test:int
163
+
164
+ # Run tests in watch mode
165
+ pnpm test:int --watch
166
+ ```
167
+
168
+ ---
169
+
170
+ ## License
171
+
172
+ MIT
@@ -0,0 +1,3 @@
1
+ import type { CollectionConfig } from 'payload';
2
+ import type { PluginOptions } from '../types';
3
+ export declare const createPermissionsCollection: (options: PluginOptions) => CollectionConfig;
@@ -0,0 +1,28 @@
1
+ export const createPermissionsCollection = (options)=>{
2
+ const slug = options.permissionsCollectionSlug || 'permissions';
3
+ const customFields = options.permissionsFields || [];
4
+ return {
5
+ slug,
6
+ admin: {
7
+ useAsTitle: 'name',
8
+ group: 'Access Control'
9
+ },
10
+ access: {
11
+ read: ()=>true
12
+ },
13
+ fields: [
14
+ {
15
+ name: 'name',
16
+ type: 'text',
17
+ required: true,
18
+ unique: true,
19
+ admin: {
20
+ description: 'The unique name of the permission (e.g., "create:users", "read:posts").'
21
+ }
22
+ },
23
+ ...customFields
24
+ ]
25
+ };
26
+ };
27
+
28
+ //# sourceMappingURL=Permissions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/collections/Permissions.ts"],"sourcesContent":["import type { CollectionConfig } from 'payload'\nimport type { PluginOptions } from '../types.js'\n\nexport const createPermissionsCollection = (options: PluginOptions): CollectionConfig => {\n const slug = options.permissionsCollectionSlug || 'permissions'\n const customFields = options.permissionsFields || []\n\n return {\n slug,\n admin: {\n useAsTitle: 'name',\n group: 'Access Control',\n },\n access: {\n read: () => true, // Access logic can be customized or injected later\n },\n fields: [\n {\n name: 'name',\n type: 'text',\n required: true,\n unique: true,\n admin: {\n description: 'The unique name of the permission (e.g., \"create:users\", \"read:posts\").',\n },\n },\n ...customFields,\n ],\n }\n}\n"],"names":["createPermissionsCollection","options","slug","permissionsCollectionSlug","customFields","permissionsFields","admin","useAsTitle","group","access","read","fields","name","type","required","unique","description"],"mappings":"AAGA,OAAO,MAAMA,8BAA8B,CAACC;IAC1C,MAAMC,OAAOD,QAAQE,yBAAyB,IAAI;IAClD,MAAMC,eAAeH,QAAQI,iBAAiB,IAAI,EAAE;IAEpD,OAAO;QACLH;QACAI,OAAO;YACLC,YAAY;YACZC,OAAO;QACT;QACAC,QAAQ;YACNC,MAAM,IAAM;QACd;QACAC,QAAQ;YACN;gBACEC,MAAM;gBACNC,MAAM;gBACNC,UAAU;gBACVC,QAAQ;gBACRT,OAAO;oBACLU,aAAa;gBACf;YACF;eACGZ;SACJ;IACH;AACF,EAAC"}
@@ -0,0 +1,3 @@
1
+ import type { CollectionConfig } from 'payload';
2
+ import type { PluginOptions } from '../types';
3
+ export declare const createRolesCollection: (options: PluginOptions) => CollectionConfig;
@@ -0,0 +1,38 @@
1
+ export const createRolesCollection = (options)=>{
2
+ const slug = options.rolesCollectionSlug || 'roles';
3
+ const permissionsSlug = options.permissionsCollectionSlug || 'permissions';
4
+ const customFields = options.rolesFields || [];
5
+ return {
6
+ slug,
7
+ admin: {
8
+ useAsTitle: 'name',
9
+ group: 'Access Control'
10
+ },
11
+ access: {
12
+ read: ()=>true
13
+ },
14
+ fields: [
15
+ {
16
+ name: 'name',
17
+ type: 'text',
18
+ required: true,
19
+ unique: true,
20
+ admin: {
21
+ description: 'The unique name of the role (e.g., "admin", "editor").'
22
+ }
23
+ },
24
+ {
25
+ name: 'permissions',
26
+ type: 'relationship',
27
+ relationTo: permissionsSlug,
28
+ hasMany: true,
29
+ admin: {
30
+ description: 'The permissions assigned to this role.'
31
+ }
32
+ },
33
+ ...customFields
34
+ ]
35
+ };
36
+ };
37
+
38
+ //# sourceMappingURL=Roles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/collections/Roles.ts"],"sourcesContent":["import type { CollectionConfig } from 'payload'\nimport type { PluginOptions } from '../types.js'\n\nexport const createRolesCollection = (options: PluginOptions): CollectionConfig => {\n const slug = options.rolesCollectionSlug || 'roles'\n const permissionsSlug = options.permissionsCollectionSlug || 'permissions'\n const customFields = options.rolesFields || []\n\n return {\n slug,\n admin: {\n useAsTitle: 'name',\n group: 'Access Control',\n },\n access: {\n read: () => true,\n },\n fields: [\n {\n name: 'name',\n type: 'text',\n required: true,\n unique: true,\n admin: {\n description: 'The unique name of the role (e.g., \"admin\", \"editor\").',\n },\n },\n {\n name: 'permissions',\n type: 'relationship',\n relationTo: permissionsSlug,\n hasMany: true,\n admin: {\n description: 'The permissions assigned to this role.',\n },\n },\n ...customFields,\n ],\n }\n}\n"],"names":["createRolesCollection","options","slug","rolesCollectionSlug","permissionsSlug","permissionsCollectionSlug","customFields","rolesFields","admin","useAsTitle","group","access","read","fields","name","type","required","unique","description","relationTo","hasMany"],"mappings":"AAGA,OAAO,MAAMA,wBAAwB,CAACC;IACpC,MAAMC,OAAOD,QAAQE,mBAAmB,IAAI;IAC5C,MAAMC,kBAAkBH,QAAQI,yBAAyB,IAAI;IAC7D,MAAMC,eAAeL,QAAQM,WAAW,IAAI,EAAE;IAE9C,OAAO;QACLL;QACAM,OAAO;YACLC,YAAY;YACZC,OAAO;QACT;QACAC,QAAQ;YACNC,MAAM,IAAM;QACd;QACAC,QAAQ;YACN;gBACEC,MAAM;gBACNC,MAAM;gBACNC,UAAU;gBACVC,QAAQ;gBACRT,OAAO;oBACLU,aAAa;gBACf;YACF;YACA;gBACEJ,MAAM;gBACNC,MAAM;gBACNI,YAAYf;gBACZgB,SAAS;gBACTZ,OAAO;oBACLU,aAAa;gBACf;YACF;eACGZ;SACJ;IACH;AACF,EAAC"}
@@ -0,0 +1 @@
1
+ export declare const BeforeDashboardClient: () => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,40 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useConfig } from '@payloadcms/ui';
4
+ import { formatAdminURL } from 'payload/shared';
5
+ import { useEffect, useState } from 'react';
6
+ export const BeforeDashboardClient = ()=>{
7
+ const { config } = useConfig();
8
+ const [message, setMessage] = useState('');
9
+ useEffect(()=>{
10
+ const fetchMessage = async ()=>{
11
+ const response = await fetch(formatAdminURL({
12
+ apiRoute: config.routes.api,
13
+ path: '/my-plugin-endpoint'
14
+ }));
15
+ const result = await response.json();
16
+ setMessage(result.message);
17
+ };
18
+ void fetchMessage();
19
+ }, [
20
+ config.serverURL,
21
+ config.routes.api
22
+ ]);
23
+ return /*#__PURE__*/ _jsxs("div", {
24
+ children: [
25
+ /*#__PURE__*/ _jsx("h1", {
26
+ children: "Added by the plugin: Before Dashboard Client"
27
+ }),
28
+ /*#__PURE__*/ _jsxs("div", {
29
+ children: [
30
+ "Message from the endpoint:",
31
+ /*#__PURE__*/ _jsx("div", {
32
+ children: message || 'Loading...'
33
+ })
34
+ ]
35
+ })
36
+ ]
37
+ });
38
+ };
39
+
40
+ //# sourceMappingURL=BeforeDashboardClient.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/BeforeDashboardClient.tsx"],"sourcesContent":["'use client'\nimport { useConfig } from '@payloadcms/ui'\nimport { formatAdminURL } from 'payload/shared'\nimport { useEffect, useState } from 'react'\n\nexport const BeforeDashboardClient = () => {\n const { config } = useConfig()\n\n const [message, setMessage] = useState('')\n\n useEffect(() => {\n const fetchMessage = async () => {\n const response = await fetch(\n formatAdminURL({\n apiRoute: config.routes.api,\n path: '/my-plugin-endpoint',\n }),\n )\n const result = await response.json()\n setMessage(result.message)\n }\n\n void fetchMessage()\n }, [config.serverURL, config.routes.api])\n\n return (\n <div>\n <h1>Added by the plugin: Before Dashboard Client</h1>\n <div>\n Message from the endpoint:\n <div>{message || 'Loading...'}</div>\n </div>\n </div>\n )\n}\n"],"names":["useConfig","formatAdminURL","useEffect","useState","BeforeDashboardClient","config","message","setMessage","fetchMessage","response","fetch","apiRoute","routes","api","path","result","json","serverURL","div","h1"],"mappings":"AAAA;;AACA,SAASA,SAAS,QAAQ,iBAAgB;AAC1C,SAASC,cAAc,QAAQ,iBAAgB;AAC/C,SAASC,SAAS,EAAEC,QAAQ,QAAQ,QAAO;AAE3C,OAAO,MAAMC,wBAAwB;IACnC,MAAM,EAAEC,MAAM,EAAE,GAAGL;IAEnB,MAAM,CAACM,SAASC,WAAW,GAAGJ,SAAS;IAEvCD,UAAU;QACR,MAAMM,eAAe;YACnB,MAAMC,WAAW,MAAMC,MACrBT,eAAe;gBACbU,UAAUN,OAAOO,MAAM,CAACC,GAAG;gBAC3BC,MAAM;YACR;YAEF,MAAMC,SAAS,MAAMN,SAASO,IAAI;YAClCT,WAAWQ,OAAOT,OAAO;QAC3B;QAEA,KAAKE;IACP,GAAG;QAACH,OAAOY,SAAS;QAAEZ,OAAOO,MAAM,CAACC,GAAG;KAAC;IAExC,qBACE,MAACK;;0BACC,KAACC;0BAAG;;0BACJ,MAACD;;oBAAI;kCAEH,KAACA;kCAAKZ,WAAW;;;;;;AAIzB,EAAC"}
@@ -0,0 +1,2 @@
1
+ import type { ServerComponentProps } from 'payload';
2
+ export declare const BeforeDashboardServer: (props: ServerComponentProps) => Promise<import("react/jsx-runtime").JSX.Element>;
@@ -0,0 +1,22 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import styles from './BeforeDashboardServer.module.css';
3
+ export const BeforeDashboardServer = async (props)=>{
4
+ const { payload } = props;
5
+ const { docs } = await payload.find({
6
+ collection: 'plugin-collection'
7
+ });
8
+ return /*#__PURE__*/ _jsxs("div", {
9
+ className: styles.wrapper,
10
+ children: [
11
+ /*#__PURE__*/ _jsx("h1", {
12
+ children: "Added by the plugin: Before Dashboard Server"
13
+ }),
14
+ "Docs from Local API:",
15
+ docs.map((doc)=>/*#__PURE__*/ _jsx("div", {
16
+ children: doc.id
17
+ }, doc.id))
18
+ ]
19
+ });
20
+ };
21
+
22
+ //# sourceMappingURL=BeforeDashboardServer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/BeforeDashboardServer.tsx"],"sourcesContent":["import type { ServerComponentProps } from 'payload'\n\nimport styles from './BeforeDashboardServer.module.css'\n\nexport const BeforeDashboardServer = async (props: ServerComponentProps) => {\n const { payload } = props\n\n const { docs } = await payload.find({ collection: 'plugin-collection' })\n\n return (\n <div className={styles.wrapper}>\n <h1>Added by the plugin: Before Dashboard Server</h1>\n Docs from Local API:\n {docs.map((doc) => (\n <div key={doc.id}>{doc.id}</div>\n ))}\n </div>\n )\n}\n"],"names":["styles","BeforeDashboardServer","props","payload","docs","find","collection","div","className","wrapper","h1","map","doc","id"],"mappings":";AAEA,OAAOA,YAAY,qCAAoC;AAEvD,OAAO,MAAMC,wBAAwB,OAAOC;IAC1C,MAAM,EAAEC,OAAO,EAAE,GAAGD;IAEpB,MAAM,EAAEE,IAAI,EAAE,GAAG,MAAMD,QAAQE,IAAI,CAAC;QAAEC,YAAY;IAAoB;IAEtE,qBACE,MAACC;QAAIC,WAAWR,OAAOS,OAAO;;0BAC5B,KAACC;0BAAG;;YAAiD;YAEpDN,KAAKO,GAAG,CAAC,CAACC,oBACT,KAACL;8BAAkBK,IAAIC,EAAE;mBAAfD,IAAIC,EAAE;;;AAIxB,EAAC"}
@@ -0,0 +1,5 @@
1
+ .wrapper {
2
+ display: flex;
3
+ gap: 5px;
4
+ flex-direction: column;
5
+ }
@@ -0,0 +1,2 @@
1
+ import type { PayloadHandler } from 'payload';
2
+ export declare const customEndpointHandler: PayloadHandler;
@@ -0,0 +1,7 @@
1
+ export const customEndpointHandler = ()=>{
2
+ return Response.json({
3
+ message: 'Hello from custom endpoint'
4
+ });
5
+ };
6
+
7
+ //# sourceMappingURL=customEndpointHandler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/endpoints/customEndpointHandler.ts"],"sourcesContent":["import type { PayloadHandler } from 'payload'\n\nexport const customEndpointHandler: PayloadHandler = () => {\n return Response.json({ message: 'Hello from custom endpoint' })\n}\n"],"names":["customEndpointHandler","Response","json","message"],"mappings":"AAEA,OAAO,MAAMA,wBAAwC;IACnD,OAAOC,SAASC,IAAI,CAAC;QAAEC,SAAS;IAA6B;AAC/D,EAAC"}
@@ -0,0 +1 @@
1
+ export { BeforeDashboardClient } from '../components/BeforeDashboardClient';
@@ -0,0 +1,3 @@
1
+ export { BeforeDashboardClient } from '../components/BeforeDashboardClient.js';
2
+
3
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/exports/client.ts"],"sourcesContent":["export { BeforeDashboardClient } from '../components/BeforeDashboardClient.js'\n"],"names":["BeforeDashboardClient"],"mappings":"AAAA,SAASA,qBAAqB,QAAQ,yCAAwC"}
@@ -0,0 +1 @@
1
+ export { BeforeDashboardServer } from '../components/BeforeDashboardServer';
@@ -0,0 +1,3 @@
1
+ export { BeforeDashboardServer } from '../components/BeforeDashboardServer.js';
2
+
3
+ //# sourceMappingURL=rsc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/exports/rsc.ts"],"sourcesContent":["export { BeforeDashboardServer } from '../components/BeforeDashboardServer.js'\n"],"names":["BeforeDashboardServer"],"mappings":"AAAA,SAASA,qBAAqB,QAAQ,yCAAwC"}
@@ -0,0 +1,2 @@
1
+ import type { CollectionBeforeChangeHook } from 'payload';
2
+ export declare const createBeforePermissionChangeHook: (slug: string) => CollectionBeforeChangeHook;
@@ -0,0 +1,5 @@
1
+ import type { Config } from 'payload';
2
+ import type { PluginOptions } from './types';
3
+ export declare const rbac: (pluginOptions?: PluginOptions) => (config: Config) => Config;
4
+ export * from './types';
5
+ export * from './utilities/index';
package/dist/index.js ADDED
@@ -0,0 +1,38 @@
1
+ import { createPermissionsCollection } from './collections/Permissions.js';
2
+ import { createRolesCollection } from './collections/Roles.js';
3
+ export const rbac = (pluginOptions)=>(config)=>{
4
+ const options = pluginOptions || {};
5
+ if (options.enabled === false) {
6
+ return config;
7
+ }
8
+ if (!config.collections) {
9
+ config.collections = [];
10
+ }
11
+ // Add Roles and Permissions collections
12
+ config.collections.push(createPermissionsCollection(options));
13
+ config.collections.push(createRolesCollection(options));
14
+ // Extend the target auth collection
15
+ const authSlug = options.authCollectionSlug || 'users';
16
+ const rolesSlug = options.rolesCollectionSlug || 'roles';
17
+ const authCollection = config.collections.find((collection)=>collection.slug === authSlug);
18
+ if (authCollection) {
19
+ authCollection.fields.push({
20
+ name: 'roles',
21
+ type: 'relationship',
22
+ relationTo: rolesSlug,
23
+ hasMany: true,
24
+ saveToJWT: true,
25
+ admin: {
26
+ description: 'Roles assigned to this user.'
27
+ }
28
+ });
29
+ } else {
30
+ console.warn(`[rbac-plugin] Auth collection '${authSlug}' not found. Cannot inject roles field.`);
31
+ }
32
+ return config;
33
+ };
34
+ // Export utilities and types for consumers
35
+ export * from './types.js';
36
+ export * from './utilities/index.js';
37
+
38
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { Config } from 'payload'\nimport type { PluginOptions } from './types.js'\n\nimport { createPermissionsCollection } from './collections/Permissions.js'\nimport { createRolesCollection } from './collections/Roles.js'\n\nexport const rbac =\n (pluginOptions?: PluginOptions) =>\n (config: Config): Config => {\n const options = pluginOptions || {}\n \n if (options.enabled === false) {\n return config\n }\n\n if (!config.collections) {\n config.collections = []\n }\n\n // Add Roles and Permissions collections\n config.collections.push(createPermissionsCollection(options))\n config.collections.push(createRolesCollection(options))\n\n // Extend the target auth collection\n const authSlug = options.authCollectionSlug || 'users'\n const rolesSlug = options.rolesCollectionSlug || 'roles'\n\n const authCollection = config.collections.find(\n (collection) => collection.slug === authSlug,\n )\n\n if (authCollection) {\n authCollection.fields.push({\n name: 'roles',\n type: 'relationship',\n relationTo: rolesSlug,\n hasMany: true,\n saveToJWT: true,\n admin: {\n description: 'Roles assigned to this user.',\n },\n })\n } else {\n console.warn(`[rbac-plugin] Auth collection '${authSlug}' not found. Cannot inject roles field.`)\n }\n\n return config\n }\n\n// Export utilities and types for consumers\nexport * from './types.js'\nexport * from './utilities/index.js'\n"],"names":["createPermissionsCollection","createRolesCollection","rbac","pluginOptions","config","options","enabled","collections","push","authSlug","authCollectionSlug","rolesSlug","rolesCollectionSlug","authCollection","find","collection","slug","fields","name","type","relationTo","hasMany","saveToJWT","admin","description","console","warn"],"mappings":"AAGA,SAASA,2BAA2B,QAAQ,+BAA8B;AAC1E,SAASC,qBAAqB,QAAQ,yBAAwB;AAE9D,OAAO,MAAMC,OACX,CAACC,gBACD,CAACC;QACC,MAAMC,UAAUF,iBAAiB,CAAC;QAElC,IAAIE,QAAQC,OAAO,KAAK,OAAO;YAC7B,OAAOF;QACT;QAEA,IAAI,CAACA,OAAOG,WAAW,EAAE;YACvBH,OAAOG,WAAW,GAAG,EAAE;QACzB;QAEA,wCAAwC;QACxCH,OAAOG,WAAW,CAACC,IAAI,CAACR,4BAA4BK;QACpDD,OAAOG,WAAW,CAACC,IAAI,CAACP,sBAAsBI;QAE9C,oCAAoC;QACpC,MAAMI,WAAWJ,QAAQK,kBAAkB,IAAI;QAC/C,MAAMC,YAAYN,QAAQO,mBAAmB,IAAI;QAEjD,MAAMC,iBAAiBT,OAAOG,WAAW,CAACO,IAAI,CAC5C,CAACC,aAAeA,WAAWC,IAAI,KAAKP;QAGtC,IAAII,gBAAgB;YAClBA,eAAeI,MAAM,CAACT,IAAI,CAAC;gBACzBU,MAAM;gBACNC,MAAM;gBACNC,YAAYT;gBACZU,SAAS;gBACTC,WAAW;gBACXC,OAAO;oBACLC,aAAa;gBACf;YACF;QACF,OAAO;YACLC,QAAQC,IAAI,CAAC,CAAC,+BAA+B,EAAEjB,SAAS,uCAAuC,CAAC;QAClG;QAEA,OAAOL;IACT,EAAC;AAEH,2CAA2C;AAC3C,cAAc,aAAY;AAC1B,cAAc,uBAAsB"}
@@ -0,0 +1,31 @@
1
+ import type { Field } from 'payload';
2
+ export interface PluginOptions {
3
+ /**
4
+ * Enable or disable plugin
5
+ * @default false
6
+ */
7
+ enabled?: boolean;
8
+ /**
9
+ * Override the default collection slug for Roles
10
+ * @default 'roles'
11
+ */
12
+ rolesCollectionSlug?: string;
13
+ /**
14
+ * Override the default collection slug for Permissions
15
+ * @default 'permissions'
16
+ */
17
+ permissionsCollectionSlug?: string;
18
+ /**
19
+ * The collection slug for the authentication collection (typically 'users')
20
+ * @default 'users'
21
+ */
22
+ authCollectionSlug?: string;
23
+ /**
24
+ * Inject additional custom fields into the generated Roles collection
25
+ */
26
+ rolesFields?: Field[];
27
+ /**
28
+ * Inject additional custom fields into the generated Permissions collection
29
+ */
30
+ permissionsFields?: Field[];
31
+ }
package/dist/types.js ADDED
@@ -0,0 +1,3 @@
1
+ export { };
2
+
3
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/types.ts"],"sourcesContent":["import type { Field } from 'payload'\n\nexport interface PluginOptions {\n /**\n * Enable or disable plugin\n * @default false\n */\n enabled?: boolean\n\n /**\n * Override the default collection slug for Roles\n * @default 'roles'\n */\n rolesCollectionSlug?: string\n\n /**\n * Override the default collection slug for Permissions\n * @default 'permissions'\n */\n permissionsCollectionSlug?: string\n\n /**\n * The collection slug for the authentication collection (typically 'users')\n * @default 'users'\n */\n authCollectionSlug?: string\n\n /**\n * Inject additional custom fields into the generated Roles collection\n */\n rolesFields?: Field[]\n\n /**\n * Inject additional custom fields into the generated Permissions collection\n */\n permissionsFields?: Field[]\n}\n"],"names":[],"mappings":"AAEA,WAkCC"}
@@ -0,0 +1,8 @@
1
+ import type { Access } from 'payload';
2
+ /**
3
+ * A Higher-Order Function to drop into Payload Collection access control fields.
4
+ *
5
+ * @param permissionName - The required permission name
6
+ * @returns Access function
7
+ */
8
+ export declare const checkPermission: (permissionName: string) => Access;
@@ -0,0 +1,16 @@
1
+ import { hasPermission } from './hasPermission.js';
2
+ /**
3
+ * A Higher-Order Function to drop into Payload Collection access control fields.
4
+ *
5
+ * @param permissionName - The required permission name
6
+ * @returns Access function
7
+ */ export const checkPermission = (permissionName)=>{
8
+ return ({ req: { user } })=>{
9
+ // We can just rely on the synchronous hasPermission check.
10
+ // If relations are unpopulated, it will return false. Ensure your auth
11
+ // collection is configured with adequate depth for saveToJWT or default depth.
12
+ return hasPermission(user, permissionName);
13
+ };
14
+ };
15
+
16
+ //# sourceMappingURL=checkPermission.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utilities/checkPermission.ts"],"sourcesContent":["import type { Access } from 'payload'\nimport { hasPermission } from './hasPermission.js'\n\n/**\n * A Higher-Order Function to drop into Payload Collection access control fields.\n * \n * @param permissionName - The required permission name\n * @returns Access function\n */\nexport const checkPermission = (permissionName: string): Access => {\n return ({ req: { user } }) => {\n // We can just rely on the synchronous hasPermission check.\n // If relations are unpopulated, it will return false. Ensure your auth\n // collection is configured with adequate depth for saveToJWT or default depth.\n return hasPermission(user, permissionName)\n }\n}\n"],"names":["hasPermission","checkPermission","permissionName","req","user"],"mappings":"AACA,SAASA,aAAa,QAAQ,qBAAoB;AAElD;;;;;CAKC,GACD,OAAO,MAAMC,kBAAkB,CAACC;IAC9B,OAAO,CAAC,EAAEC,KAAK,EAAEC,IAAI,EAAE,EAAE;QACvB,2DAA2D;QAC3D,uEAAuE;QACvE,+EAA+E;QAC/E,OAAOJ,cAAcI,MAAMF;IAC7B;AACF,EAAC"}
@@ -0,0 +1,10 @@
1
+ import type { PayloadRequest } from 'payload';
2
+ /**
3
+ * Checks if a user has a specific permission.
4
+ * Gracefully handles populated and unpopulated relationship states as far as possible synchronously.
5
+ *
6
+ * @param user - The Payload user object from req.user
7
+ * @param requiredPermission - The name of the permission to check for
8
+ * @returns boolean
9
+ */
10
+ export declare const hasPermission: (user: PayloadRequest["user"] | null | undefined, requiredPermission: string) => boolean;
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Checks if a user has a specific permission.
3
+ * Gracefully handles populated and unpopulated relationship states as far as possible synchronously.
4
+ *
5
+ * @param user - The Payload user object from req.user
6
+ * @param requiredPermission - The name of the permission to check for
7
+ * @returns boolean
8
+ */ export const hasPermission = (user, requiredPermission)=>{
9
+ if (!user || !user.roles || !Array.isArray(user.roles)) {
10
+ return false;
11
+ }
12
+ for (const role of user.roles){
13
+ // If role is just an ID (unpopulated), we can't check its permissions synchronously
14
+ if (typeof role !== 'object' || role === null) {
15
+ continue;
16
+ }
17
+ const permissions = role.permissions;
18
+ if (!permissions || !Array.isArray(permissions)) {
19
+ continue;
20
+ }
21
+ for (const permission of permissions){
22
+ // If permission is an object (populated) and has a name
23
+ if (typeof permission === 'object' && permission !== null) {
24
+ if ('name' in permission && permission.name === requiredPermission) {
25
+ return true;
26
+ }
27
+ }
28
+ // Note: If permission is just an ID (unpopulated), we cannot synchronously
29
+ // determine its name here. In a strictly synchronous check, we must return false
30
+ // or ignore it. For a fully robust check with unpopulated data, an async version
31
+ // requiring the payload instance would be necessary.
32
+ }
33
+ }
34
+ return false;
35
+ };
36
+
37
+ //# sourceMappingURL=hasPermission.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utilities/hasPermission.ts"],"sourcesContent":["import type { PayloadRequest } from 'payload'\n\n/**\n * Checks if a user has a specific permission.\n * Gracefully handles populated and unpopulated relationship states as far as possible synchronously.\n * \n * @param user - The Payload user object from req.user\n * @param requiredPermission - The name of the permission to check for\n * @returns boolean\n */\nexport const hasPermission = (\n user: PayloadRequest['user'] | null | undefined,\n requiredPermission: string,\n): boolean => {\n if (!user || !user.roles || !Array.isArray(user.roles)) {\n return false\n }\n\n for (const role of user.roles) {\n // If role is just an ID (unpopulated), we can't check its permissions synchronously\n if (typeof role !== 'object' || role === null) {\n continue\n }\n\n const permissions = role.permissions\n if (!permissions || !Array.isArray(permissions)) {\n continue\n }\n\n for (const permission of permissions) {\n // If permission is an object (populated) and has a name\n if (typeof permission === 'object' && permission !== null) {\n if ('name' in permission && permission.name === requiredPermission) {\n return true\n }\n } \n // Note: If permission is just an ID (unpopulated), we cannot synchronously\n // determine its name here. In a strictly synchronous check, we must return false \n // or ignore it. For a fully robust check with unpopulated data, an async version \n // requiring the payload instance would be necessary.\n }\n }\n\n return false\n}\n"],"names":["hasPermission","user","requiredPermission","roles","Array","isArray","role","permissions","permission","name"],"mappings":"AAEA;;;;;;;CAOC,GACD,OAAO,MAAMA,gBAAgB,CAC3BC,MACAC;IAEA,IAAI,CAACD,QAAQ,CAACA,KAAKE,KAAK,IAAI,CAACC,MAAMC,OAAO,CAACJ,KAAKE,KAAK,GAAG;QACtD,OAAO;IACT;IAEA,KAAK,MAAMG,QAAQL,KAAKE,KAAK,CAAE;QAC7B,oFAAoF;QACpF,IAAI,OAAOG,SAAS,YAAYA,SAAS,MAAM;YAC7C;QACF;QAEA,MAAMC,cAAcD,KAAKC,WAAW;QACpC,IAAI,CAACA,eAAe,CAACH,MAAMC,OAAO,CAACE,cAAc;YAC/C;QACF;QAEA,KAAK,MAAMC,cAAcD,YAAa;YACpC,wDAAwD;YACxD,IAAI,OAAOC,eAAe,YAAYA,eAAe,MAAM;gBACzD,IAAI,UAAUA,cAAcA,WAAWC,IAAI,KAAKP,oBAAoB;oBAClE,OAAO;gBACT;YACF;QACA,2EAA2E;QAC3E,kFAAkF;QAClF,kFAAkF;QAClF,qDAAqD;QACvD;IACF;IAEA,OAAO;AACT,EAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,120 @@
1
+ import { hasPermission } from './hasPermission.js';
2
+ describe('hasPermission', ()=>{
3
+ it('should return false if user is null', ()=>{
4
+ expect(hasPermission(null, 'read:posts')).toBe(false);
5
+ });
6
+ it('should return false if user has no roles array', ()=>{
7
+ const user = {
8
+ id: '1'
9
+ };
10
+ expect(hasPermission(user, 'read:posts')).toBe(false);
11
+ });
12
+ it('should return false if user roles is unpopulated (array of strings)', ()=>{
13
+ const user = {
14
+ id: '1',
15
+ roles: [
16
+ 'role-id-1'
17
+ ]
18
+ };
19
+ expect(hasPermission(user, 'read:posts')).toBe(false);
20
+ });
21
+ it('should return false if role has no permissions', ()=>{
22
+ const user = {
23
+ id: '1',
24
+ roles: [
25
+ {
26
+ id: 'role-1',
27
+ name: 'admin'
28
+ }
29
+ ]
30
+ };
31
+ expect(hasPermission(user, 'read:posts')).toBe(false);
32
+ });
33
+ it('should return false if role permissions are unpopulated (array of strings)', ()=>{
34
+ const user = {
35
+ id: '1',
36
+ roles: [
37
+ {
38
+ id: 'role-1',
39
+ name: 'admin',
40
+ permissions: [
41
+ 'perm-id-1'
42
+ ]
43
+ }
44
+ ]
45
+ };
46
+ expect(hasPermission(user, 'read:posts')).toBe(false);
47
+ });
48
+ it('should return true if permission is found in populated roles', ()=>{
49
+ const user = {
50
+ id: '1',
51
+ roles: [
52
+ {
53
+ id: 'role-1',
54
+ name: 'admin',
55
+ permissions: [
56
+ {
57
+ id: 'perm-1',
58
+ name: 'read:posts'
59
+ },
60
+ {
61
+ id: 'perm-2',
62
+ name: 'write:posts'
63
+ }
64
+ ]
65
+ }
66
+ ]
67
+ };
68
+ expect(hasPermission(user, 'write:posts')).toBe(true);
69
+ });
70
+ it('should return false if permission is not found in populated roles', ()=>{
71
+ const user = {
72
+ id: '1',
73
+ roles: [
74
+ {
75
+ id: 'role-1',
76
+ name: 'editor',
77
+ permissions: [
78
+ {
79
+ id: 'perm-1',
80
+ name: 'read:posts'
81
+ }
82
+ ]
83
+ }
84
+ ]
85
+ };
86
+ expect(hasPermission(user, 'delete:posts')).toBe(false);
87
+ });
88
+ it('should correctly search through multiple roles', ()=>{
89
+ const user = {
90
+ id: '1',
91
+ roles: [
92
+ {
93
+ id: 'role-1',
94
+ name: 'editor',
95
+ permissions: [
96
+ {
97
+ id: 'perm-1',
98
+ name: 'read:posts'
99
+ }
100
+ ]
101
+ },
102
+ {
103
+ id: 'role-2',
104
+ name: 'manager',
105
+ permissions: [
106
+ {
107
+ id: 'perm-2',
108
+ name: 'delete:posts'
109
+ }
110
+ ]
111
+ }
112
+ ]
113
+ };
114
+ expect(hasPermission(user, 'delete:posts')).toBe(true);
115
+ expect(hasPermission(user, 'read:posts')).toBe(true);
116
+ expect(hasPermission(user, 'create:users')).toBe(false);
117
+ });
118
+ });
119
+
120
+ //# sourceMappingURL=hasPermission.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utilities/hasPermission.spec.ts"],"sourcesContent":["import { hasPermission } from './hasPermission.js'\nimport type { PayloadRequest } from 'payload'\n\ndescribe('hasPermission', () => {\n it('should return false if user is null', () => {\n expect(hasPermission(null, 'read:posts')).toBe(false)\n })\n\n it('should return false if user has no roles array', () => {\n const user = { id: '1' } as PayloadRequest['user']\n expect(hasPermission(user, 'read:posts')).toBe(false)\n })\n\n it('should return false if user roles is unpopulated (array of strings)', () => {\n const user = {\n id: '1',\n roles: ['role-id-1'],\n } as unknown as PayloadRequest['user']\n expect(hasPermission(user, 'read:posts')).toBe(false)\n })\n\n it('should return false if role has no permissions', () => {\n const user = {\n id: '1',\n roles: [{ id: 'role-1', name: 'admin' }],\n } as unknown as PayloadRequest['user']\n expect(hasPermission(user, 'read:posts')).toBe(false)\n })\n\n it('should return false if role permissions are unpopulated (array of strings)', () => {\n const user = {\n id: '1',\n roles: [{ id: 'role-1', name: 'admin', permissions: ['perm-id-1'] }],\n } as unknown as PayloadRequest['user']\n expect(hasPermission(user, 'read:posts')).toBe(false)\n })\n\n it('should return true if permission is found in populated roles', () => {\n const user = {\n id: '1',\n roles: [\n {\n id: 'role-1',\n name: 'admin',\n permissions: [\n { id: 'perm-1', name: 'read:posts' },\n { id: 'perm-2', name: 'write:posts' },\n ],\n },\n ],\n } as unknown as PayloadRequest['user']\n expect(hasPermission(user, 'write:posts')).toBe(true)\n })\n\n it('should return false if permission is not found in populated roles', () => {\n const user = {\n id: '1',\n roles: [\n {\n id: 'role-1',\n name: 'editor',\n permissions: [{ id: 'perm-1', name: 'read:posts' }],\n },\n ],\n } as unknown as PayloadRequest['user']\n expect(hasPermission(user, 'delete:posts')).toBe(false)\n })\n\n it('should correctly search through multiple roles', () => {\n const user = {\n id: '1',\n roles: [\n {\n id: 'role-1',\n name: 'editor',\n permissions: [{ id: 'perm-1', name: 'read:posts' }],\n },\n {\n id: 'role-2',\n name: 'manager',\n permissions: [{ id: 'perm-2', name: 'delete:posts' }],\n },\n ],\n } as unknown as PayloadRequest['user']\n expect(hasPermission(user, 'delete:posts')).toBe(true)\n expect(hasPermission(user, 'read:posts')).toBe(true)\n expect(hasPermission(user, 'create:users')).toBe(false)\n })\n})\n"],"names":["hasPermission","describe","it","expect","toBe","user","id","roles","name","permissions"],"mappings":"AAAA,SAASA,aAAa,QAAQ,qBAAoB;AAGlDC,SAAS,iBAAiB;IACxBC,GAAG,uCAAuC;QACxCC,OAAOH,cAAc,MAAM,eAAeI,IAAI,CAAC;IACjD;IAEAF,GAAG,kDAAkD;QACnD,MAAMG,OAAO;YAAEC,IAAI;QAAI;QACvBH,OAAOH,cAAcK,MAAM,eAAeD,IAAI,CAAC;IACjD;IAEAF,GAAG,uEAAuE;QACxE,MAAMG,OAAO;YACXC,IAAI;YACJC,OAAO;gBAAC;aAAY;QACtB;QACAJ,OAAOH,cAAcK,MAAM,eAAeD,IAAI,CAAC;IACjD;IAEAF,GAAG,kDAAkD;QACnD,MAAMG,OAAO;YACXC,IAAI;YACJC,OAAO;gBAAC;oBAAED,IAAI;oBAAUE,MAAM;gBAAQ;aAAE;QAC1C;QACAL,OAAOH,cAAcK,MAAM,eAAeD,IAAI,CAAC;IACjD;IAEAF,GAAG,8EAA8E;QAC/E,MAAMG,OAAO;YACXC,IAAI;YACJC,OAAO;gBAAC;oBAAED,IAAI;oBAAUE,MAAM;oBAASC,aAAa;wBAAC;qBAAY;gBAAC;aAAE;QACtE;QACAN,OAAOH,cAAcK,MAAM,eAAeD,IAAI,CAAC;IACjD;IAEAF,GAAG,gEAAgE;QACjE,MAAMG,OAAO;YACXC,IAAI;YACJC,OAAO;gBACL;oBACED,IAAI;oBACJE,MAAM;oBACNC,aAAa;wBACX;4BAAEH,IAAI;4BAAUE,MAAM;wBAAa;wBACnC;4BAAEF,IAAI;4BAAUE,MAAM;wBAAc;qBACrC;gBACH;aACD;QACH;QACAL,OAAOH,cAAcK,MAAM,gBAAgBD,IAAI,CAAC;IAClD;IAEAF,GAAG,qEAAqE;QACtE,MAAMG,OAAO;YACXC,IAAI;YACJC,OAAO;gBACL;oBACED,IAAI;oBACJE,MAAM;oBACNC,aAAa;wBAAC;4BAAEH,IAAI;4BAAUE,MAAM;wBAAa;qBAAE;gBACrD;aACD;QACH;QACAL,OAAOH,cAAcK,MAAM,iBAAiBD,IAAI,CAAC;IACnD;IAEAF,GAAG,kDAAkD;QACnD,MAAMG,OAAO;YACXC,IAAI;YACJC,OAAO;gBACL;oBACED,IAAI;oBACJE,MAAM;oBACNC,aAAa;wBAAC;4BAAEH,IAAI;4BAAUE,MAAM;wBAAa;qBAAE;gBACrD;gBACA;oBACEF,IAAI;oBACJE,MAAM;oBACNC,aAAa;wBAAC;4BAAEH,IAAI;4BAAUE,MAAM;wBAAe;qBAAE;gBACvD;aACD;QACH;QACAL,OAAOH,cAAcK,MAAM,iBAAiBD,IAAI,CAAC;QACjDD,OAAOH,cAAcK,MAAM,eAAeD,IAAI,CAAC;QAC/CD,OAAOH,cAAcK,MAAM,iBAAiBD,IAAI,CAAC;IACnD;AACF"}
@@ -0,0 +1,2 @@
1
+ export { checkPermission } from './checkPermission';
2
+ export { hasPermission } from './hasPermission';
@@ -0,0 +1,4 @@
1
+ export { hasPermission } from './hasPermission.js';
2
+ export { checkPermission } from './checkPermission.js';
3
+
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utilities/index.ts"],"sourcesContent":["export { hasPermission } from './hasPermission.js'\nexport { checkPermission } from './checkPermission.js'\n"],"names":["hasPermission","checkPermission"],"mappings":"AAAA,SAASA,aAAa,QAAQ,qBAAoB;AAClD,SAASC,eAAe,QAAQ,uBAAsB"}
package/package.json ADDED
@@ -0,0 +1,126 @@
1
+ {
2
+ "name": "payload-rbac-plugin",
3
+ "version": "1.0.0",
4
+ "description": "RBAC plugin for payloadcms",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/Mhmod-Hsn/Payload-RBAC-Plugin.git"
8
+ },
9
+ "bugs": {
10
+ "url": "https://github.com/Mhmod-Hsn/Payload-RBAC-Plugin/issues"
11
+ },
12
+ "homepage": "https://github.com/Mhmod-Hsn/Payload-RBAC-Plugin#readme",
13
+ "license": "MIT",
14
+ "type": "module",
15
+ "exports": {
16
+ ".": {
17
+ "import": "./src/index.ts",
18
+ "types": "./src/index.ts",
19
+ "default": "./src/index.ts"
20
+ },
21
+ "./client": {
22
+ "import": "./src/exports/client.ts",
23
+ "types": "./src/exports/client.ts",
24
+ "default": "./src/exports/client.ts"
25
+ },
26
+ "./rsc": {
27
+ "import": "./src/exports/rsc.ts",
28
+ "types": "./src/exports/rsc.ts",
29
+ "default": "./src/exports/rsc.ts"
30
+ }
31
+ },
32
+ "main": "./src/index.ts",
33
+ "types": "./src/index.ts",
34
+ "files": [
35
+ "dist"
36
+ ],
37
+ "scripts": {
38
+ "build": "pnpm copyfiles && pnpm build:types && pnpm build:swc",
39
+ "build:swc": "swc ./src -d ./dist --config-file .swcrc --strip-leading-paths",
40
+ "build:types": "tsc --outDir dist --rootDir ./src",
41
+ "clean": "rimraf {dist,*.tsbuildinfo}",
42
+ "copyfiles": "copyfiles -u 1 \"src/**/*.{html,css,scss,ttf,woff,woff2,eot,svg,jpg,png,json}\" dist/",
43
+ "dev": "next dev dev --turbo",
44
+ "dev:generate-importmap": "pnpm dev:payload generate:importmap",
45
+ "dev:generate-types": "pnpm dev:payload generate:types",
46
+ "dev:payload": "cross-env PAYLOAD_CONFIG_PATH=./dev/payload.config.ts payload",
47
+ "generate:importmap": "pnpm dev:generate-importmap",
48
+ "generate:types": "pnpm dev:generate-types",
49
+ "lint": "eslint",
50
+ "lint:fix": "eslint ./src --fix",
51
+ "test": "pnpm test:int && pnpm test:e2e",
52
+ "test:e2e": "playwright test",
53
+ "test:int": "vitest"
54
+ },
55
+ "devDependencies": {
56
+ "@eslint/eslintrc": "^3.2.0",
57
+ "@payloadcms/db-mongodb": "3.84.1",
58
+ "@payloadcms/db-postgres": "3.84.1",
59
+ "@payloadcms/db-sqlite": "3.84.1",
60
+ "@payloadcms/eslint-config": "3.28.0",
61
+ "@payloadcms/next": "3.84.1",
62
+ "@payloadcms/richtext-lexical": "3.84.1",
63
+ "@payloadcms/ui": "3.84.1",
64
+ "@playwright/test": "1.58.2",
65
+ "@swc-node/register": "1.10.9",
66
+ "@swc/cli": "0.6.0",
67
+ "@types/node": "22.19.9",
68
+ "@types/react": "19.2.14",
69
+ "@types/react-dom": "19.2.3",
70
+ "copyfiles": "2.4.1",
71
+ "cross-env": "^7.0.3",
72
+ "eslint": "^9.23.0",
73
+ "eslint-config-next": "16.2.6",
74
+ "graphql": "^16.8.1",
75
+ "mongodb-memory-server": "10.1.4",
76
+ "next": "16.2.6",
77
+ "open": "^10.1.0",
78
+ "payload": "3.84.1",
79
+ "prettier": "^3.4.2",
80
+ "qs-esm": "8.0.1",
81
+ "react": "19.2.6",
82
+ "react-dom": "19.2.6",
83
+ "rimraf": "3.0.2",
84
+ "sharp": "0.34.2",
85
+ "sort-package-json": "^2.10.0",
86
+ "typescript": "5.7.3",
87
+ "vite-tsconfig-paths": "6.0.5",
88
+ "vitest": "^4.1.8"
89
+ },
90
+ "peerDependencies": {
91
+ "payload": "^3.84.1"
92
+ },
93
+ "engines": {
94
+ "node": "^18.20.2 || >=20.9.0",
95
+ "pnpm": "^9 || ^10"
96
+ },
97
+ "publishConfig": {
98
+ "exports": {
99
+ ".": {
100
+ "import": "./dist/index.js",
101
+ "types": "./dist/index.d.ts",
102
+ "default": "./dist/index.js"
103
+ },
104
+ "./client": {
105
+ "import": "./dist/exports/client.js",
106
+ "types": "./dist/exports/client.d.ts",
107
+ "default": "./dist/exports/client.js"
108
+ },
109
+ "./rsc": {
110
+ "import": "./dist/exports/rsc.js",
111
+ "types": "./dist/exports/rsc.d.ts",
112
+ "default": "./dist/exports/rsc.js"
113
+ }
114
+ },
115
+ "main": "./dist/index.js",
116
+ "types": "./dist/index.d.ts"
117
+ },
118
+ "pnpm": {
119
+ "onlyBuiltDependencies": [
120
+ "sharp",
121
+ "esbuild",
122
+ "unrs-resolver"
123
+ ]
124
+ },
125
+ "registry": "https://registry.npmjs.org/"
126
+ }