@mvriu5/payload-ai 1.4.0 → 1.6.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.
- package/README.md +60 -0
- package/dist/ai/promptCaching.d.ts +23 -0
- package/dist/ai/promptCaching.js +120 -0
- package/dist/ai/proposalRepair.d.ts +30 -0
- package/dist/ai/proposalRepair.js +76 -0
- package/dist/ai/sensitiveData.d.ts +1 -0
- package/dist/ai/sensitiveData.js +3 -1
- package/dist/components/ai-input/AIInput.js +35 -5
- package/dist/components/ai-input/AIInput.module.css +49 -0
- package/dist/components/generate-field/GenerateField.d.ts +11 -0
- package/dist/components/generate-field/GenerateField.js +109 -0
- package/dist/components/generate-field/GenerateField.module.css +39 -0
- package/dist/components/generate-field/richText.d.ts +29 -0
- package/dist/components/generate-field/richText.js +30 -0
- package/dist/components/translate-document/TranslateDocumentButton.d.ts +2 -0
- package/dist/components/translate-document/TranslateDocumentButton.js +111 -0
- package/dist/components/translate-document/TranslateDocumentButton.module.css +11 -0
- package/dist/exports/client.d.ts +4 -0
- package/dist/exports/client.js +4 -0
- package/dist/handlers/chatHandler.d.ts +1 -0
- package/dist/handlers/chatHandler.js +464 -138
- package/dist/handlers/generateFieldHandler.d.ts +14 -0
- package/dist/handlers/generateFieldHandler.js +144 -0
- package/dist/handlers/translateDocumentHandler.d.ts +14 -0
- package/dist/handlers/translateDocumentHandler.js +186 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +112 -4
- package/dist/payload/documentTranslation.d.ts +15 -0
- package/dist/payload/documentTranslation.js +120 -0
- package/dist/payload/textFieldGeneration.d.ts +26 -0
- package/dist/payload/textFieldGeneration.js +99 -0
- package/dist/payload/toolFieldSelection.d.ts +25 -0
- package/dist/payload/toolFieldSelection.js +118 -0
- package/dist/payload/toolSchemas.d.ts +7 -0
- package/dist/payload/toolSchemas.js +117 -0
- package/package.json +3 -3
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
const hasValue = (value)=>{
|
|
2
|
+
if (value === null || value === undefined || value === "") return false;
|
|
3
|
+
if (Array.isArray(value)) return value.some(hasValue);
|
|
4
|
+
if (typeof value === "object") return Object.values(value).some(hasValue);
|
|
5
|
+
return true;
|
|
6
|
+
};
|
|
7
|
+
const hasRichTextValue = (value)=>{
|
|
8
|
+
if (!value || typeof value !== "object") return false;
|
|
9
|
+
if (Array.isArray(value)) return value.some(hasRichTextValue);
|
|
10
|
+
const node = value;
|
|
11
|
+
if (node.type === "text") return typeof node.text === "string" && node.text.trim().length > 0;
|
|
12
|
+
if ([
|
|
13
|
+
"block",
|
|
14
|
+
"inlineBlock",
|
|
15
|
+
"relationship",
|
|
16
|
+
"upload"
|
|
17
|
+
].includes(String(node.type))) return true;
|
|
18
|
+
if (node.root) return hasRichTextValue(node.root);
|
|
19
|
+
return Array.isArray(node.children) && node.children.some(hasRichTextValue);
|
|
20
|
+
};
|
|
21
|
+
const getFieldType = (field)=>[
|
|
22
|
+
"json",
|
|
23
|
+
"richText",
|
|
24
|
+
"text",
|
|
25
|
+
"textarea"
|
|
26
|
+
].includes(field.type) ? field.type : "unknown";
|
|
27
|
+
const collectFields = ({ data, fields, parentPath = "", result })=>{
|
|
28
|
+
for (const field of fields){
|
|
29
|
+
if (field.type === "tabs") {
|
|
30
|
+
for (const tab of field.tabs){
|
|
31
|
+
const tabData = "name" in tab && tab.name ? data[tab.name] || {} : data;
|
|
32
|
+
collectFields({
|
|
33
|
+
data: tabData,
|
|
34
|
+
fields: tab.fields,
|
|
35
|
+
parentPath: "name" in tab && tab.name ? parentPath ? `${parentPath}.${tab.name}` : tab.name : parentPath,
|
|
36
|
+
result
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
if (!("name" in field) || !field.name) {
|
|
42
|
+
if ("fields" in field && Array.isArray(field.fields)) collectFields({
|
|
43
|
+
data,
|
|
44
|
+
fields: field.fields,
|
|
45
|
+
parentPath,
|
|
46
|
+
result
|
|
47
|
+
});
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
const path = parentPath ? `${parentPath}.${field.name}` : field.name;
|
|
51
|
+
const value = data[field.name];
|
|
52
|
+
if ("localized" in field && field.localized) {
|
|
53
|
+
const valueExists = field.type === "richText" ? hasRichTextValue(value) : hasValue(value);
|
|
54
|
+
if (valueExists) {
|
|
55
|
+
result.push({
|
|
56
|
+
fieldType: getFieldType(field),
|
|
57
|
+
path,
|
|
58
|
+
value
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
if (field.type === "array" && Array.isArray(value)) {
|
|
64
|
+
value.forEach((row, index)=>{
|
|
65
|
+
if (row && typeof row === "object") {
|
|
66
|
+
collectFields({
|
|
67
|
+
data: row,
|
|
68
|
+
fields: field.fields,
|
|
69
|
+
parentPath: `${path}.${index}`,
|
|
70
|
+
result
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
if (field.type === "blocks" && Array.isArray(value)) {
|
|
77
|
+
value.forEach((row, index)=>{
|
|
78
|
+
if (!row || typeof row !== "object") return;
|
|
79
|
+
const rowData = row;
|
|
80
|
+
const block = field.blocks.find((candidate)=>typeof candidate === "object" && candidate.slug === rowData.blockType);
|
|
81
|
+
if (block && typeof block === "object") {
|
|
82
|
+
collectFields({
|
|
83
|
+
data: rowData,
|
|
84
|
+
fields: block.fields,
|
|
85
|
+
parentPath: `${path}.${index}`,
|
|
86
|
+
result
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
if ("fields" in field && Array.isArray(field.fields) && value && typeof value === "object") {
|
|
93
|
+
collectFields({
|
|
94
|
+
data: value,
|
|
95
|
+
fields: field.fields,
|
|
96
|
+
parentPath: path,
|
|
97
|
+
result
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
export const getTranslationValues = (fields, data)=>{
|
|
103
|
+
if (!data || typeof data !== "object") return [];
|
|
104
|
+
const result = [];
|
|
105
|
+
collectFields({
|
|
106
|
+
data: data,
|
|
107
|
+
fields,
|
|
108
|
+
result
|
|
109
|
+
});
|
|
110
|
+
return result;
|
|
111
|
+
};
|
|
112
|
+
export const hasLocalizedFields = (fields)=>{
|
|
113
|
+
for (const field of fields){
|
|
114
|
+
if ("localized" in field && field.localized) return true;
|
|
115
|
+
if (field.type === "tabs" && field.tabs.some((tab)=>hasLocalizedFields(tab.fields))) return true;
|
|
116
|
+
if (field.type === "blocks" && field.blocks.some((block)=>typeof block === "object" && hasLocalizedFields(block.fields))) return true;
|
|
117
|
+
if ("fields" in field && Array.isArray(field.fields) && hasLocalizedFields(field.fields)) return true;
|
|
118
|
+
}
|
|
119
|
+
return false;
|
|
120
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Field } from "payload";
|
|
2
|
+
type GeneratableField = Extract<Field, {
|
|
3
|
+
type: "json" | "richText" | "text" | "textarea";
|
|
4
|
+
}>;
|
|
5
|
+
export type TextGenerationFieldContext = {
|
|
6
|
+
description?: string;
|
|
7
|
+
fieldType: GeneratableField["type"];
|
|
8
|
+
hasMany: boolean;
|
|
9
|
+
key: string;
|
|
10
|
+
label: string;
|
|
11
|
+
maxLength?: number;
|
|
12
|
+
name: string;
|
|
13
|
+
};
|
|
14
|
+
export type TextGenerationPageContext = {
|
|
15
|
+
fields: TextGenerationFieldContext[];
|
|
16
|
+
label: string;
|
|
17
|
+
slug: string;
|
|
18
|
+
type: "collection" | "global";
|
|
19
|
+
};
|
|
20
|
+
export declare const addTextGenerationFields: ({ fields, label, slug, type, }: {
|
|
21
|
+
fields: Field[];
|
|
22
|
+
label: string;
|
|
23
|
+
slug: string;
|
|
24
|
+
type: TextGenerationPageContext["type"];
|
|
25
|
+
}) => TextGenerationPageContext;
|
|
26
|
+
export {};
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
const generateFieldComponent = "@mvriu5/payload-ai/client#GenerateField";
|
|
2
|
+
const getLabel = (field)=>{
|
|
3
|
+
if (typeof field.label === "string") return field.label;
|
|
4
|
+
if (field.label && typeof field.label === "object") {
|
|
5
|
+
const label = Object.values(field.label).find((value)=>typeof value === "string");
|
|
6
|
+
if (typeof label === "string") return label;
|
|
7
|
+
}
|
|
8
|
+
return field.name;
|
|
9
|
+
};
|
|
10
|
+
const getDescription = (field)=>{
|
|
11
|
+
const description = field.admin?.description;
|
|
12
|
+
return typeof description === "string" ? description : undefined;
|
|
13
|
+
};
|
|
14
|
+
const addGenerateComponent = (field, key)=>{
|
|
15
|
+
field.admin = field.admin || {};
|
|
16
|
+
field.admin.components = field.admin.components || {};
|
|
17
|
+
const current = field.admin.components.afterInput || [];
|
|
18
|
+
const alreadyAdded = current.some((component)=>{
|
|
19
|
+
if (typeof component === "string") return component === generateFieldComponent;
|
|
20
|
+
return component && typeof component === "object" && "path" in component && component.path === generateFieldComponent;
|
|
21
|
+
});
|
|
22
|
+
if (alreadyAdded) return;
|
|
23
|
+
field.admin.components.afterInput = [
|
|
24
|
+
...current,
|
|
25
|
+
{
|
|
26
|
+
clientProps: {
|
|
27
|
+
generationFieldKey: key,
|
|
28
|
+
generationFieldType: field.type
|
|
29
|
+
},
|
|
30
|
+
path: generateFieldComponent
|
|
31
|
+
}
|
|
32
|
+
];
|
|
33
|
+
};
|
|
34
|
+
const visitFields = ({ fields, parentKey, result })=>{
|
|
35
|
+
for (const field of fields){
|
|
36
|
+
if (field.type === "tabs") {
|
|
37
|
+
for (const tab of field.tabs){
|
|
38
|
+
visitFields({
|
|
39
|
+
fields: tab.fields,
|
|
40
|
+
parentKey: "name" in tab ? `${parentKey}.${tab.name}` : parentKey,
|
|
41
|
+
result
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
if (field.type === "blocks") {
|
|
47
|
+
for (const block of field.blocks){
|
|
48
|
+
if (typeof block !== "object") continue;
|
|
49
|
+
visitFields({
|
|
50
|
+
fields: block.fields,
|
|
51
|
+
parentKey: `${parentKey}.${field.name}.${block.slug}`,
|
|
52
|
+
result
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
if ("fields" in field && Array.isArray(field.fields)) {
|
|
58
|
+
const nextParent = "name" in field && field.name ? `${parentKey}.${field.name}` : parentKey;
|
|
59
|
+
visitFields({
|
|
60
|
+
fields: field.fields,
|
|
61
|
+
parentKey: nextParent,
|
|
62
|
+
result
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
if (![
|
|
66
|
+
"json",
|
|
67
|
+
"richText",
|
|
68
|
+
"text",
|
|
69
|
+
"textarea"
|
|
70
|
+
].includes(field.type) || !("name" in field) || !field.name) continue;
|
|
71
|
+
const key = `${parentKey}.${field.name}`;
|
|
72
|
+
const generatableField = field;
|
|
73
|
+
if (generatableField.admin?.hidden) continue;
|
|
74
|
+
addGenerateComponent(generatableField, key);
|
|
75
|
+
result.push({
|
|
76
|
+
description: getDescription(generatableField),
|
|
77
|
+
fieldType: generatableField.type,
|
|
78
|
+
hasMany: "hasMany" in generatableField ? Boolean(generatableField.hasMany) : false,
|
|
79
|
+
key,
|
|
80
|
+
label: getLabel(generatableField),
|
|
81
|
+
maxLength: "maxLength" in generatableField ? generatableField.maxLength : undefined,
|
|
82
|
+
name: generatableField.name
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
export const addTextGenerationFields = ({ fields, label, slug, type })=>{
|
|
87
|
+
const result = [];
|
|
88
|
+
visitFields({
|
|
89
|
+
fields,
|
|
90
|
+
parentKey: slug,
|
|
91
|
+
result
|
|
92
|
+
});
|
|
93
|
+
return {
|
|
94
|
+
fields: result,
|
|
95
|
+
label,
|
|
96
|
+
slug,
|
|
97
|
+
type
|
|
98
|
+
};
|
|
99
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { type CollectionConfig } from "./normalizeData.js";
|
|
3
|
+
export declare const maxToolSelectedFields = 12;
|
|
4
|
+
export type ReadCollectionConfig = CollectionConfig & {
|
|
5
|
+
admin?: {
|
|
6
|
+
useAsTitle?: string;
|
|
7
|
+
};
|
|
8
|
+
timestamps?: boolean;
|
|
9
|
+
upload?: boolean | Record<string, unknown>;
|
|
10
|
+
};
|
|
11
|
+
export declare const getToolSelectableFieldNames: (config: ReadCollectionConfig) => string[];
|
|
12
|
+
export declare const createToolFieldNamesSchema: (configs: ReadCollectionConfig[]) => z.ZodOptional<z.ZodArray<z.ZodString | z.ZodEnum<{
|
|
13
|
+
[x: string]: string;
|
|
14
|
+
}>>>;
|
|
15
|
+
export declare const resolveToolFieldSelection: ({ config, mode, requestedFields, }: {
|
|
16
|
+
config: ReadCollectionConfig;
|
|
17
|
+
mode?: "document" | "search";
|
|
18
|
+
requestedFields?: string[];
|
|
19
|
+
}) => {
|
|
20
|
+
allowedFields: string[];
|
|
21
|
+
depth: number;
|
|
22
|
+
fields: string[];
|
|
23
|
+
invalidFields: string[];
|
|
24
|
+
select: Record<string, true>;
|
|
25
|
+
};
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { isSensitiveKey } from "../ai/sensitiveData.js";
|
|
3
|
+
import { getSchemaFields } from "./normalizeData.js";
|
|
4
|
+
export const maxToolSelectedFields = 12;
|
|
5
|
+
const cheapFieldTypes = new Set([
|
|
6
|
+
"checkbox",
|
|
7
|
+
"date",
|
|
8
|
+
"email",
|
|
9
|
+
"number",
|
|
10
|
+
"point",
|
|
11
|
+
"radio",
|
|
12
|
+
"select",
|
|
13
|
+
"text"
|
|
14
|
+
]);
|
|
15
|
+
const populatedFieldTypes = new Set([
|
|
16
|
+
"relationship",
|
|
17
|
+
"upload"
|
|
18
|
+
]);
|
|
19
|
+
const systemFieldNames = [
|
|
20
|
+
"id",
|
|
21
|
+
"createdAt",
|
|
22
|
+
"updatedAt"
|
|
23
|
+
];
|
|
24
|
+
const uploadFieldNames = [
|
|
25
|
+
"filename",
|
|
26
|
+
"mimeType",
|
|
27
|
+
"filesize",
|
|
28
|
+
"width",
|
|
29
|
+
"height",
|
|
30
|
+
"url",
|
|
31
|
+
"thumbnailURL"
|
|
32
|
+
];
|
|
33
|
+
const getSelectableFields = (config)=>{
|
|
34
|
+
return getSchemaFields(config).filter((field)=>{
|
|
35
|
+
return Boolean(field.name && field.type !== "ui" && !isSensitiveKey(field.name));
|
|
36
|
+
});
|
|
37
|
+
};
|
|
38
|
+
export const getToolSelectableFieldNames = (config)=>{
|
|
39
|
+
const names = getSelectableFields(config).flatMap((field)=>field.name ? [
|
|
40
|
+
field.name
|
|
41
|
+
] : []);
|
|
42
|
+
names.push("id");
|
|
43
|
+
if (config.timestamps !== false) names.push("createdAt", "updatedAt");
|
|
44
|
+
if (config.upload) names.push(...uploadFieldNames);
|
|
45
|
+
return [
|
|
46
|
+
...new Set(names)
|
|
47
|
+
];
|
|
48
|
+
};
|
|
49
|
+
const getDefaultFieldNames = (config, mode)=>{
|
|
50
|
+
const selectableFields = getSelectableFields(config);
|
|
51
|
+
const selectableNames = new Set(getToolSelectableFieldNames(config));
|
|
52
|
+
const candidates = [
|
|
53
|
+
config.admin?.useAsTitle,
|
|
54
|
+
"title",
|
|
55
|
+
"name",
|
|
56
|
+
"label",
|
|
57
|
+
"headline",
|
|
58
|
+
"slug",
|
|
59
|
+
"_status",
|
|
60
|
+
...config.upload ? selectableFields.flatMap((field)=>field.name && cheapFieldTypes.has(field.type || "") ? [
|
|
61
|
+
field.name
|
|
62
|
+
] : []).slice(0, 2) : [],
|
|
63
|
+
...mode === "document" ? [
|
|
64
|
+
"updatedAt"
|
|
65
|
+
] : [],
|
|
66
|
+
...config.upload ? [
|
|
67
|
+
"filename",
|
|
68
|
+
"mimeType",
|
|
69
|
+
"filesize",
|
|
70
|
+
"url"
|
|
71
|
+
] : []
|
|
72
|
+
];
|
|
73
|
+
const defaults = candidates.filter((name)=>Boolean(name && (selectableNames.has(name) || systemFieldNames.includes(name))));
|
|
74
|
+
if (!defaults.some((fieldName)=>!systemFieldNames.includes(fieldName) && !uploadFieldNames.includes(fieldName))) {
|
|
75
|
+
const firstCheapField = selectableFields.find((field)=>field.name && cheapFieldTypes.has(field.type || ""));
|
|
76
|
+
const firstSelectableField = firstCheapField || selectableFields[0];
|
|
77
|
+
defaults.unshift(firstSelectableField?.name || "id");
|
|
78
|
+
}
|
|
79
|
+
return [
|
|
80
|
+
...new Set(defaults)
|
|
81
|
+
].slice(0, mode === "search" ? 4 : 6);
|
|
82
|
+
};
|
|
83
|
+
export const createToolFieldNamesSchema = (configs)=>{
|
|
84
|
+
const names = [
|
|
85
|
+
...new Set(configs.flatMap(getToolSelectableFieldNames))
|
|
86
|
+
];
|
|
87
|
+
const fieldNameSchema = names.length > 0 ? z.enum(names) : z.string().refine(()=>false, "No fields are selectable.");
|
|
88
|
+
return z.array(fieldNameSchema).max(maxToolSelectedFields).optional();
|
|
89
|
+
};
|
|
90
|
+
export const resolveToolFieldSelection = ({ config, mode = "document", requestedFields })=>{
|
|
91
|
+
const allowedNames = new Set(getToolSelectableFieldNames(config));
|
|
92
|
+
const normalizedRequestedFields = [
|
|
93
|
+
...new Set((requestedFields || []).slice(0, maxToolSelectedFields).map((field)=>field.trim()).filter(Boolean))
|
|
94
|
+
];
|
|
95
|
+
const invalidFields = normalizedRequestedFields.filter((field)=>!allowedNames.has(field));
|
|
96
|
+
const selectedFields = [
|
|
97
|
+
...new Set((requestedFields?.length ? requestedFields : getDefaultFieldNames(config, mode)).map((field)=>field.trim()).filter((field)=>allowedNames.has(field)))
|
|
98
|
+
].slice(0, maxToolSelectedFields);
|
|
99
|
+
const fields = selectedFields.length > 0 ? selectedFields : getDefaultFieldNames(config, mode);
|
|
100
|
+
const fieldTypesByName = new Map(getSelectableFields(config).flatMap((field)=>field.name ? [
|
|
101
|
+
[
|
|
102
|
+
field.name,
|
|
103
|
+
field.type
|
|
104
|
+
]
|
|
105
|
+
] : []));
|
|
106
|
+
return {
|
|
107
|
+
allowedFields: [
|
|
108
|
+
...allowedNames
|
|
109
|
+
],
|
|
110
|
+
depth: fields.some((field)=>populatedFieldTypes.has(fieldTypesByName.get(field) || "")) ? 1 : 0,
|
|
111
|
+
fields,
|
|
112
|
+
invalidFields,
|
|
113
|
+
select: Object.fromEntries(fields.map((field)=>[
|
|
114
|
+
field,
|
|
115
|
+
true
|
|
116
|
+
]))
|
|
117
|
+
};
|
|
118
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { type CollectionConfig } from "./normalizeData.js";
|
|
3
|
+
export declare const genericPayloadDataSchema: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
4
|
+
export declare const createPayloadDataSchema: (config?: CollectionConfig | null) => z.ZodRecord<z.ZodString, z.ZodUnknown> | z.ZodObject<{
|
|
5
|
+
[x: string]: z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>;
|
|
6
|
+
}, z.core.$strict>;
|
|
7
|
+
export declare const createLocalizedPayloadDataSchema: (dataSchema: z.ZodType) => z.ZodRecord<z.ZodString, z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>>;
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { getSchemaFields } from "./normalizeData.js";
|
|
3
|
+
const relationshipIDSchema = z.union([
|
|
4
|
+
z.string().min(1),
|
|
5
|
+
z.number()
|
|
6
|
+
]);
|
|
7
|
+
const getOptionValues = (field)=>{
|
|
8
|
+
return (field.options || []).flatMap((option)=>{
|
|
9
|
+
const value = typeof option === "string" ? option : option.value;
|
|
10
|
+
return value ? [
|
|
11
|
+
value
|
|
12
|
+
] : [];
|
|
13
|
+
});
|
|
14
|
+
};
|
|
15
|
+
const createSelectSchema = (field)=>{
|
|
16
|
+
const values = getOptionValues(field);
|
|
17
|
+
const valueSchema = values.length > 0 ? z.enum(values) : z.string();
|
|
18
|
+
return field.hasMany ? z.array(valueSchema) : valueSchema;
|
|
19
|
+
};
|
|
20
|
+
const createRelationshipSchema = (field)=>{
|
|
21
|
+
const polymorphicValueSchema = Array.isArray(field.relationTo) ? z.union([
|
|
22
|
+
relationshipIDSchema,
|
|
23
|
+
z.object({
|
|
24
|
+
relationTo: z.enum(field.relationTo),
|
|
25
|
+
value: relationshipIDSchema
|
|
26
|
+
}).strict()
|
|
27
|
+
]) : relationshipIDSchema;
|
|
28
|
+
return field.hasMany ? z.array(polymorphicValueSchema) : polymorphicValueSchema;
|
|
29
|
+
};
|
|
30
|
+
const createObjectSchema = (fields)=>{
|
|
31
|
+
const shape = {};
|
|
32
|
+
const addFields = (nestedFields)=>{
|
|
33
|
+
for (const field of nestedFields){
|
|
34
|
+
if (field.type === "ui") continue;
|
|
35
|
+
if (!field.name) {
|
|
36
|
+
if (field.fields?.length) addFields(field.fields);
|
|
37
|
+
if (field.tabs?.length) {
|
|
38
|
+
for (const tab of field.tabs){
|
|
39
|
+
if (tab.name) {
|
|
40
|
+
shape[tab.name] = createObjectSchema(tab.fields || []).nullable().optional();
|
|
41
|
+
} else {
|
|
42
|
+
addFields(tab.fields || []);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
shape[field.name] = createFieldValueSchema(field).nullable().optional();
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
addFields(fields);
|
|
52
|
+
return z.object(shape).strict();
|
|
53
|
+
};
|
|
54
|
+
const createBlocksSchema = (field)=>{
|
|
55
|
+
const blockSchemas = (field.blocks || []).map((block)=>createObjectSchema(block.fields || []).extend({
|
|
56
|
+
blockType: z.literal(block.slug)
|
|
57
|
+
}));
|
|
58
|
+
if (blockSchemas.length === 0) return z.array(z.record(z.string(), z.unknown()));
|
|
59
|
+
if (blockSchemas.length === 1) return z.array(blockSchemas[0]);
|
|
60
|
+
return z.array(z.union(blockSchemas));
|
|
61
|
+
};
|
|
62
|
+
const createFieldValueSchema = (field)=>{
|
|
63
|
+
switch(field.type){
|
|
64
|
+
case "array":
|
|
65
|
+
return z.array(createObjectSchema(field.fields || []));
|
|
66
|
+
case "blocks":
|
|
67
|
+
return createBlocksSchema(field);
|
|
68
|
+
case "checkbox":
|
|
69
|
+
return z.boolean();
|
|
70
|
+
case "date":
|
|
71
|
+
return z.string().min(1);
|
|
72
|
+
case "email":
|
|
73
|
+
return z.string();
|
|
74
|
+
case "group":
|
|
75
|
+
return createObjectSchema(field.fields || []);
|
|
76
|
+
case "number":
|
|
77
|
+
return z.number();
|
|
78
|
+
case "point":
|
|
79
|
+
return z.tuple([
|
|
80
|
+
z.number(),
|
|
81
|
+
z.number()
|
|
82
|
+
]);
|
|
83
|
+
case "radio":
|
|
84
|
+
case "select":
|
|
85
|
+
return createSelectSchema(field);
|
|
86
|
+
case "relationship":
|
|
87
|
+
case "upload":
|
|
88
|
+
return createRelationshipSchema(field);
|
|
89
|
+
case "richText":
|
|
90
|
+
return z.union([
|
|
91
|
+
z.string(),
|
|
92
|
+
z.record(z.string(), z.unknown())
|
|
93
|
+
]);
|
|
94
|
+
case "code":
|
|
95
|
+
case "text":
|
|
96
|
+
case "textarea":
|
|
97
|
+
return z.string();
|
|
98
|
+
case "json":
|
|
99
|
+
default:
|
|
100
|
+
return z.unknown();
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
export const genericPayloadDataSchema = z.record(z.string(), z.unknown());
|
|
104
|
+
const payloadDataSchemaCache = new WeakMap();
|
|
105
|
+
export const createPayloadDataSchema = (config)=>{
|
|
106
|
+
if (!config) return genericPayloadDataSchema;
|
|
107
|
+
const cached = payloadDataSchemaCache.get(config);
|
|
108
|
+
if (cached) return cached;
|
|
109
|
+
const schema = createObjectSchema(getSchemaFields(config));
|
|
110
|
+
payloadDataSchemaCache.set(config, schema);
|
|
111
|
+
return schema;
|
|
112
|
+
};
|
|
113
|
+
export const createLocalizedPayloadDataSchema = (dataSchema)=>{
|
|
114
|
+
return z.record(z.string(), dataSchema).refine((value)=>Object.keys(value).length > 0, {
|
|
115
|
+
message: "localizedData must include at least one locale entry."
|
|
116
|
+
});
|
|
117
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mvriu5/payload-ai",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"description": "AI assistant plugin for Payload CMS with provider selection, CMS mentions, and signed action proposals.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"payload",
|
|
@@ -84,10 +84,10 @@
|
|
|
84
84
|
"copyfiles": "2.4.1",
|
|
85
85
|
"cross-env": "^10.1.0",
|
|
86
86
|
"eslint": "^10.5.0",
|
|
87
|
-
"graphql": "^
|
|
87
|
+
"graphql": "^16.8.1",
|
|
88
88
|
"jsdom": "^29.1.1",
|
|
89
89
|
"knip": "^6.18.0",
|
|
90
|
-
"next": "16.2.
|
|
90
|
+
"next": "16.2.11",
|
|
91
91
|
"payload": "3.86.0",
|
|
92
92
|
"prettier": "^3.8.4",
|
|
93
93
|
"react": "19.2.7",
|