@ai-stack/payloadcms 3.68.0-beta.2 → 3.68.0-beta.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/dist/ai/core/media/image/generateImage.js +2 -6
  2. package/dist/ai/core/media/image/generateImage.js.map +1 -1
  3. package/dist/ai/prompts.d.ts +1 -2
  4. package/dist/ai/prompts.js +0 -110
  5. package/dist/ai/prompts.js.map +1 -1
  6. package/dist/ai/providers/blocks/elevenlabs.js +1 -1
  7. package/dist/ai/providers/blocks/elevenlabs.js.map +1 -1
  8. package/dist/ai/providers/blocks/google.js +9 -5
  9. package/dist/ai/providers/blocks/google.js.map +1 -1
  10. package/dist/ai/providers/blocks/openai.js +1 -1
  11. package/dist/ai/providers/blocks/openai.js.map +1 -1
  12. package/dist/ai/providers/registry.js +0 -1
  13. package/dist/ai/providers/registry.js.map +1 -1
  14. package/dist/ai/utils/filterEditorSchemaByNodes.d.ts +9 -0
  15. package/dist/ai/utils/filterEditorSchemaByNodes.js +30 -3
  16. package/dist/ai/utils/filterEditorSchemaByNodes.js.map +1 -1
  17. package/dist/ai/utils/nodeToSchemaMap.d.ts +22 -0
  18. package/dist/ai/utils/nodeToSchemaMap.js +72 -0
  19. package/dist/ai/utils/nodeToSchemaMap.js.map +1 -0
  20. package/dist/collections/AISettings.js +44 -17
  21. package/dist/collections/AISettings.js.map +1 -1
  22. package/dist/defaults.d.ts +1 -0
  23. package/dist/defaults.js +8 -0
  24. package/dist/defaults.js.map +1 -1
  25. package/dist/endpoints/fetchFields.js +10 -0
  26. package/dist/endpoints/fetchFields.js.map +1 -1
  27. package/dist/endpoints/index.js +12 -0
  28. package/dist/endpoints/index.js.map +1 -1
  29. package/dist/index.d.ts +1 -1
  30. package/dist/index.js +1 -1
  31. package/dist/index.js.map +1 -1
  32. package/dist/plugin.js +16 -32
  33. package/dist/plugin.js.map +1 -1
  34. package/dist/providers/InstructionsProvider/InstructionsProvider.js +44 -15
  35. package/dist/providers/InstructionsProvider/InstructionsProvider.js.map +1 -1
  36. package/dist/providers/InstructionsProvider/InstructionsProvider.jsx +36 -16
  37. package/dist/providers/InstructionsProvider/context.d.ts +3 -0
  38. package/dist/providers/InstructionsProvider/context.js +2 -0
  39. package/dist/providers/InstructionsProvider/context.js.map +1 -1
  40. package/dist/providers/InstructionsProvider/useInstructions.js +3 -1
  41. package/dist/providers/InstructionsProvider/useInstructions.js.map +1 -1
  42. package/dist/types.d.ts +1 -5
  43. package/dist/types.js.map +1 -1
  44. package/dist/ui/AIConfigDashboard/index.js +198 -22
  45. package/dist/ui/AIConfigDashboard/index.js.map +1 -1
  46. package/dist/ui/AIConfigDashboard/index.jsx +159 -13
  47. package/dist/ui/Compose/Compose.js +21 -2
  48. package/dist/ui/Compose/Compose.js.map +1 -1
  49. package/dist/ui/Compose/Compose.jsx +21 -2
  50. package/dist/ui/Compose/hooks/useGenerate.js +26 -174
  51. package/dist/ui/Compose/hooks/useGenerate.js.map +1 -1
  52. package/dist/ui/Compose/hooks/useGenerateUpload.d.ts +11 -0
  53. package/dist/ui/Compose/hooks/useGenerateUpload.js +150 -0
  54. package/dist/ui/Compose/hooks/useGenerateUpload.js.map +1 -0
  55. package/dist/ui/Compose/hooks/useStreamingUpdate.d.ts +8 -0
  56. package/dist/ui/Compose/hooks/useStreamingUpdate.js +48 -0
  57. package/dist/ui/Compose/hooks/useStreamingUpdate.js.map +1 -0
  58. package/dist/ui/EncryptedTextField/index.js +4 -4
  59. package/dist/ui/EncryptedTextField/index.js.map +1 -1
  60. package/dist/ui/EncryptedTextField/index.jsx +4 -4
  61. package/dist/utilities/buildSmartPrompt.js +4 -6
  62. package/dist/utilities/buildSmartPrompt.js.map +1 -1
  63. package/dist/utilities/encryption.js +2 -1
  64. package/dist/utilities/encryption.js.map +1 -1
  65. package/dist/utilities/seedProperties.d.ts +7 -0
  66. package/dist/utilities/seedProperties.js +100 -0
  67. package/dist/utilities/seedProperties.js.map +1 -0
  68. package/dist/utilities/setSafeLexicalState.js +79 -6
  69. package/dist/utilities/setSafeLexicalState.js.map +1 -1
  70. package/dist/utilities/updateFieldsConfig.d.ts +1 -1
  71. package/dist/utilities/updateFieldsConfig.js +1 -0
  72. package/dist/utilities/updateFieldsConfig.js.map +1 -1
  73. package/package.json +2 -1
  74. package/dist/init.d.ts +0 -7
  75. package/dist/init.js +0 -152
  76. package/dist/init.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/ui/Compose/hooks/useGenerate.ts"],"sourcesContent":["import { experimental_useObject as useObject } from '@ai-sdk/react'\nimport { useEditorConfigContext } from '@payloadcms/richtext-lexical/client'\nimport { toast, useConfig, useDocumentInfo, useField, useForm, useLocale } from '@payloadcms/ui'\nimport { jsonSchema } from 'ai'\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react'\n\nimport type { ActionMenuItems, GenerateTextarea } from '../../../types.js'\n\nimport {\n PLUGIN_AI_JOBS_TABLE,\n PLUGIN_API_ENDPOINT_GENERATE,\n PLUGIN_API_ENDPOINT_GENERATE_UPLOAD,\n PLUGIN_INSTRUCTIONS_TABLE,\n PLUGIN_NAME,\n} from '../../../defaults.js'\nimport { useFieldProps } from '../../../providers/FieldProvider/useFieldProps.js'\nimport { editorSchemaValidator } from '../../../utilities/editorSchemaValidator.js'\nimport { fieldToJsonSchema } from '../../../utilities/fieldToJsonSchema.js'\nimport { setSafeLexicalState } from '../../../utilities/setSafeLexicalState.js'\nimport { useHistory } from './useHistory.js'\n\ntype ActionCallbackParams = { action: ActionMenuItems; params?: unknown }\n\nexport const useGenerate = ({ instructionId }: { instructionId: string }) => {\n // Create a ref to hold the current instructionId\n const instructionIdRef = useRef(instructionId)\n\n // Update the ref whenever instructionId changes\n useEffect(() => {\n instructionIdRef.current = instructionId\n }, [instructionId])\n\n const { field, path: pathFromContext } = useFieldProps()\n const editorConfigContext = useEditorConfigContext()\n\n const { editor } = editorConfigContext\n\n const { config } = useConfig()\n const {\n routes: { api },\n serverURL,\n } = config\n\n const { setValue } = useField<any>({\n path: pathFromContext ?? '',\n })\n\n const { set: setHistory } = useHistory()\n\n // Async job UI state\n const [jobStatus, setJobStatus] = useState<string | undefined>(undefined)\n const [jobProgress, setJobProgress] = useState<number>(0)\n const [isJobActive, setIsJobActive] = useState<boolean>(false)\n\n const { getData } = useForm()\n const { id: documentId, collectionSlug } = useDocumentInfo()\n\n const localFromContext = useLocale()\n \n // Reuse config from above instead of calling useConfig again\n const { collections } = config\n\n const collection = collections.find((collection) => collection.slug === PLUGIN_INSTRUCTIONS_TABLE)\n const { custom: { [PLUGIN_NAME]: { editorConfig = {} } = {} } = {} } = collection?.admin ?? {}\n const { schema: editorSchema = {} } = editorConfig\n\n const memoizedValidator = useMemo(() => {\n return editorSchemaValidator(editorSchema)\n }, [editorSchema])\n\n const memoizedSchema = useMemo(\n () =>\n jsonSchema(editorSchema, {\n validate: (value) => {\n const isValid = memoizedValidator(value)\n\n if (isValid) {\n return {\n success: true,\n value,\n }\n } else {\n return {\n error: new Error('Invalid schema'),\n success: false,\n }\n }\n },\n }),\n [memoizedValidator],\n )\n\n // Active JSON schema for useObject based on field type\n const activeSchema = useMemo(() => {\n const f = field as any\n const fieldType = f?.type as string | undefined\n if (fieldType === 'richText') {\n return memoizedSchema\n }\n if (f && f.name && fieldType) {\n const schemaJson = fieldToJsonSchema(f)\n if (schemaJson && Object.keys(schemaJson).length > 0) {\n return jsonSchema(schemaJson)\n }\n }\n return undefined\n }, [field, memoizedSchema])\n\n const {\n isLoading: loadingObject,\n object,\n stop: objectStop,\n submit,\n } = useObject({\n api: `/api${PLUGIN_API_ENDPOINT_GENERATE}`,\n onError: (error: any) => {\n toast.error(`Failed to generate: ${error.message}`)\n console.error('Error generating object:', error)\n },\n onFinish: (result) => {\n if (result.object && field) {\n if (field.type === 'richText') {\n setHistory(result.object)\n setValue(result.object)\n } else if ('name' in field) {\n setHistory(result.object[field.name])\n setValue(result.object[field.name])\n }\n } else {\n console.log('onFinish: result, field ', result, field)\n }\n },\n schema: activeSchema as any,\n })\n\n useEffect(() => {\n if (!object) {\n return\n }\n\n requestAnimationFrame(() => {\n if (field?.type === 'richText') {\n setSafeLexicalState(object, editor)\n } else if (field && 'name' in field && object[field.name]) {\n setValue(object[field.name])\n }\n })\n }, [object, editor, field, setValue])\n\n const streamObject = useCallback(\n ({ action = 'Compose', params }: ActionCallbackParams) => {\n const doc = getData()\n\n const currentInstructionId = instructionIdRef.current\n\n const options = {\n action,\n actionParams: params,\n instructionId: currentInstructionId,\n }\n\n submit({\n allowedEditorNodes: Array.from(editor?._nodes?.keys() || []),\n doc: {\n ...doc,\n id: documentId,\n },\n locale: localFromContext?.code,\n options,\n })\n },\n [localFromContext?.code, instructionIdRef, documentId],\n )\n\n const generateUpload = useCallback(async () => {\n const doc = getData()\n const currentInstructionId = instructionIdRef.current\n\n return fetch(`${serverURL}${api}${PLUGIN_API_ENDPOINT_GENERATE_UPLOAD}`, {\n body: JSON.stringify({\n collectionSlug: collectionSlug ?? '',\n doc,\n documentId,\n locale: localFromContext?.code,\n options: {\n instructionId: currentInstructionId,\n },\n } satisfies Parameters<GenerateTextarea>[0]),\n credentials: 'include',\n headers: {\n 'Content-Type': 'application/json',\n },\n method: 'POST',\n })\n .then(async (uploadResponse) => {\n if (uploadResponse.ok) {\n const json = await uploadResponse.json()\n const { job, result } = json || {}\n if (result) {\n // Set the upload ID\n setValue(result?.id)\n setHistory(result?.id)\n \n // Show toast to prompt user to save\n toast.success('Image generated successfully! Click Save to see the preview.')\n \n return uploadResponse\n }\n\n // Async job: poll AI Jobs collection for status/progress/result_id\n if (job && job.id) {\n setIsJobActive(true)\n const cancelled = false\n let attempts = 0\n const maxAttempts = 600 // up to ~10 minutes @ 1s\n\n // Basic in-hook state via closure variables; UI will re-render off fetches below\n const poll = async (): Promise<void> => {\n if (cancelled) {\n return\n }\n try {\n const res = await fetch(\n `${serverURL}${api}/${PLUGIN_AI_JOBS_TABLE}/${job.id}`,\n { credentials: 'include' },\n )\n if (res.ok) {\n const jobDoc = await res.json()\n const { progress, result_id, status } = jobDoc || {}\n setJobStatus(status)\n setJobProgress(progress ?? 0)\n // When result present, set field and finish\n if (status === 'completed' && result_id) {\n // Force upload field to refetch by clearing then setting the ID\n setValue(null)\n setTimeout(() => {\n setValue(result_id)\n }, 0)\n setHistory(result_id)\n setIsJobActive(false)\n return\n }\n if (status === 'failed') {\n setIsJobActive(false)\n throw new Error('Video generation failed')\n }\n }\n } catch (e) {\n // silent retry\n }\n\n attempts += 1\n if (!cancelled && attempts < maxAttempts) {\n setTimeout(poll, 1000)\n }\n }\n setTimeout(poll, 1000)\n return uploadResponse\n }\n\n throw new Error('generateUpload: Unexpected response')\n } else {\n const { errors = [] } = await uploadResponse.json()\n const errStr = errors.map((error: any) => error.message).join(', ')\n throw new Error(errStr)\n }\n })\n .catch((error) => {\n toast.error(`Failed to generate: ${error.message}`)\n console.error(\n 'Error generating or setting your upload, please set it manually if its saved in your media files.',\n error,\n )\n })\n }, [getData, localFromContext?.code, instructionIdRef, setValue, documentId, collectionSlug])\n\n const generate = useCallback(\n async (options?: ActionCallbackParams) => {\n if ((field as any)?.type === 'upload') {\n return generateUpload()\n }\n // All supported types use structured object generation when schema is provided server-side\n return streamObject(options ?? { action: 'Compose' })\n },\n [generateUpload, streamObject, field],\n )\n\n const stop = useCallback(() => {\n console.log('Stopping...')\n objectStop()\n }, [objectStop])\n\n return {\n generate,\n isJobActive,\n isLoading: loadingObject,\n jobProgress,\n jobStatus,\n stop,\n }\n}\n"],"names":["experimental_useObject","useObject","useEditorConfigContext","toast","useConfig","useDocumentInfo","useField","useForm","useLocale","jsonSchema","useCallback","useEffect","useMemo","useRef","useState","PLUGIN_AI_JOBS_TABLE","PLUGIN_API_ENDPOINT_GENERATE","PLUGIN_API_ENDPOINT_GENERATE_UPLOAD","PLUGIN_INSTRUCTIONS_TABLE","PLUGIN_NAME","useFieldProps","editorSchemaValidator","fieldToJsonSchema","setSafeLexicalState","useHistory","useGenerate","instructionId","instructionIdRef","current","field","path","pathFromContext","editorConfigContext","editor","config","routes","api","serverURL","setValue","set","setHistory","jobStatus","setJobStatus","undefined","jobProgress","setJobProgress","isJobActive","setIsJobActive","getData","id","documentId","collectionSlug","localFromContext","collections","collection","find","slug","custom","editorConfig","admin","schema","editorSchema","memoizedValidator","memoizedSchema","validate","value","isValid","success","error","Error","activeSchema","f","fieldType","type","name","schemaJson","Object","keys","length","isLoading","loadingObject","object","stop","objectStop","submit","onError","message","console","onFinish","result","log","requestAnimationFrame","streamObject","action","params","doc","currentInstructionId","options","actionParams","allowedEditorNodes","Array","from","_nodes","locale","code","generateUpload","fetch","body","JSON","stringify","credentials","headers","method","then","uploadResponse","ok","json","job","cancelled","attempts","maxAttempts","poll","res","jobDoc","progress","result_id","status","setTimeout","e","errors","errStr","map","join","catch","generate"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../src/ui/Compose/hooks/useGenerate.ts"],"sourcesContent":["import { experimental_useObject as useObject } from '@ai-sdk/react'\nimport { useEditorConfigContext } from '@payloadcms/richtext-lexical/client'\nimport { toast, useDocumentInfo, useField, useForm, useLocale } from '@payloadcms/ui'\nimport { jsonSchema } from 'ai'\nimport { useCallback, useEffect, useRef } from 'react'\n\nimport type { ActionMenuItems } from '../../../types.js'\n\nimport { PLUGIN_API_ENDPOINT_GENERATE } from '../../../defaults.js'\nimport { useFieldProps } from '../../../providers/FieldProvider/useFieldProps.js'\nimport { setSafeLexicalState } from '../../../utilities/setSafeLexicalState.js'\nimport { useGenerateUpload } from './useGenerateUpload.js'\nimport { useHistory } from './useHistory.js'\nimport { useStreamingUpdate } from './useStreamingUpdate.js'\n\ntype ActionCallbackParams = { action: ActionMenuItems; params?: unknown }\n\nexport const useGenerate = ({ instructionId }: { instructionId: string }) => {\n // Create a ref to hold the current instructionId\n const instructionIdRef = useRef(instructionId)\n\n // Update the ref whenever instructionId changes\n useEffect(() => {\n instructionIdRef.current = instructionId\n }, [instructionId])\n\n const { field, path: pathFromContext } = useFieldProps()\n const editorConfigContext = useEditorConfigContext()\n const { setValue } = useField<any>({\n path: pathFromContext ?? '',\n })\n const { set: setHistory } = useHistory()\n const { getData } = useForm()\n const { id: documentId } = useDocumentInfo()\n const localFromContext = useLocale()\n\n const {\n isLoading: loadingObject,\n object,\n stop: objectStop,\n submit,\n } = useObject({\n api: `/api${PLUGIN_API_ENDPOINT_GENERATE}`,\n onError: (error: any) => {\n toast.error(`Failed to generate: ${error.message}`)\n console.error('Error generating object:', error)\n },\n onFinish: (result) => {\n if (result.object && field) {\n if (field.type === 'richText') {\n setHistory(result.object)\n setSafeLexicalState(result.object, editor)\n } else if ('name' in field && result.object[field.name]) {\n setHistory(result.object[field.name])\n setValue(result.object[field.name])\n }\n } else {\n console.log('onFinish: result, field ', result, field)\n }\n },\n schema: jsonSchema({\n type: 'object',\n additionalProperties: true,\n properties: {},\n }) as any,\n })\n\n const { editor } = editorConfigContext\n\n // Hook: Handle high-frequency streaming updates\n useStreamingUpdate({\n editor,\n isLoading: loadingObject,\n object,\n })\n\n // Hook 2: Handle Upload generation and polling\n const { generateUpload, isJobActive, jobProgress, jobStatus } = useGenerateUpload({\n instructionIdRef,\n })\n\n const streamObject = useCallback(\n ({ action = 'Compose', params }: ActionCallbackParams) => {\n const doc = getData()\n\n const currentInstructionId = instructionIdRef.current\n\n const options = {\n action,\n actionParams: params,\n instructionId: currentInstructionId,\n }\n\n submit({\n allowedEditorNodes: Array.from(editor?._nodes?.keys() || []),\n doc: {\n ...doc,\n id: documentId,\n },\n locale: localFromContext?.code,\n options,\n })\n },\n [localFromContext?.code, instructionIdRef, documentId, getData, submit, editor],\n )\n\n const generate = useCallback(\n async (options?: ActionCallbackParams) => {\n if ((field as any)?.type === 'upload') {\n return generateUpload()\n }\n // All supported types use structured object generation when schema is provided server-side\n return streamObject(options ?? { action: 'Compose' })\n },\n [generateUpload, streamObject, field],\n )\n\n const stop = useCallback(() => {\n console.log('Stopping...')\n objectStop()\n }, [objectStop])\n\n return {\n generate,\n isJobActive,\n isLoading: loadingObject,\n jobProgress,\n jobStatus,\n stop,\n }\n}\n"],"names":["experimental_useObject","useObject","useEditorConfigContext","toast","useDocumentInfo","useField","useForm","useLocale","jsonSchema","useCallback","useEffect","useRef","PLUGIN_API_ENDPOINT_GENERATE","useFieldProps","setSafeLexicalState","useGenerateUpload","useHistory","useStreamingUpdate","useGenerate","instructionId","instructionIdRef","current","field","path","pathFromContext","editorConfigContext","setValue","set","setHistory","getData","id","documentId","localFromContext","isLoading","loadingObject","object","stop","objectStop","submit","api","onError","error","message","console","onFinish","result","type","editor","name","log","schema","additionalProperties","properties","generateUpload","isJobActive","jobProgress","jobStatus","streamObject","action","params","doc","currentInstructionId","options","actionParams","allowedEditorNodes","Array","from","_nodes","keys","locale","code","generate"],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import { type RefObject } from 'react';
