@softure-ai/mailing 0.0.0-stage → 0.1.6

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.
Files changed (193) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/LICENSE +21 -0
  3. package/README.md +346 -2
  4. package/dist/address.d.ts +27 -0
  5. package/dist/address.d.ts.map +1 -0
  6. package/dist/address.js +76 -0
  7. package/dist/address.js.map +1 -0
  8. package/dist/cli/bin.d.ts +3 -0
  9. package/dist/cli/bin.d.ts.map +1 -0
  10. package/dist/cli/bin.js +5 -0
  11. package/dist/cli/bin.js.map +1 -0
  12. package/dist/cli/command.d.ts +9 -0
  13. package/dist/cli/command.d.ts.map +1 -0
  14. package/dist/cli/command.js +36 -0
  15. package/dist/cli/command.js.map +1 -0
  16. package/dist/cli/index.d.ts +3 -0
  17. package/dist/cli/index.d.ts.map +1 -0
  18. package/dist/cli/index.js +4 -0
  19. package/dist/cli/index.js.map +1 -0
  20. package/dist/cli/run.d.ts +31 -0
  21. package/dist/cli/run.d.ts.map +1 -0
  22. package/dist/cli/run.js +288 -0
  23. package/dist/cli/run.js.map +1 -0
  24. package/dist/contract.d.ts +102 -0
  25. package/dist/contract.d.ts.map +1 -0
  26. package/dist/contract.js +3 -0
  27. package/dist/contract.js.map +1 -0
  28. package/dist/index.d.ts +47 -0
  29. package/dist/index.d.ts.map +1 -0
  30. package/dist/index.js +59 -0
  31. package/dist/index.js.map +1 -0
  32. package/dist/messages/en.d.ts +26 -0
  33. package/dist/messages/en.d.ts.map +1 -0
  34. package/dist/messages/en.js +26 -0
  35. package/dist/messages/en.js.map +1 -0
  36. package/dist/messages/index.d.ts +59 -0
  37. package/dist/messages/index.d.ts.map +1 -0
  38. package/dist/messages/index.js +11 -0
  39. package/dist/messages/index.js.map +1 -0
  40. package/dist/messages/pl.d.ts +3 -0
  41. package/dist/messages/pl.d.ts.map +1 -0
  42. package/dist/messages/pl.js +27 -0
  43. package/dist/messages/pl.js.map +1 -0
  44. package/dist/next/actions.d.ts +2 -0
  45. package/dist/next/actions.d.ts.map +1 -0
  46. package/dist/next/actions.js +35 -0
  47. package/dist/next/actions.js.map +1 -0
  48. package/dist/next/context.d.ts +4 -0
  49. package/dist/next/context.d.ts.map +1 -0
  50. package/dist/next/context.js +14 -0
  51. package/dist/next/context.js.map +1 -0
  52. package/dist/next/deliver-once.d.ts +4 -0
  53. package/dist/next/deliver-once.d.ts.map +1 -0
  54. package/dist/next/deliver-once.js +7 -0
  55. package/dist/next/deliver-once.js.map +1 -0
  56. package/dist/next/index.d.ts +8 -0
  57. package/dist/next/index.d.ts.map +1 -0
  58. package/dist/next/index.js +11 -0
  59. package/dist/next/index.js.map +1 -0
  60. package/dist/next/messages.d.ts +5 -0
  61. package/dist/next/messages.d.ts.map +1 -0
  62. package/dist/next/messages.js +7 -0
  63. package/dist/next/messages.js.map +1 -0
  64. package/dist/next/pages.d.ts +7 -0
  65. package/dist/next/pages.d.ts.map +1 -0
  66. package/dist/next/pages.js +40 -0
  67. package/dist/next/pages.js.map +1 -0
  68. package/dist/next/params.d.ts +4 -0
  69. package/dist/next/params.d.ts.map +1 -0
  70. package/dist/next/params.js +4 -0
  71. package/dist/next/params.js.map +1 -0
  72. package/dist/next/route.d.ts +14 -0
  73. package/dist/next/route.d.ts.map +1 -0
  74. package/dist/next/route.js +44 -0
  75. package/dist/next/route.js.map +1 -0
  76. package/dist/next/send-mail.d.ts +4 -0
  77. package/dist/next/send-mail.d.ts.map +1 -0
  78. package/dist/next/send-mail.js +16 -0
  79. package/dist/next/send-mail.js.map +1 -0
  80. package/dist/options.d.ts +13 -0
  81. package/dist/options.d.ts.map +1 -0
  82. package/dist/options.js +29 -0
  83. package/dist/options.js.map +1 -0
  84. package/dist/providers/resend.d.ts +17 -0
  85. package/dist/providers/resend.d.ts.map +1 -0
  86. package/dist/providers/resend.js +64 -0
  87. package/dist/providers/resend.js.map +1 -0
  88. package/dist/schema.d.ts +350 -0
  89. package/dist/schema.d.ts.map +1 -0
  90. package/dist/schema.js +30 -0
  91. package/dist/schema.js.map +1 -0
  92. package/dist/server/campaign-file.d.ts +28 -0
  93. package/dist/server/campaign-file.d.ts.map +1 -0
  94. package/dist/server/campaign-file.js +87 -0
  95. package/dist/server/campaign-file.js.map +1 -0
  96. package/dist/server/campaigns.d.ts +58 -0
  97. package/dist/server/campaigns.d.ts.map +1 -0
  98. package/dist/server/campaigns.js +118 -0
  99. package/dist/server/campaigns.js.map +1 -0
  100. package/dist/server/deliveries.d.ts +56 -0
  101. package/dist/server/deliveries.d.ts.map +1 -0
  102. package/dist/server/deliveries.js +100 -0
  103. package/dist/server/deliveries.js.map +1 -0
  104. package/dist/server/dns.d.ts +33 -0
  105. package/dist/server/dns.d.ts.map +1 -0
  106. package/dist/server/dns.js +113 -0
  107. package/dist/server/dns.js.map +1 -0
  108. package/dist/server/health.d.ts +3 -0
  109. package/dist/server/health.d.ts.map +1 -0
  110. package/dist/server/health.js +13 -0
  111. package/dist/server/health.js.map +1 -0
  112. package/dist/server/index.d.ts +12 -0
  113. package/dist/server/index.d.ts.map +1 -0
  114. package/dist/server/index.js +14 -0
  115. package/dist/server/index.js.map +1 -0
  116. package/dist/server/list-mail.d.ts +20 -0
  117. package/dist/server/list-mail.d.ts.map +1 -0
  118. package/dist/server/list-mail.js +31 -0
  119. package/dist/server/list-mail.js.map +1 -0
  120. package/dist/server/options.d.ts +14 -0
  121. package/dist/server/options.d.ts.map +1 -0
  122. package/dist/server/options.js +22 -0
  123. package/dist/server/options.js.map +1 -0
  124. package/dist/server/send-mail.d.ts +19 -0
  125. package/dist/server/send-mail.d.ts.map +1 -0
  126. package/dist/server/send-mail.js +129 -0
  127. package/dist/server/send-mail.js.map +1 -0
  128. package/dist/server/suppressions.d.ts +27 -0
  129. package/dist/server/suppressions.d.ts.map +1 -0
  130. package/dist/server/suppressions.js +64 -0
  131. package/dist/server/suppressions.js.map +1 -0
  132. package/dist/server/unsubscribe-link.d.ts +51 -0
  133. package/dist/server/unsubscribe-link.d.ts.map +1 -0
  134. package/dist/server/unsubscribe-link.js +78 -0
  135. package/dist/server/unsubscribe-link.js.map +1 -0
  136. package/dist/server/validate-mail.d.ts +23 -0
  137. package/dist/server/validate-mail.d.ts.map +1 -0
  138. package/dist/server/validate-mail.js +52 -0
  139. package/dist/server/validate-mail.js.map +1 -0
  140. package/dist/testing/fake-provider.d.ts +24 -0
  141. package/dist/testing/fake-provider.d.ts.map +1 -0
  142. package/dist/testing/fake-provider.js +43 -0
  143. package/dist/testing/fake-provider.js.map +1 -0
  144. package/dist/testing/index.d.ts +3 -0
  145. package/dist/testing/index.d.ts.map +1 -0
  146. package/dist/testing/index.js +5 -0
  147. package/dist/testing/index.js.map +1 -0
  148. package/dist/testing/outbox.d.ts +6 -0
  149. package/dist/testing/outbox.d.ts.map +1 -0
  150. package/dist/testing/outbox.js +32 -0
  151. package/dist/testing/outbox.js.map +1 -0
  152. package/migrations/0001_create_suppressions.sql +13 -0
  153. package/migrations/0002_create_campaigns_and_deliveries.sql +41 -0
  154. package/module.json +32 -0
  155. package/package.json +74 -4
  156. package/src/address.ts +89 -0
  157. package/src/cli/bin.ts +5 -0
  158. package/src/cli/command.ts +44 -0
  159. package/src/cli/index.ts +3 -0
  160. package/src/cli/run.ts +317 -0
  161. package/src/contract.ts +108 -0
  162. package/src/index.ts +85 -0
  163. package/src/messages/en.ts +25 -0
  164. package/src/messages/index.ts +15 -0
  165. package/src/messages/pl.ts +28 -0
  166. package/src/next/actions.ts +35 -0
  167. package/src/next/context.ts +15 -0
  168. package/src/next/deliver-once.ts +10 -0
  169. package/src/next/index.ts +10 -0
  170. package/src/next/messages.ts +9 -0
  171. package/src/next/next-modules.d.ts +12 -0
  172. package/src/next/pages.tsx +69 -0
  173. package/src/next/params.ts +6 -0
  174. package/src/next/route.ts +45 -0
  175. package/src/next/send-mail.ts +15 -0
  176. package/src/options.ts +34 -0
  177. package/src/providers/resend.ts +86 -0
  178. package/src/schema.ts +40 -0
  179. package/src/server/campaign-file.ts +101 -0
  180. package/src/server/campaigns.ts +175 -0
  181. package/src/server/deliveries.ts +155 -0
  182. package/src/server/dns.ts +153 -0
  183. package/src/server/health.ts +14 -0
  184. package/src/server/index.ts +56 -0
  185. package/src/server/list-mail.ts +42 -0
  186. package/src/server/options.ts +32 -0
  187. package/src/server/send-mail.ts +152 -0
  188. package/src/server/suppressions.ts +77 -0
  189. package/src/server/unsubscribe-link.ts +109 -0
  190. package/src/server/validate-mail.ts +68 -0
  191. package/src/testing/fake-provider.ts +66 -0
  192. package/src/testing/index.ts +4 -0
  193. package/src/testing/outbox.ts +32 -0
