@mvriu5/payload-ai 1.1.1 → 1.2.0

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.
@@ -1,15 +1,9 @@
1
1
  import { verifyActionProposal } from "../ai/proposalSigning.js";
2
2
  import { redactSensitiveData } from "../ai/sensitiveData.js";
3
- import { getCollectionFields, getLocalizedRequiredFallbackData, normalizeAuthData, normalizeDataForFields } from "../payload/normalizeData.js";
3
+ import { getSchemaFields } from "../payload/normalizeData.js";
4
+ import { applyLocalizedRequiredFallbackToPreparedData, prepareProposalWriteData } from "../payload/proposalData.js";
4
5
  import { isCollectionActionAllowed } from "../payload/collectionPermissions.js";
5
6
  import { getDefaultLocale, hasLocalizedData, isActionProposal, mergeData } from "../payload/shared.js";
6
- const applyLocalizedRequiredFallback = ({ data, fallbackSource, fields })=>{
7
- return mergeData(getLocalizedRequiredFallbackData({
8
- fields,
9
- source: fallbackSource,
10
- target: data
11
- }), data);
12
- };
13
7
  export const createProposalDiffHandler = (options = {})=>async (req)=>{
14
8
  if (!req.user) return Response.json({
15
9
  error: "Unauthorized"
@@ -34,6 +28,7 @@ export const createProposalDiffHandler = (options = {})=>async (req)=>{
34
28
  status: 400
35
29
  });
36
30
  try {
31
+ const inferenceText = body?.prompt;
37
32
  const defaultLocale = getDefaultLocale(req);
38
33
  if (proposal.action === "updateGlobal") {
39
34
  const globalConfig = req.payload.config.globals?.find((global)=>global.slug === proposal.slug);
@@ -45,7 +40,27 @@ export const createProposalDiffHandler = (options = {})=>async (req)=>{
45
40
  if (hasLocalizedData(proposal)) {
46
41
  const beforeByLocale = {};
47
42
  const afterByLocale = {};
48
- const globalFields = globalConfig.fields || [];
43
+ const globalFields = getSchemaFields({
44
+ fields: globalConfig.fields || [],
45
+ slug: proposal.slug
46
+ });
47
+ const preparedData = prepareProposalWriteData({
48
+ collectionConfig: {
49
+ fields: globalConfig.fields || [],
50
+ slug: proposal.slug
51
+ },
52
+ inferenceText,
53
+ label: proposal.label,
54
+ localizedData: proposal.localizedData,
55
+ mode: "update"
56
+ });
57
+ if (preparedData.issues.length > 0 || !preparedData.localizedData) {
58
+ return Response.json({
59
+ error: "Proposal is invalid."
60
+ }, {
61
+ status: 400
62
+ });
63
+ }
49
64
  const defaultLocaleDoc = defaultLocale ? await req.payload.findGlobal({
50
65
  depth: 2,
51
66
  fallbackLocale: false,
@@ -54,8 +69,7 @@ export const createProposalDiffHandler = (options = {})=>async (req)=>{
54
69
  req,
55
70
  slug: proposal.slug
56
71
  }) : null;
57
- for (const [locale, localeData] of Object.entries(proposal.localizedData)){
58
- const normalized = normalizeDataForFields(globalFields, localeData);
72
+ for (const [locale, localeData] of Object.entries(preparedData.localizedData)){
59
73
  const doc = await req.payload.findGlobal({
60
74
  depth: 2,
61
75
  fallbackLocale: false,
@@ -64,10 +78,10 @@ export const createProposalDiffHandler = (options = {})=>async (req)=>{
64
78
  req,
65
79
  slug: proposal.slug
66
80
  });
67
- const completedData = applyLocalizedRequiredFallback({
68
- data: normalized.data,
81
+ const completedData = applyLocalizedRequiredFallbackToPreparedData({
69
82
  fallbackSource: locale === defaultLocale ? doc : defaultLocaleDoc || doc,
70
- fields: globalFields
83
+ fields: globalFields,
84
+ preparedData: localeData
71
85
  });
72
86
  beforeByLocale[locale] = redactSensitiveData(doc);
73
87
  afterByLocale[locale] = redactSensitiveData(mergeData(doc, completedData));
@@ -77,7 +91,23 @@ export const createProposalDiffHandler = (options = {})=>async (req)=>{
77
91
  before: beforeByLocale
78
92
  });
79
93
  }
80
- const normalized = normalizeDataForFields(globalConfig.fields || [], proposal.data);
94
+ const preparedData = prepareProposalWriteData({
95
+ collectionConfig: {
96
+ fields: globalConfig.fields || [],
97
+ slug: proposal.slug
98
+ },
99
+ data: proposal.data,
100
+ inferenceText,
101
+ label: proposal.label,
102
+ mode: "update"
103
+ });
104
+ if (preparedData.issues.length > 0 || !preparedData.data) {
105
+ return Response.json({
106
+ error: "Proposal is invalid."
107
+ }, {
108
+ status: 400
109
+ });
110
+ }
81
111
  const doc = await req.payload.findGlobal({
82
112
  depth: 2,
83
113
  ...proposal.locale ? {
@@ -88,7 +118,7 @@ export const createProposalDiffHandler = (options = {})=>async (req)=>{
88
118
  slug: proposal.slug
89
119
  });
90
120
  return Response.json({
91
- after: redactSensitiveData(mergeData(doc, normalized.data)),
121
+ after: redactSensitiveData(mergeData(doc, preparedData.data)),
92
122
  before: redactSensitiveData(doc)
93
123
  });
94
124
  }
@@ -119,9 +149,22 @@ export const createProposalDiffHandler = (options = {})=>async (req)=>{
119
149
  });
120
150
  }
121
151
  const collectionConfig = req.payload.config.collections.find((collection)=>collection.slug === proposal.collection);
122
- const collectionFields = getCollectionFields(collectionConfig);
123
- const normalizeCollectionData = (data)=>normalizeAuthData(collectionConfig, normalizeDataForFields(collectionFields, data));
152
+ const collectionFields = getSchemaFields(collectionConfig);
124
153
  if (hasLocalizedData(proposal)) {
154
+ const preparedData = prepareProposalWriteData({
155
+ collectionConfig,
156
+ inferenceText,
157
+ label: proposal.label,
158
+ localizedData: proposal.localizedData,
159
+ mode: proposal.action
160
+ });
161
+ if (preparedData.issues.length > 0 || !preparedData.localizedData) {
162
+ return Response.json({
163
+ error: "Proposal is invalid."
164
+ }, {
165
+ status: 400
166
+ });
167
+ }
125
168
  const afterByLocale = {};
126
169
  const beforeByLocale = {};
127
170
  const defaultLocaleDoc = proposal.action === "update" && defaultLocale ? await req.payload.findByID({
@@ -134,8 +177,7 @@ export const createProposalDiffHandler = (options = {})=>async (req)=>{
134
177
  req
135
178
  }) : null;
136
179
  let createFallbackSource = null;
137
- for (const [locale, localeData] of Object.entries(proposal.localizedData)){
138
- const normalized = normalizeCollectionData(localeData);
180
+ for (const [locale, localeData] of Object.entries(preparedData.localizedData)){
139
181
  const fallbackSource = proposal.action === "create" ? createFallbackSource || {} : locale === defaultLocale ? await req.payload.findByID({
140
182
  collection: proposal.collection,
141
183
  depth: 2,
@@ -145,10 +187,10 @@ export const createProposalDiffHandler = (options = {})=>async (req)=>{
145
187
  overrideAccess: false,
146
188
  req
147
189
  }) : defaultLocaleDoc || {};
148
- const completedData = applyLocalizedRequiredFallback({
149
- data: normalized.data,
190
+ const completedData = applyLocalizedRequiredFallbackToPreparedData({
150
191
  fallbackSource,
151
- fields: collectionFields
192
+ fields: collectionFields,
193
+ preparedData: localeData
152
194
  });
153
195
  if (proposal.action === "create") {
154
196
  beforeByLocale[locale] = {};
@@ -173,10 +215,23 @@ export const createProposalDiffHandler = (options = {})=>async (req)=>{
173
215
  before: beforeByLocale
174
216
  });
175
217
  }
176
- const normalized = normalizeCollectionData(proposal.data);
218
+ const preparedData = prepareProposalWriteData({
219
+ collectionConfig,
220
+ data: proposal.data,
221
+ inferenceText,
222
+ label: proposal.label,
223
+ mode: proposal.action
224
+ });
225
+ if (preparedData.issues.length > 0 || !preparedData.data) {
226
+ return Response.json({
227
+ error: "Proposal is invalid."
228
+ }, {
229
+ status: 400
230
+ });
231
+ }
177
232
  if (proposal.action === "create") {
178
233
  return Response.json({
179
- after: redactSensitiveData(normalized.data),
234
+ after: redactSensitiveData(preparedData.data),
180
235
  before: {}
181
236
  });
182
237
  }
@@ -191,7 +246,7 @@ export const createProposalDiffHandler = (options = {})=>async (req)=>{
191
246
  req
192
247
  });
193
248
  return Response.json({
194
- after: redactSensitiveData(mergeData(doc, normalized.data)),
249
+ after: redactSensitiveData(mergeData(doc, preparedData.data)),
195
250
  before: redactSensitiveData(doc)
196
251
  });
197
252
  } catch (err) {
@@ -0,0 +1,9 @@
1
+ import type { PayloadHandler } from "payload";
2
+ type LogLevel = "error" | "info" | "warn";
3
+ type LogEntry = {
4
+ [key: string]: unknown;
5
+ msg: string;
6
+ };
7
+ export declare const getLogPreview: (value?: string | null) => string | null;
8
+ export declare const logHandlerEvent: (req: Parameters<PayloadHandler>[0], level: LogLevel, entry: LogEntry) => void;
9
+ export {};
@@ -0,0 +1,14 @@
1
+ const maxPreviewLength = 180;
2
+ export const getLogPreview = (value)=>{
3
+ if (!value) return null;
4
+ const trimmed = value.trim();
5
+ if (!trimmed) return null;
6
+ if (trimmed.length <= maxPreviewLength) return trimmed;
7
+ return `${trimmed.slice(0, maxPreviewLength).trim()}...`;
8
+ };
9
+ export const logHandlerEvent = (req, level, entry)=>{
10
+ const logger = req.payload?.logger;
11
+ const log = logger?.[level];
12
+ if (typeof log !== "function") return;
13
+ log.call(logger, entry);
14
+ };
@@ -1,24 +1,31 @@
1
1
  export type FieldConfig = {
2
- blocks?: BlockConfig[];
2
+ admin?: Record<string, unknown>;
3
+ blocks?: readonly BlockConfig[];
3
4
  defaultValue?: unknown;
4
- fields?: FieldConfig[];
5
+ fields?: readonly FieldConfig[];
6
+ hasMany?: boolean;
5
7
  label?: unknown;
6
8
  localized?: boolean;
7
9
  name?: string;
8
- options?: (string | {
10
+ options?: readonly (string | {
9
11
  value?: string;
10
12
  })[];
13
+ relationTo?: unknown;
11
14
  required?: boolean;
12
15
  type?: string;
13
16
  };
14
17
  type BlockConfig = {
15
- fields?: FieldConfig[];
18
+ fields?: readonly FieldConfig[];
16
19
  slug: string;
17
20
  };
18
21
  export type CollectionConfig = {
22
+ admin?: Record<string, unknown>;
19
23
  auth?: unknown;
20
- fields: FieldConfig[];
24
+ fields?: readonly FieldConfig[];
21
25
  slug: string;
26
+ versions?: boolean | {
27
+ drafts?: boolean | Record<string, unknown>;
28
+ };
22
29
  };
23
30
  export type NormalizedData = {
24
31
  coercedFields: string[];
@@ -27,10 +34,13 @@ export type NormalizedData = {
27
34
  };
28
35
  export declare const isAuthCollection: (collectionConfig?: CollectionConfig | null) => boolean;
29
36
  export declare const getCollectionFields: (collectionConfig?: CollectionConfig | null) => FieldConfig[];
37
+ export declare const hasDrafts: (collectionConfig?: CollectionConfig | null) => boolean;
38
+ export declare const getSchemaFields: (collectionConfig?: CollectionConfig | null) => FieldConfig[];
39
+ export declare const createLexicalText: (value: unknown) => Record<string, unknown>;
30
40
  export declare const normalizeAuthData: (collectionConfig: CollectionConfig | undefined, normalized: NormalizedData) => NormalizedData;
31
- export declare const normalizeDataForFields: (fields: FieldConfig[], data: Record<string, unknown>) => NormalizedData;
41
+ export declare const normalizeDataForFields: (fields: readonly FieldConfig[], data: Record<string, unknown>) => NormalizedData;
32
42
  export declare const getLocalizedRequiredFallbackData: ({ fields, source, target, }: {
33
- fields: FieldConfig[];
43
+ fields: readonly FieldConfig[];
34
44
  source: Record<string, unknown>;
35
45
  target: Record<string, unknown>;
36
46
  }) => Record<string, unknown>;
@@ -21,7 +21,28 @@ export const getCollectionFields = (collectionConfig)=>{
21
21
  }
22
22
  return fields;
23
23
  };
24
- const createLexicalText = (value)=>{
24
+ export const hasDrafts = (collectionConfig)=>{
25
+ const versions = collectionConfig?.versions;
26
+ if (!versions || versions === true || !isRecord(versions)) return false;
27
+ return Boolean(versions.drafts);
28
+ };
29
+ export const getSchemaFields = (collectionConfig)=>{
30
+ const fields = getCollectionFields(collectionConfig);
31
+ if (hasDrafts(collectionConfig)) {
32
+ fields.push({
33
+ defaultValue: "draft",
34
+ name: "_status",
35
+ options: [
36
+ "draft",
37
+ "published"
38
+ ],
39
+ required: true,
40
+ type: "select"
41
+ });
42
+ }
43
+ return fields;
44
+ };
45
+ export const createLexicalText = (value)=>{
25
46
  if (isRecord(value) && isRecord(value.root)) return value;
26
47
  const text = Array.isArray(value) ? value.join("\n") : String(value || "");
27
48
  const lines = text.split("\n").map((line)=>line.trim()).filter(Boolean);
@@ -0,0 +1,31 @@
1
+ import { type LocalizedDataInput } from "./shared.js";
2
+ import { type CollectionConfig, type FieldConfig } from "./normalizeData.js";
3
+ type ProposalMode = "create" | "update";
4
+ export type ProposalValidationIssue = {
5
+ code: "empty_localized_data" | "invalid_array" | "invalid_block_type" | "invalid_blocks" | "invalid_checkbox" | "invalid_container" | "invalid_date" | "invalid_option" | "invalid_relationship" | "missing_required_field" | "non_localized_field_in_secondary_locale" | "unknown_field";
6
+ message: string;
7
+ path: string;
8
+ };
9
+ export type PreparedProposalData = {
10
+ coercedFields: string[];
11
+ data?: Record<string, unknown>;
12
+ issues: ProposalValidationIssue[];
13
+ localizedData?: LocalizedDataInput;
14
+ };
15
+ type PrepareProposalDataArgs = {
16
+ collectionConfig?: CollectionConfig;
17
+ data?: Record<string, unknown>;
18
+ inferenceText?: string;
19
+ label: string;
20
+ localizedData?: LocalizedDataInput;
21
+ mode: ProposalMode;
22
+ };
23
+ export declare const prepareProposalWriteData: ({ collectionConfig, data, inferenceText, label, localizedData, mode }: PrepareProposalDataArgs) => PreparedProposalData;
24
+ export declare const applyLocalizedRequiredFallbackToPreparedData: ({ fallbackSource, fields, preparedData, }: {
25
+ fallbackSource: Record<string, unknown>;
26
+ fields: readonly FieldConfig[];
27
+ preparedData: Record<string, unknown>;
28
+ }) => {
29
+ [x: string]: unknown;
30
+ };
31
+ export {};