@mailmodo/cli 0.0.54-beta.pr56.91 → 0.0.54
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.js +6 -9
- package/dist/commands/deploy/index.d.ts +32 -1
- package/dist/commands/deploy/index.js +303 -52
- package/dist/commands/edit/index.js +0 -19
- package/dist/commands/init/index.d.ts +0 -1
- package/dist/commands/init/index.js +3 -25
- package/dist/commands/login/index.js +3 -18
- package/dist/commands/preview/index.js +12 -24
- package/dist/commands/settings/index.js +0 -6
- package/dist/lib/api-client.d.ts +0 -5
- package/dist/lib/api-client.js +0 -45
- package/dist/lib/base-command.d.ts +1 -25
- package/dist/lib/base-command.js +5 -91
- package/dist/lib/constants.d.ts +0 -1
- package/dist/lib/constants.js +0 -1
- package/dist/lib/messages.d.ts +0 -22
- package/dist/lib/messages.js +0 -22
- package/oclif.manifest.json +60 -60
- package/package.json +1 -1
- package/dist/lib/deploy/domain-setup.d.ts +0 -8
- package/dist/lib/deploy/domain-setup.js +0 -82
- package/dist/lib/deploy/output.d.ts +0 -5
- package/dist/lib/deploy/output.js +0 -61
- package/dist/lib/deploy/payload.d.ts +0 -41
- package/dist/lib/deploy/payload.js +0 -95
- package/dist/lib/deploy/sequence-status.d.ts +0 -3
- package/dist/lib/deploy/sequence-status.js +0 -56
- package/dist/lib/deploy/types.d.ts +0 -88
- package/dist/lib/deploy/types.js +0 -1
- package/dist/lib/templates/missing-templates.d.ts +0 -5
- package/dist/lib/templates/missing-templates.js +0 -61
- package/dist/lib/templates/types.d.ts +0 -13
- package/dist/lib/templates/types.js +0 -1
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
import { confirm, input } from '@inquirer/prompts';
|
|
2
|
-
import chalk from 'chalk';
|
|
3
|
-
import { API_ENDPOINTS } from '../constants.js';
|
|
4
|
-
import { ERRORS, INFO, PROMPTS } from '../messages.js';
|
|
5
|
-
export async function validateDeploySequence(ctx, payload, flags) {
|
|
6
|
-
const res = await ctx.spinner(' Validating sequence...', flags.json, () => ctx.post(API_ENDPOINTS.SEQUENCES_VALIDATE, payload));
|
|
7
|
-
if (!res.ok) {
|
|
8
|
-
if (res.data.error === 'senderDomainNotFound')
|
|
9
|
-
ctx.error(ERRORS.DOMAIN_NOT_REGISTERED);
|
|
10
|
-
if (res.data.error === 'senderDomainNotVerified')
|
|
11
|
-
ctx.error(ERRORS.DOMAIN_NOT_VERIFIED);
|
|
12
|
-
ctx.onApiError(res);
|
|
13
|
-
}
|
|
14
|
-
return res.data;
|
|
15
|
-
}
|
|
16
|
-
export async function verifyDomain(ctx, jsonOutput, domain) {
|
|
17
|
-
const res = await ctx.spinner(' Checking DNS...', jsonOutput, () => ctx.get(API_ENDPOINTS.DOMAIN_VERIFY, { domain }));
|
|
18
|
-
if (!res.ok)
|
|
19
|
-
ctx.onApiError(res);
|
|
20
|
-
const { dkim, dmarc, dnsGuideUrl, domainStatus, returnPath } = res.data;
|
|
21
|
-
const allPassed = domainStatus === 'VERIFIED';
|
|
22
|
-
if (!jsonOutput) {
|
|
23
|
-
ctx.log(` DKIM ${dkim ? chalk.green('✓') : chalk.red('✗')}`);
|
|
24
|
-
ctx.log(` DMARC ${dmarc ? chalk.green('✓') : chalk.red('✗')}`);
|
|
25
|
-
ctx.log(` Return-Path ${returnPath ? chalk.green('✓') : chalk.red('✗')}`);
|
|
26
|
-
if (allPassed) {
|
|
27
|
-
ctx.log(`\n ${chalk.green('Domain verified.')} Continuing deploy...\n`);
|
|
28
|
-
}
|
|
29
|
-
else {
|
|
30
|
-
ctx.log(`\n ${INFO.DNS_RECORDS_FAILED}\n ${INFO.DNS_FIX_AND_VERIFY}`);
|
|
31
|
-
if (dnsGuideUrl)
|
|
32
|
-
ctx.log(` Help: ${chalk.cyan(dnsGuideUrl)}\n`);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
return allPassed;
|
|
36
|
-
}
|
|
37
|
-
export async function runDomainSetup(ctx, yamlConfig, flags) {
|
|
38
|
-
const inputs = await ctx.collectDomainInputs(yamlConfig, flags.yes);
|
|
39
|
-
const { dnsRecords, dnsGuideUrl } = await ctx.registerDomainAndSave(yamlConfig, inputs, flags.json);
|
|
40
|
-
ctx.showDnsRecords(dnsRecords, dnsGuideUrl, flags.json);
|
|
41
|
-
if (flags.yes)
|
|
42
|
-
return verifyDomain(ctx, flags.json, inputs.domain);
|
|
43
|
-
const action = await input({
|
|
44
|
-
default: '',
|
|
45
|
-
message: PROMPTS.ENTER_AFTER_RECORDS,
|
|
46
|
-
});
|
|
47
|
-
if (action.toLowerCase() === 'skip') {
|
|
48
|
-
ctx.log(`\n ${INFO.SEQUENCES_NOT_DEPLOYED}\n ${INFO.DOMAIN_NOT_DEPLOYED_HINT}\n`);
|
|
49
|
-
return false;
|
|
50
|
-
}
|
|
51
|
-
return verifyDomain(ctx, flags.json, inputs.domain);
|
|
52
|
-
}
|
|
53
|
-
export async function ensureDomainReady(ctx, yamlConfig, flags) {
|
|
54
|
-
const check = await ctx.spinner(' Checking domain verification...', flags.json, () => ctx.get(API_ENDPOINTS.DOMAIN_VERIFY, {
|
|
55
|
-
domain: yamlConfig.project?.domain || '',
|
|
56
|
-
}));
|
|
57
|
-
if (!check.ok)
|
|
58
|
-
ctx.onApiError(check);
|
|
59
|
-
if (check.data?.domainStatus === 'VERIFIED')
|
|
60
|
-
return true;
|
|
61
|
-
if (yamlConfig.project?.domain) {
|
|
62
|
-
if (!flags.json)
|
|
63
|
-
ctx.log(`\n ${INFO.DOMAIN_PENDING_VERIFICATION}\n`);
|
|
64
|
-
return false;
|
|
65
|
-
}
|
|
66
|
-
if (!flags.json) {
|
|
67
|
-
ctx.log(`\n No sending domain set up yet.`);
|
|
68
|
-
ctx.log(` You need a verified domain before sending emails.`);
|
|
69
|
-
ctx.log(` This is a one-time setup. Takes about 5 minutes.\n`);
|
|
70
|
-
}
|
|
71
|
-
if (!flags.yes) {
|
|
72
|
-
const go = await confirm({
|
|
73
|
-
default: true,
|
|
74
|
-
message: 'Set up your sending domain now?',
|
|
75
|
-
});
|
|
76
|
-
if (!go) {
|
|
77
|
-
ctx.log(`\n ${INFO.SEQUENCES_NOT_DEPLOYED}\n Emails will not send until your domain is verified.\n ${INFO.DOMAIN_NOT_DEPLOYED_HINT}\n`);
|
|
78
|
-
return false;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
return runDomainSetup(ctx, yamlConfig, flags);
|
|
82
|
-
}
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import type { MailmodoYaml } from '../yaml-config.js';
|
|
2
|
-
import type { DeployCtx, SdkSnippet, ValidateResponse } from './types.js';
|
|
3
|
-
export declare function logDiff(ctx: DeployCtx, diff: NonNullable<ValidateResponse['diff']>): void;
|
|
4
|
-
export declare function logPreDeploySummary(ctx: DeployCtx, yamlConfig: MailmodoYaml, validateResult: ValidateResponse, jsonOutput: boolean): void;
|
|
5
|
-
export declare function logDeploySuccessInstructions(ctx: DeployCtx, sdkSnippet: SdkSnippet): void;
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import chalk from 'chalk';
|
|
2
|
-
import { SDK_IMPORT_SNIPPET, SDK_INSTALL_COMMAND } from '../constants.js';
|
|
3
|
-
import { DEPLOY, SEPARATOR } from '../messages.js';
|
|
4
|
-
export function logDiff(ctx, diff) {
|
|
5
|
-
if (!diff.hasChanges) {
|
|
6
|
-
ctx.log(` ${DEPLOY.NO_CHANGES}`);
|
|
7
|
-
return;
|
|
8
|
-
}
|
|
9
|
-
ctx.log(` ${DEPLOY.CHANGES_HEADER}`);
|
|
10
|
-
for (const email of diff.added) {
|
|
11
|
-
ctx.log(` ${chalk.green('+')} ${email.id.padEnd(24)} ${email.trigger || ''}`);
|
|
12
|
-
}
|
|
13
|
-
for (const email of diff.removed) {
|
|
14
|
-
ctx.log(` ${chalk.red('-')} ${email.id.padEnd(24)} ${email.trigger || ''}`);
|
|
15
|
-
}
|
|
16
|
-
for (const email of diff.modified) {
|
|
17
|
-
ctx.log(` ${chalk.yellow('~')} ${email.id.padEnd(24)} ${email.changedFields?.join(', ') || ''}`);
|
|
18
|
-
}
|
|
19
|
-
if (diff.unchanged.length > 0)
|
|
20
|
-
ctx.log(` ${chalk.dim(`∙ ${diff.unchanged.length} unchanged`)}`);
|
|
21
|
-
}
|
|
22
|
-
export function logPreDeploySummary(ctx, yamlConfig, validateResult, jsonOutput) {
|
|
23
|
-
if (jsonOutput)
|
|
24
|
-
return;
|
|
25
|
-
ctx.log(`\n ${chalk.green('✓')} Domain: ${yamlConfig.project?.domain || 'verified'}\n`);
|
|
26
|
-
if (!validateResult.existingDeployment || !validateResult.diff) {
|
|
27
|
-
ctx.log(` ${DEPLOY.DEPLOYING_HEADER}`);
|
|
28
|
-
for (const email of yamlConfig.emails) {
|
|
29
|
-
ctx.log(` ${chalk.green('+')} ${email.id.padEnd(24)} ${email.trigger}`);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
else {
|
|
33
|
-
logDiff(ctx, validateResult.diff);
|
|
34
|
-
}
|
|
35
|
-
ctx.log('');
|
|
36
|
-
}
|
|
37
|
-
export function logDeploySuccessInstructions(ctx, sdkSnippet) {
|
|
38
|
-
ctx.log(` ${DEPLOY.SUCCESS}\n`);
|
|
39
|
-
ctx.log(` ${SEPARATOR}`);
|
|
40
|
-
ctx.log(` ${DEPLOY.SDK_ONBOARDING_HEADER}`);
|
|
41
|
-
ctx.log(` ${SEPARATOR}\n`);
|
|
42
|
-
ctx.log(` ${chalk.cyan(sdkSnippet.install ?? SDK_INSTALL_COMMAND)}\n`);
|
|
43
|
-
ctx.log(` ${chalk.dim(SDK_IMPORT_SNIPPET)}\n`);
|
|
44
|
-
if (sdkSnippet.examples) {
|
|
45
|
-
ctx.log(` ${DEPLOY.SDK_EXAMPLE_COMMENT}`);
|
|
46
|
-
ctx.log(` ${chalk.dim(sdkSnippet.examples.track)}`);
|
|
47
|
-
ctx.log(` ${chalk.dim(sdkSnippet.examples.identify)}\n`);
|
|
48
|
-
}
|
|
49
|
-
const trackCalls = [...new Set(sdkSnippet.trackCalls ?? [])];
|
|
50
|
-
for (const call of trackCalls)
|
|
51
|
-
ctx.log(` ${chalk.dim(call)}`);
|
|
52
|
-
if (trackCalls.length > 0)
|
|
53
|
-
ctx.log('');
|
|
54
|
-
const identifyCalls = [...new Set(sdkSnippet.identifyCalls ?? [])];
|
|
55
|
-
for (const call of identifyCalls)
|
|
56
|
-
ctx.log(` ${chalk.dim(call)}`);
|
|
57
|
-
if (identifyCalls.length > 0)
|
|
58
|
-
ctx.log('');
|
|
59
|
-
ctx.log(` ${DEPLOY.SDK_DOCS_HINT}\n`);
|
|
60
|
-
ctx.log(` ${SEPARATOR}\n`);
|
|
61
|
-
}
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { type EmailConfig, type MailmodoYaml, type ProjectConfig } from '../yaml-config.js';
|
|
2
|
-
import type { DeployCtx } from './types.js';
|
|
3
|
-
export declare function mapEmailToPayload(email: EmailConfig): {
|
|
4
|
-
condition: string | null;
|
|
5
|
-
ctaText: string;
|
|
6
|
-
delay: string | number;
|
|
7
|
-
goal: string;
|
|
8
|
-
id: string;
|
|
9
|
-
isReminder: boolean;
|
|
10
|
-
previewText: string;
|
|
11
|
-
priority: string;
|
|
12
|
-
subject: string;
|
|
13
|
-
trigger: string;
|
|
14
|
-
};
|
|
15
|
-
export declare function buildProjectPayload(project: ProjectConfig, monthlyCap: number | undefined): {
|
|
16
|
-
webhookUrl?: string | undefined;
|
|
17
|
-
product: {
|
|
18
|
-
businessType: string;
|
|
19
|
-
description: string;
|
|
20
|
-
pricingModel: string;
|
|
21
|
-
productName: string;
|
|
22
|
-
saasModel: string;
|
|
23
|
-
targetUser: string;
|
|
24
|
-
url: string;
|
|
25
|
-
};
|
|
26
|
-
senderDetails: {
|
|
27
|
-
address: string;
|
|
28
|
-
domain: string;
|
|
29
|
-
fromEmail: string;
|
|
30
|
-
fromName: string;
|
|
31
|
-
replyTo: string;
|
|
32
|
-
};
|
|
33
|
-
monthlyCap?: number | undefined;
|
|
34
|
-
brand: {
|
|
35
|
-
colors: string[];
|
|
36
|
-
logoUrl: string;
|
|
37
|
-
};
|
|
38
|
-
emailStyle: "branded" | "plain";
|
|
39
|
-
};
|
|
40
|
-
export declare function buildRegeneratePayload(yamlConfig: MailmodoYaml, missingIds: string[]): Record<string, unknown>;
|
|
41
|
-
export declare function buildDeployPayload(ctx: DeployCtx, yamlConfig: MailmodoYaml): Promise<object>;
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
import { loadTemplate, } from '../yaml-config.js';
|
|
2
|
-
import { DEFAULT_BRAND_COLOR } from '../constants.js';
|
|
3
|
-
export function mapEmailToPayload(email) {
|
|
4
|
-
return {
|
|
5
|
-
condition: email.condition || null,
|
|
6
|
-
ctaText: email.ctaText || '',
|
|
7
|
-
delay: email.delay,
|
|
8
|
-
goal: email.goal || '',
|
|
9
|
-
id: email.id,
|
|
10
|
-
isReminder: false,
|
|
11
|
-
previewText: email.previewText || '',
|
|
12
|
-
priority: 'medium',
|
|
13
|
-
subject: email.subject,
|
|
14
|
-
trigger: email.trigger,
|
|
15
|
-
};
|
|
16
|
-
}
|
|
17
|
-
function buildBrandSection(project) {
|
|
18
|
-
return {
|
|
19
|
-
colors: [project?.brandColor || DEFAULT_BRAND_COLOR],
|
|
20
|
-
logoUrl: project?.logoUrl || '',
|
|
21
|
-
};
|
|
22
|
-
}
|
|
23
|
-
function buildProductSection(project) {
|
|
24
|
-
return {
|
|
25
|
-
businessType: project?.type || '',
|
|
26
|
-
description: project?.description || '',
|
|
27
|
-
pricingModel: project?.pricingModel || '',
|
|
28
|
-
productName: project?.name || '',
|
|
29
|
-
saasModel: project?.saasModel || '',
|
|
30
|
-
targetUser: project?.targetUser || '',
|
|
31
|
-
url: project?.url || '',
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
function buildSenderSection(project) {
|
|
35
|
-
return {
|
|
36
|
-
address: project?.address || '',
|
|
37
|
-
domain: project?.domain || '',
|
|
38
|
-
fromEmail: project?.fromEmail || '',
|
|
39
|
-
fromName: project?.fromName || '',
|
|
40
|
-
replyTo: project?.replyTo || project?.fromEmail || '',
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
export function buildProjectPayload(project, monthlyCap) {
|
|
44
|
-
return {
|
|
45
|
-
brand: buildBrandSection(project),
|
|
46
|
-
emailStyle: project?.emailStyle || 'branded',
|
|
47
|
-
...(monthlyCap === undefined ? {} : { monthlyCap }),
|
|
48
|
-
product: buildProductSection(project),
|
|
49
|
-
senderDetails: buildSenderSection(project),
|
|
50
|
-
...(project?.webhookUrl ? { webhookUrl: project.webhookUrl } : {}),
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
export function buildRegeneratePayload(yamlConfig, missingIds) {
|
|
54
|
-
const { emails, project } = yamlConfig;
|
|
55
|
-
const idSet = new Set(missingIds);
|
|
56
|
-
const targets = emails.filter((e) => idSet.has(e.id));
|
|
57
|
-
return {
|
|
58
|
-
brand: {
|
|
59
|
-
color: project.brandColor || DEFAULT_BRAND_COLOR,
|
|
60
|
-
logoUrl: project.logoUrl || '',
|
|
61
|
-
},
|
|
62
|
-
businessType: project.type || '',
|
|
63
|
-
description: project.description || '',
|
|
64
|
-
events: [...new Set(targets.map((e) => e.trigger))],
|
|
65
|
-
pricingModel: project.pricingModel || '',
|
|
66
|
-
productName: project.name || '',
|
|
67
|
-
recommendedEmails: targets.map((e) => ({
|
|
68
|
-
condition: e.condition || null,
|
|
69
|
-
delay: String(e.delay ?? '0'),
|
|
70
|
-
goal: e.goal || '',
|
|
71
|
-
id: e.id,
|
|
72
|
-
isReminder: false,
|
|
73
|
-
priority: 'medium',
|
|
74
|
-
trigger: e.trigger,
|
|
75
|
-
})),
|
|
76
|
-
saasModel: project.saasModel || '',
|
|
77
|
-
targetUser: project.targetUser || '',
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
export async function buildDeployPayload(ctx, yamlConfig) {
|
|
81
|
-
const [emailsWithHtml, monthlyCap] = await Promise.all([
|
|
82
|
-
Promise.all(yamlConfig.emails.map(async (email) => {
|
|
83
|
-
const html = (await loadTemplate(`${email.id}.html`)) || '';
|
|
84
|
-
const plainHtml = (await loadTemplate(`${email.id}_plain.html`)) || html;
|
|
85
|
-
return { ...mapEmailToPayload(email), html, plainHtml };
|
|
86
|
-
})),
|
|
87
|
-
yamlConfig.project.monthlyCap === undefined
|
|
88
|
-
? ctx.getBillingCap()
|
|
89
|
-
: Promise.resolve(yamlConfig.project.monthlyCap),
|
|
90
|
-
]);
|
|
91
|
-
return {
|
|
92
|
-
...buildProjectPayload(yamlConfig.project, monthlyCap),
|
|
93
|
-
emails: emailsWithHtml,
|
|
94
|
-
};
|
|
95
|
-
}
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
import type { DeployCtx, DeployFlags } from './types.js';
|
|
2
|
-
export declare function pauseSequence(ctx: DeployCtx, sequenceId: string, flags: DeployFlags): Promise<void>;
|
|
3
|
-
export declare function resumeSequence(ctx: DeployCtx, sequenceId: string, flags: DeployFlags): Promise<void>;
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import { confirm } from '@inquirer/prompts';
|
|
2
|
-
import { API_ENDPOINTS } from '../constants.js';
|
|
3
|
-
import { INFO, pauseAlready, pauseSuccess, PROMPTS, resumeAlready, resumeSuccess, } from '../messages.js';
|
|
4
|
-
function sequenceStatusPath(sequenceId) {
|
|
5
|
-
return `${API_ENDPOINTS.SEQUENCES}/${encodeURIComponent(sequenceId)}/status`;
|
|
6
|
-
}
|
|
7
|
-
async function updateSequenceStatus(ctx, opts) {
|
|
8
|
-
const response = await ctx.spinner(opts.spinnerText, opts.flags.json, () => ctx.post(sequenceStatusPath(opts.sequenceId), {
|
|
9
|
-
status: opts.status,
|
|
10
|
-
}));
|
|
11
|
-
if (!response.ok)
|
|
12
|
-
ctx.onApiError(response);
|
|
13
|
-
return response.data;
|
|
14
|
-
}
|
|
15
|
-
export async function pauseSequence(ctx, sequenceId, flags) {
|
|
16
|
-
if (!flags.yes) {
|
|
17
|
-
const confirmed = await confirm({
|
|
18
|
-
default: false,
|
|
19
|
-
message: PROMPTS.PAUSE_CONFIRM,
|
|
20
|
-
});
|
|
21
|
-
if (!confirmed) {
|
|
22
|
-
ctx.log(`\n ${INFO.PAUSE_CANCELLED}\n`);
|
|
23
|
-
return;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
const data = await updateSequenceStatus(ctx, {
|
|
27
|
-
flags,
|
|
28
|
-
sequenceId,
|
|
29
|
-
spinnerText: ' Pausing sequence...',
|
|
30
|
-
status: 'paused',
|
|
31
|
-
});
|
|
32
|
-
if (flags.json) {
|
|
33
|
-
ctx.log(JSON.stringify(data, null, 2));
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
const msg = data.alreadyInStatus
|
|
37
|
-
? pauseAlready(data.sequenceId || sequenceId)
|
|
38
|
-
: pauseSuccess(data.sequenceId || sequenceId);
|
|
39
|
-
ctx.log(`\n ${msg}\n`);
|
|
40
|
-
}
|
|
41
|
-
export async function resumeSequence(ctx, sequenceId, flags) {
|
|
42
|
-
const data = await updateSequenceStatus(ctx, {
|
|
43
|
-
flags,
|
|
44
|
-
sequenceId,
|
|
45
|
-
spinnerText: ' Resuming sequence...',
|
|
46
|
-
status: 'active',
|
|
47
|
-
});
|
|
48
|
-
if (flags.json) {
|
|
49
|
-
ctx.log(JSON.stringify(data, null, 2));
|
|
50
|
-
return;
|
|
51
|
-
}
|
|
52
|
-
const msg = data.alreadyInStatus
|
|
53
|
-
? resumeAlready(data.sequenceId || sequenceId)
|
|
54
|
-
: resumeSuccess(data.sequenceId || sequenceId);
|
|
55
|
-
ctx.log(`\n ${msg}\n`);
|
|
56
|
-
}
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import type { ApiResponse } from '../api-client.js';
|
|
2
|
-
import type { MailmodoYaml } from '../yaml-config.js';
|
|
3
|
-
import type { RegenCtx } from '../templates/types.js';
|
|
4
|
-
export interface EmailDiffEntry {
|
|
5
|
-
changedFields?: string[];
|
|
6
|
-
id: string;
|
|
7
|
-
subject?: string;
|
|
8
|
-
trigger?: string;
|
|
9
|
-
}
|
|
10
|
-
export interface ValidateResponse {
|
|
11
|
-
diff: null | {
|
|
12
|
-
added: EmailDiffEntry[];
|
|
13
|
-
hasChanges: boolean;
|
|
14
|
-
modified: EmailDiffEntry[];
|
|
15
|
-
removed: EmailDiffEntry[];
|
|
16
|
-
unchanged: EmailDiffEntry[];
|
|
17
|
-
};
|
|
18
|
-
error: null | string;
|
|
19
|
-
existingDeployment: boolean;
|
|
20
|
-
isValid: boolean;
|
|
21
|
-
}
|
|
22
|
-
export interface SdkSnippet {
|
|
23
|
-
examples: {
|
|
24
|
-
identify: string;
|
|
25
|
-
track: string;
|
|
26
|
-
};
|
|
27
|
-
identifyCalls: string[];
|
|
28
|
-
install: string;
|
|
29
|
-
trackCalls: string[];
|
|
30
|
-
}
|
|
31
|
-
export interface DeployResponse {
|
|
32
|
-
deployed: boolean;
|
|
33
|
-
diff: {
|
|
34
|
-
added: EmailDiffEntry[];
|
|
35
|
-
hasChanges: boolean;
|
|
36
|
-
modified: EmailDiffEntry[];
|
|
37
|
-
removed: EmailDiffEntry[];
|
|
38
|
-
unchanged: EmailDiffEntry[];
|
|
39
|
-
};
|
|
40
|
-
emailsLive: number;
|
|
41
|
-
sdkSnippet: SdkSnippet;
|
|
42
|
-
sequenceId: string;
|
|
43
|
-
}
|
|
44
|
-
export type DeployFlags = {
|
|
45
|
-
json: boolean;
|
|
46
|
-
yes: boolean;
|
|
47
|
-
};
|
|
48
|
-
export type DeployCtx = RegenCtx & {
|
|
49
|
-
collectDomainInputs(yaml: MailmodoYaml, skip: boolean): Promise<{
|
|
50
|
-
address: string;
|
|
51
|
-
domain: string;
|
|
52
|
-
fromEmail: string;
|
|
53
|
-
fromName: string;
|
|
54
|
-
replyTo: string;
|
|
55
|
-
}>;
|
|
56
|
-
error(msg: string): never;
|
|
57
|
-
exit(code?: number): never;
|
|
58
|
-
get<T = Record<string, unknown>>(path: string, params?: Record<string, string>): Promise<ApiResponse<T>>;
|
|
59
|
-
getBillingCap(): Promise<number | undefined>;
|
|
60
|
-
log(msg?: string): void;
|
|
61
|
-
onApiError(resp: {
|
|
62
|
-
error?: string;
|
|
63
|
-
status: number;
|
|
64
|
-
}): never;
|
|
65
|
-
post<T = Record<string, unknown>>(path: string, body?: Record<string, unknown> | unknown): Promise<ApiResponse<T>>;
|
|
66
|
-
registerDomainAndSave(yaml: MailmodoYaml, inputs: {
|
|
67
|
-
address: string;
|
|
68
|
-
domain: string;
|
|
69
|
-
fromEmail: string;
|
|
70
|
-
fromName?: string;
|
|
71
|
-
replyTo?: string;
|
|
72
|
-
}, json: boolean): Promise<{
|
|
73
|
-
dnsGuideUrl?: string;
|
|
74
|
-
dnsRecords: Array<{
|
|
75
|
-
host: string;
|
|
76
|
-
type: string;
|
|
77
|
-
value: string;
|
|
78
|
-
}>;
|
|
79
|
-
}>;
|
|
80
|
-
showDnsRecords(records: Array<{
|
|
81
|
-
host: string;
|
|
82
|
-
type: string;
|
|
83
|
-
value: string;
|
|
84
|
-
}>, guideUrl: string | undefined, json: boolean): void;
|
|
85
|
-
spinner<T>(text: string, json: boolean, work: () => Promise<T>): Promise<T>;
|
|
86
|
-
syncYaml(): Promise<void>;
|
|
87
|
-
};
|
|
88
|
-
export { type RegenCtx } from '../templates/types.js';
|
package/dist/lib/deploy/types.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import { type MailmodoYaml } from '../yaml-config.js';
|
|
2
|
-
import type { DeployFlags } from '../deploy/types.js';
|
|
3
|
-
import type { RegenCtx } from './types.js';
|
|
4
|
-
export declare function getMissingTemplateIds(yamlConfig: MailmodoYaml): string[];
|
|
5
|
-
export declare function handleMissingTemplates(ctx: RegenCtx, yamlConfig: MailmodoYaml, missingIds: string[], flags: DeployFlags): Promise<boolean>;
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { existsSync } from 'node:fs';
|
|
2
|
-
import { join } from 'node:path';
|
|
3
|
-
import { select } from '@inquirer/prompts';
|
|
4
|
-
import chalk from 'chalk';
|
|
5
|
-
import { API_ENDPOINTS, TEMPLATES_DIR } from '../constants.js';
|
|
6
|
-
import { MISSING_TEMPLATES } from '../messages.js';
|
|
7
|
-
import { getTemplateFilename, saveTemplate, } from '../yaml-config.js';
|
|
8
|
-
import { buildRegeneratePayload } from '../deploy/payload.js';
|
|
9
|
-
export function getMissingTemplateIds(yamlConfig) {
|
|
10
|
-
return yamlConfig.emails
|
|
11
|
-
.filter((e) => !existsSync(join(process.cwd(), TEMPLATES_DIR, getTemplateFilename(e.id, e.style, yamlConfig.project?.emailStyle))))
|
|
12
|
-
.map((e) => e.id);
|
|
13
|
-
}
|
|
14
|
-
async function regenerateMissingTemplates(ctx, yamlConfig, missingIds, flags) {
|
|
15
|
-
const response = await ctx.spinner(` ${MISSING_TEMPLATES.REGENERATE_SPINNER}`, flags.json, () => ctx.post(API_ENDPOINTS.GENERATE, buildRegeneratePayload(yamlConfig, missingIds)));
|
|
16
|
-
if (!response.ok)
|
|
17
|
-
ctx.onApiError(response);
|
|
18
|
-
const saves = [];
|
|
19
|
-
for (const email of response.data?.emails ?? []) {
|
|
20
|
-
if (!/^[\w-]+$/.test(email.id))
|
|
21
|
-
continue;
|
|
22
|
-
if (email.html)
|
|
23
|
-
saves.push(saveTemplate(`${email.id}.html`, email.html));
|
|
24
|
-
if (email.plainHtml)
|
|
25
|
-
saves.push(saveTemplate(`${email.id}_plain.html`, email.plainHtml));
|
|
26
|
-
}
|
|
27
|
-
await Promise.all(saves);
|
|
28
|
-
await ctx.syncYaml();
|
|
29
|
-
}
|
|
30
|
-
export async function handleMissingTemplates(ctx, yamlConfig, missingIds, flags) {
|
|
31
|
-
if (flags.json) {
|
|
32
|
-
ctx.log(JSON.stringify({
|
|
33
|
-
error: 'missing_templates',
|
|
34
|
-
message: MISSING_TEMPLATES.YES_ERROR,
|
|
35
|
-
missingTemplates: missingIds.map((id) => `${id}.html`),
|
|
36
|
-
}, null, 2));
|
|
37
|
-
ctx.exit(1);
|
|
38
|
-
}
|
|
39
|
-
if (flags.yes)
|
|
40
|
-
ctx.error(MISSING_TEMPLATES.YES_ERROR);
|
|
41
|
-
ctx.log(`\n ${MISSING_TEMPLATES.HEADER}`);
|
|
42
|
-
for (const id of missingIds)
|
|
43
|
-
ctx.log(` ${chalk.red('✗')} mailmodo/${id}.html`);
|
|
44
|
-
ctx.log(`\n ${MISSING_TEMPLATES.REGENERATE_NOTE}\n`);
|
|
45
|
-
const action = await select({
|
|
46
|
-
choices: [
|
|
47
|
-
{
|
|
48
|
-
name: MISSING_TEMPLATES.CHOICE_REGENERATE,
|
|
49
|
-
value: 'regenerate',
|
|
50
|
-
},
|
|
51
|
-
{ name: MISSING_TEMPLATES.CHOICE_ABORT, value: 'abort' },
|
|
52
|
-
],
|
|
53
|
-
message: MISSING_TEMPLATES.PROMPT_ACTION,
|
|
54
|
-
});
|
|
55
|
-
if (action === 'abort') {
|
|
56
|
-
ctx.log(`\n ${MISSING_TEMPLATES.ABORT_HINT}\n`);
|
|
57
|
-
return false;
|
|
58
|
-
}
|
|
59
|
-
await regenerateMissingTemplates(ctx, yamlConfig, missingIds, flags);
|
|
60
|
-
return true;
|
|
61
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import type { ApiResponse } from '../api-client.js';
|
|
2
|
-
export type RegenCtx = {
|
|
3
|
-
error(msg: string): never;
|
|
4
|
-
exit(code?: number): never;
|
|
5
|
-
log(msg?: string): void;
|
|
6
|
-
onApiError(resp: {
|
|
7
|
-
error?: string;
|
|
8
|
-
status: number;
|
|
9
|
-
}): never;
|
|
10
|
-
post<T = Record<string, unknown>>(path: string, body?: Record<string, unknown> | unknown): Promise<ApiResponse<T>>;
|
|
11
|
-
spinner<T>(text: string, json: boolean, work: () => Promise<T>): Promise<T>;
|
|
12
|
-
syncYaml(): Promise<void>;
|
|
13
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|