@mailmodo/cli 0.0.30-beta.pr32.52 → 0.0.30-beta.pr32.53
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.
|
@@ -3,7 +3,7 @@ import { confirm, input, select } from '@inquirer/prompts';
|
|
|
3
3
|
import chalk from 'chalk';
|
|
4
4
|
import { BaseCommand } from '../../lib/base-command.js';
|
|
5
5
|
import { API_ENDPOINTS } from '../../lib/constants.js';
|
|
6
|
-
import { loadTemplate,
|
|
6
|
+
import { loadTemplate, getTemplateFilename, saveTemplate, saveYaml, } from '../../lib/yaml-config.js';
|
|
7
7
|
export default class Edit extends BaseCommand {
|
|
8
8
|
static args = {
|
|
9
9
|
id: Args.string({
|
|
@@ -31,7 +31,7 @@ export default class Edit extends BaseCommand {
|
|
|
31
31
|
this.error(`Email '${args.id}' not found in mailmodo.yaml.`);
|
|
32
32
|
}
|
|
33
33
|
const email = yamlConfig.emails[emailIndex];
|
|
34
|
-
const templateFilename =
|
|
34
|
+
const templateFilename = getTemplateFilename(email.id, email.style, yamlConfig.project?.emailStyle);
|
|
35
35
|
const ctx = {
|
|
36
36
|
email,
|
|
37
37
|
emailIndex,
|
|
@@ -23,8 +23,12 @@ export default class Preview extends BaseCommand {
|
|
|
23
23
|
*/
|
|
24
24
|
private sendTestEmail;
|
|
25
25
|
/**
|
|
26
|
-
*
|
|
27
|
-
|
|
26
|
+
* Probes ports starting at startPort and returns the first one not in use.
|
|
27
|
+
*/
|
|
28
|
+
private findAvailablePort;
|
|
29
|
+
/**
|
|
30
|
+
* Starts a local HTTP server to serve the rendered email template,
|
|
31
|
+
* then opens the user's default browser to view it.
|
|
28
32
|
*/
|
|
29
33
|
private startPreviewServer;
|
|
30
34
|
}
|
|
@@ -4,7 +4,7 @@ import chalk from 'chalk';
|
|
|
4
4
|
import open from 'open';
|
|
5
5
|
import { BaseCommand } from '../../lib/base-command.js';
|
|
6
6
|
import { API_ENDPOINTS, PREVIEW_PORT } from '../../lib/constants.js';
|
|
7
|
-
import { loadTemplate,
|
|
7
|
+
import { loadTemplate, getEmailStyle, getTemplateFilename, } from '../../lib/yaml-config.js';
|
|
8
8
|
/* eslint-disable camelcase */
|
|
9
9
|
const SAMPLE_DATA = Object.freeze({
|
|
10
10
|
app_url: 'https://yourapp.com',
|
|
@@ -84,7 +84,8 @@ export default class Preview extends BaseCommand {
|
|
|
84
84
|
app_url: yamlConfig.project?.url || 'https://yourapp.com', // eslint-disable-line camelcase
|
|
85
85
|
product_name: yamlConfig.project?.name || 'YourApp', // eslint-disable-line camelcase
|
|
86
86
|
};
|
|
87
|
-
const
|
|
87
|
+
const effectiveStyle = getEmailStyle(email.style, yamlConfig.project?.emailStyle);
|
|
88
|
+
const templateHtml = await loadTemplate(getTemplateFilename(email.id, email.style, yamlConfig.project?.emailStyle));
|
|
88
89
|
if (flags.send) {
|
|
89
90
|
const rendered = templateHtml
|
|
90
91
|
? renderTemplate(templateHtml, sampleData)
|
|
@@ -100,7 +101,10 @@ export default class Preview extends BaseCommand {
|
|
|
100
101
|
await this.renderText(email, templateHtml, sampleData, flags.json);
|
|
101
102
|
return;
|
|
102
103
|
}
|
|
103
|
-
await this.startPreviewServer(email, templateHtml, sampleData,
|
|
104
|
+
await this.startPreviewServer(email, templateHtml, sampleData, {
|
|
105
|
+
effectiveStyle,
|
|
106
|
+
jsonOutput: flags.json,
|
|
107
|
+
});
|
|
104
108
|
}
|
|
105
109
|
/**
|
|
106
110
|
* Renders a plain text version of the email to stdout. Used by AI agents
|
|
@@ -154,10 +158,28 @@ export default class Preview extends BaseCommand {
|
|
|
154
158
|
this.log('');
|
|
155
159
|
}
|
|
156
160
|
/**
|
|
157
|
-
*
|
|
158
|
-
* template, then opens the user's default browser to view it.
|
|
161
|
+
* Probes ports starting at startPort and returns the first one not in use.
|
|
159
162
|
*/
|
|
160
|
-
async
|
|
163
|
+
async findAvailablePort(startPort, endPort = startPort + 10) {
|
|
164
|
+
if (startPort > endPort) {
|
|
165
|
+
throw new Error(`No available port found starting from port ${endPort - 10}`);
|
|
166
|
+
}
|
|
167
|
+
const available = await new Promise((resolve) => {
|
|
168
|
+
const probe = createServer();
|
|
169
|
+
probe.once('error', () => resolve(false));
|
|
170
|
+
probe.once('listening', () => probe.close(() => resolve(true)));
|
|
171
|
+
probe.listen(startPort);
|
|
172
|
+
});
|
|
173
|
+
return available
|
|
174
|
+
? startPort
|
|
175
|
+
: this.findAvailablePort(startPort + 1, endPort);
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Starts a local HTTP server to serve the rendered email template,
|
|
179
|
+
* then opens the user's default browser to view it.
|
|
180
|
+
*/
|
|
181
|
+
async startPreviewServer(email, templateHtml, sampleData, opts) {
|
|
182
|
+
const { effectiveStyle, jsonOutput } = opts;
|
|
161
183
|
const rendered = templateHtml
|
|
162
184
|
? renderTemplate(templateHtml, sampleData)
|
|
163
185
|
: '<p>No template found.</p>';
|
|
@@ -183,7 +205,7 @@ export default class Preview extends BaseCommand {
|
|
|
183
205
|
<body>
|
|
184
206
|
<div class="preview-bar">
|
|
185
207
|
<h3>Mailmodo Preview — ${email.id}</h3>
|
|
186
|
-
<span>Style: ${
|
|
208
|
+
<span>Style: ${effectiveStyle} · Press Ctrl+C in terminal to stop</span>
|
|
187
209
|
</div>
|
|
188
210
|
<div class="email-frame">
|
|
189
211
|
<div class="email-header">
|
|
@@ -194,11 +216,15 @@ export default class Preview extends BaseCommand {
|
|
|
194
216
|
</div>
|
|
195
217
|
</body>
|
|
196
218
|
</html>`;
|
|
219
|
+
const port = await this.findAvailablePort(PREVIEW_PORT);
|
|
220
|
+
if (!jsonOutput && port !== PREVIEW_PORT) {
|
|
221
|
+
this.log(`\n ${chalk.yellow('!')} Port ${PREVIEW_PORT} is already in use. Opening preview on port ${chalk.cyan(String(port))}.`);
|
|
222
|
+
}
|
|
197
223
|
if (jsonOutput) {
|
|
198
224
|
this.log(JSON.stringify({
|
|
199
225
|
id: email.id,
|
|
200
|
-
style:
|
|
201
|
-
url: `http://localhost:${
|
|
226
|
+
style: effectiveStyle,
|
|
227
|
+
url: `http://localhost:${port}`,
|
|
202
228
|
}, null, 2));
|
|
203
229
|
}
|
|
204
230
|
const server = createServer((_req, res) => {
|
|
@@ -206,11 +232,11 @@ export default class Preview extends BaseCommand {
|
|
|
206
232
|
res.end(wrapperHtml);
|
|
207
233
|
});
|
|
208
234
|
await new Promise((resolve) => {
|
|
209
|
-
server.listen(
|
|
235
|
+
server.listen(port, () => resolve());
|
|
210
236
|
});
|
|
211
|
-
const url = `http://localhost:${
|
|
237
|
+
const url = `http://localhost:${port}`;
|
|
212
238
|
if (!jsonOutput) {
|
|
213
|
-
this.log(`\n Style: ${chalk.cyan(
|
|
239
|
+
this.log(`\n Style: ${chalk.cyan(effectiveStyle)}`);
|
|
214
240
|
this.log(` Preview server at ${chalk.cyan(url)}`);
|
|
215
241
|
this.log(` Opening in browser...\n`);
|
|
216
242
|
this.log(` ${chalk.dim('Press Ctrl+C to stop the preview server.')}\n`);
|
|
@@ -68,4 +68,5 @@ export declare function saveTemplate(filename: string, html: string, cwd?: strin
|
|
|
68
68
|
* or null if the file doesn't exist or can't be read.
|
|
69
69
|
*/
|
|
70
70
|
export declare function loadTemplate(filename: string, cwd?: string): Promise<null | string>;
|
|
71
|
-
export declare function
|
|
71
|
+
export declare function getEmailStyle(emailStyle?: 'branded' | 'plain', projectStyle?: 'branded' | 'plain'): 'branded' | 'plain';
|
|
72
|
+
export declare function getTemplateFilename(emailId: string, emailStyle?: 'branded' | 'plain', projectStyle?: 'branded' | 'plain'): string;
|
package/dist/lib/yaml-config.js
CHANGED
|
@@ -72,7 +72,10 @@ export async function loadTemplate(filename, cwd) {
|
|
|
72
72
|
return null;
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
|
-
export function
|
|
76
|
-
|
|
75
|
+
export function getEmailStyle(emailStyle, projectStyle) {
|
|
76
|
+
return emailStyle ?? projectStyle ?? 'branded';
|
|
77
|
+
}
|
|
78
|
+
export function getTemplateFilename(emailId, emailStyle, projectStyle) {
|
|
79
|
+
const style = getEmailStyle(emailStyle, projectStyle);
|
|
77
80
|
return style === 'plain' ? `${emailId}_plain.html` : `${emailId}.html`;
|
|
78
81
|
}
|
package/oclif.manifest.json
CHANGED
|
@@ -137,14 +137,13 @@
|
|
|
137
137
|
"index.js"
|
|
138
138
|
]
|
|
139
139
|
},
|
|
140
|
-
"
|
|
140
|
+
"deploy": {
|
|
141
141
|
"aliases": [],
|
|
142
142
|
"args": {},
|
|
143
|
-
"description": "
|
|
143
|
+
"description": "Deploy email sequences and verify sending domain",
|
|
144
144
|
"examples": [
|
|
145
|
-
"<%= config.bin %>
|
|
146
|
-
"<%= config.bin %>
|
|
147
|
-
"<%= config.bin %> domain --status"
|
|
145
|
+
"<%= config.bin %> deploy",
|
|
146
|
+
"<%= config.bin %> deploy --yes"
|
|
148
147
|
],
|
|
149
148
|
"flags": {
|
|
150
149
|
"json": {
|
|
@@ -159,23 +158,11 @@
|
|
|
159
158
|
"name": "yes",
|
|
160
159
|
"allowNo": false,
|
|
161
160
|
"type": "boolean"
|
|
162
|
-
},
|
|
163
|
-
"status": {
|
|
164
|
-
"description": "Show domain health status",
|
|
165
|
-
"name": "status",
|
|
166
|
-
"allowNo": false,
|
|
167
|
-
"type": "boolean"
|
|
168
|
-
},
|
|
169
|
-
"verify": {
|
|
170
|
-
"description": "Verify DNS records",
|
|
171
|
-
"name": "verify",
|
|
172
|
-
"allowNo": false,
|
|
173
|
-
"type": "boolean"
|
|
174
161
|
}
|
|
175
162
|
},
|
|
176
163
|
"hasDynamicHelp": false,
|
|
177
164
|
"hiddenAliases": [],
|
|
178
|
-
"id": "
|
|
165
|
+
"id": "deploy",
|
|
179
166
|
"pluginAlias": "@mailmodo/cli",
|
|
180
167
|
"pluginName": "@mailmodo/cli",
|
|
181
168
|
"pluginType": "core",
|
|
@@ -185,17 +172,18 @@
|
|
|
185
172
|
"relativePath": [
|
|
186
173
|
"dist",
|
|
187
174
|
"commands",
|
|
188
|
-
"
|
|
175
|
+
"deploy",
|
|
189
176
|
"index.js"
|
|
190
177
|
]
|
|
191
178
|
},
|
|
192
|
-
"
|
|
179
|
+
"domain": {
|
|
193
180
|
"aliases": [],
|
|
194
181
|
"args": {},
|
|
195
|
-
"description": "
|
|
182
|
+
"description": "Set up and verify your sending domain",
|
|
196
183
|
"examples": [
|
|
197
|
-
"<%= config.bin %>
|
|
198
|
-
"<%= config.bin %>
|
|
184
|
+
"<%= config.bin %> domain",
|
|
185
|
+
"<%= config.bin %> domain --verify",
|
|
186
|
+
"<%= config.bin %> domain --status"
|
|
199
187
|
],
|
|
200
188
|
"flags": {
|
|
201
189
|
"json": {
|
|
@@ -210,11 +198,23 @@
|
|
|
210
198
|
"name": "yes",
|
|
211
199
|
"allowNo": false,
|
|
212
200
|
"type": "boolean"
|
|
201
|
+
},
|
|
202
|
+
"status": {
|
|
203
|
+
"description": "Show domain health status",
|
|
204
|
+
"name": "status",
|
|
205
|
+
"allowNo": false,
|
|
206
|
+
"type": "boolean"
|
|
207
|
+
},
|
|
208
|
+
"verify": {
|
|
209
|
+
"description": "Verify DNS records",
|
|
210
|
+
"name": "verify",
|
|
211
|
+
"allowNo": false,
|
|
212
|
+
"type": "boolean"
|
|
213
213
|
}
|
|
214
214
|
},
|
|
215
215
|
"hasDynamicHelp": false,
|
|
216
216
|
"hiddenAliases": [],
|
|
217
|
-
"id": "
|
|
217
|
+
"id": "domain",
|
|
218
218
|
"pluginAlias": "@mailmodo/cli",
|
|
219
219
|
"pluginName": "@mailmodo/cli",
|
|
220
220
|
"pluginType": "core",
|
|
@@ -224,7 +224,7 @@
|
|
|
224
224
|
"relativePath": [
|
|
225
225
|
"dist",
|
|
226
226
|
"commands",
|
|
227
|
-
"
|
|
227
|
+
"domain",
|
|
228
228
|
"index.js"
|
|
229
229
|
]
|
|
230
230
|
},
|
|
@@ -442,19 +442,15 @@
|
|
|
442
442
|
"index.js"
|
|
443
443
|
]
|
|
444
444
|
},
|
|
445
|
-
"
|
|
445
|
+
"logs": {
|
|
446
446
|
"aliases": [],
|
|
447
|
-
"args": {
|
|
448
|
-
|
|
449
|
-
"description": "Email template ID to preview",
|
|
450
|
-
"name": "id"
|
|
451
|
-
}
|
|
452
|
-
},
|
|
453
|
-
"description": "Preview an email in browser, as text, or send a test",
|
|
447
|
+
"args": {},
|
|
448
|
+
"description": "View email send logs and delivery events",
|
|
454
449
|
"examples": [
|
|
455
|
-
"<%= config.bin %>
|
|
456
|
-
"<%= config.bin %>
|
|
457
|
-
"<%= config.bin %>
|
|
450
|
+
"<%= config.bin %> logs",
|
|
451
|
+
"<%= config.bin %> logs --email sarah@example.com",
|
|
452
|
+
"<%= config.bin %> logs --failed",
|
|
453
|
+
"<%= config.bin %> logs --json"
|
|
458
454
|
],
|
|
459
455
|
"flags": {
|
|
460
456
|
"json": {
|
|
@@ -470,23 +466,39 @@
|
|
|
470
466
|
"allowNo": false,
|
|
471
467
|
"type": "boolean"
|
|
472
468
|
},
|
|
473
|
-
"
|
|
474
|
-
"description": "
|
|
475
|
-
"name": "
|
|
469
|
+
"email": {
|
|
470
|
+
"description": "Filter logs by contact email",
|
|
471
|
+
"name": "email",
|
|
476
472
|
"hasDynamicHelp": false,
|
|
477
473
|
"multiple": false,
|
|
478
474
|
"type": "option"
|
|
479
475
|
},
|
|
480
|
-
"
|
|
481
|
-
"description": "
|
|
482
|
-
"name": "
|
|
476
|
+
"failed": {
|
|
477
|
+
"description": "Show only failed/bounced events",
|
|
478
|
+
"name": "failed",
|
|
483
479
|
"allowNo": false,
|
|
484
480
|
"type": "boolean"
|
|
481
|
+
},
|
|
482
|
+
"limit": {
|
|
483
|
+
"description": "Entries per page (max 200)",
|
|
484
|
+
"name": "limit",
|
|
485
|
+
"default": 50,
|
|
486
|
+
"hasDynamicHelp": false,
|
|
487
|
+
"multiple": false,
|
|
488
|
+
"type": "option"
|
|
489
|
+
},
|
|
490
|
+
"page": {
|
|
491
|
+
"description": "Page number",
|
|
492
|
+
"name": "page",
|
|
493
|
+
"default": 1,
|
|
494
|
+
"hasDynamicHelp": false,
|
|
495
|
+
"multiple": false,
|
|
496
|
+
"type": "option"
|
|
485
497
|
}
|
|
486
498
|
},
|
|
487
499
|
"hasDynamicHelp": false,
|
|
488
500
|
"hiddenAliases": [],
|
|
489
|
-
"id": "
|
|
501
|
+
"id": "logs",
|
|
490
502
|
"pluginAlias": "@mailmodo/cli",
|
|
491
503
|
"pluginName": "@mailmodo/cli",
|
|
492
504
|
"pluginType": "core",
|
|
@@ -496,19 +508,23 @@
|
|
|
496
508
|
"relativePath": [
|
|
497
509
|
"dist",
|
|
498
510
|
"commands",
|
|
499
|
-
"
|
|
511
|
+
"logs",
|
|
500
512
|
"index.js"
|
|
501
513
|
]
|
|
502
514
|
},
|
|
503
|
-
"
|
|
515
|
+
"preview": {
|
|
504
516
|
"aliases": [],
|
|
505
|
-
"args": {
|
|
506
|
-
|
|
517
|
+
"args": {
|
|
518
|
+
"id": {
|
|
519
|
+
"description": "Email template ID to preview",
|
|
520
|
+
"name": "id"
|
|
521
|
+
}
|
|
522
|
+
},
|
|
523
|
+
"description": "Preview an email in browser, as text, or send a test",
|
|
507
524
|
"examples": [
|
|
508
|
-
"<%= config.bin %>
|
|
509
|
-
"<%= config.bin %>
|
|
510
|
-
"<%= config.bin %>
|
|
511
|
-
"<%= config.bin %> logs --json"
|
|
525
|
+
"<%= config.bin %> preview welcome",
|
|
526
|
+
"<%= config.bin %> preview welcome --text",
|
|
527
|
+
"<%= config.bin %> preview welcome --send me@example.com"
|
|
512
528
|
],
|
|
513
529
|
"flags": {
|
|
514
530
|
"json": {
|
|
@@ -524,39 +540,23 @@
|
|
|
524
540
|
"allowNo": false,
|
|
525
541
|
"type": "boolean"
|
|
526
542
|
},
|
|
527
|
-
"
|
|
528
|
-
"description": "
|
|
529
|
-
"name": "
|
|
543
|
+
"send": {
|
|
544
|
+
"description": "Send test email to this address",
|
|
545
|
+
"name": "send",
|
|
530
546
|
"hasDynamicHelp": false,
|
|
531
547
|
"multiple": false,
|
|
532
548
|
"type": "option"
|
|
533
549
|
},
|
|
534
|
-
"
|
|
535
|
-
"description": "
|
|
536
|
-
"name": "
|
|
550
|
+
"text": {
|
|
551
|
+
"description": "Output plain text version (for AI agents)",
|
|
552
|
+
"name": "text",
|
|
537
553
|
"allowNo": false,
|
|
538
554
|
"type": "boolean"
|
|
539
|
-
},
|
|
540
|
-
"limit": {
|
|
541
|
-
"description": "Entries per page (max 200)",
|
|
542
|
-
"name": "limit",
|
|
543
|
-
"default": 50,
|
|
544
|
-
"hasDynamicHelp": false,
|
|
545
|
-
"multiple": false,
|
|
546
|
-
"type": "option"
|
|
547
|
-
},
|
|
548
|
-
"page": {
|
|
549
|
-
"description": "Page number",
|
|
550
|
-
"name": "page",
|
|
551
|
-
"default": 1,
|
|
552
|
-
"hasDynamicHelp": false,
|
|
553
|
-
"multiple": false,
|
|
554
|
-
"type": "option"
|
|
555
555
|
}
|
|
556
556
|
},
|
|
557
557
|
"hasDynamicHelp": false,
|
|
558
558
|
"hiddenAliases": [],
|
|
559
|
-
"id": "
|
|
559
|
+
"id": "preview",
|
|
560
560
|
"pluginAlias": "@mailmodo/cli",
|
|
561
561
|
"pluginName": "@mailmodo/cli",
|
|
562
562
|
"pluginType": "core",
|
|
@@ -566,7 +566,7 @@
|
|
|
566
566
|
"relativePath": [
|
|
567
567
|
"dist",
|
|
568
568
|
"commands",
|
|
569
|
-
"
|
|
569
|
+
"preview",
|
|
570
570
|
"index.js"
|
|
571
571
|
]
|
|
572
572
|
},
|
|
@@ -657,5 +657,5 @@
|
|
|
657
657
|
]
|
|
658
658
|
}
|
|
659
659
|
},
|
|
660
|
-
"version": "0.0.30-beta.pr32.
|
|
660
|
+
"version": "0.0.30-beta.pr32.53"
|
|
661
661
|
}
|
package/package.json
CHANGED