@mvriu5/payload-ai 0.5.6 → 0.5.9
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/README.md +52 -5
- package/dist/ai/providerOptions.d.ts +1 -14
- package/dist/ai/providerOptions.js +0 -23
- package/dist/ai/providerRuntime.d.ts +3 -2
- package/dist/ai/providerRuntime.js +6 -15
- package/dist/components/AIActionProposalList.d.ts +2 -1
- package/dist/components/AIActionProposalList.js +74 -6
- package/dist/components/AIActionProposalList.module.css +49 -5
- package/dist/components/AIApiKeyField.js +0 -8
- package/dist/components/AIInput.d.ts +1 -1
- package/dist/components/AIInput.js +156 -23
- package/dist/components/AIInput.module.css +185 -147
- package/dist/components/CollectionMentionPopover.d.ts +1 -1
- package/dist/components/DiffDialog.d.ts +12 -0
- package/dist/components/DiffDialog.js +310 -0
- package/dist/components/DiffDialog.module.css +249 -0
- package/dist/components/Icons.d.ts +9 -0
- package/dist/components/Icons.js +266 -0
- package/dist/components/RecentChangesList.d.ts +18 -0
- package/dist/components/RecentChangesList.js +66 -0
- package/dist/components/RecentChangesList.module.css +135 -0
- package/dist/components/hooks/useAISettings.d.ts +1 -1
- package/dist/endpoints/aiApplyActionEndpointHandler.d.ts +3 -1
- package/dist/endpoints/aiApplyActionEndpointHandler.js +209 -8
- package/dist/endpoints/aiChatEndpointHandler.d.ts +5 -1
- package/dist/endpoints/aiChatEndpointHandler.js +106 -21
- package/dist/endpoints/aiMentionSuggestionsEndpointHandler.d.ts +2 -1
- package/dist/endpoints/aiMentionSuggestionsEndpointHandler.js +7 -7
- package/dist/endpoints/aiProposalDiffEndpointHandler.d.ts +7 -0
- package/dist/endpoints/aiProposalDiffEndpointHandler.js +138 -0
- package/dist/endpoints/aiRecentChangesEndpointHandler.d.ts +6 -0
- package/dist/endpoints/aiRecentChangesEndpointHandler.js +35 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +140 -18
- package/dist/payload/collectionPermissions.d.ts +22 -0
- package/dist/payload/collectionPermissions.js +68 -0
- package/dist/payload/schemaContext.d.ts +46 -2
- package/dist/payload/schemaContext.js +105 -14
- package/package.json +113 -121
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { verifyAIActionProposal } from "../ai/proposals.js";
|
|
2
|
+
import { redactSensitiveData } from "../ai/sensitiveData.js";
|
|
3
|
+
import { getCollectionFields, normalizeAuthData, normalizeDataForFields } from "../payload/normalizeData.js";
|
|
4
|
+
import { isCollectionActionAllowed } from "../payload/collectionPermissions.js";
|
|
5
|
+
const isRecord = (value)=>{
|
|
6
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
7
|
+
};
|
|
8
|
+
const isActionProposal = (proposal)=>{
|
|
9
|
+
if (!isRecord(proposal) || typeof proposal.label !== "string") return false;
|
|
10
|
+
if (proposal.action === "create") {
|
|
11
|
+
return typeof proposal.collection === "string" && isRecord(proposal.data);
|
|
12
|
+
}
|
|
13
|
+
if (proposal.action === "update") {
|
|
14
|
+
return typeof proposal.collection === "string" && typeof proposal.id === "string" && isRecord(proposal.data);
|
|
15
|
+
}
|
|
16
|
+
if (proposal.action === "delete") {
|
|
17
|
+
return typeof proposal.collection === "string" && typeof proposal.id === "string";
|
|
18
|
+
}
|
|
19
|
+
if (proposal.action === "updateGlobal") {
|
|
20
|
+
return typeof proposal.slug === "string" && isRecord(proposal.data);
|
|
21
|
+
}
|
|
22
|
+
return false;
|
|
23
|
+
};
|
|
24
|
+
const mergeData = (current, next)=>{
|
|
25
|
+
return Object.fromEntries(Object.entries({
|
|
26
|
+
...current,
|
|
27
|
+
...next
|
|
28
|
+
}).map(([key, value])=>{
|
|
29
|
+
const currentValue = current[key];
|
|
30
|
+
if (isRecord(currentValue) && isRecord(value)) {
|
|
31
|
+
return [
|
|
32
|
+
key,
|
|
33
|
+
mergeData(currentValue, value)
|
|
34
|
+
];
|
|
35
|
+
}
|
|
36
|
+
return [
|
|
37
|
+
key,
|
|
38
|
+
value
|
|
39
|
+
];
|
|
40
|
+
}));
|
|
41
|
+
};
|
|
42
|
+
export const createAIProposalDiffEndpointHandler = (options = {})=>async (req)=>{
|
|
43
|
+
if (!req.user) return Response.json({
|
|
44
|
+
error: "Unauthorized"
|
|
45
|
+
}, {
|
|
46
|
+
status: 401
|
|
47
|
+
});
|
|
48
|
+
const body = req.json ? await req.json().catch(()=>null) : null;
|
|
49
|
+
const proposal = body?.proposal;
|
|
50
|
+
if (!proposal) return Response.json({
|
|
51
|
+
error: "Proposal is required"
|
|
52
|
+
}, {
|
|
53
|
+
status: 400
|
|
54
|
+
});
|
|
55
|
+
if (!verifyAIActionProposal(proposal)) return Response.json({
|
|
56
|
+
error: "Proposal signature is invalid or expired."
|
|
57
|
+
}, {
|
|
58
|
+
status: 400
|
|
59
|
+
});
|
|
60
|
+
if (!isActionProposal(proposal)) return Response.json({
|
|
61
|
+
error: "Proposal is invalid."
|
|
62
|
+
}, {
|
|
63
|
+
status: 400
|
|
64
|
+
});
|
|
65
|
+
try {
|
|
66
|
+
if (proposal.action === "updateGlobal") {
|
|
67
|
+
const globalConfig = req.payload.config.globals?.find((global)=>global.slug === proposal.slug);
|
|
68
|
+
if (!globalConfig) return Response.json({
|
|
69
|
+
error: "Unknown global"
|
|
70
|
+
}, {
|
|
71
|
+
status: 400
|
|
72
|
+
});
|
|
73
|
+
const normalized = normalizeDataForFields(globalConfig.fields || [], proposal.data);
|
|
74
|
+
const doc = await req.payload.findGlobal({
|
|
75
|
+
depth: 2,
|
|
76
|
+
overrideAccess: false,
|
|
77
|
+
req,
|
|
78
|
+
slug: proposal.slug
|
|
79
|
+
});
|
|
80
|
+
return Response.json({
|
|
81
|
+
after: redactSensitiveData(mergeData(doc, normalized.data)),
|
|
82
|
+
before: redactSensitiveData(doc)
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
if (!isCollectionActionAllowed({
|
|
86
|
+
action: proposal.action === "delete" ? "delete" : "read",
|
|
87
|
+
permissions: options.collections,
|
|
88
|
+
req,
|
|
89
|
+
slug: proposal.collection
|
|
90
|
+
})) return Response.json({
|
|
91
|
+
error: "Unknown collection"
|
|
92
|
+
}, {
|
|
93
|
+
status: 400
|
|
94
|
+
});
|
|
95
|
+
if (proposal.action === "delete") {
|
|
96
|
+
const doc = await req.payload.findByID({
|
|
97
|
+
collection: proposal.collection,
|
|
98
|
+
depth: 2,
|
|
99
|
+
id: proposal.id,
|
|
100
|
+
overrideAccess: false,
|
|
101
|
+
req
|
|
102
|
+
});
|
|
103
|
+
return Response.json({
|
|
104
|
+
after: {},
|
|
105
|
+
before: redactSensitiveData(doc)
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
const collectionConfig = req.payload.config.collections.find((collection)=>collection.slug === proposal.collection);
|
|
109
|
+
const normalized = normalizeAuthData(collectionConfig, normalizeDataForFields(getCollectionFields(collectionConfig), proposal.data));
|
|
110
|
+
if (proposal.action === "create") {
|
|
111
|
+
return Response.json({
|
|
112
|
+
after: redactSensitiveData(normalized.data),
|
|
113
|
+
before: {}
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
const doc = await req.payload.findByID({
|
|
117
|
+
collection: proposal.collection,
|
|
118
|
+
depth: 2,
|
|
119
|
+
id: proposal.id,
|
|
120
|
+
overrideAccess: false,
|
|
121
|
+
req
|
|
122
|
+
});
|
|
123
|
+
return Response.json({
|
|
124
|
+
after: redactSensitiveData(mergeData(doc, normalized.data)),
|
|
125
|
+
before: redactSensitiveData(doc)
|
|
126
|
+
});
|
|
127
|
+
} catch (err) {
|
|
128
|
+
req.payload.logger.error({
|
|
129
|
+
err,
|
|
130
|
+
msg: "AI proposal diff failed"
|
|
131
|
+
});
|
|
132
|
+
return Response.json({
|
|
133
|
+
error: "Could not load proposal diff."
|
|
134
|
+
}, {
|
|
135
|
+
status: 400
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
const getNumber = (value)=>{
|
|
2
|
+
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
3
|
+
};
|
|
4
|
+
const getString = (value)=>{
|
|
5
|
+
return typeof value === "string" ? value : null;
|
|
6
|
+
};
|
|
7
|
+
export const createAIRecentChangesEndpointHandler = (options)=>async (req)=>{
|
|
8
|
+
if (!req.user) return Response.json({
|
|
9
|
+
error: "Unauthorized"
|
|
10
|
+
}, {
|
|
11
|
+
status: 401
|
|
12
|
+
});
|
|
13
|
+
const result = await req.payload.find({
|
|
14
|
+
collection: options.changeLogCollection,
|
|
15
|
+
depth: 0,
|
|
16
|
+
limit: 12,
|
|
17
|
+
overrideAccess: false,
|
|
18
|
+
req,
|
|
19
|
+
sort: "-createdAt"
|
|
20
|
+
});
|
|
21
|
+
return Response.json({
|
|
22
|
+
changes: result.docs.map((doc)=>({
|
|
23
|
+
action: getString(doc.action),
|
|
24
|
+
additions: getNumber(doc.additions),
|
|
25
|
+
after: doc.after,
|
|
26
|
+
before: doc.before,
|
|
27
|
+
collection: getString(doc.collection),
|
|
28
|
+
documentID: getString(doc.documentID),
|
|
29
|
+
removals: getNumber(doc.removals),
|
|
30
|
+
slug: getString(doc.slug),
|
|
31
|
+
title: getString(doc.title) || "AI change",
|
|
32
|
+
url: getString(doc.targetURL)
|
|
33
|
+
}))
|
|
34
|
+
});
|
|
35
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import type { CollectionSlug, Config } from "payload";
|
|
2
2
|
import { type AIModelConfig } from "./ai/providerOptions.js";
|
|
3
|
+
import { type AICollectionPermissionConfig } from "./payload/collectionPermissions.js";
|
|
3
4
|
export type PayloadAiPluginOptions = {
|
|
4
|
-
|
|
5
|
+
allowUserApiKeys?: boolean;
|
|
6
|
+
collections?: Partial<Record<CollectionSlug, AICollectionPermissionConfig>>;
|
|
5
7
|
disabled?: boolean;
|
|
6
8
|
models?: AIModelConfig;
|
|
7
9
|
};
|
package/dist/index.js
CHANGED
|
@@ -2,11 +2,102 @@ import { aiProviders, getResolvedAIModelConfig } from "./ai/providerOptions.js";
|
|
|
2
2
|
import { createAIApplyActionEndpointHandler } from "./endpoints/aiApplyActionEndpointHandler.js";
|
|
3
3
|
import { createAIChatEndpointHandler } from "./endpoints/aiChatEndpointHandler.js";
|
|
4
4
|
import { createAIMentionSuggestionsEndpointHandler } from "./endpoints/aiMentionSuggestionsEndpointHandler.js";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
};
|
|
9
|
-
const
|
|
5
|
+
import { createAIProposalDiffEndpointHandler } from "./endpoints/aiProposalDiffEndpointHandler.js";
|
|
6
|
+
import { createAIRecentChangesEndpointHandler } from "./endpoints/aiRecentChangesEndpointHandler.js";
|
|
7
|
+
import { resolveCollectionPermissions } from "./payload/collectionPermissions.js";
|
|
8
|
+
import { isInternalCollection } from "./payload/shared.js";
|
|
9
|
+
const aiChangesCollectionSlug = "payload-ai-changes";
|
|
10
|
+
const createAIChangesCollection = ()=>({
|
|
11
|
+
slug: aiChangesCollectionSlug,
|
|
12
|
+
access: {
|
|
13
|
+
create: ()=>false,
|
|
14
|
+
delete: ()=>false,
|
|
15
|
+
read: ({ req })=>Boolean(req.user),
|
|
16
|
+
update: ()=>false
|
|
17
|
+
},
|
|
18
|
+
admin: {
|
|
19
|
+
defaultColumns: [
|
|
20
|
+
"title",
|
|
21
|
+
"action",
|
|
22
|
+
"additions",
|
|
23
|
+
"removals",
|
|
24
|
+
"createdAt"
|
|
25
|
+
],
|
|
26
|
+
group: "AI",
|
|
27
|
+
useAsTitle: "title"
|
|
28
|
+
},
|
|
29
|
+
fields: [
|
|
30
|
+
{
|
|
31
|
+
name: "title",
|
|
32
|
+
type: "text",
|
|
33
|
+
required: true
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
name: "action",
|
|
37
|
+
type: "select",
|
|
38
|
+
options: [
|
|
39
|
+
"create",
|
|
40
|
+
"update",
|
|
41
|
+
"delete",
|
|
42
|
+
"updateGlobal"
|
|
43
|
+
],
|
|
44
|
+
required: true
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
name: "targetType",
|
|
48
|
+
type: "select",
|
|
49
|
+
options: [
|
|
50
|
+
"collection",
|
|
51
|
+
"global"
|
|
52
|
+
],
|
|
53
|
+
required: true
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
name: "collection",
|
|
57
|
+
type: "text"
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
name: "slug",
|
|
61
|
+
type: "text"
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
name: "documentID",
|
|
65
|
+
type: "text"
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
name: "targetURL",
|
|
69
|
+
type: "text"
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
name: "additions",
|
|
73
|
+
type: "number",
|
|
74
|
+
defaultValue: 0
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
name: "removals",
|
|
78
|
+
type: "number",
|
|
79
|
+
defaultValue: 0
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
name: "before",
|
|
83
|
+
type: "json"
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
name: "after",
|
|
87
|
+
type: "json"
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
name: "proposal",
|
|
91
|
+
type: "json"
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
name: "userID",
|
|
95
|
+
type: "text"
|
|
96
|
+
}
|
|
97
|
+
],
|
|
98
|
+
timestamps: true
|
|
99
|
+
});
|
|
100
|
+
const addAccountFields = ({ allowUserApiKeys, config })=>{
|
|
10
101
|
const adminUserSlug = config.admin?.user;
|
|
11
102
|
if (!adminUserSlug || !config.collections) return;
|
|
12
103
|
const userCollection = config.collections.find((c)=>c.slug === adminUserSlug);
|
|
@@ -16,29 +107,43 @@ const addAccountFields = (config)=>{
|
|
|
16
107
|
type: "select",
|
|
17
108
|
defaultValue: "openai",
|
|
18
109
|
options: aiProviders
|
|
19
|
-
}, {
|
|
20
|
-
name: "aiApiKey",
|
|
21
|
-
type: "text",
|
|
22
|
-
admin: {
|
|
23
|
-
components: {
|
|
24
|
-
Field: "payload-ai-plugin/client#AIApiKeyField"
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
110
|
});
|
|
111
|
+
if (allowUserApiKeys) {
|
|
112
|
+
userCollection.fields.push({
|
|
113
|
+
name: "aiApiKey",
|
|
114
|
+
type: "text",
|
|
115
|
+
admin: {
|
|
116
|
+
components: {
|
|
117
|
+
Field: "payload-ai-plugin/client#AIApiKeyField"
|
|
118
|
+
},
|
|
119
|
+
description: "Optional. If empty, the plugin uses the provider API key from environment variables."
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
}
|
|
28
123
|
};
|
|
29
124
|
export const payloadAiPlugin = (pluginOptions)=>(config)=>{
|
|
30
125
|
const incomingOnInit = config.onInit;
|
|
31
|
-
const
|
|
126
|
+
const collectionPermissions = resolveCollectionPermissions(pluginOptions.collections);
|
|
127
|
+
const allowUserApiKeys = pluginOptions.allowUserApiKeys !== false;
|
|
32
128
|
const modelConfig = getResolvedAIModelConfig(pluginOptions.models);
|
|
33
129
|
if (!config.collections) config.collections = [];
|
|
34
|
-
|
|
130
|
+
if (!config.collections.some((collection)=>collection.slug === aiChangesCollectionSlug)) {
|
|
131
|
+
config.collections.push(createAIChangesCollection());
|
|
132
|
+
}
|
|
133
|
+
addAccountFields({
|
|
134
|
+
allowUserApiKeys,
|
|
135
|
+
config
|
|
136
|
+
});
|
|
35
137
|
if (pluginOptions.disabled) return config;
|
|
138
|
+
const mentionCollectionSlugs = config.collections.filter((collection)=>!isInternalCollection(collection.slug)).filter((collection)=>collectionPermissions ? Boolean(collectionPermissions[collection.slug]?.read) : true).map((collection)=>collection.slug);
|
|
36
139
|
if (!config.endpoints) config.endpoints = [];
|
|
37
140
|
if (!config.admin) config.admin = {};
|
|
38
141
|
config.admin.custom = {
|
|
39
142
|
...config.admin.custom || {},
|
|
40
143
|
payloadAiPlugin: {
|
|
41
144
|
...config.admin.custom?.payloadAiPlugin || {},
|
|
145
|
+
collectionSlugs: mentionCollectionSlugs,
|
|
146
|
+
allowUserApiKeys,
|
|
42
147
|
models: modelConfig
|
|
43
148
|
}
|
|
44
149
|
};
|
|
@@ -47,21 +152,38 @@ export const payloadAiPlugin = (pluginOptions)=>(config)=>{
|
|
|
47
152
|
config.admin.components.beforeDashboard.push(`payload-ai-plugin/client#AIInput`);
|
|
48
153
|
config.endpoints.push({
|
|
49
154
|
handler: createAIChatEndpointHandler({
|
|
50
|
-
|
|
155
|
+
allowUserApiKeys,
|
|
156
|
+
collections: collectionPermissions,
|
|
157
|
+
models: modelConfig
|
|
51
158
|
}),
|
|
52
159
|
method: "post",
|
|
53
160
|
path: "/ai-chat"
|
|
54
161
|
});
|
|
55
162
|
config.endpoints.push({
|
|
56
163
|
handler: createAIApplyActionEndpointHandler({
|
|
57
|
-
|
|
164
|
+
changeLogCollection: aiChangesCollectionSlug,
|
|
165
|
+
collections: collectionPermissions
|
|
58
166
|
}),
|
|
59
167
|
method: "post",
|
|
60
168
|
path: "/ai-apply-action"
|
|
61
169
|
});
|
|
170
|
+
config.endpoints.push({
|
|
171
|
+
handler: createAIRecentChangesEndpointHandler({
|
|
172
|
+
changeLogCollection: aiChangesCollectionSlug
|
|
173
|
+
}),
|
|
174
|
+
method: "get",
|
|
175
|
+
path: "/ai-recent-changes"
|
|
176
|
+
});
|
|
177
|
+
config.endpoints.push({
|
|
178
|
+
handler: createAIProposalDiffEndpointHandler({
|
|
179
|
+
collections: collectionPermissions
|
|
180
|
+
}),
|
|
181
|
+
method: "post",
|
|
182
|
+
path: "/ai-proposal-diff"
|
|
183
|
+
});
|
|
62
184
|
config.endpoints.push({
|
|
63
185
|
handler: createAIMentionSuggestionsEndpointHandler({
|
|
64
|
-
collections:
|
|
186
|
+
collections: collectionPermissions
|
|
65
187
|
}),
|
|
66
188
|
method: "post",
|
|
67
189
|
path: "/ai-mention-suggestions"
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { PayloadHandler } from "payload";
|
|
2
|
+
export type AICollectionAction = "create" | "delete" | "read" | "update";
|
|
3
|
+
export type AICollectionPermissions = Partial<Record<AICollectionAction, boolean>>;
|
|
4
|
+
export type AICollectionPermissionConfig = true | AICollectionPermissions;
|
|
5
|
+
export type AICollectionPermissionMap = Partial<Record<string, AICollectionPermissionConfig>>;
|
|
6
|
+
export type ResolvedAICollectionPermissionMap = Partial<Record<string, Record<AICollectionAction, boolean>>>;
|
|
7
|
+
export declare const resolveCollectionPermissions: (collections?: AICollectionPermissionMap) => ResolvedAICollectionPermissionMap | undefined;
|
|
8
|
+
export declare const getCollectionSlugsForAction: ({ action, permissions, req, }: {
|
|
9
|
+
action: AICollectionAction;
|
|
10
|
+
permissions?: ResolvedAICollectionPermissionMap;
|
|
11
|
+
req: Parameters<PayloadHandler>[0];
|
|
12
|
+
}) => string[];
|
|
13
|
+
export declare const isCollectionActionAllowed: ({ action, permissions, req, slug, }: {
|
|
14
|
+
action: AICollectionAction;
|
|
15
|
+
permissions?: ResolvedAICollectionPermissionMap;
|
|
16
|
+
req: Parameters<PayloadHandler>[0];
|
|
17
|
+
slug: string;
|
|
18
|
+
}) => boolean;
|
|
19
|
+
export declare const getCollectionPermissions: ({ permissions, slug, }: {
|
|
20
|
+
permissions?: ResolvedAICollectionPermissionMap;
|
|
21
|
+
slug: string;
|
|
22
|
+
}) => Record<AICollectionAction, boolean>;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { isInternalCollection } from "./shared.js";
|
|
2
|
+
const allActions = [
|
|
3
|
+
"create",
|
|
4
|
+
"delete",
|
|
5
|
+
"read",
|
|
6
|
+
"update"
|
|
7
|
+
];
|
|
8
|
+
const getKnownCollectionSlugs = (req)=>{
|
|
9
|
+
return req.payload.config.collections.map((collection)=>collection.slug).filter((slug)=>!isInternalCollection(slug));
|
|
10
|
+
};
|
|
11
|
+
export const resolveCollectionPermissions = (collections)=>{
|
|
12
|
+
if (!collections) return undefined;
|
|
13
|
+
return Object.fromEntries(Object.entries(collections).map(([slug, config])=>{
|
|
14
|
+
if (config === true) {
|
|
15
|
+
return [
|
|
16
|
+
slug,
|
|
17
|
+
{
|
|
18
|
+
create: true,
|
|
19
|
+
delete: true,
|
|
20
|
+
read: true,
|
|
21
|
+
update: true
|
|
22
|
+
}
|
|
23
|
+
];
|
|
24
|
+
}
|
|
25
|
+
if (!config) {
|
|
26
|
+
return [
|
|
27
|
+
slug,
|
|
28
|
+
{
|
|
29
|
+
create: false,
|
|
30
|
+
delete: false,
|
|
31
|
+
read: false,
|
|
32
|
+
update: false
|
|
33
|
+
}
|
|
34
|
+
];
|
|
35
|
+
}
|
|
36
|
+
return [
|
|
37
|
+
slug,
|
|
38
|
+
{
|
|
39
|
+
create: Boolean(config.create),
|
|
40
|
+
delete: Boolean(config.delete),
|
|
41
|
+
read: Boolean(config.read),
|
|
42
|
+
update: Boolean(config.update)
|
|
43
|
+
}
|
|
44
|
+
];
|
|
45
|
+
}));
|
|
46
|
+
};
|
|
47
|
+
export const getCollectionSlugsForAction = ({ action, permissions, req })=>{
|
|
48
|
+
const knownSlugs = getKnownCollectionSlugs(req);
|
|
49
|
+
if (!permissions) return knownSlugs;
|
|
50
|
+
return knownSlugs.filter((slug)=>Boolean(permissions[slug]?.[action]));
|
|
51
|
+
};
|
|
52
|
+
export const isCollectionActionAllowed = ({ action, permissions, req, slug })=>{
|
|
53
|
+
if (!getKnownCollectionSlugs(req).includes(slug)) return false;
|
|
54
|
+
if (!permissions) return true;
|
|
55
|
+
return Boolean(permissions[slug]?.[action]);
|
|
56
|
+
};
|
|
57
|
+
export const getCollectionPermissions = ({ permissions, slug })=>{
|
|
58
|
+
if (!permissions) {
|
|
59
|
+
return Object.fromEntries(allActions.map((action)=>[
|
|
60
|
+
action,
|
|
61
|
+
true
|
|
62
|
+
]));
|
|
63
|
+
}
|
|
64
|
+
return permissions[slug] || Object.fromEntries(allActions.map((action)=>[
|
|
65
|
+
action,
|
|
66
|
+
false
|
|
67
|
+
]));
|
|
68
|
+
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { PayloadHandler } from "payload";
|
|
2
|
+
import { type ResolvedAICollectionPermissionMap } from "./collectionPermissions.js";
|
|
2
3
|
export type AIChatMention = {
|
|
3
4
|
collection?: string;
|
|
4
5
|
id?: string;
|
|
@@ -8,11 +9,19 @@ export type AIChatMention = {
|
|
|
8
9
|
type?: "block" | "collection" | "doc" | "global";
|
|
9
10
|
};
|
|
10
11
|
export type FieldConfig = {
|
|
12
|
+
admin?: {
|
|
13
|
+
condition?: unknown;
|
|
14
|
+
};
|
|
11
15
|
blocks?: BlockConfig[];
|
|
12
16
|
fields?: FieldConfig[];
|
|
13
17
|
hasMany?: boolean;
|
|
14
18
|
label?: unknown;
|
|
19
|
+
localized?: boolean;
|
|
15
20
|
name?: string;
|
|
21
|
+
options?: (string | {
|
|
22
|
+
label?: unknown;
|
|
23
|
+
value?: string;
|
|
24
|
+
})[];
|
|
16
25
|
relationTo?: unknown;
|
|
17
26
|
required?: boolean;
|
|
18
27
|
type?: string;
|
|
@@ -25,16 +34,51 @@ type BlockConfig = {
|
|
|
25
34
|
};
|
|
26
35
|
slug: string;
|
|
27
36
|
};
|
|
37
|
+
type VersionConfig = boolean | {
|
|
38
|
+
drafts?: boolean | Record<string, unknown>;
|
|
39
|
+
};
|
|
40
|
+
type CollectionLikeConfig = {
|
|
41
|
+
access?: Record<string, unknown>;
|
|
42
|
+
auth?: unknown;
|
|
43
|
+
fields?: FieldConfig[];
|
|
44
|
+
label?: unknown;
|
|
45
|
+
labels?: {
|
|
46
|
+
plural?: unknown;
|
|
47
|
+
singular?: unknown;
|
|
48
|
+
};
|
|
49
|
+
slug: string;
|
|
50
|
+
versions?: VersionConfig;
|
|
51
|
+
};
|
|
28
52
|
type MentionContext = {
|
|
29
53
|
blockContexts: (Record<string, unknown> & {
|
|
30
54
|
parent: string;
|
|
31
55
|
slug: string;
|
|
32
56
|
})[];
|
|
33
57
|
collectionSlugs: string[];
|
|
58
|
+
collections?: ResolvedAICollectionPermissionMap;
|
|
34
59
|
globalSlugs: string[];
|
|
35
60
|
mentions?: AIChatMention[];
|
|
36
61
|
req: Parameters<PayloadHandler>[0];
|
|
37
62
|
};
|
|
63
|
+
export declare const describeCollectionLikeConfig: ({ config, permissions, type, }: {
|
|
64
|
+
config: CollectionLikeConfig;
|
|
65
|
+
permissions?: ResolvedAICollectionPermissionMap;
|
|
66
|
+
type: "collection" | "global";
|
|
67
|
+
}) => {
|
|
68
|
+
access: {
|
|
69
|
+
aiPermissions: Record<import("./collectionPermissions.js").AICollectionAction, boolean> | {
|
|
70
|
+
read: boolean;
|
|
71
|
+
update: boolean;
|
|
72
|
+
};
|
|
73
|
+
hasPayloadAccessControl: boolean;
|
|
74
|
+
};
|
|
75
|
+
fields: Record<string, unknown>[];
|
|
76
|
+
hasAuth: true | undefined;
|
|
77
|
+
hasDrafts: true | undefined;
|
|
78
|
+
label: {};
|
|
79
|
+
slug: string;
|
|
80
|
+
type: "collection" | "global";
|
|
81
|
+
};
|
|
38
82
|
export declare const describeField: (field: FieldConfig) => Record<string, unknown>;
|
|
39
83
|
export declare const collectBlocks: ({ fields, parent, }: {
|
|
40
84
|
fields: FieldConfig[];
|
|
@@ -43,8 +87,8 @@ export declare const collectBlocks: ({ fields, parent, }: {
|
|
|
43
87
|
parent: string;
|
|
44
88
|
slug: string;
|
|
45
89
|
})[];
|
|
46
|
-
export declare const getAllowedCollectionSlugs: (req: Parameters<PayloadHandler>[0], collections?:
|
|
47
|
-
export declare const getMentionContext: ({ blockContexts, collectionSlugs, globalSlugs, mentions, req, }: MentionContext) => Promise<Record<string, unknown>[]>;
|
|
90
|
+
export declare const getAllowedCollectionSlugs: (req: Parameters<PayloadHandler>[0], collections?: ResolvedAICollectionPermissionMap) => string[];
|
|
91
|
+
export declare const getMentionContext: ({ blockContexts, collectionSlugs, collections, globalSlugs, mentions, req, }: MentionContext) => Promise<Record<string, unknown>[]>;
|
|
48
92
|
export declare const buildPromptWithMentionContext: ({ mentionContext, prompt, }: {
|
|
49
93
|
mentionContext: Record<string, unknown>[];
|
|
50
94
|
prompt: string;
|