2
+ type UseGenerateUploadParams = {
3
+ instructionIdRef: RefObject<string>;
4
+ };
5
+ export declare const useGenerateUpload: ({ instructionIdRef }: UseGenerateUploadParams) => {
6
+ generateUpload: () => Promise<void | Response>;
7
+ isJobActive: boolean;
8
+ jobProgress: number;
9
+ jobStatus: string | undefined;
10
+ };
11
+ export {};
@@ -0,0 +1,150 @@
1
+ import { toast, useConfig, useDocumentInfo, useField, useForm, useLocale } from '@payloadcms/ui';
2
+ import { useCallback, useState } from 'react';
3
+ import { PLUGIN_AI_JOBS_TABLE, PLUGIN_API_ENDPOINT_GENERATE_UPLOAD } from '../../../defaults.js';
4
+ import { useFieldProps } from '../../../providers/FieldProvider/useFieldProps.js';
5
+ import { useHistory } from './useHistory.js';
6
+ export const useGenerateUpload = ({ instructionIdRef })=>{
7
+ const { config } = useConfig();
8
+ const { routes: { api }, serverURL } = config;
9
+ const { id: documentId, collectionSlug } = useDocumentInfo();
10
+ const localFromContext = useLocale();
11
+ const { getData } = useForm();
12
+ const { set: setHistory } = useHistory();
13
+ const { field, path: pathFromContext } = useFieldProps();
14
+ const { setValue } = useField({
15
+ path: pathFromContext ?? ''
16
+ });
17
+ // Async job UI state
18
+ const [jobStatus, setJobStatus] = useState(undefined);
19
+ const [jobProgress, setJobProgress] = useState(0);
20
+ const [isJobActive, setIsJobActive] = useState(false);
21
+ const generateUpload = useCallback(async ()=>{
22
+ const doc = getData();
23
+ const currentInstructionId = instructionIdRef.current;
24
+ return fetch(`${serverURL}${api}${PLUGIN_API_ENDPOINT_GENERATE_UPLOAD}`, {
25
+ body: JSON.stringify({
26
+ collectionSlug: collectionSlug ?? '',
27
+ doc,
28
+ documentId,
29
+ locale: localFromContext?.code,
30
+ options: {
31
+ instructionId: currentInstructionId
32
+ }
33
+ }),
34
+ credentials: 'include',
35
+ headers: {
36
+ 'Content-Type': 'application/json'
37
+ },
38
+ method: 'POST'
39
+ }).then(async (uploadResponse)=>{
40
+ if (uploadResponse.ok) {
41
+ const json = await uploadResponse.json();
42
+ const { job, result } = json || {};
43
+ if (result) {
44
+ setValue(result?.id);
45
+ setHistory(result?.id);
46
+ // Show toast to prompt user to save
47
+ toast.success('Image generated successfully! Click Save to see the preview.');
48
+ return uploadResponse;
49
+ }
50
+ // Async job: poll AI Jobs collection for status/progress/result_id
51
+ if (job && job.id) {
52
+ setIsJobActive(true);
53
+ const cancelled = false;
54
+ let attempts = 0;
55
+ const maxAttempts = 600 // up to ~10 minutes @ 1s
56
+ ;
57
+ // Basic in-hook state via closure variables; UI will re-render off fetches below
58
+ const poll = async ()=>{
59
+ if (cancelled) {
60
+ return;
61
+ }
62
+ try {
63
+ const res = await fetch(`${serverURL}${api}/${PLUGIN_AI_JOBS_TABLE}/${job.id}`, {
64
+ credentials: 'include'
65
+ });
66
+ if (res.ok) {
67
+ const jobDoc = await res.json();
68
+ const { progress, result_id, status } = jobDoc || {};
69
+ setJobStatus(status);
70
+ setJobProgress(progress ?? 0);
71
+ // When result present, set field and finish
72
+ if (status === 'completed' && result_id) {
73
+ let valueToSet = result_id;
74
+ // Attempt to fetch full document for immediate preview
75
+ if (field && 'relationTo' in field && typeof field.relationTo === 'string') {
76
+ let attempts = 0;
77
+ const maxAttempts = 3;
78
+ while(attempts < maxAttempts){
79
+ try {
80
+ const docRes = await fetch(`${serverURL}${api}/${field.relationTo}/${result_id}`, {
81
+ credentials: 'include'
82
+ });
83
+ if (docRes.ok) {
84
+ const doc = await docRes.json();
85
+ // Verify we have a URL for preview
86
+ if (doc && doc.url) {
87
+ valueToSet = doc;
88
+ break;
89
+ }
90
+ }
91
+ } catch (e) {
92
+ console.error('Failed to fetch generated document for preview:', e);
93
+ }
94
+ attempts++;
95
+ if (attempts < maxAttempts) {
96
+ await new Promise((resolve)=>setTimeout(resolve, 500));
97
+ }
98
+ }
99
+ }
100
+ setValue(valueToSet);
101
+ setHistory(result_id);
102
+ setIsJobActive(false);
103
+ return;
104
+ }
105
+ if (status === 'failed') {
106
+ setIsJobActive(false);
107
+ throw new Error('Video generation failed');
108
+ }
109
+ }
110
+ } catch (_) {
111
+ // silent retry
112
+ }
113
+ attempts += 1;
114
+ if (!cancelled && attempts < maxAttempts) {
115
+ setTimeout(poll, 1000);
116
+ }
117
+ };
118
+ setTimeout(poll, 1000);
119
+ return uploadResponse;
120
+ }
121
+ throw new Error('generateUpload: Unexpected response');
122
+ } else {
123
+ const { errors = [] } = await uploadResponse.json();
124
+ const errStr = errors.map((error)=>error.message).join(', ');
125
+ throw new Error(errStr);
126
+ }
127
+ }).catch((error)=>{
128
+ toast.error(`Failed to generate: ${error.message}`);
129
+ console.error('Error generating or setting your upload, please set it manually if its saved in your media files.', error);
130
+ });
131
+ }, [
132
+ getData,
133
+ localFromContext?.code,
134
+ instructionIdRef,
135
+ // setValue,
136
+ documentId,
137
+ collectionSlug,
138
+ serverURL,
139
+ api,
140
+ setHistory
141
+ ]);
142
+ return {
143
+ generateUpload,
144
+ isJobActive,
145
+ jobProgress,
146
+ jobStatus
147
+ };
148
+ };
149
+
150
+ //# sourceMappingURL=useGenerateUpload.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/ui/Compose/hooks/useGenerateUpload.ts"],"sourcesContent":["import { toast, useConfig, useDocumentInfo, useField, useForm, useLocale } from '@payloadcms/ui'\nimport { type RefObject, useCallback, useState } from 'react'\n\nimport type { GenerateTextarea } from '../../../types.js'\n\nimport { PLUGIN_AI_JOBS_TABLE, PLUGIN_API_ENDPOINT_GENERATE_UPLOAD } from '../../../defaults.js'\nimport { useFieldProps } from '../../../providers/FieldProvider/useFieldProps.js'\nimport { useHistory } from './useHistory.js'\n\ntype UseGenerateUploadParams = {\n instructionIdRef: RefObject<string>\n}\n\nexport const useGenerateUpload = ({ instructionIdRef }: UseGenerateUploadParams) => {\n const { config } = useConfig()\n const {\n routes: { api },\n serverURL,\n } = config\n const { id: documentId, collectionSlug } = useDocumentInfo()\n const localFromContext = useLocale()\n const { getData } = useForm()\n const { set: setHistory } = useHistory()\n\n const { field, path: pathFromContext } = useFieldProps()\n const { setValue } = useField<any>({\n path: pathFromContext ?? '',\n })\n\n // Async job UI state\n const [jobStatus, setJobStatus] = useState<string | undefined>(undefined)\n const [jobProgress, setJobProgress] = useState<number>(0)\n const [isJobActive, setIsJobActive] = useState<boolean>(false)\n\n const generateUpload = useCallback(async () => {\n const doc = getData()\n const currentInstructionId = instructionIdRef.current\n\n return fetch(`${serverURL}${api}${PLUGIN_API_ENDPOINT_GENERATE_UPLOAD}`, {\n body: JSON.stringify({\n collectionSlug: collectionSlug ?? '',\n doc,\n documentId,\n locale: localFromContext?.code,\n options: {\n instructionId: currentInstructionId,\n },\n } satisfies Parameters<GenerateTextarea>[0]),\n credentials: 'include',\n headers: {\n 'Content-Type': 'application/json',\n },\n method: 'POST',\n })\n .then(async (uploadResponse) => {\n if (uploadResponse.ok) {\n const json = await uploadResponse.json()\n const { job, result } = json || {}\n if (result) {\n setValue(result?.id)\n setHistory(result?.id)\n\n // Show toast to prompt user to save\n toast.success('Image generated successfully! Click Save to see the preview.')\n\n return uploadResponse\n }\n\n // Async job: poll AI Jobs collection for status/progress/result_id\n if (job && job.id) {\n setIsJobActive(true)\n const cancelled = false\n let attempts = 0\n const maxAttempts = 600 // up to ~10 minutes @ 1s\n\n // Basic in-hook state via closure variables; UI will re-render off fetches below\n const poll = async (): Promise<void> => {\n if (cancelled) {\n return\n }\n try {\n const res = await fetch(`${serverURL}${api}/${PLUGIN_AI_JOBS_TABLE}/${job.id}`, {\n credentials: 'include',\n })\n if (res.ok) {\n const jobDoc = await res.json()\n const { progress, result_id, status } = jobDoc || {}\n setJobStatus(status)\n setJobProgress(progress ?? 0)\n // When result present, set field and finish\n if (status === 'completed' && result_id) {\n let valueToSet = result_id\n\n // Attempt to fetch full document for immediate preview\n if (field && 'relationTo' in field && typeof field.relationTo === 'string') {\n let attempts = 0\n const maxAttempts = 3\n while (attempts < maxAttempts) {\n try {\n const docRes = await fetch(\n `${serverURL}${api}/${field.relationTo}/${result_id}`,\n {\n credentials: 'include',\n },\n )\n if (docRes.ok) {\n const doc = await docRes.json()\n // Verify we have a URL for preview\n if (doc && doc.url) {\n valueToSet = doc\n break\n }\n }\n } catch (e) {\n console.error('Failed to fetch generated document for preview:', e)\n }\n attempts++\n if (attempts < maxAttempts) {\n await new Promise((resolve) => setTimeout(resolve, 500))\n }\n }\n }\n\n setValue(valueToSet)\n setHistory(result_id)\n setIsJobActive(false)\n return\n }\n if (status === 'failed') {\n setIsJobActive(false)\n throw new Error('Video generation failed')\n }\n }\n } catch (_) {\n // silent retry\n }\n\n attempts += 1\n if (!cancelled && attempts < maxAttempts) {\n setTimeout(poll, 1000)\n }\n }\n setTimeout(poll, 1000)\n return uploadResponse\n }\n\n throw new Error('generateUpload: Unexpected response')\n } else {\n const { errors = [] } = await uploadResponse.json()\n const errStr = errors.map((error: any) => error.message).join(', ')\n throw new Error(errStr)\n }\n })\n .catch((error) => {\n toast.error(`Failed to generate: ${error.message}`)\n console.error(\n 'Error generating or setting your upload, please set it manually if its saved in your media files.',\n error,\n )\n })\n }, [\n getData,\n localFromContext?.code,\n instructionIdRef,\n // setValue,\n documentId,\n collectionSlug,\n serverURL,\n api,\n setHistory,\n ])\n\n return {\n generateUpload,\n isJobActive,\n jobProgress,\n jobStatus,\n }\n}\n"],"names":["toast","useConfig","useDocumentInfo","useField","useForm","useLocale","useCallback","useState","PLUGIN_AI_JOBS_TABLE","PLUGIN_API_ENDPOINT_GENERATE_UPLOAD","useFieldProps","useHistory","useGenerateUpload","instructionIdRef","config","routes","api","serverURL","id","documentId","collectionSlug","localFromContext","getData","set","setHistory","field","path","pathFromContext","setValue","jobStatus","setJobStatus","undefined","jobProgress","setJobProgress","isJobActive","setIsJobActive","generateUpload","doc","currentInstructionId","current","fetch","body","JSON","stringify","locale","code","options","instructionId","credentials","headers","method","then","uploadResponse","ok","json","job","result","success","cancelled","attempts","maxAttempts","poll","res","jobDoc","progress","result_id","status","valueToSet","relationTo","docRes","url","e","console","error","Promise","resolve","setTimeout","Error","_","errors","errStr","map","message","join","catch"],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { LexicalEditor } from 'lexical';
2
+ type UseStreamingUpdateParams = {
3
+ editor: LexicalEditor;
4
+ isLoading: boolean;
5
+ object: any;
6
+ };
7
+ export declare const useStreamingUpdate: ({ editor, isLoading, object }: UseStreamingUpdateParams) => void;
8
+ export {};
@@ -0,0 +1,48 @@
1
+ import { useForm } from '@payloadcms/ui';
2
+ import { useEffect, useRef } from 'react';
3
+ import { useFieldProps } from '../../../providers/FieldProvider/useFieldProps.js';
4
+ import { setSafeLexicalState } from '../../../utilities/setSafeLexicalState.js';
5
+ export const useStreamingUpdate = ({ editor, isLoading, object })=>{
6
+ const { field, path: pathFromContext } = useFieldProps();
7
+ const { dispatchFields } = useForm();
8
+ // Ref for latest object to avoid effect re-runs during high-frequency streaming
9
+ const objectRef = useRef(object);
10
+ objectRef.current = object;
11
+ useEffect(()=>{
12
+ // Only run the animation loop while loading (streaming)
13
+ if (!isLoading) {
14
+ return;
15
+ }
16
+ let reqId;
17
+ const loop = ()=>{
18
+ const currentObject = objectRef.current;
19
+ if (currentObject) {
20
+ if (field?.type === 'richText') {
21
+ setSafeLexicalState(currentObject, editor);
22
+ } else if (field && 'name' in field && currentObject[field.name]) {
23
+ // Use dispatchFields for high-frequency streaming updates to avoid re-renders
24
+ dispatchFields({
25
+ type: 'UPDATE',
26
+ path: pathFromContext ?? '',
27
+ value: currentObject[field.name]
28
+ });
29
+ }
30
+ }
31
+ // Continue loop
32
+ reqId = requestAnimationFrame(loop);
33
+ };
34
+ // Start loop
35
+ loop();
36
+ return ()=>{
37
+ cancelAnimationFrame(reqId);
38
+ };
39
+ }, [
40
+ isLoading,
41
+ editor,
42
+ field,
43
+ dispatchFields,
44
+ pathFromContext
45
+ ]);
46
+ };
47
+
48
+ //# sourceMappingURL=useStreamingUpdate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/ui/Compose/hooks/useStreamingUpdate.ts"],"sourcesContent":["import type { LexicalEditor } from 'lexical'\n\nimport { useForm } from '@payloadcms/ui'\nimport { useEffect, useRef } from 'react'\n\nimport { useFieldProps } from '../../../providers/FieldProvider/useFieldProps.js'\nimport { setSafeLexicalState } from '../../../utilities/setSafeLexicalState.js'\n\ntype UseStreamingUpdateParams = {\n editor: LexicalEditor\n isLoading: boolean\n object: any\n}\n\nexport const useStreamingUpdate = ({ editor, isLoading, object }: UseStreamingUpdateParams) => {\n const { field, path: pathFromContext } = useFieldProps()\n const { dispatchFields } = useForm()\n\n // Ref for latest object to avoid effect re-runs during high-frequency streaming\n const objectRef = useRef(object)\n objectRef.current = object\n\n useEffect(() => {\n // Only run the animation loop while loading (streaming)\n if (!isLoading) {\n return\n }\n\n let reqId: number\n\n const loop = () => {\n const currentObject = objectRef.current\n\n if (currentObject) {\n if (field?.type === 'richText') {\n setSafeLexicalState(currentObject, editor)\n } else if (field && 'name' in field && currentObject[field.name]) {\n // Use dispatchFields for high-frequency streaming updates to avoid re-renders\n dispatchFields({\n type: 'UPDATE',\n path: pathFromContext ?? '',\n value: currentObject[field.name],\n } as any)\n }\n }\n\n // Continue loop\n reqId = requestAnimationFrame(loop)\n }\n\n // Start loop\n loop()\n\n return () => {\n cancelAnimationFrame(reqId)\n }\n }, [isLoading, editor, field, dispatchFields, pathFromContext])\n}\n"],"names":["useForm","useEffect","useRef","useFieldProps","setSafeLexicalState","useStreamingUpdate","editor","isLoading","object","field","path","pathFromContext","dispatchFields","objectRef","current","reqId","loop","currentObject","type","name","value","requestAnimationFrame","cancelAnimationFrame"],"mappings":"AAEA,SAASA,OAAO,QAAQ,iBAAgB;AACxC,SAASC,SAAS,EAAEC,MAAM,QAAQ,QAAO;AAEzC,SAASC,aAAa,QAAQ,oDAAmD;AACjF,SAASC,mBAAmB,QAAQ,4CAA2C;AAQ/E,OAAO,MAAMC,qBAAqB,CAAC,EAAEC,MAAM,EAAEC,SAAS,EAAEC,MAAM,EAA4B;IACxF,MAAM,EAAEC,KAAK,EAAEC,MAAMC,eAAe,EAAE,GAAGR;IACzC,MAAM,EAAES,cAAc,EAAE,GAAGZ;IAE3B,gFAAgF;IAChF,MAAMa,YAAYX,OAAOM;IACzBK,UAAUC,OAAO,GAAGN;IAEpBP,UAAU;QACR,wDAAwD;QACxD,IAAI,CAACM,WAAW;YACd;QACF;QAEA,IAAIQ;QAEJ,MAAMC,OAAO;YACX,MAAMC,gBAAgBJ,UAAUC,OAAO;YAEvC,IAAIG,eAAe;gBACjB,IAAIR,OAAOS,SAAS,YAAY;oBAC9Bd,oBAAoBa,eAAeX;gBACrC,OAAO,IAAIG,SAAS,UAAUA,SAASQ,aAAa,CAACR,MAAMU,IAAI,CAAC,EAAE;oBAChE,8EAA8E;oBAC9EP,eAAe;wBACbM,MAAM;wBACNR,MAAMC,mBAAmB;wBACzBS,OAAOH,aAAa,CAACR,MAAMU,IAAI,CAAC;oBAClC;gBACF;YACF;YAEA,gBAAgB;YAChBJ,QAAQM,sBAAsBL;QAChC;QAEA,aAAa;QACbA;QAEA,OAAO;YACLM,qBAAqBP;QACvB;IACF,GAAG;QAACR;QAAWD;QAAQG;QAAOG;QAAgBD;KAAgB;AAChE,EAAC"}
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { useField } from '@payloadcms/ui';
3
+ import { Button, useField } from '@payloadcms/ui';
4
4
  import React, { useState } from 'react';
