@ainsleydev/payload-helper 0.0.40 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +227 -0
- package/dist/cli/bin.js +20 -0
- package/dist/cli/bin.js.map +1 -1
- package/dist/cli/commands/preview-emails.d.ts +5 -0
- package/dist/cli/commands/preview-emails.js +123 -0
- package/dist/cli/commands/preview-emails.js.map +1 -0
- package/dist/collections/index.d.ts +3 -0
- package/dist/collections/index.js +4 -0
- package/dist/collections/index.js.map +1 -0
- package/dist/common/index.d.ts +1 -0
- package/dist/common/index.js +3 -0
- package/dist/common/index.js.map +1 -0
- package/dist/email/ForgotPasswordEmail.d.ts +38 -0
- package/dist/email/ForgotPasswordEmail.js +61 -0
- package/dist/email/ForgotPasswordEmail.js.map +1 -0
- package/dist/email/ForgotPasswordEmail.test.d.ts +1 -0
- package/dist/email/ForgotPasswordEmail.test.js +202 -0
- package/dist/email/ForgotPasswordEmail.test.js.map +1 -0
- package/dist/email/VerifyAccountEmail.d.ts +38 -0
- package/dist/email/VerifyAccountEmail.js +61 -0
- package/dist/email/VerifyAccountEmail.js.map +1 -0
- package/dist/email/VerifyAccountEmail.test.d.ts +1 -0
- package/dist/email/VerifyAccountEmail.test.js +212 -0
- package/dist/email/VerifyAccountEmail.test.js.map +1 -0
- package/dist/endpoints/index.d.ts +1 -0
- package/dist/endpoints/index.js +3 -0
- package/dist/endpoints/index.js.map +1 -0
- package/dist/globals/index.d.ts +5 -0
- package/dist/globals/index.js +6 -0
- package/dist/globals/index.js.map +1 -0
- package/dist/index.d.ts +15 -10
- package/dist/index.js +14 -66
- package/dist/index.js.map +1 -1
- package/dist/plugin/email.d.ts +10 -0
- package/dist/plugin/email.js +98 -0
- package/dist/plugin/email.js.map +1 -0
- package/dist/plugin/email.test.js +265 -0
- package/dist/plugin/email.test.js.map +1 -0
- package/dist/plugin.d.ts +9 -0
- package/dist/plugin.js +78 -0
- package/dist/plugin.js.map +1 -0
- package/dist/types.d.ts +134 -0
- package/dist/types.js +3 -1
- package/dist/types.js.map +1 -1
- package/dist/util/index.d.ts +4 -0
- package/dist/util/index.js +6 -0
- package/dist/util/index.js.map +1 -0
- package/package.json +47 -14
- package/dist/plugin/icon.d.ts +0 -6
- package/dist/plugin/icon.js +0 -26
- package/dist/plugin/icon.js.map +0 -1
- package/dist/plugin/logo.d.ts +0 -6
- package/dist/plugin/logo.js +0 -26
- package/dist/plugin/logo.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,68 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
if (pluginOptions.admin?.logo) {
|
|
16
|
-
config = injectAdminLogo(config, pluginOptions.admin.logo, pluginOptions.siteName);
|
|
17
|
-
}
|
|
18
|
-
// Inject admin Icon component if icon config is provided
|
|
19
|
-
if (pluginOptions.admin?.icon) {
|
|
20
|
-
config = injectAdminIcon(config, pluginOptions.admin.icon, pluginOptions.siteName);
|
|
21
|
-
}
|
|
22
|
-
// Map collections & add hooks
|
|
23
|
-
config.collections = (config.collections || []).map((collection)=>{
|
|
24
|
-
if (collection.upload !== undefined && collection.upload !== true) {
|
|
25
|
-
return collection;
|
|
26
|
-
}
|
|
27
|
-
const hooks = collection.hooks || {};
|
|
28
|
-
// Add afterChange hook only if webServer is defined
|
|
29
|
-
if (pluginOptions.webServer) {
|
|
30
|
-
hooks.afterChange = [
|
|
31
|
-
...hooks.afterChange || [],
|
|
32
|
-
cacheHookCollections({
|
|
33
|
-
server: pluginOptions.webServer,
|
|
34
|
-
slug: collection.slug,
|
|
35
|
-
fields: collection.fields,
|
|
36
|
-
isCollection: true
|
|
37
|
-
})
|
|
38
|
-
];
|
|
39
|
-
}
|
|
40
|
-
return {
|
|
41
|
-
...collection,
|
|
42
|
-
hooks
|
|
43
|
-
};
|
|
44
|
-
});
|
|
45
|
-
// Map globals & add hooks
|
|
46
|
-
config.globals = (config.globals || []).map((global)=>{
|
|
47
|
-
const hooks = global.hooks || {};
|
|
48
|
-
// Add afterChange hook only if webServer is defined
|
|
49
|
-
if (pluginOptions.webServer) {
|
|
50
|
-
hooks.afterChange = [
|
|
51
|
-
...hooks.afterChange || [],
|
|
52
|
-
cacheHookGlobals({
|
|
53
|
-
server: pluginOptions.webServer,
|
|
54
|
-
slug: global.slug,
|
|
55
|
-
fields: global.fields,
|
|
56
|
-
isCollection: true
|
|
57
|
-
})
|
|
58
|
-
];
|
|
59
|
-
}
|
|
60
|
-
return {
|
|
61
|
-
...global,
|
|
62
|
-
hooks
|
|
63
|
-
};
|
|
64
|
-
});
|
|
65
|
-
return config;
|
|
66
|
-
};
|
|
1
|
+
// Main plugin
|
|
2
|
+
export { payloadHelper } from './plugin.js';
|
|
3
|
+
export { Media, imageSizes, imageSizesWithAvif, Redirects } from './collections/index.js';
|
|
4
|
+
export { Settings, Navigation, countries, languages } from './globals/index.js';
|
|
5
|
+
// Email Components
|
|
6
|
+
export { ForgotPasswordEmail } from './email/ForgotPasswordEmail.js';
|
|
7
|
+
export { VerifyAccountEmail } from './email/VerifyAccountEmail.js';
|
|
8
|
+
// Utilities
|
|
9
|
+
export { env, fieldHasName, validateURL, validatePostcode, htmlToLexical, lexicalToHtml } from './util/index.js';
|
|
10
|
+
// Common/Reusable
|
|
11
|
+
export { SEOFields } from './common/index.js';
|
|
12
|
+
// Endpoints
|
|
13
|
+
export { findBySlug } from './endpoints/index.js';
|
|
14
|
+
export { fieldMapper, schemas, addGoJSONSchema } from './plugin/schema.js';
|
|
67
15
|
|
|
68
16
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["// Main plugin\nexport { payloadHelper } from './plugin.js';\n\n// Types\nexport type {\n\tPayloadHelperPluginConfig,\n\tAdminConfig,\n\tAdminIconConfig,\n\tAdminLogoConfig,\n\tEmailConfig,\n\tEmailContentOverrides,\n\tSettingsConfig,\n\tWebServerConfig,\n} from './types.js';\n\n// Collections\nexport type { MediaArgs } from './collections/Media.js';\nexport { Media, imageSizes, imageSizesWithAvif, Redirects } from './collections/index.js';\n\n// Globals\nexport type { SettingsArgs } from './globals/Settings.js';\nexport { Settings, Navigation, countries, languages } from './globals/index.js';\n\n// Email Components\nexport { ForgotPasswordEmail } from './email/ForgotPasswordEmail.js';\nexport type { ForgotPasswordEmailProps } from './email/ForgotPasswordEmail.js';\nexport { VerifyAccountEmail } from './email/VerifyAccountEmail.js';\nexport type { VerifyAccountEmailProps } from './email/VerifyAccountEmail.js';\n\n// Admin Components\nexport type { IconProps } from './admin/components/Icon.js';\nexport type { LogoProps } from './admin/components/Logo.js';\n\n// Utilities\nexport {\n\tenv,\n\tfieldHasName,\n\tvalidateURL,\n\tvalidatePostcode,\n\thtmlToLexical,\n\tlexicalToHtml,\n} from './util/index.js';\n\n// Common/Reusable\nexport { SEOFields } from './common/index.js';\n\n// Endpoints\nexport { findBySlug } from './endpoints/index.js';\n\n// Schema utilities\nexport type { SchemaOptions } from './plugin/schema.js';\nexport { fieldMapper, schemas, addGoJSONSchema } from './plugin/schema.js';\n"],"names":["payloadHelper","Media","imageSizes","imageSizesWithAvif","Redirects","Settings","Navigation","countries","languages","ForgotPasswordEmail","VerifyAccountEmail","env","fieldHasName","validateURL","validatePostcode","htmlToLexical","lexicalToHtml","SEOFields","findBySlug","fieldMapper","schemas","addGoJSONSchema"],"mappings":"AAAA,cAAc;AACd,SAASA,aAAa,QAAQ,cAAc;AAgB5C,SAASC,KAAK,EAAEC,UAAU,EAAEC,kBAAkB,EAAEC,SAAS,QAAQ,yBAAyB;AAI1F,SAASC,QAAQ,EAAEC,UAAU,EAAEC,SAAS,EAAEC,SAAS,QAAQ,qBAAqB;AAEhF,mBAAmB;AACnB,SAASC,mBAAmB,QAAQ,iCAAiC;AAErE,SAASC,kBAAkB,QAAQ,gCAAgC;AAOnE,YAAY;AACZ,SACCC,GAAG,EACHC,YAAY,EACZC,WAAW,EACXC,gBAAgB,EAChBC,aAAa,EACbC,aAAa,QACP,kBAAkB;AAEzB,kBAAkB;AAClB,SAASC,SAAS,QAAQ,oBAAoB;AAE9C,YAAY;AACZ,SAASC,UAAU,QAAQ,uBAAuB;AAIlD,SAASC,WAAW,EAAEC,OAAO,EAAEC,eAAe,QAAQ,qBAAqB"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Config } from 'payload';
|
|
2
|
+
import type { EmailConfig } from '../types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Injects email templates into all auth-enabled collections in the Payload config.
|
|
5
|
+
*
|
|
6
|
+
* @param config - The Payload configuration object
|
|
7
|
+
* @param emailConfig - The email configuration from plugin options
|
|
8
|
+
* @returns The modified Payload configuration with email templates injected
|
|
9
|
+
*/
|
|
10
|
+
export declare const injectEmailTemplates: (config: Config, emailConfig: EmailConfig) => Config;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { renderEmail } from '@ainsleydev/email-templates';
|
|
2
|
+
import { ForgotPasswordEmail } from '../email/ForgotPasswordEmail.js';
|
|
3
|
+
import { VerifyAccountEmail } from '../email/VerifyAccountEmail.js';
|
|
4
|
+
/**
|
|
5
|
+
* Injects email templates into all auth-enabled collections in the Payload config.
|
|
6
|
+
*
|
|
7
|
+
* @param config - The Payload configuration object
|
|
8
|
+
* @param emailConfig - The email configuration from plugin options
|
|
9
|
+
* @returns The modified Payload configuration with email templates injected
|
|
10
|
+
*/ export const injectEmailTemplates = (config, emailConfig)=>{
|
|
11
|
+
// Get the website URL for branding, defaulting to Payload's serverUrl
|
|
12
|
+
const websiteUrl = emailConfig.frontEndUrl || config.serverURL || '';
|
|
13
|
+
// Merge user theme with websiteUrl for branding
|
|
14
|
+
const themeOverride = {
|
|
15
|
+
...emailConfig.theme,
|
|
16
|
+
branding: {
|
|
17
|
+
...emailConfig.theme?.branding,
|
|
18
|
+
websiteUrl
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
// Find all collections with auth enabled
|
|
22
|
+
const collectionsWithAuth = config.collections?.filter((collection)=>collection.auth) || [];
|
|
23
|
+
// If no collections with auth, return config unchanged
|
|
24
|
+
if (collectionsWithAuth.length === 0) {
|
|
25
|
+
return config;
|
|
26
|
+
}
|
|
27
|
+
// Inject email templates into each auth-enabled collection
|
|
28
|
+
const updatedCollections = config.collections?.map((collection)=>{
|
|
29
|
+
// Skip collections without auth
|
|
30
|
+
if (!collection.auth) {
|
|
31
|
+
return collection;
|
|
32
|
+
}
|
|
33
|
+
// Clone the collection to avoid mutation
|
|
34
|
+
const updatedCollection = {
|
|
35
|
+
...collection
|
|
36
|
+
};
|
|
37
|
+
// Ensure auth is an object (it could be true or an object)
|
|
38
|
+
if (typeof updatedCollection.auth === 'boolean') {
|
|
39
|
+
updatedCollection.auth = {};
|
|
40
|
+
} else {
|
|
41
|
+
updatedCollection.auth = {
|
|
42
|
+
...updatedCollection.auth
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
// Inject forgotPassword email template
|
|
46
|
+
const currentForgotPassword = updatedCollection.auth.forgotPassword;
|
|
47
|
+
updatedCollection.auth.forgotPassword = {
|
|
48
|
+
...typeof currentForgotPassword === 'object' ? currentForgotPassword : {},
|
|
49
|
+
generateEmailHTML: async (args)=>{
|
|
50
|
+
const token = args?.token || '';
|
|
51
|
+
const user = args?.user || {};
|
|
52
|
+
const resetUrl = `${config.serverURL}/admin/reset/${token}`;
|
|
53
|
+
return renderEmail({
|
|
54
|
+
component: ForgotPasswordEmail,
|
|
55
|
+
props: {
|
|
56
|
+
user: {
|
|
57
|
+
firstName: user?.firstName,
|
|
58
|
+
email: user?.email
|
|
59
|
+
},
|
|
60
|
+
resetUrl,
|
|
61
|
+
content: emailConfig.forgotPassword
|
|
62
|
+
},
|
|
63
|
+
theme: themeOverride
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
// Inject verify email template
|
|
68
|
+
const currentVerify = updatedCollection.auth.verify;
|
|
69
|
+
updatedCollection.auth.verify = {
|
|
70
|
+
...typeof currentVerify === 'object' ? currentVerify : {},
|
|
71
|
+
generateEmailHTML: async (args)=>{
|
|
72
|
+
const token = args?.token || '';
|
|
73
|
+
const user = args?.user || {};
|
|
74
|
+
// For verify emails, the token is used in the verification URL
|
|
75
|
+
const verifyUrl = `${config.serverURL}/admin/${collection.slug}/verify/${token}`;
|
|
76
|
+
return renderEmail({
|
|
77
|
+
component: VerifyAccountEmail,
|
|
78
|
+
props: {
|
|
79
|
+
user: {
|
|
80
|
+
firstName: user?.firstName,
|
|
81
|
+
email: user?.email
|
|
82
|
+
},
|
|
83
|
+
verifyUrl,
|
|
84
|
+
content: emailConfig.verifyAccount
|
|
85
|
+
},
|
|
86
|
+
theme: themeOverride
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
return updatedCollection;
|
|
91
|
+
});
|
|
92
|
+
return {
|
|
93
|
+
...config,
|
|
94
|
+
collections: updatedCollections
|
|
95
|
+
};
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
//# sourceMappingURL=email.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/plugin/email.ts"],"sourcesContent":["import { renderEmail } from '@ainsleydev/email-templates';\nimport type { Config } from 'payload';\n\nimport { ForgotPasswordEmail } from '../email/ForgotPasswordEmail.js';\nimport { VerifyAccountEmail } from '../email/VerifyAccountEmail.js';\nimport type { EmailConfig } from '../types.js';\n\n/**\n * Injects email templates into all auth-enabled collections in the Payload config.\n *\n * @param config - The Payload configuration object\n * @param emailConfig - The email configuration from plugin options\n * @returns The modified Payload configuration with email templates injected\n */\nexport const injectEmailTemplates = (config: Config, emailConfig: EmailConfig): Config => {\n\t// Get the website URL for branding, defaulting to Payload's serverUrl\n\tconst websiteUrl = emailConfig.frontEndUrl || config.serverURL || '';\n\n\t// Merge user theme with websiteUrl for branding\n\tconst themeOverride = {\n\t\t...emailConfig.theme,\n\t\tbranding: {\n\t\t\t...emailConfig.theme?.branding,\n\t\t\twebsiteUrl,\n\t\t},\n\t};\n\n\t// Find all collections with auth enabled\n\tconst collectionsWithAuth = config.collections?.filter((collection) => collection.auth) || [];\n\n\t// If no collections with auth, return config unchanged\n\tif (collectionsWithAuth.length === 0) {\n\t\treturn config;\n\t}\n\n\t// Inject email templates into each auth-enabled collection\n\tconst updatedCollections = config.collections?.map((collection) => {\n\t\t// Skip collections without auth\n\t\tif (!collection.auth) {\n\t\t\treturn collection;\n\t\t}\n\n\t\t// Clone the collection to avoid mutation\n\t\tconst updatedCollection = { ...collection };\n\n\t\t// Ensure auth is an object (it could be true or an object)\n\t\tif (typeof updatedCollection.auth === 'boolean') {\n\t\t\tupdatedCollection.auth = {};\n\t\t} else {\n\t\t\tupdatedCollection.auth = { ...updatedCollection.auth };\n\t\t}\n\n\t\t// Inject forgotPassword email template\n\t\tconst currentForgotPassword = updatedCollection.auth.forgotPassword;\n\t\tupdatedCollection.auth.forgotPassword = {\n\t\t\t...(typeof currentForgotPassword === 'object' ? currentForgotPassword : {}),\n\t\t\tgenerateEmailHTML: async (args) => {\n\t\t\t\tconst token = args?.token || '';\n\t\t\t\tconst user = args?.user || {};\n\t\t\t\tconst resetUrl = `${config.serverURL}/admin/reset/${token}`;\n\n\t\t\t\treturn renderEmail({\n\t\t\t\t\tcomponent: ForgotPasswordEmail,\n\t\t\t\t\tprops: {\n\t\t\t\t\t\tuser: {\n\t\t\t\t\t\t\tfirstName: user?.firstName,\n\t\t\t\t\t\t\temail: user?.email,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tresetUrl,\n\t\t\t\t\t\tcontent: emailConfig.forgotPassword,\n\t\t\t\t\t},\n\t\t\t\t\ttheme: themeOverride,\n\t\t\t\t});\n\t\t\t},\n\t\t};\n\n\t\t// Inject verify email template\n\t\tconst currentVerify = updatedCollection.auth.verify;\n\t\tupdatedCollection.auth.verify = {\n\t\t\t...(typeof currentVerify === 'object' ? currentVerify : {}),\n\t\t\tgenerateEmailHTML: async (args) => {\n\t\t\t\tconst token = args?.token || '';\n\t\t\t\tconst user = args?.user || {};\n\t\t\t\t// For verify emails, the token is used in the verification URL\n\t\t\t\tconst verifyUrl = `${config.serverURL}/admin/${collection.slug}/verify/${token}`;\n\n\t\t\t\treturn renderEmail({\n\t\t\t\t\tcomponent: VerifyAccountEmail,\n\t\t\t\t\tprops: {\n\t\t\t\t\t\tuser: {\n\t\t\t\t\t\t\tfirstName: user?.firstName,\n\t\t\t\t\t\t\temail: user?.email,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tverifyUrl,\n\t\t\t\t\t\tcontent: emailConfig.verifyAccount,\n\t\t\t\t\t},\n\t\t\t\t\ttheme: themeOverride,\n\t\t\t\t});\n\t\t\t},\n\t\t};\n\n\t\treturn updatedCollection;\n\t});\n\n\treturn {\n\t\t...config,\n\t\tcollections: updatedCollections,\n\t};\n};\n"],"names":["renderEmail","ForgotPasswordEmail","VerifyAccountEmail","injectEmailTemplates","config","emailConfig","websiteUrl","frontEndUrl","serverURL","themeOverride","theme","branding","collectionsWithAuth","collections","filter","collection","auth","length","updatedCollections","map","updatedCollection","currentForgotPassword","forgotPassword","generateEmailHTML","args","token","user","resetUrl","component","props","firstName","email","content","currentVerify","verify","verifyUrl","slug","verifyAccount"],"mappings":"AAAA,SAASA,WAAW,QAAQ,8BAA8B;AAG1D,SAASC,mBAAmB,QAAQ,kCAAkC;AACtE,SAASC,kBAAkB,QAAQ,iCAAiC;AAGpE;;;;;;CAMC,GACD,OAAO,MAAMC,uBAAuB,CAACC,QAAgBC;IACpD,sEAAsE;IACtE,MAAMC,aAAaD,YAAYE,WAAW,IAAIH,OAAOI,SAAS,IAAI;IAElE,gDAAgD;IAChD,MAAMC,gBAAgB;QACrB,GAAGJ,YAAYK,KAAK;QACpBC,UAAU;YACT,GAAGN,YAAYK,KAAK,EAAEC,QAAQ;YAC9BL;QACD;IACD;IAEA,yCAAyC;IACzC,MAAMM,sBAAsBR,OAAOS,WAAW,EAAEC,OAAO,CAACC,aAAeA,WAAWC,IAAI,KAAK,EAAE;IAE7F,uDAAuD;IACvD,IAAIJ,oBAAoBK,MAAM,KAAK,GAAG;QACrC,OAAOb;IACR;IAEA,2DAA2D;IAC3D,MAAMc,qBAAqBd,OAAOS,WAAW,EAAEM,IAAI,CAACJ;QACnD,gCAAgC;QAChC,IAAI,CAACA,WAAWC,IAAI,EAAE;YACrB,OAAOD;QACR;QAEA,yCAAyC;QACzC,MAAMK,oBAAoB;YAAE,GAAGL,UAAU;QAAC;QAE1C,2DAA2D;QAC3D,IAAI,OAAOK,kBAAkBJ,IAAI,KAAK,WAAW;YAChDI,kBAAkBJ,IAAI,GAAG,CAAC;QAC3B,OAAO;YACNI,kBAAkBJ,IAAI,GAAG;gBAAE,GAAGI,kBAAkBJ,IAAI;YAAC;QACtD;QAEA,uCAAuC;QACvC,MAAMK,wBAAwBD,kBAAkBJ,IAAI,CAACM,cAAc;QACnEF,kBAAkBJ,IAAI,CAACM,cAAc,GAAG;YACvC,GAAI,OAAOD,0BAA0B,WAAWA,wBAAwB,CAAC,CAAC;YAC1EE,mBAAmB,OAAOC;gBACzB,MAAMC,QAAQD,MAAMC,SAAS;gBAC7B,MAAMC,OAAOF,MAAME,QAAQ,CAAC;gBAC5B,MAAMC,WAAW,GAAGvB,OAAOI,SAAS,CAAC,aAAa,EAAEiB,OAAO;gBAE3D,OAAOzB,YAAY;oBAClB4B,WAAW3B;oBACX4B,OAAO;wBACNH,MAAM;4BACLI,WAAWJ,MAAMI;4BACjBC,OAAOL,MAAMK;wBACd;wBACAJ;wBACAK,SAAS3B,YAAYiB,cAAc;oBACpC;oBACAZ,OAAOD;gBACR;YACD;QACD;QAEA,+BAA+B;QAC/B,MAAMwB,gBAAgBb,kBAAkBJ,IAAI,CAACkB,MAAM;QACnDd,kBAAkBJ,IAAI,CAACkB,MAAM,GAAG;YAC/B,GAAI,OAAOD,kBAAkB,WAAWA,gBAAgB,CAAC,CAAC;YAC1DV,mBAAmB,OAAOC;gBACzB,MAAMC,QAAQD,MAAMC,SAAS;gBAC7B,MAAMC,OAAOF,MAAME,QAAQ,CAAC;gBAC5B,+DAA+D;gBAC/D,MAAMS,YAAY,GAAG/B,OAAOI,SAAS,CAAC,OAAO,EAAEO,WAAWqB,IAAI,CAAC,QAAQ,EAAEX,OAAO;gBAEhF,OAAOzB,YAAY;oBAClB4B,WAAW1B;oBACX2B,OAAO;wBACNH,MAAM;4BACLI,WAAWJ,MAAMI;4BACjBC,OAAOL,MAAMK;wBACd;wBACAI;wBACAH,SAAS3B,YAAYgC,aAAa;oBACnC;oBACA3B,OAAOD;gBACR;YACD;QACD;QAEA,OAAOW;IACR;IAEA,OAAO;QACN,GAAGhB,MAAM;QACTS,aAAaK;IACd;AACD,EAAE"}
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
import { describe, expect, test, vi } from 'vitest';
|
|
2
|
+
import { injectEmailTemplates } from './email.js';
|
|
3
|
+
// Mock the email templates module
|
|
4
|
+
vi.mock('@ainsleydev/email-templates', ()=>({
|
|
5
|
+
renderEmail: vi.fn(async ()=>'<html>Mocked Email</html>')
|
|
6
|
+
}));
|
|
7
|
+
describe('injectEmailTemplates', ()=>{
|
|
8
|
+
const mockEmailConfig = {
|
|
9
|
+
frontEndUrl: 'https://example.com',
|
|
10
|
+
theme: {
|
|
11
|
+
branding: {
|
|
12
|
+
companyName: 'Test Company'
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
forgotPassword: {
|
|
16
|
+
heading: 'Reset Your Password',
|
|
17
|
+
bodyText: 'Click below to reset your password'
|
|
18
|
+
},
|
|
19
|
+
verifyAccount: {
|
|
20
|
+
heading: 'Verify Your Account',
|
|
21
|
+
bodyText: 'Click below to verify your account'
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
test('should inject email templates into auth-enabled collections', ()=>{
|
|
25
|
+
const config = {
|
|
26
|
+
collections: [
|
|
27
|
+
{
|
|
28
|
+
slug: 'users',
|
|
29
|
+
fields: [],
|
|
30
|
+
auth: true
|
|
31
|
+
}
|
|
32
|
+
],
|
|
33
|
+
serverURL: 'https://api.example.com'
|
|
34
|
+
};
|
|
35
|
+
const result = injectEmailTemplates(config, mockEmailConfig);
|
|
36
|
+
const usersCollection = result.collections?.[0];
|
|
37
|
+
expect(usersCollection).toBeDefined();
|
|
38
|
+
expect(typeof usersCollection?.auth).toBe('object');
|
|
39
|
+
if (typeof usersCollection?.auth === 'object') {
|
|
40
|
+
expect(usersCollection.auth.forgotPassword).toBeDefined();
|
|
41
|
+
expect(usersCollection.auth.verify).toBeDefined();
|
|
42
|
+
expect(usersCollection.auth.forgotPassword?.generateEmailHTML).toBeDefined();
|
|
43
|
+
expect(usersCollection.auth.verify?.generateEmailHTML).toBeDefined();
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
test('should not inject email templates into non-auth collections', ()=>{
|
|
47
|
+
const config = {
|
|
48
|
+
collections: [
|
|
49
|
+
{
|
|
50
|
+
slug: 'posts',
|
|
51
|
+
fields: []
|
|
52
|
+
}
|
|
53
|
+
],
|
|
54
|
+
serverURL: 'https://api.example.com'
|
|
55
|
+
};
|
|
56
|
+
const result = injectEmailTemplates(config, mockEmailConfig);
|
|
57
|
+
const postsCollection = result.collections?.[0];
|
|
58
|
+
expect(postsCollection).toBeDefined();
|
|
59
|
+
expect(postsCollection?.auth).toBeUndefined();
|
|
60
|
+
});
|
|
61
|
+
test('should return config unchanged when no auth-enabled collections exist', ()=>{
|
|
62
|
+
const config = {
|
|
63
|
+
collections: [
|
|
64
|
+
{
|
|
65
|
+
slug: 'posts',
|
|
66
|
+
fields: []
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
slug: 'media',
|
|
70
|
+
fields: []
|
|
71
|
+
}
|
|
72
|
+
],
|
|
73
|
+
serverURL: 'https://api.example.com'
|
|
74
|
+
};
|
|
75
|
+
const result = injectEmailTemplates(config, mockEmailConfig);
|
|
76
|
+
expect(result.collections).toEqual(config.collections);
|
|
77
|
+
});
|
|
78
|
+
test('should handle auth as boolean and convert to object', ()=>{
|
|
79
|
+
const config = {
|
|
80
|
+
collections: [
|
|
81
|
+
{
|
|
82
|
+
slug: 'users',
|
|
83
|
+
fields: [],
|
|
84
|
+
auth: true
|
|
85
|
+
}
|
|
86
|
+
],
|
|
87
|
+
serverURL: 'https://api.example.com'
|
|
88
|
+
};
|
|
89
|
+
const result = injectEmailTemplates(config, mockEmailConfig);
|
|
90
|
+
const usersCollection = result.collections?.[0];
|
|
91
|
+
expect(typeof usersCollection?.auth).toBe('object');
|
|
92
|
+
});
|
|
93
|
+
test('should preserve existing auth configuration', ()=>{
|
|
94
|
+
const config = {
|
|
95
|
+
collections: [
|
|
96
|
+
{
|
|
97
|
+
slug: 'users',
|
|
98
|
+
fields: [],
|
|
99
|
+
auth: {
|
|
100
|
+
tokenExpiration: 7200,
|
|
101
|
+
maxLoginAttempts: 5
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
],
|
|
105
|
+
serverURL: 'https://api.example.com'
|
|
106
|
+
};
|
|
107
|
+
const result = injectEmailTemplates(config, mockEmailConfig);
|
|
108
|
+
const usersCollection = result.collections?.[0];
|
|
109
|
+
if (typeof usersCollection?.auth === 'object') {
|
|
110
|
+
expect(usersCollection.auth.tokenExpiration).toBe(7200);
|
|
111
|
+
expect(usersCollection.auth.maxLoginAttempts).toBe(5);
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
test('should merge theme with websiteUrl from frontEndUrl', ()=>{
|
|
115
|
+
const config = {
|
|
116
|
+
collections: [
|
|
117
|
+
{
|
|
118
|
+
slug: 'users',
|
|
119
|
+
fields: [],
|
|
120
|
+
auth: true
|
|
121
|
+
}
|
|
122
|
+
],
|
|
123
|
+
serverURL: 'https://api.example.com'
|
|
124
|
+
};
|
|
125
|
+
const emailConfig = {
|
|
126
|
+
frontEndUrl: 'https://custom-frontend.com',
|
|
127
|
+
theme: {
|
|
128
|
+
branding: {
|
|
129
|
+
companyName: 'Test Company'
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
const result = injectEmailTemplates(config, emailConfig);
|
|
134
|
+
// The theme should have websiteUrl merged into branding
|
|
135
|
+
// This is tested indirectly through the generateEmailHTML function
|
|
136
|
+
const usersCollection = result.collections?.[0];
|
|
137
|
+
expect(usersCollection).toBeDefined();
|
|
138
|
+
});
|
|
139
|
+
test('should use serverURL when frontEndUrl is not provided', ()=>{
|
|
140
|
+
const config = {
|
|
141
|
+
collections: [
|
|
142
|
+
{
|
|
143
|
+
slug: 'users',
|
|
144
|
+
fields: [],
|
|
145
|
+
auth: true
|
|
146
|
+
}
|
|
147
|
+
],
|
|
148
|
+
serverURL: 'https://api.example.com'
|
|
149
|
+
};
|
|
150
|
+
const emailConfig = {
|
|
151
|
+
// No frontEndUrl provided
|
|
152
|
+
theme: {
|
|
153
|
+
branding: {
|
|
154
|
+
companyName: 'Test Company'
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
const result = injectEmailTemplates(config, emailConfig);
|
|
159
|
+
const usersCollection = result.collections?.[0];
|
|
160
|
+
expect(usersCollection).toBeDefined();
|
|
161
|
+
// The websiteUrl should default to serverURL
|
|
162
|
+
});
|
|
163
|
+
test('should handle multiple auth-enabled collections', ()=>{
|
|
164
|
+
const config = {
|
|
165
|
+
collections: [
|
|
166
|
+
{
|
|
167
|
+
slug: 'users',
|
|
168
|
+
fields: [],
|
|
169
|
+
auth: true
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
slug: 'admins',
|
|
173
|
+
fields: [],
|
|
174
|
+
auth: {
|
|
175
|
+
tokenExpiration: 3600
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
slug: 'posts',
|
|
180
|
+
fields: []
|
|
181
|
+
}
|
|
182
|
+
],
|
|
183
|
+
serverURL: 'https://api.example.com'
|
|
184
|
+
};
|
|
185
|
+
const result = injectEmailTemplates(config, mockEmailConfig);
|
|
186
|
+
expect(result.collections).toHaveLength(3);
|
|
187
|
+
const usersCollection = result.collections?.[0];
|
|
188
|
+
const adminsCollection = result.collections?.[1];
|
|
189
|
+
const postsCollection = result.collections?.[2];
|
|
190
|
+
// Users should have email templates
|
|
191
|
+
if (typeof usersCollection?.auth === 'object') {
|
|
192
|
+
expect(usersCollection.auth.forgotPassword?.generateEmailHTML).toBeDefined();
|
|
193
|
+
expect(usersCollection.auth.verify?.generateEmailHTML).toBeDefined();
|
|
194
|
+
}
|
|
195
|
+
// Admins should have email templates and preserve existing config
|
|
196
|
+
if (typeof adminsCollection?.auth === 'object') {
|
|
197
|
+
expect(adminsCollection.auth.forgotPassword?.generateEmailHTML).toBeDefined();
|
|
198
|
+
expect(adminsCollection.auth.verify?.generateEmailHTML).toBeDefined();
|
|
199
|
+
expect(adminsCollection.auth.tokenExpiration).toBe(3600);
|
|
200
|
+
}
|
|
201
|
+
// Posts should remain unchanged
|
|
202
|
+
expect(postsCollection?.auth).toBeUndefined();
|
|
203
|
+
});
|
|
204
|
+
test('should handle empty collections array', ()=>{
|
|
205
|
+
const config = {
|
|
206
|
+
collections: [],
|
|
207
|
+
serverURL: 'https://api.example.com'
|
|
208
|
+
};
|
|
209
|
+
const result = injectEmailTemplates(config, mockEmailConfig);
|
|
210
|
+
expect(result.collections).toEqual([]);
|
|
211
|
+
});
|
|
212
|
+
test('should handle config without collections property', ()=>{
|
|
213
|
+
const config = {
|
|
214
|
+
serverURL: 'https://api.example.com'
|
|
215
|
+
};
|
|
216
|
+
const result = injectEmailTemplates(config, mockEmailConfig);
|
|
217
|
+
expect(result.collections).toBeUndefined();
|
|
218
|
+
});
|
|
219
|
+
test('should preserve existing forgotPassword configuration', ()=>{
|
|
220
|
+
const config = {
|
|
221
|
+
collections: [
|
|
222
|
+
{
|
|
223
|
+
slug: 'users',
|
|
224
|
+
fields: [],
|
|
225
|
+
auth: {
|
|
226
|
+
forgotPassword: {
|
|
227
|
+
generateEmailSubject: ()=>'Custom Subject'
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
],
|
|
232
|
+
serverURL: 'https://api.example.com'
|
|
233
|
+
};
|
|
234
|
+
const result = injectEmailTemplates(config, mockEmailConfig);
|
|
235
|
+
const usersCollection = result.collections?.[0];
|
|
236
|
+
if (typeof usersCollection?.auth === 'object') {
|
|
237
|
+
expect(usersCollection.auth.forgotPassword?.generateEmailSubject).toBeDefined();
|
|
238
|
+
expect(usersCollection.auth.forgotPassword?.generateEmailHTML).toBeDefined();
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
test('should preserve existing verify configuration', ()=>{
|
|
242
|
+
const config = {
|
|
243
|
+
collections: [
|
|
244
|
+
{
|
|
245
|
+
slug: 'users',
|
|
246
|
+
fields: [],
|
|
247
|
+
auth: {
|
|
248
|
+
verify: {
|
|
249
|
+
generateEmailSubject: ()=>'Custom Verify Subject'
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
],
|
|
254
|
+
serverURL: 'https://api.example.com'
|
|
255
|
+
};
|
|
256
|
+
const result = injectEmailTemplates(config, mockEmailConfig);
|
|
257
|
+
const usersCollection = result.collections?.[0];
|
|
258
|
+
if (typeof usersCollection?.auth === 'object') {
|
|
259
|
+
expect(usersCollection.auth.verify?.generateEmailSubject).toBeDefined();
|
|
260
|
+
expect(usersCollection.auth.verify?.generateEmailHTML).toBeDefined();
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
//# sourceMappingURL=email.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/plugin/email.test.ts"],"sourcesContent":["import type { CollectionConfig, Config } from 'payload';\nimport { describe, expect, test, vi } from 'vitest';\nimport type { EmailConfig } from '../types.js';\nimport { injectEmailTemplates } from './email.js';\n\n// Mock the email templates module\nvi.mock('@ainsleydev/email-templates', () => ({\n\trenderEmail: vi.fn(async () => '<html>Mocked Email</html>'),\n}));\n\ndescribe('injectEmailTemplates', () => {\n\tconst mockEmailConfig: EmailConfig = {\n\t\tfrontEndUrl: 'https://example.com',\n\t\ttheme: {\n\t\t\tbranding: {\n\t\t\t\tcompanyName: 'Test Company',\n\t\t\t},\n\t\t},\n\t\tforgotPassword: {\n\t\t\theading: 'Reset Your Password',\n\t\t\tbodyText: 'Click below to reset your password',\n\t\t},\n\t\tverifyAccount: {\n\t\t\theading: 'Verify Your Account',\n\t\t\tbodyText: 'Click below to verify your account',\n\t\t},\n\t};\n\n\ttest('should inject email templates into auth-enabled collections', () => {\n\t\tconst config: Config = {\n\t\t\tcollections: [\n\t\t\t\t{\n\t\t\t\t\tslug: 'users',\n\t\t\t\t\tfields: [],\n\t\t\t\t\tauth: true,\n\t\t\t\t},\n\t\t\t] as CollectionConfig[],\n\t\t\tserverURL: 'https://api.example.com',\n\t\t};\n\n\t\tconst result = injectEmailTemplates(config, mockEmailConfig);\n\n\t\tconst usersCollection = result.collections?.[0];\n\t\texpect(usersCollection).toBeDefined();\n\t\texpect(typeof usersCollection?.auth).toBe('object');\n\n\t\tif (typeof usersCollection?.auth === 'object') {\n\t\t\texpect(usersCollection.auth.forgotPassword).toBeDefined();\n\t\t\texpect(usersCollection.auth.verify).toBeDefined();\n\t\t\texpect(usersCollection.auth.forgotPassword?.generateEmailHTML).toBeDefined();\n\t\t\texpect(usersCollection.auth.verify?.generateEmailHTML).toBeDefined();\n\t\t}\n\t});\n\n\ttest('should not inject email templates into non-auth collections', () => {\n\t\tconst config: Config = {\n\t\t\tcollections: [\n\t\t\t\t{\n\t\t\t\t\tslug: 'posts',\n\t\t\t\t\tfields: [],\n\t\t\t\t\t// No auth property\n\t\t\t\t},\n\t\t\t] as CollectionConfig[],\n\t\t\tserverURL: 'https://api.example.com',\n\t\t};\n\n\t\tconst result = injectEmailTemplates(config, mockEmailConfig);\n\n\t\tconst postsCollection = result.collections?.[0];\n\t\texpect(postsCollection).toBeDefined();\n\t\texpect(postsCollection?.auth).toBeUndefined();\n\t});\n\n\ttest('should return config unchanged when no auth-enabled collections exist', () => {\n\t\tconst config: Config = {\n\t\t\tcollections: [\n\t\t\t\t{\n\t\t\t\t\tslug: 'posts',\n\t\t\t\t\tfields: [],\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tslug: 'media',\n\t\t\t\t\tfields: [],\n\t\t\t\t},\n\t\t\t] as CollectionConfig[],\n\t\t\tserverURL: 'https://api.example.com',\n\t\t};\n\n\t\tconst result = injectEmailTemplates(config, mockEmailConfig);\n\n\t\texpect(result.collections).toEqual(config.collections);\n\t});\n\n\ttest('should handle auth as boolean and convert to object', () => {\n\t\tconst config: Config = {\n\t\t\tcollections: [\n\t\t\t\t{\n\t\t\t\t\tslug: 'users',\n\t\t\t\t\tfields: [],\n\t\t\t\t\tauth: true,\n\t\t\t\t},\n\t\t\t] as CollectionConfig[],\n\t\t\tserverURL: 'https://api.example.com',\n\t\t};\n\n\t\tconst result = injectEmailTemplates(config, mockEmailConfig);\n\n\t\tconst usersCollection = result.collections?.[0];\n\t\texpect(typeof usersCollection?.auth).toBe('object');\n\t});\n\n\ttest('should preserve existing auth configuration', () => {\n\t\tconst config: Config = {\n\t\t\tcollections: [\n\t\t\t\t{\n\t\t\t\t\tslug: 'users',\n\t\t\t\t\tfields: [],\n\t\t\t\t\tauth: {\n\t\t\t\t\t\ttokenExpiration: 7200,\n\t\t\t\t\t\tmaxLoginAttempts: 5,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t] as CollectionConfig[],\n\t\t\tserverURL: 'https://api.example.com',\n\t\t};\n\n\t\tconst result = injectEmailTemplates(config, mockEmailConfig);\n\n\t\tconst usersCollection = result.collections?.[0];\n\t\tif (typeof usersCollection?.auth === 'object') {\n\t\t\texpect(usersCollection.auth.tokenExpiration).toBe(7200);\n\t\t\texpect(usersCollection.auth.maxLoginAttempts).toBe(5);\n\t\t}\n\t});\n\n\ttest('should merge theme with websiteUrl from frontEndUrl', () => {\n\t\tconst config: Config = {\n\t\t\tcollections: [\n\t\t\t\t{\n\t\t\t\t\tslug: 'users',\n\t\t\t\t\tfields: [],\n\t\t\t\t\tauth: true,\n\t\t\t\t},\n\t\t\t] as CollectionConfig[],\n\t\t\tserverURL: 'https://api.example.com',\n\t\t};\n\n\t\tconst emailConfig: EmailConfig = {\n\t\t\tfrontEndUrl: 'https://custom-frontend.com',\n\t\t\ttheme: {\n\t\t\t\tbranding: {\n\t\t\t\t\tcompanyName: 'Test Company',\n\t\t\t\t},\n\t\t\t},\n\t\t};\n\n\t\tconst result = injectEmailTemplates(config, emailConfig);\n\n\t\t// The theme should have websiteUrl merged into branding\n\t\t// This is tested indirectly through the generateEmailHTML function\n\t\tconst usersCollection = result.collections?.[0];\n\t\texpect(usersCollection).toBeDefined();\n\t});\n\n\ttest('should use serverURL when frontEndUrl is not provided', () => {\n\t\tconst config: Config = {\n\t\t\tcollections: [\n\t\t\t\t{\n\t\t\t\t\tslug: 'users',\n\t\t\t\t\tfields: [],\n\t\t\t\t\tauth: true,\n\t\t\t\t},\n\t\t\t] as CollectionConfig[],\n\t\t\tserverURL: 'https://api.example.com',\n\t\t};\n\n\t\tconst emailConfig: EmailConfig = {\n\t\t\t// No frontEndUrl provided\n\t\t\ttheme: {\n\t\t\t\tbranding: {\n\t\t\t\t\tcompanyName: 'Test Company',\n\t\t\t\t},\n\t\t\t},\n\t\t};\n\n\t\tconst result = injectEmailTemplates(config, emailConfig);\n\n\t\tconst usersCollection = result.collections?.[0];\n\t\texpect(usersCollection).toBeDefined();\n\t\t// The websiteUrl should default to serverURL\n\t});\n\n\ttest('should handle multiple auth-enabled collections', () => {\n\t\tconst config: Config = {\n\t\t\tcollections: [\n\t\t\t\t{\n\t\t\t\t\tslug: 'users',\n\t\t\t\t\tfields: [],\n\t\t\t\t\tauth: true,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tslug: 'admins',\n\t\t\t\t\tfields: [],\n\t\t\t\t\tauth: {\n\t\t\t\t\t\ttokenExpiration: 3600,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tslug: 'posts',\n\t\t\t\t\tfields: [],\n\t\t\t\t\t// No auth\n\t\t\t\t},\n\t\t\t] as CollectionConfig[],\n\t\t\tserverURL: 'https://api.example.com',\n\t\t};\n\n\t\tconst result = injectEmailTemplates(config, mockEmailConfig);\n\n\t\texpect(result.collections).toHaveLength(3);\n\n\t\tconst usersCollection = result.collections?.[0];\n\t\tconst adminsCollection = result.collections?.[1];\n\t\tconst postsCollection = result.collections?.[2];\n\n\t\t// Users should have email templates\n\t\tif (typeof usersCollection?.auth === 'object') {\n\t\t\texpect(usersCollection.auth.forgotPassword?.generateEmailHTML).toBeDefined();\n\t\t\texpect(usersCollection.auth.verify?.generateEmailHTML).toBeDefined();\n\t\t}\n\n\t\t// Admins should have email templates and preserve existing config\n\t\tif (typeof adminsCollection?.auth === 'object') {\n\t\t\texpect(adminsCollection.auth.forgotPassword?.generateEmailHTML).toBeDefined();\n\t\t\texpect(adminsCollection.auth.verify?.generateEmailHTML).toBeDefined();\n\t\t\texpect(adminsCollection.auth.tokenExpiration).toBe(3600);\n\t\t}\n\n\t\t// Posts should remain unchanged\n\t\texpect(postsCollection?.auth).toBeUndefined();\n\t});\n\n\ttest('should handle empty collections array', () => {\n\t\tconst config: Config = {\n\t\t\tcollections: [],\n\t\t\tserverURL: 'https://api.example.com',\n\t\t};\n\n\t\tconst result = injectEmailTemplates(config, mockEmailConfig);\n\n\t\texpect(result.collections).toEqual([]);\n\t});\n\n\ttest('should handle config without collections property', () => {\n\t\tconst config: Config = {\n\t\t\tserverURL: 'https://api.example.com',\n\t\t};\n\n\t\tconst result = injectEmailTemplates(config, mockEmailConfig);\n\n\t\texpect(result.collections).toBeUndefined();\n\t});\n\n\ttest('should preserve existing forgotPassword configuration', () => {\n\t\tconst config: Config = {\n\t\t\tcollections: [\n\t\t\t\t{\n\t\t\t\t\tslug: 'users',\n\t\t\t\t\tfields: [],\n\t\t\t\t\tauth: {\n\t\t\t\t\t\tforgotPassword: {\n\t\t\t\t\t\t\tgenerateEmailSubject: () => 'Custom Subject',\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t] as CollectionConfig[],\n\t\t\tserverURL: 'https://api.example.com',\n\t\t};\n\n\t\tconst result = injectEmailTemplates(config, mockEmailConfig);\n\n\t\tconst usersCollection = result.collections?.[0];\n\t\tif (typeof usersCollection?.auth === 'object') {\n\t\t\texpect(usersCollection.auth.forgotPassword?.generateEmailSubject).toBeDefined();\n\t\t\texpect(usersCollection.auth.forgotPassword?.generateEmailHTML).toBeDefined();\n\t\t}\n\t});\n\n\ttest('should preserve existing verify configuration', () => {\n\t\tconst config: Config = {\n\t\t\tcollections: [\n\t\t\t\t{\n\t\t\t\t\tslug: 'users',\n\t\t\t\t\tfields: [],\n\t\t\t\t\tauth: {\n\t\t\t\t\t\tverify: {\n\t\t\t\t\t\t\tgenerateEmailSubject: () => 'Custom Verify Subject',\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t] as CollectionConfig[],\n\t\t\tserverURL: 'https://api.example.com',\n\t\t};\n\n\t\tconst result = injectEmailTemplates(config, mockEmailConfig);\n\n\t\tconst usersCollection = result.collections?.[0];\n\t\tif (typeof usersCollection?.auth === 'object') {\n\t\t\texpect(usersCollection.auth.verify?.generateEmailSubject).toBeDefined();\n\t\t\texpect(usersCollection.auth.verify?.generateEmailHTML).toBeDefined();\n\t\t}\n\t});\n});\n"],"names":["describe","expect","test","vi","injectEmailTemplates","mock","renderEmail","fn","mockEmailConfig","frontEndUrl","theme","branding","companyName","forgotPassword","heading","bodyText","verifyAccount","config","collections","slug","fields","auth","serverURL","result","usersCollection","toBeDefined","toBe","verify","generateEmailHTML","postsCollection","toBeUndefined","toEqual","tokenExpiration","maxLoginAttempts","emailConfig","toHaveLength","adminsCollection","generateEmailSubject"],"mappings":"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"}
|
package/dist/plugin.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Config } from 'payload';
|
|
2
|
+
import type { PayloadHelperPluginConfig } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Payload Helper Plugin for websites at ainsley.dev
|
|
5
|
+
*
|
|
6
|
+
* @constructor
|
|
7
|
+
* @param pluginOptions
|
|
8
|
+
*/
|
|
9
|
+
export declare const payloadHelper: (pluginOptions: PayloadHelperPluginConfig) => (incomingConfig: Config) => Config;
|
package/dist/plugin.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { injectAdminIcon, injectAdminLogo } from './plugin/admin.js';
|
|
2
|
+
import { injectEmailTemplates } from './plugin/email.js';
|
|
3
|
+
import { cacheHookCollections, cacheHookGlobals } from './plugin/hooks.js';
|
|
4
|
+
/**
|
|
5
|
+
* Payload Helper Plugin for websites at ainsley.dev
|
|
6
|
+
*
|
|
7
|
+
* @constructor
|
|
8
|
+
* @param pluginOptions
|
|
9
|
+
*/ export const payloadHelper = (pluginOptions)=>(incomingConfig)=>{
|
|
10
|
+
// TODO: Validate Config
|
|
11
|
+
let config = incomingConfig;
|
|
12
|
+
// Update typescript generation file
|
|
13
|
+
config.typescript = config.typescript || {};
|
|
14
|
+
config.typescript.outputFile = './src/types/payload.ts';
|
|
15
|
+
// Inject admin Logo component if logo config is provided
|
|
16
|
+
if (pluginOptions.admin?.logo) {
|
|
17
|
+
config = injectAdminLogo(config, pluginOptions.admin.logo, pluginOptions.siteName);
|
|
18
|
+
}
|
|
19
|
+
// Inject admin Icon component if icon config is provided
|
|
20
|
+
if (pluginOptions.admin?.icon) {
|
|
21
|
+
config = injectAdminIcon(config, pluginOptions.admin.icon, pluginOptions.siteName);
|
|
22
|
+
}
|
|
23
|
+
// Inject email templates for auth-enabled collections if email config is provided
|
|
24
|
+
if (pluginOptions.email) {
|
|
25
|
+
config = injectEmailTemplates(config, pluginOptions.email);
|
|
26
|
+
}
|
|
27
|
+
// Map collections & add hooks
|
|
28
|
+
config.collections = (config.collections || []).map((collection)=>{
|
|
29
|
+
if (collection.upload !== undefined && collection.upload !== true) {
|
|
30
|
+
return collection;
|
|
31
|
+
}
|
|
32
|
+
const hooks = collection.hooks || {};
|
|
33
|
+
// Add afterChange hook only if webServer is defined
|
|
34
|
+
if (pluginOptions.webServer) {
|
|
35
|
+
hooks.afterChange = [
|
|
36
|
+
...hooks.afterChange || [],
|
|
37
|
+
cacheHookCollections({
|
|
38
|
+
server: pluginOptions.webServer,
|
|
39
|
+
slug: collection.slug,
|
|
40
|
+
fields: collection.fields,
|
|
41
|
+
isCollection: true
|
|
42
|
+
})
|
|
43
|
+
];
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
...collection,
|
|
47
|
+
hooks
|
|
48
|
+
};
|
|
49
|
+
});
|
|
50
|
+
// Map globals & add hooks
|
|
51
|
+
config.globals = (config.globals || []).map((global)=>{
|
|
52
|
+
const hooks = global.hooks || {};
|
|
53
|
+
// Add afterChange hook only if webServer is defined
|
|
54
|
+
if (pluginOptions.webServer) {
|
|
55
|
+
hooks.afterChange = [
|
|
56
|
+
...hooks.afterChange || [],
|
|
57
|
+
cacheHookGlobals({
|
|
58
|
+
server: pluginOptions.webServer,
|
|
59
|
+
slug: global.slug,
|
|
60
|
+
fields: global.fields,
|
|
61
|
+
isCollection: true
|
|
62
|
+
})
|
|
63
|
+
];
|
|
64
|
+
}
|
|
65
|
+
return {
|
|
66
|
+
...global,
|
|
67
|
+
hooks
|
|
68
|
+
};
|
|
69
|
+
});
|
|
70
|
+
// Store plugin options in config.custom for CLI access (e.g., preview-emails command)
|
|
71
|
+
config.custom = {
|
|
72
|
+
...config.custom,
|
|
73
|
+
payloadHelperOptions: pluginOptions
|
|
74
|
+
};
|
|
75
|
+
return config;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
//# sourceMappingURL=plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/plugin.ts"],"sourcesContent":["import type { CollectionConfig, Config } from 'payload';\nimport { injectAdminIcon, injectAdminLogo } from './plugin/admin.js';\nimport { injectEmailTemplates } from './plugin/email.js';\nimport { cacheHookCollections, cacheHookGlobals } from './plugin/hooks.js';\nimport type { PayloadHelperPluginConfig } from './types.js';\n\n/**\n * Payload Helper Plugin for websites at ainsley.dev\n *\n * @constructor\n * @param pluginOptions\n */\nexport const payloadHelper =\n\t(pluginOptions: PayloadHelperPluginConfig) =>\n\t(incomingConfig: Config): Config => {\n\t\t// TODO: Validate Config\n\n\t\tlet config = incomingConfig;\n\n\t\t// Update typescript generation file\n\t\tconfig.typescript = config.typescript || {};\n\t\tconfig.typescript.outputFile = './src/types/payload.ts';\n\n\t\t// Inject admin Logo component if logo config is provided\n\t\tif (pluginOptions.admin?.logo) {\n\t\t\tconfig = injectAdminLogo(config, pluginOptions.admin.logo, pluginOptions.siteName);\n\t\t}\n\n\t\t// Inject admin Icon component if icon config is provided\n\t\tif (pluginOptions.admin?.icon) {\n\t\t\tconfig = injectAdminIcon(config, pluginOptions.admin.icon, pluginOptions.siteName);\n\t\t}\n\n\t\t// Inject email templates for auth-enabled collections if email config is provided\n\t\tif (pluginOptions.email) {\n\t\t\tconfig = injectEmailTemplates(config, pluginOptions.email);\n\t\t}\n\n\t\t// Map collections & add hooks\n\t\tconfig.collections = (config.collections || []).map((collection): CollectionConfig => {\n\t\t\tif (collection.upload !== undefined && collection.upload !== true) {\n\t\t\t\treturn collection;\n\t\t\t}\n\n\t\t\tconst hooks = collection.hooks || {};\n\n\t\t\t// Add afterChange hook only if webServer is defined\n\t\t\tif (pluginOptions.webServer) {\n\t\t\t\thooks.afterChange = [\n\t\t\t\t\t...(hooks.afterChange || []),\n\t\t\t\t\tcacheHookCollections({\n\t\t\t\t\t\tserver: pluginOptions.webServer,\n\t\t\t\t\t\tslug: collection.slug,\n\t\t\t\t\t\tfields: collection.fields,\n\t\t\t\t\t\tisCollection: true,\n\t\t\t\t\t}),\n\t\t\t\t];\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\t...collection,\n\t\t\t\thooks,\n\t\t\t};\n\t\t});\n\n\t\t// Map globals & add hooks\n\t\tconfig.globals = (config.globals || []).map((global) => {\n\t\t\tconst hooks = global.hooks || {};\n\n\t\t\t// Add afterChange hook only if webServer is defined\n\t\t\tif (pluginOptions.webServer) {\n\t\t\t\thooks.afterChange = [\n\t\t\t\t\t...(hooks.afterChange || []),\n\t\t\t\t\tcacheHookGlobals({\n\t\t\t\t\t\tserver: pluginOptions.webServer,\n\t\t\t\t\t\tslug: global.slug,\n\t\t\t\t\t\tfields: global.fields,\n\t\t\t\t\t\tisCollection: true,\n\t\t\t\t\t}),\n\t\t\t\t];\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\t...global,\n\t\t\t\thooks,\n\t\t\t};\n\t\t});\n\n\t\t// Store plugin options in config.custom for CLI access (e.g., preview-emails command)\n\t\tconfig.custom = {\n\t\t\t...config.custom,\n\t\t\tpayloadHelperOptions: pluginOptions,\n\t\t};\n\n\t\treturn config;\n\t};\n"],"names":["injectAdminIcon","injectAdminLogo","injectEmailTemplates","cacheHookCollections","cacheHookGlobals","payloadHelper","pluginOptions","incomingConfig","config","typescript","outputFile","admin","logo","siteName","icon","email","collections","map","collection","upload","undefined","hooks","webServer","afterChange","server","slug","fields","isCollection","globals","global","custom","payloadHelperOptions"],"mappings":"AACA,SAASA,eAAe,EAAEC,eAAe,QAAQ,oBAAoB;AACrE,SAASC,oBAAoB,QAAQ,oBAAoB;AACzD,SAASC,oBAAoB,EAAEC,gBAAgB,QAAQ,oBAAoB;AAG3E;;;;;CAKC,GACD,OAAO,MAAMC,gBACZ,CAACC,gBACD,CAACC;QACA,wBAAwB;QAExB,IAAIC,SAASD;QAEb,oCAAoC;QACpCC,OAAOC,UAAU,GAAGD,OAAOC,UAAU,IAAI,CAAC;QAC1CD,OAAOC,UAAU,CAACC,UAAU,GAAG;QAE/B,yDAAyD;QACzD,IAAIJ,cAAcK,KAAK,EAAEC,MAAM;YAC9BJ,SAASP,gBAAgBO,QAAQF,cAAcK,KAAK,CAACC,IAAI,EAAEN,cAAcO,QAAQ;QAClF;QAEA,yDAAyD;QACzD,IAAIP,cAAcK,KAAK,EAAEG,MAAM;YAC9BN,SAASR,gBAAgBQ,QAAQF,cAAcK,KAAK,CAACG,IAAI,EAAER,cAAcO,QAAQ;QAClF;QAEA,kFAAkF;QAClF,IAAIP,cAAcS,KAAK,EAAE;YACxBP,SAASN,qBAAqBM,QAAQF,cAAcS,KAAK;QAC1D;QAEA,8BAA8B;QAC9BP,OAAOQ,WAAW,GAAG,AAACR,CAAAA,OAAOQ,WAAW,IAAI,EAAE,AAAD,EAAGC,GAAG,CAAC,CAACC;YACpD,IAAIA,WAAWC,MAAM,KAAKC,aAAaF,WAAWC,MAAM,KAAK,MAAM;gBAClE,OAAOD;YACR;YAEA,MAAMG,QAAQH,WAAWG,KAAK,IAAI,CAAC;YAEnC,oDAAoD;YACpD,IAAIf,cAAcgB,SAAS,EAAE;gBAC5BD,MAAME,WAAW,GAAG;uBACfF,MAAME,WAAW,IAAI,EAAE;oBAC3BpB,qBAAqB;wBACpBqB,QAAQlB,cAAcgB,SAAS;wBAC/BG,MAAMP,WAAWO,IAAI;wBACrBC,QAAQR,WAAWQ,MAAM;wBACzBC,cAAc;oBACf;iBACA;YACF;YAEA,OAAO;gBACN,GAAGT,UAAU;gBACbG;YACD;QACD;QAEA,0BAA0B;QAC1Bb,OAAOoB,OAAO,GAAG,AAACpB,CAAAA,OAAOoB,OAAO,IAAI,EAAE,AAAD,EAAGX,GAAG,CAAC,CAACY;YAC5C,MAAMR,QAAQQ,OAAOR,KAAK,IAAI,CAAC;YAE/B,oDAAoD;YACpD,IAAIf,cAAcgB,SAAS,EAAE;gBAC5BD,MAAME,WAAW,GAAG;uBACfF,MAAME,WAAW,IAAI,EAAE;oBAC3BnB,iBAAiB;wBAChBoB,QAAQlB,cAAcgB,SAAS;wBAC/BG,MAAMI,OAAOJ,IAAI;wBACjBC,QAAQG,OAAOH,MAAM;wBACrBC,cAAc;oBACf;iBACA;YACF;YAEA,OAAO;gBACN,GAAGE,MAAM;gBACTR;YACD;QACD;QAEA,sFAAsF;QACtFb,OAAOsB,MAAM,GAAG;YACf,GAAGtB,OAAOsB,MAAM;YAChBC,sBAAsBzB;QACvB;QAEA,OAAOE;IACR,EAAE"}
|