@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.
- package/LICENSE +21 -0
- package/README.md +175 -0
- package/dist/collections/permission-actions/default-data.js +35 -0
- package/dist/collections/permission-actions/default-data.js.map +1 -0
- package/dist/collections/permission-actions/index.js +96 -0
- package/dist/collections/permission-actions/index.js.map +1 -0
- package/dist/collections/permission-actions/types.js +3 -0
- package/dist/collections/permission-actions/types.js.map +1 -0
- package/dist/collections/permission-features/default-data.js +29 -0
- package/dist/collections/permission-features/default-data.js.map +1 -0
- package/dist/collections/permission-features/index.js +82 -0
- package/dist/collections/permission-features/index.js.map +1 -0
- package/dist/collections/permission-features/types.js +3 -0
- package/dist/collections/permission-features/types.js.map +1 -0
- package/dist/collections/permissions/default-data.js +37 -0
- package/dist/collections/permissions/default-data.js.map +1 -0
- package/dist/collections/permissions/index.js +102 -0
- package/dist/collections/permissions/index.js.map +1 -0
- package/dist/collections/permissions/types.js +3 -0
- package/dist/collections/permissions/types.js.map +1 -0
- package/dist/collections/roles/default-data.js +44 -0
- package/dist/collections/roles/default-data.js.map +1 -0
- package/dist/collections/roles/hooks/sync-permission-matrix-draft.js +66 -0
- package/dist/collections/roles/hooks/sync-permission-matrix-draft.js.map +1 -0
- package/dist/collections/roles/index.js +122 -0
- package/dist/collections/roles/index.js.map +1 -0
- package/dist/collections/roles/types.js +3 -0
- package/dist/collections/roles/types.js.map +1 -0
- package/dist/collections/roles-permissions/default-data.js +27 -0
- package/dist/collections/roles-permissions/default-data.js.map +1 -0
- package/dist/collections/roles-permissions/index.js +75 -0
- package/dist/collections/roles-permissions/index.js.map +1 -0
- package/dist/collections/roles-permissions/types.js +3 -0
- package/dist/collections/roles-permissions/types.js.map +1 -0
- package/dist/collections/users/default-data.js +19 -0
- package/dist/collections/users/default-data.js.map +1 -0
- package/dist/collections/users/index.js +135 -0
- package/dist/collections/users/index.js.map +1 -0
- package/dist/collections/users/parent-path.js +210 -0
- package/dist/collections/users/parent-path.js.map +1 -0
- package/dist/collections/users/types.js +3 -0
- package/dist/collections/users/types.js.map +1 -0
- package/dist/components/role-permission-matrix-client/default-data.js +23 -0
- package/dist/components/role-permission-matrix-client/default-data.js.map +1 -0
- package/dist/components/role-permission-matrix-client/index.js +299 -0
- package/dist/components/role-permission-matrix-client/index.js.map +1 -0
- package/dist/components/role-permission-matrix-client/types.js +3 -0
- package/dist/components/role-permission-matrix-client/types.js.map +1 -0
- package/dist/endpoints/customEndpointHandler.js +7 -0
- package/dist/endpoints/customEndpointHandler.js.map +1 -0
- package/dist/exports/client.js +3 -0
- package/dist/exports/client.js.map +1 -0
- package/dist/exports/rsc.js +2 -0
- package/dist/exports/rsc.js.map +1 -0
- package/dist/general-types.d.js +2 -0
- package/dist/general-types.d.js.map +1 -0
- package/dist/index.js +184 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/constants/general.js +3 -0
- package/dist/lib/constants/general.js.map +1 -0
- package/dist/lib/constants/index.js +16 -0
- package/dist/lib/constants/index.js.map +1 -0
- package/dist/lib/constants/permission-action.js +10 -0
- package/dist/lib/constants/permission-action.js.map +1 -0
- package/dist/lib/constants/permission-feature.js +6 -0
- package/dist/lib/constants/permission-feature.js.map +1 -0
- package/dist/lib/constants/permission.js +6 -0
- package/dist/lib/constants/permission.js.map +1 -0
- package/dist/lib/constants/role.js +11 -0
- package/dist/lib/constants/role.js.map +1 -0
- package/dist/lib/constants/user.js +3 -0
- package/dist/lib/constants/user.js.map +1 -0
- package/dist/lib/utils/access.js +452 -0
- package/dist/lib/utils/access.js.map +1 -0
- package/dist/lib/utils/data.js +7 -0
- package/dist/lib/utils/data.js.map +1 -0
- package/dist/lib/utils/fields.js +41 -0
- package/dist/lib/utils/fields.js.map +1 -0
- package/dist/lib/utils/index.js +6 -0
- package/dist/lib/utils/index.js.map +1 -0
- package/dist/lib/utils/localization.js +52 -0
- package/dist/lib/utils/localization.js.map +1 -0
- package/dist/types.js +10 -0
- package/dist/types.js.map +1 -0
- 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 @@
|
|
|
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 @@
|
|
|
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 @@
|
|
|
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 @@
|
|
|
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 @@
|
|
|
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 @@
|
|
|
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 @@
|
|
|
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"}
|