@mailmodo/cli 0.0.55 → 0.0.56-beta.pr58.100
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/dist/commands/billing/index.d.ts +1 -11
- package/dist/commands/billing/index.js +28 -184
- package/dist/commands/contacts/index.d.ts +1 -19
- package/dist/commands/contacts/index.js +21 -114
- package/dist/commands/deploy/index.js +12 -7
- package/dist/commands/deployments/index.d.ts +1 -4
- package/dist/commands/deployments/index.js +11 -52
- package/dist/commands/domain/index.d.ts +1 -14
- package/dist/commands/domain/index.js +19 -100
- package/dist/commands/edit/index.d.ts +2 -20
- package/dist/commands/edit/index.js +33 -258
- package/dist/commands/emails/index.d.ts +1 -2
- package/dist/commands/emails/index.js +26 -91
- package/dist/commands/init/index.d.ts +1 -3
- package/dist/commands/init/index.js +47 -199
- package/dist/commands/login/index.d.ts +2 -0
- package/dist/commands/login/index.js +32 -79
- package/dist/commands/logs/index.d.ts +1 -8
- package/dist/commands/logs/index.js +12 -55
- package/dist/commands/preview/index.d.ts +1 -19
- package/dist/commands/preview/index.js +32 -212
- package/dist/commands/sdk/index.d.ts +1 -3
- package/dist/commands/sdk/index.js +14 -46
- package/dist/commands/settings/index.d.ts +1 -22
- package/dist/commands/settings/index.js +34 -246
- package/dist/commands/status/index.d.ts +1 -0
- package/dist/commands/status/index.js +13 -39
- package/dist/lib/base-command.d.ts +35 -10
- package/dist/lib/base-command.js +169 -17
- package/dist/lib/commands/billing/checkout-status.d.ts +3 -0
- package/dist/lib/commands/billing/checkout-status.js +63 -0
- package/dist/lib/commands/billing/format.d.ts +7 -0
- package/dist/lib/commands/billing/format.js +63 -0
- package/dist/lib/commands/billing/purchase-cap.d.ts +7 -0
- package/dist/lib/commands/billing/purchase-cap.js +57 -0
- package/dist/lib/commands/billing/types.d.ts +72 -0
- package/dist/lib/commands/contacts/actions.d.ts +3 -0
- package/dist/lib/commands/contacts/actions.js +49 -0
- package/dist/lib/commands/contacts/export-delete.d.ts +9 -0
- package/dist/lib/commands/contacts/export-delete.js +51 -0
- package/dist/lib/commands/contacts/types.d.ts +35 -0
- package/dist/lib/commands/contacts/types.js +1 -0
- package/dist/lib/{deploy → commands/deploy}/domain-setup.d.ts +1 -1
- package/dist/lib/{deploy → commands/deploy}/domain-setup.js +2 -2
- package/dist/lib/{deploy → commands/deploy}/output.d.ts +1 -1
- package/dist/lib/{deploy → commands/deploy}/output.js +2 -2
- package/dist/lib/{deploy → commands/deploy}/payload.d.ts +1 -1
- package/dist/lib/{deploy → commands/deploy}/payload.js +2 -2
- package/dist/lib/{deploy → commands/deploy}/sequence-status.js +2 -2
- package/dist/lib/{deploy → commands/deploy}/types.d.ts +4 -4
- package/dist/lib/commands/deploy/types.js +1 -0
- package/dist/lib/commands/deployments/output.d.ts +2 -0
- package/dist/lib/commands/deployments/output.js +68 -0
- package/dist/lib/commands/deployments/types.d.ts +24 -0
- package/dist/lib/commands/deployments/types.js +1 -0
- package/dist/lib/commands/domain/setup.d.ts +8 -0
- package/dist/lib/commands/domain/setup.js +53 -0
- package/dist/lib/commands/domain/types.d.ts +56 -0
- package/dist/lib/commands/domain/types.js +1 -0
- package/dist/lib/commands/domain/verify.d.ts +5 -0
- package/dist/lib/commands/domain/verify.js +50 -0
- package/dist/lib/commands/edit/diff.d.ts +7 -0
- package/dist/lib/commands/edit/diff.js +65 -0
- package/dist/lib/commands/edit/display.d.ts +5 -0
- package/dist/lib/commands/edit/display.js +53 -0
- package/dist/lib/commands/edit/flow.d.ts +8 -0
- package/dist/lib/commands/edit/flow.js +70 -0
- package/dist/lib/commands/edit/persist.d.ts +5 -0
- package/dist/lib/commands/edit/persist.js +67 -0
- package/dist/lib/commands/edit/types.d.ts +38 -0
- package/dist/lib/commands/edit/types.js +1 -0
- package/dist/lib/commands/emails/editor.d.ts +2 -0
- package/dist/lib/commands/emails/editor.js +43 -0
- package/dist/lib/commands/emails/output.d.ts +4 -0
- package/dist/lib/commands/emails/output.js +36 -0
- package/dist/lib/commands/emails/types.d.ts +3 -0
- package/dist/lib/commands/emails/types.js +1 -0
- package/dist/lib/commands/init/analysis.d.ts +3 -0
- package/dist/lib/commands/init/analysis.js +73 -0
- package/dist/lib/commands/init/output.d.ts +12 -0
- package/dist/lib/commands/init/output.js +39 -0
- package/dist/lib/commands/init/payload.d.ts +8 -0
- package/dist/lib/commands/init/payload.js +78 -0
- package/dist/lib/commands/init/types.d.ts +57 -0
- package/dist/lib/commands/init/types.js +1 -0
- package/dist/lib/commands/login/output.d.ts +8 -0
- package/dist/lib/commands/login/output.js +40 -0
- package/dist/lib/commands/login/types.d.ts +19 -0
- package/dist/lib/commands/login/types.js +1 -0
- package/dist/lib/commands/logs/output.d.ts +2 -0
- package/dist/lib/commands/logs/output.js +52 -0
- package/dist/lib/commands/logs/types.d.ts +23 -0
- package/dist/lib/commands/logs/types.js +1 -0
- package/dist/lib/commands/preview/actions.d.ts +11 -0
- package/dist/lib/commands/preview/actions.js +43 -0
- package/dist/lib/commands/preview/render.d.ts +3 -0
- package/dist/lib/commands/preview/render.js +30 -0
- package/dist/lib/commands/preview/server.d.ts +8 -0
- package/dist/lib/commands/preview/server.js +63 -0
- package/dist/lib/commands/preview/types.d.ts +22 -0
- package/dist/lib/commands/preview/types.js +1 -0
- package/dist/lib/commands/preview/wrapper-html.d.ts +2 -0
- package/dist/lib/commands/preview/wrapper-html.js +35 -0
- package/dist/lib/commands/sdk/output.d.ts +2 -0
- package/dist/lib/commands/sdk/output.js +42 -0
- package/dist/lib/commands/sdk/types.d.ts +21 -0
- package/dist/lib/commands/sdk/types.js +1 -0
- package/dist/lib/commands/settings/actions.d.ts +10 -0
- package/dist/lib/commands/settings/actions.js +56 -0
- package/dist/lib/commands/settings/display.d.ts +15 -0
- package/dist/lib/commands/settings/display.js +69 -0
- package/dist/lib/commands/settings/logo-domain.d.ts +3 -0
- package/dist/lib/commands/settings/logo-domain.js +47 -0
- package/dist/lib/commands/settings/prompt.d.ts +2 -0
- package/dist/lib/commands/settings/prompt.js +82 -0
- package/dist/lib/commands/settings/types.d.ts +65 -0
- package/dist/lib/commands/settings/types.js +1 -0
- package/dist/lib/commands/status/output.d.ts +2 -0
- package/dist/lib/commands/status/output.js +49 -0
- package/dist/lib/commands/status/types.d.ts +28 -0
- package/dist/lib/commands/status/types.js +1 -0
- package/dist/lib/constants.d.ts +3 -2
- package/dist/lib/constants.js +4 -5
- package/dist/lib/messages.d.ts +11 -0
- package/dist/lib/messages.js +31 -0
- package/dist/lib/templates/missing-templates.d.ts +16 -2
- package/dist/lib/templates/missing-templates.js +34 -22
- package/dist/lib/templates/regenerate.d.ts +10 -0
- package/dist/lib/templates/regenerate.js +29 -0
- package/dist/lib/templates/sync.d.ts +33 -0
- package/dist/lib/templates/sync.js +106 -0
- package/dist/lib/templates/types.d.ts +3 -0
- package/oclif.manifest.json +54 -54
- package/package.json +1 -1
- /package/dist/lib/{deploy → commands/billing}/types.js +0 -0
- /package/dist/lib/{deploy → commands/deploy}/sequence-status.d.ts +0 -0
|
@@ -1,57 +1,10 @@
|
|
|
1
1
|
import { Args, Flags } from '@oclif/core';
|
|
2
|
-
import { createServer } from 'node:http';
|
|
3
|
-
import chalk from 'chalk';
|
|
4
|
-
import open from 'open';
|
|
5
2
|
import { BaseCommand } from '../../lib/base-command.js';
|
|
6
|
-
import { API_ENDPOINTS, PREVIEW_PORT } from '../../lib/constants.js';
|
|
7
|
-
import { INFO } from '../../lib/messages.js';
|
|
8
|
-
import { loadTemplate, getEmailStyle, getTemplateFilename, } from '../../lib/yaml-config.js';
|
|
9
3
|
import { handleMissingTemplates } from '../../lib/templates/missing-templates.js';
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
first_name: 'Sarah',
|
|
15
|
-
product_name: 'YourApp',
|
|
16
|
-
unsubscribe_url: '#',
|
|
17
|
-
});
|
|
18
|
-
/* eslint-enable camelcase */
|
|
19
|
-
/**
|
|
20
|
-
* Replaces all {{variable}} template placeholders in the HTML string
|
|
21
|
-
* with corresponding values from the sample data map.
|
|
22
|
-
*
|
|
23
|
-
* @param {string} html - Raw HTML template with {{variable}} placeholders.
|
|
24
|
-
* @param {Record<string, string>} data - Key-value map of template variable replacements.
|
|
25
|
-
* @returns {string} HTML with all recognized placeholders replaced.
|
|
26
|
-
*/
|
|
27
|
-
function renderTemplate(html, data) {
|
|
28
|
-
return html.replaceAll(/\{\{(\w+)\}\}/g, (match, key) => data[key] || match);
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* Strips HTML tags and decodes common HTML entities to produce
|
|
32
|
-
* a readable plain text representation of an email.
|
|
33
|
-
*
|
|
34
|
-
* @param {string} html - The HTML content to convert.
|
|
35
|
-
* @returns {string} Plain text with tags removed and entities decoded.
|
|
36
|
-
*/
|
|
37
|
-
function htmlToText(html) {
|
|
38
|
-
return html
|
|
39
|
-
.replaceAll(/<style[^>]*>[\s\S]*?<\/style>/gi, '')
|
|
40
|
-
.replaceAll(/<script[^>]*>[\s\S]*?<\/script>/gi, '')
|
|
41
|
-
.replaceAll(/<br\s*\/?>/gi, '\n')
|
|
42
|
-
.replaceAll(/<\/p>/gi, '\n\n')
|
|
43
|
-
.replaceAll(/<\/div>/gi, '\n')
|
|
44
|
-
.replaceAll(/<\/tr>/gi, '\n')
|
|
45
|
-
.replaceAll(/<\/li>/gi, '\n')
|
|
46
|
-
.replaceAll(/<[^>]+>/g, '')
|
|
47
|
-
.replaceAll(' ', ' ')
|
|
48
|
-
.replaceAll('&', '&')
|
|
49
|
-
.replaceAll('<', '<')
|
|
50
|
-
.replaceAll('>', '>')
|
|
51
|
-
.replaceAll('"', '"')
|
|
52
|
-
.replaceAll(/\n{3,}/g, '\n\n')
|
|
53
|
-
.trim();
|
|
54
|
-
}
|
|
4
|
+
import { loadTemplate, getEmailStyle, getTemplateFilename, } from '../../lib/yaml-config.js';
|
|
5
|
+
import { renderText, sendTestEmail, } from '../../lib/commands/preview/actions.js';
|
|
6
|
+
import { SAMPLE_DATA, renderTemplate, } from '../../lib/commands/preview/render.js';
|
|
7
|
+
import { startPreviewServer } from '../../lib/commands/preview/server.js';
|
|
55
8
|
export default class Preview extends BaseCommand {
|
|
56
9
|
static args = {
|
|
57
10
|
id: Args.string({ description: 'Email template ID to preview' }),
|
|
@@ -81,34 +34,29 @@ export default class Preview extends BaseCommand {
|
|
|
81
34
|
if (!email) {
|
|
82
35
|
this.error(`Template '${templateId}' not found in mailmodo.yaml.`);
|
|
83
36
|
}
|
|
37
|
+
/* eslint-disable camelcase */
|
|
84
38
|
const sampleData = {
|
|
85
39
|
...SAMPLE_DATA,
|
|
86
|
-
app_url: yamlConfig.project?.url || 'https://yourapp.com',
|
|
87
|
-
product_name: yamlConfig.project?.name || 'YourApp',
|
|
40
|
+
app_url: yamlConfig.project?.url || 'https://yourapp.com',
|
|
41
|
+
product_name: yamlConfig.project?.name || 'YourApp',
|
|
88
42
|
};
|
|
43
|
+
/* eslint-enable camelcase */
|
|
89
44
|
const effectiveStyle = getEmailStyle(email.style, yamlConfig.project?.emailStyle);
|
|
90
45
|
const templateFilename = getTemplateFilename(email.id, email.style, yamlConfig.project?.emailStyle);
|
|
91
46
|
let templateHtml = await loadTemplate(templateFilename);
|
|
92
47
|
if (!templateHtml) {
|
|
93
48
|
await this.ensureAuth();
|
|
94
|
-
const
|
|
95
|
-
error: (msg) => this.error(msg),
|
|
96
|
-
exit: (code) => this.exit(code),
|
|
97
|
-
log: (msg) => this.log(msg),
|
|
98
|
-
onApiError: (r) => this.handleApiError(r),
|
|
99
|
-
post: (path, body) => this.apiClient.post(path, body),
|
|
100
|
-
spinner: (text, json, work) => this.withApiSpinner({ json, text }, work),
|
|
101
|
-
syncYaml: () => this.syncYamlToServer(),
|
|
102
|
-
};
|
|
103
|
-
const regenerated = await handleMissingTemplates(regenCtx, yamlConfig, [email.id], { json: flags.json, yes: flags.yes });
|
|
49
|
+
const regenerated = await handleMissingTemplates(this.makeCtx(), yamlConfig, [email.id], { json: flags.json, yes: flags.yes });
|
|
104
50
|
if (!regenerated)
|
|
105
51
|
return;
|
|
106
52
|
templateHtml = await loadTemplate(templateFilename);
|
|
107
53
|
if (!templateHtml)
|
|
108
54
|
this.error('Template regeneration failed.');
|
|
109
55
|
}
|
|
56
|
+
const ctx = this.makeCtx();
|
|
110
57
|
if (flags.send) {
|
|
111
|
-
await this.
|
|
58
|
+
await this.ensureAuth();
|
|
59
|
+
await sendTestEmail(ctx, email, renderTemplate(templateHtml, sampleData), {
|
|
112
60
|
domain: yamlConfig.project?.domain,
|
|
113
61
|
jsonOutput: flags.json,
|
|
114
62
|
toAddress: flags.send,
|
|
@@ -116,159 +64,31 @@ export default class Preview extends BaseCommand {
|
|
|
116
64
|
return;
|
|
117
65
|
}
|
|
118
66
|
if (flags.text) {
|
|
119
|
-
await
|
|
67
|
+
await renderText(ctx, email, {
|
|
68
|
+
jsonOutput: flags.json,
|
|
69
|
+
sampleData,
|
|
70
|
+
templateHtml,
|
|
71
|
+
});
|
|
120
72
|
return;
|
|
121
73
|
}
|
|
122
|
-
await
|
|
74
|
+
await startPreviewServer(ctx, email, {
|
|
123
75
|
effectiveStyle,
|
|
124
76
|
jsonOutput: flags.json,
|
|
77
|
+
rendered: renderTemplate(templateHtml, sampleData),
|
|
78
|
+
sampleData,
|
|
125
79
|
});
|
|
126
80
|
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
this.
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
}, null, 2));
|
|
140
|
-
return;
|
|
141
|
-
}
|
|
142
|
-
this.log(`\n ${chalk.bold('SUBJECT:')} ${email.subject}`);
|
|
143
|
-
if (email.previewText) {
|
|
144
|
-
this.log(` ${chalk.bold('PREVIEW:')} ${email.previewText}`);
|
|
145
|
-
}
|
|
146
|
-
this.log(`\n${plainText}\n`);
|
|
147
|
-
}
|
|
148
|
-
/**
|
|
149
|
-
* Calls the API to send a test email to the specified address.
|
|
150
|
-
* Before domain verification, tests send via the mailmodo.com domain.
|
|
151
|
-
*/
|
|
152
|
-
async sendTestEmail(email, html, opts) {
|
|
153
|
-
const { domain, jsonOutput, toAddress } = opts;
|
|
154
|
-
await this.ensureAuth();
|
|
155
|
-
const response = await this.withApiSpinner({ json: jsonOutput, text: ' Sending test email...' }, () => this.apiClient.post(`${API_ENDPOINTS.PREVIEW}/send`, {
|
|
156
|
-
domain,
|
|
157
|
-
html,
|
|
158
|
-
subject: email.subject,
|
|
159
|
-
to: toAddress,
|
|
160
|
-
}));
|
|
161
|
-
if (!response.ok) {
|
|
162
|
-
this.handleApiError(response);
|
|
163
|
-
}
|
|
164
|
-
const { note, sentTo, sentVia, status } = response.data;
|
|
165
|
-
if (jsonOutput) {
|
|
166
|
-
this.log(JSON.stringify({ note, sentTo, sentVia, status }, null, 2));
|
|
167
|
-
return;
|
|
168
|
-
}
|
|
169
|
-
this.log(`\n ${chalk.green('✓')} Test email sent to ${chalk.cyan(sentTo)} via ${chalk.cyan(sentVia)}.`);
|
|
170
|
-
if (note) {
|
|
171
|
-
this.log(` ${chalk.dim(note)}`);
|
|
172
|
-
}
|
|
173
|
-
this.log('');
|
|
174
|
-
}
|
|
175
|
-
/**
|
|
176
|
-
* Probes ports starting at startPort and returns the first one not in use.
|
|
177
|
-
*/
|
|
178
|
-
async findAvailablePort(startPort, endPort = startPort + 10) {
|
|
179
|
-
if (startPort > endPort) {
|
|
180
|
-
throw new Error(`No available port found starting from port ${endPort - 10}`);
|
|
181
|
-
}
|
|
182
|
-
const available = await new Promise((resolve) => {
|
|
183
|
-
const probe = createServer();
|
|
184
|
-
probe.once('error', () => resolve(false));
|
|
185
|
-
probe.once('listening', () => probe.close(() => resolve(true)));
|
|
186
|
-
probe.listen(startPort);
|
|
187
|
-
});
|
|
188
|
-
return available
|
|
189
|
-
? startPort
|
|
190
|
-
: this.findAvailablePort(startPort + 1, endPort);
|
|
191
|
-
}
|
|
192
|
-
/**
|
|
193
|
-
* Starts a local HTTP server to serve the rendered email template,
|
|
194
|
-
* then opens the user's default browser to view it.
|
|
195
|
-
*/
|
|
196
|
-
async startPreviewServer(email, templateHtml, sampleData, opts) {
|
|
197
|
-
const { effectiveStyle, jsonOutput } = opts;
|
|
198
|
-
const rendered = renderTemplate(templateHtml, sampleData);
|
|
199
|
-
const wrapperHtml = `<!DOCTYPE html>
|
|
200
|
-
<html>
|
|
201
|
-
<head>
|
|
202
|
-
<meta charset="utf-8">
|
|
203
|
-
<title>Preview: ${email.subject}</title>
|
|
204
|
-
<style>
|
|
205
|
-
body { margin: 0; padding: 2rem; background: #f5f5f5; font-family: system-ui; }
|
|
206
|
-
.preview-bar { background: #1a1a2e; color: #fff; padding: 0.75rem 1.5rem; border-radius: 0.5rem;
|
|
207
|
-
margin-bottom: 1.5rem; display: flex; justify-content: space-between; align-items: center; }
|
|
208
|
-
.preview-bar h3 { margin: 0; font-size: 0.875rem; }
|
|
209
|
-
.preview-bar span { font-size: 0.75rem; opacity: 0.7; }
|
|
210
|
-
.email-frame { background: #fff; max-width: 40rem; margin: 0 auto; border-radius: 0.5rem;
|
|
211
|
-
box-shadow: 0 0.125rem 0.5rem rgba(0,0,0,0.1); overflow: hidden; }
|
|
212
|
-
.email-header { padding: 1rem 1.5rem; border-bottom: 1px solid #eee; }
|
|
213
|
-
.email-header .subject { font-weight: 600; font-size: 1rem; }
|
|
214
|
-
.email-header .meta { font-size: 0.75rem; color: #666; margin-top: 0.25rem; }
|
|
215
|
-
.email-body { padding: 1.5rem; }
|
|
216
|
-
</style>
|
|
217
|
-
</head>
|
|
218
|
-
<body>
|
|
219
|
-
<div class="preview-bar">
|
|
220
|
-
<h3>Mailmodo Preview — ${email.id}</h3>
|
|
221
|
-
<span>Style: ${effectiveStyle} · Press Ctrl+C in terminal to stop</span>
|
|
222
|
-
</div>
|
|
223
|
-
<div class="email-frame">
|
|
224
|
-
<div class="email-header">
|
|
225
|
-
<div class="subject">${email.subject}</div>
|
|
226
|
-
<div class="meta">To: ${sampleData.first_name} · From: ${sampleData.product_name}</div>
|
|
227
|
-
</div>
|
|
228
|
-
<div class="email-body">${rendered}</div>
|
|
229
|
-
</div>
|
|
230
|
-
</body>
|
|
231
|
-
</html>`;
|
|
232
|
-
const port = await this.findAvailablePort(PREVIEW_PORT);
|
|
233
|
-
if (!jsonOutput && port !== PREVIEW_PORT) {
|
|
234
|
-
this.log(`\n ${chalk.yellow('!')} Port ${PREVIEW_PORT} is already in use. Opening preview on port ${chalk.cyan(String(port))}.`);
|
|
235
|
-
}
|
|
236
|
-
if (jsonOutput) {
|
|
237
|
-
this.log(JSON.stringify({
|
|
238
|
-
id: email.id,
|
|
239
|
-
style: effectiveStyle,
|
|
240
|
-
url: `http://localhost:${port}`,
|
|
241
|
-
}, null, 2));
|
|
242
|
-
}
|
|
243
|
-
const server = createServer((_req, res) => {
|
|
244
|
-
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
245
|
-
res.end(wrapperHtml);
|
|
246
|
-
});
|
|
247
|
-
await new Promise((resolve) => {
|
|
248
|
-
server.listen(port, () => resolve());
|
|
249
|
-
});
|
|
250
|
-
const url = `http://localhost:${port}`;
|
|
251
|
-
if (!jsonOutput) {
|
|
252
|
-
this.log(`\n Style: ${chalk.cyan(effectiveStyle)}`);
|
|
253
|
-
this.log(` Preview server at ${chalk.cyan(url)}`);
|
|
254
|
-
this.log(` ${INFO.BROWSER_OPENING}\n`);
|
|
255
|
-
this.log(` ${chalk.dim('Press Ctrl+C to stop the preview server.')}\n`);
|
|
256
|
-
}
|
|
257
|
-
try {
|
|
258
|
-
await open(url);
|
|
259
|
-
}
|
|
260
|
-
catch {
|
|
261
|
-
if (!jsonOutput) {
|
|
262
|
-
this.log(` ${INFO.BROWSER_OPEN_FAILED}`);
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
await new Promise((resolve) => {
|
|
266
|
-
const shutdown = () => {
|
|
267
|
-
server.closeAllConnections?.();
|
|
268
|
-
server.close(() => resolve());
|
|
269
|
-
};
|
|
270
|
-
process.once('SIGINT', shutdown);
|
|
271
|
-
process.once('SIGTERM', shutdown);
|
|
272
|
-
});
|
|
81
|
+
makeCtx() {
|
|
82
|
+
return {
|
|
83
|
+
error: (msg) => this.error(msg),
|
|
84
|
+
exit: (code) => this.exit(code),
|
|
85
|
+
fetchTemplate: (emailId) => this.getTemplateFromServer(emailId),
|
|
86
|
+
log: (msg) => this.log(msg),
|
|
87
|
+
onApiError: (r) => this.handleApiError(r),
|
|
88
|
+
post: (path, body) => this.apiClient.post(path, body),
|
|
89
|
+
spinner: (text, json, work) => this.withApiSpinner({ json, text }, work),
|
|
90
|
+
syncTemplates: (yaml) => this.syncTemplatesToServer(yaml),
|
|
91
|
+
syncYaml: () => this.syncYamlToServer(),
|
|
92
|
+
};
|
|
273
93
|
}
|
|
274
94
|
}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { Flags } from '@oclif/core';
|
|
2
|
-
import chalk from 'chalk';
|
|
3
2
|
import { BaseCommand } from '../../lib/base-command.js';
|
|
4
|
-
import { API_ENDPOINTS
|
|
5
|
-
import {
|
|
3
|
+
import { API_ENDPOINTS } from '../../lib/constants.js';
|
|
4
|
+
import { renderSdkSnippets } from '../../lib/commands/sdk/output.js';
|
|
6
5
|
export default class Sdk extends BaseCommand {
|
|
7
6
|
static description = 'Show the SDK track() / identify() reference for deployed sequences';
|
|
8
7
|
static examples = [
|
|
@@ -19,56 +18,25 @@ export default class Sdk extends BaseCommand {
|
|
|
19
18
|
async run() {
|
|
20
19
|
const { flags } = await this.parse(Sdk);
|
|
21
20
|
await this.ensureAuth();
|
|
21
|
+
const ctx = this.makeCtx();
|
|
22
22
|
const params = flags['sequence-id']
|
|
23
23
|
? { sequenceId: flags['sequence-id'] }
|
|
24
24
|
: undefined;
|
|
25
|
-
const response = await
|
|
26
|
-
if (!response.ok)
|
|
27
|
-
|
|
28
|
-
}
|
|
25
|
+
const response = await ctx.spinner(' Loading SDK reference...', flags.json, () => ctx.get(API_ENDPOINTS.SEQUENCES_SDK, params));
|
|
26
|
+
if (!response.ok)
|
|
27
|
+
ctx.onApiError(response);
|
|
29
28
|
if (flags.json) {
|
|
30
29
|
this.log(JSON.stringify(response.data, null, 2));
|
|
31
30
|
return;
|
|
32
31
|
}
|
|
33
|
-
|
|
34
|
-
}
|
|
35
|
-
renderSnippets(data) {
|
|
36
|
-
const snippets = data.sdkSnippets ?? [];
|
|
37
|
-
if (snippets.length === 0) {
|
|
38
|
-
this.log(`\n ${chalk.dim('No active deployed sequences.')}`);
|
|
39
|
-
this.log(` Run ${chalk.cyan('mailmodo deploy')} to deploy one.\n`);
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
42
|
-
this.log(`\n ${chalk.bold(String(snippets.length))} active ${snippets.length === 1 ? 'sequence' : 'sequences'}:\n`);
|
|
43
|
-
this.log(` ${SEPARATOR}`);
|
|
44
|
-
this.log(` ${chalk.bold('SDK EVENT REFERENCE')}`);
|
|
45
|
-
this.log(` ${SEPARATOR}\n`);
|
|
46
|
-
this.log(` ${chalk.cyan(SDK_INSTALL_COMMAND)}\n`);
|
|
47
|
-
this.log(` ${chalk.dim(SDK_IMPORT_SNIPPET)}\n`);
|
|
48
|
-
for (const [index, snippet] of snippets.entries()) {
|
|
49
|
-
this.renderSequenceBlock(snippet);
|
|
50
|
-
if (index < snippets.length - 1)
|
|
51
|
-
this.log('');
|
|
52
|
-
}
|
|
53
|
-
this.log(` ${SEPARATOR}\n`);
|
|
32
|
+
renderSdkSnippets(ctx, response.data);
|
|
54
33
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
if (trackCalls.length === 0 && identifyCalls.length === 0) {
|
|
63
|
-
this.log(` ${chalk.dim('No track() or identify() calls available.')}`);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
renderCallBlock(label, calls) {
|
|
67
|
-
if (calls.length === 0)
|
|
68
|
-
return;
|
|
69
|
-
this.log(` ${chalk.dim(label)}`);
|
|
70
|
-
for (const call of calls) {
|
|
71
|
-
this.log(` ${chalk.dim(call)}`);
|
|
72
|
-
}
|
|
34
|
+
makeCtx() {
|
|
35
|
+
return {
|
|
36
|
+
get: (path, params) => this.apiClient.get(path, params),
|
|
37
|
+
log: (msg) => this.log(msg),
|
|
38
|
+
onApiError: (r) => this.handleApiError(r),
|
|
39
|
+
spinner: (text, json, work) => this.withApiSpinner({ json, text }, work),
|
|
40
|
+
};
|
|
73
41
|
}
|
|
74
42
|
}
|
|
@@ -8,26 +8,5 @@ export default class Settings extends BaseCommand {
|
|
|
8
8
|
yes: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
9
9
|
};
|
|
10
10
|
run(): Promise<void>;
|
|
11
|
-
private
|
|
12
|
-
private applyMonthlyCapChange;
|
|
13
|
-
private displaySettingsGroup;
|
|
14
|
-
/**
|
|
15
|
-
* Prompts the user to pick a setting key to edit and dispatches
|
|
16
|
-
* to the appropriate handler for that key.
|
|
17
|
-
*/
|
|
18
|
-
private promptEditSetting;
|
|
19
|
-
/**
|
|
20
|
-
* Fetches the domain verification status from the API.
|
|
21
|
-
* Returns true/false for verified/unverified, or null if unavailable.
|
|
22
|
-
*/
|
|
23
|
-
private fetchDomainVerified;
|
|
24
|
-
private handleDomainChange;
|
|
25
|
-
/**
|
|
26
|
-
* Handles the logo file upload flow: validates the local file exists,
|
|
27
|
-
* reads it, uploads to Mailmodo CDN via API, and updates both logoFile
|
|
28
|
-
* and logoUrl in the project config.
|
|
29
|
-
*
|
|
30
|
-
* @param {import('../../lib/yaml-config.js').MailmodoYaml} yamlConfig - The full YAML config to update and save.
|
|
31
|
-
*/
|
|
32
|
-
private handleLogoUpload;
|
|
11
|
+
private makeCtx;
|
|
33
12
|
}
|