5
5
  export const EncryptedTextField = ({ label, path, required })=>{
6
6
  const { setValue, value } = useField({
@@ -48,13 +48,13 @@ export const EncryptedTextField = ({ label, path, required })=>{
48
48
  })
49
49
  ]
50
50
  }),
51
- /*#__PURE__*/ _jsx("button", {
52
- className: "btn btn--style-secondary btn--size-small",
51
+ /*#__PURE__*/ _jsx(Button, {
52
+ buttonStyle: "secondary",
53
53
  onClick: ()=>{
54
54
  setValue('');
55
55
  setIsEditing(true);
56
56
  },
57
- type: "button",
57
+ size: "medium",
58
58
  children: "Change"
59
59
  })
60
60
  ]
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/ui/EncryptedTextField/index.tsx"],"sourcesContent":["'use client'\n\nimport { useField } from '@payloadcms/ui'\nimport React, { useState } from 'react'\n\ntype Props = {\n label?: string\n path: string\n required?: boolean\n}\n\nexport const EncryptedTextField: React.FC<Props> = ({ label, path, required }) => {\n const { setValue, value } = useField<string>({ path })\n const [isEditing, setIsEditing] = useState(!value)\n\n const isMasked = typeof value === 'string' && value.startsWith('sk-') && value.includes('****')\n\n return (\n <div className=\"field-type text\">\n <label className=\"field-label\">\n {label || 'API Key'}\n {required && <span className=\"required\">*</span>}\n </label>\n\n {!isEditing && isMasked ? (\n <div style={{ alignItems: 'center', display: 'flex', gap: '10px' }}>\n <div\n style={{\n background: 'var(--theme-elevation-100)',\n borderRadius: '4px',\n flexGrow: 1,\n fontFamily: 'monospace',\n padding: '8px 12px',\n }}\n >\n {value}\n <span\n style={{ color: 'var(--theme-success-500)', fontSize: '0.8em', marginLeft: '10px' }}\n >\n ✓ Configured\n </span>\n </div>\n <button\n className=\"btn btn--style-secondary btn--size-small\"\n onClick={() => {\n setValue('')\n setIsEditing(true)\n }}\n type=\"button\"\n >\n Change\n </button>\n </div>\n ) : (\n <input\n onChange={(e) => setValue(e.target.value)}\n placeholder=\"sk-...\"\n style={{ width: '100%' }}\n type=\"password\"\n value={value || ''}\n />\n )}\n </div>\n )\n}\n"],"names":["useField","React","useState","EncryptedTextField","label","path","required","setValue","value","isEditing","setIsEditing","isMasked","startsWith","includes","div","className","span","style","alignItems","display","gap","background","borderRadius","flexGrow","fontFamily","padding","color","fontSize","marginLeft","button","onClick","type","input","onChange","e","target","placeholder","width"],"mappings":"AAAA;;AAEA,SAASA,QAAQ,QAAQ,iBAAgB;AACzC,OAAOC,SAASC,QAAQ,QAAQ,QAAO;AAQvC,OAAO,MAAMC,qBAAsC,CAAC,EAAEC,KAAK,EAAEC,IAAI,EAAEC,QAAQ,EAAE;IAC3E,MAAM,EAAEC,QAAQ,EAAEC,KAAK,EAAE,GAAGR,SAAiB;QAAEK;IAAK;IACpD,MAAM,CAACI,WAAWC,aAAa,GAAGR,SAAS,CAACM;IAE5C,MAAMG,WAAW,OAAOH,UAAU,YAAYA,MAAMI,UAAU,CAAC,UAAUJ,MAAMK,QAAQ,CAAC;IAExF,qBACE,MAACC;QAAIC,WAAU;;0BACb,MAACX;gBAAMW,WAAU;;oBACdX,SAAS;oBACTE,0BAAY,KAACU;wBAAKD,WAAU;kCAAW;;;;YAGzC,CAACN,aAAaE,yBACb,MAACG;gBAAIG,OAAO;oBAAEC,YAAY;oBAAUC,SAAS;oBAAQC,KAAK;gBAAO;;kCAC/D,MAACN;wBACCG,OAAO;4BACLI,YAAY;4BACZC,cAAc;4BACdC,UAAU;4BACVC,YAAY;4BACZC,SAAS;wBACX;;4BAECjB;0CACD,KAACQ;gCACCC,OAAO;oCAAES,OAAO;oCAA4BC,UAAU;oCAASC,YAAY;gCAAO;0CACnF;;;;kCAIH,KAACC;wBACCd,WAAU;wBACVe,SAAS;4BACPvB,SAAS;4BACTG,aAAa;wBACf;wBACAqB,MAAK;kCACN;;;+BAKH,KAACC;gBACCC,UAAU,CAACC,IAAM3B,SAAS2B,EAAEC,MAAM,CAAC3B,KAAK;gBACxC4B,aAAY;gBACZnB,OAAO;oBAAEoB,OAAO;gBAAO;gBACvBN,MAAK;gBACLvB,OAAOA,SAAS;;;;AAK1B,EAAC"}
1
+ {"version":3,"sources":["../../../src/ui/EncryptedTextField/index.tsx"],"sourcesContent":["'use client'\n\nimport { Button, useField } from '@payloadcms/ui'\nimport React, { useState } from 'react'\n\ntype Props = {\n label?: string\n path: string\n required?: boolean\n}\n\nexport const EncryptedTextField: React.FC<Props> = ({ label, path, required }) => {\n const { setValue, value } = useField<string>({ path })\n const [isEditing, setIsEditing] = useState(!value)\n\n const isMasked = typeof value === 'string' && value.startsWith('sk-') && value.includes('****')\n\n return (\n <div className=\"field-type text\">\n <label className=\"field-label\">\n {label || 'API Key'}\n {required && <span className=\"required\">*</span>}\n </label>\n\n {!isEditing && isMasked ? (\n <div style={{ alignItems: 'center', display: 'flex', gap: '10px' }}>\n <div\n style={{\n background: 'var(--theme-elevation-100)',\n borderRadius: '4px',\n flexGrow: 1,\n fontFamily: 'monospace',\n padding: '8px 12px',\n }}\n >\n {value}\n <span\n style={{ color: 'var(--theme-success-500)', fontSize: '0.8em', marginLeft: '10px' }}\n >\n ✓ Configured\n </span>\n </div>\n <Button\n buttonStyle=\"secondary\"\n onClick={() => {\n setValue('')\n setIsEditing(true)\n }}\n size=\"medium\"\n >\n Change\n </Button>\n </div>\n ) : (\n <input\n onChange={(e) => setValue(e.target.value)}\n placeholder=\"sk-...\"\n style={{ width: '100%' }}\n type=\"password\"\n value={value || ''}\n />\n )}\n </div>\n )\n}\n"],"names":["Button","useField","React","useState","EncryptedTextField","label","path","required","setValue","value","isEditing","setIsEditing","isMasked","startsWith","includes","div","className","span","style","alignItems","display","gap","background","borderRadius","flexGrow","fontFamily","padding","color","fontSize","marginLeft","buttonStyle","onClick","size","input","onChange","e","target","placeholder","width","type"],"mappings":"AAAA;;AAEA,SAASA,MAAM,EAAEC,QAAQ,QAAQ,iBAAgB;AACjD,OAAOC,SAASC,QAAQ,QAAQ,QAAO;AAQvC,OAAO,MAAMC,qBAAsC,CAAC,EAAEC,KAAK,EAAEC,IAAI,EAAEC,QAAQ,EAAE;IAC3E,MAAM,EAAEC,QAAQ,EAAEC,KAAK,EAAE,GAAGR,SAAiB;QAAEK;IAAK;IACpD,MAAM,CAACI,WAAWC,aAAa,GAAGR,SAAS,CAACM;IAE5C,MAAMG,WAAW,OAAOH,UAAU,YAAYA,MAAMI,UAAU,CAAC,UAAUJ,MAAMK,QAAQ,CAAC;IAExF,qBACE,MAACC;QAAIC,WAAU;;0BACb,MAACX;gBAAMW,WAAU;;oBACdX,SAAS;oBACTE,0BAAY,KAACU;wBAAKD,WAAU;kCAAW;;;;YAGzC,CAACN,aAAaE,yBACb,MAACG;gBAAIG,OAAO;oBAAEC,YAAY;oBAAUC,SAAS;oBAAQC,KAAK;gBAAO;;kCAC/D,MAACN;wBACCG,OAAO;4BACLI,YAAY;4BACZC,cAAc;4BACdC,UAAU;4BACVC,YAAY;4BACZC,SAAS;wBACX;;4BAECjB;0CACD,KAACQ;gCACCC,OAAO;oCAAES,OAAO;oCAA4BC,UAAU;oCAASC,YAAY;gCAAO;0CACnF;;;;kCAIH,KAAC7B;wBACC8B,aAAY;wBACZC,SAAS;4BACPvB,SAAS;4BACTG,aAAa;wBACf;wBACAqB,MAAK;kCACN;;;+BAKH,KAACC;gBACCC,UAAU,CAACC,IAAM3B,SAAS2B,EAAEC,MAAM,CAAC3B,KAAK;gBACxC4B,aAAY;gBACZnB,OAAO;oBAAEoB,OAAO;gBAAO;gBACvBC,MAAK;gBACL9B,OAAOA,SAAS;;;;AAK1B,EAAC"}
@@ -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
- <button className="btn btn--style-secondary btn--size-small" onClick={() => {
27
+ <Button buttonStyle="secondary" onClick={() => {
28
28
  setValue('');
29
29
  setIsEditing(true);
30
- }} type="button">
30
+ }} size="medium">
31
31
  Change
32
- </button>
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
  };
