@webiny/api-core 0.0.0-unstable.e622468070 → 6.0.0-rc.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/features/security/IdentityContext/IdentityContext.js +1 -1
- package/features/security/IdentityContext/IdentityContext.js.map +1 -1
- package/features/security/permissions/createPermissions.d.ts +8 -0
- package/features/security/permissions/createPermissions.js +221 -0
- package/features/security/permissions/createPermissions.js.map +1 -0
- package/features/security/permissions/index.d.ts +2 -0
- package/features/security/permissions/index.js +3 -0
- package/features/security/permissions/index.js.map +1 -0
- package/features/security/permissions/types.d.ts +134 -0
- package/features/security/permissions/types.js +3 -0
- package/features/security/permissions/types.js.map +1 -0
- package/models/cms/FieldBuilder.d.ts +1 -1
- package/models/cms/FieldBuilder.js +5 -5
- package/models/cms/FieldBuilder.js.map +1 -1
- package/models/cms/fieldsBuilder.md +2 -2
- package/models/cms/types.d.ts +2 -2
- package/models/cms/types.js.map +1 -1
- package/package.json +19 -19
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AsyncLocalStorage } from "async_hooks";
|
|
2
|
-
import minimatch from "minimatch";
|
|
2
|
+
import { minimatch } from "minimatch";
|
|
3
3
|
import { createImplementation } from "@webiny/di";
|
|
4
4
|
import { IdentityContext as Abstraction } from "./abstractions.js";
|
|
5
5
|
import { AnonymousIdentity } from "./AnonymousIdentity.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["AsyncLocalStorage","minimatch","createImplementation","IdentityContext","Abstraction","AnonymousIdentity","AuthorizationContext","filterOutCustomWbyAppsPermissions","WcpContext","identityStorage","IdentityContextImpl","identity","constructor","authorizationContext","wcpContext","getIdentity","override","getStore","undefined","setIdentity","clearPermissionsCache","withIdentity","cb","run","withoutAuthorization","isAuthorizationEnabled","getPermission","name","permissions","listPermissions","exactMatch","find","p","patternMatch","getPermissions","filter","loadPermissions","applyAaclLogic","hasFullAccess","some","aaclEnabled","canUseAacl","teamsEnabled","canUseTeams","teams","abstraction","implementation","dependencies"],"sources":["IdentityContext.ts"],"sourcesContent":["import { AsyncLocalStorage } from \"async_hooks\";\nimport minimatch from \"minimatch\";\nimport { createImplementation } from \"@webiny/di\";\nimport { IdentityContext as Abstraction } from \"./abstractions.js\";\nimport { Identity } from \"./Identity.js\";\nimport { AnonymousIdentity } from \"./AnonymousIdentity.js\";\nimport { AuthorizationContext } from \"../authorization/AuthorizationContext/index.js\";\nimport { filterOutCustomWbyAppsPermissions } from \"../utils/filterOutCustomWbyAppsPermissions.js\";\nimport type { SecurityPermission } from \"~/types/security.js\";\nimport type { AaclPermission } from \"../../wcp/WcpContext/types.js\";\nimport { WcpContext } from \"../../wcp/WcpContext/index.js\";\n\nconst identityStorage = new AsyncLocalStorage<Identity | undefined>();\n\nclass IdentityContextImpl implements Abstraction.Interface {\n private identity: Identity = new AnonymousIdentity();\n\n constructor(\n private authorizationContext: AuthorizationContext.Interface,\n private wcpContext: WcpContext.Interface\n ) {}\n\n // ========================================================================\n // Identity Methods\n // ========================================================================\n\n getIdentity(): Identity {\n // Check for identity override first (from withIdentity())\n const override = identityStorage.getStore();\n if (override !== undefined) {\n return override;\n }\n return this.identity;\n }\n\n setIdentity(identity: Identity | undefined): void {\n // If undefined, set to anonymous identity\n this.identity = identity ?? new AnonymousIdentity();\n\n // Clear permissions cache when identity changes\n this.authorizationContext.clearPermissionsCache();\n }\n\n withIdentity<T>(identity: Identity | undefined, cb: () => Promise<T>): Promise<T> {\n return identityStorage.run(identity, cb);\n }\n\n // ========================================================================\n // Authorization State Methods\n // ========================================================================\n\n withoutAuthorization<T>(cb: () => Promise<T>): Promise<T> {\n return this.authorizationContext.withoutAuthorization(cb);\n }\n\n isAuthorizationEnabled(): boolean {\n return this.authorizationContext.isAuthorizationEnabled();\n }\n\n // ========================================================================\n // Permission Methods\n // ========================================================================\n\n async getPermission<TPermission extends SecurityPermission = SecurityPermission>(\n name: string\n ): Promise<TPermission | null> {\n if (!this.isAuthorizationEnabled()) {\n return { name: \"*\" } as TPermission;\n }\n\n const permissions = await this.listPermissions();\n\n // Try exact match first\n const exactMatch = permissions.find(p => p.name === name);\n if (exactMatch) {\n return exactMatch as TPermission;\n }\n\n // Try pattern matching\n const patternMatch = permissions.find(p => minimatch(name, p.name));\n if (patternMatch) {\n return patternMatch as TPermission;\n }\n\n return null;\n }\n\n async getPermissions<TPermission extends SecurityPermission = SecurityPermission>(\n name: string\n ): Promise<TPermission[]> {\n if (!this.isAuthorizationEnabled()) {\n return [{ name: \"*\" }] as TPermission[];\n }\n\n const permissions = await this.listPermissions();\n\n return permissions.filter(p => {\n if (p.name === name) {\n return true;\n }\n return minimatch(name, p.name);\n }) as TPermission[];\n }\n\n async listPermissions(): Promise<SecurityPermission[]> {\n const permissions = await this.authorizationContext.loadPermissions(this.getIdentity());\n // TODO: extract this into a decorator\n return this.applyAaclLogic(permissions);\n }\n\n async hasFullAccess(): Promise<boolean> {\n const permissions = await this.listPermissions();\n return permissions.some(p => p.name === \"*\");\n }\n\n // ========================================================================\n // Private Helper Methods\n // ========================================================================\n\n private applyAaclLogic(permissions: SecurityPermission[]): SecurityPermission[] {\n const aaclEnabled = this.wcpContext.canUseAacl();\n const teamsEnabled = this.wcpContext.canUseTeams();\n\n if (aaclEnabled) {\n // Add AACL metadata permission\n return [\n ...permissions,\n {\n name: \"aacl\",\n teams: teamsEnabled\n } as AaclPermission\n ];\n }\n\n // If AACL is not enabled, filter out custom Webiny apps permissions\n return filterOutCustomWbyAppsPermissions(permissions);\n }\n}\n\nexport const IdentityContext = createImplementation({\n abstraction: Abstraction,\n implementation: IdentityContextImpl,\n dependencies: [AuthorizationContext, WcpContext]\n});\n"],"mappings":"AAAA,SAASA,iBAAiB,QAAQ,aAAa;AAC/C,
|
|
1
|
+
{"version":3,"names":["AsyncLocalStorage","minimatch","createImplementation","IdentityContext","Abstraction","AnonymousIdentity","AuthorizationContext","filterOutCustomWbyAppsPermissions","WcpContext","identityStorage","IdentityContextImpl","identity","constructor","authorizationContext","wcpContext","getIdentity","override","getStore","undefined","setIdentity","clearPermissionsCache","withIdentity","cb","run","withoutAuthorization","isAuthorizationEnabled","getPermission","name","permissions","listPermissions","exactMatch","find","p","patternMatch","getPermissions","filter","loadPermissions","applyAaclLogic","hasFullAccess","some","aaclEnabled","canUseAacl","teamsEnabled","canUseTeams","teams","abstraction","implementation","dependencies"],"sources":["IdentityContext.ts"],"sourcesContent":["import { AsyncLocalStorage } from \"async_hooks\";\nimport { minimatch } from \"minimatch\";\nimport { createImplementation } from \"@webiny/di\";\nimport { IdentityContext as Abstraction } from \"./abstractions.js\";\nimport { Identity } from \"./Identity.js\";\nimport { AnonymousIdentity } from \"./AnonymousIdentity.js\";\nimport { AuthorizationContext } from \"../authorization/AuthorizationContext/index.js\";\nimport { filterOutCustomWbyAppsPermissions } from \"../utils/filterOutCustomWbyAppsPermissions.js\";\nimport type { SecurityPermission } from \"~/types/security.js\";\nimport type { AaclPermission } from \"../../wcp/WcpContext/types.js\";\nimport { WcpContext } from \"../../wcp/WcpContext/index.js\";\n\nconst identityStorage = new AsyncLocalStorage<Identity | undefined>();\n\nclass IdentityContextImpl implements Abstraction.Interface {\n private identity: Identity = new AnonymousIdentity();\n\n constructor(\n private authorizationContext: AuthorizationContext.Interface,\n private wcpContext: WcpContext.Interface\n ) {}\n\n // ========================================================================\n // Identity Methods\n // ========================================================================\n\n getIdentity(): Identity {\n // Check for identity override first (from withIdentity())\n const override = identityStorage.getStore();\n if (override !== undefined) {\n return override;\n }\n return this.identity;\n }\n\n setIdentity(identity: Identity | undefined): void {\n // If undefined, set to anonymous identity\n this.identity = identity ?? new AnonymousIdentity();\n\n // Clear permissions cache when identity changes\n this.authorizationContext.clearPermissionsCache();\n }\n\n withIdentity<T>(identity: Identity | undefined, cb: () => Promise<T>): Promise<T> {\n return identityStorage.run(identity, cb);\n }\n\n // ========================================================================\n // Authorization State Methods\n // ========================================================================\n\n withoutAuthorization<T>(cb: () => Promise<T>): Promise<T> {\n return this.authorizationContext.withoutAuthorization(cb);\n }\n\n isAuthorizationEnabled(): boolean {\n return this.authorizationContext.isAuthorizationEnabled();\n }\n\n // ========================================================================\n // Permission Methods\n // ========================================================================\n\n async getPermission<TPermission extends SecurityPermission = SecurityPermission>(\n name: string\n ): Promise<TPermission | null> {\n if (!this.isAuthorizationEnabled()) {\n return { name: \"*\" } as TPermission;\n }\n\n const permissions = await this.listPermissions();\n\n // Try exact match first\n const exactMatch = permissions.find(p => p.name === name);\n if (exactMatch) {\n return exactMatch as TPermission;\n }\n\n // Try pattern matching\n const patternMatch = permissions.find(p => minimatch(name, p.name));\n if (patternMatch) {\n return patternMatch as TPermission;\n }\n\n return null;\n }\n\n async getPermissions<TPermission extends SecurityPermission = SecurityPermission>(\n name: string\n ): Promise<TPermission[]> {\n if (!this.isAuthorizationEnabled()) {\n return [{ name: \"*\" }] as TPermission[];\n }\n\n const permissions = await this.listPermissions();\n\n return permissions.filter(p => {\n if (p.name === name) {\n return true;\n }\n return minimatch(name, p.name);\n }) as TPermission[];\n }\n\n async listPermissions(): Promise<SecurityPermission[]> {\n const permissions = await this.authorizationContext.loadPermissions(this.getIdentity());\n // TODO: extract this into a decorator\n return this.applyAaclLogic(permissions);\n }\n\n async hasFullAccess(): Promise<boolean> {\n const permissions = await this.listPermissions();\n return permissions.some(p => p.name === \"*\");\n }\n\n // ========================================================================\n // Private Helper Methods\n // ========================================================================\n\n private applyAaclLogic(permissions: SecurityPermission[]): SecurityPermission[] {\n const aaclEnabled = this.wcpContext.canUseAacl();\n const teamsEnabled = this.wcpContext.canUseTeams();\n\n if (aaclEnabled) {\n // Add AACL metadata permission\n return [\n ...permissions,\n {\n name: \"aacl\",\n teams: teamsEnabled\n } as AaclPermission\n ];\n }\n\n // If AACL is not enabled, filter out custom Webiny apps permissions\n return filterOutCustomWbyAppsPermissions(permissions);\n }\n}\n\nexport const IdentityContext = createImplementation({\n abstraction: Abstraction,\n implementation: IdentityContextImpl,\n dependencies: [AuthorizationContext, WcpContext]\n});\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { PermissionSchemaConfig } from "./types.js";
|
|
2
|
+
import type { Permissions } from "./types.js";
|
|
3
|
+
export declare function createPermissions<const S extends PermissionSchemaConfig>(schema: S): {
|
|
4
|
+
Abstraction: import("@webiny/di").Abstraction<Permissions<S>>;
|
|
5
|
+
Implementation: import("@webiny/di").Constructor<Permissions<S>> & {
|
|
6
|
+
__abstraction: import("@webiny/di").Abstraction<Permissions<S>>;
|
|
7
|
+
};
|
|
8
|
+
};
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { createAbstraction } from "@webiny/feature/api";
|
|
2
|
+
import { IdentityContext } from "../IdentityContext/index.js";
|
|
3
|
+
function buildEntityMap(schema) {
|
|
4
|
+
const map = new Map();
|
|
5
|
+
for (const entity of schema.entities ?? []) {
|
|
6
|
+
const actions = new Set();
|
|
7
|
+
for (const action of entity.actions ?? []) {
|
|
8
|
+
actions.add(action.name);
|
|
9
|
+
}
|
|
10
|
+
map.set(entity.id, {
|
|
11
|
+
permission: entity.permission,
|
|
12
|
+
actions,
|
|
13
|
+
hasOwn: entity.scopes.includes("own")
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
return map;
|
|
17
|
+
}
|
|
18
|
+
function getEntity(entityMap, entityId) {
|
|
19
|
+
const entity = entityMap.get(entityId);
|
|
20
|
+
if (!entity) {
|
|
21
|
+
throw new Error(`Unknown entity "${entityId}" in permission schema.`);
|
|
22
|
+
}
|
|
23
|
+
return entity;
|
|
24
|
+
}
|
|
25
|
+
export function createPermissions(schema) {
|
|
26
|
+
const entityMap = buildEntityMap(schema);
|
|
27
|
+
class SchemaPermissions {
|
|
28
|
+
constructor(identityContext) {
|
|
29
|
+
this.identityContext = identityContext;
|
|
30
|
+
}
|
|
31
|
+
async canAccess(entityId, item) {
|
|
32
|
+
if (await this.identityContext.hasFullAccess()) {
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
if (await this.hasFullSchemaAccess()) {
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
const entity = getEntity(entityMap, entityId);
|
|
39
|
+
const permissions = await this.identityContext.getPermissions(entity.permission);
|
|
40
|
+
if (!permissions.length) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
// No item — just check entity-level access.
|
|
44
|
+
if (item === undefined) {
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
// Item provided — if all permissions require own, verify ownership.
|
|
48
|
+
const ownOnly = !permissions.some(p => !p.own);
|
|
49
|
+
if (!ownOnly) {
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
const identity = this.identityContext.getIdentity();
|
|
53
|
+
return item.createdBy?.id === identity.id;
|
|
54
|
+
}
|
|
55
|
+
async onlyOwnRecords(entityId) {
|
|
56
|
+
if (await this.identityContext.hasFullAccess()) {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
if (await this.hasFullSchemaAccess()) {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
const entity = getEntity(entityMap, entityId);
|
|
63
|
+
const permissions = await this.identityContext.getPermissions(entity.permission);
|
|
64
|
+
if (!permissions.length) {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
return !permissions.some(p => !p.own);
|
|
68
|
+
}
|
|
69
|
+
async canRead(entityId) {
|
|
70
|
+
if (await this.identityContext.hasFullAccess()) {
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
if (await this.hasFullSchemaAccess()) {
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
const entity = getEntity(entityMap, entityId);
|
|
77
|
+
const permissions = await this.identityContext.getPermissions(entity.permission);
|
|
78
|
+
if (!permissions.length) {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
return permissions.some(permission => {
|
|
82
|
+
if (typeof permission.rwd !== "string") {
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
return permission.rwd.includes("r");
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
async canCreate(entityId) {
|
|
89
|
+
if (await this.identityContext.hasFullAccess()) {
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
if (await this.hasFullSchemaAccess()) {
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
const entity = getEntity(entityMap, entityId);
|
|
96
|
+
const permissions = await this.identityContext.getPermissions(entity.permission);
|
|
97
|
+
if (!permissions.length) {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
return permissions.some(permission => {
|
|
101
|
+
if (typeof permission.rwd !== "string") {
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
104
|
+
return permission.rwd.includes("w");
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
async canEdit(entityId, item) {
|
|
108
|
+
if (await this.identityContext.hasFullAccess()) {
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
if (await this.hasFullSchemaAccess()) {
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
const entity = getEntity(entityMap, entityId);
|
|
115
|
+
const permissions = await this.identityContext.getPermissions(entity.permission);
|
|
116
|
+
if (!permissions.length) {
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
const identity = this.identityContext.getIdentity();
|
|
120
|
+
return permissions.some(permission => {
|
|
121
|
+
if (permission.own) {
|
|
122
|
+
// No item provided (new/unsaved) — allow access.
|
|
123
|
+
if (!item?.createdBy) {
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
return item.createdBy.id === identity.id;
|
|
127
|
+
}
|
|
128
|
+
if (typeof permission.rwd !== "string") {
|
|
129
|
+
return true;
|
|
130
|
+
}
|
|
131
|
+
return permission.rwd.includes("w");
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
async canDelete(entityId, item) {
|
|
135
|
+
if (await this.identityContext.hasFullAccess()) {
|
|
136
|
+
return true;
|
|
137
|
+
}
|
|
138
|
+
if (await this.hasFullSchemaAccess()) {
|
|
139
|
+
return true;
|
|
140
|
+
}
|
|
141
|
+
const entity = getEntity(entityMap, entityId);
|
|
142
|
+
const permissions = await this.identityContext.getPermissions(entity.permission);
|
|
143
|
+
if (!permissions.length) {
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
const identity = this.identityContext.getIdentity();
|
|
147
|
+
return permissions.some(permission => {
|
|
148
|
+
if (permission.own) {
|
|
149
|
+
return item?.createdBy?.id === identity.id;
|
|
150
|
+
}
|
|
151
|
+
if (typeof permission.rwd !== "string") {
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
return permission.rwd.includes("d");
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
async canPublish(entityId) {
|
|
158
|
+
if (await this.identityContext.hasFullAccess()) {
|
|
159
|
+
return true;
|
|
160
|
+
}
|
|
161
|
+
if (await this.hasFullSchemaAccess()) {
|
|
162
|
+
return true;
|
|
163
|
+
}
|
|
164
|
+
const entity = getEntity(entityMap, entityId);
|
|
165
|
+
const permissions = await this.identityContext.getPermissions(entity.permission);
|
|
166
|
+
if (!permissions.length) {
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
return permissions.some(permission => {
|
|
170
|
+
return permission.pw?.includes("p");
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
async canUnpublish(entityId) {
|
|
174
|
+
if (await this.identityContext.hasFullAccess()) {
|
|
175
|
+
return true;
|
|
176
|
+
}
|
|
177
|
+
if (await this.hasFullSchemaAccess()) {
|
|
178
|
+
return true;
|
|
179
|
+
}
|
|
180
|
+
const entity = getEntity(entityMap, entityId);
|
|
181
|
+
const permissions = await this.identityContext.getPermissions(entity.permission);
|
|
182
|
+
if (!permissions.length) {
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
return permissions.some(permission => {
|
|
186
|
+
return permission.pw?.includes("u");
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
async canAction(action, entityId) {
|
|
190
|
+
if (await this.identityContext.hasFullAccess()) {
|
|
191
|
+
return true;
|
|
192
|
+
}
|
|
193
|
+
if (await this.hasFullSchemaAccess()) {
|
|
194
|
+
return true;
|
|
195
|
+
}
|
|
196
|
+
const entity = getEntity(entityMap, entityId);
|
|
197
|
+
const permissions = await this.identityContext.getPermissions(entity.permission);
|
|
198
|
+
if (!permissions.length) {
|
|
199
|
+
return false;
|
|
200
|
+
}
|
|
201
|
+
return permissions.some(permission => {
|
|
202
|
+
return permission[action] === true;
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
async hasFullSchemaAccess() {
|
|
206
|
+
const permission = await this.identityContext.getPermission(schema.fullAccess.name);
|
|
207
|
+
return permission !== null;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
const Abstraction = createAbstraction(`${schema.prefix}:Permissions`);
|
|
211
|
+
const Implementation = Abstraction.createImplementation({
|
|
212
|
+
implementation: SchemaPermissions,
|
|
213
|
+
dependencies: [IdentityContext]
|
|
214
|
+
});
|
|
215
|
+
return {
|
|
216
|
+
Abstraction,
|
|
217
|
+
Implementation
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
//# sourceMappingURL=createPermissions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["createAbstraction","IdentityContext","buildEntityMap","schema","map","Map","entity","entities","actions","Set","action","add","name","set","id","permission","hasOwn","scopes","includes","getEntity","entityMap","entityId","get","Error","createPermissions","SchemaPermissions","constructor","identityContext","canAccess","item","hasFullAccess","hasFullSchemaAccess","permissions","getPermissions","length","undefined","ownOnly","some","p","own","identity","getIdentity","createdBy","onlyOwnRecords","canRead","rwd","canCreate","canEdit","canDelete","canPublish","pw","canUnpublish","canAction","getPermission","fullAccess","Abstraction","prefix","Implementation","createImplementation","implementation","dependencies"],"sources":["createPermissions.ts"],"sourcesContent":["import { createAbstraction } from \"@webiny/feature/api\";\nimport { IdentityContext } from \"~/features/security/IdentityContext/index.js\";\nimport type { PermissionSchemaConfig } from \"./types.js\";\nimport type { OwnableItem } from \"./types.js\";\nimport type { Permissions } from \"./types.js\";\n\ninterface EntityLookup {\n permission: string;\n actions: Set<string>;\n hasOwn: boolean;\n}\n\nfunction buildEntityMap(schema: PermissionSchemaConfig): Map<string, EntityLookup> {\n const map = new Map<string, EntityLookup>();\n for (const entity of schema.entities ?? []) {\n const actions = new Set<string>();\n for (const action of entity.actions ?? []) {\n actions.add(action.name);\n }\n map.set(entity.id, {\n permission: entity.permission,\n actions,\n hasOwn: entity.scopes.includes(\"own\")\n });\n }\n return map;\n}\n\nfunction getEntity(entityMap: Map<string, EntityLookup>, entityId: string): EntityLookup {\n const entity = entityMap.get(entityId);\n if (!entity) {\n throw new Error(`Unknown entity \"${entityId}\" in permission schema.`);\n }\n return entity;\n}\n\nexport function createPermissions<const S extends PermissionSchemaConfig>(schema: S) {\n const entityMap = buildEntityMap(schema);\n\n class SchemaPermissions {\n constructor(private identityContext: IdentityContext.Interface) {}\n\n async canAccess(entityId: string, item?: OwnableItem): Promise<boolean> {\n if (await this.identityContext.hasFullAccess()) {\n return true;\n }\n if (await this.hasFullSchemaAccess()) {\n return true;\n }\n const entity = getEntity(entityMap, entityId);\n const permissions = await this.identityContext.getPermissions(entity.permission);\n if (!permissions.length) {\n return false;\n }\n // No item — just check entity-level access.\n if (item === undefined) {\n return true;\n }\n // Item provided — if all permissions require own, verify ownership.\n const ownOnly = !permissions.some(p => !p.own);\n if (!ownOnly) {\n return true;\n }\n const identity = this.identityContext.getIdentity();\n return item.createdBy?.id === identity.id;\n }\n\n async onlyOwnRecords(entityId: string): Promise<boolean> {\n if (await this.identityContext.hasFullAccess()) {\n return false;\n }\n if (await this.hasFullSchemaAccess()) {\n return false;\n }\n const entity = getEntity(entityMap, entityId);\n const permissions = await this.identityContext.getPermissions(entity.permission);\n if (!permissions.length) {\n return false;\n }\n return !permissions.some(p => !p.own);\n }\n\n async canRead(entityId: string): Promise<boolean> {\n if (await this.identityContext.hasFullAccess()) {\n return true;\n }\n if (await this.hasFullSchemaAccess()) {\n return true;\n }\n const entity = getEntity(entityMap, entityId);\n const permissions = await this.identityContext.getPermissions(entity.permission);\n if (!permissions.length) {\n return false;\n }\n return permissions.some(permission => {\n if (typeof permission.rwd !== \"string\") {\n return true;\n }\n return permission.rwd.includes(\"r\");\n });\n }\n\n async canCreate(entityId: string): Promise<boolean> {\n if (await this.identityContext.hasFullAccess()) {\n return true;\n }\n if (await this.hasFullSchemaAccess()) {\n return true;\n }\n const entity = getEntity(entityMap, entityId);\n const permissions = await this.identityContext.getPermissions(entity.permission);\n if (!permissions.length) {\n return false;\n }\n return permissions.some(permission => {\n if (typeof permission.rwd !== \"string\") {\n return true;\n }\n return permission.rwd.includes(\"w\");\n });\n }\n\n async canEdit(entityId: string, item?: OwnableItem): Promise<boolean> {\n if (await this.identityContext.hasFullAccess()) {\n return true;\n }\n if (await this.hasFullSchemaAccess()) {\n return true;\n }\n const entity = getEntity(entityMap, entityId);\n const permissions = await this.identityContext.getPermissions(entity.permission);\n if (!permissions.length) {\n return false;\n }\n const identity = this.identityContext.getIdentity();\n return permissions.some(permission => {\n if (permission.own) {\n // No item provided (new/unsaved) — allow access.\n if (!item?.createdBy) {\n return true;\n }\n return item.createdBy.id === identity.id;\n }\n if (typeof permission.rwd !== \"string\") {\n return true;\n }\n return permission.rwd.includes(\"w\");\n });\n }\n\n async canDelete(entityId: string, item?: OwnableItem): Promise<boolean> {\n if (await this.identityContext.hasFullAccess()) {\n return true;\n }\n if (await this.hasFullSchemaAccess()) {\n return true;\n }\n const entity = getEntity(entityMap, entityId);\n const permissions = await this.identityContext.getPermissions(entity.permission);\n if (!permissions.length) {\n return false;\n }\n const identity = this.identityContext.getIdentity();\n return permissions.some(permission => {\n if (permission.own) {\n return item?.createdBy?.id === identity.id;\n }\n if (typeof permission.rwd !== \"string\") {\n return true;\n }\n return permission.rwd.includes(\"d\");\n });\n }\n\n async canPublish(entityId: string): Promise<boolean> {\n if (await this.identityContext.hasFullAccess()) {\n return true;\n }\n if (await this.hasFullSchemaAccess()) {\n return true;\n }\n const entity = getEntity(entityMap, entityId);\n const permissions = await this.identityContext.getPermissions(entity.permission);\n if (!permissions.length) {\n return false;\n }\n return permissions.some(permission => {\n return permission.pw?.includes(\"p\");\n });\n }\n\n async canUnpublish(entityId: string): Promise<boolean> {\n if (await this.identityContext.hasFullAccess()) {\n return true;\n }\n if (await this.hasFullSchemaAccess()) {\n return true;\n }\n const entity = getEntity(entityMap, entityId);\n const permissions = await this.identityContext.getPermissions(entity.permission);\n if (!permissions.length) {\n return false;\n }\n return permissions.some(permission => {\n return permission.pw?.includes(\"u\");\n });\n }\n\n async canAction(action: string, entityId: string): Promise<boolean> {\n if (await this.identityContext.hasFullAccess()) {\n return true;\n }\n if (await this.hasFullSchemaAccess()) {\n return true;\n }\n const entity = getEntity(entityMap, entityId);\n const permissions = await this.identityContext.getPermissions(entity.permission);\n if (!permissions.length) {\n return false;\n }\n return permissions.some(permission => {\n return permission[action] === true;\n });\n }\n\n private async hasFullSchemaAccess(): Promise<boolean> {\n const permission = await this.identityContext.getPermission(schema.fullAccess.name);\n return permission !== null;\n }\n }\n\n const Abstraction = createAbstraction<Permissions<S>>(`${schema.prefix}:Permissions`);\n\n const Implementation = Abstraction.createImplementation({\n implementation: SchemaPermissions,\n dependencies: [IdentityContext]\n } as any);\n\n return { Abstraction, Implementation };\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["createPermissions"],"sources":["index.ts"],"sourcesContent":["export { createPermissions } from \"./createPermissions.js\";\nexport type {\n ActionDefinition,\n EntityDefinition,\n PermissionSchemaConfig,\n OwnableItem,\n Permissions,\n AllEntityIds,\n RwdEntityId,\n PwEntityId,\n CustomActionNames\n} from \"./types.js\";\n"],"mappings":"AAAA,SAASA,iBAAiB","ignoreList":[]}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* An action definition on an entity.
|
|
3
|
+
*
|
|
4
|
+
* Built-in actions:
|
|
5
|
+
* - `{ name: "rwd" }` — read/write/delete (serialized as joined string, e.g. "rw")
|
|
6
|
+
* - `{ name: "pw" }` — publish/unpublish (serialized as joined string, e.g. "pu")
|
|
7
|
+
*
|
|
8
|
+
* Custom actions:
|
|
9
|
+
* - `{ name: "install" }` — boolean flag (serialized as `install: true`)
|
|
10
|
+
*/
|
|
11
|
+
export interface ActionDefinition {
|
|
12
|
+
/** Key on the permission object (e.g. "rwd", "pw", "install"). */
|
|
13
|
+
name: string;
|
|
14
|
+
/** Display label for the UI. Required for custom actions; ignored for built-in "rwd"/"pw". */
|
|
15
|
+
label?: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Defines an entity within a permission schema.
|
|
19
|
+
*/
|
|
20
|
+
export interface EntityDefinition {
|
|
21
|
+
/** Unique ID, used for form field naming: ${id}AccessScope, ${id}RWD, etc. */
|
|
22
|
+
id: string;
|
|
23
|
+
/** Display title for the UI renderer (e.g. "Files", "Settings"). */
|
|
24
|
+
title?: string;
|
|
25
|
+
/** Permission name emitted for this entity (e.g. "fm.file"). */
|
|
26
|
+
permission: string;
|
|
27
|
+
/** Available access scopes. */
|
|
28
|
+
scopes: readonly ("full" | "own")[];
|
|
29
|
+
/** Action definitions for this entity. */
|
|
30
|
+
actions?: readonly ActionDefinition[];
|
|
31
|
+
/** Dependency on another entity. */
|
|
32
|
+
dependsOn?: {
|
|
33
|
+
/** ID of parent entity. */
|
|
34
|
+
entity: string;
|
|
35
|
+
/** Required action character (e.g. "r"). */
|
|
36
|
+
requires: string;
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Configuration for creating a permission schema.
|
|
41
|
+
*/
|
|
42
|
+
export interface PermissionSchemaConfig {
|
|
43
|
+
/** Permission prefix — used to filter permissions from the array. */
|
|
44
|
+
prefix: string;
|
|
45
|
+
/** Permission object emitted on "full access". All properties are spread onto the permission. */
|
|
46
|
+
fullAccess: {
|
|
47
|
+
name: string;
|
|
48
|
+
[key: string]: any;
|
|
49
|
+
};
|
|
50
|
+
/** Entity definitions (optional — simple apps have none). */
|
|
51
|
+
entities?: readonly EntityDefinition[];
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Item that may have an owner (used for own-scope permission checks).
|
|
55
|
+
*/
|
|
56
|
+
export interface OwnableItem {
|
|
57
|
+
createdBy?: {
|
|
58
|
+
id: string;
|
|
59
|
+
} | null;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Extract the union of entity definitions from a schema config type.
|
|
63
|
+
*/
|
|
64
|
+
type EntitiesOf<S extends PermissionSchemaConfig> = S extends {
|
|
65
|
+
entities: ReadonlyArray<infer E extends EntityDefinition>;
|
|
66
|
+
} ? E : never;
|
|
67
|
+
/**
|
|
68
|
+
* Extract entity IDs whose actions array contains an action with the given name.
|
|
69
|
+
*/
|
|
70
|
+
type EntityIdWithAction<S extends PermissionSchemaConfig, A extends string> = {
|
|
71
|
+
[K in EntitiesOf<S> as K extends {
|
|
72
|
+
actions: ReadonlyArray<infer Act>;
|
|
73
|
+
} ? Act extends {
|
|
74
|
+
name: A;
|
|
75
|
+
} ? K["id"] : never : never]: never;
|
|
76
|
+
} extends infer M ? keyof M & string : never;
|
|
77
|
+
/**
|
|
78
|
+
* All entity IDs in the schema.
|
|
79
|
+
*/
|
|
80
|
+
export type AllEntityIds<S extends PermissionSchemaConfig> = EntitiesOf<S>["id"];
|
|
81
|
+
/**
|
|
82
|
+
* Entity IDs that have the "rwd" action.
|
|
83
|
+
*/
|
|
84
|
+
export type RwdEntityId<S extends PermissionSchemaConfig> = EntityIdWithAction<S, "rwd">;
|
|
85
|
+
/**
|
|
86
|
+
* Entity IDs that have the "pw" action.
|
|
87
|
+
*/
|
|
88
|
+
export type PwEntityId<S extends PermissionSchemaConfig> = EntityIdWithAction<S, "pw">;
|
|
89
|
+
/**
|
|
90
|
+
* Custom (non-builtin) action names across all entities.
|
|
91
|
+
*/
|
|
92
|
+
export type CustomActionNames<S extends PermissionSchemaConfig> = Exclude<EntitiesOf<S> extends {
|
|
93
|
+
actions: ReadonlyArray<infer Act extends ActionDefinition>;
|
|
94
|
+
} ? Act["name"] : never, "rwd" | "pw">;
|
|
95
|
+
/**
|
|
96
|
+
* Typed permissions interface for a given schema.
|
|
97
|
+
*
|
|
98
|
+
* When the schema has literal entity types, methods are narrowed to only accept valid entity IDs.
|
|
99
|
+
* When the schema is dynamically typed, all methods accept `string`.
|
|
100
|
+
*/
|
|
101
|
+
export type Permissions<S extends PermissionSchemaConfig> = string extends AllEntityIds<S> ? PermissionsUntyped : PermissionsTyped<S>;
|
|
102
|
+
export interface PermissionsUntyped {
|
|
103
|
+
canAccess(entityId: string, item?: OwnableItem): Promise<boolean>;
|
|
104
|
+
onlyOwnRecords(entityId: string): Promise<boolean>;
|
|
105
|
+
canRead(entityId: string): Promise<boolean>;
|
|
106
|
+
canCreate(entityId: string): Promise<boolean>;
|
|
107
|
+
canEdit(entityId: string, item?: OwnableItem): Promise<boolean>;
|
|
108
|
+
canDelete(entityId: string, item?: OwnableItem): Promise<boolean>;
|
|
109
|
+
canPublish(entityId: string): Promise<boolean>;
|
|
110
|
+
canUnpublish(entityId: string): Promise<boolean>;
|
|
111
|
+
canAction(action: string, entityId: string): Promise<boolean>;
|
|
112
|
+
}
|
|
113
|
+
export type PermissionsTyped<S extends PermissionSchemaConfig> = {
|
|
114
|
+
canAccess(entityId: AllEntityIds<S>, item?: OwnableItem): Promise<boolean>;
|
|
115
|
+
onlyOwnRecords(entityId: AllEntityIds<S>): Promise<boolean>;
|
|
116
|
+
canAction(action: CustomActionNames<S> extends never ? string : CustomActionNames<S>, entityId: AllEntityIds<S>): Promise<boolean>;
|
|
117
|
+
} & ([RwdEntityId<S>] extends [never] ? {
|
|
118
|
+
canRead(entityId: string): Promise<boolean>;
|
|
119
|
+
canCreate(entityId: string): Promise<boolean>;
|
|
120
|
+
canEdit(entityId: string, item?: OwnableItem): Promise<boolean>;
|
|
121
|
+
canDelete(entityId: string, item?: OwnableItem): Promise<boolean>;
|
|
122
|
+
} : {
|
|
123
|
+
canRead(entityId: RwdEntityId<S>): Promise<boolean>;
|
|
124
|
+
canCreate(entityId: RwdEntityId<S>): Promise<boolean>;
|
|
125
|
+
canEdit(entityId: RwdEntityId<S>, item?: OwnableItem): Promise<boolean>;
|
|
126
|
+
canDelete(entityId: RwdEntityId<S>, item?: OwnableItem): Promise<boolean>;
|
|
127
|
+
}) & ([PwEntityId<S>] extends [never] ? {
|
|
128
|
+
canPublish(entityId: string): Promise<boolean>;
|
|
129
|
+
canUnpublish(entityId: string): Promise<boolean>;
|
|
130
|
+
} : {
|
|
131
|
+
canPublish(entityId: PwEntityId<S>): Promise<boolean>;
|
|
132
|
+
canUnpublish(entityId: PwEntityId<S>): Promise<boolean>;
|
|
133
|
+
});
|
|
134
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":[],"sources":["types.ts"],"sourcesContent":["/**\n * An action definition on an entity.\n *\n * Built-in actions:\n * - `{ name: \"rwd\" }` — read/write/delete (serialized as joined string, e.g. \"rw\")\n * - `{ name: \"pw\" }` — publish/unpublish (serialized as joined string, e.g. \"pu\")\n *\n * Custom actions:\n * - `{ name: \"install\" }` — boolean flag (serialized as `install: true`)\n */\nexport interface ActionDefinition {\n /** Key on the permission object (e.g. \"rwd\", \"pw\", \"install\"). */\n name: string;\n /** Display label for the UI. Required for custom actions; ignored for built-in \"rwd\"/\"pw\". */\n label?: string;\n}\n\n/**\n * Defines an entity within a permission schema.\n */\nexport interface EntityDefinition {\n /** Unique ID, used for form field naming: ${id}AccessScope, ${id}RWD, etc. */\n id: string;\n /** Display title for the UI renderer (e.g. \"Files\", \"Settings\"). */\n title?: string;\n /** Permission name emitted for this entity (e.g. \"fm.file\"). */\n permission: string;\n /** Available access scopes. */\n scopes: readonly (\"full\" | \"own\")[];\n /** Action definitions for this entity. */\n actions?: readonly ActionDefinition[];\n /** Dependency on another entity. */\n dependsOn?: {\n /** ID of parent entity. */\n entity: string;\n /** Required action character (e.g. \"r\"). */\n requires: string;\n };\n}\n\n/**\n * Configuration for creating a permission schema.\n */\nexport interface PermissionSchemaConfig {\n /** Permission prefix — used to filter permissions from the array. */\n prefix: string;\n /** Permission object emitted on \"full access\". All properties are spread onto the permission. */\n fullAccess: { name: string; [key: string]: any };\n /** Entity definitions (optional — simple apps have none). */\n entities?: readonly EntityDefinition[];\n}\n\n/**\n * Item that may have an owner (used for own-scope permission checks).\n */\nexport interface OwnableItem {\n createdBy?: { id: string } | null;\n}\n\n/**\n * Extract the union of entity definitions from a schema config type.\n */\ntype EntitiesOf<S extends PermissionSchemaConfig> = S extends {\n entities: ReadonlyArray<infer E extends EntityDefinition>;\n}\n ? E\n : never;\n\n/**\n * Extract entity IDs whose actions array contains an action with the given name.\n */\ntype EntityIdWithAction<S extends PermissionSchemaConfig, A extends string> = {\n [K in EntitiesOf<S> as K extends { actions: ReadonlyArray<infer Act> }\n ? Act extends { name: A }\n ? K[\"id\"]\n : never\n : never]: never;\n} extends infer M\n ? keyof M & string\n : never;\n\n/**\n * All entity IDs in the schema.\n */\nexport type AllEntityIds<S extends PermissionSchemaConfig> = EntitiesOf<S>[\"id\"];\n\n/**\n * Entity IDs that have the \"rwd\" action.\n */\nexport type RwdEntityId<S extends PermissionSchemaConfig> = EntityIdWithAction<S, \"rwd\">;\n\n/**\n * Entity IDs that have the \"pw\" action.\n */\nexport type PwEntityId<S extends PermissionSchemaConfig> = EntityIdWithAction<S, \"pw\">;\n\n/**\n * Custom (non-builtin) action names across all entities.\n */\nexport type CustomActionNames<S extends PermissionSchemaConfig> = Exclude<\n EntitiesOf<S> extends { actions: ReadonlyArray<infer Act extends ActionDefinition> }\n ? Act[\"name\"]\n : never,\n \"rwd\" | \"pw\"\n>;\n\n/**\n * Typed permissions interface for a given schema.\n *\n * When the schema has literal entity types, methods are narrowed to only accept valid entity IDs.\n * When the schema is dynamically typed, all methods accept `string`.\n */\nexport type Permissions<S extends PermissionSchemaConfig> =\n string extends AllEntityIds<S> ? PermissionsUntyped : PermissionsTyped<S>;\n\nexport interface PermissionsUntyped {\n canAccess(entityId: string, item?: OwnableItem): Promise<boolean>;\n onlyOwnRecords(entityId: string): Promise<boolean>;\n canRead(entityId: string): Promise<boolean>;\n canCreate(entityId: string): Promise<boolean>;\n canEdit(entityId: string, item?: OwnableItem): Promise<boolean>;\n canDelete(entityId: string, item?: OwnableItem): Promise<boolean>;\n canPublish(entityId: string): Promise<boolean>;\n canUnpublish(entityId: string): Promise<boolean>;\n canAction(action: string, entityId: string): Promise<boolean>;\n}\n\nexport type PermissionsTyped<S extends PermissionSchemaConfig> = {\n canAccess(entityId: AllEntityIds<S>, item?: OwnableItem): Promise<boolean>;\n onlyOwnRecords(entityId: AllEntityIds<S>): Promise<boolean>;\n canAction(\n action: CustomActionNames<S> extends never ? string : CustomActionNames<S>,\n entityId: AllEntityIds<S>\n ): Promise<boolean>;\n} & ([RwdEntityId<S>] extends [never]\n ? {\n canRead(entityId: string): Promise<boolean>;\n canCreate(entityId: string): Promise<boolean>;\n canEdit(entityId: string, item?: OwnableItem): Promise<boolean>;\n canDelete(entityId: string, item?: OwnableItem): Promise<boolean>;\n }\n : {\n canRead(entityId: RwdEntityId<S>): Promise<boolean>;\n canCreate(entityId: RwdEntityId<S>): Promise<boolean>;\n canEdit(entityId: RwdEntityId<S>, item?: OwnableItem): Promise<boolean>;\n canDelete(entityId: RwdEntityId<S>, item?: OwnableItem): Promise<boolean>;\n }) &\n ([PwEntityId<S>] extends [never]\n ? {\n canPublish(entityId: string): Promise<boolean>;\n canUnpublish(entityId: string): Promise<boolean>;\n }\n : {\n canPublish(entityId: PwEntityId<S>): Promise<boolean>;\n canUnpublish(entityId: PwEntityId<S>): Promise<boolean>;\n });\n"],"mappings":"","ignoreList":[]}
|
|
@@ -6,7 +6,7 @@ export declare abstract class FieldBuilder<TZod extends z.ZodTypeAny> {
|
|
|
6
6
|
protected zodSchema: TZod;
|
|
7
7
|
constructor(type: string, initialSchema: TZod);
|
|
8
8
|
label(text: string): this;
|
|
9
|
-
|
|
9
|
+
help(text: string): this;
|
|
10
10
|
placeholder(text: string): this;
|
|
11
11
|
toConfig(): FieldConfig;
|
|
12
12
|
getZodSchema(): TZod;
|
|
@@ -12,12 +12,12 @@ export class FieldBuilder {
|
|
|
12
12
|
this.config.label = text;
|
|
13
13
|
return this;
|
|
14
14
|
}
|
|
15
|
-
|
|
16
|
-
this.config.
|
|
15
|
+
help(text) {
|
|
16
|
+
this.config.help = text;
|
|
17
17
|
return this;
|
|
18
18
|
}
|
|
19
19
|
placeholder(text) {
|
|
20
|
-
this.config.
|
|
20
|
+
this.config.placeholder = text;
|
|
21
21
|
return this;
|
|
22
22
|
}
|
|
23
23
|
toConfig() {
|
|
@@ -25,8 +25,8 @@ export class FieldBuilder {
|
|
|
25
25
|
fieldId: "",
|
|
26
26
|
type: this.config.type,
|
|
27
27
|
label: this.config.label,
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
help: this.config.help,
|
|
29
|
+
placeholder: this.config.placeholder,
|
|
30
30
|
validation: this.config.validation || [],
|
|
31
31
|
settings: this.config.settings || {},
|
|
32
32
|
zodSchema: this.zodSchema
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["z","FieldBuilder","config","validation","constructor","type","initialSchema","zodSchema","label","text","
|
|
1
|
+
{"version":3,"names":["z","FieldBuilder","config","validation","constructor","type","initialSchema","zodSchema","label","text","help","placeholder","toConfig","fieldId","settings","getZodSchema","TextFieldBuilder","schema","string","required","message","newSchema","min","push","name","minLength","length","maxLength","max","slug","regex","email","url","unique","ObjectFieldBuilder","nestedFields","fields","registry","fieldBuilders","shape","nestedConfigs","builder","Object","entries","object","optional","nullable"],"sources":["FieldBuilder.ts"],"sourcesContent":["import { z } from \"zod\";\nimport type { FieldConfig } from \"./types.js\";\nimport type { IFieldBuilderRegistry } from \"./abstractions.js\";\n\n// Make FieldBuilder return the Zod type it produces\nexport abstract class FieldBuilder<TZod extends z.ZodTypeAny> {\n protected config: Partial<FieldConfig> = {\n validation: []\n };\n protected zodSchema: TZod;\n\n constructor(type: string, initialSchema: TZod) {\n this.config.type = type;\n this.zodSchema = initialSchema;\n }\n\n label(text: string): this {\n this.config.label = text;\n return this;\n }\n\n help(text: string): this {\n this.config.help = text;\n return this;\n }\n\n placeholder(text: string): this {\n this.config.placeholder = text;\n return this;\n }\n\n toConfig(): FieldConfig {\n return {\n fieldId: \"\",\n type: this.config.type!,\n label: this.config.label,\n help: this.config.help,\n placeholder: this.config.placeholder,\n validation: this.config.validation || [],\n settings: this.config.settings || {},\n zodSchema: this.zodSchema\n };\n }\n\n getZodSchema(): TZod {\n return this.zodSchema;\n }\n}\n\n// Text Field Builder - properly track type transformations\nexport class TextFieldBuilder<TZod extends z.ZodString = z.ZodString> extends FieldBuilder<TZod> {\n constructor(schema?: TZod) {\n super(\"text\", (schema || z.string()) as TZod);\n }\n\n required(message?: string): TextFieldBuilder<z.ZodString> {\n const newSchema = (this.zodSchema as z.ZodString).min(1, message || \"Field is required\");\n this.zodSchema = newSchema as any;\n this.config.validation?.push({\n name: \"required\",\n message: message || \"Field is required\",\n settings: {}\n });\n return this as any;\n }\n\n minLength(length: number, message?: string): this {\n this.zodSchema = (this.zodSchema as z.ZodString).min(length, message) as TZod;\n return this;\n }\n\n maxLength(length: number, message?: string): this {\n this.zodSchema = (this.zodSchema as z.ZodString).max(length, message) as TZod;\n return this;\n }\n\n slug(): this {\n this.zodSchema = (this.zodSchema as z.ZodString).regex(\n /^[a-z0-9-]+$/,\n \"Must be a valid slug (lowercase letters, numbers, and hyphens only)\"\n ) as TZod;\n this.config.validation?.push({\n name: \"slug\",\n message: \"Must be a valid slug\",\n settings: {}\n });\n return this;\n }\n\n email(): this {\n this.zodSchema = (this.zodSchema as z.ZodString).email(\"Must be a valid email\") as TZod;\n this.config.validation?.push({\n name: \"email\",\n message: \"Must be a valid email\",\n settings: {}\n });\n return this;\n }\n\n url(): this {\n this.zodSchema = (this.zodSchema as z.ZodString).url(\"Must be a valid URL\") as TZod;\n this.config.validation?.push({\n name: \"url\",\n message: \"Must be a valid URL\",\n settings: {}\n });\n return this;\n }\n\n unique(): this {\n this.config.validation?.push({\n name: \"unique\",\n message: \"Value must be unique\",\n settings: {}\n });\n return this;\n }\n}\n\n// Object Field Builder - track the shape type\nexport class ObjectFieldBuilder<TShape extends z.ZodRawShape> extends FieldBuilder<\n z.ZodObject<TShape>\n> {\n private nestedFields: FieldConfig[] = [];\n\n constructor(\n fields: (registry: IFieldBuilderRegistry) => {\n [K in keyof TShape]: FieldBuilder<TShape[K]>;\n },\n registry: IFieldBuilderRegistry\n ) {\n const fieldBuilders = fields(registry);\n const shape = {} as TShape;\n const nestedConfigs: FieldConfig[] = [];\n\n for (const [fieldId, builder] of Object.entries(fieldBuilders) as Array<\n [keyof TShape, FieldBuilder<any>]\n >) {\n const config = builder.toConfig();\n config.fieldId = fieldId as string;\n nestedConfigs.push(config);\n shape[fieldId] = builder.getZodSchema() as TShape[keyof TShape];\n }\n\n super(\"object\", z.object(shape));\n this.nestedFields = nestedConfigs;\n this.config.fields = nestedConfigs;\n }\n\n optional(): ObjectFieldBuilder<TShape> & {\n getZodSchema(): z.ZodOptional<z.ZodObject<TShape>>;\n } {\n this.zodSchema = this.zodSchema.optional() as any;\n return this as any;\n }\n\n nullable(): ObjectFieldBuilder<TShape> & {\n getZodSchema(): z.ZodNullable<z.ZodObject<TShape>>;\n } {\n this.zodSchema = this.zodSchema.nullable() as any;\n return this as any;\n }\n\n override toConfig(): FieldConfig {\n const config = super.toConfig();\n config.fields = this.nestedFields;\n return config;\n }\n}\n"],"mappings":"AAAA,SAASA,CAAC,QAAQ,KAAK;AAIvB;AACA,OAAO,MAAeC,YAAY,CAA4B;EAChDC,MAAM,GAAyB;IACrCC,UAAU,EAAE;EAChB,CAAC;EAGDC,WAAWA,CAACC,IAAY,EAAEC,aAAmB,EAAE;IAC3C,IAAI,CAACJ,MAAM,CAACG,IAAI,GAAGA,IAAI;IACvB,IAAI,CAACE,SAAS,GAAGD,aAAa;EAClC;EAEAE,KAAKA,CAACC,IAAY,EAAQ;IACtB,IAAI,CAACP,MAAM,CAACM,KAAK,GAAGC,IAAI;IACxB,OAAO,IAAI;EACf;EAEAC,IAAIA,CAACD,IAAY,EAAQ;IACrB,IAAI,CAACP,MAAM,CAACQ,IAAI,GAAGD,IAAI;IACvB,OAAO,IAAI;EACf;EAEAE,WAAWA,CAACF,IAAY,EAAQ;IAC5B,IAAI,CAACP,MAAM,CAACS,WAAW,GAAGF,IAAI;IAC9B,OAAO,IAAI;EACf;EAEAG,QAAQA,CAAA,EAAgB;IACpB,OAAO;MACHC,OAAO,EAAE,EAAE;MACXR,IAAI,EAAE,IAAI,CAACH,MAAM,CAACG,IAAK;MACvBG,KAAK,EAAE,IAAI,CAACN,MAAM,CAACM,KAAK;MACxBE,IAAI,EAAE,IAAI,CAACR,MAAM,CAACQ,IAAI;MACtBC,WAAW,EAAE,IAAI,CAACT,MAAM,CAACS,WAAW;MACpCR,UAAU,EAAE,IAAI,CAACD,MAAM,CAACC,UAAU,IAAI,EAAE;MACxCW,QAAQ,EAAE,IAAI,CAACZ,MAAM,CAACY,QAAQ,IAAI,CAAC,CAAC;MACpCP,SAAS,EAAE,IAAI,CAACA;IACpB,CAAC;EACL;EAEAQ,YAAYA,CAAA,EAAS;IACjB,OAAO,IAAI,CAACR,SAAS;EACzB;AACJ;;AAEA;AACA,OAAO,MAAMS,gBAAgB,SAAiDf,YAAY,CAAO;EAC7FG,WAAWA,CAACa,MAAa,EAAE;IACvB,KAAK,CAAC,MAAM,EAAGA,MAAM,IAAIjB,CAAC,CAACkB,MAAM,CAAC,CAAU,CAAC;EACjD;EAEAC,QAAQA,CAACC,OAAgB,EAAiC;IACtD,MAAMC,SAAS,GAAI,IAAI,CAACd,SAAS,CAAiBe,GAAG,CAAC,CAAC,EAAEF,OAAO,IAAI,mBAAmB,CAAC;IACxF,IAAI,CAACb,SAAS,GAAGc,SAAgB;IACjC,IAAI,CAACnB,MAAM,CAACC,UAAU,EAAEoB,IAAI,CAAC;MACzBC,IAAI,EAAE,UAAU;MAChBJ,OAAO,EAAEA,OAAO,IAAI,mBAAmB;MACvCN,QAAQ,EAAE,CAAC;IACf,CAAC,CAAC;IACF,OAAO,IAAI;EACf;EAEAW,SAASA,CAACC,MAAc,EAAEN,OAAgB,EAAQ;IAC9C,IAAI,CAACb,SAAS,GAAI,IAAI,CAACA,SAAS,CAAiBe,GAAG,CAACI,MAAM,EAAEN,OAAO,CAAS;IAC7E,OAAO,IAAI;EACf;EAEAO,SAASA,CAACD,MAAc,EAAEN,OAAgB,EAAQ;IAC9C,IAAI,CAACb,SAAS,GAAI,IAAI,CAACA,SAAS,CAAiBqB,GAAG,CAACF,MAAM,EAAEN,OAAO,CAAS;IAC7E,OAAO,IAAI;EACf;EAEAS,IAAIA,CAAA,EAAS;IACT,IAAI,CAACtB,SAAS,GAAI,IAAI,CAACA,SAAS,CAAiBuB,KAAK,CAClD,cAAc,EACd,qEACJ,CAAS;IACT,IAAI,CAAC5B,MAAM,CAACC,UAAU,EAAEoB,IAAI,CAAC;MACzBC,IAAI,EAAE,MAAM;MACZJ,OAAO,EAAE,sBAAsB;MAC/BN,QAAQ,EAAE,CAAC;IACf,CAAC,CAAC;IACF,OAAO,IAAI;EACf;EAEAiB,KAAKA,CAAA,EAAS;IACV,IAAI,CAACxB,SAAS,GAAI,IAAI,CAACA,SAAS,CAAiBwB,KAAK,CAAC,uBAAuB,CAAS;IACvF,IAAI,CAAC7B,MAAM,CAACC,UAAU,EAAEoB,IAAI,CAAC;MACzBC,IAAI,EAAE,OAAO;MACbJ,OAAO,EAAE,uBAAuB;MAChCN,QAAQ,EAAE,CAAC;IACf,CAAC,CAAC;IACF,OAAO,IAAI;EACf;EAEAkB,GAAGA,CAAA,EAAS;IACR,IAAI,CAACzB,SAAS,GAAI,IAAI,CAACA,SAAS,CAAiByB,GAAG,CAAC,qBAAqB,CAAS;IACnF,IAAI,CAAC9B,MAAM,CAACC,UAAU,EAAEoB,IAAI,CAAC;MACzBC,IAAI,EAAE,KAAK;MACXJ,OAAO,EAAE,qBAAqB;MAC9BN,QAAQ,EAAE,CAAC;IACf,CAAC,CAAC;IACF,OAAO,IAAI;EACf;EAEAmB,MAAMA,CAAA,EAAS;IACX,IAAI,CAAC/B,MAAM,CAACC,UAAU,EAAEoB,IAAI,CAAC;MACzBC,IAAI,EAAE,QAAQ;MACdJ,OAAO,EAAE,sBAAsB;MAC/BN,QAAQ,EAAE,CAAC;IACf,CAAC,CAAC;IACF,OAAO,IAAI;EACf;AACJ;;AAEA;AACA,OAAO,MAAMoB,kBAAkB,SAAuCjC,YAAY,CAEhF;EACUkC,YAAY,GAAkB,EAAE;EAExC/B,WAAWA,CACPgC,MAEC,EACDC,QAA+B,EACjC;IACE,MAAMC,aAAa,GAAGF,MAAM,CAACC,QAAQ,CAAC;IACtC,MAAME,KAAK,GAAG,CAAC,CAAW;IAC1B,MAAMC,aAA4B,GAAG,EAAE;IAEvC,KAAK,MAAM,CAAC3B,OAAO,EAAE4B,OAAO,CAAC,IAAIC,MAAM,CAACC,OAAO,CAACL,aAAa,CAAC,EAE3D;MACC,MAAMpC,MAAM,GAAGuC,OAAO,CAAC7B,QAAQ,CAAC,CAAC;MACjCV,MAAM,CAACW,OAAO,GAAGA,OAAiB;MAClC2B,aAAa,CAACjB,IAAI,CAACrB,MAAM,CAAC;MAC1BqC,KAAK,CAAC1B,OAAO,CAAC,GAAG4B,OAAO,CAAC1B,YAAY,CAAC,CAAyB;IACnE;IAEA,KAAK,CAAC,QAAQ,EAAEf,CAAC,CAAC4C,MAAM,CAACL,KAAK,CAAC,CAAC;IAChC,IAAI,CAACJ,YAAY,GAAGK,aAAa;IACjC,IAAI,CAACtC,MAAM,CAACkC,MAAM,GAAGI,aAAa;EACtC;EAEAK,QAAQA,CAAA,EAEN;IACE,IAAI,CAACtC,SAAS,GAAG,IAAI,CAACA,SAAS,CAACsC,QAAQ,CAAC,CAAQ;IACjD,OAAO,IAAI;EACf;EAEAC,QAAQA,CAAA,EAEN;IACE,IAAI,CAACvC,SAAS,GAAG,IAAI,CAACA,SAAS,CAACuC,QAAQ,CAAC,CAAQ;IACjD,OAAO,IAAI;EACf;EAESlC,QAAQA,CAAA,EAAgB;IAC7B,MAAMV,MAAM,GAAG,KAAK,CAACU,QAAQ,CAAC,CAAC;IAC/BV,MAAM,CAACkC,MAAM,GAAG,IAAI,CAACD,YAAY;IACjC,OAAOjC,MAAM;EACjB;AACJ","ignoreList":[]}
|
|
@@ -41,7 +41,7 @@ export interface ITextFieldBuilder extends FieldBuilder<z.ZodString> {
|
|
|
41
41
|
email(): this;
|
|
42
42
|
url(): this;
|
|
43
43
|
unique(): this;
|
|
44
|
-
|
|
44
|
+
help(text: string): this;
|
|
45
45
|
placeholder(text: string): this;
|
|
46
46
|
}
|
|
47
47
|
|
|
@@ -219,7 +219,7 @@ export interface IColorFieldBuilder extends FieldBuilder<z.ZodString> {
|
|
|
219
219
|
default(hex: string): this;
|
|
220
220
|
allowAlpha(): this;
|
|
221
221
|
palette(colors: string[]): this;
|
|
222
|
-
|
|
222
|
+
help(text: string): this;
|
|
223
223
|
}
|
|
224
224
|
|
|
225
225
|
class ColorFieldBuilder extends FieldBuilder<z.ZodString> implements IColorFieldBuilder {
|
package/models/cms/types.d.ts
CHANGED
|
@@ -6,8 +6,8 @@ export interface FieldConfig {
|
|
|
6
6
|
fieldId: string;
|
|
7
7
|
type: string;
|
|
8
8
|
label?: string;
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
help?: string;
|
|
10
|
+
placeholder?: string;
|
|
11
11
|
validation?: ValidationRule[];
|
|
12
12
|
settings?: Record<string, any>;
|
|
13
13
|
zodSchema: z.ZodTypeAny;
|
package/models/cms/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":[],"sources":["types.ts"],"sourcesContent":["import { z } from \"zod\";\nimport type { ModelClass } from \"~/models/base/ModelBuilder.js\";\nimport type { BaseModel } from \"~/models/base/BaseModel.js\";\nimport type { IModelData } from \"~/models/base/abstractions.js\";\n\nexport interface FieldConfig {\n fieldId: string;\n type: string;\n label?: string;\n
|
|
1
|
+
{"version":3,"names":[],"sources":["types.ts"],"sourcesContent":["import { z } from \"zod\";\nimport type { ModelClass } from \"~/models/base/ModelBuilder.js\";\nimport type { BaseModel } from \"~/models/base/BaseModel.js\";\nimport type { IModelData } from \"~/models/base/abstractions.js\";\n\nexport interface FieldConfig {\n fieldId: string;\n type: string;\n label?: string;\n help?: string;\n placeholder?: string;\n validation?: ValidationRule[];\n settings?: Record<string, any>;\n zodSchema: z.ZodTypeAny;\n fields?: FieldConfig[]; // For nested object fields\n}\n\nexport interface ValidationRule {\n name: string;\n message: string;\n settings: Record<string, any>;\n}\n\nexport interface CmsModelMetadata {\n icon?: string;\n description?: string;\n titleFieldId?: string;\n descriptionFieldId?: string;\n imageFieldId?: string;\n tags?: string[];\n}\n\n// Make PrivateCmsModel generic!\nexport interface PrivateCmsModel<TModel extends BaseModel<any>> {\n type: \"private\";\n modelType: \"private\";\n Model: ModelClass<TModel>;\n modelId: string;\n name: string;\n icon?: string;\n description?: string;\n fields: FieldConfig[];\n schema: TModel[\"__schema\"];\n create: (data: IModelData<TModel>) => TModel;\n}\n\nexport interface FieldBuilderConfig {\n fieldId: string;\n config: FieldConfig;\n zodSchema: z.ZodTypeAny;\n}\n"],"mappings":"","ignoreList":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webiny/api-core",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "6.0.0-rc.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"repository": {
|
|
@@ -57,22 +57,22 @@
|
|
|
57
57
|
},
|
|
58
58
|
"license": "MIT",
|
|
59
59
|
"dependencies": {
|
|
60
|
-
"@webiny/api": "
|
|
60
|
+
"@webiny/api": "6.0.0-rc.1",
|
|
61
61
|
"@webiny/di": "0.2.3",
|
|
62
|
-
"@webiny/error": "
|
|
63
|
-
"@webiny/feature": "
|
|
64
|
-
"@webiny/handler": "
|
|
65
|
-
"@webiny/handler-db": "
|
|
66
|
-
"@webiny/handler-graphql": "
|
|
67
|
-
"@webiny/plugins": "
|
|
68
|
-
"@webiny/project": "
|
|
69
|
-
"@webiny/utils": "
|
|
70
|
-
"@webiny/wcp": "
|
|
62
|
+
"@webiny/error": "6.0.0-rc.1",
|
|
63
|
+
"@webiny/feature": "6.0.0-rc.1",
|
|
64
|
+
"@webiny/handler": "6.0.0-rc.1",
|
|
65
|
+
"@webiny/handler-db": "6.0.0-rc.1",
|
|
66
|
+
"@webiny/handler-graphql": "6.0.0-rc.1",
|
|
67
|
+
"@webiny/plugins": "6.0.0-rc.1",
|
|
68
|
+
"@webiny/project": "6.0.0-rc.1",
|
|
69
|
+
"@webiny/utils": "6.0.0-rc.1",
|
|
70
|
+
"@webiny/wcp": "6.0.0-rc.1",
|
|
71
71
|
"dataloader": "2.2.3",
|
|
72
72
|
"jose": "5.10.0",
|
|
73
73
|
"jsonwebtoken": "9.0.3",
|
|
74
74
|
"lodash": "4.17.23",
|
|
75
|
-
"minimatch": "
|
|
75
|
+
"minimatch": "10.2.2",
|
|
76
76
|
"pino": "9.14.0",
|
|
77
77
|
"pino-lambda": "4.4.1",
|
|
78
78
|
"ts-morph": "27.0.2",
|
|
@@ -81,18 +81,18 @@
|
|
|
81
81
|
"devDependencies": {
|
|
82
82
|
"@types/jsonwebtoken": "9.0.10",
|
|
83
83
|
"@types/md5": "2.3.6",
|
|
84
|
-
"@webiny/build-tools": "
|
|
85
|
-
"@webiny/db-dynamodb": "
|
|
86
|
-
"@webiny/handler-aws": "
|
|
87
|
-
"@webiny/project-utils": "
|
|
84
|
+
"@webiny/build-tools": "6.0.0-rc.1",
|
|
85
|
+
"@webiny/db-dynamodb": "6.0.0-rc.1",
|
|
86
|
+
"@webiny/handler-aws": "6.0.0-rc.1",
|
|
87
|
+
"@webiny/project-utils": "6.0.0-rc.1",
|
|
88
88
|
"jest-dynalite": "3.6.1",
|
|
89
|
-
"rimraf": "6.1.
|
|
89
|
+
"rimraf": "6.1.3",
|
|
90
90
|
"typescript": "5.9.3",
|
|
91
|
-
"vitest": "
|
|
91
|
+
"vitest": "4.0.18"
|
|
92
92
|
},
|
|
93
93
|
"publishConfig": {
|
|
94
94
|
"access": "public",
|
|
95
95
|
"directory": "dist"
|
|
96
96
|
},
|
|
97
|
-
"gitHead": "
|
|
97
|
+
"gitHead": "36d702721ff9ed39fb21d6f5fe7922a2a8716e63"
|
|
98
98
|
}
|