@xeplr/actions 1.1.1 → 1.1.5

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.
@@ -1,8 +1,117 @@
1
- // send-email action — sends email via @xeplr/utils (which wraps
2
- // SMTP/SES/Brevo). Peer dep: @xeplr/utils.
3
- // Implementation pending user spec.
1
+ // email-send action — send one message over SMTP.
2
+ //
3
+ // Peer dep: `nodemailer`, lazy-required inside the driver so a read-only
4
+ // deployment never has to install it.
5
+ //
6
+ // ATTACHMENTS take nodemailer's own descriptors — { filename, path },
7
+ // { filename, content }, { filename, href } — which is why the output of
8
+ // email-download-attachments (`saved[]`, each with filename + path) can be
9
+ // passed straight back in to forward what was just downloaded.
10
+ //
11
+ // `accepted` / `rejected` are both returned. SMTP accepts per-recipient, so a
12
+ // send can succeed at the protocol level while dropping half its recipients;
13
+ // reporting only success would hide that.
14
+
15
+ var emailDrivers = require('../drivers/email');
16
+ var emailConfig = require('../drivers/email/config');
4
17
 
5
18
  module.exports = {
6
- requires: ['@xeplr/utils']
7
- // rest to be filled in
19
+ name: 'email-send',
20
+ description: 'Send an email over SMTP. Attachments accept { filename, path } descriptors, ' +
21
+ 'so email-download-attachments output can be forwarded directly.',
22
+ requires: [], // nodemailer checked at runtime
23
+
24
+ // ORDERED THE WAY THE DECISIONS ARE ACTUALLY MADE. Which server sends this
25
+ // comes first, because it is the only answer that changes what the rest of
26
+ // the form means — and it is one tick, which either ends the subject there
27
+ // or opens a connection box. Then what to say, then who else gets a copy.
28
+ //
29
+ // Two ways to keep the form short, and they are not the same thing:
30
+ //
31
+ // showWhen the field is not asked AT ALL right now. Choosing a template
32
+ // means there is no subject to write, so the subject box is
33
+ // gone rather than sitting there collecting a discarded value.
34
+ //
35
+ // group the field is always legitimate, just rarely wanted. cc, bcc,
36
+ // reply-to, attachments — real inputs, none of them what you
37
+ // opened the step for. Same group name → one collapsed section.
38
+ //
39
+ // Both are layout only; @xeplr/schema-handler validates every field the same
40
+ // way regardless (see its validate.js).
41
+ inputSchema: [
42
+ { name: 'useCustomConnection', type: 'boolean', default: false, order: 1,
43
+ description: 'Off: send through this install\'s email service. On: supply your own SMTP connection below.' },
44
+ { name: 'connection', type: 'object', order: 2,
45
+ showWhen: { field: 'useCustomConnection', equals: true },
46
+ description: '{ host, port, secure, user, password, from, replyTo, envelopeFrom, tls? }' },
47
+
48
+ // A stored template replaces subject+html entirely: pick "User
49
+ // Registration" and supply its variables, instead of restating the same
50
+ // message in every workflow that sends it. Templates live in the EMAIL
51
+ // service (@xeplr/email) because everything here sends mail — one store,
52
+ // every product.
53
+ { name: 'templateName', type: 'string', order: 3,
54
+ description: 'Name of a stored email template. Leave empty to write the subject and body here instead.' },
55
+ { name: 'templateVars', type: 'object', order: 4,
56
+ showWhen: { field: 'templateName', isSet: true },
57
+ description: 'Values for the template\'s {{variables}}. Bind them like any other input, e.g. { "firstName": "{params.name}" }.' },
58
+ { name: 'to', type: 'array', required: true, order: 5,
59
+ description: 'Recipient addresses. A bare string is accepted and wrapped.' },
60
+ { name: 'subject', type: 'string', order: 6,
61
+ showWhen: { field: 'templateName', isSet: false },
62
+ description: 'Message subject. Required unless a template supplies it.' },
63
+ { name: 'html', type: 'string', order: 7,
64
+ showWhen: { field: 'templateName', isSet: false },
65
+ description: 'HTML body. Required unless a template supplies it.' },
66
+
67
+ { name: 'text', type: 'string', order: 8, group: 'Message options',
68
+ showWhen: { field: 'templateName', isSet: false },
69
+ description: 'Plain-text body, as an alternative to html.' },
70
+ { name: 'attachments', type: 'array', order: 9, group: 'Message options',
71
+ description: 'Nodemailer attachment descriptors, e.g. [{ filename, path }].' },
72
+
73
+ { name: 'cc', type: 'array', order: 10, group: 'More addresses',
74
+ description: 'CC addresses.' },
75
+ { name: 'bcc', type: 'array', order: 11, group: 'More addresses',
76
+ description: 'BCC addresses.' },
77
+ { name: 'from', type: 'string', order: 12, group: 'More addresses',
78
+ description: 'Sender address. Falls back to connection.from.' },
79
+ { name: 'replyTo', type: 'string', order: 13, group: 'More addresses',
80
+ description: 'Reply-To address. Falls back to connection.replyTo, then from.' }
81
+ ],
82
+
83
+ execute: async function(ctx) {
84
+ var input = ctx.input || {};
85
+
86
+ // A template supplies subject/html/text; anything typed on the step is
87
+ // OVERRIDDEN rather than merged, so "which subject actually went out" has
88
+ // one answer. @xeplr/email is an optional peer — an install that never
89
+ // uses templates does not need it, and only a step naming one hits this.
90
+ if (input.templateName) {
91
+ var email;
92
+ try {
93
+ email = require('@xeplr/email');
94
+ } catch (_) {
95
+ throw new Error("email-send: templateName is set but '@xeplr/email' is not installed. Install it, or write the subject and body on the step.");
96
+ }
97
+ if (!email.templatesReady || !email.templatesReady()) {
98
+ throw new Error('email-send: the template store is not initialised — the host must call initTemplates() at boot (see @xeplr/email).');
99
+ }
100
+ var rendered = await email.renderTemplate(input.templateName, input.templateVars || {});
101
+ input = Object.assign({}, input, {
102
+ subject: rendered.subject,
103
+ html: rendered.html,
104
+ text: rendered.text
105
+ });
106
+ }
107
+
108
+ var provider = emailDrivers.getOutboundProvider(input.emailType);
109
+ // 'system' hands off to the install's configured sender (which may not be
110
+ // SMTP at all), so there is no connection to resolve and no SMTP library
111
+ // to require. 'custom' builds a transport from the step's own connection.
112
+ var useSystem = emailConfig.usesSystemSender(input);
113
+ provider.checkRequires('email-send', { useSystem: useSystem });
114
+ var conn = useSystem ? null : emailConfig.resolveOutbound(input);
115
+ return provider.send(conn, input, useSystem);
116
+ }
8
117
  };