types.ts 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457
  1. import { LanguageCode } from '@vendure/common/lib/generated-types';
  2. import { Omit } from '@vendure/common/lib/omit';
  3. import { Injector, RequestContext, SerializedRequestContext, VendureEvent } from '@vendure/core';
  4. import { Attachment } from 'nodemailer/lib/mailer';
  5. import SESTransport from 'nodemailer/lib/ses-transport'
  6. import SMTPTransport from 'nodemailer/lib/smtp-transport';
  7. import { EmailGenerator } from './email-generator';
  8. import { EmailSender } from './email-sender';
  9. import { EmailEventHandler } from './event-handler';
  10. /**
  11. * @description
  12. * A VendureEvent which also includes a `ctx` property containing the current
  13. * {@link RequestContext}, which is used to determine the channel and language
  14. * to use when generating the email.
  15. *
  16. * @docsCategory EmailPlugin
  17. * @docsPage Email Plugin Types
  18. */
  19. export type EventWithContext = VendureEvent & { ctx: RequestContext };
  20. /**
  21. * @description
  22. * A VendureEvent with a {@link RequestContext} and a `data` property which contains the
  23. * value resolved from the {@link EmailEventHandler}`.loadData()` callback.
  24. *
  25. * @docsCategory EmailPlugin
  26. * @docsPage Email Plugin Types
  27. */
  28. export type EventWithAsyncData<Event extends EventWithContext, R> = Event & { data: R };
  29. /**
  30. * @description
  31. * Configuration for the EmailPlugin.
  32. *
  33. * @docsCategory EmailPlugin
  34. * @docsPage EmailPluginOptions
  35. * */
  36. export interface EmailPluginOptions {
  37. /**
  38. * @description
  39. * The path to the location of the email templates. In a default Vendure installation,
  40. * the templates are installed to `<project root>/vendure/email/templates`.
  41. *
  42. * @deprecated Use `templateLoader` to define a template path: `templateLoader: new FileBasedTemplateLoader('../your-path/templates')`
  43. */
  44. templatePath?: string;
  45. /**
  46. * @description
  47. * An optional TemplateLoader which can be used to load templates from a custom location or async service.
  48. * The default uses the FileBasedTemplateLoader which loads templates from `<project root>/vendure/email/templates`
  49. *
  50. * @since 2.0.0
  51. */
  52. templateLoader?: TemplateLoader;
  53. /**
  54. * @description
  55. * Configures how the emails are sent.
  56. */
  57. transport: EmailTransportOptions | ((injector?: Injector, ctx?: RequestContext) => EmailTransportOptions | Promise<EmailTransportOptions>)
  58. /**
  59. * @description
  60. * An array of {@link EmailEventHandler}s which define which Vendure events will trigger
  61. * emails, and how those emails are generated.
  62. */
  63. handlers: Array<EmailEventHandler<string, any>>;
  64. /**
  65. * @description
  66. * An object containing variables which are made available to all templates. For example,
  67. * the storefront URL could be defined here and then used in the "email address verification"
  68. * email.
  69. */
  70. globalTemplateVars?: { [key: string]: any };
  71. /**
  72. * @description
  73. * An optional allowed EmailSender, used to allow custom implementations of the send functionality
  74. * while still utilizing the existing emailPlugin functionality.
  75. *
  76. * @default NodemailerEmailSender
  77. */
  78. emailSender?: EmailSender;
  79. /**
  80. * @description
  81. * An optional allowed EmailGenerator, used to allow custom email generation functionality to
  82. * better match with custom email sending functionality.
  83. *
  84. * @default HandlebarsMjmlGenerator
  85. */
  86. emailGenerator?: EmailGenerator;
  87. }
  88. /**
  89. * EmailPLuginOptions type after initialization, where templateLoader is no longer optional
  90. */
  91. export type InitializedEmailPluginOptions = EmailPluginOptions & { templateLoader: TemplateLoader };
  92. /**
  93. * @description
  94. * Configuration for running the EmailPlugin in development mode.
  95. *
  96. * @docsCategory EmailPlugin
  97. * @docsPage EmailPluginOptions
  98. */
  99. export interface EmailPluginDevModeOptions extends Omit<EmailPluginOptions, 'transport'> {
  100. devMode: true;
  101. /**
  102. * @description
  103. * The path to which html email files will be saved rather than being sent.
  104. */
  105. outputPath: string;
  106. /**
  107. * @description
  108. * The route to the dev mailbox server.
  109. */
  110. route: string;
  111. }
  112. /**
  113. * @description
  114. * A union of all the possible transport options for sending emails.
  115. *
  116. * @docsCategory EmailPlugin
  117. * @docsPage Transport Options
  118. */
  119. export type EmailTransportOptions =
  120. | SMTPTransportOptions
  121. | SendmailTransportOptions
  122. | FileTransportOptions
  123. | NoopTransportOptions
  124. | SESTransportOptions
  125. | TestingTransportOptions;
  126. /**
  127. * @description
  128. * The SMTP transport options of [Nodemailer](https://nodemailer.com/smtp/)
  129. *
  130. * @docsCategory EmailPlugin
  131. * @docsPage Transport Options
  132. */
  133. export interface SMTPTransportOptions extends SMTPTransport.Options {
  134. type: 'smtp';
  135. /**
  136. * @description
  137. * If true, uses the configured {@link VendureLogger} to log messages from Nodemailer as it interacts with
  138. * the SMTP server.
  139. *
  140. * @default false
  141. */
  142. logging?: boolean;
  143. }
  144. /**
  145. * @description
  146. * The SES transport options of [Nodemailer](https://nodemailer.com/transports/ses//)
  147. *
  148. * See [Nodemailers's SES docs](https://nodemailer.com/transports/ses/) for more details
  149. *
  150. * @example
  151. * ```TypeScript
  152. * import { SES, SendRawEmailCommand } from '\@aws-sdk/client-ses'
  153. *
  154. * const ses = new SES({
  155. * apiVersion: '2010-12-01',
  156. * region: 'eu-central-1',
  157. * credentials: {
  158. * accessKeyId: process.env.SES_ACCESS_KEY || '',
  159. * secretAccessKey: process.env.SES_SECRET_KEY || '',
  160. * },
  161. * })
  162. *
  163. * const config: VendureConfig = {
  164. * // Add an instance of the plugin to the plugins array
  165. * plugins: [
  166. * EmailPlugin.init({
  167. * handlers: defaultEmailHandlers,
  168. * templatePath: path.join(__dirname, 'static/email/templates'),
  169. * transport: {
  170. * type: 'ses',
  171. * SES: { ses, aws: { SendRawEmailCommand } },
  172. * sendingRate: 10, // optional messages per second sending rate
  173. * },
  174. * }),
  175. * ],
  176. * };
  177. * ```
  178. * @docsCategory EmailPlugin
  179. * @docsPage Transport Options
  180. */
  181. export interface SESTransportOptions extends SESTransport.Options {
  182. type: 'ses';
  183. }
  184. /**
  185. * @description
  186. * Uses the local Sendmail program to send the email.
  187. *
  188. * @docsCategory EmailPlugin
  189. * @docsPage Transport Options
  190. */
  191. export interface SendmailTransportOptions {
  192. type: 'sendmail';
  193. /** path to the sendmail command (defaults to ‘sendmail’) */
  194. path?: string;
  195. /** either ‘windows’ or ‘unix’ (default). Forces all newlines in the output to either use Windows syntax <CR><LF> or Unix syntax <LF> */
  196. newline?: string;
  197. }
  198. /**
  199. * @description
  200. * Outputs the email as an HTML file for development purposes.
  201. *
  202. * @docsCategory EmailPlugin
  203. * @docsPage Transport Options
  204. */
  205. export interface FileTransportOptions {
  206. type: 'file';
  207. /** The directory in which the emails will be saved */
  208. outputPath: string;
  209. /** When set to true, a raw text file will be output rather than an HTML file */
  210. raw?: boolean;
  211. }
  212. /**
  213. * @description
  214. * Does nothing with the generated email. Intended for use in testing where we don't care about the email transport,
  215. * or when using a custom {@link EmailSender} which does not require transport options.
  216. *
  217. * @docsCategory EmailPlugin
  218. * @docsPage Transport Options
  219. */
  220. export interface NoopTransportOptions {
  221. type: 'none';
  222. }
  223. /**
  224. * @description
  225. * The final, generated email details to be sent.
  226. *
  227. * @docsCategory EmailPlugin
  228. * @docsPage Email Plugin Types
  229. */
  230. export interface EmailDetails<Type extends 'serialized' | 'unserialized' = 'unserialized'> {
  231. from: string;
  232. recipient: string;
  233. subject: string;
  234. body: string;
  235. attachments: Array<Type extends 'serialized' ? SerializedAttachment : Attachment>;
  236. cc?: string;
  237. bcc?: string;
  238. replyTo?: string;
  239. }
  240. /**
  241. * @description
  242. * Forwards the raw GeneratedEmailContext object to a provided callback, for use in testing.
  243. *
  244. * @docsCategory EmailPlugin
  245. * @docsPage Transport Options
  246. */
  247. export interface TestingTransportOptions {
  248. type: 'testing';
  249. /**
  250. * @description
  251. * Callback to be invoked when an email would be sent.
  252. */
  253. onSend: (details: EmailDetails) => void;
  254. }
  255. /**
  256. * @description
  257. * A function used to load async data for use by an {@link EmailEventHandler}.
  258. *
  259. * @docsCategory EmailPlugin
  260. * @docsPage Email Plugin Types
  261. */
  262. export type LoadDataFn<Event extends EventWithContext, R> = (context: {
  263. event: Event;
  264. injector: Injector;
  265. }) => Promise<R>;
  266. export type OptionalToNullable<O> = {
  267. [K in keyof O]-?: undefined extends O[K] ? NonNullable<O[K]> | null : O[K];
  268. };
  269. /**
  270. * @description
  271. * An object defining a file attachment for an email. Based on the object described
  272. * [here in the Nodemailer docs](https://nodemailer.com/message/attachments/), but
  273. * only uses the `path` property to define a filesystem path or a URL pointing to
  274. * the attachment file.
  275. *
  276. * @docsCategory EmailPlugin
  277. * @docsPage Email Plugin Types
  278. */
  279. export type EmailAttachment = Omit<Attachment, 'raw'> & { path?: string };
  280. export type SerializedAttachment = OptionalToNullable<
  281. Omit<EmailAttachment, 'content'> & { content: string | null }
  282. >;
  283. export type IntermediateEmailDetails = {
  284. ctx: SerializedRequestContext;
  285. type: string;
  286. from: string;
  287. recipient: string;
  288. templateVars: any;
  289. subject: string;
  290. templateFile: string;
  291. attachments: SerializedAttachment[];
  292. cc?: string;
  293. bcc?: string;
  294. replyTo?: string;
  295. };
  296. /**
  297. * @description
  298. * Configures the {@link EmailEventHandler} to handle a particular channel & languageCode
  299. * combination.
  300. *
  301. * @deprecated Use a custom {@link TemplateLoader} instead.
  302. */
  303. export interface EmailTemplateConfig {
  304. /**
  305. * @description
  306. * Specifies the channel to which this configuration will apply. If set to `'default'`, it will be applied to all
  307. * channels.
  308. */
  309. channelCode: string | 'default';
  310. /**
  311. * @description
  312. * Specifies the languageCode to which this configuration will apply. If set to `'default'`, it will be applied to all
  313. * languages.
  314. */
  315. languageCode: LanguageCode | 'default';
  316. /**
  317. * @description
  318. * Defines the file name of the Handlebars template file to be used to when generating this email.
  319. */
  320. templateFile: string;
  321. /**
  322. * @description
  323. * A string defining the email subject line. Handlebars variables defined in the `templateVars` object may
  324. * be used inside the subject.
  325. */
  326. subject: string;
  327. }
  328. export interface LoadTemplateInput {
  329. type: string,
  330. templateName: string
  331. }
  332. export interface Partial {
  333. name: string,
  334. content: string
  335. }
  336. /**
  337. * @description
  338. * Load an email template based on the given request context, type and template name
  339. * and return the template as a string.
  340. *
  341. * @example
  342. * ```TypeScript
  343. * import { EmailPlugin, TemplateLoader } from '@vendure/email-plugin';
  344. *
  345. * class MyTemplateLoader implements TemplateLoader {
  346. * loadTemplate(injector, ctx, { type, templateName }){
  347. * return myCustomTemplateFunction(ctx);
  348. * }
  349. * }
  350. *
  351. * // In vendure-config.ts:
  352. * ...
  353. * EmailPlugin.init({
  354. * templateLoader: new MyTemplateLoader()
  355. * ...
  356. * })
  357. * ```
  358. *
  359. * @docsCategory EmailPlugin
  360. * @docsPage Custom Template Loader
  361. */
  362. export interface TemplateLoader {
  363. /**
  364. * Load template and return it's content as a string
  365. */
  366. loadTemplate(injector: Injector, ctx: RequestContext, input: LoadTemplateInput): Promise<string>;
  367. /**
  368. * Load partials and return their contents.
  369. * This method is only called during initalization, i.e. during server startup.
  370. */
  371. loadPartials?(): Promise<Partial[]>;
  372. }
  373. /**
  374. * @description
  375. * A function used to define template variables available to email templates.
  376. * See {@link EmailEventHandler}.setTemplateVars().
  377. *
  378. * @docsCategory EmailPlugin
  379. * @docsPage Email Plugin Types
  380. */
  381. export type SetTemplateVarsFn<Event> = (
  382. event: Event,
  383. globals: { [key: string]: any },
  384. ) => { [key: string]: any };
  385. /**
  386. * @description
  387. * A function used to define attachments to be sent with the email.
  388. * See https://nodemailer.com/message/attachments/ for more information about
  389. * how attachments work in Nodemailer.
  390. *
  391. * @docsCategory EmailPlugin
  392. * @docsPage Email Plugin Types
  393. */
  394. export type SetAttachmentsFn<Event> = (event: Event) => EmailAttachment[] | Promise<EmailAttachment[]>;
  395. /**
  396. * @description
  397. * Optional address-related fields for sending the email.
  398. *
  399. * @since 1.1.0
  400. * @docsCategory EmailPlugin
  401. * @docsPage Email Plugin Types
  402. */
  403. export interface OptionalAddressFields {
  404. /**
  405. * @description
  406. * Comma separated list of recipients email addresses that will appear on the _Cc:_ field
  407. */
  408. cc?: string;
  409. /**
  410. * @description
  411. * Comma separated list of recipients email addresses that will appear on the _Bcc:_ field
  412. */
  413. bcc?: string;
  414. /**
  415. * @description
  416. * An email address that will appear on the _Reply-To:_ field
  417. */
  418. replyTo?: string;
  419. }
  420. /**
  421. * @description
  422. * A function used to set the {@link OptionalAddressFields}.
  423. *
  424. * @since 1.1.0
  425. * @docsCategory EmailPlugin
  426. * @docsPage Email Plugin Types
  427. */
  428. export type SetOptionalAddressFieldsFn<Event> = (
  429. event: Event,
  430. ) => OptionalAddressFields | Promise<OptionalAddressFields>;