@@ -0,0 +1,288 @@
1
+ // `softure-mail`: sends a campaign from a content file and checks the sender domain's DNS. The app
2
+ // passes its validated config, so the same function serves the bin and an app script that a
3
+ // bundler packs for a container:
4
+ //
5
+ // // scripts/mail.ts
6
+ // import { runMailCli } from "@softure-ai/mailing/cli";
7
+ // import config from "../softure.config";
8
+ // process.exitCode = await runMailCli({ config, argv: process.argv.slice(2) });
9
+ import { readFile } from "node:fs/promises";
10
+ import { dirname, resolve } from "node:path";
11
+ import { parseArgs } from "node:util";
12
+ import { systemClock } from "@softure-ai/core";
13
+ import { openCommandDatabase } from "@softure-ai/db";
14
+ import { parseCampaignFile, parseRecipientList } from "../server/campaign-file.js";
15
+ import { planCampaign, sendCampaign } from "../server/campaigns.js";
16
+ import { checkSenderDns, getSenderDomain } from "../server/dns.js";
17
+ import { getMailingOptions } from "../server/options.js";
18
+ import { MIN_UNSUBSCRIBE_SECRET_LENGTH, readUnsubscribeSecrets, UNSUBSCRIBE_SECRET_ENV } from "../server/unsubscribe-link.js";
19
+ export const EXIT_OK = 0;
20
+ export const EXIT_FAILED = 1;
21
+ export const EXIT_USAGE = 2;
22
+ /** Pause between sends: Resend's default limit is two requests per second. */
23
+ export const DEFAULT_PAUSE_MS = 500;
24
+ export const MAIL_USAGE = `Usage:
25
+ softure-mail campaign <content-file> --recipients <file> [--dry-run] [--pause-ms <ms>]
26
+ softure-mail dns [--domain <domain>] [--dkim-selector <name>]... [--spf-host <host>]...
27
+
28
+ campaign Sends the campaign in <content-file> (frontmatter id, kind, subject, optional html,
29
+ then the text body) to every address in the recipients file (one per line), at most
30
+ once each. Re-running sends only to recipients without an outcome yet.
31
+ --dry-run count what would be sent and change nothing
32
+ --pause-ms <ms> pause after every send (default ${String(DEFAULT_PAUSE_MS)})
33
+
34
+ dns Checks SPF, DKIM and DMARC for the sender domain (default: the domain of "from").
35
+ --domain <domain> check this domain instead
36
+ --dkim-selector <name> DKIM selector to look up (default resend; repeatable)
37
+ --spf-host <host> where SPF is published (default the domain; repeatable;
38
+ Resend publishes it on send.<domain>)
39
+
40
+ Options for both:
41
+ --config <file> the app's softure.config file (bin only)
42
+ --help show this help`;
43
+ const consoleOutput = {
44
+ log: (line) => console.log(line),
45
+ error: (line) => console.error(line),
46
+ };
47
+ /** Runs the command and returns the process exit code: 0 done, 1 failed or incomplete, 2 usage error. */
48
+ export async function runMailCli(options) {
49
+ const output = options.output ?? consoleOutput;
50
+ const command = parseCommand(options.argv);
51
+ if (typeof command === "string") {
52
+ output.error(`softure-mail: ${command}`);
53
+ output.error(MAIL_USAGE);
54
+ return EXIT_USAGE;
55
+ }
56
+ switch (command.kind) {
57
+ case "help":
58
+ output.log(MAIL_USAGE);
59
+ return EXIT_OK;
60
+ case "dns":
61
+ return runDns(command, options, output);
62
+ case "campaign":
63
+ return runCampaign(command, options, output);
64
+ }
65
+ }
66
+ function parseCommand(argv) {
67
+ const [name, ...rest] = argv;
68
+ if (name === undefined)
69
+ return "missing command; use campaign or dns";
70
+ if (name === "--help")
71
+ return { kind: "help" };
72
+ if (name !== "campaign" && name !== "dns")
73
+ return `unknown command "${name}"; use campaign or dns`;
74
+ let parsed;
75
+ try {
76
+ parsed = parseCommandArgs(rest);
77
+ }
78
+ catch (error) {
79
+ return describeError(error);
80
+ }
81
+ const { values, positionals } = parsed;
82
+ if (values.help === true)
83
+ return { kind: "help" };
84
+ if (name === "dns") {
85
+ const misplaced = ["recipients", "dry-run", "pause-ms"].filter((option) => values[option] !== undefined);
86
+ if (misplaced.length > 0)
87
+ return `dns does not take --${misplaced.join(", --")}`;
88
+ if (positionals.length > 0)
89
+ return `dns takes no file, got "${positionals.join(" ")}"`;
90
+ return { kind: "dns", domain: values.domain, dkimSelectors: values["dkim-selector"], spfHosts: values["spf-host"] };
91
+ }
92
+ const misplaced = ["domain", "dkim-selector", "spf-host"].filter((option) => values[option] !== undefined);
93
+ if (misplaced.length > 0)
94
+ return `campaign does not take --${misplaced.join(", --")}`;
95
+ const [contentFile, ...extra] = positionals;
96
+ if (contentFile === undefined)
97
+ return "campaign needs a content file";
98
+ if (extra.length > 0)
99
+ return `campaign takes one content file, got also "${extra.join(" ")}"`;
100
+ if (values.recipients === undefined)
101
+ return "campaign needs --recipients <file>";
102
+ const pauseMs = values["pause-ms"] === undefined ? DEFAULT_PAUSE_MS : Number(values["pause-ms"]);
103
+ if (!Number.isInteger(pauseMs) || pauseMs < 0 || pauseMs > 60_000)
104
+ return `--pause-ms expects whole milliseconds from 0 to 60000, got "${values["pause-ms"] ?? ""}"`;
105
+ return { kind: "campaign", contentFile, recipientsFile: values.recipients, dryRun: values["dry-run"] === true, pauseMs };
106
+ }
107
+ function parseCommandArgs(args) {
108
+ return parseArgs({
109
+ args: [...args],
110
+ options: {
111
+ recipients: { type: "string" },
112
+ "dry-run": { type: "boolean" },
113
+ "pause-ms": { type: "string" },
114
+ domain: { type: "string" },
115
+ "dkim-selector": { type: "string", multiple: true },
116
+ "spf-host": { type: "string", multiple: true },
117
+ help: { type: "boolean" },
118
+ },
119
+ strict: true,
120
+ allowPositionals: true,
121
+ });
122
+ }
123
+ async function runDns(command, options, output) {
124
+ let domain = command.domain;
125
+ if (domain === undefined) {
126
+ if (options.config === undefined) {
127
+ output.error("softure-mail dns: pass --domain <domain>, or run it with the app's config to check the sender's domain");
128
+ return EXIT_FAILED;
129
+ }
130
+ domain = getSenderDomain(getMailingOptions(options.config).from);
131
+ }
132
+ const report = await checkSenderDns(domain, {
133
+ ...(command.dkimSelectors === undefined ? {} : { dkimSelectors: command.dkimSelectors }),
134
+ ...(command.spfHosts === undefined ? {} : { spfHosts: command.spfHosts }),
135
+ ...(options.resolveTxt === undefined ? {} : { resolveTxt: options.resolveTxt }),
136
+ });
137
+ const checks = [
138
+ ["SPF", report.spf],
139
+ ["DKIM", report.dkim],
140
+ ["DMARC", report.dmarc],
141
+ ];
142
+ output.log(`sender domain ${report.domain}`);
143
+ for (const [name, check] of checks) {
144
+ const line = `${name.padEnd(6)}${check.status.padEnd(6)}${check.finding.padEnd(15)}${check.host}${check.record === null ? "" : ` ${check.record}`}`;
145
+ if (check.status === "fail")
146
+ output.error(line);
147
+ else
148
+ output.log(line);
149
+ }
150
+ const failed = checks.filter(([, check]) => check.status === "fail").length;
151
+ output.log(failed === 0 ? "receivers can authenticate mail from this domain" : `${String(failed)} check(s) failed: list mail from this domain will land in spam or be refused`);
152
+ return failed === 0 ? EXIT_OK : EXIT_FAILED;
153
+ }
154
+ async function runCampaign(command, options, output) {
155
+ const cwd = options.cwd ?? process.cwd();
156
+ const config = options.config;
157
+ if (config === undefined) {
158
+ output.error("softure-mail campaign: needs the app's config");
159
+ return EXIT_FAILED;
160
+ }
161
+ const loaded = await loadCampaign(resolve(cwd, command.contentFile), resolve(cwd, command.recipientsFile));
162
+ if (typeof loaded === "string") {
163
+ output.error(`softure-mail campaign: ${loaded}`);
164
+ return EXIT_FAILED;
165
+ }
166
+ if (!command.dryRun && readUnsubscribeSecrets(options.env ?? process.env).current === null) {
167
+ output.error(`softure-mail campaign: campaigns are list mail; set ${UNSUBSCRIBE_SECRET_ENV} (at least ${String(MIN_UNSUBSCRIBE_SECRET_LENGTH)} characters) to sign their unsubscribe links`);
168
+ return EXIT_FAILED;
169
+ }
170
+ if (config.database === null) {
171
+ output.error("softure-mail campaign: the config has no database; set database.url in softure.config");
172
+ return EXIT_FAILED;
173
+ }
174
+ let opened;
175
+ try {
176
+ opened = await openDatabase(config.database, options.openDatabase);
177
+ }
178
+ catch (error) {
179
+ output.error(`softure-mail campaign: ${describeError(error)}`);
180
+ return EXIT_FAILED;
181
+ }
182
+ const ctx = { db: opened.handle.db, clock: systemClock, config };
183
+ try {
184
+ return command.dryRun ? await reportPlan(ctx, loaded, output) : await reportSend(ctx, loaded, { pauseMs: command.pauseMs, sleep: options.sleep }, output);
185
+ }
186
+ catch (error) {
187
+ // Driver errors: the message only, never a stack, an address or the database URL.
188
+ output.error(`softure-mail campaign: ${describeError(error)} (did softure migrate run?)`);
189
+ return EXIT_FAILED;
190
+ }
191
+ finally {
192
+ await opened.close();
193
+ }
194
+ }
195
+ async function loadCampaign(contentPath, recipientsPath) {
196
+ const source = await readText(contentPath);
197
+ if (source === null)
198
+ return `cannot read the content file ${contentPath}`;
199
+ const parsed = parseCampaignFile(source);
200
+ if (!parsed.ok)
201
+ return `${contentPath} is not a campaign:\n ${parsed.problems.join("\n ")}`;
202
+ const { htmlPath, ...content } = parsed.value;
203
+ let html = null;
204
+ if (htmlPath !== null) {
205
+ const htmlFile = resolve(dirname(contentPath), htmlPath);
206
+ html = await readText(htmlFile);
207
+ if (html === null)
208
+ return `cannot read the HTML body ${htmlFile}`;
209
+ if (html.trim() === "")
210
+ return `the HTML body ${htmlFile} is empty`;
211
+ }
212
+ const list = await readText(recipientsPath);
213
+ if (list === null)
214
+ return `cannot read the recipients file ${recipientsPath}`;
215
+ return { campaign: { ...content, html }, recipients: parseRecipientList(list) };
216
+ }
217
+ async function reportPlan(ctx, loaded, output) {
218
+ const plan = await planCampaign(ctx, loaded);
219
+ output.log(`campaign ${loaded.campaign.id} (${loaded.campaign.kind}), dry run: nothing sent or written`);
220
+ output.log(`recipients ${String(plan.recipients)}, already done ${String(plan.done)}, unsubscribed ${String(plan.suppressed)}, to send ${String(plan.toSend)}`);
221
+ if (plan.contentChanged) {
222
+ output.error(`campaign ${loaded.campaign.id} was sent with other content; give this content a new id`);
223
+ return EXIT_FAILED;
224
+ }
225
+ return EXIT_OK;
226
+ }
227
+ async function reportSend(ctx, loaded, pace, output) {
228
+ let handled = 0;
229
+ const total = loaded.recipients.length;
230
+ const result = await sendCampaign(ctx, loaded, {
231
+ pauseMs: pace.pauseMs,
232
+ ...(pace.sleep === undefined ? {} : { sleep: pace.sleep }),
233
+ onDelivery: () => {
234
+ handled += 1;
235
+ if (handled % 100 === 0)
236
+ output.log(`progress ${String(handled)}/${String(total)}`);
237
+ },
238
+ });
239
+ if (!result.ok) {
240
+ output.error(`campaign ${loaded.campaign.id} was sent with other content; give this content a new id`);
241
+ return EXIT_FAILED;
242
+ }
243
+ output.log(`campaign ${loaded.campaign.id} (${loaded.campaign.kind}): ${formatSummary(result.value)}`);
244
+ const open = result.value.inFlight + result.value.retryLater;
245
+ if (open > 0) {
246
+ output.error(`${String(open)} recipient(s) have no outcome yet; run the same command again later`);
247
+ return EXIT_FAILED;
248
+ }
249
+ return EXIT_OK;
250
+ }
251
+ function formatSummary(summary) {
252
+ const rejected = Object.entries(summary.rejected).filter(([, count]) => count > 0);
253
+ const rejectedTotal = rejected.reduce((sum, [, count]) => sum + count, 0);
254
+ const reasons = rejected.length === 0 ? "" : ` (${rejected.map(([code, count]) => `${code.slice("mailing.".length)} ${String(count)}`).join(", ")})`;
255
+ return [
256
+ `recipients ${String(summary.recipients)}`,
257
+ `sent ${String(summary.sent)}`,
258
+ `rejected ${String(rejectedTotal)}${reasons}`,
259
+ `already done ${String(summary.done)}`,
260
+ `in flight ${String(summary.inFlight)}`,
261
+ `retry later ${String(summary.retryLater)}`,
262
+ ].join(", ");
263
+ }
264
+ async function openDatabase(database, open) {
265
+ if (open === undefined)
266
+ return openCommandDatabase(database, { max: 1 });
267
+ const handle = await open(database.url);
268
+ return { handle, close: handle.close };
269
+ }
270
+ async function readText(path) {
271
+ try {
272
+ return await readFile(path, "utf8");
273
+ }
274
+ catch {
275
+ // The caller names the path; the error code adds nothing an operator can act on.
276
+ return null;
277
+ }
278
+ }
279
+ /**
280
+ * The driver's own message. Drizzle wraps it in "Failed query: <sql> params: <values>", and the
281
+ * values hold recipient keys, so the wrapper is dropped.
282
+ */
283
+ function describeError(error) {
284
+ if (!(error instanceof Error))
285
+ return String(error);
286
+ return error.cause instanceof Error ? error.cause.message : error.message;
287
+ }
288
+ //# sourceMappingURL=run.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.js","sourceRoot":"","sources":["../../src/cli/run.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,102 @@
1
+ import type { ModuleContext, Result } from "@softure-ai/core";
2
+ import type { Queryable } from "@softure-ai/db";
3
+ export type MailingErrorCode =
4
+ /** The mail or its options were refused before anything left the process. Fix the input; do not retry. */
5
+ "mailing.invalid_input"
6
+ /** The provider refused the mail. Retrying the same request will not help. */
7
+ | "mailing.rejected"
8
+ /** The provider could not be reached or did not answer in time. Retry later, with the same idempotency key. */
9
+ | "mailing.unavailable"
10
+ /** A list mail to a recipient who unsubscribed. Nothing was sent; do not retry. */
11
+ | "mailing.suppressed";
12
+ /** Why an unsubscribe link was refused: missing, malformed, or not signed by a current secret. */
13
+ export type UnsubscribeErrorCode = "mailing.invalid_link";
14
+ /** The kind of every mail that is not list mail: sent whatever the recipient unsubscribed from. */
15
+ export declare const TRANSACTIONAL_KIND = "transactional";
16
+ /** How an opt-out arrived: a mail client's one-click POST, the page's button, or an operator. */
17
+ export type SuppressionSource = "one-click" | "page" | "operator";
18
+ /** What `onUnsubscribed` receives: who opted out (never the address) and how. */
19
+ export interface UnsubscribeEvent {
20
+ /** The recipient key of the link: privacy's email key of the same address (`getEmailKey`). */
21
+ readonly recipientKey: string;
22
+ readonly source: Exclude<SuppressionSource, "operator">;
23
+ }
24
+ /**
25
+ * Called on every verified unsubscribe, in the suppression's transaction (`ctx.db` is that
26
+ * transaction): a throw rolls the opt-out back and the unsubscribe fails, so the consent ledger
27
+ * and the suppression list never disagree. Set in `mailing({ onUnsubscribed })`.
28
+ */
29
+ export type OnUnsubscribedHook = (event: UnsubscribeEvent, ctx: ModuleContext<Queryable>) => Promise<void>;
30
+ /** One mail to one recipient. A mail to a list is a loop over recipients, never a list in `to`. */
31
+ export interface OutgoingMail {
32
+ /** Exactly one address: `ada@example.com`. */
33
+ readonly to: string;
34
+ /** One line, 1 to 998 characters. */
35
+ readonly subject: string;
36
+ /** The plain-text body. Required: every mail carries a text part. */
37
+ readonly text: string;
38
+ /** The HTML body, already rendered to a string. */
39
+ readonly html?: string;
40
+ /**
41
+ * Extra headers, for example `List-Unsubscribe`. They cannot replace the envelope, the sender or
42
+ * the body's encoding: reserved names are refused (`RESERVED_HEADERS`).
43
+ */
44
+ readonly headers?: Readonly<Record<string, string>>;
45
+ /**
46
+ * `transactional` (the default) for mail the recipient needs whatever they unsubscribed from:
47
+ * password resets, receipts, account notices. Any other kebab-case name (e.g. `newsletter`) is
48
+ * list mail: it gets a signed unsubscribe link in a footer and the RFC 8058 headers, and is
49
+ * refused with `mailing.suppressed` for a recipient who unsubscribed. Unsubscribing covers every
50
+ * list kind.
51
+ */
52
+ readonly kind?: string;
53
+ }
54
+ export interface SendMailOptions {
55
+ /**
56
+ * Makes a retry safe: the provider treats requests with the same key as one mail (Resend keeps a
57
+ * key for 24 hours). 1 to 256 visible ASCII characters, e.g. `campaign-42:recipient-7`.
58
+ */
59
+ readonly idempotencyKey?: string;
60
+ }
61
+ /** A mail the provider accepted. */
62
+ export interface SentMail {
63
+ /** The provider's message id. */
64
+ readonly id: string;
65
+ /** The provider's name, e.g. `resend`. */
66
+ readonly provider: string;
67
+ }
68
+ export type SendMailResult = Result<SentMail, MailingErrorCode>;
69
+ /** What a provider receives: a validated mail with the sender and reply-to from the configuration. */
70
+ export interface ProviderMessage {
71
+ readonly from: string;
72
+ readonly to: string;
73
+ readonly replyTo: string | null;
74
+ readonly subject: string;
75
+ readonly text: string;
76
+ readonly html: string | null;
77
+ readonly headers: Readonly<Record<string, string>>;
78
+ readonly idempotencyKey: string | null;
79
+ }
80
+ /** What a provider answers. `httpStatus` is logged; the mail never is. */
81
+ export type ProviderOutcome = {
82
+ readonly status: "sent";
83
+ readonly id: string;
84
+ } | {
85
+ readonly status: "rejected";
86
+ readonly httpStatus?: number;
87
+ } | {
88
+ readonly status: "unavailable";
89
+ readonly httpStatus?: number;
90
+ };
91
+ /**
92
+ * A mail service adapter. `send` should resolve with an outcome for every failure it knows; a
93
+ * throw reads as `unavailable`. It receives a signal that aborts at the configured timeout.
94
+ */
95
+ export interface MailProvider {
96
+ /** A short lowercase name for logs and `SentMail.provider`. */
97
+ readonly name: string;
98
+ send(message: ProviderMessage, context: {
99
+ readonly signal: AbortSignal;
100
+ }): Promise<ProviderOutcome>;
101
+ }
102
+ //# sourceMappingURL=contract.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ /** The kind of every mail that is not list mail: sent whatever the recipient unsubscribed from. */
2
+ export const TRANSACTIONAL_KIND = "transactional";
3
+ //# sourceMappingURL=contract.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contract.js","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAkBA,mGAAmG;AACnG,MAAM,CAAC,MAAM,kBAAkB,GAAG,eAAe,CAAC"}
@@ -0,0 +1,47 @@
1
+ export declare const MODULE_ID = "mailing";
2
+ /**
3
+ * Enables mail in `softure.config.ts`:
4
+ * `mailing({ from: "Plan <hello@mail.example.com>", replyTo: "support@example.com", provider: resend() })`.
5
+ */
6
+ export declare const mailing: import("@softure-ai/core").ModuleFactory<{
7
+ unsubscribe: string;
8
+ oneClick: string;
9
+ }, {
10
+ errors: {
11
+ mailing: {
12
+ invalid_input: string;
13
+ rejected: string;
14
+ unavailable: string;
15
+ suppressed: string;
16
+ };
17
+ };
18
+ footer: {
19
+ text: string;
20
+ htmlLead: string;
21
+ htmlLink: string;
22
+ };
23
+ unsubscribe: {
24
+ title: string;
25
+ lead: string;
26
+ submit: string;
27
+ doneTitle: string;
28
+ doneBody: string;
29
+ invalidTitle: string;
30
+ invalidBody: string;
31
+ failed: string;
32
+ };
33
+ }, import("zod").ZodObject<{
34
+ from: import("zod").ZodString;
35
+ replyTo: import("zod").ZodOptional<import("zod").ZodString>;
36
+ provider: import("zod").ZodCustom<import("./contract.js").MailProvider, import("./contract.js").MailProvider>;
37
+ timeoutMs: import("zod").ZodDefault<import("zod").ZodNumber>;
38
+ onUnsubscribed: import("zod").ZodOptional<import("zod").ZodCustom<import("./contract.js").OnUnsubscribedHook, import("./contract.js").OnUnsubscribedHook>>;
39
+ }, import("zod/v4/core").$strict>>;
40
+ export { isMailbox, isMailKind, isSingleAddress, LIST_MAIL_HEADERS, MAX_ADDRESS_LENGTH, MAX_IDEMPOTENCY_KEY_LENGTH, MAX_MAIL_KIND_LENGTH, MAX_SUBJECT_LENGTH, RESERVED_HEADERS, } from "./address.js";
41
+ export { TRANSACTIONAL_KIND } from "./contract.js";
42
+ export type { MailingErrorCode, MailProvider, OnUnsubscribedHook, OutgoingMail, ProviderMessage, ProviderOutcome, SendMailOptions, SendMailResult, SentMail, SuppressionSource, UnsubscribeErrorCode, UnsubscribeEvent, } from "./contract.js";
43
+ export { getMailingErrorMessage, mailingMessages, type MailingMessages } from "./messages/index.js";
44
+ export { DEFAULT_TIMEOUT_MS, type MailingOptions, type MailingOptionsInput } from "./options.js";
45
+ export { resend, RESEND_API_KEY_ENV, RESEND_ENDPOINT, type ResendOptions } from "./providers/resend.js";
46
+ export { campaigns, deliveries, mailingSchema, suppressions, type DeliveryStatus } from "./schema.js";
47
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAUA,eAAO,MAAM,SAAS,YAAY,CAAC;AAEnC;;;GAGG;AACH,eAAO,MAAM,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kCAqClB,CAAC;AAEH,OAAO,EACL,SAAS,EACT,UAAU,EACV,eAAe,EACf,iBAAiB,EACjB,kBAAkB,EAClB,0BAA0B,EAC1B,oBAAoB,EACpB,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACnD,YAAY,EACV,gBAAgB,EAChB,YAAY,EACZ,kBAAkB,EAClB,YAAY,EACZ,eAAe,EACf,eAAe,EACf,eAAe,EACf,cAAc,EACd,QAAQ,EACR,iBAAiB,EACjB,oBAAoB,EACpB,gBAAgB,GACjB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,sBAAsB,EAAE,eAAe,EAAE,KAAK,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACpG,OAAO,EAAE,kBAAkB,EAAE,KAAK,cAAc,EAAE,KAAK,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACjG,OAAO,EAAE,MAAM,EAAE,kBAAkB,EAAE,eAAe,EAAE,KAAK,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACxG,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,aAAa,EAAE,YAAY,EAAE,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,59 @@
1
+ // Public API of @softure-ai/mailing: the module factory for softure.config.ts, the provider
2
+ // contract, the Resend adapter, types, messages and the tables. Sending, the suppression list, the
3
+ // delivery ledger and campaigns are in `@softure-ai/mailing/server` (and `/next` on the registered
4
+ // config, with the unsubscribe page and one-click route); the `softure-mail` command is in `/cli`;
5
+ // the fake provider for tests is in `/testing`.
6
+ import { defineModule, resolveMigrationsDir } from "@softure-ai/core";
7
+ import { mailingMessages } from "./messages/index.js";
8
+ import { mailingOptionsSchema } from "./options.js";
9
+ import { checkMailingTables } from "./server/health.js";
10
+ export const MODULE_ID = "mailing";
11
+ /**
12
+ * Enables mail in `softure.config.ts`:
13
+ * `mailing({ from: "Plan <hello@mail.example.com>", replyTo: "support@example.com", provider: resend() })`.
14
+ */
15
+ export const mailing = defineModule({
16
+ manifest: {
17
+ id: MODULE_ID,
18
+ version: "0.1.6",
19
+ dependsOn: {},
20
+ dbSchema: "mailing",
21
+ tables: ["suppressions", "campaigns", "deliveries"],
22
+ env: [
23
+ {
24
+ name: "RESEND_API_KEY",
25
+ required: false,
26
+ description: "API key of the resend() provider, read on every send; not needed with resend({ apiKey }) or another provider.",
27
+ },
28
+ {
29
+ name: "MAILING_UNSUBSCRIBE_SECRET",
30
+ required: false,
31
+ description: "Signs and verifies unsubscribe links, at least 32 characters; required to send list mail (any kind but transactional). Never change it without moving the old value to MAILING_UNSUBSCRIBE_SECRET_PREVIOUS.",
32
+ },
33
+ {
34
+ name: "MAILING_UNSUBSCRIBE_SECRET_PREVIOUS",
35
+ required: false,
36
+ description: "The secret before a rotation, at least 32 characters: verifies links in mail already sent, never signs.",
37
+ },
38
+ ],
39
+ switches: [],
40
+ routes: { unsubscribe: "/unsubscribe", oneClick: "/api/mailing/unsubscribe" },
41
+ mount: [
42
+ { kind: "page", path: "app/unsubscribe/page.tsx", export: "UnsubscribePage" },
43
+ { kind: "route-handler", path: "app/api/mailing/unsubscribe/route.ts", export: "postUnsubscribeRoute" },
44
+ { kind: "route-handler", path: "app/api/mailing/unsubscribe/route.ts", export: "getUnsubscribeRoute" },
45
+ ],
46
+ privacy: { exports: false, deletes: false },
47
+ },
48
+ messages: mailingMessages,
49
+ options: mailingOptionsSchema,
50
+ migrations: { dir: resolveMigrationsDir(import.meta.url, "../migrations/") },
51
+ health: checkMailingTables,
52
+ });
53
+ export { isMailbox, isMailKind, isSingleAddress, LIST_MAIL_HEADERS, MAX_ADDRESS_LENGTH, MAX_IDEMPOTENCY_KEY_LENGTH, MAX_MAIL_KIND_LENGTH, MAX_SUBJECT_LENGTH, RESERVED_HEADERS, } from "./address.js";
54
+ export { TRANSACTIONAL_KIND } from "./contract.js";
55
+ export { getMailingErrorMessage, mailingMessages } from "./messages/index.js";
56
+ export { DEFAULT_TIMEOUT_MS } from "./options.js";
57
+ export { resend, RESEND_API_KEY_ENV, RESEND_ENDPOINT } from "./providers/resend.js";
58
+ export { campaigns, deliveries, mailingSchema, suppressions } from "./schema.js";
59
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,4FAA4F;AAC5F,mGAAmG;AACnG,mGAAmG;AACnG,mGAAmG;AACnG,gDAAgD;AAChD,OAAO,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AACtE,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAExD,MAAM,CAAC,MAAM,SAAS,GAAG,SAAS,CAAC;AAEnC;;;GAGG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,YAAY,CAAC;IAClC,QAAQ,EAAE;QACR,EAAE,EAAE,SAAS;QACb,OAAO,EAAE,OAAO;QAChB,SAAS,EAAE,EAAE;QACb,QAAQ,EAAE,SAAS;QACnB,MAAM,EAAE,CAAC,cAAc,EAAE,WAAW,EAAE,YAAY,CAAC;QACnD,GAAG,EAAE;YACH;gBACE,IAAI,EAAE,gBAAgB;gBACtB,QAAQ,EAAE,KAAK;gBACf,WAAW,EAAE,+GAA+G;aAC7H;YACD;gBACE,IAAI,EAAE,4BAA4B;gBAClC,QAAQ,EAAE,KAAK;gBACf,WAAW,EAAE,6MAA6M;aAC3N;YACD;gBACE,IAAI,EAAE,qCAAqC;gBAC3C,QAAQ,EAAE,KAAK;gBACf,WAAW,EAAE,yGAAyG;aACvH;SACF;QACD,QAAQ,EAAE,EAAE;QACZ,MAAM,EAAE,EAAE,WAAW,EAAE,cAAc,EAAE,QAAQ,EAAE,0BAA0B,EAAE;QAC7E,KAAK,EAAE;YACL,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,0BAA0B,EAAE,MAAM,EAAE,iBAAiB,EAAE;YAC7E,EAAE,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE,sCAAsC,EAAE,MAAM,EAAE,sBAAsB,EAAE;YACvG,EAAE,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE,sCAAsC,EAAE,MAAM,EAAE,qBAAqB,EAAE;SACvG;QACD,OAAO,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE;KAC5C;IACD,QAAQ,EAAE,eAAe;IACzB,OAAO,EAAE,oBAAoB;IAC7B,UAAU,EAAE,EAAE,GAAG,EAAE,oBAAoB,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,gBAAgB,CAAC,EAAE;IAC5E,MAAM,EAAE,kBAAkB;CAC3B,CAAC,CAAC;AAEH,OAAO,EACL,SAAS,EACT,UAAU,EACV,eAAe,EACf,iBAAiB,EACjB,kBAAkB,EAClB,0BAA0B,EAC1B,oBAAoB,EACpB,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAenD,OAAO,EAAE,sBAAsB,EAAE,eAAe,EAAwB,MAAM,qBAAqB,CAAC;AACpG,OAAO,EAAE,kBAAkB,EAAiD,MAAM,cAAc,CAAC;AACjG,OAAO,EAAE,MAAM,EAAE,kBAAkB,EAAE,eAAe,EAAsB,MAAM,uBAAuB,CAAC;AACxG,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,aAAa,EAAE,YAAY,EAAuB,MAAM,aAAa,CAAC"}
@@ -0,0 +1,26 @@
1
+ export declare const en: {
2
+ errors: {
3
+ mailing: {
4
+ invalid_input: string;
5
+ rejected: string;
6
+ unavailable: string;
7
+ suppressed: string;
8
+ };
9
+ };
10
+ footer: {
11
+ text: string;
12
+ htmlLead: string;
13
+ htmlLink: string;
14
+ };
15
+ unsubscribe: {
16
+ title: string;
17
+ lead: string;
18
+ submit: string;
19
+ doneTitle: string;
20
+ doneBody: string;
21
+ invalidTitle: string;
22
+ invalidBody: string;
23
+ failed: string;
24
+ };
25
+ };
26
+ //# sourceMappingURL=en.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"en.d.ts","sourceRoot":"","sources":["../../src/messages/en.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;CAwBd,CAAC"}
@@ -0,0 +1,26 @@
1
+ export const en = {
2
+ errors: {
3
+ mailing: {
4
+ invalid_input: "This message cannot be sent. Check the address and try again.",
5
+ rejected: "The mail service refused this message.",
6
+ unavailable: "The mail service is not answering. Try again in a moment.",
7
+ suppressed: "This address unsubscribed from these emails, so nothing was sent.",
8
+ },
9
+ },
10
+ footer: {
11
+ text: "Don't want these emails? Unsubscribe here:",
12
+ htmlLead: "Don't want these emails?",
13
+ htmlLink: "Unsubscribe",
14
+ },
15
+ unsubscribe: {
16
+ title: "Unsubscribe",
17
+ lead: "You will stop getting these emails from us. Mail about your account, such as password resets, still arrives.",
18
+ submit: "Unsubscribe me",
19
+ doneTitle: "You are unsubscribed",
20
+ doneBody: "We will not send you these emails any more.",
21
+ invalidTitle: "This link does not work",
22
+ invalidBody: "Open the link from the email again, the whole of it. If it still fails, reply to the email and ask us to stop.",
23
+ failed: "We could not save that. Try again in a moment.",
24
+ },
25
+ };
26
+ //# sourceMappingURL=en.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"en.js","sourceRoot":"","sources":["../../src/messages/en.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,EAAE,GAAG;IAChB,MAAM,EAAE;QACN,OAAO,EAAE;YACP,aAAa,EAAE,+DAA+D;YAC9E,QAAQ,EAAE,wCAAwC;YAClD,WAAW,EAAE,2DAA2D;YACxE,UAAU,EAAE,mEAAmE;SAChF;KACF;IACD,MAAM,EAAE;QACN,IAAI,EAAE,4CAA4C;QAClD,QAAQ,EAAE,0BAA0B;QACpC,QAAQ,EAAE,aAAa;KACxB;IACD,WAAW,EAAE;QACX,KAAK,EAAE,aAAa;QACpB,IAAI,EAAE,8GAA8G;QACpH,MAAM,EAAE,gBAAgB;QACxB,SAAS,EAAE,sBAAsB;QACjC,QAAQ,EAAE,6CAA6C;QACvD,YAAY,EAAE,yBAAyB;QACvC,WAAW,EAAE,gHAAgH;QAC7H,MAAM,EAAE,gDAAgD;KACzD;CACF,CAAC"}