roles-privileges-payload-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 (2) hide show
  1. package/README.md +650 -0
  2. package/package.json +130 -0
package/README.md ADDED
@@ -0,0 +1,650 @@
1
+ # Roles & Privileges Payload Plugin
2
+
3
+ A powerful Payload CMS plugin that automatically generates role-based access control (RBAC) with granular CRUD privileges for all your collections.
4
+
5
+ ## Features
6
+
7
+ - 🔐 **Automatic Privilege Generation**: Automatically creates CRUD privileges for collections and read/update privileges for globals
8
+ - 🎯 **Smart Access Control Wrapping**: Seamlessly wraps existing collection and global access controls with privilege checks
9
+ - 🌐 **Full Global Support**: Generates privileges for Payload globals (read/update operations)
10
+ - 👑 **Super Admin Role**: Auto-seeds a Super Admin role with all privileges
11
+ - 🎨 **Beautiful UI**: Custom privilege selector component with collapsible interface showing both collections and globals
12
+ - ⭐ **Custom Privileges**: Register custom privileges that appear in the admin UI alongside auto-generated ones
13
+ - 🗣️ **Multilingual**: Full support for any language with fallback chains (\_default → en → first available)
14
+ - ⚙️ **Highly Configurable**: Exclude collections/globals, disable features, or customize behavior
15
+ - 🔄 **Zero Configuration**: Works out of the box with sensible defaults
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ npm install roles-privileges-payload-plugin
21
+ # or
22
+ pnpm add roles-privileges-payload-plugin
23
+ # or
24
+ yarn add roles-privileges-payload-plugin
25
+ ```
26
+
27
+ ## Basic Usage
28
+
29
+ Add the plugin to your Payload configuration:
30
+
31
+ ```ts
32
+ import { buildConfig } from 'payload'
33
+ import { rolesPrivilegesPayloadPlugin } from 'roles-privileges-payload-plugin'
34
+
35
+ export default buildConfig({
36
+ collections: [
37
+ // Your collections here
38
+ ],
39
+ plugins: [rolesPrivilegesPayloadPlugin()],
40
+ })
41
+ ```
42
+
43
+ That's it! The plugin will:
44
+
45
+ 1. Scan all your collections and globals
46
+ 2. Generate CRUD privileges for each collection (create, read, update, delete)
47
+ 3. Generate read/update privileges for each global
48
+ 4. Add a `roles` collection with a privilege selector UI
49
+ 5. Wrap all collection and global access controls with privilege checks
50
+ 6. Seed a Super Admin role with all privileges
51
+
52
+ ## Configuration Options
53
+
54
+ ```ts
55
+ rolesPrivilegesPayloadPlugin({
56
+ // Enable the plugin (defaults to true). When false, the plugin does nothing.
57
+ enable: true,
58
+
59
+ // Disable the plugin (roles collection will still be added for schema consistency)
60
+ disabled: false,
61
+
62
+ // Collections to exclude from automatic privilege generation
63
+ excludeCollections: ['media', 'payload-preferences'],
64
+
65
+ // Globals to exclude from automatic privilege generation
66
+ excludeGlobals: [],
67
+
68
+ // Automatically wrap collection access controls with privilege checks
69
+ wrapCollectionAccess: true,
70
+
71
+ // Automatically wrap global access controls with privilege checks
72
+ wrapGlobalAccess: true,
73
+
74
+ // Seed a Super Admin role with all privileges on init
75
+ seedSuperAdmin: true,
76
+
77
+ // Provide a custom roles collection configuration (optional)
78
+ // Use createRolesCollection() helper to create a base and customize it
79
+ customRolesCollection: undefined,
80
+ })
81
+ ```
82
+
83
+ ## How It Works
84
+
85
+ ### 1. Privilege Generation
86
+
87
+ **For Collections:**
88
+
89
+ The plugin automatically generates seven privileges for each collection:
90
+
91
+ - `{collection-slug}-admin`: Permission to access the collection's admin UI
92
+ - `{collection-slug}-create`: Permission to create new documents
93
+ - `{collection-slug}-read`: Permission to read/view documents
94
+ - `{collection-slug}-readVersions`: Permission to view document version history
95
+ - `{collection-slug}-update`: Permission to update existing documents
96
+ - `{collection-slug}-delete`: Permission to delete documents
97
+ - `{collection-slug}-unlock`: Permission to unlock documents being edited by others
98
+
99
+ Example for a `posts` collection:
100
+
101
+ - `posts-admin`
102
+ - `posts-create`
103
+ - `posts-read`
104
+ - `posts-readVersions`
105
+ - `posts-update`
106
+ - `posts-delete`
107
+ - `posts-unlock`
108
+
109
+ **For Globals:**
110
+
111
+ The plugin generates four privileges for each global:
112
+
113
+ - `{global-slug}-read`: Permission to read/view the global
114
+ - `{global-slug}-readDrafts`: Permission to view draft versions
115
+ - `{global-slug}-readVersions`: Permission to view version history
116
+ - `{global-slug}-update`: Permission to update the global
117
+
118
+ Example for a `site-settings` global:
119
+
120
+ - `site-settings-read`
121
+ - `site-settings-update`
122
+
123
+ ### 2. Access Control Wrapping
124
+
125
+ The plugin wraps your existing collection access controls. For example:
126
+
127
+ **Before:**
128
+
129
+ ```ts
130
+ {
131
+ slug: 'posts',
132
+ access: {
133
+ read: () => true,
134
+ create: ({ req: { user } }) => !!user,
135
+ }
136
+ }
137
+ ```
138
+
139
+ **After (automatically wrapped):**
140
+
141
+ ```ts
142
+ {
143
+ slug: 'posts',
144
+ access: {
145
+ read: async (args) => {
146
+ const hasOriginalAccess = true // from original function
147
+ if (!hasOriginalAccess) return false
148
+ return hasPrivilege('posts-read')(args)
149
+ },
150
+ create: async (args) => {
151
+ const hasOriginalAccess = !!args.req.user // from original function
152
+ if (!hasOriginalAccess) return false
153
+ return hasPrivilege('posts-create')(args)
154
+ }
155
+ }
156
+ }
157
+ ```
158
+
159
+ ### 3. The Roles Collection
160
+
161
+ The plugin adds a `roles` collection with:
162
+
163
+ - `title`: The role name
164
+ - `slug`: Unique identifier (auto-generated from title)
165
+ - `privileges`: Array of privilege keys
166
+ - `description`: Optional role description
167
+
168
+ The privileges field uses a custom UI component that provides:
169
+
170
+ - Collapsible interface organized by collections and globals
171
+ - Visual distinction between collections (📦) and globals (🌐)
172
+ - Star icon (⭐) for custom privileges
173
+ - Checkbox selection for easy privilege management
174
+ - Real-time privilege descriptions with multilingual support
175
+ - Badge counter showing selected privileges per collection/global
176
+
177
+ ### 4. User Integration
178
+
179
+ To use roles in your users collection, add a relationship field:
180
+
181
+ ```ts
182
+ {
183
+ slug: 'users',
184
+ fields: [
185
+ {
186
+ name: 'roles',
187
+ type: 'relationship',
188
+ relationTo: 'roles',
189
+ hasMany: true,
190
+ required: true,
191
+ },
192
+ // other fields...
193
+ ]
194
+ }
195
+ ```
196
+
197
+ ## Advanced Usage
198
+
199
+ ### Creating Custom Privileges
200
+
201
+ You can create custom privileges beyond the auto-generated CRUD operations. Custom privileges registered with `registerCustomPrivilege` will appear in the admin UI with a star icon (⭐) to differentiate them from auto-generated privileges:
202
+
203
+ ```ts
204
+ import { registerCustomPrivilege, hasPrivilege } from 'roles-privileges-payload-plugin'
205
+
206
+ // Register a custom privilege (will appear in admin UI)
207
+ const publishPrivilege = registerCustomPrivilege('posts', {
208
+ privilegeKey: 'posts-publish',
209
+ label: {
210
+ en: 'Publish Posts',
211
+ fr: 'Publier les articles',
212
+ },
213
+ description: {
214
+ en: 'Ability to publish posts to make them publicly visible',
215
+ fr: 'Capacité de publier des articles pour les rendre publiquement visibles',
216
+ },
217
+ })
218
+
219
+ // Use it in your collection
220
+ export const Posts = {
221
+ slug: 'posts',
222
+ fields: [
223
+ {
224
+ name: 'status',
225
+ type: 'select',
226
+ options: ['draft', 'published'],
227
+ access: {
228
+ update: async ({ req, data }) => {
229
+ if (data?.status === 'published') {
230
+ return hasPrivilege(publishPrivilege.privilegeKey)({ req })
231
+ }
232
+ return true
233
+ },
234
+ },
235
+ },
236
+ ],
237
+ }
238
+
239
+ // Register multiple custom privileges at once
240
+ import { registerCustomPrivileges } from 'roles-privileges-payload-plugin'
241
+
242
+ const customPrivileges = registerCustomPrivileges('posts', [
243
+ {
244
+ privilegeKey: 'posts-publish',
245
+ label: { en: 'Publish Posts' },
246
+ description: { en: 'Publish posts to make them visible' },
247
+ },
248
+ {
249
+ privilegeKey: 'posts-feature',
250
+ label: { en: 'Feature Posts' },
251
+ description: { en: 'Feature posts on the homepage' },
252
+ },
253
+ ])
254
+ ```
255
+
256
+ Custom privileges are visually distinguished in the admin UI with:
257
+
258
+ - ⭐ Star icon next to the privilege label
259
+ - Yellow/warning background for the privilege key badge
260
+ - Clear separation from auto-generated CRUD privileges
261
+
262
+ For more details, see [CUSTOM_PRIVILEGES.md](./CUSTOM_PRIVILEGES.md).
263
+
264
+ ### Custom Privilege Checks
265
+
266
+ You can use the privilege access functions in your own access controls:
267
+
268
+ ```ts
269
+ import {
270
+ hasPrivilege,
271
+ hasAnyPrivilege,
272
+ hasAllPrivileges,
273
+ checkPrivilege,
274
+ } from 'roles-privileges-payload-plugin'
275
+
276
+ // Single privilege check (for collection/global access)
277
+ {
278
+ access: {
279
+ read: hasPrivilege('posts-read')
280
+ }
281
+ }
282
+
283
+ // For field-level access, use checkPrivilege (synchronous)
284
+ {
285
+ fields: [
286
+ {
287
+ name: 'sensitiveField',
288
+ type: 'text',
289
+ access: {
290
+ read: ({ req }) => checkPrivilege('posts-admin', req.user),
291
+ update: ({ req }) => checkPrivilege('posts-admin', req.user),
292
+ },
293
+ },
294
+ ]
295
+ }
296
+
297
+ // Field-level access with ANY privilege (OR logic)
298
+ {
299
+ fields: [
300
+ {
301
+ name: 'status',
302
+ access: {
303
+ update: ({ req }) => checkAnyPrivilege(req.user, 'posts-update', 'posts-admin'),
304
+ },
305
+ },
306
+ ]
307
+ }
308
+
309
+ // Field-level access with ALL privileges (AND logic)
310
+ {
311
+ fields: [
312
+ {
313
+ name: 'publishedDate',
314
+ access: {
315
+ update: ({ req }) => checkAllPrivileges(req.user, 'posts-update', 'posts-publish'),
316
+ },
317
+ },
318
+ ]
319
+ }
320
+
321
+ // Complex field-level privilege logic
322
+ {
323
+ fields: [
324
+ {
325
+ name: 'featured',
326
+ access: {
327
+ // User needs (posts-update AND posts-feature) OR (posts-admin)
328
+ update: ({ req }) =>
329
+ checkPrivileges([['posts-update', 'posts-feature'], ['posts-admin']], req.user),
330
+ },
331
+ },
332
+ ]
333
+ }
334
+
335
+ // User needs ANY of these privileges (OR logic)
336
+ {
337
+ access: {
338
+ read: hasAnyPrivilege('posts-read', 'posts-update')
339
+ }
340
+ }
341
+
342
+ // User needs ALL of these privileges (AND logic)
343
+ {
344
+ access: {
345
+ read: hasAllPrivileges('posts-read', 'posts-update')
346
+ }
347
+ }
348
+
349
+ // Complex privilege logic
350
+ import { privilegesAccess } from 'roles-privileges-payload-plugin'
351
+
352
+ {
353
+ access: {
354
+ // User needs (posts-create AND posts-read) OR (pages-create AND pages-read)
355
+ read: privilegesAccess([
356
+ ['posts-create', 'posts-read'],
357
+ ['pages-create', 'pages-read'],
358
+ ])
359
+ }
360
+ }
361
+ ```
362
+
363
+ ### Excluding Collections and Globals
364
+
365
+ Some collections or globals don't need privilege-based access control:
366
+
367
+ ```ts
368
+ rolesPrivilegesPayloadPlugin({
369
+ excludeCollections: [
370
+ 'media', // Public media access
371
+ 'payload-preferences', // User preferences
372
+ 'payload-migrations', // System migrations
373
+ ],
374
+ excludeGlobals: [
375
+ 'site-settings', // Public site settings
376
+ ],
377
+ })
378
+ ```
379
+
380
+ ### Accessing Generated Privileges
381
+
382
+ You can access all generated privileges programmatically:
383
+
384
+ ```ts
385
+ import {
386
+ allPrivilegesMap,
387
+ getAllPrivileges,
388
+ getAllPrivilegeKeys,
389
+ allGlobalPrivilegesMap,
390
+ getAllGlobalPrivileges,
391
+ getAllGlobalPrivilegeKeys,
392
+ } from 'roles-privileges-payload-plugin'
393
+
394
+ // Get all collection privileges as a Map
395
+ const privilegesMap = allPrivilegesMap
396
+
397
+ // Get all collection privileges as a flat array
398
+ const allPrivileges = getAllPrivileges()
399
+
400
+ // Get just the collection privilege keys
401
+ const privilegeKeys = getAllPrivilegeKeys()
402
+
403
+ // Get all global privileges
404
+ const globalPrivilegesMap = allGlobalPrivilegesMap
405
+ const allGlobalPrivileges = getAllGlobalPrivileges()
406
+ const globalPrivilegeKeys = getAllGlobalPrivilegeKeys()
407
+ ```
408
+
409
+ ### Custom Roles Collection
410
+
411
+ By default, the plugin creates a standard `roles` collection. However, you can provide your own custom roles collection configuration if you need to:
412
+
413
+ - Add additional fields to the roles collection
414
+ - Customize the collection's access control
415
+ - Add custom hooks or endpoints
416
+ - Modify the admin UI
417
+
418
+ ```ts
419
+ import {
420
+ rolesPrivilegesPayloadPlugin,
421
+ createRolesCollection,
422
+ ensureSuperAdminDontGetDeleted,
423
+ ensureSuperAdminDontGetUpdated,
424
+ } from 'roles-privileges-payload-plugin'
425
+
426
+ // Create a custom roles collection based on the default
427
+ const customRolesCollection = createRolesCollection()
428
+
429
+ // Customize it by adding additional fields
430
+ customRolesCollection.fields.push({
431
+ name: 'department',
432
+ type: 'select',
433
+ options: ['Engineering', 'Marketing', 'Sales'],
434
+ admin: {
435
+ position: 'sidebar',
436
+ },
437
+ })
438
+
439
+ // Add custom hooks
440
+ customRolesCollection.hooks = {
441
+ ...customRolesCollection.hooks,
442
+ afterChange: [
443
+ async ({ doc, req }) => {
444
+ // Send notification when role is changed
445
+ console.log(`Role ${doc.title} was modified`)
446
+ },
447
+ ],
448
+ }
449
+
450
+ // Use the custom collection in the plugin
451
+ export default buildConfig({
452
+ collections: [
453
+ // Your other collections
454
+ ],
455
+ plugins: [
456
+ rolesPrivilegesPayloadPlugin({
457
+ customRolesCollection,
458
+ }),
459
+ ],
460
+ })
461
+ ```
462
+
463
+ **Important Notes:**
464
+
465
+ - The custom roles collection **must** have the slug `'roles'`
466
+ - The plugin provides helper functions to maintain Super Admin protection:
467
+ - `createRolesCollection()`: Base factory function to create the roles collection
468
+ - `ensureSuperAdminDontGetDeleted`: Hook to prevent Super Admin role deletion
469
+ - `ensureSuperAdminDontGetUpdated`: Hook to prevent Super Admin slug modification
470
+ - You can start with `createRolesCollection()` and customize from there, or build entirely from scratch
471
+ - The `privileges` field must remain for the privilege system to work
472
+
473
+ **Available Exports for Custom Roles:**
474
+
475
+ ```ts
476
+ import {
477
+ // Collection creation
478
+ createRolesCollection,
479
+
480
+ // Types
481
+ CollectionData,
482
+ GlobalData,
483
+
484
+ // Hooks
485
+ ensureSuperAdminDontGetDeleted,
486
+ ensureSuperAdminDontGetUpdated,
487
+
488
+ // Utilities
489
+ seedSuperAdminRole,
490
+ } from 'roles-privileges-payload-plugin'
491
+ ```
492
+
493
+ ## Super Admin Role
494
+
495
+ The plugin automatically creates/updates a Super Admin role with:
496
+
497
+ - Slug: `super-admin`
498
+ - All available privileges
499
+ - Protection against deletion and slug modification
500
+
501
+ To assign the Super Admin role to a user:
502
+
503
+ ```ts
504
+ await payload.update({
505
+ collection: 'users',
506
+ id: userId,
507
+ data: {
508
+ roles: ['super-admin-role-id'],
509
+ },
510
+ })
511
+ ```
512
+
513
+ ## Privilege Label Customization
514
+
515
+ Privilege labels are automatically generated based on collection labels:
516
+
517
+ ```ts
518
+ // If your collection has labels:
519
+ {
520
+ slug: 'blog-posts',
521
+ labels: {
522
+ singular: { en: 'Blog Post', fr: 'Article de blog' },
523
+ plural: { en: 'Blog Posts', fr: 'Articles de blog' }
524
+ }
525
+ }
526
+
527
+ // Generated privilege labels:
528
+ // - Create Blog Post / Créer un article de blog
529
+ // - Read Blog Post / Lire un article de blog
530
+ // - Update Blog Post / Modifier un article de blog
531
+ // - Delete Blog Post / Supprimer un article de blog
532
+ ```
533
+
534
+ If no labels are provided, the plugin capitalizes the slug:
535
+
536
+ - `blog-posts` → "Blog Posts"
537
+
538
+ ## TypeScript Support
539
+
540
+ The plugin is fully typed. All exports include TypeScript definitions:
541
+
542
+ ```ts
543
+ import type {
544
+ RolesPrivilegesPayloadPluginConfig,
545
+ Privilege,
546
+ CollectionPrivileges,
547
+ PrivilegeType,
548
+ GlobalPrivilege,
549
+ GlobalPrivileges,
550
+ GlobalPrivilegeType,
551
+ } from 'roles-privileges-payload-plugin'
552
+ ```
553
+
554
+ ## API Reference
555
+
556
+ ### Plugin Configuration
557
+
558
+ ```ts
559
+ type RolesPrivilegesPayloadPluginConfig = {
560
+ enable?: boolean
561
+ disabled?: boolean
562
+ excludeCollections?: string[]
563
+ excludeGlobals?: string[]
564
+ wrapCollectionAccess?: boolean
565
+ wrapGlobalAccess?: boolean
566
+ seedSuperAdmin?: boolean
567
+ }
568
+ ```
569
+
570
+ ### Exported Functions
571
+
572
+ **Main Plugin:**
573
+
574
+ - `rolesPrivilegesPayloadPlugin(config?)`: Main plugin function
575
+
576
+ **Access Control (Collection/Global Level - Async):**
577
+
578
+ - `hasPrivilege(key: string)`: Check for a single privilege
579
+ - `hasAnyPrivilege(...keys: string[])`: Check for any privilege (OR logic)
580
+ - `hasAllPrivileges(...keys: string[])`: Check for all privileges (AND logic)
581
+ - `privilegesAccess(arrays: string[][])`: Complex privilege logic
582
+
583
+ **Access Control (Field Level - Synchronous):**
584
+
585
+ - `checkPrivilege(key: string, user: any)`: Check for a single privilege
586
+ - `checkAnyPrivilege(user: any, ...keys: string[])`: Check for any privilege (OR logic)
587
+ - `checkAllPrivileges(user: any, ...keys: string[])`: Check for all privileges (AND logic)
588
+ - `checkPrivileges(arrays: string[][], user: any)`: Complex privilege logic
589
+
590
+ **Collection Privileges:**
591
+
592
+ - `generateCollectionPrivileges(collection)`: Generate privileges for a collection
593
+ - `getAllPrivileges()`: Get all collection privileges
594
+ - `getAllPrivilegeKeys()`: Get all collection privilege keys
595
+ - `allPrivilegesMap`: Map of all collection privileges
596
+
597
+ **Global Privileges:**
598
+
599
+ - `generateGlobalPrivileges(global)`: Generate privileges for a global
600
+ - `getAllGlobalPrivileges()`: Get all global privileges
601
+ - `getAllGlobalPrivilegeKeys()`: Get all global privilege keys
602
+ - `allGlobalPrivilegesMap`: Map of all global privileges
603
+
604
+ **Custom Privileges:**
605
+
606
+ - `registerCustomPrivilege(slug, config, options?)`: Register a single custom privilege
607
+ - `registerCustomPrivileges(slug, configs, options?)`: Register multiple custom privileges
608
+ - `customPrivilegesRegistry`: Map of all registered custom privileges
609
+
610
+ ## Best Practices
611
+
612
+ 1. **Always use the roles relationship**: Connect users to roles via a relationship field
613
+ 2. **Start with Super Admin**: Create your first user with the Super Admin role
614
+ 3. **Exclude system collections**: Exclude collections like migrations and preferences
615
+ 4. **Consider global permissions**: Remember that globals only have read/update (no create/delete)
616
+ 5. **Register custom privileges early**: Register custom privileges before the plugin initializes
617
+ 6. **Use multilingual labels**: Provide translations for all languages your app supports
618
+ 7. **Test privilege combinations**: Test different role configurations thoroughly
619
+ 8. **Document custom roles**: Maintain documentation for custom roles you create
620
+
621
+ ## Troubleshooting
622
+
623
+ ### Privileges not working
624
+
625
+ Ensure your users collection has the roles relationship:
626
+
627
+ ```ts
628
+ describe('Plugin tests', () => {
629
+ // Create tests to ensure expected behavior from the plugin
630
+ it('some condition that must be met', () => {
631
+ // Write your test logic here
632
+ expect(...)
633
+ })
634
+ })
635
+ ```
636
+
637
+ ## Best practices
638
+
639
+ With this tutorial and the plugin template, you should have everything you need to start building your own plugin.
640
+ In addition to the setup, here are other best practices aim we follow:
641
+
642
+ - **Providing an enable / disable option:** For a better user experience, provide a way to disable the plugin without uninstalling it. This is especially important if your plugin adds additional webpack aliases, this will allow you to still let the webpack run to prevent errors.
643
+ - **Include tests in your GitHub CI workflow**: If you’ve configured tests for your package, integrate them into your workflow to run the tests each time you commit to the plugin repository. Learn more about [how to configure tests into your GitHub CI workflow.](https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs)
644
+ - **Publish your finished plugin to NPM**: The best way to share and allow others to use your plugin once it is complete is to publish an NPM package. This process is straightforward and well documented, find out more [creating and publishing a NPM package here.](https://docs.npmjs.com/creating-and-publishing-scoped-public-packages/).
645
+ - **Add payload-plugin topic tag**: Apply the tag **payload-plugin **to your GitHub repository. This will boost the visibility of your plugin and ensure it gets listed with [existing payload plugins](https://github.com/topics/payload-plugin).
646
+ - **Use [Semantic Versioning](https://semver.org/) (SemVar)** - With the SemVar system you release version numbers that reflect the nature of changes (major, minor, patch). Ensure all major versions reference their Payload compatibility.
647
+
648
+ # Questions
649
+
650
+ Please contact [Payload](mailto:dev@payloadcms.com) with any questions about using this plugin template.
package/package.json ADDED
@@ -0,0 +1,130 @@
1
+ {
2
+ "name": "roles-privileges-payload-plugin",
3
+ "version": "1.0.0",
4
+ "description": "Automatic role-based access control (RBAC) plugin for Payload CMS that generates granular CRUD privileges for all collections with beautiful UI and zero configuration",
5
+ "author": "Hassine",
6
+ "license": "MIT",
7
+ "type": "module",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./src/index.ts",
11
+ "types": "./src/index.ts",
12
+ "default": "./src/index.ts"
13
+ },
14
+ "./client": {
15
+ "import": "./src/exports/client.ts",
16
+ "types": "./src/exports/client.ts",
17
+ "default": "./src/exports/client.ts"
18
+ },
19
+ "./utilities": {
20
+ "import": "./src/exports/utilities.ts",
21
+ "types": "./src/exports/utilities.ts",
22
+ "default": "./src/exports/utilities.ts"
23
+ },
24
+ "./types": {
25
+ "import": "./src/exports/types.ts",
26
+ "types": "./src/exports/types.ts",
27
+ "default": "./src/exports/types.ts"
28
+ }
29
+ },
30
+ "main": "./src/index.ts",
31
+ "types": "./src/index.ts",
32
+ "files": [
33
+ "dist"
34
+ ],
35
+ "scripts": {
36
+ "build": "npm run copyfiles && npm run build:types && npm run build:swc",
37
+ "build:swc": "swc ./src -d ./dist --config-file .swcrc --strip-leading-paths",
38
+ "build:types": "tsc --outDir dist --rootDir ./src",
39
+ "clean": "rimraf {dist,*.tsbuildinfo}",
40
+ "copyfiles": "copyfiles -u 1 \"src/**/*.{html,css,scss,ttf,woff,woff2,eot,svg,jpg,png,json}\" dist/",
41
+ "dev": "next dev dev --turbo",
42
+ "dev:generate-importmap": "npm run dev:payload generate:importmap",
43
+ "dev:generate-types": "npm run dev:payload generate:types",
44
+ "dev:payload": "cross-env PAYLOAD_CONFIG_PATH=./dev/payload.config.ts payload",
45
+ "generate:importmap": "npm run dev:generate-importmap",
46
+ "generate:types": "npm run dev:generate-types",
47
+ "lint": "eslint",
48
+ "lint:fix": "eslint ./src --fix",
49
+ "test": "npm run test:int && npm run test:e2e",
50
+ "test:e2e": "playwright test",
51
+ "test:int": "vitest"
52
+ },
53
+ "devDependencies": {
54
+ "@eslint/eslintrc": "^3.2.0",
55
+ "@payloadcms/db-mongodb": "3.37.0",
56
+ "@payloadcms/db-postgres": "3.37.0",
57
+ "@payloadcms/db-sqlite": "3.37.0",
58
+ "@payloadcms/eslint-config": "3.9.0",
59
+ "@payloadcms/next": "3.37.0",
60
+ "@payloadcms/richtext-lexical": "3.37.0",
61
+ "@payloadcms/ui": "3.37.0",
62
+ "@playwright/test": "1.56.1",
63
+ "@swc-node/register": "1.10.9",
64
+ "@swc/cli": "0.6.0",
65
+ "@types/node": "^22.5.4",
66
+ "@types/react": "19.2.1",
67
+ "@types/react-dom": "19.2.1",
68
+ "copyfiles": "2.4.1",
69
+ "cross-env": "^7.0.3",
70
+ "eslint": "^9.23.0",
71
+ "eslint-config-next": "15.4.7",
72
+ "graphql": "^16.8.1",
73
+ "mongodb-memory-server": "10.1.4",
74
+ "next": "15.4.10",
75
+ "open": "^10.1.0",
76
+ "payload": "3.37.0",
77
+ "prettier": "^3.4.2",
78
+ "qs-esm": "7.0.2",
79
+ "react": "19.2.1",
80
+ "react-dom": "19.2.1",
81
+ "rimraf": "3.0.2",
82
+ "sharp": "0.34.2",
83
+ "sort-package-json": "^2.10.0",
84
+ "typescript": "5.7.3",
85
+ "vite-tsconfig-paths": "^5.1.4",
86
+ "vitest": "^3.1.2"
87
+ },
88
+ "peerDependencies": {
89
+ "payload": "^3.37.0"
90
+ },
91
+ "engines": {
92
+ "node": "^18.20.2 || >=20.9.0",
93
+ "pnpm": "^9 || ^10"
94
+ },
95
+ "publishConfig": {
96
+ "exports": {
97
+ ".": {
98
+ "import": "./dist/index.js",
99
+ "types": "./dist/index.d.ts",
100
+ "default": "./dist/index.js"
101
+ },
102
+ "./client": {
103
+ "import": "./dist/exports/client.js",
104
+ "types": "./dist/exports/client.d.ts",
105
+ "default": "./dist/exports/client.js"
106
+ },
107
+ "./utilities": {
108
+ "import": "./dist/exports/utilities.js",
109
+ "types": "./dist/exports/utilities.d.ts",
110
+ "default": "./dist/exports/utilities.js"
111
+ },
112
+ "./types": {
113
+ "import": "./dist/exports/types.js",
114
+ "types": "./dist/exports/types.d.ts",
115
+ "default": "./dist/exports/types.js"
116
+ }
117
+ },
118
+ "main": "./dist/index.js",
119
+ "types": "./dist/index.d.ts"
120
+ },
121
+ "pnpm": {
122
+ "onlyBuiltDependencies": [
123
+ "sharp",
124
+ "esbuild",
125
+ "unrs-resolver"
126
+ ]
127
+ },
128
+ "registry": "https://registry.npmjs.org/",
129
+ "dependencies": {}
130
+ }