@zealamic/payload-auth-rbac-plugin 1.0.0-beta.1

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 (85) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +175 -0
  3. package/dist/collections/permission-actions/default-data.js +35 -0
  4. package/dist/collections/permission-actions/default-data.js.map +1 -0
  5. package/dist/collections/permission-actions/index.js +96 -0
  6. package/dist/collections/permission-actions/index.js.map +1 -0
  7. package/dist/collections/permission-actions/types.js +3 -0
  8. package/dist/collections/permission-actions/types.js.map +1 -0
  9. package/dist/collections/permission-features/default-data.js +29 -0
  10. package/dist/collections/permission-features/default-data.js.map +1 -0
  11. package/dist/collections/permission-features/index.js +82 -0
  12. package/dist/collections/permission-features/index.js.map +1 -0
  13. package/dist/collections/permission-features/types.js +3 -0
  14. package/dist/collections/permission-features/types.js.map +1 -0
  15. package/dist/collections/permissions/default-data.js +37 -0
  16. package/dist/collections/permissions/default-data.js.map +1 -0
  17. package/dist/collections/permissions/index.js +102 -0
  18. package/dist/collections/permissions/index.js.map +1 -0
  19. package/dist/collections/permissions/types.js +3 -0
  20. package/dist/collections/permissions/types.js.map +1 -0
  21. package/dist/collections/roles/default-data.js +44 -0
  22. package/dist/collections/roles/default-data.js.map +1 -0
  23. package/dist/collections/roles/hooks/sync-permission-matrix-draft.js +66 -0
  24. package/dist/collections/roles/hooks/sync-permission-matrix-draft.js.map +1 -0
  25. package/dist/collections/roles/index.js +122 -0
  26. package/dist/collections/roles/index.js.map +1 -0
  27. package/dist/collections/roles/types.js +3 -0
  28. package/dist/collections/roles/types.js.map +1 -0
  29. package/dist/collections/roles-permissions/default-data.js +27 -0
  30. package/dist/collections/roles-permissions/default-data.js.map +1 -0
  31. package/dist/collections/roles-permissions/index.js +75 -0
  32. package/dist/collections/roles-permissions/index.js.map +1 -0
  33. package/dist/collections/roles-permissions/types.js +3 -0
  34. package/dist/collections/roles-permissions/types.js.map +1 -0
  35. package/dist/collections/users/default-data.js +19 -0
  36. package/dist/collections/users/default-data.js.map +1 -0
  37. package/dist/collections/users/index.js +135 -0
  38. package/dist/collections/users/index.js.map +1 -0
  39. package/dist/collections/users/parent-path.js +210 -0
  40. package/dist/collections/users/parent-path.js.map +1 -0
  41. package/dist/collections/users/types.js +3 -0
  42. package/dist/collections/users/types.js.map +1 -0
  43. package/dist/components/role-permission-matrix-client/default-data.js +23 -0
  44. package/dist/components/role-permission-matrix-client/default-data.js.map +1 -0
  45. package/dist/components/role-permission-matrix-client/index.js +299 -0
  46. package/dist/components/role-permission-matrix-client/index.js.map +1 -0
  47. package/dist/components/role-permission-matrix-client/types.js +3 -0
  48. package/dist/components/role-permission-matrix-client/types.js.map +1 -0
  49. package/dist/endpoints/customEndpointHandler.js +7 -0
  50. package/dist/endpoints/customEndpointHandler.js.map +1 -0
  51. package/dist/exports/client.js +3 -0
  52. package/dist/exports/client.js.map +1 -0
  53. package/dist/exports/rsc.js +2 -0
  54. package/dist/exports/rsc.js.map +1 -0
  55. package/dist/general-types.d.js +2 -0
  56. package/dist/general-types.d.js.map +1 -0
  57. package/dist/index.js +184 -0
  58. package/dist/index.js.map +1 -0
  59. package/dist/lib/constants/general.js +3 -0
  60. package/dist/lib/constants/general.js.map +1 -0
  61. package/dist/lib/constants/index.js +16 -0
  62. package/dist/lib/constants/index.js.map +1 -0
  63. package/dist/lib/constants/permission-action.js +10 -0
  64. package/dist/lib/constants/permission-action.js.map +1 -0
  65. package/dist/lib/constants/permission-feature.js +6 -0
  66. package/dist/lib/constants/permission-feature.js.map +1 -0
  67. package/dist/lib/constants/permission.js +6 -0
  68. package/dist/lib/constants/permission.js.map +1 -0
  69. package/dist/lib/constants/role.js +11 -0
  70. package/dist/lib/constants/role.js.map +1 -0
  71. package/dist/lib/constants/user.js +3 -0
  72. package/dist/lib/constants/user.js.map +1 -0
  73. package/dist/lib/utils/access.js +452 -0
  74. package/dist/lib/utils/access.js.map +1 -0
  75. package/dist/lib/utils/data.js +7 -0
  76. package/dist/lib/utils/data.js.map +1 -0
  77. package/dist/lib/utils/fields.js +41 -0
  78. package/dist/lib/utils/fields.js.map +1 -0
  79. package/dist/lib/utils/index.js +6 -0
  80. package/dist/lib/utils/index.js.map +1 -0
  81. package/dist/lib/utils/localization.js +52 -0
  82. package/dist/lib/utils/localization.js.map +1 -0
  83. package/dist/types.js +10 -0
  84. package/dist/types.js.map +1 -0
  85. package/package.json +122 -0
