types.ts 12 KB

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