@ai-stack/payloadcms 3.76.0-beta.6 → 3.76.0-beta.8
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/endpoints/generate.js +4 -2
- package/dist/endpoints/generate.js.map +1 -1
- package/dist/endpoints/upload.js +10 -6
- package/dist/endpoints/upload.js.map +1 -1
- package/dist/libraries/{handlebars → templates}/helpersMap.d.ts +3 -3
- package/dist/libraries/templates/helpersMap.js +13 -0
- package/dist/libraries/templates/helpersMap.js.map +1 -0
- package/dist/libraries/templates/legacySyntax.d.ts +5 -0
- package/dist/libraries/templates/legacySyntax.js +273 -0
- package/dist/libraries/templates/legacySyntax.js.map +1 -0
- package/dist/libraries/templates/legacySyntax.test.d.ts +1 -0
- package/dist/libraries/templates/legacySyntax.test.js +41 -0
- package/dist/libraries/templates/legacySyntax.test.js.map +1 -0
- package/dist/libraries/templates/renderTemplate.d.ts +7 -0
- package/dist/libraries/templates/renderTemplate.js +108 -0
- package/dist/libraries/templates/renderTemplate.js.map +1 -0
- package/dist/providers/InstructionsProvider/useInstructions.js +2 -2
- package/dist/providers/InstructionsProvider/useInstructions.js.map +1 -1
- package/dist/utilities/buildPromptUtils.d.ts +4 -2
- package/dist/utilities/buildPromptUtils.js +13 -15
- package/dist/utilities/buildPromptUtils.js.map +1 -1
- package/dist/utilities/lexical/lexicalToPromptTemplate.js +3 -13
- package/dist/utilities/lexical/lexicalToPromptTemplate.js.map +1 -1
- package/dist/utilities/runtime/resolveServerURL.js +16 -1
- package/dist/utilities/runtime/resolveServerURL.js.map +1 -1
- package/package.json +2 -3
- package/dist/libraries/handlebars/asyncHandlebars.d.ts +0 -1
- package/dist/libraries/handlebars/asyncHandlebars.js +0 -5
- package/dist/libraries/handlebars/asyncHandlebars.js.map +0 -1
- package/dist/libraries/handlebars/helpers.d.ts +0 -1
- package/dist/libraries/handlebars/helpers.js +0 -27
- package/dist/libraries/handlebars/helpers.js.map +0 -1
- package/dist/libraries/handlebars/helpersMap.js +0 -13
- package/dist/libraries/handlebars/helpersMap.js.map +0 -1
- package/dist/libraries/handlebars/replacePlaceholders.d.ts +0 -1
- package/dist/libraries/handlebars/replacePlaceholders.js +0 -12
- package/dist/libraries/handlebars/replacePlaceholders.js.map +0 -1
- package/dist/types/handlebars-async-helpers.d.ts +0 -1
- package/dist/types/handlebars-dist-handlebars.d.ts +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { convertLegacyTemplateToLiquid, convertLiquidTemplateToLegacySuggestions, isLegacyBlockExpression, isLiquidBlockExpression, usesLegacyToHTMLHelper } from './legacySyntax.js';
|
|
3
|
+
describe('convertLegacyTemplateToLiquid', ()=>{
|
|
4
|
+
it('sanitizes mention shorthand syntax', ()=>{
|
|
5
|
+
expect(convertLegacyTemplateToLiquid('Name: {{ #title }}')).toBe('Name: {{ title }}');
|
|
6
|
+
});
|
|
7
|
+
it('converts toHTML helper to liquid filter syntax', ()=>{
|
|
8
|
+
expect(convertLegacyTemplateToLiquid('HTML: {{toHTML details}}')).toBe("HTML: {{ details | toHTML: 'details' }}");
|
|
9
|
+
});
|
|
10
|
+
it('converts if/else blocks', ()=>{
|
|
11
|
+
const source = '{{#if title}}Yes{{else}}No{{/if}}';
|
|
12
|
+
expect(convertLegacyTemplateToLiquid(source)).toBe('{% if title %}Yes{% else %}No{% endif %}');
|
|
13
|
+
});
|
|
14
|
+
it('converts each blocks with loop aliases', ()=>{
|
|
15
|
+
const source = '{{#each items}}{{@index}}: {{this.name}}{{/each}}';
|
|
16
|
+
expect(convertLegacyTemplateToLiquid(source)).toBe('{% for __item0 in items %}{{ forloop.index0 }}: {{ __item0.name }}{% endfor %}');
|
|
17
|
+
});
|
|
18
|
+
it('converts with blocks with scoped assignments', ()=>{
|
|
19
|
+
const source = '{{#with author}}{{name}}{{/with}}';
|
|
20
|
+
expect(convertLegacyTemplateToLiquid(source)).toBe('{% assign __with0 = author %}{% if __with0 %}{{ __with0.name }}{% endif %}');
|
|
21
|
+
});
|
|
22
|
+
it('normalizes nested template braces from mention insertion', ()=>{
|
|
23
|
+
const source = '{{toHTML {{content}}}}';
|
|
24
|
+
expect(convertLegacyTemplateToLiquid(source)).toBe("{{ content | toHTML: 'content' }}");
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
describe('template syntax helpers', ()=>{
|
|
28
|
+
it('detects legacy and liquid block syntax', ()=>{
|
|
29
|
+
expect(isLegacyBlockExpression('{{#if title}}x{{/if}}')).toBe(true);
|
|
30
|
+
expect(isLiquidBlockExpression('{% if title %}x{% endif %}')).toBe(true);
|
|
31
|
+
});
|
|
32
|
+
it('detects legacy toHTML helper usage', ()=>{
|
|
33
|
+
expect(usesLegacyToHTMLHelper('{{toHTML details}}')).toBe(true);
|
|
34
|
+
expect(usesLegacyToHTMLHelper('{{ details | toHTML }}')).toBe(false);
|
|
35
|
+
});
|
|
36
|
+
it('converts liquid toHTML filter back to legacy suggestion format', ()=>{
|
|
37
|
+
expect(convertLiquidTemplateToLegacySuggestions("{{ details | toHTML: 'details' }}")).toBe('{{toHTML details}}');
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
//# sourceMappingURL=legacySyntax.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/libraries/templates/legacySyntax.test.ts"],"sourcesContent":["import { describe, expect, it } from 'vitest'\n\nimport {\n convertLegacyTemplateToLiquid,\n convertLiquidTemplateToLegacySuggestions,\n isLegacyBlockExpression,\n isLiquidBlockExpression,\n usesLegacyToHTMLHelper,\n} from './legacySyntax.js'\n\ndescribe('convertLegacyTemplateToLiquid', () => {\n it('sanitizes mention shorthand syntax', () => {\n expect(convertLegacyTemplateToLiquid('Name: {{ #title }}')).toBe('Name: {{ title }}')\n })\n\n it('converts toHTML helper to liquid filter syntax', () => {\n expect(convertLegacyTemplateToLiquid('HTML: {{toHTML details}}')).toBe(\n \"HTML: {{ details | toHTML: 'details' }}\",\n )\n })\n\n it('converts if/else blocks', () => {\n const source = '{{#if title}}Yes{{else}}No{{/if}}'\n expect(convertLegacyTemplateToLiquid(source)).toBe('{% if title %}Yes{% else %}No{% endif %}')\n })\n\n it('converts each blocks with loop aliases', () => {\n const source = '{{#each items}}{{@index}}: {{this.name}}{{/each}}'\n expect(convertLegacyTemplateToLiquid(source)).toBe(\n '{% for __item0 in items %}{{ forloop.index0 }}: {{ __item0.name }}{% endfor %}',\n )\n })\n\n it('converts with blocks with scoped assignments', () => {\n const source = '{{#with author}}{{name}}{{/with}}'\n expect(convertLegacyTemplateToLiquid(source)).toBe(\n '{% assign __with0 = author %}{% if __with0 %}{{ __with0.name }}{% endif %}',\n )\n })\n\n it('normalizes nested template braces from mention insertion', () => {\n const source = '{{toHTML {{content}}}}'\n expect(convertLegacyTemplateToLiquid(source)).toBe(\"{{ content | toHTML: 'content' }}\")\n })\n})\n\ndescribe('template syntax helpers', () => {\n it('detects legacy and liquid block syntax', () => {\n expect(isLegacyBlockExpression('{{#if title}}x{{/if}}')).toBe(true)\n expect(isLiquidBlockExpression('{% if title %}x{% endif %}')).toBe(true)\n })\n\n it('detects legacy toHTML helper usage', () => {\n expect(usesLegacyToHTMLHelper('{{toHTML details}}')).toBe(true)\n expect(usesLegacyToHTMLHelper('{{ details | toHTML }}')).toBe(false)\n })\n\n it('converts liquid toHTML filter back to legacy suggestion format', () => {\n expect(convertLiquidTemplateToLegacySuggestions(\"{{ details | toHTML: 'details' }}\")).toBe(\n '{{toHTML details}}',\n )\n })\n})\n"],"names":["describe","expect","it","convertLegacyTemplateToLiquid","convertLiquidTemplateToLegacySuggestions","isLegacyBlockExpression","isLiquidBlockExpression","usesLegacyToHTMLHelper","toBe","source"],"mappings":"AAAA,SAASA,QAAQ,EAAEC,MAAM,EAAEC,EAAE,QAAQ,SAAQ;AAE7C,SACEC,6BAA6B,EAC7BC,wCAAwC,EACxCC,uBAAuB,EACvBC,uBAAuB,EACvBC,sBAAsB,QACjB,oBAAmB;AAE1BP,SAAS,iCAAiC;IACxCE,GAAG,sCAAsC;QACvCD,OAAOE,8BAA8B,uBAAuBK,IAAI,CAAC;IACnE;IAEAN,GAAG,kDAAkD;QACnDD,OAAOE,8BAA8B,6BAA6BK,IAAI,CACpE;IAEJ;IAEAN,GAAG,2BAA2B;QAC5B,MAAMO,SAAS;QACfR,OAAOE,8BAA8BM,SAASD,IAAI,CAAC;IACrD;IAEAN,GAAG,0CAA0C;QAC3C,MAAMO,SAAS;QACfR,OAAOE,8BAA8BM,SAASD,IAAI,CAChD;IAEJ;IAEAN,GAAG,gDAAgD;QACjD,MAAMO,SAAS;QACfR,OAAOE,8BAA8BM,SAASD,IAAI,CAChD;IAEJ;IAEAN,GAAG,4DAA4D;QAC7D,MAAMO,SAAS;QACfR,OAAOE,8BAA8BM,SAASD,IAAI,CAAC;IACrD;AACF;AAEAR,SAAS,2BAA2B;IAClCE,GAAG,0CAA0C;QAC3CD,OAAOI,wBAAwB,0BAA0BG,IAAI,CAAC;QAC9DP,OAAOK,wBAAwB,+BAA+BE,IAAI,CAAC;IACrE;IAEAN,GAAG,sCAAsC;QACvCD,OAAOM,uBAAuB,uBAAuBC,IAAI,CAAC;QAC1DP,OAAOM,uBAAuB,2BAA2BC,IAAI,CAAC;IAChE;IAEAN,GAAG,kEAAkE;QACnED,OAAOG,yCAAyC,sCAAsCI,IAAI,CACxF;IAEJ;AACF"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { Liquid } from 'liquidjs';
|
|
2
|
+
import { getFieldInfo } from '../../utilities/fields/getFieldInfo.js';
|
|
3
|
+
import { lexicalToHTML } from '../../utilities/lexical/lexicalToHTML.js';
|
|
4
|
+
import { convertLegacyTemplateToLiquid } from './legacySyntax.js';
|
|
5
|
+
const TEMPLATE_CACHE_LIMIT = 500;
|
|
6
|
+
const parsedTemplateCache = new Map();
|
|
7
|
+
const templateEngine = new Liquid({
|
|
8
|
+
jsTruthy: true,
|
|
9
|
+
strictFilters: false,
|
|
10
|
+
strictVariables: false
|
|
11
|
+
});
|
|
12
|
+
const isLexicalEditorState = (value)=>{
|
|
13
|
+
if (!value || typeof value !== 'object') {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
const root = value.root;
|
|
17
|
+
if (!root || typeof root !== 'object') {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
const children = root.children;
|
|
21
|
+
return Array.isArray(children);
|
|
22
|
+
};
|
|
23
|
+
const getCollectionSlugFromSchemaPath = (schemaPath)=>{
|
|
24
|
+
if (!schemaPath) {
|
|
25
|
+
return '';
|
|
26
|
+
}
|
|
27
|
+
return schemaPath.split('.')[0] || '';
|
|
28
|
+
};
|
|
29
|
+
const normalizeLegacyFieldPath = (fieldPath)=>{
|
|
30
|
+
return fieldPath.trim().replace(/^['"]|['"]$/g, '').replace(/^\.\.\//, '').replace(/^__item\d+\./, '').replace(/^__with\d+\./, '');
|
|
31
|
+
};
|
|
32
|
+
const getRuntimeFromFilterContext = (ctx)=>{
|
|
33
|
+
const liquidFilterContext = ctx;
|
|
34
|
+
const getter = liquidFilterContext?.context?.get;
|
|
35
|
+
if (!getter) {
|
|
36
|
+
return {};
|
|
37
|
+
}
|
|
38
|
+
try {
|
|
39
|
+
const value = getter.call(liquidFilterContext.context, [
|
|
40
|
+
'__templateRuntime'
|
|
41
|
+
]);
|
|
42
|
+
if (value && typeof value === 'object') {
|
|
43
|
+
return value;
|
|
44
|
+
}
|
|
45
|
+
} catch {
|
|
46
|
+
// No-op: fallback to string path lookup below.
|
|
47
|
+
}
|
|
48
|
+
try {
|
|
49
|
+
const value = getter.call(liquidFilterContext.context, '__templateRuntime');
|
|
50
|
+
if (value && typeof value === 'object') {
|
|
51
|
+
return value;
|
|
52
|
+
}
|
|
53
|
+
} catch {
|
|
54
|
+
// No-op: ignore runtime lookup failures.
|
|
55
|
+
}
|
|
56
|
+
return {};
|
|
57
|
+
};
|
|
58
|
+
templateEngine.registerFilter('toHTML', async function(value, fieldPath) {
|
|
59
|
+
if (!isLexicalEditorState(value)) {
|
|
60
|
+
return typeof value === 'string' ? value : '';
|
|
61
|
+
}
|
|
62
|
+
const runtime = getRuntimeFromFilterContext(this);
|
|
63
|
+
const payloadCollections = runtime.payload?.collections;
|
|
64
|
+
if (!payloadCollections) {
|
|
65
|
+
return '';
|
|
66
|
+
}
|
|
67
|
+
const collectionSlug = getCollectionSlugFromSchemaPath(runtime.schemaPath);
|
|
68
|
+
const normalizedFieldPath = normalizeLegacyFieldPath(fieldPath || '');
|
|
69
|
+
const schemaPath = normalizedFieldPath.startsWith(`${collectionSlug}.`) ? normalizedFieldPath : `${collectionSlug}.${normalizedFieldPath}`;
|
|
70
|
+
if (!collectionSlug || !normalizedFieldPath) {
|
|
71
|
+
return '';
|
|
72
|
+
}
|
|
73
|
+
const fieldInfo = getFieldInfo(payloadCollections, schemaPath);
|
|
74
|
+
if (!fieldInfo || !('editor' in fieldInfo) || !fieldInfo.editor || typeof fieldInfo.editor !== 'object' || !('editorConfig' in fieldInfo.editor) || !fieldInfo.editor.editorConfig || typeof fieldInfo.editor.editorConfig !== 'object' || !('features' in fieldInfo.editor.editorConfig) || !('lexical' in fieldInfo.editor.editorConfig) || !('resolvedFeatureMap' in fieldInfo.editor.editorConfig)) {
|
|
75
|
+
return '';
|
|
76
|
+
}
|
|
77
|
+
return lexicalToHTML(value, fieldInfo.editor.editorConfig);
|
|
78
|
+
});
|
|
79
|
+
const getParsedTemplate = (template)=>{
|
|
80
|
+
const cached = parsedTemplateCache.get(template);
|
|
81
|
+
if (cached) {
|
|
82
|
+
return cached;
|
|
83
|
+
}
|
|
84
|
+
const parsed = templateEngine.parse(template);
|
|
85
|
+
parsedTemplateCache.set(template, parsed);
|
|
86
|
+
if (parsedTemplateCache.size > TEMPLATE_CACHE_LIMIT) {
|
|
87
|
+
const oldestKey = parsedTemplateCache.keys().next().value;
|
|
88
|
+
if (oldestKey) {
|
|
89
|
+
parsedTemplateCache.delete(oldestKey);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return parsed;
|
|
93
|
+
};
|
|
94
|
+
export const renderTemplate = async (template, values, runtime = {})=>{
|
|
95
|
+
if (!template || typeof template !== 'string') {
|
|
96
|
+
return '';
|
|
97
|
+
}
|
|
98
|
+
const normalizedTemplate = convertLegacyTemplateToLiquid(template);
|
|
99
|
+
const parsed = getParsedTemplate(normalizedTemplate);
|
|
100
|
+
const renderContext = {
|
|
101
|
+
...values,
|
|
102
|
+
__templateRuntime: runtime
|
|
103
|
+
};
|
|
104
|
+
const rendered = await templateEngine.render(parsed, renderContext);
|
|
105
|
+
return typeof rendered === 'string' ? rendered : String(rendered ?? '');
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
//# sourceMappingURL=renderTemplate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/libraries/templates/renderTemplate.ts"],"sourcesContent":["import { Liquid } from 'liquidjs'\n\nimport { getFieldInfo } from '../../utilities/fields/getFieldInfo.js'\nimport { lexicalToHTML } from '../../utilities/lexical/lexicalToHTML.js'\nimport { convertLegacyTemplateToLiquid } from './legacySyntax.js'\n\nexport interface TemplateRuntime {\n payload?: {\n collections?: Record<string, unknown>\n }\n schemaPath?: string\n}\n\nconst TEMPLATE_CACHE_LIMIT = 500\nconst parsedTemplateCache = new Map<string, unknown>()\n\nconst templateEngine = new Liquid({\n jsTruthy: true,\n strictFilters: false,\n strictVariables: false,\n})\n\nconst isLexicalEditorState = (\n value: unknown,\n): value is { root: { children: unknown[]; type?: string } } => {\n if (!value || typeof value !== 'object') {\n return false\n }\n\n const root = (value as { root?: unknown }).root\n if (!root || typeof root !== 'object') {\n return false\n }\n\n const children = (root as { children?: unknown }).children\n return Array.isArray(children)\n}\n\nconst getCollectionSlugFromSchemaPath = (schemaPath?: string): string => {\n if (!schemaPath) {\n return ''\n }\n return schemaPath.split('.')[0] || ''\n}\n\nconst normalizeLegacyFieldPath = (fieldPath: string): string => {\n return fieldPath\n .trim()\n .replace(/^['\"]|['\"]$/g, '')\n .replace(/^\\.\\.\\//, '')\n .replace(/^__item\\d+\\./, '')\n .replace(/^__with\\d+\\./, '')\n}\n\nconst getRuntimeFromFilterContext = (ctx: unknown): TemplateRuntime => {\n const liquidFilterContext = ctx as {\n context?: {\n get?: (path: string | string[]) => unknown\n }\n }\n\n const getter = liquidFilterContext?.context?.get\n if (!getter) {\n return {}\n }\n\n try {\n const value = getter.call(liquidFilterContext.context, ['__templateRuntime'])\n if (value && typeof value === 'object') {\n return value as TemplateRuntime\n }\n } catch {\n // No-op: fallback to string path lookup below.\n }\n\n try {\n const value = getter.call(liquidFilterContext.context, '__templateRuntime')\n if (value && typeof value === 'object') {\n return value as TemplateRuntime\n }\n } catch {\n // No-op: ignore runtime lookup failures.\n }\n\n return {}\n}\n\ntemplateEngine.registerFilter('toHTML', async function (value: unknown, fieldPath?: string) {\n if (!isLexicalEditorState(value)) {\n return typeof value === 'string' ? value : ''\n }\n\n const runtime = getRuntimeFromFilterContext(this)\n const payloadCollections = runtime.payload?.collections\n if (!payloadCollections) {\n return ''\n }\n\n const collectionSlug = getCollectionSlugFromSchemaPath(runtime.schemaPath)\n const normalizedFieldPath = normalizeLegacyFieldPath(fieldPath || '')\n const schemaPath = normalizedFieldPath.startsWith(`${collectionSlug}.`)\n ? normalizedFieldPath\n : `${collectionSlug}.${normalizedFieldPath}`\n\n if (!collectionSlug || !normalizedFieldPath) {\n return ''\n }\n\n const fieldInfo = getFieldInfo(payloadCollections as any, schemaPath)\n if (\n !fieldInfo ||\n !('editor' in fieldInfo) ||\n !fieldInfo.editor ||\n typeof fieldInfo.editor !== 'object' ||\n !('editorConfig' in fieldInfo.editor) ||\n !fieldInfo.editor.editorConfig ||\n typeof fieldInfo.editor.editorConfig !== 'object' ||\n !('features' in fieldInfo.editor.editorConfig) ||\n !('lexical' in fieldInfo.editor.editorConfig) ||\n !('resolvedFeatureMap' in fieldInfo.editor.editorConfig)\n ) {\n return ''\n }\n\n return lexicalToHTML(value as any, fieldInfo.editor.editorConfig as any)\n})\n\nconst getParsedTemplate = (template: string): unknown => {\n const cached = parsedTemplateCache.get(template)\n if (cached) {\n return cached\n }\n\n const parsed = templateEngine.parse(template)\n parsedTemplateCache.set(template, parsed)\n\n if (parsedTemplateCache.size > TEMPLATE_CACHE_LIMIT) {\n const oldestKey = parsedTemplateCache.keys().next().value\n if (oldestKey) {\n parsedTemplateCache.delete(oldestKey)\n }\n }\n\n return parsed\n}\n\nexport const renderTemplate = async (\n template: string,\n values: object,\n runtime: TemplateRuntime = {},\n): Promise<string> => {\n if (!template || typeof template !== 'string') {\n return ''\n }\n\n const normalizedTemplate = convertLegacyTemplateToLiquid(template)\n const parsed = getParsedTemplate(normalizedTemplate)\n const renderContext = {\n ...(values as Record<string, unknown>),\n __templateRuntime: runtime,\n }\n\n const rendered = await templateEngine.render(parsed as any, renderContext)\n return typeof rendered === 'string' ? rendered : String(rendered ?? '')\n}\n"],"names":["Liquid","getFieldInfo","lexicalToHTML","convertLegacyTemplateToLiquid","TEMPLATE_CACHE_LIMIT","parsedTemplateCache","Map","templateEngine","jsTruthy","strictFilters","strictVariables","isLexicalEditorState","value","root","children","Array","isArray","getCollectionSlugFromSchemaPath","schemaPath","split","normalizeLegacyFieldPath","fieldPath","trim","replace","getRuntimeFromFilterContext","ctx","liquidFilterContext","getter","context","get","call","registerFilter","runtime","payloadCollections","payload","collections","collectionSlug","normalizedFieldPath","startsWith","fieldInfo","editor","editorConfig","getParsedTemplate","template","cached","parsed","parse","set","size","oldestKey","keys","next","delete","renderTemplate","values","normalizedTemplate","renderContext","__templateRuntime","rendered","render","String"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useDocumentInfo } from '@payloadcms/ui';
|
|
2
2
|
import { useContext, useEffect, useMemo, useState } from 'react';
|
|
3
3
|
import { PLUGIN_INSTRUCTIONS_TABLE } from '../../defaults.js';
|
|
4
|
-
import {
|
|
4
|
+
import { templateHelpers, templateHelpersMap } from '../../libraries/templates/helpersMap.js';
|
|
5
5
|
import { InstructionsContext } from './context.js';
|
|
6
6
|
/**
|
|
7
7
|
* Normalize a schema path by removing array indices.
|
|
@@ -82,7 +82,7 @@ export const useInstructions = (update = {})=>{
|
|
|
82
82
|
if (fieldInfo.fieldType === 'upload') {
|
|
83
83
|
return;
|
|
84
84
|
}
|
|
85
|
-
const helpers =
|
|
85
|
+
const helpers = templateHelpers.filter((h)=>templateHelpersMap[h]?.field === fieldInfo.fieldType);
|
|
86
86
|
if (helpers.length) {
|
|
87
87
|
for (const helper of helpers){
|
|
88
88
|
suggestions.push(`${helper} ${f}`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/providers/InstructionsProvider/useInstructions.ts"],"sourcesContent":["import { useDocumentInfo } from '@payloadcms/ui'\nimport { useContext, useEffect, useMemo, useState } from 'react'\n\nimport { PLUGIN_INSTRUCTIONS_TABLE } from '../../defaults.js'\nimport {
|
|
1
|
+
{"version":3,"sources":["../../../src/providers/InstructionsProvider/useInstructions.ts"],"sourcesContent":["import { useDocumentInfo } from '@payloadcms/ui'\nimport { useContext, useEffect, useMemo, useState } from 'react'\n\nimport { PLUGIN_INSTRUCTIONS_TABLE } from '../../defaults.js'\nimport { templateHelpers, templateHelpersMap } from '../../libraries/templates/helpersMap.js'\nimport { InstructionsContext } from './context.js'\n\n/**\n * Normalize a schema path by removing array indices.\n * This allows fields inside arrays to match their instruction records.\n * \n * Example:\n * 'array-test-cases.keywords.0.keyword' -> 'array-test-cases.keywords.keyword'\n * 'characters.views.2.description' -> 'characters.views.description'\n * 'posts.title' -> 'posts.title' (no change)\n */\nconst normalizeSchemaPath = (path: string): string => {\n if (!path) {\n return path\n }\n // Remove numeric path segments (array indices) and new Lexical _index- based keys\n return path\n .split('.')\n .filter((segment) => !/^\\d+$/.test(segment) && !segment.startsWith('_index-'))\n .join('.')\n}\n\nconst warnedOnceOnNoInstructionId = new Set<string>()\nconst warnOnceOnMissingInstructions = (path: string) => {\n if (!warnedOnceOnNoInstructionId.has(path)) {\n warnedOnceOnNoInstructionId.add(path)\n // eslint-disable-next-line no-console\n console.info(`[AI Plugin] There are no AI instructions for this field: ${path}.`)\n }\n}\n\nexport const useInstructions = (\n update: {\n schemaPath?: unknown\n } = {},\n) => {\n const context = useContext(InstructionsContext)\n const { collectionSlug } = useDocumentInfo()\n const { activeCollection, debugging, hasInstructions, instructions, promptFields, setActiveCollection } = context\n\n const [schemaPath, setSchemaPath] = useState(update.schemaPath as string)\n\n useEffect(() => {\n if (update.schemaPath !== schemaPath) {\n setSchemaPath((update.schemaPath as string) ?? '')\n }\n }, [update.schemaPath, schemaPath])\n\n useEffect(() => {\n if (\n activeCollection !== collectionSlug &&\n collectionSlug !== PLUGIN_INSTRUCTIONS_TABLE &&\n typeof setActiveCollection === 'function'\n ) {\n setActiveCollection(collectionSlug ?? '')\n }\n }, [activeCollection, collectionSlug, setActiveCollection])\n\n const groupedFields = useMemo(() => {\n const result: Record<string, string[]> = {}\n\n for (const fullKey of Object.keys(instructions || {})) {\n const [collection, ...pathParts] = fullKey.split('.')\n const path = pathParts.join('.')\n if (!result[collection]) {\n result[collection] = []\n }\n result[collection].push(path)\n }\n\n return result\n }, [instructions])\n\n // Suggestions for prompt editor\n const promptEditorSuggestions = useMemo(() => {\n const activeFields = groupedFields[activeCollection as string] || []\n const suggestions: string[] = []\n\n // Build instruction lookup map once for O(1) access instead of O(n) search per field\n const instructionLookup = new Map(\n Object.entries(instructions).map(([key, value]) => {\n const path = key.split('.').slice(1).join('.')\n return [path, value]\n })\n )\n\n activeFields.forEach((f) => {\n const fieldInfo = instructionLookup.get(f)\n\n if (!fieldInfo) {return}\n\n if (fieldInfo.fieldType === 'upload') {\n return\n }\n\n const helpers = templateHelpers.filter(\n (h) => (templateHelpersMap as Record<string, { field?: string }>)[h]?.field === fieldInfo.fieldType,\n )\n\n if (helpers.length) {\n for (const helper of helpers) {\n suggestions.push(`${helper} ${f}`)\n }\n } else {\n suggestions.push(f)\n }\n })\n\n promptFields.forEach(({ name, collections }) => {\n if (!activeCollection) {return}\n\n if (!collections || collections.includes(activeCollection)) {\n suggestions.push(name)\n }\n })\n\n return suggestions\n }, [groupedFields, activeCollection, instructions, promptFields])\n\n // Normalize the schema path to handle array indices\n const normalizedSchemaPath = normalizeSchemaPath(schemaPath)\n const pathInstructions = instructions[normalizedSchemaPath]\n\n if (debugging && !pathInstructions && schemaPath && hasInstructions) {\n warnOnceOnMissingInstructions(schemaPath)\n }\n\n const isCollectionEnabled = context.enabledCollections?.includes(collectionSlug || activeCollection || '') ?? false\n\n return {\n ...context,\n ...(pathInstructions || {}),\n disabled: !isCollectionEnabled || (pathInstructions?.disabled ?? false),\n promptEditorSuggestions,\n }\n}\n"],"names":["useDocumentInfo","useContext","useEffect","useMemo","useState","PLUGIN_INSTRUCTIONS_TABLE","templateHelpers","templateHelpersMap","InstructionsContext","normalizeSchemaPath","path","split","filter","segment","test","startsWith","join","warnedOnceOnNoInstructionId","Set","warnOnceOnMissingInstructions","has","add","console","info","useInstructions","update","context","collectionSlug","activeCollection","debugging","hasInstructions","instructions","promptFields","setActiveCollection","schemaPath","setSchemaPath","groupedFields","result","fullKey","Object","keys","collection","pathParts","push","promptEditorSuggestions","activeFields","suggestions","instructionLookup","Map","entries","map","key","value","slice","forEach","f","fieldInfo","get","fieldType","helpers","h","field","length","helper","name","collections","includes","normalizedSchemaPath","pathInstructions","isCollectionEnabled","enabledCollections","disabled"],"mappings":"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"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { CollectionSlug } from 'payload';
|
|
2
2
|
import type { ActionMenuItems, PluginConfig, PromptFieldGetterContext } from '../types.js';
|
|
3
|
-
|
|
3
|
+
import { type TemplateRuntime } from '../libraries/templates/renderTemplate.js';
|
|
4
|
+
export declare const assignPrompt: (action: ActionMenuItems, { type, actionParams, collection, context, field, layout, locale, pluginConfig, systemPrompt, templateRuntime, template, }: {
|
|
4
5
|
actionParams: Record<any, any>;
|
|
5
6
|
collection: CollectionSlug;
|
|
6
7
|
context: object;
|
|
@@ -9,6 +10,7 @@ export declare const assignPrompt: (action: ActionMenuItems, { type, actionParam
|
|
|
9
10
|
locale: string;
|
|
10
11
|
pluginConfig: PluginConfig;
|
|
11
12
|
systemPrompt: string;
|
|
13
|
+
templateRuntime?: TemplateRuntime;
|
|
12
14
|
template: string;
|
|
13
15
|
type: string;
|
|
14
16
|
}) => Promise<{
|
|
@@ -16,4 +18,4 @@ export declare const assignPrompt: (action: ActionMenuItems, { type, actionParam
|
|
|
16
18
|
prompt: string;
|
|
17
19
|
system: string | undefined;
|
|
18
20
|
}>;
|
|
19
|
-
export declare const extendContextWithPromptFields: (data: object, ctx: PromptFieldGetterContext, pluginConfig: PluginConfig) => object;
|
|
21
|
+
export declare const extendContextWithPromptFields: (data: object, ctx: PromptFieldGetterContext, pluginConfig: PluginConfig, templateRuntime?: TemplateRuntime) => object;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { defaultPrompts } from '../ai/utilities/prompts.js';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { replacePlaceholders } from '../libraries/handlebars/replacePlaceholders.js';
|
|
2
|
+
import { templateHelpersMap } from '../libraries/templates/helpersMap.js';
|
|
3
|
+
import { renderTemplate } from '../libraries/templates/renderTemplate.js';
|
|
5
4
|
import { lexicalToPromptTemplate } from '../utilities/lexical/lexicalToPromptTemplate.js';
|
|
6
5
|
const buildRichTextSystem = (baseSystem, layout)=>{
|
|
7
6
|
return `${baseSystem}
|
|
@@ -23,13 +22,13 @@ ADDITIONAL GUIDELINES:
|
|
|
23
22
|
- Use clear and concise language appropriate for the target audience.
|
|
24
23
|
`;
|
|
25
24
|
};
|
|
26
|
-
export const assignPrompt = async (action, { type, actionParams, collection, context, field, layout, locale, pluginConfig, systemPrompt = '', template })=>{
|
|
25
|
+
export const assignPrompt = async (action, { type, actionParams, collection, context, field, layout, locale, pluginConfig, systemPrompt = '', templateRuntime, template })=>{
|
|
27
26
|
const extendedContext = extendContextWithPromptFields(context, {
|
|
28
27
|
type,
|
|
29
28
|
collection
|
|
30
|
-
}, pluginConfig);
|
|
31
|
-
const prompt = await
|
|
32
|
-
const toLexicalHTML = type === 'richText' ?
|
|
29
|
+
}, pluginConfig, templateRuntime);
|
|
30
|
+
const prompt = await renderTemplate(template, extendedContext, templateRuntime);
|
|
31
|
+
const toLexicalHTML = type === 'richText' ? templateHelpersMap.toHTML.name : '';
|
|
33
32
|
const assignedPrompts = {
|
|
34
33
|
layout: type === 'richText' ? layout : undefined,
|
|
35
34
|
prompt,
|
|
@@ -68,11 +67,11 @@ export const assignPrompt = async (action, { type, actionParams, collection, con
|
|
|
68
67
|
return {
|
|
69
68
|
layout: type === 'richText' ? updatedLayout : undefined,
|
|
70
69
|
// TODO: revisit this toLexicalHTML
|
|
71
|
-
prompt: await
|
|
70
|
+
prompt: await renderTemplate(`{{${toLexicalHTML} ${field}}}`, extendedContext, templateRuntime),
|
|
72
71
|
system: type === 'richText' ? buildRichTextSystem(system, updatedLayout) : system
|
|
73
72
|
};
|
|
74
73
|
};
|
|
75
|
-
export const extendContextWithPromptFields = (data, ctx, pluginConfig)=>{
|
|
74
|
+
export const extendContextWithPromptFields = (data, ctx, pluginConfig, templateRuntime)=>{
|
|
76
75
|
const { promptFields = [] } = pluginConfig;
|
|
77
76
|
const fieldsMap = new Map((promptFields || []).filter((f)=>!f.collections || f.collections.includes(ctx.collection)).map((f)=>[
|
|
78
77
|
f.name,
|
|
@@ -83,21 +82,20 @@ export const extendContextWithPromptFields = (data, ctx, pluginConfig)=>{
|
|
|
83
82
|
const field = fieldsMap.get(prop);
|
|
84
83
|
if (field && 'getter' in field && typeof field.getter === 'function') {
|
|
85
84
|
const value = field.getter(data, ctx);
|
|
86
|
-
return Promise.resolve(value)
|
|
85
|
+
return Promise.resolve(value);
|
|
87
86
|
}
|
|
88
|
-
// {{prop}} escapes content by default. Here we make sure it won't be escaped.
|
|
89
87
|
const value = typeof target === 'object' ? target[prop] : undefined;
|
|
90
88
|
// If the value is a Lexical JSON object (e.g. a PromptField / richText field),
|
|
91
89
|
// convert it to a template string AND resolve any internal mention variables
|
|
92
90
|
// (like {{name}}) against the raw document data to avoid unresolved placeholders.
|
|
93
91
|
if (value && typeof value === 'object' && value.root && Array.isArray(value.root.children)) {
|
|
94
92
|
const template = lexicalToPromptTemplate(value);
|
|
95
|
-
// Resolve inner variables using the raw target (not the Proxy) to avoid recursion
|
|
96
|
-
return
|
|
93
|
+
// Resolve inner variables using the raw target (not the Proxy) to avoid recursion.
|
|
94
|
+
return renderTemplate(template, target, templateRuntime);
|
|
97
95
|
}
|
|
98
|
-
return
|
|
96
|
+
return value;
|
|
99
97
|
},
|
|
100
|
-
//
|
|
98
|
+
// Keep virtual promptFields enumerable so template rendering can resolve dynamic fields.
|
|
101
99
|
getOwnPropertyDescriptor: (target, prop)=>{
|
|
102
100
|
const field = fieldsMap.get(prop);
|
|
103
101
|
if (field) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utilities/buildPromptUtils.ts"],"sourcesContent":["import type { CollectionSlug } from 'payload'\n\nimport type { ActionMenuItems, PluginConfig, PromptFieldGetterContext } from '../types.js'\n\nimport { defaultPrompts } from '../ai/utilities/prompts.js'\nimport {
|
|
1
|
+
{"version":3,"sources":["../../src/utilities/buildPromptUtils.ts"],"sourcesContent":["import type { CollectionSlug } from 'payload'\n\nimport type { ActionMenuItems, PluginConfig, PromptFieldGetterContext } from '../types.js'\n\nimport { defaultPrompts } from '../ai/utilities/prompts.js'\nimport { templateHelpersMap } from '../libraries/templates/helpersMap.js'\nimport { renderTemplate, type TemplateRuntime } from '../libraries/templates/renderTemplate.js'\nimport { lexicalToPromptTemplate } from '../utilities/lexical/lexicalToPromptTemplate.js'\n\nconst buildRichTextSystem = (baseSystem: string, layout: string) => {\n return `${baseSystem}\n\nRULES:\n- Generate original and unique content based on the given topic.\n- Strictly adhere to the specified layout and formatting instructions.\n- Utilize the provided rich text editor tools for appropriate formatting.\n- Ensure the output follows the structure of the sample output object.\n- Produce valid JSON with no undefined or null values.\n---\nLAYOUT INSTRUCTIONS:\n${layout}\n\n---\nADDITIONAL GUIDELINES:\n- Ensure coherence and logical flow between all sections.\n- Maintain a consistent tone and style throughout the content.\n- Use clear and concise language appropriate for the target audience.\n`\n}\n\nexport const assignPrompt = async (\n action: ActionMenuItems,\n {\n type,\n actionParams,\n collection,\n context,\n field,\n layout,\n locale,\n pluginConfig,\n systemPrompt = '',\n templateRuntime,\n template,\n }: {\n actionParams: Record<any, any>\n collection: CollectionSlug\n context: object\n field: string\n layout: string\n locale: string\n pluginConfig: PluginConfig\n systemPrompt: string\n templateRuntime?: TemplateRuntime\n template: string\n type: string\n },\n) => {\n const extendedContext = extendContextWithPromptFields(\n context,\n { type, collection },\n pluginConfig,\n templateRuntime,\n )\n const prompt = await renderTemplate(template, extendedContext, templateRuntime)\n const toLexicalHTML = type === 'richText' ? templateHelpersMap.toHTML.name : ''\n\n const assignedPrompts = {\n layout: type === 'richText' ? layout : undefined,\n prompt,\n //TODO: Define only once on a collection level\n system: type === 'richText' ? buildRichTextSystem(systemPrompt, layout) : undefined,\n }\n\n if (action === 'Compose') {\n if (locale && locale !== 'en') {\n /**\n * NOTE: Avoid using the \"system prompt\" for setting the output language,\n * as it causes quotation marks to appear in the output (Currently only tested with openai models).\n * Appending the language instruction directly to the prompt resolves this issue. - revalidate\n **/\n assignedPrompts.prompt += `\n --- \n OUTPUT LANGUAGE: ${locale}\n `\n }\n\n return assignedPrompts\n }\n\n const prompts = [...(pluginConfig.prompts || []), ...defaultPrompts]\n const foundPrompt = prompts.find((p) => p.name === action)\n const getLayout = foundPrompt?.layout\n const getSystemPrompt = foundPrompt?.system\n\n let updatedLayout = layout\n if (getLayout) {\n updatedLayout = getLayout()\n }\n\n const system = getSystemPrompt\n ? getSystemPrompt({\n ...(actionParams || {}),\n prompt,\n systemPrompt,\n })\n : ''\n\n return {\n layout: type === 'richText' ? updatedLayout : undefined,\n // TODO: revisit this toLexicalHTML\n prompt: await renderTemplate(\n `{{${toLexicalHTML} ${field}}}`,\n extendedContext,\n templateRuntime,\n ),\n system: type === 'richText' ? buildRichTextSystem(system, updatedLayout) : system,\n }\n}\n\nexport const extendContextWithPromptFields = (\n data: object,\n ctx: PromptFieldGetterContext,\n pluginConfig: PluginConfig,\n templateRuntime?: TemplateRuntime,\n) => {\n const { promptFields = [] } = pluginConfig\n const fieldsMap = new Map(\n (promptFields || [])\n .filter((f: any) => !f.collections || f.collections.includes(ctx.collection))\n .map((f: any) => [f.name, f]),\n )\n return new Proxy(data, {\n get: (target, prop: string) => {\n const field = fieldsMap.get(prop)\n if (field && 'getter' in field && typeof field.getter === 'function') {\n const value = field.getter(data, ctx)\n return Promise.resolve(value)\n }\n\n const value = typeof target === 'object' ? (target as any)[prop] : undefined\n\n // If the value is a Lexical JSON object (e.g. a PromptField / richText field),\n // convert it to a template string AND resolve any internal mention variables\n // (like {{name}}) against the raw document data to avoid unresolved placeholders.\n if (value && typeof value === 'object' && value.root && Array.isArray(value.root.children)) {\n const template = lexicalToPromptTemplate(value)\n // Resolve inner variables using the raw target (not the Proxy) to avoid recursion.\n return renderTemplate(template, target, templateRuntime)\n }\n\n return value\n },\n // Keep virtual promptFields enumerable so template rendering can resolve dynamic fields.\n getOwnPropertyDescriptor: (target, prop) => {\n const field = fieldsMap.get(prop as string)\n if (field) {\n return {\n configurable: true,\n enumerable: true,\n }\n }\n return Object.getOwnPropertyDescriptor(target, prop)\n },\n has: (target, prop) => {\n return fieldsMap.has(prop as string) || (target && prop in target)\n },\n ownKeys: (target) => {\n return [...Array.from(fieldsMap.keys()), ...Object.keys(target || {})]\n },\n })\n}\n"],"names":["defaultPrompts","templateHelpersMap","renderTemplate","lexicalToPromptTemplate","buildRichTextSystem","baseSystem","layout","assignPrompt","action","type","actionParams","collection","context","field","locale","pluginConfig","systemPrompt","templateRuntime","template","extendedContext","extendContextWithPromptFields","prompt","toLexicalHTML","toHTML","name","assignedPrompts","undefined","system","prompts","foundPrompt","find","p","getLayout","getSystemPrompt","updatedLayout","data","ctx","promptFields","fieldsMap","Map","filter","f","collections","includes","map","Proxy","get","target","prop","getter","value","Promise","resolve","root","Array","isArray","children","getOwnPropertyDescriptor","configurable","enumerable","Object","has","ownKeys","from","keys"],"mappings":"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"}
|
|
@@ -10,10 +10,8 @@ export const lexicalToPromptTemplate = (editorState)=>{
|
|
|
10
10
|
const mentionNode = node;
|
|
11
11
|
// Handle # (Field) -> {{value}}
|
|
12
12
|
if (mentionNode.trigger === '#') {
|
|
13
|
-
// If the value contains spaces or special chars, it might need brackets,
|
|
14
|
-
// but usually for Handlebars variable replacement we expect {{field_name}}
|
|
15
13
|
// The value from our endpoint is the full path e.g. "array.0.field"
|
|
16
|
-
//
|
|
14
|
+
// and is consumed by the server-side template renderer.
|
|
17
15
|
return `{{${mentionNode.value}}}`;
|
|
18
16
|
}
|
|
19
17
|
// Handle @ (Image) -> @value
|
|
@@ -32,18 +30,10 @@ export const lexicalToPromptTemplate = (editorState)=>{
|
|
|
32
30
|
return '';
|
|
33
31
|
};
|
|
34
32
|
const rawTemplate = (editorState.root.children || []).map(traverse).join('').trim();
|
|
35
|
-
// Post-process the template to fix nested
|
|
33
|
+
// Post-process the template to fix nested template syntax
|
|
36
34
|
// Example 1: `{{toHTML {{content}}}}` -> `{{toHTML content}}`
|
|
37
35
|
// Example 2: `{{#each {{array}}}}` -> `{{#each array}}`
|
|
38
|
-
//
|
|
39
|
-
// Actually, a simpler regex: replace `{{(` with `{{` is wrong.
|
|
40
|
-
// We want to replace `{{... {{value}} ...}}` with `{{... value ...}}`
|
|
41
|
-
// Regex to find `{{` inside another `{{` `}}` block is tricky.
|
|
42
|
-
// Instead, let's just globally replace `{{` and `}}` that are immediately inside another tag.
|
|
43
|
-
// Let's use a simpler heuristic: if a mention trigger `#` was used, it returned `{{value}}`.
|
|
44
|
-
// If the user typed `{{toHTML `, then inserted `#content`, then typed `}}`, the raw string is `{{toHTML {{content}}}}`.
|
|
45
|
-
// We can just replace `{{` and `}}` if they are surrounded by an outer bracket context.
|
|
46
|
-
// For safety, let's just fix the specific pattern of nested brackets for mentions.
|
|
36
|
+
// This is intentionally a narrow transform to normalize mention insertions.
|
|
47
37
|
return rawTemplate.replace(/(\{\{[^{}]*)\{\{([^}]+)\}\}(.*?\}\})/g, '$1$2$3');
|
|
48
38
|
};
|
|
49
39
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/utilities/lexical/lexicalToPromptTemplate.ts"],"sourcesContent":["import type { SerializedEditorState, SerializedLexicalNode } from 'lexical'\n\ninterface BeautifulMentionNode extends SerializedLexicalNode {\n data?: Record<string, unknown>\n trigger: string\n value: string\n}\n\nexport const lexicalToPromptTemplate = (editorState: any | SerializedEditorState): string => {\n if (!editorState || !editorState.root) {\n return ''\n }\n\n const traverse = (node: SerializedLexicalNode): string => {\n if (node.type === 'text') {\n return (node as any).text || ''\n }\n\n if (node.type === 'beautifulMention') {\n const mentionNode = node as BeautifulMentionNode\n // Handle # (Field) -> {{value}}\n if (mentionNode.trigger === '#') {\n //
|
|
1
|
+
{"version":3,"sources":["../../../src/utilities/lexical/lexicalToPromptTemplate.ts"],"sourcesContent":["import type { SerializedEditorState, SerializedLexicalNode } from 'lexical'\n\ninterface BeautifulMentionNode extends SerializedLexicalNode {\n data?: Record<string, unknown>\n trigger: string\n value: string\n}\n\nexport const lexicalToPromptTemplate = (editorState: any | SerializedEditorState): string => {\n if (!editorState || !editorState.root) {\n return ''\n }\n\n const traverse = (node: SerializedLexicalNode): string => {\n if (node.type === 'text') {\n return (node as any).text || ''\n }\n\n if (node.type === 'beautifulMention') {\n const mentionNode = node as BeautifulMentionNode\n // Handle # (Field) -> {{value}}\n if (mentionNode.trigger === '#') {\n // The value from our endpoint is the full path e.g. \"array.0.field\"\n // and is consumed by the server-side template renderer.\n return `{{${mentionNode.value}}}`\n }\n // Handle @ (Image) -> @value\n if (mentionNode.trigger === '@') {\n return `@${mentionNode.value}`\n }\n return mentionNode.value\n }\n\n if (node.type === 'paragraph') {\n const childrenContent = (node as any).children?.map(traverse).join('') || ''\n return childrenContent + '\\n'\n }\n\n if ((node as any).children) {\n return (node as any).children.map(traverse).join('')\n }\n\n return ''\n }\n\n const rawTemplate = (editorState.root.children || []).map(traverse).join('').trim()\n \n // Post-process the template to fix nested template syntax\n // Example 1: `{{toHTML {{content}}}}` -> `{{toHTML content}}`\n // Example 2: `{{#each {{array}}}}` -> `{{#each array}}`\n // This is intentionally a narrow transform to normalize mention insertions.\n return rawTemplate.replace(/(\\{\\{[^{}]*)\\{\\{([^}]+)\\}\\}(.*?\\}\\})/g, '$1$2$3')\n}\n"],"names":["lexicalToPromptTemplate","editorState","root","traverse","node","type","text","mentionNode","trigger","value","childrenContent","children","map","join","rawTemplate","trim","replace"],"mappings":"AAQA,OAAO,MAAMA,0BAA0B,CAACC;IACtC,IAAI,CAACA,eAAe,CAACA,YAAYC,IAAI,EAAE;QACrC,OAAO;IACT;IAEA,MAAMC,WAAW,CAACC;QAChB,IAAIA,KAAKC,IAAI,KAAK,QAAQ;YACxB,OAAO,AAACD,KAAaE,IAAI,IAAI;QAC/B;QAEA,IAAIF,KAAKC,IAAI,KAAK,oBAAoB;YACpC,MAAME,cAAcH;YACpB,gCAAgC;YAChC,IAAIG,YAAYC,OAAO,KAAK,KAAK;gBAC7B,oEAAoE;gBACpE,wDAAwD;gBACxD,OAAO,CAAC,EAAE,EAAED,YAAYE,KAAK,CAAC,EAAE,CAAC;YACrC;YACA,6BAA6B;YAC7B,IAAIF,YAAYC,OAAO,KAAK,KAAK;gBAC7B,OAAO,CAAC,CAAC,EAAED,YAAYE,KAAK,CAAC,CAAC;YAClC;YACA,OAAOF,YAAYE,KAAK;QAC1B;QAEA,IAAIL,KAAKC,IAAI,KAAK,aAAa;YAC7B,MAAMK,kBAAkB,AAACN,KAAaO,QAAQ,EAAEC,IAAIT,UAAUU,KAAK,OAAO;YAC1E,OAAOH,kBAAkB;QAC3B;QAEA,IAAI,AAACN,KAAaO,QAAQ,EAAE;YAC1B,OAAO,AAACP,KAAaO,QAAQ,CAACC,GAAG,CAACT,UAAUU,IAAI,CAAC;QACnD;QAEA,OAAO;IACT;IAEA,MAAMC,cAAc,AAACb,CAAAA,YAAYC,IAAI,CAACS,QAAQ,IAAI,EAAE,AAAD,EAAGC,GAAG,CAACT,UAAUU,IAAI,CAAC,IAAIE,IAAI;IAEjF,0DAA0D;IAC1D,8DAA8D;IAC9D,wDAAwD;IACxD,4EAA4E;IAC5E,OAAOD,YAAYE,OAAO,CAAC,yCAAyC;AACtE,EAAC"}
|
|
@@ -37,11 +37,26 @@ const getHeaderOrigin = (req)=>{
|
|
|
37
37
|
}
|
|
38
38
|
return `https://${host}`;
|
|
39
39
|
};
|
|
40
|
+
const getRequestOrigin = (req)=>{
|
|
41
|
+
const requestURL = req.url?.trim();
|
|
42
|
+
if (!requestURL) {
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
try {
|
|
46
|
+
const parsed = new URL(requestURL);
|
|
47
|
+
if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
return parsed.origin;
|
|
51
|
+
} catch {
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
};
|
|
40
55
|
export function resolveServerURL(req) {
|
|
41
56
|
const candidates = [
|
|
42
57
|
req.payload.config?.serverURL || undefined,
|
|
43
58
|
...PLUGIN_SERVER_URL_ENV_KEYS.map((key)=>getProcessEnvValue(key)),
|
|
44
|
-
req
|
|
59
|
+
getRequestOrigin(req),
|
|
45
60
|
getHeaderOrigin(req)
|
|
46
61
|
];
|
|
47
62
|
for (const candidate of candidates){
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/utilities/runtime/resolveServerURL.ts"],"sourcesContent":["import type { PayloadRequest } from 'payload'\n\nimport { PLUGIN_SERVER_URL_ENV_KEYS } from '../../defaults.js'\n\nconst normalizeServerURL = (value: string | undefined): string | undefined => {\n if (!value) {\n return undefined\n }\n\n const trimmed = value.trim()\n if (!trimmed) {\n return undefined\n }\n\n try {\n const parsed = new URL(trimmed)\n if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {\n return undefined\n }\n\n const normalizedPath = parsed.pathname === '/' ? '' : parsed.pathname.replace(/\\/+$/, '')\n return `${parsed.origin}${normalizedPath}`\n } catch {\n return undefined\n }\n}\n\nconst getProcessEnvValue = (key: string): string | undefined => {\n if (typeof process === 'undefined' || !process.env) {\n return undefined\n }\n\n const value = process.env[key]\n return typeof value === 'string' && value.length > 0 ? value : undefined\n}\n\nconst getHeaderOrigin = (req: PayloadRequest): string | undefined => {\n const forwardedProto = req.headers.get('x-forwarded-proto')?.split(',')[0]?.trim()\n const forwardedHost = req.headers.get('x-forwarded-host')?.split(',')[0]?.trim()\n\n if (forwardedProto && forwardedHost) {\n return `${forwardedProto}://${forwardedHost}`\n }\n\n const host = req.headers.get('host')?.trim()\n if (!host) {\n return undefined\n }\n\n return `https://${host}`\n}\n\nexport function resolveServerURL(req: PayloadRequest): string | undefined {\n const candidates: Array<string | undefined> = [\n req.payload.config?.serverURL || undefined,\n ...PLUGIN_SERVER_URL_ENV_KEYS.map((key) => getProcessEnvValue(key)),\n req
|
|
1
|
+
{"version":3,"sources":["../../../src/utilities/runtime/resolveServerURL.ts"],"sourcesContent":["import type { PayloadRequest } from 'payload'\n\nimport { PLUGIN_SERVER_URL_ENV_KEYS } from '../../defaults.js'\n\nconst normalizeServerURL = (value: string | undefined): string | undefined => {\n if (!value) {\n return undefined\n }\n\n const trimmed = value.trim()\n if (!trimmed) {\n return undefined\n }\n\n try {\n const parsed = new URL(trimmed)\n if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {\n return undefined\n }\n\n const normalizedPath = parsed.pathname === '/' ? '' : parsed.pathname.replace(/\\/+$/, '')\n return `${parsed.origin}${normalizedPath}`\n } catch {\n return undefined\n }\n}\n\nconst getProcessEnvValue = (key: string): string | undefined => {\n if (typeof process === 'undefined' || !process.env) {\n return undefined\n }\n\n const value = process.env[key]\n return typeof value === 'string' && value.length > 0 ? value : undefined\n}\n\nconst getHeaderOrigin = (req: PayloadRequest): string | undefined => {\n const forwardedProto = req.headers.get('x-forwarded-proto')?.split(',')[0]?.trim()\n const forwardedHost = req.headers.get('x-forwarded-host')?.split(',')[0]?.trim()\n\n if (forwardedProto && forwardedHost) {\n return `${forwardedProto}://${forwardedHost}`\n }\n\n const host = req.headers.get('host')?.trim()\n if (!host) {\n return undefined\n }\n\n return `https://${host}`\n}\n\nconst getRequestOrigin = (req: PayloadRequest): string | undefined => {\n const requestURL = req.url?.trim()\n if (!requestURL) {\n return undefined\n }\n\n try {\n const parsed = new URL(requestURL)\n if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {\n return undefined\n }\n\n return parsed.origin\n } catch {\n return undefined\n }\n}\n\nexport function resolveServerURL(req: PayloadRequest): string | undefined {\n const candidates: Array<string | undefined> = [\n req.payload.config?.serverURL || undefined,\n ...PLUGIN_SERVER_URL_ENV_KEYS.map((key) => getProcessEnvValue(key)),\n getRequestOrigin(req),\n getHeaderOrigin(req),\n ]\n\n for (const candidate of candidates) {\n const normalized = normalizeServerURL(candidate)\n if (normalized) {\n return normalized\n }\n }\n\n return undefined\n}\n\nexport function resolveAbsoluteURL(input: string, req: PayloadRequest): string {\n if (/^https?:\\/\\//i.test(input)) {\n return input\n }\n\n const baseURL = resolveServerURL(req)\n if (!baseURL) {\n throw new Error(\n 'Could not resolve a server URL for relative asset path. Set `payload.config.serverURL` or SERVER_URL.',\n )\n }\n\n const normalizedPath = input.startsWith('/') ? input : `/${input}`\n return `${baseURL}${normalizedPath}`\n}\n"],"names":["PLUGIN_SERVER_URL_ENV_KEYS","normalizeServerURL","value","undefined","trimmed","trim","parsed","URL","protocol","normalizedPath","pathname","replace","origin","getProcessEnvValue","key","process","env","length","getHeaderOrigin","req","forwardedProto","headers","get","split","forwardedHost","host","getRequestOrigin","requestURL","url","resolveServerURL","candidates","payload","config","serverURL","map","candidate","normalized","resolveAbsoluteURL","input","test","baseURL","Error","startsWith"],"mappings":"AAEA,SAASA,0BAA0B,QAAQ,oBAAmB;AAE9D,MAAMC,qBAAqB,CAACC;IAC1B,IAAI,CAACA,OAAO;QACV,OAAOC;IACT;IAEA,MAAMC,UAAUF,MAAMG,IAAI;IAC1B,IAAI,CAACD,SAAS;QACZ,OAAOD;IACT;IAEA,IAAI;QACF,MAAMG,SAAS,IAAIC,IAAIH;QACvB,IAAIE,OAAOE,QAAQ,KAAK,WAAWF,OAAOE,QAAQ,KAAK,UAAU;YAC/D,OAAOL;QACT;QAEA,MAAMM,iBAAiBH,OAAOI,QAAQ,KAAK,MAAM,KAAKJ,OAAOI,QAAQ,CAACC,OAAO,CAAC,QAAQ;QACtF,OAAO,CAAC,EAAEL,OAAOM,MAAM,CAAC,EAAEH,eAAe,CAAC;IAC5C,EAAE,OAAM;QACN,OAAON;IACT;AACF;AAEA,MAAMU,qBAAqB,CAACC;IAC1B,IAAI,OAAOC,YAAY,eAAe,CAACA,QAAQC,GAAG,EAAE;QAClD,OAAOb;IACT;IAEA,MAAMD,QAAQa,QAAQC,GAAG,CAACF,IAAI;IAC9B,OAAO,OAAOZ,UAAU,YAAYA,MAAMe,MAAM,GAAG,IAAIf,QAAQC;AACjE;AAEA,MAAMe,kBAAkB,CAACC;IACvB,MAAMC,iBAAiBD,IAAIE,OAAO,CAACC,GAAG,CAAC,sBAAsBC,MAAM,IAAI,CAAC,EAAE,EAAElB;IAC5E,MAAMmB,gBAAgBL,IAAIE,OAAO,CAACC,GAAG,CAAC,qBAAqBC,MAAM,IAAI,CAAC,EAAE,EAAElB;IAE1E,IAAIe,kBAAkBI,eAAe;QACnC,OAAO,CAAC,EAAEJ,eAAe,GAAG,EAAEI,cAAc,CAAC;IAC/C;IAEA,MAAMC,OAAON,IAAIE,OAAO,CAACC,GAAG,CAAC,SAASjB;IACtC,IAAI,CAACoB,MAAM;QACT,OAAOtB;IACT;IAEA,OAAO,CAAC,QAAQ,EAAEsB,KAAK,CAAC;AAC1B;AAEA,MAAMC,mBAAmB,CAACP;IACxB,MAAMQ,aAAaR,IAAIS,GAAG,EAAEvB;IAC5B,IAAI,CAACsB,YAAY;QACf,OAAOxB;IACT;IAEA,IAAI;QACF,MAAMG,SAAS,IAAIC,IAAIoB;QACvB,IAAIrB,OAAOE,QAAQ,KAAK,WAAWF,OAAOE,QAAQ,KAAK,UAAU;YAC/D,OAAOL;QACT;QAEA,OAAOG,OAAOM,MAAM;IACtB,EAAE,OAAM;QACN,OAAOT;IACT;AACF;AAEA,OAAO,SAAS0B,iBAAiBV,GAAmB;IAClD,MAAMW,aAAwC;QAC5CX,IAAIY,OAAO,CAACC,MAAM,EAAEC,aAAa9B;WAC9BH,2BAA2BkC,GAAG,CAAC,CAACpB,MAAQD,mBAAmBC;QAC9DY,iBAAiBP;QACjBD,gBAAgBC;KACjB;IAED,KAAK,MAAMgB,aAAaL,WAAY;QAClC,MAAMM,aAAanC,mBAAmBkC;QACtC,IAAIC,YAAY;YACd,OAAOA;QACT;IACF;IAEA,OAAOjC;AACT;AAEA,OAAO,SAASkC,mBAAmBC,KAAa,EAAEnB,GAAmB;IACnE,IAAI,gBAAgBoB,IAAI,CAACD,QAAQ;QAC/B,OAAOA;IACT;IAEA,MAAME,UAAUX,iBAAiBV;IACjC,IAAI,CAACqB,SAAS;QACZ,MAAM,IAAIC,MACR;IAEJ;IAEA,MAAMhC,iBAAiB6B,MAAMI,UAAU,CAAC,OAAOJ,QAAQ,CAAC,CAAC,EAAEA,MAAM,CAAC;IAClE,OAAO,CAAC,EAAEE,QAAQ,EAAE/B,eAAe,CAAC;AACtC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-stack/payloadcms",
|
|
3
|
-
"version": "3.76.0-beta.
|
|
3
|
+
"version": "3.76.0-beta.8",
|
|
4
4
|
"private": false,
|
|
5
5
|
"bugs": "https://github.com/ashbuilds/payload-ai/issues",
|
|
6
6
|
"repository": "https://github.com/ashbuilds/payload-ai",
|
|
@@ -230,8 +230,7 @@
|
|
|
230
230
|
"ajv": "^8.17.1",
|
|
231
231
|
"elevenlabs": "^0.8.2",
|
|
232
232
|
"get-input-selection": "^1.1.4",
|
|
233
|
-
"
|
|
234
|
-
"handlebars-async-helpers": "^1.0.6",
|
|
233
|
+
"liquidjs": "^10.24.0",
|
|
235
234
|
"lexical-beautiful-mentions": "^0.1.48",
|
|
236
235
|
"locale-codes": "^1.3.1",
|
|
237
236
|
"lodash.isequal": "^4.5.0",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const asyncHandlebars: any;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/libraries/handlebars/asyncHandlebars.ts"],"sourcesContent":["import Handlebars from 'handlebars/dist/handlebars.js'\nimport asyncHelpers from 'handlebars-async-helpers'\n\nexport const asyncHandlebars = asyncHelpers(Handlebars)\n"],"names":["Handlebars","asyncHelpers","asyncHandlebars"],"mappings":"AAAA,OAAOA,gBAAgB,gCAA+B;AACtD,OAAOC,kBAAkB,2BAA0B;AAEnD,OAAO,MAAMC,kBAAkBD,aAAaD,YAAW"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const registerEditorHelper: (payload: any, schemaPath: string) => void;
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { getFieldInfo } from '../../utilities/fields/getFieldInfo.js';
|
|
2
|
-
import { lexicalToHTML } from '../../utilities/lexical/lexicalToHTML.js';
|
|
3
|
-
import { asyncHandlebars } from './asyncHandlebars.js';
|
|
4
|
-
import { handlebarsHelpersMap } from './helpersMap.js';
|
|
5
|
-
export const registerEditorHelper = (payload, schemaPath)=>{
|
|
6
|
-
//TODO: add autocomplete ability using handlebars template on PromptEditorField and include custom helpers in dropdown
|
|
7
|
-
let fieldInfo = getFieldInfo(payload.collections, schemaPath);
|
|
8
|
-
const schemaPathChunks = schemaPath.split('.');
|
|
9
|
-
asyncHandlebars.registerHelper(handlebarsHelpersMap.toHTML.name, async function(content, options) {
|
|
10
|
-
const collectionSlug = schemaPathChunks[0];
|
|
11
|
-
const { ids } = options;
|
|
12
|
-
for (const id of ids){
|
|
13
|
-
//TODO: Find a better way to get schemaPath of defined field in prompt editor
|
|
14
|
-
const path = `${collectionSlug}.${id}`;
|
|
15
|
-
fieldInfo = getFieldInfo(payload.collections, path);
|
|
16
|
-
}
|
|
17
|
-
let html = '';
|
|
18
|
-
if (fieldInfo && 'editor' in fieldInfo && fieldInfo.editor && typeof fieldInfo.editor === 'object' && 'editorConfig' in fieldInfo.editor && fieldInfo.editor.editorConfig) {
|
|
19
|
-
if (fieldInfo.editor.editorConfig && typeof fieldInfo.editor.editorConfig === 'object' && 'features' in fieldInfo.editor.editorConfig && 'lexical' in fieldInfo.editor.editorConfig && 'resolvedFeatureMap' in fieldInfo.editor.editorConfig) {
|
|
20
|
-
html = await lexicalToHTML(content, fieldInfo.editor.editorConfig);
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
return new asyncHandlebars.SafeString(html);
|
|
24
|
-
});
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
//# sourceMappingURL=helpers.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/libraries/handlebars/helpers.ts"],"sourcesContent":["import type { SerializedEditorState } from 'lexical'\n\nimport { getFieldInfo } from '../../utilities/fields/getFieldInfo.js'\nimport { lexicalToHTML } from '../../utilities/lexical/lexicalToHTML.js'\nimport { asyncHandlebars } from './asyncHandlebars.js'\nimport { handlebarsHelpersMap } from './helpersMap.js'\n\nexport const registerEditorHelper = (payload: any, schemaPath: string) => {\n //TODO: add autocomplete ability using handlebars template on PromptEditorField and include custom helpers in dropdown\n\n let fieldInfo = getFieldInfo(payload.collections, schemaPath)\n const schemaPathChunks = schemaPath.split('.')\n\n asyncHandlebars.registerHelper(\n handlebarsHelpersMap.toHTML.name,\n async function (content: SerializedEditorState, options: any) {\n const collectionSlug = schemaPathChunks[0]\n const { ids } = options\n for (const id of ids) {\n //TODO: Find a better way to get schemaPath of defined field in prompt editor\n const path = `${collectionSlug}.${id}`\n fieldInfo = getFieldInfo(payload.collections, path)\n }\n\n let html = ''\n if (\n fieldInfo &&\n 'editor' in fieldInfo &&\n fieldInfo.editor &&\n typeof fieldInfo.editor === 'object' &&\n 'editorConfig' in fieldInfo.editor &&\n fieldInfo.editor.editorConfig\n ) {\n if (\n fieldInfo.editor.editorConfig &&\n typeof fieldInfo.editor.editorConfig === 'object' &&\n 'features' in fieldInfo.editor.editorConfig &&\n 'lexical' in fieldInfo.editor.editorConfig &&\n 'resolvedFeatureMap' in fieldInfo.editor.editorConfig\n ) {\n html = await lexicalToHTML(\n content,\n fieldInfo.editor.editorConfig as any, // as SanitizedServerEditorConfig\n )\n }\n }\n return new asyncHandlebars.SafeString(html)\n },\n )\n}\n"],"names":["getFieldInfo","lexicalToHTML","asyncHandlebars","handlebarsHelpersMap","registerEditorHelper","payload","schemaPath","fieldInfo","collections","schemaPathChunks","split","registerHelper","toHTML","name","content","options","collectionSlug","ids","id","path","html","editor","editorConfig","SafeString"],"mappings":"AAEA,SAASA,YAAY,QAAQ,yCAAwC;AACrE,SAASC,aAAa,QAAQ,2CAA0C;AACxE,SAASC,eAAe,QAAQ,uBAAsB;AACtD,SAASC,oBAAoB,QAAQ,kBAAiB;AAEtD,OAAO,MAAMC,uBAAuB,CAACC,SAAcC;IACjD,sHAAsH;IAEtH,IAAIC,YAAYP,aAAaK,QAAQG,WAAW,EAAEF;IAClD,MAAMG,mBAAmBH,WAAWI,KAAK,CAAC;IAE1CR,gBAAgBS,cAAc,CAC5BR,qBAAqBS,MAAM,CAACC,IAAI,EAChC,eAAgBC,OAA8B,EAAEC,OAAY;QAC1D,MAAMC,iBAAiBP,gBAAgB,CAAC,EAAE;QAC1C,MAAM,EAAEQ,GAAG,EAAE,GAAGF;QAChB,KAAK,MAAMG,MAAMD,IAAK;YACpB,6EAA6E;YAC7E,MAAME,OAAO,CAAC,EAAEH,eAAe,CAAC,EAAEE,GAAG,CAAC;YACtCX,YAAYP,aAAaK,QAAQG,WAAW,EAAEW;QAChD;QAEA,IAAIC,OAAO;QACX,IACEb,aACA,YAAYA,aACZA,UAAUc,MAAM,IAChB,OAAOd,UAAUc,MAAM,KAAK,YAC5B,kBAAkBd,UAAUc,MAAM,IAClCd,UAAUc,MAAM,CAACC,YAAY,EAC7B;YACA,IACEf,UAAUc,MAAM,CAACC,YAAY,IAC7B,OAAOf,UAAUc,MAAM,CAACC,YAAY,KAAK,YACzC,cAAcf,UAAUc,MAAM,CAACC,YAAY,IAC3C,aAAaf,UAAUc,MAAM,CAACC,YAAY,IAC1C,wBAAwBf,UAAUc,MAAM,CAACC,YAAY,EACrD;gBACAF,OAAO,MAAMnB,cACXa,SACAP,UAAUc,MAAM,CAACC,YAAY;YAEjC;QACF;QACA,OAAO,IAAIpB,gBAAgBqB,UAAU,CAACH;IACxC;AAEJ,EAAC"}
|