@@ -92,7 +92,7 @@ import { getFieldBySchemaPath } from './getFieldBySchemaPath.js';
92
92
  /**
93
93
  * Build a smart contextual prompt based on field metadata.
94
94
  * This is used as a fallback when the user hasn't set a custom prompt.
95
- *
95
+ *
96
96
  * @param context - The context containing schema path and document data
97
97
  * @returns A contextual prompt string that can be used for AI generation
98
98
  */ export const buildSmartPrompt = (context)=>{
@@ -105,18 +105,16 @@ import { getFieldBySchemaPath } from './getFieldBySchemaPath.js';
105
105
  const parts = [];
106
106
  // Use description as primary guidance if available
107
107
  if (description) {
108
- parts.push(description);
109
- } else {
110
- // Fall back to type-based guidance, prefer label over name for better context
111
- parts.push(getTypeGuidance(type, label || name));
108
+ parts.push(`Field description for user: ${description}\n`);
112
109
  }
110
+ parts.push(getTypeGuidance(type, label || name));
113
111
  // Add parent context if nested
114
112
  const parentPhrase = getParentContextPhrase(parentContext);
115
113
  if (parentPhrase) {
116
114
  parts.push(parentPhrase);
117
115
  }
118
116
  // Add document title context if available
119
- const title = documentData?.title;
117
+ const title = documentData?.title || documentData?.name;
120
118
  if (title && typeof title === 'string') {
121
119
  parts.push(`in the context of "${title}"`);
122
120
  }
@@ -1 +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) {return null}\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(description)\n } else {\n // Fall back to type-based guidance, prefer label over name for better context\n parts.push(getTypeGuidance(type, label || name))\n }\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\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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"}
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
- return iv.toString('hex') + ':' + encrypted.toString('hex');
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 return iv.toString('hex') + ':' + encrypted.toString('hex')\n}\n\nexport function decrypt(text: string, secret: string): string {\n if (!text) {return text}\n if (!secret) {throw new Error('No secret provided for decryption')}\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","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;QAAC,OAAOA;IAAI;IACvB,IAAI,CAACC,QAAQ;QAAC,MAAM,IAAIC,MAAM;IAAoC;IAElE,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,OAAOP,GAAGQ,QAAQ,CAAC,SAAS,MAAMJ,UAAUI,QAAQ,CAAC;AACvD;AAEA,OAAO,SAASC,QAAQhB,IAAY,EAAEC,MAAc;IAClD,IAAI,CAACD,MAAM;QAAC,OAAOA;IAAI;IACvB,IAAI,CAACC,QAAQ;QAAC,MAAM,IAAIC,MAAM;IAAoC;IAElE,IAAI;QACF,MAAMe,YAAYjB,KAAKkB,KAAK,CAAC;QAC7B,MAAMX,KAAKK,OAAOO,IAAI,CAACF,UAAUG,KAAK,IAAK;QAC3C,MAAMC,gBAAgBT,OAAOO,IAAI,CAACF,UAAUK,IAAI,CAAC,MAAM;QACvD,MAAMnB,MAAMP,OAAOQ,UAAU,CAAC,UAAUC,MAAM,CAACJ,QAAQK,MAAM;QAC7D,MAAMiB,WAAW3B,OAAO4B,gBAAgB,CAAC3B,WAAWM,KAAKI;QACzD,IAAIkB,YAAYF,SAASlB,MAAM,CAACgB;QAChCI,YAAYb,OAAOC,MAAM,CAAC;YAACY;YAAWF,SAAST,KAAK;SAAG;QACvD,OAAOW,UAAUV,QAAQ;IAC3B,EAAE,OAAOW,GAAG;QACV,gFAAgF;QAChF,OAAO1B;IACT;AACF"}
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"}
@@ -0,0 +1,7 @@
1
+ import type { PayloadRequest } from 'payload';
2
+ interface SeedPropertiesArgs {
3
+ enabledCollections: string[];
4
+ req: PayloadRequest;
5
+ }
6
+ export declare const seedProperties: ({ enabledCollections, req }: SeedPropertiesArgs) => Promise<void>;
7
+ export {};
@@ -0,0 +1,100 @@
1
+ import { PLUGIN_INSTRUCTIONS_TABLE } from '../defaults.js';
2
+ import { updateFieldsConfig } from './updateFieldsConfig.js';
3
+ export const seedProperties = async ({ enabledCollections, req })=>{
4
+ const { payload } = req;
5
+ if (!enabledCollections || enabledCollections.length === 0) {
6
+ return;
7
+ }
8
+ // Get all collections from payload config
9
+ const allCollections = payload.config.collections;
10
+ for (const collectionSlug of enabledCollections){
11
+ const collectionConfig = allCollections.find((c)=>c.slug === collectionSlug);
12
+ if (!collectionConfig) {
13
+ continue;
14
+ }
15
+ // Traverse the collection config effectively using updateFieldsConfig
16
+ // Use the side-effect of getting schemaPathMap from it
17
+ const { schemaPathMap } = updateFieldsConfig(collectionConfig);
18
+ for (const [schemaPath, fieldInfo] of Object.entries(schemaPathMap)){
19
+ const { type, custom, relationTo } = fieldInfo;
20
+ // Check if instruction already exists
21
+ const existingInstruction = await payload.find({
22
+ collection: PLUGIN_INSTRUCTIONS_TABLE,
23
+ depth: 0,
24
+ limit: 1,
25
+ overrideAccess: true,
26
+ where: {
27
+ 'schema-path': {
28
+ equals: schemaPath
29
+ }
30
+ }
31
+ });
32
+ if (existingInstruction.totalDocs > 0) {
33
+ // If developer has provided custom prompts in the schema, update the existing record
34
+ // but only if the values have actually changed.
35
+ if (custom?.ai?.prompt || custom?.ai?.system) {
36
+ const doc = existingInstruction.docs[0];
37
+ const currentPrompt = doc.prompt;
38
+ const currentSystem = doc.system;
39
+ let needsUpdate = false;
40
+ const updateData = {};
41
+ if (custom?.ai?.prompt && custom.ai.prompt !== currentPrompt) {
42
+ updateData.prompt = custom.ai.prompt;
43
+ needsUpdate = true;
44
+ }
45
+ if (custom?.ai?.system && custom.ai.system !== currentSystem) {
46
+ updateData.system = custom.ai.system;
47
+ needsUpdate = true;
48
+ }
49
+ if (needsUpdate) {
50
+ try {
51
+ await payload.update({
52
+ id: doc.id,
53
+ collection: PLUGIN_INSTRUCTIONS_TABLE,
54
+ data: updateData,
55
+ overrideAccess: true
56
+ });
57
+ } catch (error) {
58
+ payload.logger.error(`Failed to update instruction for ${schemaPath}: ${error}`);
59
+ }
60
+ }
61
+ }
62
+ continue;
63
+ }
64
+ // Use custom prompts if provided, otherwise leave empty
65
+ const prompt = custom?.ai?.prompt || '';
66
+ const system = custom?.ai?.system || '';
67
+ // Determine model-id based on field type
68
+ let modelId = 'text';
69
+ if (type === 'richText') {
70
+ modelId = 'richtext';
71
+ }
72
+ if (type === 'upload') {
73
+ modelId = 'image';
74
+ } // defaulting to image generation for uploads
75
+ if (type === 'array') {
76
+ modelId = 'array';
77
+ }
78
+ // Create new instruction
79
+ try {
80
+ await payload.create({
81
+ collection: PLUGIN_INSTRUCTIONS_TABLE,
82
+ data: {
83
+ disabled: false,
84
+ 'field-type': type,
85
+ 'model-id': modelId,
86
+ prompt,
87
+ 'relation-to': relationTo,
88
+ 'schema-path': schemaPath,
89
+ system
90
+ },
91
+ overrideAccess: true
92
+ });
93
+ } catch (error) {
94
+ payload.logger.error(`Failed to seed instruction for ${schemaPath}: ${error}`);
95
+ }
96
+ }
97
+ }
98
+ };
99
+
100
+ //# sourceMappingURL=seedProperties.js.map