@mvriu5/payload-ai 1.6.2 → 1.6.4
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/dist/components/action-toast/ActionToast.d.ts +1 -14
- package/dist/components/action-toast/ActionToast.js +1 -1
- package/dist/components/ai-input/AIInput.js +1 -1
- package/dist/components/audit-log-list/AuditLogList.d.ts +2 -3
- package/dist/components/diff-dialog/DiffDialog.d.ts +2 -6
- package/dist/components/diff-dialog/DiffDialog.js +1 -1
- package/dist/components/generate-field/GenerateField.js +2 -0
- package/dist/components/hooks/useAIChatStream.d.ts +1 -1
- package/dist/components/hooks/useAISettings.d.ts +1 -1
- package/dist/components/hooks/useAISettings.js +1 -1
- package/dist/components/hooks/useDocumentMentionSuggestions.js +1 -1
- package/dist/components/hooks/useMentions.js +1 -1
- package/dist/components/hooks/usePluginConfig.d.ts +6 -6
- package/dist/components/hooks/usePluginConfig.js +1 -1
- package/dist/{payload → features}/collectionPermissions.js +1 -1
- package/dist/{payload/textFieldGeneration.d.ts → features/content/fieldGeneration.d.ts} +4 -0
- package/dist/{payload/textFieldGeneration.js → features/content/fieldGeneration.js} +19 -3
- package/dist/{ai → features}/promptCaching.d.ts +2 -2
- package/dist/features/proposals/containerNormalization.d.ts +8 -0
- package/dist/features/proposals/containerNormalization.js +112 -0
- package/dist/{payload/proposalData.d.ts → features/proposals/data.d.ts} +5 -10
- package/dist/features/proposals/data.js +113 -0
- package/dist/features/proposals/fieldNormalization.d.ts +2 -0
- package/dist/features/proposals/fieldNormalization.js +69 -0
- package/dist/features/proposals/normalizationTypes.d.ts +31 -0
- package/dist/features/proposals/normalizationTypes.js +1 -0
- package/dist/features/proposals/optionInference.d.ts +13 -0
- package/dist/features/proposals/optionInference.js +46 -0
- package/dist/features/proposals/recordNormalization.d.ts +6 -0
- package/dist/features/proposals/recordNormalization.js +78 -0
- package/dist/features/proposals/relationshipNormalization.d.ts +8 -0
- package/dist/features/proposals/relationshipNormalization.js +71 -0
- package/dist/{ai/proposalRepair.d.ts → features/proposals/repair.d.ts} +1 -1
- package/dist/{ai/proposalSigning.d.ts → features/proposals/signing.d.ts} +2 -5
- package/dist/{ai/proposalSigning.js → features/proposals/signing.js} +1 -1
- package/dist/features/proposals/target.d.ts +52 -0
- package/dist/features/proposals/target.js +125 -0
- package/dist/features/proposals/types.d.ts +49 -0
- package/dist/features/proposals/types.js +1 -0
- package/dist/features/proposals/validation.d.ts +12 -0
- package/dist/features/proposals/validation.js +26 -0
- package/dist/features/providers/requestContext.d.ts +53 -0
- package/dist/features/providers/requestContext.js +90 -0
- package/dist/{ai/providerRuntime.d.ts → features/providers/runtime.d.ts} +1 -1
- package/dist/{ai/providerRuntime.js → features/providers/runtime.js} +1 -1
- package/dist/features/schema/context.d.ts +7 -0
- package/dist/features/schema/context.js +11 -0
- package/dist/{payload/schemaContext.d.ts → features/schema/description.d.ts} +6 -33
- package/dist/{payload/schemaContext.js → features/schema/description.js} +3 -207
- package/dist/{payload/toolFieldSelection.d.ts → features/schema/fieldSelection.d.ts} +1 -1
- package/dist/{payload/toolFieldSelection.js → features/schema/fieldSelection.js} +2 -2
- package/dist/features/schema/mentionDocuments.d.ts +15 -0
- package/dist/features/schema/mentionDocuments.js +50 -0
- package/dist/features/schema/mentions.d.ts +18 -0
- package/dist/features/schema/mentions.js +100 -0
- package/dist/{payload/normalizeData.js → features/schema/normalize.js} +1 -1
- package/dist/features/schema/registry.d.ts +30 -0
- package/dist/features/schema/registry.js +73 -0
- package/dist/{payload → features/schema}/toolSchemas.d.ts +1 -1
- package/dist/{payload → features/schema}/toolSchemas.js +1 -1
- package/dist/{ai → features}/sensitiveData.js +1 -1
- package/dist/{ai → features}/tokenUsage.d.ts +1 -2
- package/dist/handlers/apply/audit.d.ts +42 -0
- package/dist/handlers/apply/audit.js +150 -0
- package/dist/handlers/apply/operations.d.ts +18 -0
- package/dist/handlers/apply/operations.js +377 -0
- package/dist/handlers/apply/types.d.ts +33 -0
- package/dist/handlers/apply/types.js +1 -0
- package/dist/handlers/apply/validation.d.ts +45 -0
- package/dist/handlers/apply/validation.js +125 -0
- package/dist/handlers/applyActionHandler.d.ts +1 -6
- package/dist/handlers/applyActionHandler.js +13 -757
- package/dist/handlers/auditLogHandler.js +4 -8
- package/dist/handlers/chat/intent.d.ts +32 -0
- package/dist/handlers/chat/intent.js +155 -0
- package/dist/handlers/chat/prompt.d.ts +20 -0
- package/dist/handlers/chat/prompt.js +55 -0
- package/dist/handlers/chat/proposalTools.d.ts +96 -0
- package/dist/handlers/chat/proposalTools.js +451 -0
- package/dist/handlers/chat/streaming.d.ts +38 -0
- package/dist/handlers/chat/streaming.js +136 -0
- package/dist/handlers/chat/toolFactory.d.ts +79 -0
- package/dist/handlers/chat/toolFactory.js +156 -0
- package/dist/handlers/chat/types.d.ts +57 -0
- package/dist/handlers/chat/types.js +1 -0
- package/dist/handlers/chatHandler.d.ts +1 -44
- package/dist/handlers/chatHandler.js +186 -1162
- package/dist/handlers/generateFieldHandler.d.ts +3 -8
- package/dist/handlers/generateFieldHandler.js +71 -99
- package/dist/handlers/http.d.ts +17 -0
- package/dist/handlers/http.js +21 -0
- package/dist/handlers/mediaUploadHandler.js +12 -34
- package/dist/handlers/mentionSuggestionHandler.d.ts +1 -1
- package/dist/handlers/mentionSuggestionHandler.js +6 -10
- package/dist/handlers/proposalDiffHandler.d.ts +1 -1
- package/dist/handlers/proposalDiffHandler.js +72 -195
- package/dist/handlers/translateDocumentHandler.d.ts +3 -8
- package/dist/handlers/translateDocumentHandler.js +26 -82
- package/dist/index.d.ts +4 -30
- package/dist/index.js +31 -421
- package/dist/plugin/admin.d.ts +30 -0
- package/dist/plugin/admin.js +155 -0
- package/dist/plugin/collections.d.ts +3 -0
- package/dist/plugin/collections.js +169 -0
- package/dist/plugin/endpoints.d.ts +23 -0
- package/dist/plugin/endpoints.js +84 -0
- package/dist/plugin/options.d.ts +4 -0
- package/dist/plugin/options.js +13 -0
- package/dist/plugin/types.d.ts +28 -0
- package/dist/plugin/types.js +1 -0
- package/dist/{payload/shared.d.ts → utils/data.d.ts} +2 -12
- package/package.json +1 -1
- package/dist/payload/proposalData.js +0 -578
- /package/dist/{payload → features}/collectionPermissions.d.ts +0 -0
- /package/dist/{payload → features/content}/documentTranslation.d.ts +0 -0
- /package/dist/{payload → features/content}/documentTranslation.js +0 -0
- /package/dist/{ai → features}/promptCaching.js +0 -0
- /package/dist/{ai/proposalRepair.js → features/proposals/repair.js} +0 -0
- /package/dist/{ai/providerOptions.d.ts → features/providers/options.d.ts} +0 -0
- /package/dist/{ai/providerOptions.js → features/providers/options.js} +0 -0
- /package/dist/{payload/normalizeData.d.ts → features/schema/normalize.d.ts} +0 -0
- /package/dist/{ai → features}/sensitiveData.d.ts +0 -0
- /package/dist/{ai → features}/tokenUsage.js +0 -0
- /package/dist/{payload/shared.js → utils/data.js} +0 -0
- /package/dist/{payload → utils}/logging.d.ts +0 -0
- /package/dist/{payload → utils}/logging.js +0 -0
|
@@ -1,285 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { isCollectionActionAllowed } from "../payload/collectionPermissions.js";
|
|
6
|
-
import { getDefaultLocale, getJSONLineKey, getOptionalNumber, hasLocalizedData, isActionProposal, isKnownGlobal, isRecord, mergeData } from "../payload/shared.js";
|
|
7
|
-
import { getLogPreview, logHandlerEvent } from "../payload/logging.js";
|
|
8
|
-
const getProposalLogSummary = (proposal)=>({
|
|
9
|
-
action: proposal.action,
|
|
10
|
-
collection: "collection" in proposal ? proposal.collection : undefined,
|
|
11
|
-
hasData: "data" in proposal && Boolean(proposal.data),
|
|
12
|
-
hasLocalizedData: "localizedData" in proposal && Boolean(proposal.localizedData),
|
|
13
|
-
id: "id" in proposal ? proposal.id : undefined,
|
|
14
|
-
label: proposal.label,
|
|
15
|
-
locale: proposal.locale,
|
|
16
|
-
locales: "localizedData" in proposal && proposal.localizedData ? Object.keys(proposal.localizedData) : undefined,
|
|
17
|
-
slug: "slug" in proposal ? proposal.slug : undefined
|
|
18
|
-
});
|
|
19
|
-
const getProposalMeta = (proposal)=>{
|
|
20
|
-
if (!proposal) return {};
|
|
21
|
-
return {
|
|
22
|
-
action: proposal.action,
|
|
23
|
-
collection: "collection" in proposal ? proposal.collection : undefined,
|
|
24
|
-
id: "id" in proposal ? proposal.id : undefined,
|
|
25
|
-
slug: "slug" in proposal ? proposal.slug : undefined
|
|
26
|
-
};
|
|
27
|
-
};
|
|
28
|
-
const createApplyDebugPayload = ({ details, phase, proposal, reason })=>({
|
|
29
|
-
...getProposalMeta(proposal),
|
|
30
|
-
details,
|
|
31
|
-
phase,
|
|
32
|
-
reason
|
|
33
|
-
});
|
|
34
|
-
const getAppliedDocReference = (doc)=>{
|
|
35
|
-
return doc?.id === undefined ? undefined : {
|
|
36
|
-
id: doc.id
|
|
37
|
-
};
|
|
38
|
-
};
|
|
39
|
-
const isAllowedCollection = (req, collection, collections, action = "read")=>{
|
|
40
|
-
return isCollectionActionAllowed({
|
|
41
|
-
action,
|
|
42
|
-
permissions: collections,
|
|
43
|
-
req,
|
|
44
|
-
slug: collection
|
|
45
|
-
});
|
|
46
|
-
};
|
|
47
|
-
const countDiffChanges = ({ after, before })=>{
|
|
48
|
-
const beforeLines = JSON.stringify(before, null, 2).split("\n");
|
|
49
|
-
const afterLines = JSON.stringify(after, null, 2).split("\n");
|
|
50
|
-
const dp = Array.from({
|
|
51
|
-
length: beforeLines.length + 1
|
|
52
|
-
}, ()=>Array(afterLines.length + 1).fill(0));
|
|
53
|
-
let additions = 0;
|
|
54
|
-
let removals = 0;
|
|
55
|
-
for(let i = beforeLines.length - 1; i >= 0; i -= 1){
|
|
56
|
-
for(let j = afterLines.length - 1; j >= 0; j -= 1){
|
|
57
|
-
dp[i][j] = beforeLines[i] === afterLines[j] ? dp[i + 1][j + 1] + 1 : Math.max(dp[i + 1][j], dp[i][j + 1]);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
let beforeIndex = 0;
|
|
61
|
-
let afterIndex = 0;
|
|
62
|
-
while(beforeIndex < beforeLines.length && afterIndex < afterLines.length){
|
|
63
|
-
if (beforeLines[beforeIndex] === afterLines[afterIndex]) {
|
|
64
|
-
beforeIndex += 1;
|
|
65
|
-
afterIndex += 1;
|
|
66
|
-
continue;
|
|
67
|
-
}
|
|
68
|
-
const beforeKey = getJSONLineKey(beforeLines[beforeIndex]);
|
|
69
|
-
const afterKey = getJSONLineKey(afterLines[afterIndex]);
|
|
70
|
-
if (beforeKey && beforeKey === afterKey) {
|
|
71
|
-
removals += 1;
|
|
72
|
-
additions += 1;
|
|
73
|
-
beforeIndex += 1;
|
|
74
|
-
afterIndex += 1;
|
|
75
|
-
continue;
|
|
76
|
-
}
|
|
77
|
-
if (dp[beforeIndex + 1][afterIndex] >= dp[beforeIndex][afterIndex + 1]) {
|
|
78
|
-
removals += 1;
|
|
79
|
-
beforeIndex += 1;
|
|
80
|
-
} else {
|
|
81
|
-
additions += 1;
|
|
82
|
-
afterIndex += 1;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
removals += beforeLines.length - beforeIndex;
|
|
86
|
-
additions += afterLines.length - afterIndex;
|
|
87
|
-
return {
|
|
88
|
-
additions,
|
|
89
|
-
removals
|
|
90
|
-
};
|
|
91
|
-
};
|
|
92
|
-
const getTargetURL = ({ documentID, proposal, req })=>{
|
|
93
|
-
const adminRoute = req.payload.config.routes.admin || "/admin";
|
|
94
|
-
if (proposal.action === "updateGlobal") return `${adminRoute}/globals/${proposal.slug}`;
|
|
95
|
-
if (proposal.action === "delete") return null;
|
|
96
|
-
const id = proposal.action === "create" ? documentID : proposal.id;
|
|
97
|
-
if (!id) return null;
|
|
98
|
-
return `${adminRoute}/collections/${proposal.collection}/${id}`;
|
|
99
|
-
};
|
|
100
|
-
const getUserID = (req)=>{
|
|
101
|
-
const user = req.user;
|
|
102
|
-
return user?.id === undefined ? undefined : String(user.id);
|
|
103
|
-
};
|
|
104
|
-
const getUserLabel = (req)=>{
|
|
105
|
-
const user = req.user;
|
|
106
|
-
if (typeof user?.email === "string" && user.email) return user.email;
|
|
107
|
-
if (typeof user?.name === "string" && user.name) return user.name;
|
|
108
|
-
if (typeof user?.username === "string" && user.username) return user.username;
|
|
109
|
-
if (user?.id !== undefined) return String(user.id);
|
|
110
|
-
return null;
|
|
111
|
-
};
|
|
112
|
-
const logAIChange = async ({ changeLogCollection, context, target, proposal, req })=>{
|
|
113
|
-
if (!changeLogCollection) return null;
|
|
114
|
-
const before = redactSensitiveData(target.before);
|
|
115
|
-
const after = redactSensitiveData(target.after);
|
|
116
|
-
const { additions, removals } = countDiffChanges({
|
|
117
|
-
after,
|
|
118
|
-
before
|
|
119
|
-
});
|
|
120
|
-
const proposalForLog = {
|
|
121
|
-
...proposal
|
|
122
|
-
};
|
|
123
|
-
delete proposalForLog._aiSignature;
|
|
124
|
-
try {
|
|
125
|
-
await req.payload.create({
|
|
126
|
-
collection: changeLogCollection,
|
|
127
|
-
data: {
|
|
128
|
-
action: proposal.action,
|
|
129
|
-
additions,
|
|
130
|
-
after,
|
|
131
|
-
aiResponse: context?.aiResponse,
|
|
132
|
-
before,
|
|
133
|
-
collection: "collection" in proposal ? proposal.collection : undefined,
|
|
134
|
-
documentID: target.documentID === undefined ? undefined : String(target.documentID),
|
|
135
|
-
inputTokens: context?.tokenUsage?.inputTokens,
|
|
136
|
-
outputTokens: context?.tokenUsage?.outputTokens,
|
|
137
|
-
prompt: context?.prompt,
|
|
138
|
-
proposal: redactSensitiveData(proposalForLog),
|
|
139
|
-
removals,
|
|
140
|
-
slug: "slug" in proposal ? proposal.slug : undefined,
|
|
141
|
-
totalTokens: context?.tokenUsage?.totalTokens,
|
|
142
|
-
targetType: proposal.action === "updateGlobal" ? "global" : "collection",
|
|
143
|
-
targetURL: getTargetURL({
|
|
144
|
-
documentID: target.documentID,
|
|
145
|
-
proposal,
|
|
146
|
-
req
|
|
147
|
-
}),
|
|
148
|
-
title: proposal.label,
|
|
149
|
-
userID: getUserID(req),
|
|
150
|
-
userLabel: getUserLabel(req)
|
|
151
|
-
},
|
|
152
|
-
overrideAccess: true,
|
|
153
|
-
req
|
|
154
|
-
});
|
|
155
|
-
return {
|
|
156
|
-
action: proposal.action,
|
|
157
|
-
additions,
|
|
158
|
-
after,
|
|
159
|
-
aiResponse: context?.aiResponse || null,
|
|
160
|
-
before,
|
|
161
|
-
collection: "collection" in proposal ? proposal.collection : null,
|
|
162
|
-
createdAt: new Date().toISOString(),
|
|
163
|
-
documentID: target.documentID === undefined ? null : String(target.documentID),
|
|
164
|
-
inputTokens: context?.tokenUsage?.inputTokens ?? null,
|
|
165
|
-
outputTokens: context?.tokenUsage?.outputTokens ?? null,
|
|
166
|
-
prompt: context?.prompt || null,
|
|
167
|
-
removals,
|
|
168
|
-
slug: "slug" in proposal ? proposal.slug : null,
|
|
169
|
-
totalTokens: context?.tokenUsage?.totalTokens ?? null,
|
|
170
|
-
targetType: proposal.action === "updateGlobal" ? "global" : "collection",
|
|
171
|
-
title: proposal.label,
|
|
172
|
-
userID: getUserID(req) || null,
|
|
173
|
-
userLabel: getUserLabel(req),
|
|
174
|
-
url: getTargetURL({
|
|
175
|
-
documentID: target.documentID,
|
|
176
|
-
proposal,
|
|
177
|
-
req
|
|
178
|
-
})
|
|
179
|
-
};
|
|
180
|
-
} catch (err) {
|
|
181
|
-
req.payload.logger.error({
|
|
182
|
-
err,
|
|
183
|
-
msg: "AI change log entry could not be written",
|
|
184
|
-
proposal: getProposalMeta(proposal)
|
|
185
|
-
});
|
|
186
|
-
return null;
|
|
187
|
-
}
|
|
188
|
-
};
|
|
1
|
+
import { getLogPreview, logHandlerEvent } from "../utils/logging.js";
|
|
2
|
+
import { isRecord } from "../utils/data.js";
|
|
3
|
+
import { applyActionProposal } from "./apply/operations.js";
|
|
4
|
+
import { createApplyDebugPayload, createApplyFailureResponse, getProposalLogSummary, getProposalMeta, resolveApplyRequest } from "./apply/validation.js";
|
|
189
5
|
export const createApplyActionHandler = (options = {})=>async (req)=>{
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
proposal: getProposalMeta(proposal)
|
|
195
|
-
});
|
|
196
|
-
return Response.json({
|
|
197
|
-
debug,
|
|
198
|
-
error
|
|
199
|
-
}, {
|
|
200
|
-
status
|
|
201
|
-
});
|
|
202
|
-
};
|
|
203
|
-
if (!req.user) {
|
|
204
|
-
return failApply({
|
|
205
|
-
debug: createApplyDebugPayload({
|
|
206
|
-
phase: "authorization",
|
|
207
|
-
reason: "unauthorized"
|
|
208
|
-
}),
|
|
209
|
-
error: "Unauthorized",
|
|
210
|
-
logMessage: "AI apply blocked: unauthorized request",
|
|
211
|
-
status: 401
|
|
212
|
-
});
|
|
213
|
-
}
|
|
214
|
-
const body = req.json ? await req.json().catch(()=>null) : null;
|
|
215
|
-
const proposal = body?.proposal;
|
|
216
|
-
if (!proposal) {
|
|
217
|
-
return failApply({
|
|
218
|
-
debug: createApplyDebugPayload({
|
|
219
|
-
phase: "apply_validation",
|
|
220
|
-
reason: "missing_proposal"
|
|
221
|
-
}),
|
|
222
|
-
error: "Proposal is required",
|
|
223
|
-
logMessage: "AI apply blocked: missing proposal payload"
|
|
224
|
-
});
|
|
225
|
-
}
|
|
226
|
-
if (!verifyActionProposal(proposal)) {
|
|
227
|
-
return failApply({
|
|
228
|
-
debug: createApplyDebugPayload({
|
|
229
|
-
phase: "apply_validation",
|
|
230
|
-
proposal,
|
|
231
|
-
reason: "invalid_signature"
|
|
232
|
-
}),
|
|
233
|
-
error: "Proposal signature is invalid or expired.",
|
|
234
|
-
logMessage: "AI apply blocked: invalid proposal signature",
|
|
235
|
-
proposal
|
|
236
|
-
});
|
|
237
|
-
}
|
|
238
|
-
if (!isActionProposal(proposal)) {
|
|
239
|
-
return failApply({
|
|
240
|
-
debug: createApplyDebugPayload({
|
|
241
|
-
phase: "apply_validation",
|
|
242
|
-
proposal,
|
|
243
|
-
reason: "invalid_proposal_shape"
|
|
244
|
-
}),
|
|
245
|
-
error: "Proposal is invalid.",
|
|
246
|
-
logMessage: "AI apply blocked: invalid proposal shape",
|
|
247
|
-
proposal
|
|
248
|
-
});
|
|
249
|
-
}
|
|
250
|
-
if ("data" in proposal && proposal.data && containsSensitiveData(proposal.data)) {
|
|
251
|
-
return failApply({
|
|
252
|
-
debug: createApplyDebugPayload({
|
|
253
|
-
phase: "apply_validation",
|
|
254
|
-
proposal,
|
|
255
|
-
reason: "sensitive_data_in_data"
|
|
256
|
-
}),
|
|
257
|
-
error: "Proposal contains sensitive fields and cannot be applied.",
|
|
258
|
-
logMessage: "AI apply blocked: sensitive data detected in proposal data",
|
|
259
|
-
proposal
|
|
260
|
-
});
|
|
261
|
-
}
|
|
262
|
-
if (hasLocalizedData(proposal) && Object.values(proposal.localizedData).some((value)=>containsSensitiveData(value))) {
|
|
263
|
-
return failApply({
|
|
264
|
-
debug: createApplyDebugPayload({
|
|
265
|
-
phase: "apply_validation",
|
|
266
|
-
proposal,
|
|
267
|
-
reason: "sensitive_data_in_localized_data"
|
|
268
|
-
}),
|
|
269
|
-
error: "Proposal contains sensitive fields and cannot be applied.",
|
|
270
|
-
logMessage: "AI apply blocked: sensitive data detected in localized proposal data",
|
|
271
|
-
proposal
|
|
272
|
-
});
|
|
273
|
-
}
|
|
274
|
-
const logContext = {
|
|
275
|
-
aiResponse: typeof body?.aiResponse === "string" && body.aiResponse.trim() ? body.aiResponse.trim() : undefined,
|
|
276
|
-
prompt: typeof body?.prompt === "string" && body.prompt.trim() ? body.prompt.trim() : undefined,
|
|
277
|
-
tokenUsage: body?.tokenUsage ? {
|
|
278
|
-
inputTokens: getOptionalNumber(body.tokenUsage.inputTokens),
|
|
279
|
-
outputTokens: getOptionalNumber(body.tokenUsage.outputTokens),
|
|
280
|
-
totalTokens: getOptionalNumber(body.tokenUsage.totalTokens)
|
|
281
|
-
} : undefined
|
|
282
|
-
};
|
|
6
|
+
const resolution = await resolveApplyRequest(req);
|
|
7
|
+
if (!resolution.ok) return createApplyFailureResponse(req, resolution.failure);
|
|
8
|
+
const { context: logContext, proposal } = resolution;
|
|
9
|
+
const failApply = (failure)=>createApplyFailureResponse(req, failure);
|
|
283
10
|
logHandlerEvent(req, "info", {
|
|
284
11
|
msg: "AI apply started",
|
|
285
12
|
promptPreview: getLogPreview(logContext.prompt),
|
|
@@ -287,483 +14,12 @@ export const createApplyActionHandler = (options = {})=>async (req)=>{
|
|
|
287
14
|
tokenUsage: logContext.tokenUsage
|
|
288
15
|
});
|
|
289
16
|
try {
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
return failApply({
|
|
295
|
-
debug: createApplyDebugPayload({
|
|
296
|
-
phase: "apply_validation",
|
|
297
|
-
proposal,
|
|
298
|
-
reason: "unknown_global"
|
|
299
|
-
}),
|
|
300
|
-
error: "Unknown global",
|
|
301
|
-
logMessage: "AI apply blocked: unknown global",
|
|
302
|
-
proposal
|
|
303
|
-
});
|
|
304
|
-
}
|
|
305
|
-
const globalConfig = req.payload.config.globals?.find((global)=>global.slug === proposal.slug);
|
|
306
|
-
if (hasLocalizedData(proposal)) {
|
|
307
|
-
const beforeByLocale = {};
|
|
308
|
-
const afterByLocale = {};
|
|
309
|
-
const globalFields = getSchemaFields({
|
|
310
|
-
fields: globalConfig?.fields || [],
|
|
311
|
-
slug: proposal.slug
|
|
312
|
-
});
|
|
313
|
-
const preparedData = prepareProposalWriteData({
|
|
314
|
-
collectionConfig: {
|
|
315
|
-
fields: globalConfig?.fields || [],
|
|
316
|
-
slug: proposal.slug
|
|
317
|
-
},
|
|
318
|
-
inferenceText,
|
|
319
|
-
label: proposal.label,
|
|
320
|
-
localizedData: proposal.localizedData,
|
|
321
|
-
mode: "update"
|
|
322
|
-
});
|
|
323
|
-
if (preparedData.issues.length > 0 || !preparedData.localizedData) {
|
|
324
|
-
return failApply({
|
|
325
|
-
debug: createApplyDebugPayload({
|
|
326
|
-
details: {
|
|
327
|
-
issues: preparedData.issues
|
|
328
|
-
},
|
|
329
|
-
phase: "apply_validation",
|
|
330
|
-
proposal,
|
|
331
|
-
reason: "invalid_global_write_shape"
|
|
332
|
-
}),
|
|
333
|
-
error: "Proposal is invalid.",
|
|
334
|
-
logMessage: "AI apply blocked: invalid global proposal data",
|
|
335
|
-
proposal
|
|
336
|
-
});
|
|
337
|
-
}
|
|
338
|
-
const defaultLocaleDoc = defaultLocale ? await req.payload.findGlobal({
|
|
339
|
-
depth: 2,
|
|
340
|
-
fallbackLocale: false,
|
|
341
|
-
locale: defaultLocale,
|
|
342
|
-
req,
|
|
343
|
-
slug: proposal.slug
|
|
344
|
-
}) : null;
|
|
345
|
-
const localizedResults = await Promise.all(Object.entries(preparedData.localizedData).map(async ([locale, localeData])=>{
|
|
346
|
-
const before = await req.payload.findGlobal({
|
|
347
|
-
depth: 2,
|
|
348
|
-
fallbackLocale: false,
|
|
349
|
-
locale,
|
|
350
|
-
req,
|
|
351
|
-
slug: proposal.slug
|
|
352
|
-
});
|
|
353
|
-
const completedData = applyLocalizedRequiredFallbackToPreparedData({
|
|
354
|
-
fallbackSource: locale === defaultLocale ? before : defaultLocaleDoc || before,
|
|
355
|
-
fields: globalFields,
|
|
356
|
-
preparedData: localeData
|
|
357
|
-
});
|
|
358
|
-
await req.payload.updateGlobal({
|
|
359
|
-
data: completedData,
|
|
360
|
-
locale,
|
|
361
|
-
overrideAccess: false,
|
|
362
|
-
req,
|
|
363
|
-
slug: proposal.slug
|
|
364
|
-
});
|
|
365
|
-
return {
|
|
366
|
-
before,
|
|
367
|
-
completedData,
|
|
368
|
-
locale
|
|
369
|
-
};
|
|
370
|
-
}));
|
|
371
|
-
localizedResults.forEach(({ before, completedData, locale })=>{
|
|
372
|
-
beforeByLocale[locale] = before;
|
|
373
|
-
afterByLocale[locale] = mergeData(before, completedData);
|
|
374
|
-
});
|
|
375
|
-
const change = await logAIChange({
|
|
376
|
-
changeLogCollection: options.changeLogCollection,
|
|
377
|
-
context: logContext,
|
|
378
|
-
proposal,
|
|
379
|
-
req,
|
|
380
|
-
target: {
|
|
381
|
-
after: afterByLocale,
|
|
382
|
-
before: beforeByLocale
|
|
383
|
-
}
|
|
384
|
-
});
|
|
385
|
-
logHandlerEvent(req, "info", {
|
|
386
|
-
changeLogged: Boolean(change),
|
|
387
|
-
locales: Object.keys(proposal.localizedData),
|
|
388
|
-
msg: "AI apply succeeded",
|
|
389
|
-
proposal: getProposalLogSummary(proposal)
|
|
390
|
-
});
|
|
391
|
-
return Response.json({
|
|
392
|
-
change,
|
|
393
|
-
doc: undefined,
|
|
394
|
-
status: "applied"
|
|
395
|
-
});
|
|
396
|
-
}
|
|
397
|
-
const preparedData = prepareProposalWriteData({
|
|
398
|
-
collectionConfig: {
|
|
399
|
-
fields: globalConfig?.fields || [],
|
|
400
|
-
slug: proposal.slug
|
|
401
|
-
},
|
|
402
|
-
data: proposal.data,
|
|
403
|
-
inferenceText,
|
|
404
|
-
label: proposal.label,
|
|
405
|
-
mode: "update"
|
|
406
|
-
});
|
|
407
|
-
if (preparedData.issues.length > 0 || !preparedData.data) {
|
|
408
|
-
return failApply({
|
|
409
|
-
debug: createApplyDebugPayload({
|
|
410
|
-
details: {
|
|
411
|
-
issues: preparedData.issues
|
|
412
|
-
},
|
|
413
|
-
phase: "apply_validation",
|
|
414
|
-
proposal,
|
|
415
|
-
reason: "invalid_global_write_shape"
|
|
416
|
-
}),
|
|
417
|
-
error: "Proposal is invalid.",
|
|
418
|
-
logMessage: "AI apply blocked: invalid global proposal data",
|
|
419
|
-
proposal
|
|
420
|
-
});
|
|
421
|
-
}
|
|
422
|
-
const before = await req.payload.findGlobal({
|
|
423
|
-
depth: 2,
|
|
424
|
-
...proposal.locale ? {
|
|
425
|
-
locale: proposal.locale
|
|
426
|
-
} : {},
|
|
427
|
-
req,
|
|
428
|
-
slug: proposal.slug
|
|
429
|
-
});
|
|
430
|
-
const doc = await req.payload.updateGlobal({
|
|
431
|
-
data: preparedData.data,
|
|
432
|
-
...proposal.locale ? {
|
|
433
|
-
locale: proposal.locale
|
|
434
|
-
} : {},
|
|
435
|
-
overrideAccess: false,
|
|
436
|
-
req,
|
|
437
|
-
slug: proposal.slug
|
|
438
|
-
});
|
|
439
|
-
const change = await logAIChange({
|
|
440
|
-
changeLogCollection: options.changeLogCollection,
|
|
441
|
-
context: logContext,
|
|
442
|
-
proposal,
|
|
443
|
-
req,
|
|
444
|
-
target: {
|
|
445
|
-
after: mergeData(before, preparedData.data),
|
|
446
|
-
before
|
|
447
|
-
}
|
|
448
|
-
});
|
|
449
|
-
logHandlerEvent(req, "info", {
|
|
450
|
-
changeLogged: Boolean(change),
|
|
451
|
-
locale: proposal.locale,
|
|
452
|
-
msg: "AI apply succeeded",
|
|
453
|
-
proposal: getProposalLogSummary(proposal)
|
|
454
|
-
});
|
|
455
|
-
return Response.json({
|
|
456
|
-
change,
|
|
457
|
-
doc: getAppliedDocReference(doc),
|
|
458
|
-
status: "applied"
|
|
459
|
-
});
|
|
460
|
-
}
|
|
461
|
-
if (!isAllowedCollection(req, proposal.collection, options.collections, proposal.action)) {
|
|
462
|
-
return failApply({
|
|
463
|
-
debug: createApplyDebugPayload({
|
|
464
|
-
phase: "authorization",
|
|
465
|
-
proposal,
|
|
466
|
-
reason: "unknown_or_disallowed_collection"
|
|
467
|
-
}),
|
|
468
|
-
error: "Unknown collection",
|
|
469
|
-
logMessage: "AI apply blocked: unknown or disallowed collection",
|
|
470
|
-
proposal
|
|
471
|
-
});
|
|
472
|
-
}
|
|
473
|
-
if (proposal.action === "delete") {
|
|
474
|
-
const doc = await req.payload.delete({
|
|
475
|
-
collection: proposal.collection,
|
|
476
|
-
id: proposal.id,
|
|
477
|
-
overrideAccess: false,
|
|
478
|
-
req
|
|
479
|
-
});
|
|
480
|
-
const change = await logAIChange({
|
|
481
|
-
changeLogCollection: options.changeLogCollection,
|
|
482
|
-
context: logContext,
|
|
483
|
-
proposal,
|
|
484
|
-
req,
|
|
485
|
-
target: {
|
|
486
|
-
after: {},
|
|
487
|
-
before: doc,
|
|
488
|
-
documentID: proposal.id
|
|
489
|
-
}
|
|
490
|
-
});
|
|
491
|
-
logHandlerEvent(req, "info", {
|
|
492
|
-
changeLogged: Boolean(change),
|
|
493
|
-
documentID: proposal.id,
|
|
494
|
-
msg: "AI apply succeeded",
|
|
495
|
-
proposal: getProposalLogSummary(proposal)
|
|
496
|
-
});
|
|
497
|
-
return Response.json({
|
|
498
|
-
change,
|
|
499
|
-
doc: getAppliedDocReference(doc),
|
|
500
|
-
status: "applied"
|
|
501
|
-
});
|
|
502
|
-
}
|
|
503
|
-
const collectionConfig = req.payload.config.collections.find((collection)=>collection.slug === proposal.collection);
|
|
504
|
-
const collectionFields = getSchemaFields(collectionConfig);
|
|
505
|
-
if (hasLocalizedData(proposal)) {
|
|
506
|
-
const preparedData = prepareProposalWriteData({
|
|
507
|
-
collectionConfig,
|
|
508
|
-
inferenceText,
|
|
509
|
-
label: proposal.label,
|
|
510
|
-
localizedData: proposal.localizedData,
|
|
511
|
-
mode: proposal.action
|
|
512
|
-
});
|
|
513
|
-
if (preparedData.issues.length > 0 || !preparedData.localizedData) {
|
|
514
|
-
return failApply({
|
|
515
|
-
debug: createApplyDebugPayload({
|
|
516
|
-
details: {
|
|
517
|
-
issues: preparedData.issues
|
|
518
|
-
},
|
|
519
|
-
phase: "apply_validation",
|
|
520
|
-
proposal,
|
|
521
|
-
reason: "invalid_collection_write_shape"
|
|
522
|
-
}),
|
|
523
|
-
error: "Proposal is invalid.",
|
|
524
|
-
logMessage: "AI apply blocked: invalid collection proposal data",
|
|
525
|
-
proposal
|
|
526
|
-
});
|
|
527
|
-
}
|
|
528
|
-
if (proposal.action === "create") {
|
|
529
|
-
const localeEntries = Object.entries(preparedData.localizedData);
|
|
530
|
-
const [firstLocale, firstLocaleData] = localeEntries[0] || [];
|
|
531
|
-
if (!firstLocale) {
|
|
532
|
-
return failApply({
|
|
533
|
-
debug: createApplyDebugPayload({
|
|
534
|
-
phase: "apply_validation",
|
|
535
|
-
proposal,
|
|
536
|
-
reason: "localized_create_without_locales"
|
|
537
|
-
}),
|
|
538
|
-
error: "Proposal is invalid.",
|
|
539
|
-
logMessage: "AI apply blocked: localized create proposal has no locales",
|
|
540
|
-
proposal
|
|
541
|
-
});
|
|
542
|
-
}
|
|
543
|
-
const doc = await req.payload.create({
|
|
544
|
-
collection: proposal.collection,
|
|
545
|
-
data: firstLocaleData,
|
|
546
|
-
locale: firstLocale,
|
|
547
|
-
overrideAccess: false,
|
|
548
|
-
req
|
|
549
|
-
});
|
|
550
|
-
const beforeByLocale = {
|
|
551
|
-
[firstLocale]: {}
|
|
552
|
-
};
|
|
553
|
-
const afterByLocale = {
|
|
554
|
-
[firstLocale]: doc
|
|
555
|
-
};
|
|
556
|
-
const fallbackSource = doc;
|
|
557
|
-
const localizedResults = await Promise.all(localeEntries.slice(1).map(async ([locale, localeData])=>{
|
|
558
|
-
const completedData = applyLocalizedRequiredFallbackToPreparedData({
|
|
559
|
-
fallbackSource,
|
|
560
|
-
fields: collectionFields,
|
|
561
|
-
preparedData: localeData
|
|
562
|
-
});
|
|
563
|
-
await req.payload.update({
|
|
564
|
-
collection: proposal.collection,
|
|
565
|
-
data: completedData,
|
|
566
|
-
id: String(doc.id),
|
|
567
|
-
locale,
|
|
568
|
-
overrideAccess: false,
|
|
569
|
-
req
|
|
570
|
-
});
|
|
571
|
-
return {
|
|
572
|
-
completedData,
|
|
573
|
-
locale
|
|
574
|
-
};
|
|
575
|
-
}));
|
|
576
|
-
localizedResults.forEach(({ completedData, locale })=>{
|
|
577
|
-
beforeByLocale[locale] = {};
|
|
578
|
-
afterByLocale[locale] = completedData;
|
|
579
|
-
});
|
|
580
|
-
const change = await logAIChange({
|
|
581
|
-
changeLogCollection: options.changeLogCollection,
|
|
582
|
-
context: logContext,
|
|
583
|
-
proposal,
|
|
584
|
-
req,
|
|
585
|
-
target: {
|
|
586
|
-
after: afterByLocale,
|
|
587
|
-
before: beforeByLocale,
|
|
588
|
-
documentID: doc.id
|
|
589
|
-
}
|
|
590
|
-
});
|
|
591
|
-
logHandlerEvent(req, "info", {
|
|
592
|
-
changeLogged: Boolean(change),
|
|
593
|
-
documentID: doc.id,
|
|
594
|
-
locales: localeEntries.map(([locale])=>locale),
|
|
595
|
-
msg: "AI apply succeeded",
|
|
596
|
-
proposal: getProposalLogSummary(proposal)
|
|
597
|
-
});
|
|
598
|
-
return Response.json({
|
|
599
|
-
change,
|
|
600
|
-
doc: getAppliedDocReference(doc),
|
|
601
|
-
status: "applied"
|
|
602
|
-
});
|
|
603
|
-
}
|
|
604
|
-
const beforeByLocale = {};
|
|
605
|
-
const afterByLocale = {};
|
|
606
|
-
const defaultLocaleDoc = defaultLocale ? await req.payload.findByID({
|
|
607
|
-
collection: proposal.collection,
|
|
608
|
-
depth: 2,
|
|
609
|
-
fallbackLocale: false,
|
|
610
|
-
id: proposal.id,
|
|
611
|
-
locale: defaultLocale,
|
|
612
|
-
req
|
|
613
|
-
}) : null;
|
|
614
|
-
const localizedResults = await Promise.all(Object.entries(preparedData.localizedData).map(async ([locale, localeData])=>{
|
|
615
|
-
const before = await req.payload.findByID({
|
|
616
|
-
collection: proposal.collection,
|
|
617
|
-
depth: 2,
|
|
618
|
-
fallbackLocale: false,
|
|
619
|
-
id: proposal.id,
|
|
620
|
-
locale,
|
|
621
|
-
req
|
|
622
|
-
});
|
|
623
|
-
const completedData = applyLocalizedRequiredFallbackToPreparedData({
|
|
624
|
-
fallbackSource: locale === defaultLocale ? before : defaultLocaleDoc || before,
|
|
625
|
-
fields: collectionFields,
|
|
626
|
-
preparedData: localeData
|
|
627
|
-
});
|
|
628
|
-
await req.payload.update({
|
|
629
|
-
collection: proposal.collection,
|
|
630
|
-
data: completedData,
|
|
631
|
-
id: proposal.id,
|
|
632
|
-
locale,
|
|
633
|
-
overrideAccess: false,
|
|
634
|
-
req
|
|
635
|
-
});
|
|
636
|
-
return {
|
|
637
|
-
before,
|
|
638
|
-
completedData,
|
|
639
|
-
locale
|
|
640
|
-
};
|
|
641
|
-
}));
|
|
642
|
-
localizedResults.forEach(({ before, completedData, locale })=>{
|
|
643
|
-
beforeByLocale[locale] = before;
|
|
644
|
-
afterByLocale[locale] = mergeData(before, completedData);
|
|
645
|
-
});
|
|
646
|
-
const change = await logAIChange({
|
|
647
|
-
changeLogCollection: options.changeLogCollection,
|
|
648
|
-
context: logContext,
|
|
649
|
-
proposal,
|
|
650
|
-
req,
|
|
651
|
-
target: {
|
|
652
|
-
after: afterByLocale,
|
|
653
|
-
before: beforeByLocale,
|
|
654
|
-
documentID: proposal.id
|
|
655
|
-
}
|
|
656
|
-
});
|
|
657
|
-
logHandlerEvent(req, "info", {
|
|
658
|
-
changeLogged: Boolean(change),
|
|
659
|
-
documentID: proposal.id,
|
|
660
|
-
locales: Object.keys(proposal.localizedData),
|
|
661
|
-
msg: "AI apply succeeded",
|
|
662
|
-
proposal: getProposalLogSummary(proposal)
|
|
663
|
-
});
|
|
664
|
-
return Response.json({
|
|
665
|
-
change,
|
|
666
|
-
doc: {
|
|
667
|
-
id: proposal.id
|
|
668
|
-
},
|
|
669
|
-
status: "applied"
|
|
670
|
-
});
|
|
671
|
-
}
|
|
672
|
-
const preparedData = prepareProposalWriteData({
|
|
673
|
-
collectionConfig,
|
|
674
|
-
data: proposal.data,
|
|
675
|
-
inferenceText,
|
|
676
|
-
label: proposal.label,
|
|
677
|
-
mode: proposal.action
|
|
678
|
-
});
|
|
679
|
-
if (preparedData.issues.length > 0 || !preparedData.data) {
|
|
680
|
-
return failApply({
|
|
681
|
-
debug: createApplyDebugPayload({
|
|
682
|
-
details: {
|
|
683
|
-
issues: preparedData.issues
|
|
684
|
-
},
|
|
685
|
-
phase: "apply_validation",
|
|
686
|
-
proposal,
|
|
687
|
-
reason: "invalid_collection_write_shape"
|
|
688
|
-
}),
|
|
689
|
-
error: "Proposal is invalid.",
|
|
690
|
-
logMessage: "AI apply blocked: invalid collection proposal data",
|
|
691
|
-
proposal
|
|
692
|
-
});
|
|
693
|
-
}
|
|
694
|
-
if (proposal.action === "create") {
|
|
695
|
-
const doc = await req.payload.create({
|
|
696
|
-
collection: proposal.collection,
|
|
697
|
-
data: preparedData.data,
|
|
698
|
-
...proposal.locale ? {
|
|
699
|
-
locale: proposal.locale
|
|
700
|
-
} : {},
|
|
701
|
-
overrideAccess: false,
|
|
702
|
-
req
|
|
703
|
-
});
|
|
704
|
-
const change = await logAIChange({
|
|
705
|
-
changeLogCollection: options.changeLogCollection,
|
|
706
|
-
context: logContext,
|
|
707
|
-
proposal,
|
|
708
|
-
req,
|
|
709
|
-
target: {
|
|
710
|
-
after: doc,
|
|
711
|
-
before: {},
|
|
712
|
-
documentID: doc.id
|
|
713
|
-
}
|
|
714
|
-
});
|
|
715
|
-
logHandlerEvent(req, "info", {
|
|
716
|
-
changeLogged: Boolean(change),
|
|
717
|
-
documentID: doc.id,
|
|
718
|
-
msg: "AI apply succeeded",
|
|
719
|
-
proposal: getProposalLogSummary(proposal)
|
|
720
|
-
});
|
|
721
|
-
return Response.json({
|
|
722
|
-
change,
|
|
723
|
-
doc: getAppliedDocReference(doc),
|
|
724
|
-
status: "applied"
|
|
725
|
-
});
|
|
726
|
-
}
|
|
727
|
-
const before = await req.payload.findByID({
|
|
728
|
-
collection: proposal.collection,
|
|
729
|
-
depth: 2,
|
|
730
|
-
id: proposal.id,
|
|
731
|
-
...proposal.locale ? {
|
|
732
|
-
locale: proposal.locale
|
|
733
|
-
} : {},
|
|
734
|
-
req
|
|
735
|
-
});
|
|
736
|
-
const doc = await req.payload.update({
|
|
737
|
-
collection: proposal.collection,
|
|
738
|
-
data: preparedData.data,
|
|
739
|
-
id: proposal.id,
|
|
740
|
-
...proposal.locale ? {
|
|
741
|
-
locale: proposal.locale
|
|
742
|
-
} : {},
|
|
743
|
-
overrideAccess: false,
|
|
744
|
-
req
|
|
745
|
-
});
|
|
746
|
-
const change = await logAIChange({
|
|
747
|
-
changeLogCollection: options.changeLogCollection,
|
|
748
|
-
context: logContext,
|
|
17
|
+
return await applyActionProposal({
|
|
18
|
+
failApply,
|
|
19
|
+
logContext,
|
|
20
|
+
options,
|
|
749
21
|
proposal,
|
|
750
|
-
req
|
|
751
|
-
target: {
|
|
752
|
-
after: mergeData(before, preparedData.data),
|
|
753
|
-
before,
|
|
754
|
-
documentID: proposal.id
|
|
755
|
-
}
|
|
756
|
-
});
|
|
757
|
-
logHandlerEvent(req, "info", {
|
|
758
|
-
changeLogged: Boolean(change),
|
|
759
|
-
documentID: proposal.id,
|
|
760
|
-
msg: "AI apply succeeded",
|
|
761
|
-
proposal: getProposalLogSummary(proposal)
|
|
762
|
-
});
|
|
763
|
-
return Response.json({
|
|
764
|
-
change,
|
|
765
|
-
doc: getAppliedDocReference(doc),
|
|
766
|
-
status: "applied"
|
|
22
|
+
req
|
|
767
23
|
});
|
|
768
24
|
} catch (err) {
|
|
769
25
|
const debug = createApplyDebugPayload({
|