@@ -0,0 +1,16 @@
1
+ import * as general from "./general.js";
2
+ import * as permission from "./permission.js";
3
+ import * as permissionAction from "./permission-action.js";
4
+ import * as permissionFeature from "./permission-feature.js";
5
+ import * as role from "./role.js";
6
+ import * as user from "./user.js";
7
+ export const CONSTANTS = {
8
+ GENERAL: general,
9
+ PERMISSION: permission,
10
+ PERMISSION_FEATURE: permissionFeature,
11
+ PERMISSION_ACTION: permissionAction,
12
+ ROLE: role,
13
+ USER: user
14
+ };
15
+
16
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/lib/constants/index.ts"],"sourcesContent":["import * as general from \"./general.js\";\nimport * as permission from \"./permission.js\";\nimport * as permissionAction from \"./permission-action.js\";\nimport * as permissionFeature from \"./permission-feature.js\";\nimport * as role from \"./role.js\";\nimport * as user from \"./user.js\";\n\nexport const CONSTANTS = {\n GENERAL: general,\n PERMISSION: permission,\n PERMISSION_FEATURE: permissionFeature,\n PERMISSION_ACTION: permissionAction,\n ROLE: role,\n USER: user,\n} as const;\n"],"names":["general","permission","permissionAction","permissionFeature","role","user","CONSTANTS","GENERAL","PERMISSION","PERMISSION_FEATURE","PERMISSION_ACTION","ROLE","USER"],"mappings":"AAAA,YAAYA,aAAa,eAAe;AACxC,YAAYC,gBAAgB,kBAAkB;AAC9C,YAAYC,sBAAsB,yBAAyB;AAC3D,YAAYC,uBAAuB,0BAA0B;AAC7D,YAAYC,UAAU,YAAY;AAClC,YAAYC,UAAU,YAAY;AAElC,OAAO,MAAMC,YAAY;IACvBC,SAASP;IACTQ,YAAYP;IACZQ,oBAAoBN;IACpBO,mBAAmBR;IACnBS,MAAMP;IACNQ,MAAMP;AACR,EAAW"}
@@ -0,0 +1,10 @@
1
+ export const STATUS = {
2
+ ACTIVE: "active",
3
+ INACTIVE: "inactive"
4
+ };
5
+ export const TYPE = {
6
+ MAIN: "main",
7
+ SUB: "sub"
8
+ };
9
+
10
+ //# sourceMappingURL=permission-action.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/lib/constants/permission-action.ts"],"sourcesContent":["export const STATUS = {\n ACTIVE: \"active\",\n INACTIVE: \"inactive\",\n} as const\n\nexport const TYPE = {\n MAIN: \"main\",\n SUB: \"sub\",\n} as const\n"],"names":["STATUS","ACTIVE","INACTIVE","TYPE","MAIN","SUB"],"mappings":"AAAA,OAAO,MAAMA,SAAS;IACpBC,QAAQ;IACRC,UAAU;AACZ,EAAU;AAEV,OAAO,MAAMC,OAAO;IAClBC,MAAM;IACNC,KAAK;AACP,EAAU"}
@@ -0,0 +1,6 @@
1
+ export const STATUS = {
2
+ ACTIVE: "active",
3
+ INACTIVE: "inactive"
4
+ };
5
+
6
+ //# sourceMappingURL=permission-feature.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/lib/constants/permission-feature.ts"],"sourcesContent":["export const STATUS = {\n ACTIVE: \"active\",\n INACTIVE: \"inactive\",\n} as const\n"],"names":["STATUS","ACTIVE","INACTIVE"],"mappings":"AAAA,OAAO,MAAMA,SAAS;IACpBC,QAAQ;IACRC,UAAU;AACZ,EAAU"}
@@ -0,0 +1,6 @@
1
+ export const STATUS = {
2
+ ACTIVE: "active",
3
+ INACTIVE: "inactive"
4
+ };
5
+
6
+ //# sourceMappingURL=permission.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/lib/constants/permission.ts"],"sourcesContent":["export const STATUS = {\n ACTIVE: \"active\",\n INACTIVE: \"inactive\",\n} as const\n"],"names":["STATUS","ACTIVE","INACTIVE"],"mappings":"AAAA,OAAO,MAAMA,SAAS;IACpBC,QAAQ;IACRC,UAAU;AACZ,EAAU"}
@@ -0,0 +1,11 @@
1
+ export const STATUS = {
2
+ ACTIVE: "active",
3
+ INACTIVE: "inactive"
4
+ };
5
+ export const DATA_SCOPE = {
6
+ ALL: "all",
7
+ OWN: "own",
8
+ HIERARCHY: "hierarchy"
9
+ };
10
+
11
+ //# sourceMappingURL=role.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/lib/constants/role.ts"],"sourcesContent":["export const STATUS = {\n ACTIVE: \"active\",\n INACTIVE: \"inactive\",\n} as const;\n\nexport const DATA_SCOPE = {\n ALL: \"all\",\n OWN: \"own\",\n HIERARCHY: \"hierarchy\",\n} as const;\n"],"names":["STATUS","ACTIVE","INACTIVE","DATA_SCOPE","ALL","OWN","HIERARCHY"],"mappings":"AAAA,OAAO,MAAMA,SAAS;IACpBC,QAAQ;IACRC,UAAU;AACZ,EAAW;AAEX,OAAO,MAAMC,aAAa;IACxBC,KAAK;IACLC,KAAK;IACLC,WAAW;AACb,EAAW"}
@@ -0,0 +1,3 @@
1
+ export const PARENT_PATH_SEPARATOR = ",";
2
+
3
+ //# sourceMappingURL=user.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/lib/constants/user.ts"],"sourcesContent":["export const PARENT_PATH_SEPARATOR = \",\" as const;"],"names":["PARENT_PATH_SEPARATOR"],"mappings":"AAAA,OAAO,MAAMA,wBAAwB,IAAa"}
@@ -0,0 +1,452 @@
1
+ import { STATUS as PERMISSION_STATUS } from "../constants/permission.js";
2
+ import { DATA_SCOPE, STATUS as ROLE_STATUS } from "../constants/role.js";
3
+ import { PARENT_PATH_SEPARATOR } from "../constants/user.js";
4
+ import { toID } from "./data.js";
5
+ const DEFAULT_CREATED_BY_FIELD = "createdBy";
6
+ const DEFAULT_USERS_COLLECTION = "users";
7
+ const SCOPE_PRIORITY = {
8
+ [DATA_SCOPE.OWN]: 0,
9
+ [DATA_SCOPE.HIERARCHY]: 1,
10
+ [DATA_SCOPE.ALL]: 2
11
+ };
12
+ /** Normalize role refs from `req.user` to an always-iterable array. */ const getRoleRefs = (user)=>user.roles ?? [];
13
+ const getRoleIds = (user)=>getRoleRefs(user).map((ref)=>toID(ref)).filter((id)=>Boolean(id));
14
+ /**
15
+ * Fast-path super-admin check from session payload (`req.user`).
16
+ * Avoids a database round-trip when the field is already present.
17
+ */ const hasInlineSuperAdmin = (user)=>{
18
+ if (!user) {
19
+ return false;
20
+ }
21
+ return Boolean(user.isSuperAdmin);
22
+ };
23
+ const pickWidestDataScope = (scopes)=>{
24
+ let widest = DATA_SCOPE.OWN;
25
+ for (const scope of scopes){
26
+ if (!scope) {
27
+ continue;
28
+ }
29
+ if (SCOPE_PRIORITY[scope] > SCOPE_PRIORITY[widest]) {
30
+ widest = scope;
31
+ }
32
+ }
33
+ return widest;
34
+ };
35
+ /**
36
+ * Fallback super-admin check against persisted users data.
37
+ * Use when session payload may be stale or missing `isSuperAdmin`.
38
+ */ const resolveSuperAdminFromUserID = async ({ req, user })=>{
39
+ if (!user.id) {
40
+ return false;
41
+ }
42
+ const userDocs = await req.payload.find({
43
+ collection: "users",
44
+ depth: 0,
45
+ limit: 1,
46
+ pagination: false,
47
+ req,
48
+ where: {
49
+ and: [
50
+ {
51
+ id: {
52
+ equals: user.id
53
+ }
54
+ },
55
+ {
56
+ isSuperAdmin: {
57
+ equals: true
58
+ }
59
+ }
60
+ ]
61
+ }
62
+ });
63
+ return userDocs.docs.length > 0;
64
+ };
65
+ /**
66
+ * Resolve RBAC permission from role assignments:
67
+ * 1) find active `permissions` by feature/action code
68
+ * 2) check an enabled `roles-permissions` row for any user role
69
+ */ const resolvePermissionFromRoleID = async ({ req, user, featureCode, actionCode })=>{
70
+ const roleIDs = getRoleIds(user);
71
+ if (!roleIDs.length) {
72
+ return false;
73
+ }
74
+ const permissions = await req.payload.find({
75
+ collection: "permissions",
76
+ depth: 0,
77
+ limit: 0,
78
+ pagination: false,
79
+ req,
80
+ where: {
81
+ and: [
82
+ {
83
+ status: {
84
+ equals: PERMISSION_STATUS.ACTIVE
85
+ }
86
+ },
87
+ {
88
+ "permissionFeature.code": {
89
+ equals: featureCode
90
+ }
91
+ },
92
+ {
93
+ "permissionAction.code": {
94
+ equals: actionCode
95
+ }
96
+ }
97
+ ]
98
+ }
99
+ });
100
+ if (!permissions.docs.length) {
101
+ return false;
102
+ }
103
+ const permissionIDs = permissions.docs.map((item)=>item.id);
104
+ const rolePermissions = await req.payload.find({
105
+ collection: "roles-permissions",
106
+ depth: 0,
107
+ limit: 1,
108
+ pagination: false,
109
+ req,
110
+ where: {
111
+ and: [
112
+ {
113
+ role: {
114
+ in: roleIDs
115
+ }
116
+ },
117
+ {
118
+ permission: {
119
+ in: permissionIDs
120
+ }
121
+ },
122
+ {
123
+ enabled: {
124
+ equals: true
125
+ }
126
+ }
127
+ ]
128
+ }
129
+ });
130
+ return rolePermissions.docs.length > 0;
131
+ };
132
+ /**
133
+ * Access helper: allow only super admins.
134
+ * Check session first, then persisted users data.
135
+ */ export const getSuperAdminAccess = async ({ req })=>{
136
+ const user = req.user;
137
+ if (!user) {
138
+ return false;
139
+ }
140
+ if (hasInlineSuperAdmin(user)) {
141
+ return true;
142
+ }
143
+ return await resolveSuperAdminFromUserID({
144
+ req,
145
+ user
146
+ });
147
+ };
148
+ /**
149
+ * Access helper: allow current document owner or super admin.
150
+ */ export const getAuthenticatedOrSuperAdminAccess = async ({ req, id })=>{
151
+ const user = req.user;
152
+ if (!user?.id) {
153
+ return false;
154
+ }
155
+ if (String(user.id) === String(id)) {
156
+ return true;
157
+ }
158
+ if (hasInlineSuperAdmin(user)) {
159
+ return true;
160
+ }
161
+ return await resolveSuperAdminFromUserID({
162
+ req,
163
+ user
164
+ });
165
+ };
166
+ /**
167
+ * Core RBAC access function (permission-only).
168
+ * Super admins bypass; others are evaluated via roles + `roles-permissions`.
169
+ */ const getBasePermissionAccess = ({ featureCode, actionCode })=>{
170
+ return async ({ req })=>{
171
+ const user = req.user;
172
+ if (!user) {
173
+ return false;
174
+ }
175
+ if (hasInlineSuperAdmin(user)) {
176
+ return true;
177
+ }
178
+ return await resolvePermissionFromRoleID({
179
+ req,
180
+ user,
181
+ featureCode,
182
+ actionCode
183
+ });
184
+ };
185
+ };
186
+ /**
187
+ * Resolve effective data scope from active roles.
188
+ * Widest scope wins: `all` > `hierarchy` > `own`.
189
+ */ export const resolveEffectiveDataScope = async (req, options = {})=>{
190
+ const user = req.user;
191
+ if (!user) {
192
+ return DATA_SCOPE.OWN;
193
+ }
194
+ if (hasInlineSuperAdmin(user)) {
195
+ return DATA_SCOPE.ALL;
196
+ }
197
+ const roleRefs = getRoleRefs(user);
198
+ const roleIDs = getRoleIds(user);
199
+ if (!roleIDs.length) {
200
+ return DATA_SCOPE.OWN;
201
+ }
202
+ const inlineScopes = roleRefs.filter((ref)=>typeof ref === "object" && ref !== null).map((ref)=>ref.dataScope).filter((scope)=>Boolean(scope));
203
+ if (inlineScopes.length === roleRefs.length) {
204
+ return pickWidestDataScope(inlineScopes);
205
+ }
206
+ const roles = await req.payload.find({
207
+ collection: "roles",
208
+ depth: 0,
209
+ limit: 0,
210
+ pagination: false,
211
+ req,
212
+ where: {
213
+ and: [
214
+ {
215
+ id: {
216
+ in: roleIDs
217
+ }
218
+ },
219
+ {
220
+ status: {
221
+ equals: ROLE_STATUS.ACTIVE
222
+ }
223
+ }
224
+ ]
225
+ }
226
+ });
227
+ return pickWidestDataScope(roles.docs.map((role)=>role.dataScope));
228
+ };
229
+ /**
230
+ * Collect visible user IDs for hierarchy scope:
231
+ * current user + direct/indirect descendants from `parent` / `parentPath`.
232
+ */ export const getHierarchyVisibleUserIds = async (req, options = {})=>{
233
+ const usersCollectionSlug = options.usersCollectionSlug ?? DEFAULT_USERS_COLLECTION;
234
+ const user = req.user;
235
+ const userId = user?.id;
236
+ if (!userId) {
237
+ return [];
238
+ }
239
+ const selfId = String(userId);
240
+ const descendants = await req.payload.find({
241
+ collection: usersCollectionSlug,
242
+ depth: 0,
243
+ limit: 0,
244
+ pagination: false,
245
+ req,
246
+ where: {
247
+ or: [
248
+ {
249
+ parent: {
250
+ equals: selfId
251
+ }
252
+ },
253
+ {
254
+ parentPath: {
255
+ equals: selfId
256
+ }
257
+ },
258
+ {
259
+ parentPath: {
260
+ contains: `${selfId}${PARENT_PATH_SEPARATOR}`
261
+ }
262
+ }
263
+ ]
264
+ }
265
+ });
266
+ const ids = new Set([
267
+ selfId
268
+ ]);
269
+ for (const doc of descendants.docs){
270
+ const id = toID(doc);
271
+ if (id) {
272
+ ids.add(id);
273
+ }
274
+ }
275
+ return [
276
+ ...ids
277
+ ];
278
+ };
279
+ /**
280
+ * Build a read `Where` filter from data scope.
281
+ * Returns `true` when no extra filtering is required (`all` scope / super admin).
282
+ */ export const getDataScopeReadWhere = async (req, options = {})=>{
283
+ const user = req.user;
284
+ const createdByField = options.createdByField ?? DEFAULT_CREATED_BY_FIELD;
285
+ if (!user?.id) {
286
+ return {
287
+ [createdByField]: {
288
+ equals: "___none___"
289
+ }
290
+ };
291
+ }
292
+ if (hasInlineSuperAdmin(user)) {
293
+ return true;
294
+ }
295
+ const scope = await resolveEffectiveDataScope(req, options);
296
+ if (scope === DATA_SCOPE.ALL) {
297
+ return true;
298
+ }
299
+ const selfId = String(user.id);
300
+ if (scope === DATA_SCOPE.OWN) {
301
+ return {
302
+ [createdByField]: {
303
+ equals: selfId
304
+ }
305
+ };
306
+ }
307
+ const visibleUserIds = await getHierarchyVisibleUserIds(req, options);
308
+ return {
309
+ [createdByField]: {
310
+ in: visibleUserIds.length ? visibleUserIds : [
311
+ selfId
312
+ ]
313
+ }
314
+ };
315
+ };
316
+ const getCreatedById = (doc, createdByField)=>{
317
+ const value = doc[createdByField];
318
+ return toID(value) || undefined;
319
+ };
320
+ const isUsersCollection = (collectionSlug, usersCollectionSlug)=>collectionSlug === usersCollectionSlug;
321
+ /**
322
+ * Guard for privileged user documents.
323
+ * Non-super-admins cannot mutate users where `isSuperAdmin === true`.
324
+ */ export const isProtectedSuperAdminUserDoc = (doc)=>Boolean(doc.isSuperAdmin);
325
+ /**
326
+ * Document-level access check:
327
+ * RBAC permission (`featureCode` + `actionCode`) + data-scope evaluation.
328
+ * Super admins bypass.
329
+ */ export const canAccessDocumentByDataScope = async ({ req, doc, featureCode, actionCode, collectionSlug, options = {} })=>{
330
+ const user = req.user;
331
+ const createdByField = options.createdByField ?? DEFAULT_CREATED_BY_FIELD;
332
+ const usersCollectionSlug = options.usersCollectionSlug ?? DEFAULT_USERS_COLLECTION;
333
+ if (!user?.id) {
334
+ return false;
335
+ }
336
+ if (hasInlineSuperAdmin(user)) {
337
+ return true;
338
+ }
339
+ const hasPermission = await resolvePermissionFromRoleID({
340
+ req,
341
+ user,
342
+ featureCode,
343
+ actionCode
344
+ });
345
+ if (!hasPermission) {
346
+ return false;
347
+ }
348
+ const scope = await resolveEffectiveDataScope(req, options);
349
+ const creatorId = getCreatedById(doc, createdByField);
350
+ if (!creatorId) {
351
+ return true;
352
+ }
353
+ if (scope === DATA_SCOPE.ALL && isUsersCollection(collectionSlug, usersCollectionSlug) && isProtectedSuperAdminUserDoc(doc)) {
354
+ return false;
355
+ }
356
+ if (scope === DATA_SCOPE.ALL) {
357
+ return true;
358
+ }
359
+ const selfId = String(user.id);
360
+ if (scope === DATA_SCOPE.OWN) {
361
+ return creatorId === selfId;
362
+ }
363
+ const visibleUserIds = await getHierarchyVisibleUserIds(req, options);
364
+ return visibleUserIds.includes(creatorId);
365
+ };
366
+ /**
367
+ * Merge an existing `where` with scope-derived constraints.
368
+ */ export const mergeDataScopeWhere = (base, scopeWhere)=>{
369
+ if (scopeWhere === true) {
370
+ return base ?? {};
371
+ }
372
+ if (!base || Object.keys(base).length === 0) {
373
+ return scopeWhere;
374
+ }
375
+ return {
376
+ and: [
377
+ base,
378
+ scopeWhere
379
+ ]
380
+ };
381
+ };
382
+ /**
383
+ * Access helper for collection `read`:
384
+ * RBAC permission check + data-scope `Where` filter.
385
+ */ const getPermissionAndDataScopeReadAccess = ({ featureCode, actionCode, options = {} })=>{
386
+ return async ({ req })=>{
387
+ const hasPermission = await getBasePermissionAccess({
388
+ featureCode,
389
+ actionCode
390
+ })({
391
+ req
392
+ });
393
+ if (!hasPermission) {
394
+ return false;
395
+ }
396
+ return getDataScopeReadWhere(req, options);
397
+ };
398
+ };
399
+ /**
400
+ * Access helper for document mutations (`update`/`delete`):
401
+ * load target document then apply RBAC + data-scope checks.
402
+ */ const getPermissionAndDataScopeModifyAccess = ({ featureCode, actionCode, collectionSlug, options = {} })=>{
403
+ return async ({ req, id })=>{
404
+ if (!id || !collectionSlug) {
405
+ return false;
406
+ }
407
+ const doc = await req.payload.findByID({
408
+ collection: collectionSlug,
409
+ id,
410
+ depth: 0,
411
+ req
412
+ });
413
+ return canAccessDocumentByDataScope({
414
+ req,
415
+ doc: doc,
416
+ featureCode,
417
+ actionCode,
418
+ collectionSlug,
419
+ options
420
+ });
421
+ };
422
+ };
423
+ /**
424
+ * Unified access entrypoint.
425
+ *
426
+ * Modes:
427
+ * - `none`: permission-only (boolean)
428
+ * - `modify`: per-document mutation check (requires `collectionSlug` + runtime `id`)
429
+ * - implicit read mode: pass `options` to get a read `Where` filter after permission check
430
+ */ export const getPermissionAccess = ({ featureCode, actionCode, mode = "none", collectionSlug, options })=>{
431
+ if (mode === "modify") {
432
+ return getPermissionAndDataScopeModifyAccess({
433
+ featureCode,
434
+ actionCode,
435
+ collectionSlug,
436
+ options
437
+ });
438
+ }
439
+ if (options && Object.keys(options).length > 0) {
440
+ return getPermissionAndDataScopeReadAccess({
441
+ featureCode,
442
+ actionCode,
443
+ options
444
+ });
445
+ }
446
+ return getBasePermissionAccess({
447
+ featureCode,
448
+ actionCode
449
+ });
450
+ };
451
+
452
+ //# sourceMappingURL=access.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/lib/utils/access.ts"],"sourcesContent":["import { Access, type PayloadRequest, type Where } from \"payload\";\nimport type { DataScope } from \"../../collections/roles/types.js\";\nimport { STATUS as PERMISSION_STATUS } from \"../constants/permission.js\";\nimport { DATA_SCOPE, STATUS as ROLE_STATUS } from \"../constants/role.js\";\nimport { PARENT_PATH_SEPARATOR } from \"../constants/user.js\";\nimport { toID } from \"./data.js\";\n\ntype UserRoleRef =\n | number\n | string\n | {\n id?: number | string;\n isSuperAdmin?: boolean | null;\n dataScope?: DataScope;\n };\n\ntype RequestUser = {\n id?: number | string;\n roles?: UserRoleRef[] | null;\n isSuperAdmin?: boolean | null;\n};\n\nexport type DataScopeOptions = {\n /** Field on business collections storing the creator user id. Default: `createdBy`. */\n createdByField?: string;\n /** Users collection slug. Default: `users`. */\n usersCollectionSlug?: string;\n};\n\nconst DEFAULT_CREATED_BY_FIELD = \"createdBy\";\nconst DEFAULT_USERS_COLLECTION = \"users\";\n\nconst SCOPE_PRIORITY: Record<DataScope, number> = {\n [DATA_SCOPE.OWN]: 0,\n [DATA_SCOPE.HIERARCHY]: 1,\n [DATA_SCOPE.ALL]: 2,\n};\n\n/** Normalize role refs from `req.user` to an always-iterable array. */\nconst getRoleRefs = (user: RequestUser): UserRoleRef[] => user.roles ?? [];\n\nconst getRoleIds = (user: RequestUser): string[] =>\n getRoleRefs(user)\n .map((ref) => toID(ref))\n .filter((id): id is string => Boolean(id));\n\n/**\n * Fast-path super-admin check from session payload (`req.user`).\n * Avoids a database round-trip when the field is already present.\n */\nconst hasInlineSuperAdmin = (user: RequestUser | null | undefined): boolean => {\n if (!user) {\n return false;\n }\n return Boolean(user.isSuperAdmin);\n};\n\nconst pickWidestDataScope = (\n scopes: Array<DataScope | null | undefined>,\n): DataScope => {\n let widest: DataScope = DATA_SCOPE.OWN;\n\n for (const scope of scopes) {\n if (!scope) {\n continue;\n }\n if (SCOPE_PRIORITY[scope] > SCOPE_PRIORITY[widest]) {\n widest = scope;\n }\n }\n\n return widest;\n};\n\n/**\n * Fallback super-admin check against persisted users data.\n * Use when session payload may be stale or missing `isSuperAdmin`.\n */\nconst resolveSuperAdminFromUserID = async ({\n req,\n user,\n}: {\n req: PayloadRequest;\n user: RequestUser;\n}): Promise<boolean> => {\n if (!user.id) {\n return false;\n }\n\n const userDocs = await req.payload.find({\n collection: \"users\",\n depth: 0,\n limit: 1,\n pagination: false,\n req,\n where: {\n and: [\n {\n id: { equals: user.id },\n },\n {\n isSuperAdmin: { equals: true },\n },\n ],\n },\n });\n\n return userDocs.docs.length > 0;\n};\n\n/**\n * Resolve RBAC permission from role assignments:\n * 1) find active `permissions` by feature/action code\n * 2) check an enabled `roles-permissions` row for any user role\n */\nconst resolvePermissionFromRoleID = async ({\n req,\n user,\n featureCode,\n actionCode,\n}: {\n req: PayloadRequest;\n user: RequestUser;\n featureCode: string;\n actionCode: string;\n}): Promise<boolean> => {\n const roleIDs = getRoleIds(user);\n if (!roleIDs.length) {\n return false;\n }\n\n const permissions = await req.payload.find({\n collection: \"permissions\",\n depth: 0,\n limit: 0,\n pagination: false,\n req,\n where: {\n and: [\n {\n status: { equals: PERMISSION_STATUS.ACTIVE },\n },\n {\n \"permissionFeature.code\": { equals: featureCode },\n },\n {\n \"permissionAction.code\": { equals: actionCode },\n },\n ],\n },\n });\n\n if (!permissions.docs.length) {\n return false;\n }\n const permissionIDs = permissions.docs.map((item) => item.id);\n const rolePermissions = await req.payload.find({\n collection: \"roles-permissions\",\n depth: 0,\n limit: 1,\n pagination: false,\n req,\n where: {\n and: [\n {\n role: { in: roleIDs },\n },\n {\n permission: { in: permissionIDs },\n },\n {\n enabled: { equals: true },\n },\n ],\n },\n });\n\n return rolePermissions.docs.length > 0;\n};\n\n/**\n * Access helper: allow only super admins.\n * Check session first, then persisted users data.\n */\nexport const getSuperAdminAccess = async ({ req }: { req: PayloadRequest }) => {\n const user = req.user as RequestUser | undefined;\n if (!user) {\n return false;\n }\n\n if (hasInlineSuperAdmin(user)) {\n return true;\n }\n\n return await resolveSuperAdminFromUserID({ req, user });\n};\n\n/**\n * Access helper: allow current document owner or super admin.\n */\nexport const getAuthenticatedOrSuperAdminAccess:\n | Access\n | Promise<boolean> = async ({ req, id }) => {\n const user = req.user as RequestUser | undefined;\n if (!user?.id) {\n return false;\n }\n\n if (String(user.id) === String(id)) {\n return true;\n }\n\n if (hasInlineSuperAdmin(user)) {\n return true;\n }\n return await resolveSuperAdminFromUserID({ req, user });\n};\n\n/**\n * Core RBAC access function (permission-only).\n * Super admins bypass; others are evaluated via roles + `roles-permissions`.\n */\nconst getBasePermissionAccess = ({\n featureCode,\n actionCode,\n}: {\n featureCode: string;\n actionCode: string;\n}) => {\n return async ({ req }: { req: PayloadRequest }) => {\n const user = req.user as RequestUser | undefined;\n if (!user) {\n return false;\n }\n\n if (hasInlineSuperAdmin(user)) {\n return true;\n }\n\n return await resolvePermissionFromRoleID({\n req,\n user,\n featureCode,\n actionCode,\n });\n };\n};\n\n/**\n * Resolve effective data scope from active roles.\n * Widest scope wins: `all` > `hierarchy` > `own`.\n */\nexport const resolveEffectiveDataScope = async (\n req: PayloadRequest,\n options: DataScopeOptions = {},\n): Promise<DataScope> => {\n const user = req.user as RequestUser | undefined;\n\n if (!user) {\n return DATA_SCOPE.OWN;\n }\n\n if (hasInlineSuperAdmin(user)) {\n return DATA_SCOPE.ALL;\n }\n\n const roleRefs = getRoleRefs(user);\n const roleIDs = getRoleIds(user);\n\n if (!roleIDs.length) {\n return DATA_SCOPE.OWN;\n }\n\n const inlineScopes = roleRefs\n .filter(\n (ref): ref is { id?: string | number; dataScope?: DataScope } =>\n typeof ref === \"object\" && ref !== null,\n )\n .map((ref) => ref.dataScope)\n .filter((scope): scope is DataScope => Boolean(scope));\n\n if (inlineScopes.length === roleRefs.length) {\n return pickWidestDataScope(inlineScopes);\n }\n\n const roles = await req.payload.find({\n collection: \"roles\",\n depth: 0,\n limit: 0,\n pagination: false,\n req,\n where: {\n and: [\n { id: { in: roleIDs } },\n { status: { equals: ROLE_STATUS.ACTIVE } },\n ],\n },\n });\n\n return pickWidestDataScope(\n roles.docs.map((role) => (role as { dataScope?: DataScope }).dataScope),\n );\n};\n\n/**\n * Collect visible user IDs for hierarchy scope:\n * current user + direct/indirect descendants from `parent` / `parentPath`.\n */\nexport const getHierarchyVisibleUserIds = async (\n req: PayloadRequest,\n options: DataScopeOptions = {},\n): Promise<string[]> => {\n const usersCollectionSlug =\n options.usersCollectionSlug ?? DEFAULT_USERS_COLLECTION;\n const user = req.user as RequestUser | undefined;\n const userId = user?.id;\n\n if (!userId) {\n return [];\n }\n\n const selfId = String(userId);\n\n const descendants = await req.payload.find({\n collection: usersCollectionSlug,\n depth: 0,\n limit: 0,\n pagination: false,\n req,\n where: {\n or: [\n { parent: { equals: selfId } },\n { parentPath: { equals: selfId } },\n { parentPath: { contains: `${selfId}${PARENT_PATH_SEPARATOR}` } },\n ],\n },\n });\n\n const ids = new Set<string>([selfId]);\n\n for (const doc of descendants.docs) {\n const id = toID(doc as UserRoleRef);\n if (id) {\n ids.add(id);\n }\n }\n\n return [...ids];\n};\n\n/**\n * Build a read `Where` filter from data scope.\n * Returns `true` when no extra filtering is required (`all` scope / super admin).\n */\nexport const getDataScopeReadWhere = async (\n req: PayloadRequest,\n options: DataScopeOptions = {},\n): Promise<Where | true> => {\n const user = req.user as RequestUser | undefined;\n const createdByField = options.createdByField ?? DEFAULT_CREATED_BY_FIELD;\n\n if (!user?.id) {\n return { [createdByField]: { equals: \"___none___\" } };\n }\n\n if (hasInlineSuperAdmin(user)) {\n return true;\n }\n\n const scope = await resolveEffectiveDataScope(req, options);\n\n if (scope === DATA_SCOPE.ALL) {\n return true;\n }\n\n const selfId = String(user.id);\n\n if (scope === DATA_SCOPE.OWN) {\n return { [createdByField]: { equals: selfId } };\n }\n\n const visibleUserIds = await getHierarchyVisibleUserIds(req, options);\n\n return {\n [createdByField]: {\n in: visibleUserIds.length ? visibleUserIds : [selfId],\n },\n };\n};\n\nconst getCreatedById = (\n doc: Record<string, unknown>,\n createdByField: string,\n): string | undefined => {\n const value = doc[createdByField];\n return toID(value as UserRoleRef) || undefined;\n};\n\nconst isUsersCollection = (\n collectionSlug: string,\n usersCollectionSlug: string,\n) => collectionSlug === usersCollectionSlug;\n\n/**\n * Guard for privileged user documents.\n * Non-super-admins cannot mutate users where `isSuperAdmin === true`.\n */\nexport const isProtectedSuperAdminUserDoc = (\n doc: Record<string, unknown>,\n): boolean => Boolean(doc.isSuperAdmin);\n\n/**\n * Document-level access check:\n * RBAC permission (`featureCode` + `actionCode`) + data-scope evaluation.\n * Super admins bypass.\n */\nexport const canAccessDocumentByDataScope = async ({\n req,\n doc,\n featureCode,\n actionCode,\n collectionSlug,\n options = {},\n}: {\n req: PayloadRequest;\n doc: Record<string, unknown>;\n featureCode: string;\n actionCode: string;\n collectionSlug: string;\n options?: DataScopeOptions;\n}): Promise<boolean> => {\n const user = req.user as RequestUser | undefined;\n const createdByField = options.createdByField ?? DEFAULT_CREATED_BY_FIELD;\n const usersCollectionSlug =\n options.usersCollectionSlug ?? DEFAULT_USERS_COLLECTION;\n\n if (!user?.id) {\n return false;\n }\n\n if (hasInlineSuperAdmin(user)) {\n return true;\n }\n\n const hasPermission = await resolvePermissionFromRoleID({\n req,\n user,\n featureCode,\n actionCode,\n });\n\n if (!hasPermission) {\n return false;\n }\n\n const scope = await resolveEffectiveDataScope(req, options);\n const creatorId = getCreatedById(doc, createdByField);\n\n if (!creatorId) {\n return true;\n }\n\n if (\n scope === DATA_SCOPE.ALL &&\n isUsersCollection(collectionSlug, usersCollectionSlug) &&\n isProtectedSuperAdminUserDoc(doc)\n ) {\n return false;\n }\n\n if (scope === DATA_SCOPE.ALL) {\n return true;\n }\n\n const selfId = String(user.id);\n\n if (scope === DATA_SCOPE.OWN) {\n return creatorId === selfId;\n }\n\n const visibleUserIds = await getHierarchyVisibleUserIds(req, options);\n return visibleUserIds.includes(creatorId);\n};\n\n/**\n * Merge an existing `where` with scope-derived constraints.\n */\nexport const mergeDataScopeWhere = (\n base: Where | undefined,\n scopeWhere: Where | true,\n): Where => {\n if (scopeWhere === true) {\n return base ?? {};\n }\n\n if (!base || Object.keys(base).length === 0) {\n return scopeWhere;\n }\n\n return {\n and: [base, scopeWhere],\n };\n};\n\n/**\n * Access helper for collection `read`:\n * RBAC permission check + data-scope `Where` filter.\n */\nconst getPermissionAndDataScopeReadAccess = ({\n featureCode,\n actionCode,\n options = {},\n}: {\n featureCode: string;\n actionCode: string;\n options?: DataScopeOptions;\n}) => {\n return async ({ req }: { req: PayloadRequest }) => {\n const hasPermission = await getBasePermissionAccess({\n featureCode,\n actionCode,\n })({\n req,\n });\n\n if (!hasPermission) {\n return false;\n }\n\n return getDataScopeReadWhere(req, options);\n };\n};\n\n/**\n * Access helper for document mutations (`update`/`delete`):\n * load target document then apply RBAC + data-scope checks.\n */\nconst getPermissionAndDataScopeModifyAccess = ({\n featureCode,\n actionCode,\n collectionSlug,\n options = {},\n}: {\n featureCode: string;\n actionCode: string;\n collectionSlug?: string;\n options?: DataScopeOptions;\n}) => {\n return async ({ req, id }: { req: PayloadRequest; id?: string | number }) => {\n if (!id || !collectionSlug) {\n return false;\n }\n\n const doc = await req.payload.findByID({\n collection: collectionSlug,\n id,\n depth: 0,\n req,\n });\n\n return canAccessDocumentByDataScope({\n req,\n doc: doc as Record<string, unknown>,\n featureCode,\n actionCode,\n collectionSlug,\n options,\n });\n };\n};\n\n/**\n * Unified access entrypoint.\n *\n * Modes:\n * - `none`: permission-only (boolean)\n * - `modify`: per-document mutation check (requires `collectionSlug` + runtime `id`)\n * - implicit read mode: pass `options` to get a read `Where` filter after permission check\n */\n\nexport const getPermissionAccess = ({\n featureCode,\n actionCode,\n mode = \"none\",\n collectionSlug,\n options,\n}: {\n featureCode: string;\n actionCode: string;\n mode?: \"none\" | \"modify\";\n collectionSlug?: string;\n options?: DataScopeOptions;\n}) => {\n if (mode === \"modify\") {\n return getPermissionAndDataScopeModifyAccess({\n featureCode,\n actionCode,\n collectionSlug,\n options,\n });\n }\n\n if (options && Object.keys(options).length > 0) {\n return getPermissionAndDataScopeReadAccess({\n featureCode,\n actionCode,\n options,\n });\n }\n return getBasePermissionAccess({ featureCode, actionCode });\n};\n"],"names":["STATUS","PERMISSION_STATUS","DATA_SCOPE","ROLE_STATUS","PARENT_PATH_SEPARATOR","toID","DEFAULT_CREATED_BY_FIELD","DEFAULT_USERS_COLLECTION","SCOPE_PRIORITY","OWN","HIERARCHY","ALL","getRoleRefs","user","roles","getRoleIds","map","ref","filter","id","Boolean","hasInlineSuperAdmin","isSuperAdmin","pickWidestDataScope","scopes","widest","scope","resolveSuperAdminFromUserID","req","userDocs","payload","find","collection","depth","limit","pagination","where","and","equals","docs","length","resolvePermissionFromRoleID","featureCode","actionCode","roleIDs","permissions","status","ACTIVE","permissionIDs","item","rolePermissions","role","in","permission","enabled","getSuperAdminAccess","getAuthenticatedOrSuperAdminAccess","String","getBasePermissionAccess","resolveEffectiveDataScope","options","roleRefs","inlineScopes","dataScope","getHierarchyVisibleUserIds","usersCollectionSlug","userId","selfId","descendants","or","parent","parentPath","contains","ids","Set","doc","add","getDataScopeReadWhere","createdByField","visibleUserIds","getCreatedById","value","undefined","isUsersCollection","collectionSlug","isProtectedSuperAdminUserDoc","canAccessDocumentByDataScope","hasPermission","creatorId","includes","mergeDataScopeWhere","base","scopeWhere","Object","keys","getPermissionAndDataScopeReadAccess","getPermissionAndDataScopeModifyAccess","findByID","getPermissionAccess","mode"],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Converts a value to an ID string.
3
+ * @param value - The value to convert.
4
+ * @returns The ID string.
5
+ */ export const toID = (value)=>value ? typeof value === "object" ? String(value.id) : String(value) : "";
6
+
7
+ //# sourceMappingURL=data.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/lib/utils/data.ts"],"sourcesContent":["/**\n * Converts a value to an ID string.\n * @param value - The value to convert.\n * @returns The ID string.\n */\nexport const toID = (value?: ItemRef) =>\n value ? (typeof value === \"object\" ? String(value.id) : String(value)) : \"\";\n"],"names":["toID","value","String","id"],"mappings":"AAAA;;;;CAIC,GACD,OAAO,MAAMA,OAAO,CAACC,QACnBA,QAAS,OAAOA,UAAU,WAAWC,OAAOD,MAAME,EAAE,IAAID,OAAOD,SAAU,GAAG"}
@@ -0,0 +1,41 @@
1
+ import { fieldAffectsData } from "payload/shared";
2
+ /**
3
+ * Merges one plugin default field with a host override that shares the same `name`.
4
+ * Custom properties win (`{ ...defaultField, ...customField }`). Non-data fields
5
+ * (e.g. tabs, unnamed layout fields) are returned unchanged.
6
+ */ export const getMergedFieldAffectingData = ({ fields, defaultField })=>{
7
+ if (!defaultField || !fieldAffectsData(defaultField)) {
8
+ return defaultField;
9
+ }
10
+ const defaultName = defaultField.name;
11
+ const customField = fields.find((field)=>fieldAffectsData(field) && field.name === defaultName);
12
+ if (customField) {
13
+ return {
14
+ ...defaultField,
15
+ ...customField
16
+ };
17
+ }
18
+ return defaultField;
19
+ };
20
+ /**
21
+ * Builds the final `fields` array for a collection: each default is merged by name,
22
+ * then host-only fields are appended (data fields with new names, plus layout fields).
23
+ */ export const getArrayOfMergedFieldAffectingData = ({ fields, defaultFields })=>{
24
+ const defaultNames = new Set(defaultFields.filter(fieldAffectsData).map((field)=>field.name));
25
+ const mergedFromDefaults = defaultFields.map((defaultField)=>getMergedFieldAffectingData({
26
+ fields,
27
+ defaultField
28
+ }));
29
+ const unMatchedFields = fields.filter((field)=>{
30
+ if (!fieldAffectsData(field)) {
31
+ return true;
32
+ }
33
+ return !defaultNames.has(field.name);
34
+ });
35
+ return [
36
+ ...mergedFromDefaults,
37
+ ...unMatchedFields
38
+ ];
39
+ };
40
+
41
+ //# sourceMappingURL=fields.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/lib/utils/fields.ts"],"sourcesContent":["import type { Field } from \"payload\";\nimport { fieldAffectsData } from \"payload/shared\";\n\n/**\n * Merges one plugin default field with a host override that shares the same `name`.\n * Custom properties win (`{ ...defaultField, ...customField }`). Non-data fields\n * (e.g. tabs, unnamed layout fields) are returned unchanged.\n */\nexport const getMergedFieldAffectingData = ({\n fields,\n defaultField,\n}: {\n fields: Field[];\n defaultField: Field;\n}): Field => {\n if (!defaultField || !fieldAffectsData(defaultField)) {\n return defaultField;\n }\n\n const defaultName = defaultField.name;\n const customField = fields.find(\n (field) => fieldAffectsData(field) && field.name === defaultName,\n );\n\n if (customField) {\n return {\n ...defaultField,\n ...customField,\n } as Field;\n }\n\n return defaultField;\n};\n\n/**\n * Builds the final `fields` array for a collection: each default is merged by name,\n * then host-only fields are appended (data fields with new names, plus layout fields).\n */\nexport const getArrayOfMergedFieldAffectingData = ({\n fields,\n defaultFields,\n}: {\n fields: Field[];\n defaultFields: Field[];\n}): Field[] => {\n const defaultNames = new Set(\n defaultFields.filter(fieldAffectsData).map((field) => field.name),\n );\n\n const mergedFromDefaults = defaultFields.map((defaultField) =>\n getMergedFieldAffectingData({ fields, defaultField }),\n );\n\n const unMatchedFields = fields.filter((field) => {\n if (!fieldAffectsData(field)) {\n return true;\n }\n return !defaultNames.has(field.name);\n });\n\n return [...mergedFromDefaults, ...unMatchedFields];\n};\n"],"names":["fieldAffectsData","getMergedFieldAffectingData","fields","defaultField","defaultName","name","customField","find","field","getArrayOfMergedFieldAffectingData","defaultFields","defaultNames","Set","filter","map","mergedFromDefaults","unMatchedFields","has"],"mappings":"AACA,SAASA,gBAAgB,QAAQ,iBAAiB;AAElD;;;;CAIC,GACD,OAAO,MAAMC,8BAA8B,CAAC,EAC1CC,MAAM,EACNC,YAAY,EAIb;IACC,IAAI,CAACA,gBAAgB,CAACH,iBAAiBG,eAAe;QACpD,OAAOA;IACT;IAEA,MAAMC,cAAcD,aAAaE,IAAI;IACrC,MAAMC,cAAcJ,OAAOK,IAAI,CAC7B,CAACC,QAAUR,iBAAiBQ,UAAUA,MAAMH,IAAI,KAAKD;IAGvD,IAAIE,aAAa;QACf,OAAO;YACL,GAAGH,YAAY;YACf,GAAGG,WAAW;QAChB;IACF;IAEA,OAAOH;AACT,EAAE;AAEF;;;CAGC,GACD,OAAO,MAAMM,qCAAqC,CAAC,EACjDP,MAAM,EACNQ,aAAa,EAId;IACC,MAAMC,eAAe,IAAIC,IACvBF,cAAcG,MAAM,CAACb,kBAAkBc,GAAG,CAAC,CAACN,QAAUA,MAAMH,IAAI;IAGlE,MAAMU,qBAAqBL,cAAcI,GAAG,CAAC,CAACX,eAC5CF,4BAA4B;YAAEC;YAAQC;QAAa;IAGrD,MAAMa,kBAAkBd,OAAOW,MAAM,CAAC,CAACL;QACrC,IAAI,CAACR,iBAAiBQ,QAAQ;YAC5B,OAAO;QACT;QACA,OAAO,CAACG,aAAaM,GAAG,CAACT,MAAMH,IAAI;IACrC;IAEA,OAAO;WAAIU;WAAuBC;KAAgB;AACpD,EAAE"}
@@ -0,0 +1,6 @@
1
+ export * from "./access.js";
2
+ export * from "./data.js";
3
+ export * from "./fields.js";
4
+ export * from "./localization.js";
5
+
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/lib/utils/index.ts"],"sourcesContent":["export * from \"./access.js\"\nexport * from \"./data.js\"\nexport * from \"./fields.js\"\nexport * from \"./localization.js\"\n"],"names":[],"mappings":"AAAA,cAAc,cAAa;AAC3B,cAAc,YAAW;AACzB,cAAc,cAAa;AAC3B,cAAc,oBAAmB"}