@ai-stack/payloadcms 3.68.0 → 3.76.0-beta.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/dist/ai/core/media/image/generateImage.js +2 -6
- package/dist/ai/core/media/image/generateImage.js.map +1 -1
- package/dist/ai/core/media/image/handlers/multimodal.js +5 -0
- package/dist/ai/core/media/image/handlers/multimodal.js.map +1 -1
- package/dist/ai/core/streamObject.js +3 -3
- package/dist/ai/core/streamObject.js.map +1 -1
- package/dist/ai/core/types.d.ts +3 -0
- package/dist/ai/core/types.js.map +1 -1
- package/dist/ai/prompts.d.ts +1 -2
- package/dist/ai/prompts.js +0 -110
- package/dist/ai/prompts.js.map +1 -1
- package/dist/ai/providers/blocks/anthropic.js +2 -1
- package/dist/ai/providers/blocks/anthropic.js.map +1 -1
- package/dist/ai/providers/blocks/elevenlabs.js +3 -2
- package/dist/ai/providers/blocks/elevenlabs.js.map +1 -1
- package/dist/ai/providers/blocks/fal.js +2 -1
- package/dist/ai/providers/blocks/fal.js.map +1 -1
- package/dist/ai/providers/blocks/google.js +11 -6
- package/dist/ai/providers/blocks/google.js.map +1 -1
- package/dist/ai/providers/blocks/openai-compatible.js +2 -1
- package/dist/ai/providers/blocks/openai-compatible.js.map +1 -1
- package/dist/ai/providers/blocks/openai.js +3 -2
- package/dist/ai/providers/blocks/openai.js.map +1 -1
- package/dist/ai/providers/blocks/xai.js +2 -1
- package/dist/ai/providers/blocks/xai.js.map +1 -1
- package/dist/ai/providers/icons.d.ts +7 -0
- package/dist/ai/providers/icons.js +9 -0
- package/dist/ai/providers/icons.js.map +1 -0
- package/dist/ai/providers/registry.js +34 -24
- package/dist/ai/providers/registry.js.map +1 -1
- package/dist/ai/utils/filterEditorSchemaByNodes.d.ts +9 -0
- package/dist/ai/utils/filterEditorSchemaByNodes.js +30 -3
- package/dist/ai/utils/filterEditorSchemaByNodes.js.map +1 -1
- package/dist/ai/utils/nodeToSchemaMap.d.ts +22 -0
- package/dist/ai/utils/nodeToSchemaMap.js +72 -0
- package/dist/ai/utils/nodeToSchemaMap.js.map +1 -0
- package/dist/collections/AIJobs.js +1 -1
- package/dist/collections/AIJobs.js.map +1 -1
- package/dist/collections/AISettings.js +47 -20
- package/dist/collections/AISettings.js.map +1 -1
- package/dist/collections/Instructions.js +37 -0
- package/dist/collections/Instructions.js.map +1 -1
- package/dist/defaults.d.ts +1 -0
- package/dist/defaults.js +8 -0
- package/dist/defaults.js.map +1 -1
- package/dist/endpoints/chat.d.ts +4 -0
- package/dist/endpoints/fetchFields.js +10 -0
- package/dist/endpoints/fetchFields.js.map +1 -1
- package/dist/endpoints/fetchVoices.js +41 -24
- package/dist/endpoints/fetchVoices.js.map +1 -1
- package/dist/endpoints/index.js +194 -16
- package/dist/endpoints/index.js.map +1 -1
- package/dist/exports/fields.d.ts +1 -0
- package/dist/exports/fields.js +1 -0
- package/dist/exports/fields.js.map +1 -1
- package/dist/fields/ArrayComposeField/ArrayComposeField.d.ts +15 -0
- package/dist/fields/ArrayComposeField/ArrayComposeField.js +87 -0
- package/dist/fields/ArrayComposeField/ArrayComposeField.js.map +1 -0
- package/dist/fields/ArrayComposeField/ArrayComposeField.jsx +73 -0
- package/dist/fields/PromptEditorField/PromptEditorField.js +7 -2
- package/dist/fields/PromptEditorField/PromptEditorField.js.map +1 -1
- package/dist/fields/PromptEditorField/PromptEditorField.jsx +5 -2
- package/dist/index.d.ts +3 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/payload-ai.d.ts +152 -0
- package/dist/plugin.js +16 -32
- package/dist/plugin.js.map +1 -1
- package/dist/providers/InstructionsProvider/InstructionsProvider.js +47 -15
- package/dist/providers/InstructionsProvider/InstructionsProvider.js.map +1 -1
- package/dist/providers/InstructionsProvider/InstructionsProvider.jsx +39 -16
- package/dist/providers/InstructionsProvider/context.d.ts +3 -0
- package/dist/providers/InstructionsProvider/context.js +2 -0
- package/dist/providers/InstructionsProvider/context.js.map +1 -1
- package/dist/providers/InstructionsProvider/useInstructions.js +21 -2
- package/dist/providers/InstructionsProvider/useInstructions.js.map +1 -1
- package/dist/styles.d.ts +11 -0
- package/dist/types/handlebars-async-helpers.d.ts +1 -0
- package/dist/types/handlebars-dist-handlebars.d.ts +1 -0
- package/dist/types/react-mentions.d.ts +1 -0
- package/dist/types.d.ts +34 -5
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -1
- package/dist/ui/AIConfigDashboard/index.js +198 -22
- package/dist/ui/AIConfigDashboard/index.js.map +1 -1
- package/dist/ui/AIConfigDashboard/index.jsx +159 -13
- package/dist/ui/Compose/Compose.d.ts +1 -0
- package/dist/ui/Compose/Compose.js +23 -4
- package/dist/ui/Compose/Compose.js.map +1 -1
- package/dist/ui/Compose/Compose.jsx +23 -4
- package/dist/ui/Compose/UndoRedoActions.d.ts +2 -2
- package/dist/ui/Compose/UndoRedoActions.js +8 -5
- package/dist/ui/Compose/UndoRedoActions.js.map +1 -1
- package/dist/ui/Compose/UndoRedoActions.jsx +6 -5
- package/dist/ui/Compose/compose.module.css +56 -16
- package/dist/ui/Compose/hooks/menu/itemsMap.js +12 -6
- package/dist/ui/Compose/hooks/menu/itemsMap.js.map +1 -1
- package/dist/ui/Compose/hooks/menu/useMenu.js +26 -15
- package/dist/ui/Compose/hooks/menu/useMenu.js.map +1 -1
- package/dist/ui/Compose/hooks/menu/useMenu.jsx +25 -12
- package/dist/ui/Compose/hooks/useGenerate.js +26 -174
- package/dist/ui/Compose/hooks/useGenerate.js.map +1 -1
- package/dist/ui/Compose/hooks/useGenerateUpload.d.ts +11 -0
- package/dist/ui/Compose/hooks/useGenerateUpload.js +150 -0
- package/dist/ui/Compose/hooks/useGenerateUpload.js.map +1 -0
- package/dist/ui/Compose/hooks/useHistory.d.ts +0 -1
- package/dist/ui/Compose/hooks/useHistory.js +65 -25
- package/dist/ui/Compose/hooks/useHistory.js.map +1 -1
- package/dist/ui/Compose/hooks/useStreamingUpdate.d.ts +8 -0
- package/dist/ui/Compose/hooks/useStreamingUpdate.js +48 -0
- package/dist/ui/Compose/hooks/useStreamingUpdate.js.map +1 -0
- package/dist/ui/DynamicVoiceSelect/index.js +63 -11
- package/dist/ui/DynamicVoiceSelect/index.js.map +1 -1
- package/dist/ui/DynamicVoiceSelect/index.jsx +47 -14
- package/dist/ui/EncryptedTextField/index.js +4 -4
- package/dist/ui/EncryptedTextField/index.js.map +1 -1
- package/dist/ui/EncryptedTextField/index.jsx +4 -4
- package/dist/ui/VoicesFetcher/index.js +34 -16
- package/dist/ui/VoicesFetcher/index.js.map +1 -1
- package/dist/ui/VoicesFetcher/index.jsx +32 -15
- package/dist/utilities/buildSmartPrompt.d.ts +22 -0
- package/dist/utilities/buildSmartPrompt.js +141 -0
- package/dist/utilities/buildSmartPrompt.js.map +1 -0
- package/dist/utilities/encryption.js +2 -1
- package/dist/utilities/encryption.js.map +1 -1
- package/dist/utilities/fieldToJsonSchema.js +32 -3
- package/dist/utilities/fieldToJsonSchema.js.map +1 -1
- package/dist/utilities/resolveImageReferences.d.ts +3 -1
- package/dist/utilities/resolveImageReferences.js +21 -2
- package/dist/utilities/resolveImageReferences.js.map +1 -1
- package/dist/utilities/seedProperties.d.ts +7 -0
- package/dist/utilities/seedProperties.js +100 -0
- package/dist/utilities/seedProperties.js.map +1 -0
- package/dist/utilities/setSafeLexicalState.js +79 -6
- package/dist/utilities/setSafeLexicalState.js.map +1 -1
- package/dist/utilities/updateFieldsConfig.d.ts +1 -1
- package/dist/utilities/updateFieldsConfig.js +8 -1
- package/dist/utilities/updateFieldsConfig.js.map +1 -1
- package/package.json +35 -33
- package/dist/endpoints/chat.d.js +0 -3
- package/dist/endpoints/chat.d.js.map +0 -1
- package/dist/init.d.ts +0 -7
- package/dist/init.js +0 -135
- package/dist/init.js.map +0 -1
- package/dist/payload-ai.d.js +0 -3
- package/dist/payload-ai.d.js.map +0 -1
- package/dist/styles.d.js +0 -2
- package/dist/styles.d.js.map +0 -1
- package/dist/types/handlebars-async-helpers.d.js +0 -2
- package/dist/types/handlebars-async-helpers.d.js.map +0 -1
- package/dist/types/handlebars-dist-handlebars.d.js +0 -2
- package/dist/types/handlebars-dist-handlebars.d.js.map +0 -1
- package/dist/types/react-mentions.d.js +0 -2
- package/dist/types/react-mentions.d.js.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import { useField } from '@payloadcms/ui';
|
|
2
|
+
import { Button, useField } from '@payloadcms/ui';
|
|
3
3
|
import React, { useState } from 'react';
|
|
4
4
|
export const EncryptedTextField = ({ label, path, required }) => {
|
|
5
5
|
const { setValue, value } = useField({ path });
|
|
@@ -24,12 +24,12 @@ export const EncryptedTextField = ({ label, path, required }) => {
|
|
|
24
24
|
✓ Configured
|
|
25
25
|
</span>
|
|
26
26
|
</div>
|
|
27
|
-
<
|
|
27
|
+
<Button buttonStyle="secondary" onClick={() => {
|
|
28
28
|
setValue('');
|
|
29
29
|
setIsEditing(true);
|
|
30
|
-
}}
|
|
30
|
+
}} size="medium">
|
|
31
31
|
Change
|
|
32
|
-
</
|
|
32
|
+
</Button>
|
|
33
33
|
</div>) : (<input onChange={(e) => setValue(e.target.value)} placeholder="sk-..." style={{ width: '100%' }} type="password" value={value || ''}/>)}
|
|
34
34
|
</div>);
|
|
35
35
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { Button,
|
|
3
|
+
import { Button, toast, useField } from '@payloadcms/ui';
|
|
4
4
|
import React, { useCallback, useState } from 'react';
|
|
5
5
|
import { PLUGIN_API_ENDPOINT_FETCH_VOICES } from '../../defaults.js';
|
|
6
6
|
/**
|
|
@@ -9,41 +9,58 @@ import { PLUGIN_API_ENDPOINT_FETCH_VOICES } from '../../defaults.js';
|
|
|
9
9
|
* SECURE: API key is never exposed to the client
|
|
10
10
|
*/ export const VoicesFetcher = ({ path })=>{
|
|
11
11
|
const [loading, setLoading] = useState(false);
|
|
12
|
-
const { dispatchFields } = useForm();
|
|
13
12
|
// Get the parent path (the block path)
|
|
14
13
|
const fieldPath = path || '';
|
|
15
14
|
const blockPath = fieldPath.split('.').slice(0, -1).join('.');
|
|
16
15
|
const voicesPath = `${blockPath}.voices`;
|
|
16
|
+
const { setValue } = useField({
|
|
17
|
+
path: voicesPath
|
|
18
|
+
});
|
|
17
19
|
const fetchVoices = useCallback(async ()=>{
|
|
18
20
|
setLoading(true);
|
|
21
|
+
const controller = new AbortController();
|
|
22
|
+
const timeoutId = setTimeout(()=>controller.abort(), 30000) // 30s timeout
|
|
23
|
+
;
|
|
19
24
|
try {
|
|
20
25
|
// Call server endpoint - it will read the API key from the database
|
|
21
26
|
const response = await fetch(`/api${PLUGIN_API_ENDPOINT_FETCH_VOICES}`, {
|
|
22
27
|
headers: {
|
|
23
28
|
'Content-Type': 'application/json'
|
|
24
29
|
},
|
|
25
|
-
method: 'POST'
|
|
30
|
+
method: 'POST',
|
|
31
|
+
signal: controller.signal
|
|
26
32
|
});
|
|
33
|
+
clearTimeout(timeoutId);
|
|
27
34
|
if (!response.ok) {
|
|
28
|
-
|
|
29
|
-
|
|
35
|
+
let errorMessage = 'Failed to fetch voices';
|
|
36
|
+
try {
|
|
37
|
+
const error = await response.json();
|
|
38
|
+
errorMessage = error.message || errorMessage;
|
|
39
|
+
} catch (e) {
|
|
40
|
+
// If response is not JSON (e.g. 504 Gateway Timeout HTML), use status text
|
|
41
|
+
errorMessage = `Error ${response.status}: ${response.statusText}`;
|
|
42
|
+
}
|
|
43
|
+
throw new Error(errorMessage);
|
|
30
44
|
}
|
|
31
45
|
const data = await response.json();
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
});
|
|
38
|
-
alert(`Successfully fetched ${data.voices?.length || 0} voices!`);
|
|
46
|
+
const voices = data.voices || [];
|
|
47
|
+
// Replace the entire array value at once
|
|
48
|
+
// This is much more performant than dispatching ADD_ROW actions in a loop
|
|
49
|
+
setValue(voices);
|
|
50
|
+
toast.success(`Successfully fetched ${voices.length} voices!`);
|
|
39
51
|
} catch (error) {
|
|
40
|
-
|
|
52
|
+
const msg = error instanceof Error ? error.message : 'Failed to fetch voices';
|
|
53
|
+
if (error instanceof Error && error.name === 'AbortError') {
|
|
54
|
+
toast.error('Request timed out. Please try again.');
|
|
55
|
+
} else {
|
|
56
|
+
toast.error(`Error: ${msg}`);
|
|
57
|
+
}
|
|
41
58
|
} finally{
|
|
42
59
|
setLoading(false);
|
|
60
|
+
clearTimeout(timeoutId);
|
|
43
61
|
}
|
|
44
62
|
}, [
|
|
45
|
-
|
|
46
|
-
voicesPath
|
|
63
|
+
setValue
|
|
47
64
|
]);
|
|
48
65
|
return /*#__PURE__*/ _jsxs("div", {
|
|
49
66
|
style: {
|
|
@@ -53,9 +70,10 @@ import { PLUGIN_API_ENDPOINT_FETCH_VOICES } from '../../defaults.js';
|
|
|
53
70
|
/*#__PURE__*/ _jsx(Button, {
|
|
54
71
|
buttonStyle: "secondary",
|
|
55
72
|
disabled: loading,
|
|
73
|
+
margin: false,
|
|
56
74
|
onClick: fetchVoices,
|
|
57
75
|
size: "medium",
|
|
58
|
-
children: loading ? 'Fetching Voices...' : 'Fetch Voices
|
|
76
|
+
children: loading ? 'Fetching Voices...' : 'Fetch Voices'
|
|
59
77
|
}),
|
|
60
78
|
/*#__PURE__*/ _jsx("p", {
|
|
61
79
|
style: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/ui/VoicesFetcher/index.tsx"],"sourcesContent":["'use client'\n\nimport type { FieldClientComponent } from 'payload'\n\nimport { Button,
|
|
1
|
+
{"version":3,"sources":["../../../src/ui/VoicesFetcher/index.tsx"],"sourcesContent":["'use client'\n\nimport type { FieldClientComponent } from 'payload'\n\nimport { Button, toast, useField, useFormFields } from '@payloadcms/ui'\nimport React, { useCallback, useState } from 'react'\n\nimport { PLUGIN_API_ENDPOINT_FETCH_VOICES } from '../../defaults.js'\n\ninterface Voice {\n category?: string\n enabled?: boolean\n id: string\n labels?: Record<string, unknown>\n name: string\n preview_url?: string\n}\n\n/**\n * VoicesFetcher Component\n * Fetches voices from ElevenLabs API (server-side) and populates the voices array field\n * SECURE: API key is never exposed to the client\n */\nexport const VoicesFetcher: FieldClientComponent = ({ path }) => {\n const [loading, setLoading] = useState(false)\n\n // Get the parent path (the block path)\n const fieldPath = (path as string) || ''\n const blockPath = fieldPath.split('.').slice(0, -1).join('.')\n const voicesPath = `${blockPath}.voices`\n\n const { setValue } = useField<Voice[]>({ path: voicesPath })\n\n const fetchVoices = useCallback(async () => {\n setLoading(true)\n\n const controller = new AbortController()\n const timeoutId = setTimeout(() => controller.abort(), 30000) // 30s timeout\n\n try {\n // Call server endpoint - it will read the API key from the database\n const response = await fetch(`/api${PLUGIN_API_ENDPOINT_FETCH_VOICES}`, {\n headers: {\n 'Content-Type': 'application/json',\n },\n method: 'POST',\n signal: controller.signal,\n })\n\n clearTimeout(timeoutId)\n\n if (!response.ok) {\n let errorMessage = 'Failed to fetch voices'\n try {\n const error = await response.json()\n errorMessage = error.message || errorMessage\n } catch (e) {\n // If response is not JSON (e.g. 504 Gateway Timeout HTML), use status text\n errorMessage = `Error ${response.status}: ${response.statusText}`\n }\n throw new Error(errorMessage)\n }\n\n const data = await response.json()\n const voices: Voice[] = data.voices || []\n\n // Replace the entire array value at once\n // This is much more performant than dispatching ADD_ROW actions in a loop\n setValue(voices)\n\n toast.success(`Successfully fetched ${voices.length} voices!`)\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : 'Failed to fetch voices'\n if (error instanceof Error && error.name === 'AbortError') {\n toast.error('Request timed out. Please try again.')\n } else {\n toast.error(`Error: ${msg}`)\n }\n } finally {\n setLoading(false)\n clearTimeout(timeoutId)\n }\n }, [setValue])\n\n return (\n <div style={{ marginBottom: '20px' }}>\n <Button buttonStyle=\"secondary\" disabled={loading} margin={false} onClick={fetchVoices} size=\"medium\">\n {loading ? 'Fetching Voices...' : 'Fetch Voices'}\n </Button>\n <p style={{ color: 'var(--theme-elevation-600)', fontSize: '13px', marginTop: '8px' }}>\n This will fetch all available voices from your ElevenLabs account. Make sure you have saved\n your API key in the Setup tab first.\n </p>\n </div>\n )\n}\n"],"names":["Button","toast","useField","React","useCallback","useState","PLUGIN_API_ENDPOINT_FETCH_VOICES","VoicesFetcher","path","loading","setLoading","fieldPath","blockPath","split","slice","join","voicesPath","setValue","fetchVoices","controller","AbortController","timeoutId","setTimeout","abort","response","fetch","headers","method","signal","clearTimeout","ok","errorMessage","error","json","message","e","status","statusText","Error","data","voices","success","length","msg","name","div","style","marginBottom","buttonStyle","disabled","margin","onClick","size","p","color","fontSize","marginTop"],"mappings":"AAAA;;AAIA,SAASA,MAAM,EAAEC,KAAK,EAAEC,QAAQ,QAAuB,iBAAgB;AACvE,OAAOC,SAASC,WAAW,EAAEC,QAAQ,QAAQ,QAAO;AAEpD,SAASC,gCAAgC,QAAQ,oBAAmB;AAWpE;;;;CAIC,GACD,OAAO,MAAMC,gBAAsC,CAAC,EAAEC,IAAI,EAAE;IAC1D,MAAM,CAACC,SAASC,WAAW,GAAGL,SAAS;IAEvC,uCAAuC;IACvC,MAAMM,YAAY,AAACH,QAAmB;IACtC,MAAMI,YAAYD,UAAUE,KAAK,CAAC,KAAKC,KAAK,CAAC,GAAG,CAAC,GAAGC,IAAI,CAAC;IACzD,MAAMC,aAAa,CAAC,EAAEJ,UAAU,OAAO,CAAC;IAExC,MAAM,EAAEK,QAAQ,EAAE,GAAGf,SAAkB;QAAEM,MAAMQ;IAAW;IAE1D,MAAME,cAAcd,YAAY;QAC9BM,WAAW;QAEX,MAAMS,aAAa,IAAIC;QACvB,MAAMC,YAAYC,WAAW,IAAMH,WAAWI,KAAK,IAAI,OAAO,cAAc;;QAE5E,IAAI;YACF,oEAAoE;YACpE,MAAMC,WAAW,MAAMC,MAAM,CAAC,IAAI,EAAEnB,iCAAiC,CAAC,EAAE;gBACtEoB,SAAS;oBACP,gBAAgB;gBAClB;gBACAC,QAAQ;gBACRC,QAAQT,WAAWS,MAAM;YAC3B;YAEAC,aAAaR;YAEb,IAAI,CAACG,SAASM,EAAE,EAAE;gBAChB,IAAIC,eAAe;gBACnB,IAAI;oBACF,MAAMC,QAAQ,MAAMR,SAASS,IAAI;oBACjCF,eAAeC,MAAME,OAAO,IAAIH;gBAClC,EAAE,OAAOI,GAAG;oBACV,2EAA2E;oBAC3EJ,eAAe,CAAC,MAAM,EAAEP,SAASY,MAAM,CAAC,EAAE,EAAEZ,SAASa,UAAU,CAAC,CAAC;gBACnE;gBACA,MAAM,IAAIC,MAAMP;YAClB;YAEA,MAAMQ,OAAO,MAAMf,SAASS,IAAI;YAChC,MAAMO,SAAkBD,KAAKC,MAAM,IAAI,EAAE;YAEzC,yCAAyC;YACzC,0EAA0E;YAC1EvB,SAASuB;YAETvC,MAAMwC,OAAO,CAAC,CAAC,qBAAqB,EAAED,OAAOE,MAAM,CAAC,QAAQ,CAAC;QAC/D,EAAE,OAAOV,OAAgB;YACvB,MAAMW,MAAMX,iBAAiBM,QAAQN,MAAME,OAAO,GAAG;YACrD,IAAIF,iBAAiBM,SAASN,MAAMY,IAAI,KAAK,cAAc;gBACzD3C,MAAM+B,KAAK,CAAC;YACd,OAAO;gBACL/B,MAAM+B,KAAK,CAAC,CAAC,OAAO,EAAEW,IAAI,CAAC;YAC7B;QACF,SAAU;YACRjC,WAAW;YACXmB,aAAaR;QACf;IACF,GAAG;QAACJ;KAAS;IAEb,qBACE,MAAC4B;QAAIC,OAAO;YAAEC,cAAc;QAAO;;0BACjC,KAAC/C;gBAAOgD,aAAY;gBAAYC,UAAUxC;gBAASyC,QAAQ;gBAAOC,SAASjC;gBAAakC,MAAK;0BAC1F3C,UAAU,uBAAuB;;0BAEpC,KAAC4C;gBAAEP,OAAO;oBAAEQ,OAAO;oBAA8BC,UAAU;oBAAQC,WAAW;gBAAM;0BAAG;;;;AAM7F,EAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import { Button,
|
|
2
|
+
import { Button, toast, useField } from '@payloadcms/ui';
|
|
3
3
|
import React, { useCallback, useState } from 'react';
|
|
4
4
|
import { PLUGIN_API_ENDPOINT_FETCH_VOICES } from '../../defaults.js';
|
|
5
5
|
/**
|
|
@@ -9,13 +9,15 @@ import { PLUGIN_API_ENDPOINT_FETCH_VOICES } from '../../defaults.js';
|
|
|
9
9
|
*/
|
|
10
10
|
export const VoicesFetcher = ({ path }) => {
|
|
11
11
|
const [loading, setLoading] = useState(false);
|
|
12
|
-
const { dispatchFields } = useForm();
|
|
13
12
|
// Get the parent path (the block path)
|
|
14
13
|
const fieldPath = path || '';
|
|
15
14
|
const blockPath = fieldPath.split('.').slice(0, -1).join('.');
|
|
16
15
|
const voicesPath = `${blockPath}.voices`;
|
|
16
|
+
const { setValue } = useField({ path: voicesPath });
|
|
17
17
|
const fetchVoices = useCallback(async () => {
|
|
18
18
|
setLoading(true);
|
|
19
|
+
const controller = new AbortController();
|
|
20
|
+
const timeoutId = setTimeout(() => controller.abort(), 30000); // 30s timeout
|
|
19
21
|
try {
|
|
20
22
|
// Call server endpoint - it will read the API key from the database
|
|
21
23
|
const response = await fetch(`/api${PLUGIN_API_ENDPOINT_FETCH_VOICES}`, {
|
|
@@ -23,30 +25,45 @@ export const VoicesFetcher = ({ path }) => {
|
|
|
23
25
|
'Content-Type': 'application/json',
|
|
24
26
|
},
|
|
25
27
|
method: 'POST',
|
|
28
|
+
signal: controller.signal,
|
|
26
29
|
});
|
|
30
|
+
clearTimeout(timeoutId);
|
|
27
31
|
if (!response.ok) {
|
|
28
|
-
|
|
29
|
-
|
|
32
|
+
let errorMessage = 'Failed to fetch voices';
|
|
33
|
+
try {
|
|
34
|
+
const error = await response.json();
|
|
35
|
+
errorMessage = error.message || errorMessage;
|
|
36
|
+
}
|
|
37
|
+
catch (e) {
|
|
38
|
+
// If response is not JSON (e.g. 504 Gateway Timeout HTML), use status text
|
|
39
|
+
errorMessage = `Error ${response.status}: ${response.statusText}`;
|
|
40
|
+
}
|
|
41
|
+
throw new Error(errorMessage);
|
|
30
42
|
}
|
|
31
43
|
const data = await response.json();
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
});
|
|
38
|
-
alert(`Successfully fetched ${data.voices?.length || 0} voices!`);
|
|
44
|
+
const voices = data.voices || [];
|
|
45
|
+
// Replace the entire array value at once
|
|
46
|
+
// This is much more performant than dispatching ADD_ROW actions in a loop
|
|
47
|
+
setValue(voices);
|
|
48
|
+
toast.success(`Successfully fetched ${voices.length} voices!`);
|
|
39
49
|
}
|
|
40
50
|
catch (error) {
|
|
41
|
-
|
|
51
|
+
const msg = error instanceof Error ? error.message : 'Failed to fetch voices';
|
|
52
|
+
if (error instanceof Error && error.name === 'AbortError') {
|
|
53
|
+
toast.error('Request timed out. Please try again.');
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
toast.error(`Error: ${msg}`);
|
|
57
|
+
}
|
|
42
58
|
}
|
|
43
59
|
finally {
|
|
44
60
|
setLoading(false);
|
|
61
|
+
clearTimeout(timeoutId);
|
|
45
62
|
}
|
|
46
|
-
}, [
|
|
63
|
+
}, [setValue]);
|
|
47
64
|
return (<div style={{ marginBottom: '20px' }}>
|
|
48
|
-
<Button buttonStyle="secondary" disabled={loading} onClick={fetchVoices} size="medium">
|
|
49
|
-
{loading ? 'Fetching Voices...' : 'Fetch Voices
|
|
65
|
+
<Button buttonStyle="secondary" disabled={loading} margin={false} onClick={fetchVoices} size="medium">
|
|
66
|
+
{loading ? 'Fetching Voices...' : 'Fetch Voices'}
|
|
50
67
|
</Button>
|
|
51
68
|
<p style={{ color: 'var(--theme-elevation-600)', fontSize: '13px', marginTop: '8px' }}>
|
|
52
69
|
This will fetch all available voices from your ElevenLabs account. Make sure you have saved
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Payload } from 'payload';
|
|
2
|
+
export interface SmartPromptContext {
|
|
3
|
+
/** The document data for template interpolation */
|
|
4
|
+
documentData?: Record<string, unknown>;
|
|
5
|
+
/** The Payload instance to access collection config */
|
|
6
|
+
payload: Payload;
|
|
7
|
+
/** The schema path like 'array-test-cases.teamMembers.contact.email' */
|
|
8
|
+
schemaPath: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Build a smart contextual prompt based on field metadata.
|
|
12
|
+
* This is used as a fallback when the user hasn't set a custom prompt.
|
|
13
|
+
*
|
|
14
|
+
* @param context - The context containing schema path and document data
|
|
15
|
+
* @returns A contextual prompt string that can be used for AI generation
|
|
16
|
+
*/
|
|
17
|
+
export declare const buildSmartPrompt: (context: SmartPromptContext) => string;
|
|
18
|
+
/**
|
|
19
|
+
* Check if a prompt template is empty and should be replaced with a smart prompt.
|
|
20
|
+
* Only triggers when the prompt is completely empty or whitespace-only.
|
|
21
|
+
*/
|
|
22
|
+
export declare const isGenericPrompt: (template: null | string | undefined) => boolean;
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
import { getFieldBySchemaPath } from './getFieldBySchemaPath.js';
|
|
3
|
+
/**
|
|
4
|
+
* Extract field information from a schema path
|
|
5
|
+
*/ const getFieldInfo = (schemaPath, payload)=>{
|
|
6
|
+
const parts = schemaPath.split('.');
|
|
7
|
+
const collectionSlug = parts[0];
|
|
8
|
+
const fieldPath = parts.slice(1);
|
|
9
|
+
const fieldName = fieldPath[fieldPath.length - 1] || '';
|
|
10
|
+
// Get parent context (e.g., 'teamMembers' for 'teamMembers.name')
|
|
11
|
+
let parentContext = null;
|
|
12
|
+
if (fieldPath.length > 1) {
|
|
13
|
+
parentContext = fieldPath[fieldPath.length - 2];
|
|
14
|
+
}
|
|
15
|
+
// Try to get the actual field configuration from the collection
|
|
16
|
+
let field = null;
|
|
17
|
+
const collection = payload.config.collections.find((c)=>c.slug === collectionSlug);
|
|
18
|
+
if (collection) {
|
|
19
|
+
field = getFieldBySchemaPath(collection, schemaPath);
|
|
20
|
+
}
|
|
21
|
+
return {
|
|
22
|
+
name: fieldName,
|
|
23
|
+
type: field?.type || 'text',
|
|
24
|
+
field,
|
|
25
|
+
label: field?.label || fieldName,
|
|
26
|
+
parentContext
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Humanize a camelCase or snake_case field name
|
|
31
|
+
* e.g., 'teamMembers' -> 'team members', 'first_name' -> 'first name'
|
|
32
|
+
*/ const humanize = (str)=>{
|
|
33
|
+
return str.replace(/([a-z])([A-Z])/g, '$1 $2') // camelCase to spaces
|
|
34
|
+
.replace(/[_-]/g, ' ') // underscores/dashes to spaces
|
|
35
|
+
.toLowerCase().trim();
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Get a description snippet from field admin config
|
|
39
|
+
*/ const getFieldDescription = (field)=>{
|
|
40
|
+
if (!field) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
const admin = field.admin;
|
|
44
|
+
if (admin?.description && typeof admin.description === 'string') {
|
|
45
|
+
return admin.description;
|
|
46
|
+
}
|
|
47
|
+
return null;
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Build type-specific prompt guidance
|
|
51
|
+
*/ const getTypeGuidance = (type, fieldName)=>{
|
|
52
|
+
const nameHint = humanize(fieldName);
|
|
53
|
+
switch(type){
|
|
54
|
+
case 'code':
|
|
55
|
+
return `Generate code for ${nameHint}`;
|
|
56
|
+
case 'date':
|
|
57
|
+
return `Generate an appropriate date for ${nameHint}`;
|
|
58
|
+
case 'email':
|
|
59
|
+
return `Generate a valid professional email address`;
|
|
60
|
+
case 'json':
|
|
61
|
+
return `Generate valid JSON data for ${nameHint}`;
|
|
62
|
+
case 'number':
|
|
63
|
+
return `Generate an appropriate numeric value for ${nameHint}`;
|
|
64
|
+
case 'select':
|
|
65
|
+
return `Select an appropriate option for ${nameHint}`;
|
|
66
|
+
case 'text':
|
|
67
|
+
return `Generate appropriate text for ${nameHint}`;
|
|
68
|
+
case 'textarea':
|
|
69
|
+
return `Write detailed content for ${nameHint}`;
|
|
70
|
+
case 'upload':
|
|
71
|
+
// Explicit image generation instruction for multimodal models
|
|
72
|
+
return `Generate an image of ${nameHint}`;
|
|
73
|
+
default:
|
|
74
|
+
return `Generate content for ${nameHint}`;
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* Build context from parent field name using generic humanization.
|
|
79
|
+
* Works universally for any collection structure.
|
|
80
|
+
*/ const getParentContextPhrase = (parentContext)=>{
|
|
81
|
+
if (!parentContext) {
|
|
82
|
+
return '';
|
|
83
|
+
}
|
|
84
|
+
const humanized = humanize(parentContext);
|
|
85
|
+
// Use singular form if the name ends with 's' (common for arrays)
|
|
86
|
+
// e.g., "teamMembers" → "team member", "products" → "product"
|
|
87
|
+
if (humanized.endsWith('s') && humanized.length > 2) {
|
|
88
|
+
return `for a ${humanized.slice(0, -1)} entry`;
|
|
89
|
+
}
|
|
90
|
+
return `for ${humanized}`;
|
|
91
|
+
};
|
|
92
|
+
/**
|
|
93
|
+
* Build a smart contextual prompt based on field metadata.
|
|
94
|
+
* This is used as a fallback when the user hasn't set a custom prompt.
|
|
95
|
+
*
|
|
96
|
+
* @param context - The context containing schema path and document data
|
|
97
|
+
* @returns A contextual prompt string that can be used for AI generation
|
|
98
|
+
*/ export const buildSmartPrompt = (context)=>{
|
|
99
|
+
const { documentData, payload, schemaPath } = context;
|
|
100
|
+
const fieldInfo = getFieldInfo(schemaPath, payload);
|
|
101
|
+
const { name, type, field, label, parentContext } = fieldInfo;
|
|
102
|
+
// Start with the field's own description if available
|
|
103
|
+
const description = getFieldDescription(field);
|
|
104
|
+
// Build the prompt components
|
|
105
|
+
const parts = [];
|
|
106
|
+
// Use description as primary guidance if available
|
|
107
|
+
if (description) {
|
|
108
|
+
parts.push(`Field description for user: ${description}\n`);
|
|
109
|
+
}
|
|
110
|
+
parts.push(getTypeGuidance(type, label || name));
|
|
111
|
+
// Add parent context if nested
|
|
112
|
+
const parentPhrase = getParentContextPhrase(parentContext);
|
|
113
|
+
if (parentPhrase) {
|
|
114
|
+
parts.push(parentPhrase);
|
|
115
|
+
}
|
|
116
|
+
// Add document title context if available
|
|
117
|
+
const title = documentData?.title || documentData?.name;
|
|
118
|
+
if (title && typeof title === 'string') {
|
|
119
|
+
parts.push(`in the context of "${title}"`);
|
|
120
|
+
}
|
|
121
|
+
// Build the final prompt
|
|
122
|
+
let prompt = parts.join(' ');
|
|
123
|
+
// Ensure first letter is capitalized
|
|
124
|
+
prompt = prompt.charAt(0).toUpperCase() + prompt.slice(1);
|
|
125
|
+
// Add instruction suffix for clarity
|
|
126
|
+
if (!prompt.endsWith('.')) {
|
|
127
|
+
prompt += '.';
|
|
128
|
+
}
|
|
129
|
+
return prompt;
|
|
130
|
+
};
|
|
131
|
+
/**
|
|
132
|
+
* Check if a prompt template is empty and should be replaced with a smart prompt.
|
|
133
|
+
* Only triggers when the prompt is completely empty or whitespace-only.
|
|
134
|
+
*/ export const isGenericPrompt = (template)=>{
|
|
135
|
+
if (!template) {
|
|
136
|
+
return true;
|
|
137
|
+
}
|
|
138
|
+
return template.trim() === '';
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
//# sourceMappingURL=buildSmartPrompt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/utilities/buildSmartPrompt.ts"],"sourcesContent":["'use strict'\n\nimport type { Field, Payload } from 'payload'\n\nimport { getFieldBySchemaPath } from './getFieldBySchemaPath.js'\n\nexport interface SmartPromptContext {\n /** The document data for template interpolation */\n documentData?: Record<string, unknown>\n /** The Payload instance to access collection config */\n payload: Payload\n /** The schema path like 'array-test-cases.teamMembers.contact.email' */\n schemaPath: string\n}\n\ninterface FieldInfo {\n /** The field configuration */\n field: Field | null\n /** Human-readable field label */\n label: string\n /** Field name from the path */\n name: string\n /** Parent field name if nested (e.g., 'teamMembers' for 'teamMembers.name') */\n parentContext: null | string\n /** The field type */\n type: string\n}\n\n/**\n * Extract field information from a schema path\n */\nconst getFieldInfo = (schemaPath: string, payload: Payload): FieldInfo => {\n const parts = schemaPath.split('.')\n const collectionSlug = parts[0]\n const fieldPath = parts.slice(1)\n const fieldName = fieldPath[fieldPath.length - 1] || ''\n\n // Get parent context (e.g., 'teamMembers' for 'teamMembers.name')\n let parentContext: null | string = null\n if (fieldPath.length > 1) {\n parentContext = fieldPath[fieldPath.length - 2]\n }\n\n // Try to get the actual field configuration from the collection\n let field: Field | null = null\n const collection = payload.config.collections.find((c) => c.slug === collectionSlug)\n if (collection) {\n field = getFieldBySchemaPath(collection, schemaPath)\n }\n\n return {\n name: fieldName,\n type: field?.type || 'text',\n field,\n label: (field as { label?: string })?.label || fieldName,\n parentContext,\n }\n}\n\n/**\n * Humanize a camelCase or snake_case field name\n * e.g., 'teamMembers' -> 'team members', 'first_name' -> 'first name'\n */\nconst humanize = (str: string): string => {\n return str\n .replace(/([a-z])([A-Z])/g, '$1 $2') // camelCase to spaces\n .replace(/[_-]/g, ' ') // underscores/dashes to spaces\n .toLowerCase()\n .trim()\n}\n\n/**\n * Get a description snippet from field admin config\n */\nconst getFieldDescription = (field: Field | null): null | string => {\n if (!field) {\n return null\n }\n const admin = (field as { admin?: { description?: string } }).admin\n if (admin?.description && typeof admin.description === 'string') {\n return admin.description\n }\n return null\n}\n\n/**\n * Build type-specific prompt guidance\n */\nconst getTypeGuidance = (type: string, fieldName: string): string => {\n const nameHint = humanize(fieldName)\n\n switch (type) {\n case 'code':\n return `Generate code for ${nameHint}`\n case 'date':\n return `Generate an appropriate date for ${nameHint}`\n case 'email':\n return `Generate a valid professional email address`\n case 'json':\n return `Generate valid JSON data for ${nameHint}`\n case 'number':\n return `Generate an appropriate numeric value for ${nameHint}`\n case 'select':\n return `Select an appropriate option for ${nameHint}`\n case 'text':\n return `Generate appropriate text for ${nameHint}`\n case 'textarea':\n return `Write detailed content for ${nameHint}`\n case 'upload':\n // Explicit image generation instruction for multimodal models\n return `Generate an image of ${nameHint}`\n default:\n return `Generate content for ${nameHint}`\n }\n}\n\n/**\n * Build context from parent field name using generic humanization.\n * Works universally for any collection structure.\n */\nconst getParentContextPhrase = (parentContext: null | string): string => {\n if (!parentContext) {\n return ''\n }\n\n const humanized = humanize(parentContext)\n\n // Use singular form if the name ends with 's' (common for arrays)\n // e.g., \"teamMembers\" → \"team member\", \"products\" → \"product\"\n if (humanized.endsWith('s') && humanized.length > 2) {\n return `for a ${humanized.slice(0, -1)} entry`\n }\n\n return `for ${humanized}`\n}\n\n/**\n * Build a smart contextual prompt based on field metadata.\n * This is used as a fallback when the user hasn't set a custom prompt.\n *\n * @param context - The context containing schema path and document data\n * @returns A contextual prompt string that can be used for AI generation\n */\nexport const buildSmartPrompt = (context: SmartPromptContext): string => {\n const { documentData, payload, schemaPath } = context\n\n const fieldInfo = getFieldInfo(schemaPath, payload)\n const { name, type, field, label, parentContext } = fieldInfo\n\n // Start with the field's own description if available\n const description = getFieldDescription(field)\n\n // Build the prompt components\n const parts: string[] = []\n\n // Use description as primary guidance if available\n if (description) {\n parts.push(`Field description for user: ${description}\\n`)\n }\n\n parts.push(getTypeGuidance(type, label || name))\n\n // Add parent context if nested\n const parentPhrase = getParentContextPhrase(parentContext)\n if (parentPhrase) {\n parts.push(parentPhrase)\n }\n\n // Add document title context if available\n const title = documentData?.title || documentData?.name\n\n if (title && typeof title === 'string') {\n parts.push(`in the context of \"${title}\"`)\n }\n\n // Build the final prompt\n let prompt = parts.join(' ')\n\n // Ensure first letter is capitalized\n prompt = prompt.charAt(0).toUpperCase() + prompt.slice(1)\n\n // Add instruction suffix for clarity\n if (!prompt.endsWith('.')) {\n prompt += '.'\n }\n\n return prompt\n}\n\n/**\n * Check if a prompt template is empty and should be replaced with a smart prompt.\n * Only triggers when the prompt is completely empty or whitespace-only.\n */\nexport const isGenericPrompt = (template: null | string | undefined): boolean => {\n if (!template) {\n return true\n }\n return template.trim() === ''\n}\n"],"names":["getFieldBySchemaPath","getFieldInfo","schemaPath","payload","parts","split","collectionSlug","fieldPath","slice","fieldName","length","parentContext","field","collection","config","collections","find","c","slug","name","type","label","humanize","str","replace","toLowerCase","trim","getFieldDescription","admin","description","getTypeGuidance","nameHint","getParentContextPhrase","humanized","endsWith","buildSmartPrompt","context","documentData","fieldInfo","push","parentPhrase","title","prompt","join","charAt","toUpperCase","isGenericPrompt","template"],"mappings":"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"}
|
|
@@ -17,7 +17,8 @@ export function encrypt(text, secret) {
|
|
|
17
17
|
encrypted,
|
|
18
18
|
cipher.final()
|
|
19
19
|
]);
|
|
20
|
-
|
|
20
|
+
const result = iv.toString('hex') + ':' + encrypted.toString('hex');
|
|
21
|
+
return result;
|
|
21
22
|
}
|
|
22
23
|
export function decrypt(text, secret) {
|
|
23
24
|
if (!text) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utilities/encryption.ts"],"sourcesContent":["import crypto from 'crypto'\n\nconst algorithm = 'aes-256-cbc'\nconst ivLength = 16\n\nexport function encrypt(text: string, secret: string): string {\n if (!text) {return text}\n if (!secret) {throw new Error('No secret provided for encryption')}\n\n // Ensure secret is 32 bytes\n const key = crypto.createHash('sha256').update(secret).digest()\n const iv = crypto.randomBytes(ivLength)\n const cipher = crypto.createCipheriv(algorithm, key, iv)\n let encrypted = cipher.update(text)\n encrypted = Buffer.concat([encrypted, cipher.final()])\n
|
|
1
|
+
{"version":3,"sources":["../../src/utilities/encryption.ts"],"sourcesContent":["import crypto from 'crypto'\n\nconst algorithm = 'aes-256-cbc'\nconst ivLength = 16\n\nexport function encrypt(text: string, secret: string): string {\n if (!text) {\n return text\n }\n if (!secret) {\n throw new Error('No secret provided for encryption')\n }\n\n // Ensure secret is 32 bytes\n const key = crypto.createHash('sha256').update(secret).digest()\n const iv = crypto.randomBytes(ivLength)\n const cipher = crypto.createCipheriv(algorithm, key, iv)\n let encrypted = cipher.update(text)\n encrypted = Buffer.concat([encrypted, cipher.final()])\n const result = iv.toString('hex') + ':' + encrypted.toString('hex')\n\n return result\n}\n\nexport function decrypt(text: string, secret: string): string {\n if (!text) {\n return text\n }\n if (!secret) {\n throw new Error('No secret provided for decryption')\n }\n\n try {\n const textParts = text.split(':')\n const iv = Buffer.from(textParts.shift()!, 'hex')\n const encryptedText = Buffer.from(textParts.join(':'), 'hex')\n const key = crypto.createHash('sha256').update(secret).digest()\n const decipher = crypto.createDecipheriv(algorithm, key, iv)\n let decrypted = decipher.update(encryptedText)\n decrypted = Buffer.concat([decrypted, decipher.final()])\n return decrypted.toString()\n } catch (e) {\n // If decryption fails, return original text (might be already plain or invalid)\n return text\n }\n}\n"],"names":["crypto","algorithm","ivLength","encrypt","text","secret","Error","key","createHash","update","digest","iv","randomBytes","cipher","createCipheriv","encrypted","Buffer","concat","final","result","toString","decrypt","textParts","split","from","shift","encryptedText","join","decipher","createDecipheriv","decrypted","e"],"mappings":"AAAA,OAAOA,YAAY,SAAQ;AAE3B,MAAMC,YAAY;AAClB,MAAMC,WAAW;AAEjB,OAAO,SAASC,QAAQC,IAAY,EAAEC,MAAc;IAClD,IAAI,CAACD,MAAM;QACT,OAAOA;IACT;IACA,IAAI,CAACC,QAAQ;QACX,MAAM,IAAIC,MAAM;IAClB;IAEA,4BAA4B;IAC5B,MAAMC,MAAMP,OAAOQ,UAAU,CAAC,UAAUC,MAAM,CAACJ,QAAQK,MAAM;IAC7D,MAAMC,KAAKX,OAAOY,WAAW,CAACV;IAC9B,MAAMW,SAASb,OAAOc,cAAc,CAACb,WAAWM,KAAKI;IACrD,IAAII,YAAYF,OAAOJ,MAAM,CAACL;IAC9BW,YAAYC,OAAOC,MAAM,CAAC;QAACF;QAAWF,OAAOK,KAAK;KAAG;IACrD,MAAMC,SAASR,GAAGS,QAAQ,CAAC,SAAS,MAAML,UAAUK,QAAQ,CAAC;IAE7D,OAAOD;AACT;AAEA,OAAO,SAASE,QAAQjB,IAAY,EAAEC,MAAc;IAClD,IAAI,CAACD,MAAM;QACT,OAAOA;IACT;IACA,IAAI,CAACC,QAAQ;QACX,MAAM,IAAIC,MAAM;IAClB;IAEA,IAAI;QACF,MAAMgB,YAAYlB,KAAKmB,KAAK,CAAC;QAC7B,MAAMZ,KAAKK,OAAOQ,IAAI,CAACF,UAAUG,KAAK,IAAK;QAC3C,MAAMC,gBAAgBV,OAAOQ,IAAI,CAACF,UAAUK,IAAI,CAAC,MAAM;QACvD,MAAMpB,MAAMP,OAAOQ,UAAU,CAAC,UAAUC,MAAM,CAACJ,QAAQK,MAAM;QAC7D,MAAMkB,WAAW5B,OAAO6B,gBAAgB,CAAC5B,WAAWM,KAAKI;QACzD,IAAImB,YAAYF,SAASnB,MAAM,CAACiB;QAChCI,YAAYd,OAAOC,MAAM,CAAC;YAACa;YAAWF,SAASV,KAAK;SAAG;QACvD,OAAOY,UAAUV,QAAQ;IAC3B,EAAE,OAAOW,GAAG;QACV,gFAAgF;QAChF,OAAO3B;IACT;AACF"}
|
|
@@ -138,7 +138,7 @@ export function fieldToJsonSchema(fieldInput, opts) {
|
|
|
138
138
|
}
|
|
139
139
|
continue;
|
|
140
140
|
}
|
|
141
|
-
if (!subField.name) {
|
|
141
|
+
if (!subField.name || subField.name === 'id') {
|
|
142
142
|
continue;
|
|
143
143
|
}
|
|
144
144
|
const subSchema = fieldToJsonSchema(subField, {
|
|
@@ -146,8 +146,31 @@ export function fieldToJsonSchema(fieldInput, opts) {
|
|
|
146
146
|
});
|
|
147
147
|
if (subSchema && Object.keys(subSchema).length > 0) {
|
|
148
148
|
properties[subField.name] = subSchema;
|
|
149
|
-
|
|
150
|
-
|
|
149
|
+
// OpenAI Strict Mode: All fields must be required
|
|
150
|
+
required.push(subField.name);
|
|
151
|
+
// If field is optional in Payload, allow null in schema
|
|
152
|
+
if (!subField.required) {
|
|
153
|
+
// Arrays usually default to empty array [], so we don't make them nullable
|
|
154
|
+
// Groups and other types can be null
|
|
155
|
+
if (subSchema.type !== 'array') {
|
|
156
|
+
if (Array.isArray(subSchema.type)) {
|
|
157
|
+
if (!subSchema.type.includes('null')) {
|
|
158
|
+
subSchema.type.push('null');
|
|
159
|
+
}
|
|
160
|
+
} else if (typeof subSchema.type === 'string' && subSchema.type !== 'null') {
|
|
161
|
+
subSchema.type = [
|
|
162
|
+
subSchema.type,
|
|
163
|
+
'null'
|
|
164
|
+
];
|
|
165
|
+
}
|
|
166
|
+
// If enum is present, we must allow null in enum or use anyOf?
|
|
167
|
+
// OpenAI strict mode: "enum values must be consistent with type".
|
|
168
|
+
// If type is [string, null], null is a valid value for the type, but if enum is ["a"], null is not in enum.
|
|
169
|
+
// valid: { type: ["string", "null"], enum: ["a", null] }
|
|
170
|
+
if (Array.isArray(subSchema.enum) && !subSchema.enum.includes(null)) {
|
|
171
|
+
subSchema.enum.push(null);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
151
174
|
}
|
|
152
175
|
}
|
|
153
176
|
}
|
|
@@ -164,6 +187,12 @@ export function fieldToJsonSchema(fieldInput, opts) {
|
|
|
164
187
|
type: 'array',
|
|
165
188
|
items: objSchema
|
|
166
189
|
};
|
|
190
|
+
if (typeof field.maxRows === 'number') {
|
|
191
|
+
valueSchema.maxItems = field.maxRows;
|
|
192
|
+
}
|
|
193
|
+
if (typeof field.minRows === 'number') {
|
|
194
|
+
valueSchema.minItems = field.minRows;
|
|
195
|
+
}
|
|
167
196
|
} else {
|
|
168
197
|
valueSchema = objSchema;
|
|
169
198
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utilities/fieldToJsonSchema.ts"],"sourcesContent":["/**\n * fieldToJsonSchema\n * Convert a Payload Field (server or client) into a minimal JSON Schema object,\n * wrapped as { type: 'object', properties: { [name]: valueSchema }, required: [...] }\n *\n * Supported types:\n * - text, textarea, select, number, date, code, email, json\n * Arrays are emitted only when field.hasMany is true and the field type supports hasMany\n * (text, textarea, select; for others only if your config truly sets hasMany).\n */\nimport type { Field } from 'payload'\n\nexport type JsonSchema = Record<string, any>;\n\n// Narrowed minimal typing to avoid anywhere possible without pulling the full payload types at runtime.\n// In a Payload project, you can import type { Field, ClientField } from 'payload' and use those instead.\ntype BaseField = {\n admin?: {\n description?: unknown;\n language?: unknown;\n };\n hasMany?: boolean;\n // json\n jsonSchema?: unknown;\n max?: number;\n maxRows?: number;\n // number\n min?: number;\n // text/textarea\n minRows?: number;\n name?: string;\n // select\n options?: Array<\n | {\n label?: unknown;\n value: number | string;\n }\n | number\n | string\n >;\n required?: boolean;\n schema?: unknown;\n type?: string;\n typescriptSchema?: unknown;\n};\n\nfunction isString(s: unknown): s is string {\n return typeof s === 'string';\n}\n\nfunction isPlainObject(o: unknown): o is Record<string, unknown> {\n return !!o && typeof o === 'object' && !Array.isArray(o);\n}\n\nfunction getDescription(field: BaseField): string | undefined {\n const d = field?.admin?.description;\n return typeof d === 'string' ? d : undefined;\n}\n\nfunction stringWithDescription(field: BaseField) {\n const out: Record<string, any> = { type: 'string' };\n const description = getDescription(field);\n if (description) {out.description = description;}\n return out;\n}\n\nfunction numberWithBounds(field: BaseField) {\n const out: Record<string, any> = { type: 'number' };\n if (typeof field.min === 'number') {out.minimum = field.min;}\n if (typeof field.max === 'number') {out.maximum = field.max;}\n const description = getDescription(field);\n if (description) {out.description = description;}\n return out;\n}\n\nfunction dateSchema(field: BaseField) {\n const out: Record<string, any> = { type: 'string', format: 'date-time' };\n const description = getDescription(field);\n if (description) {out.description = description;}\n return out;\n}\n\nfunction codeSchema(field: BaseField) {\n const out: Record<string, any> = { type: 'string' };\n let description = getDescription(field);\n const lang = field?.admin?.language;\n if (typeof lang === 'string' && lang.trim()) {\n description = description ? `${description} (language: ${lang})` : `language: ${lang}`;\n }\n if (description) {out.description = description;}\n return out;\n}\n\nfunction emailSchema(field: BaseField) {\n const out: Record<string, any> = { type: 'string', format: 'email' };\n const description = getDescription(field);\n if (description) {out.description = description;}\n return out;\n}\n\nfunction jsonValueSchema(field: BaseField) {\n // Prefer a provided JSON Schema object\n if (isPlainObject(field.jsonSchema)) {return field.jsonSchema as object;}\n if (isPlainObject(field.schema)) {return field.schema as object;}\n\n // typescriptSchema cannot be executed here; default to object\n return { type: 'object' };\n}\n\nfunction normalizeSelectOptions(field: BaseField): { values: Array<number | string>; valueType: 'number' | 'string' } {\n const raw = field.options || [];\n const values: Array<number | string> = [];\n\n for (const opt of raw) {\n if (typeof opt === 'string' || typeof opt === 'number') {\n values.push(opt);\n } else if (isPlainObject(opt) && ('value' in opt)) {\n const v = (opt as any).value;\n if (typeof v === 'string' || typeof v === 'number') {\n values.push(v);\n }\n }\n }\n\n // Infer primitive type\n const allNumbers = values.length > 0 && values.every((v) => typeof v === 'number');\n const valueType: 'number' | 'string' = allNumbers ? 'number' : 'string';\n return { values, valueType };\n}\n\nfunction supportsHasMany(fieldType: string | undefined): boolean {\n // Out of the box: text, textarea, select support hasMany\n // Others can be arrays only if your config truly sets hasMany; we return boolean based on type here.\n return fieldType === 'text' || fieldType === 'textarea' || fieldType === 'select';\n}\n\nexport function fieldToJsonSchema(\n fieldInput: BaseField & Field,\n opts?: { nameOverride?: string; wrapObject?: boolean },\n): JsonSchema {\n const field = fieldInput || {};\n const name = isString(opts?.nameOverride) && opts?.nameOverride.length ? opts.nameOverride : (field.name || 'value');\n const type = field.type;\n\n let valueSchema: null | Record<string, any> = null;\n\n switch (type) {\n case 'array':\n case 'group': {\n if ('fields' in field && Array.isArray(field.fields)) {\n const properties: Record<string, any> = {}\n const required: string[] = []\n \n const processFields = (fields: any[]) => {\n for (const subField of fields) {\n if (subField.type === 'row' || subField.type === 'collapsible') {\n if (subField.fields) {processFields(subField.fields)}\n continue\n }\n if (!subField.name) {continue}\n\n const subSchema = fieldToJsonSchema(subField, { wrapObject: false })\n if (subSchema && Object.keys(subSchema).length > 0) {\n properties[subField.name] = subSchema\n if (subField.required) {required.push(subField.name)}\n }\n }\n }\n \n processFields(field.fields)\n\n const objSchema = {\n type: 'object',\n additionalProperties: false,\n properties,\n required: required.length ? required : undefined\n }\n\n if (type === 'array') {\n valueSchema = {\n type: 'array',\n items: objSchema\n }\n } else {\n valueSchema = objSchema\n }\n \n const description = getDescription(field);\n if (description) {valueSchema.description = description;}\n }\n break;\n }\n\n case 'checkbox': {\n valueSchema = { type: 'boolean' };\n const description = getDescription(field);\n if (description) {valueSchema.description = description;}\n break;\n }\n\n case 'code': {\n const base = codeSchema(field);\n if (field.hasMany) {\n valueSchema = { type: 'array', items: base };\n } else {\n valueSchema = base;\n }\n break;\n }\n\n case 'date': {\n const base = dateSchema(field);\n if (field.hasMany) {\n valueSchema = { type: 'array', items: base };\n } else {\n valueSchema = base;\n }\n break;\n }\n\n case 'email': {\n const base = emailSchema(field);\n if (field.hasMany) {\n valueSchema = { type: 'array', items: base };\n } else {\n valueSchema = base;\n }\n break;\n }\n\n case 'json': {\n const base = jsonValueSchema(field);\n if (field.hasMany) {\n valueSchema = { type: 'array', items: base };\n } else {\n valueSchema = base as Record<string, any>;\n }\n break;\n }\n\n case 'number': {\n const base = numberWithBounds(field);\n if (field.hasMany) {\n valueSchema = { type: 'array', items: base };\n } else {\n valueSchema = base;\n }\n break;\n }\n case 'select': {\n const { values, valueType } = normalizeSelectOptions(field);\n const baseSingle: Record<string, any> = { type: valueType, enum: values };\n const description = getDescription(field);\n if (description) {baseSingle.description = description;}\n\n if (field.hasMany && supportsHasMany(type)) {\n valueSchema = {\n type: 'array',\n items: { type: valueType, enum: values },\n ...(description ? { description } : {}),\n };\n } else {\n valueSchema = baseSingle;\n }\n break;\n }\n\n case 'text':\n case 'textarea': {\n const base = stringWithDescription(field);\n if (field.hasMany && supportsHasMany(type)) {\n const arr: Record<string, any> = {\n type: 'array',\n items: { type: 'string' },\n };\n if (typeof field.minRows === 'number') {arr.minItems = field.minRows;}\n if (typeof field.maxRows === 'number') {arr.maxItems = field.maxRows;}\n if (base.description) {arr.description = base.description;}\n valueSchema = arr;\n } else {\n valueSchema = base;\n }\n break;\n }\n \n // Explicitly handle organizational types to avoid default breakage if passed directly\n // though usually they are handled by parent recursion in group/array case above.\n // If passed as root, we can't really return a meaningful single-value schema without a wrapper?\n // But let's leave default for them or handle if necessary. \n // For now, if someone passes a 'row' as root, it will fall to default (null).\n\n default: {\n valueSchema = null;\n break;\n }\n }\n\n const wrap = opts?.wrapObject !== false;\n\n if (!wrap) {\n return (valueSchema || {}) as JsonSchema;\n }\n\n if (!valueSchema) {\n return {} as JsonSchema;\n }\n\n const schema: JsonSchema = {\n type: 'object',\n additionalProperties: false,\n properties: {\n [name]: valueSchema,\n },\n required: [name]\n };\n\n return schema;\n}\n"],"names":["isString","s","isPlainObject","o","Array","isArray","getDescription","field","d","admin","description","undefined","stringWithDescription","out","type","numberWithBounds","min","minimum","max","maximum","dateSchema","format","codeSchema","lang","language","trim","emailSchema","jsonValueSchema","jsonSchema","schema","normalizeSelectOptions","raw","options","values","opt","push","v","value","allNumbers","length","every","valueType","supportsHasMany","fieldType","fieldToJsonSchema","fieldInput","opts","name","nameOverride","valueSchema","fields","properties","required","processFields","subField","subSchema","wrapObject","Object","keys","objSchema","additionalProperties","items","base","hasMany","baseSingle","enum","arr","minRows","minItems","maxRows","maxItems","wrap"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/utilities/fieldToJsonSchema.ts"],"sourcesContent":["/**\n * fieldToJsonSchema\n * Convert a Payload Field (server or client) into a minimal JSON Schema object,\n * wrapped as { type: 'object', properties: { [name]: valueSchema }, required: [...] }\n *\n * Supported types:\n * - text, textarea, select, number, date, code, email, json\n * Arrays are emitted only when field.hasMany is true and the field type supports hasMany\n * (text, textarea, select; for others only if your config truly sets hasMany).\n */\nimport type { Field } from 'payload'\n\nexport type JsonSchema = Record<string, any>;\n\n// Narrowed minimal typing to avoid anywhere possible without pulling the full payload types at runtime.\n// In a Payload project, you can import type { Field, ClientField } from 'payload' and use those instead.\ntype BaseField = {\n admin?: {\n description?: unknown;\n language?: unknown;\n };\n hasMany?: boolean;\n // json\n jsonSchema?: unknown;\n max?: number;\n maxRows?: number;\n // number\n min?: number;\n // text/textarea\n minRows?: number;\n name?: string;\n // select\n options?: Array<\n | {\n label?: unknown;\n value: number | string;\n }\n | number\n | string\n >;\n required?: boolean;\n schema?: unknown;\n type?: string;\n typescriptSchema?: unknown;\n};\n\nfunction isString(s: unknown): s is string {\n return typeof s === 'string';\n}\n\nfunction isPlainObject(o: unknown): o is Record<string, unknown> {\n return !!o && typeof o === 'object' && !Array.isArray(o);\n}\n\nfunction getDescription(field: BaseField): string | undefined {\n const d = field?.admin?.description;\n return typeof d === 'string' ? d : undefined;\n}\n\nfunction stringWithDescription(field: BaseField) {\n const out: Record<string, any> = { type: 'string' };\n const description = getDescription(field);\n if (description) {out.description = description;}\n return out;\n}\n\nfunction numberWithBounds(field: BaseField) {\n const out: Record<string, any> = { type: 'number' };\n if (typeof field.min === 'number') {out.minimum = field.min;}\n if (typeof field.max === 'number') {out.maximum = field.max;}\n const description = getDescription(field);\n if (description) {out.description = description;}\n return out;\n}\n\nfunction dateSchema(field: BaseField) {\n const out: Record<string, any> = { type: 'string', format: 'date-time' };\n const description = getDescription(field);\n if (description) {out.description = description;}\n return out;\n}\n\nfunction codeSchema(field: BaseField) {\n const out: Record<string, any> = { type: 'string' };\n let description = getDescription(field);\n const lang = field?.admin?.language;\n if (typeof lang === 'string' && lang.trim()) {\n description = description ? `${description} (language: ${lang})` : `language: ${lang}`;\n }\n if (description) {out.description = description;}\n return out;\n}\n\nfunction emailSchema(field: BaseField) {\n const out: Record<string, any> = { type: 'string', format: 'email' };\n const description = getDescription(field);\n if (description) {out.description = description;}\n return out;\n}\n\nfunction jsonValueSchema(field: BaseField) {\n // Prefer a provided JSON Schema object\n if (isPlainObject(field.jsonSchema)) {return field.jsonSchema as object;}\n if (isPlainObject(field.schema)) {return field.schema as object;}\n\n // typescriptSchema cannot be executed here; default to object\n return { type: 'object' };\n}\n\nfunction normalizeSelectOptions(field: BaseField): { values: Array<number | string>; valueType: 'number' | 'string' } {\n const raw = field.options || [];\n const values: Array<number | string> = [];\n\n for (const opt of raw) {\n if (typeof opt === 'string' || typeof opt === 'number') {\n values.push(opt);\n } else if (isPlainObject(opt) && ('value' in opt)) {\n const v = (opt as any).value;\n if (typeof v === 'string' || typeof v === 'number') {\n values.push(v);\n }\n }\n }\n\n // Infer primitive type\n const allNumbers = values.length > 0 && values.every((v) => typeof v === 'number');\n const valueType: 'number' | 'string' = allNumbers ? 'number' : 'string';\n return { values, valueType };\n}\n\nfunction supportsHasMany(fieldType: string | undefined): boolean {\n // Out of the box: text, textarea, select support hasMany\n // Others can be arrays only if your config truly sets hasMany; we return boolean based on type here.\n return fieldType === 'text' || fieldType === 'textarea' || fieldType === 'select';\n}\n\nexport function fieldToJsonSchema(\n fieldInput: BaseField & Field,\n opts?: { nameOverride?: string; wrapObject?: boolean },\n): JsonSchema {\n const field = fieldInput || {};\n const name = isString(opts?.nameOverride) && opts?.nameOverride.length ? opts.nameOverride : (field.name || 'value');\n const type = field.type;\n\n let valueSchema: null | Record<string, any> = null;\n\n switch (type) {\n case 'array':\n case 'group': {\n if ('fields' in field && Array.isArray(field.fields)) {\n const properties: Record<string, any> = {}\n const required: string[] = []\n \n const processFields = (fields: any[]) => {\n for (const subField of fields) {\n if (subField.type === 'row' || subField.type === 'collapsible') {\n if (subField.fields) {processFields(subField.fields)}\n continue\n }\n if (!subField.name || subField.name === 'id') {continue}\n\n const subSchema = fieldToJsonSchema(subField, { wrapObject: false })\n if (subSchema && Object.keys(subSchema).length > 0) {\n properties[subField.name] = subSchema\n // OpenAI Strict Mode: All fields must be required\n required.push(subField.name)\n \n // If field is optional in Payload, allow null in schema\n if (!subField.required) {\n // Arrays usually default to empty array [], so we don't make them nullable\n // Groups and other types can be null\n if (subSchema.type !== 'array') {\n if (Array.isArray(subSchema.type)) {\n if (!subSchema.type.includes('null')) {\n subSchema.type.push('null')\n }\n } else if (typeof subSchema.type === 'string' && subSchema.type !== 'null') {\n subSchema.type = [subSchema.type, 'null']\n }\n \n // If enum is present, we must allow null in enum or use anyOf? \n // OpenAI strict mode: \"enum values must be consistent with type\". \n // If type is [string, null], null is a valid value for the type, but if enum is [\"a\"], null is not in enum.\n // valid: { type: [\"string\", \"null\"], enum: [\"a\", null] } \n if (Array.isArray(subSchema.enum) && !subSchema.enum.includes(null)) {\n subSchema.enum.push(null)\n }\n }\n }\n }\n }\n }\n \n processFields(field.fields)\n\n const objSchema = {\n type: 'object',\n additionalProperties: false,\n properties,\n required: required.length ? required : undefined\n }\n\n if (type === 'array') {\n valueSchema = {\n type: 'array',\n items: objSchema\n }\n if (typeof field.maxRows === 'number') {\n valueSchema.maxItems = field.maxRows\n }\n if (typeof field.minRows === 'number') {\n valueSchema.minItems = field.minRows\n }\n } else {\n valueSchema = objSchema\n }\n \n const description = getDescription(field);\n if (description) {valueSchema.description = description;}\n }\n break;\n }\n\n case 'checkbox': {\n valueSchema = { type: 'boolean' };\n const description = getDescription(field);\n if (description) {valueSchema.description = description;}\n break;\n }\n\n case 'code': {\n const base = codeSchema(field);\n if (field.hasMany) {\n valueSchema = { type: 'array', items: base };\n } else {\n valueSchema = base;\n }\n break;\n }\n\n case 'date': {\n const base = dateSchema(field);\n if (field.hasMany) {\n valueSchema = { type: 'array', items: base };\n } else {\n valueSchema = base;\n }\n break;\n }\n\n case 'email': {\n const base = emailSchema(field);\n if (field.hasMany) {\n valueSchema = { type: 'array', items: base };\n } else {\n valueSchema = base;\n }\n break;\n }\n\n case 'json': {\n const base = jsonValueSchema(field);\n if (field.hasMany) {\n valueSchema = { type: 'array', items: base };\n } else {\n valueSchema = base as Record<string, any>;\n }\n break;\n }\n\n case 'number': {\n const base = numberWithBounds(field);\n if (field.hasMany) {\n valueSchema = { type: 'array', items: base };\n } else {\n valueSchema = base;\n }\n break;\n }\n case 'select': {\n const { values, valueType } = normalizeSelectOptions(field);\n const baseSingle: Record<string, any> = { type: valueType, enum: values };\n const description = getDescription(field);\n if (description) {baseSingle.description = description;}\n\n if (field.hasMany && supportsHasMany(type)) {\n valueSchema = {\n type: 'array',\n items: { type: valueType, enum: values },\n ...(description ? { description } : {}),\n };\n } else {\n valueSchema = baseSingle;\n }\n break;\n }\n\n case 'text':\n case 'textarea': {\n const base = stringWithDescription(field);\n if (field.hasMany && supportsHasMany(type)) {\n const arr: Record<string, any> = {\n type: 'array',\n items: { type: 'string' },\n };\n if (typeof field.minRows === 'number') {arr.minItems = field.minRows;}\n if (typeof field.maxRows === 'number') {arr.maxItems = field.maxRows;}\n if (base.description) {arr.description = base.description;}\n valueSchema = arr;\n } else {\n valueSchema = base;\n }\n break;\n }\n \n // Explicitly handle organizational types to avoid default breakage if passed directly\n // though usually they are handled by parent recursion in group/array case above.\n // If passed as root, we can't really return a meaningful single-value schema without a wrapper?\n // But let's leave default for them or handle if necessary. \n // For now, if someone passes a 'row' as root, it will fall to default (null).\n\n default: {\n valueSchema = null;\n break;\n }\n }\n\n const wrap = opts?.wrapObject !== false;\n\n if (!wrap) {\n return (valueSchema || {}) as JsonSchema;\n }\n\n if (!valueSchema) {\n return {} as JsonSchema;\n }\n\n const schema: JsonSchema = {\n type: 'object',\n additionalProperties: false,\n properties: {\n [name]: valueSchema,\n },\n required: [name]\n };\n\n return schema;\n}\n"],"names":["isString","s","isPlainObject","o","Array","isArray","getDescription","field","d","admin","description","undefined","stringWithDescription","out","type","numberWithBounds","min","minimum","max","maximum","dateSchema","format","codeSchema","lang","language","trim","emailSchema","jsonValueSchema","jsonSchema","schema","normalizeSelectOptions","raw","options","values","opt","push","v","value","allNumbers","length","every","valueType","supportsHasMany","fieldType","fieldToJsonSchema","fieldInput","opts","name","nameOverride","valueSchema","fields","properties","required","processFields","subField","subSchema","wrapObject","Object","keys","includes","enum","objSchema","additionalProperties","items","maxRows","maxItems","minRows","minItems","base","hasMany","baseSingle","arr","wrap"],"mappings":"AAAA;;;;;;;;;CASC,GAqCD,SAASA,SAASC,CAAU;IAC1B,OAAO,OAAOA,MAAM;AACtB;AAEA,SAASC,cAAcC,CAAU;IAC/B,OAAO,CAAC,CAACA,KAAK,OAAOA,MAAM,YAAY,CAACC,MAAMC,OAAO,CAACF;AACxD;AAEA,SAASG,eAAeC,KAAgB;IACtC,MAAMC,IAAID,OAAOE,OAAOC;IACxB,OAAO,OAAOF,MAAM,WAAWA,IAAIG;AACrC;AAEA,SAASC,sBAAsBL,KAAgB;IAC7C,MAAMM,MAA2B;QAAEC,MAAM;IAAS;IAClD,MAAMJ,cAAcJ,eAAeC;IACnC,IAAIG,aAAa;QAACG,IAAIH,WAAW,GAAGA;IAAY;IAChD,OAAOG;AACT;AAEA,SAASE,iBAAiBR,KAAgB;IACxC,MAAMM,MAA2B;QAAEC,MAAM;IAAS;IAClD,IAAI,OAAOP,MAAMS,GAAG,KAAK,UAAU;QAACH,IAAII,OAAO,GAAGV,MAAMS,GAAG;IAAC;IAC5D,IAAI,OAAOT,MAAMW,GAAG,KAAK,UAAU;QAACL,IAAIM,OAAO,GAAGZ,MAAMW,GAAG;IAAC;IAC5D,MAAMR,cAAcJ,eAAeC;IACnC,IAAIG,aAAa;QAACG,IAAIH,WAAW,GAAGA;IAAY;IAChD,OAAOG;AACT;AAEA,SAASO,WAAWb,KAAgB;IAClC,MAAMM,MAA2B;QAAEC,MAAM;QAAUO,QAAQ;IAAY;IACvE,MAAMX,cAAcJ,eAAeC;IACnC,IAAIG,aAAa;QAACG,IAAIH,WAAW,GAAGA;IAAY;IAChD,OAAOG;AACT;AAEA,SAASS,WAAWf,KAAgB;IAClC,MAAMM,MAA2B;QAAEC,MAAM;IAAS;IAClD,IAAIJ,cAAcJ,eAAeC;IACjC,MAAMgB,OAAOhB,OAAOE,OAAOe;IAC3B,IAAI,OAAOD,SAAS,YAAYA,KAAKE,IAAI,IAAI;QAC3Cf,cAAcA,cAAc,CAAC,EAAEA,YAAY,YAAY,EAAEa,KAAK,CAAC,CAAC,GAAG,CAAC,UAAU,EAAEA,KAAK,CAAC;IACxF;IACA,IAAIb,aAAa;QAACG,IAAIH,WAAW,GAAGA;IAAY;IAChD,OAAOG;AACT;AAEA,SAASa,YAAYnB,KAAgB;IACnC,MAAMM,MAA2B;QAAEC,MAAM;QAAUO,QAAQ;IAAQ;IACnE,MAAMX,cAAcJ,eAAeC;IACnC,IAAIG,aAAa;QAACG,IAAIH,WAAW,GAAGA;IAAY;IAChD,OAAOG;AACT;AAEA,SAASc,gBAAgBpB,KAAgB;IACvC,uCAAuC;IACvC,IAAIL,cAAcK,MAAMqB,UAAU,GAAG;QAAC,OAAOrB,MAAMqB,UAAU;IAAW;IACxE,IAAI1B,cAAcK,MAAMsB,MAAM,GAAG;QAAC,OAAOtB,MAAMsB,MAAM;IAAW;IAEhE,8DAA8D;IAC9D,OAAO;QAAEf,MAAM;IAAS;AAC1B;AAEA,SAASgB,uBAAuBvB,KAAgB;IAC9C,MAAMwB,MAAMxB,MAAMyB,OAAO,IAAI,EAAE;IAC/B,MAAMC,SAAiC,EAAE;IAEzC,KAAK,MAAMC,OAAOH,IAAK;QACrB,IAAI,OAAOG,QAAQ,YAAY,OAAOA,QAAQ,UAAU;YACtDD,OAAOE,IAAI,CAACD;QACd,OAAO,IAAIhC,cAAcgC,QAAS,WAAWA,KAAM;YACjD,MAAME,IAAI,AAACF,IAAYG,KAAK;YAC5B,IAAI,OAAOD,MAAM,YAAY,OAAOA,MAAM,UAAU;gBAClDH,OAAOE,IAAI,CAACC;YACd;QACF;IACF;IAEA,uBAAuB;IACvB,MAAME,aAAaL,OAAOM,MAAM,GAAG,KAAKN,OAAOO,KAAK,CAAC,CAACJ,IAAM,OAAOA,MAAM;IACzE,MAAMK,YAAiCH,aAAa,WAAW;IAC/D,OAAO;QAAEL;QAAQQ;IAAU;AAC7B;AAEA,SAASC,gBAAgBC,SAA6B;IACpD,yDAAyD;IACzD,qGAAqG;IACrG,OAAOA,cAAc,UAAUA,cAAc,cAAcA,cAAc;AAC3E;AAEA,OAAO,SAASC,kBACdC,UAA6B,EAC7BC,IAAsD;IAEtD,MAAMvC,QAAQsC,cAAc,CAAC;IAC7B,MAAME,OAAO/C,SAAS8C,MAAME,iBAAiBF,MAAME,aAAaT,SAASO,KAAKE,YAAY,GAAIzC,MAAMwC,IAAI,IAAI;IAC5G,MAAMjC,OAAOP,MAAMO,IAAI;IAEvB,IAAImC,cAA0C;IAE9C,OAAQnC;QACN,KAAK;QACL,KAAK;YAAS;gBACZ,IAAI,YAAYP,SAASH,MAAMC,OAAO,CAACE,MAAM2C,MAAM,GAAG;oBACpD,MAAMC,aAAkC,CAAC;oBACzC,MAAMC,WAAqB,EAAE;oBAE7B,MAAMC,gBAAgB,CAACH;wBACrB,KAAK,MAAMI,YAAYJ,OAAQ;4BAC7B,IAAII,SAASxC,IAAI,KAAK,SAASwC,SAASxC,IAAI,KAAK,eAAe;gCAC7D,IAAIwC,SAASJ,MAAM,EAAE;oCAACG,cAAcC,SAASJ,MAAM;gCAAC;gCACpD;4BACH;4BACA,IAAI,CAACI,SAASP,IAAI,IAAIO,SAASP,IAAI,KAAK,MAAM;gCAAC;4BAAQ;4BAEvD,MAAMQ,YAAYX,kBAAkBU,UAAU;gCAAEE,YAAY;4BAAM;4BAClE,IAAID,aAAaE,OAAOC,IAAI,CAACH,WAAWhB,MAAM,GAAG,GAAG;gCAClDY,UAAU,CAACG,SAASP,IAAI,CAAC,GAAGQ;gCAC5B,kDAAkD;gCAClDH,SAASjB,IAAI,CAACmB,SAASP,IAAI;gCAE3B,wDAAwD;gCACxD,IAAI,CAACO,SAASF,QAAQ,EAAE;oCACtB,2EAA2E;oCAC3E,qCAAqC;oCACpC,IAAIG,UAAUzC,IAAI,KAAK,SAAS;wCAC9B,IAAIV,MAAMC,OAAO,CAACkD,UAAUzC,IAAI,GAAG;4CACjC,IAAI,CAACyC,UAAUzC,IAAI,CAAC6C,QAAQ,CAAC,SAAS;gDACpCJ,UAAUzC,IAAI,CAACqB,IAAI,CAAC;4CACtB;wCACF,OAAO,IAAI,OAAOoB,UAAUzC,IAAI,KAAK,YAAYyC,UAAUzC,IAAI,KAAK,QAAQ;4CAC1EyC,UAAUzC,IAAI,GAAG;gDAACyC,UAAUzC,IAAI;gDAAE;6CAAO;wCAC3C;wCAEA,gEAAgE;wCAChE,mEAAmE;wCACnE,4GAA4G;wCAC5G,0DAA0D;wCAC1D,IAAIV,MAAMC,OAAO,CAACkD,UAAUK,IAAI,KAAK,CAACL,UAAUK,IAAI,CAACD,QAAQ,CAAC,OAAO;4CACnEJ,UAAUK,IAAI,CAACzB,IAAI,CAAC;wCACtB;oCACF;gCACH;4BACF;wBACF;oBACF;oBAEAkB,cAAc9C,MAAM2C,MAAM;oBAE1B,MAAMW,YAAY;wBAChB/C,MAAM;wBACNgD,sBAAsB;wBACtBX;wBACAC,UAAUA,SAASb,MAAM,GAAGa,WAAWzC;oBACzC;oBAEA,IAAIG,SAAS,SAAS;wBACpBmC,cAAc;4BACZnC,MAAM;4BACNiD,OAAOF;wBACT;wBACA,IAAI,OAAOtD,MAAMyD,OAAO,KAAK,UAAU;4BACrCf,YAAYgB,QAAQ,GAAG1D,MAAMyD,OAAO;wBACtC;wBACC,IAAI,OAAOzD,MAAM2D,OAAO,KAAK,UAAU;4BACtCjB,YAAYkB,QAAQ,GAAG5D,MAAM2D,OAAO;wBACtC;oBACF,OAAO;wBACLjB,cAAcY;oBAChB;oBAEA,MAAMnD,cAAcJ,eAAeC;oBACnC,IAAIG,aAAa;wBAACuC,YAAYvC,WAAW,GAAGA;oBAAY;gBAC1D;gBACA;YACF;QAEA,KAAK;YAAY;gBACfuC,cAAc;oBAAEnC,MAAM;gBAAU;gBAChC,MAAMJ,cAAcJ,eAAeC;gBACnC,IAAIG,aAAa;oBAACuC,YAAYvC,WAAW,GAAGA;gBAAY;gBACxD;YACF;QAEA,KAAK;YAAQ;gBACX,MAAM0D,OAAO9C,WAAWf;gBACxB,IAAIA,MAAM8D,OAAO,EAAE;oBACjBpB,cAAc;wBAAEnC,MAAM;wBAASiD,OAAOK;oBAAK;gBAC7C,OAAO;oBACLnB,cAAcmB;gBAChB;gBACA;YACF;QAEA,KAAK;YAAQ;gBACX,MAAMA,OAAOhD,WAAWb;gBACxB,IAAIA,MAAM8D,OAAO,EAAE;oBACjBpB,cAAc;wBAAEnC,MAAM;wBAASiD,OAAOK;oBAAK;gBAC7C,OAAO;oBACLnB,cAAcmB;gBAChB;gBACA;YACF;QAEA,KAAK;YAAS;gBACZ,MAAMA,OAAO1C,YAAYnB;gBACzB,IAAIA,MAAM8D,OAAO,EAAE;oBACjBpB,cAAc;wBAAEnC,MAAM;wBAASiD,OAAOK;oBAAK;gBAC7C,OAAO;oBACLnB,cAAcmB;gBAChB;gBACA;YACF;QAEA,KAAK;YAAQ;gBACX,MAAMA,OAAOzC,gBAAgBpB;gBAC7B,IAAIA,MAAM8D,OAAO,EAAE;oBACjBpB,cAAc;wBAAEnC,MAAM;wBAASiD,OAAOK;oBAAK;gBAC7C,OAAO;oBACLnB,cAAcmB;gBAChB;gBACA;YACF;QAEA,KAAK;YAAU;gBACb,MAAMA,OAAOrD,iBAAiBR;gBAC9B,IAAIA,MAAM8D,OAAO,EAAE;oBACjBpB,cAAc;wBAAEnC,MAAM;wBAASiD,OAAOK;oBAAK;gBAC7C,OAAO;oBACLnB,cAAcmB;gBAChB;gBACA;YACF;QACA,KAAK;YAAU;gBACb,MAAM,EAAEnC,MAAM,EAAEQ,SAAS,EAAE,GAAGX,uBAAuBvB;gBACrD,MAAM+D,aAAkC;oBAAExD,MAAM2B;oBAAWmB,MAAM3B;gBAAO;gBACxE,MAAMvB,cAAcJ,eAAeC;gBACnC,IAAIG,aAAa;oBAAC4D,WAAW5D,WAAW,GAAGA;gBAAY;gBAEvD,IAAIH,MAAM8D,OAAO,IAAI3B,gBAAgB5B,OAAO;oBAC1CmC,cAAc;wBACZnC,MAAM;wBACNiD,OAAO;4BAAEjD,MAAM2B;4BAAWmB,MAAM3B;wBAAO;wBACvC,GAAIvB,cAAc;4BAAEA;wBAAY,IAAI,CAAC,CAAC;oBACxC;gBACF,OAAO;oBACLuC,cAAcqB;gBAChB;gBACA;YACF;QAEA,KAAK;QACL,KAAK;YAAY;gBACf,MAAMF,OAAOxD,sBAAsBL;gBACnC,IAAIA,MAAM8D,OAAO,IAAI3B,gBAAgB5B,OAAO;oBAC1C,MAAMyD,MAA2B;wBAC/BzD,MAAM;wBACNiD,OAAO;4BAAEjD,MAAM;wBAAS;oBAC1B;oBACA,IAAI,OAAOP,MAAM2D,OAAO,KAAK,UAAU;wBAACK,IAAIJ,QAAQ,GAAG5D,MAAM2D,OAAO;oBAAC;oBACrE,IAAI,OAAO3D,MAAMyD,OAAO,KAAK,UAAU;wBAACO,IAAIN,QAAQ,GAAG1D,MAAMyD,OAAO;oBAAC;oBACrE,IAAII,KAAK1D,WAAW,EAAE;wBAAC6D,IAAI7D,WAAW,GAAG0D,KAAK1D,WAAW;oBAAC;oBAC1DuC,cAAcsB;gBAChB,OAAO;oBACLtB,cAAcmB;gBAChB;gBACA;YACF;QAEA,sFAAsF;QACtF,iFAAiF;QACjF,gGAAgG;QAChG,4DAA4D;QAC5D,8EAA8E;QAE9E;YAAS;gBACPnB,cAAc;gBACd;YACF;IACF;IAEA,MAAMuB,OAAO1B,MAAMU,eAAe;IAElC,IAAI,CAACgB,MAAM;QACT,OAAQvB,eAAe,CAAC;IAC1B;IAEA,IAAI,CAACA,aAAa;QAChB,OAAO,CAAC;IACV;IAEA,MAAMpB,SAAqB;QACzBf,MAAM;QACNgD,sBAAsB;QACtBX,YAAY;YACV,CAACJ,KAAK,EAAEE;QACV;QACAG,UAAU;YAACL;SAAK;IAClB;IAEA,OAAOlB;AACT"}
|
|
@@ -17,12 +17,14 @@ interface ResolveImageReferencesResult {
|
|
|
17
17
|
*
|
|
18
18
|
* Supports two formats:
|
|
19
19
|
* - @fieldName - for single upload fields
|
|
20
|
+
* - @collection.fieldName - schema path format (collection prefix is stripped)
|
|
20
21
|
* - @fieldName:filename.jpg - for specific images in hasMany fields
|
|
21
22
|
*
|
|
22
23
|
* @param prompt - The prompt text containing @field references
|
|
23
24
|
* @param contextData - The document data to resolve field values from
|
|
24
25
|
* @param req - Payload request object for fetching media
|
|
26
|
+
* @param collectionSlug - Optional collection slug to strip from schema path references
|
|
25
27
|
* @returns Processed prompt with references removed and array of resolved images
|
|
26
28
|
*/
|
|
27
|
-
export declare function resolveImageReferences(prompt: string, contextData: Record<string, unknown>, req: PayloadRequest): Promise<ResolveImageReferencesResult>;
|
|
29
|
+
export declare function resolveImageReferences(prompt: string, contextData: Record<string, unknown>, req: PayloadRequest, collectionSlug?: string): Promise<ResolveImageReferencesResult>;
|
|
28
30
|
export {};
|