@softure-ai/mailing 0.1.14 → 0.2.1
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/CHANGELOG.md +38 -0
- package/README.md +76 -7
- package/dist/contract.d.ts +13 -1
- package/dist/contract.d.ts.map +1 -1
- package/dist/contract.js.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/jobs/delivery-job.d.ts +72 -0
- package/dist/jobs/delivery-job.d.ts.map +1 -0
- package/dist/jobs/delivery-job.js +85 -0
- package/dist/jobs/delivery-job.js.map +1 -0
- package/dist/jobs/index.d.ts +2 -0
- package/dist/jobs/index.d.ts.map +1 -0
- package/dist/jobs/index.js +4 -0
- package/dist/jobs/index.js.map +1 -0
- package/dist/messages/en.d.ts +2 -0
- package/dist/messages/en.d.ts.map +1 -1
- package/dist/messages/en.js +2 -0
- package/dist/messages/en.js.map +1 -1
- package/dist/messages/index.d.ts +5 -30
- package/dist/messages/index.d.ts.map +1 -1
- package/dist/messages/index.js +2 -3
- package/dist/messages/index.js.map +1 -1
- package/dist/next/messages.d.ts +1 -1
- package/dist/next/messages.d.ts.map +1 -1
- package/dist/next/messages.js +2 -1
- package/dist/next/messages.js.map +1 -1
- package/dist/providers/resend.d.ts +5 -0
- package/dist/providers/resend.d.ts.map +1 -1
- package/dist/providers/resend.js +23 -3
- package/dist/providers/resend.js.map +1 -1
- package/dist/server/deliveries.d.ts +6 -1
- package/dist/server/deliveries.d.ts.map +1 -1
- package/dist/server/deliveries.js +3 -2
- package/dist/server/deliveries.js.map +1 -1
- package/dist/server/dns.d.ts +2 -1
- package/dist/server/dns.d.ts.map +1 -1
- package/dist/server/dns.js +14 -6
- package/dist/server/dns.js.map +1 -1
- package/dist/server/send-mail.d.ts +2 -1
- package/dist/server/send-mail.d.ts.map +1 -1
- package/dist/server/send-mail.js +19 -7
- package/dist/server/send-mail.js.map +1 -1
- package/module.json +2 -2
- package/package.json +15 -4
- package/src/contract.ts +14 -3
- package/src/index.ts +4 -3
- package/src/jobs/delivery-job.ts +129 -0
- package/src/jobs/index.ts +12 -0
- package/src/messages/en.ts +2 -0
- package/src/messages/index.ts +4 -5
- package/src/next/messages.ts +2 -2
- package/src/providers/resend.ts +22 -3
- package/src/server/deliveries.ts +9 -5
- package/src/server/dns.ts +16 -7
- package/src/server/send-mail.ts +20 -7
- package/dist/messages/pl.d.ts +0 -3
- package/dist/messages/pl.d.ts.map +0 -1
- package/dist/messages/pl.js +0 -29
- package/dist/messages/pl.js.map +0 -1
- package/src/messages/pl.ts +0 -30
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,44 @@ Newest first. Each version lists what changed for an app that uses `@softure-ai/
|
|
|
4
4
|
production, the version gets a line `verified in: <app>@<commit>` ([docs/05](../../docs/05-adoption-playbook.md),
|
|
5
5
|
"Definition of done"). Versions before the first one below are described in their GitHub Releases (`mailing@x.y.z`).
|
|
6
6
|
|
|
7
|
+
## 0.2.1
|
|
8
|
+
|
|
9
|
+
- New entry `@softure-ai/mailing/jobs` (#492), with `@softure-ai/jobs` as an optional peer dependency:
|
|
10
|
+
`enqueueDelivery(ctx, { queue, scope, payload })` enqueues a delivery in the caller's transaction (the scope is
|
|
11
|
+
the job's dedup key); `defineDeliveryJobHandler({ payload, buildMail })` sends it through `deliverOnce` from the
|
|
12
|
+
jobs worker and retries `retry-later` and `halted` (after the provider's `Retry-After`, else the queue's backoff);
|
|
13
|
+
rejected mail fails at once; `getDeliveryJobResult` is the same mapping for an app's own handler. The payload holds
|
|
14
|
+
the app's ids; the handler builds the mail when it runs. New job codes `mailing.delivery_in_flight` and
|
|
15
|
+
`mailing.delivery_uncertain` with copy in `errors.mailing`. README, "Deliveries through jobs".
|
|
16
|
+
- `resend()` reads `Retry-After` (seconds or an HTTP date); `ProviderOutcome` failures, `SendMailFailure` and
|
|
17
|
+
`deliverOnce`'s `retry-later` and `halted` outcomes carry it as the optional `retryAfterSeconds`. New export
|
|
18
|
+
`readRetryAfter(header, nowMs)` for a provider adapter of the app's own. A strict equality on these results
|
|
19
|
+
(`toStrictEqual`) sees the new field when the provider sends the header.
|
|
20
|
+
- `module.json → dependsOn` names `jobs` as optional (`"*?"`); an app with both modules lists `jobs()` anywhere in
|
|
21
|
+
`modules`.
|
|
22
|
+
- Migrate an app that retries `retry-later` by hand (a cron that calls `deliverOnce` again, a "try again" kept for
|
|
23
|
+
the user):
|
|
24
|
+
1. `npm install @softure-ai/jobs`, add `jobs({ queues: { "<queue>": { maxAttempts } } })` and run the worker
|
|
25
|
+
(jobs README, "Running the worker"); run the migrations.
|
|
26
|
+
2. Register `defineDeliveryJobHandler({ payload, buildMail })` for the queue; move the code that built the mail
|
|
27
|
+
into `buildMail`, reading the app's data by the ids in the payload.
|
|
28
|
+
3. Replace the `deliverOnce` call in the request with `enqueueDelivery({ ...ctx, db: tx }, { queue, scope,
|
|
29
|
+
payload })` inside the transaction of the write it belongs to, keeping the same `scope` (the ledger rows already
|
|
30
|
+
sent stay sent).
|
|
31
|
+
4. Drop the retry cron and the per-app attempt counting; `mailing({ maxAttempts })` now only applies to direct
|
|
32
|
+
`deliverOnce` calls.
|
|
33
|
+
|
|
34
|
+
## 0.2.0
|
|
35
|
+
|
|
36
|
+
- **Breaking (#446):** the built-in `pl` dictionary is gone; English is the only built-in copy and the fallback for
|
|
37
|
+
every other locale, which is now any BCP 47 tag. Migrate: an app that shows Polish keeps its own translation and
|
|
38
|
+
passes it as the overrides for its locale, `mailing({ messages: { pl: myTranslation } })`; a key it leaves out reads
|
|
39
|
+
English.
|
|
40
|
+
- `checkSenderDns(domain, { inbound: "cloudflare" })` (and `softure-mail dns --inbound cloudflare`) fails the inbound
|
|
41
|
+
SPF check as `weak` unless the record's first `all` is `-all` or `~all`. Before, a record with the Email Routing
|
|
42
|
+
include and `?all`, `+all`, a bare `all` or no `all` passed, though it lets anyone send as the domain. An app that
|
|
43
|
+
kept its own check for this next to the report can drop it.
|
|
44
|
+
|
|
7
45
|
## 0.1.14
|
|
8
46
|
|
|
9
47
|
- The `@softure-ai/ui` peer range also accepts 0.2.0.
|
package/README.md
CHANGED
|
@@ -44,6 +44,8 @@ can be imported into it, and `softure-mail` sends campaigns from a content file.
|
|
|
44
44
|
in `/next`) sends a mail at most once per scope and recipient through the delivery ledger;
|
|
45
45
|
`runDeliveries` does it for a whole list of recipients, with a limit, a pause and a dry run;
|
|
46
46
|
`importDeliveries` (and `softure-mail import`) seeds it with the app's earlier history.
|
|
47
|
+
- **Deliveries through jobs.** `enqueueDelivery` and `defineDeliveryJobHandler` (`/jobs`) let an app enqueue a
|
|
48
|
+
mail with its own write and have the `@softure-ai/jobs` worker send it, retrying while the provider is down.
|
|
47
49
|
- **Preview and test send.** `previewMail(config, mail)` returns exactly what `sendMail` hands the
|
|
48
50
|
provider; `softure-mail test` sends a mail to the configured `testAddress` and nowhere else.
|
|
49
51
|
- **A SQL surface** for the app's own queries: `mailing.recipient_key(address)`,
|
|
@@ -128,8 +130,8 @@ const outcome = await deliverOnce({
|
|
|
128
130
|
| `{ status: "done", outcome }` | an earlier call closed it (`sent` or `rejected`); nothing sent |
|
|
129
131
|
| `{ status: "in-flight" }` | another sender holds a fresh claim; nothing sent |
|
|
130
132
|
| `{ status: "uncertain" }` | a claim older than `uncertainClaimMs` is open: its send may have gone out; nothing sent (see Retries) |
|
|
131
|
-
| `{ status: "retry-later", httpStatus? }` | the provider was unavailable; the claim is released, call again later |
|
|
132
|
-
| `{ status: "halted", reason, httpStatus? }` | `mailing.provider_refused` or `mailing.quota_exceeded`: the account cannot send. The claim is released with its attempt given back; stop sending and call again once the account can send |
|
|
133
|
+
| `{ status: "retry-later", httpStatus?, retryAfterSeconds? }` | the provider was unavailable; the claim is released, call again later (after `retryAfterSeconds` when the provider named a wait), or let a job do it (Deliveries through jobs) |
|
|
134
|
+
| `{ status: "halted", reason, httpStatus?, retryAfterSeconds? }` | `mailing.provider_refused` or `mailing.quota_exceeded`: the account cannot send. The claim is released with its attempt given back; stop sending and call again once the account can send |
|
|
133
135
|
|
|
134
136
|
- **Scopes** name what the mail is about and are the only registration a lifecycle mail needs:
|
|
135
137
|
`<module>.<event>:<entity>` (`billing.trial-ending:sub_42`, `waitlist.welcome:<signup id>`).
|
|
@@ -153,6 +155,69 @@ const outcome = await deliverOnce({
|
|
|
153
155
|
claim cannot overwrite the one that took over.
|
|
154
156
|
- A malformed scope or kind throws (a bug); a database failure propagates.
|
|
155
157
|
|
|
158
|
+
### Deliveries through jobs: `@softure-ai/mailing/jobs`
|
|
159
|
+
|
|
160
|
+
A mail sent from a request path (a welcome mail, a notice) should not be lost when the provider is down, nor slow
|
|
161
|
+
the request. With `@softure-ai/jobs` (an optional peer dependency) the app enqueues the delivery in the same
|
|
162
|
+
transaction as its own write, and the jobs worker sends it through `deliverOnce`, retrying until it is sent. The
|
|
163
|
+
job runs at least once; the ledger and its idempotency key make the mail go out once.
|
|
164
|
+
|
|
165
|
+
```ts
|
|
166
|
+
// softure.config.ts: a queue for the mail, next to the mailing module
|
|
167
|
+
jobs({ queues: { "mail.welcome": { maxAttempts: 8, backoff: { baseSeconds: 30, maxSeconds: 3600 } } } }),
|
|
168
|
+
mailing({ from: "Example <hello@mail.example.com>", provider: resend() }),
|
|
169
|
+
|
|
170
|
+
// src/jobs/handlers.ts: the handler builds the mail from the app's data when it runs
|
|
171
|
+
import { defineDeliveryJobHandler } from "@softure-ai/mailing/jobs";
|
|
172
|
+
|
|
173
|
+
export const handlers: JobHandlers = {
|
|
174
|
+
"mail.welcome": defineDeliveryJobHandler({
|
|
175
|
+
payload: z.object({ userId: z.string() }),
|
|
176
|
+
buildMail: async ({ userId }, { db }) => {
|
|
177
|
+
const user = await findUser(db, userId);
|
|
178
|
+
return user === null ? null : { to: user.email, subject: "Welcome", text: renderWelcome(user) };
|
|
179
|
+
},
|
|
180
|
+
}),
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
// the request: enqueue with the write, answer at once
|
|
184
|
+
import { enqueueDelivery } from "@softure-ai/mailing/jobs";
|
|
185
|
+
|
|
186
|
+
await db.transaction(async (tx) => {
|
|
187
|
+
const [user] = await tx.insert(users).values(input).returning();
|
|
188
|
+
await enqueueDelivery({ db: tx, clock, config }, { queue: "mail.welcome", scope: `app.welcome:${user.id}`, payload: { userId: user.id } });
|
|
189
|
+
});
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
- **The payload holds ids, never the mail.** `enqueueDelivery` stores `{ scope, payload }`; no address, name or
|
|
193
|
+
token reaches `jobs.jobs`. `buildMail` reads the app's data on every attempt, before the ledger is read, so it
|
|
194
|
+
must have no side effects and return the same mail each time: the provider refuses a different body under the
|
|
195
|
+
same idempotency key (`mailing.rejected`). A link with a one-time token must be reproducible per request (derive
|
|
196
|
+
the token from the request's row, or store it encrypted), never minted per attempt. `buildMail` returning `null`
|
|
197
|
+
ends the job as done with nothing sent (the account is gone).
|
|
198
|
+
- **The scope is the ledger key and the job's dedup key.** One scope per mail the app means to send once:
|
|
199
|
+
`auth.password-reset:<request id>`, `app.welcome:<user id>`. While a job for it is pending or running, a second
|
|
200
|
+
`enqueueDelivery` returns that job (`isDuplicate: true`). `runAt`, `delaySeconds` and `maxAttempts` pass through to
|
|
201
|
+
`enqueueJob`; a malformed scope throws.
|
|
202
|
+
- **Outcomes** (`getDeliveryJobResult`, also exported for an app that writes its own handler):
|
|
203
|
+
|
|
204
|
+
| `deliverOnce` | Job |
|
|
205
|
+
| --- | --- |
|
|
206
|
+
| `sent`, `done` | succeeded |
|
|
207
|
+
| `rejected` (`invalid_input`, `suppressed`, `rejected`) | failed at once with the reason |
|
|
208
|
+
| `retry-later` | retried after the provider's `Retry-After`, else after the queue's backoff (`mailing.unavailable`) |
|
|
209
|
+
| `halted` | the same, with `mailing.quota_exceeded` or `mailing.provider_refused`: the job waits while the account is fixed |
|
|
210
|
+
| `in-flight` | retried once the other sender's claim could be stale (`staleClaimMs`), as `mailing.delivery_in_flight` |
|
|
211
|
+
| `uncertain` | failed as `mailing.delivery_uncertain`: the operator checks the provider's log first |
|
|
212
|
+
|
|
213
|
+
- **Attempts are the job's.** The handler calls `deliverOnce` with `maxAttempts: null`, so the queue's
|
|
214
|
+
`maxAttempts` decides when a delivery stops; the ledger never closes it as rejected for being unavailable. A job
|
|
215
|
+
that runs out of attempts leaves the ledger row `pending`: `retryJob(ctx, id)` (`@softure-ai/jobs/server`) runs it
|
|
216
|
+
again, and the ledger still sends at most once. `deliverOptions` passes the claim windows
|
|
217
|
+
(`staleClaimMs`, `uncertainClaimMs`, `retakeUncertain`) to `deliverOnce`.
|
|
218
|
+
- Without jobs, `deliverOnce` keeps its own attempt counting (`maxAttempts`, Retries above): nothing changes for an
|
|
219
|
+
app that does not import `/jobs`. Campaigns and `runDeliveries` stay operator commands that stop at an outage.
|
|
220
|
+
|
|
156
221
|
### Many recipients at once: `runDeliveries`
|
|
157
222
|
|
|
158
223
|
Lifecycle mail (a trial that ends, a renewal) goes to every recipient a query finds, each at most
|
|
@@ -410,8 +475,9 @@ It reports each check as `pass`, `warn` or `fail`, exiting 1 on any `fail`:
|
|
|
410
475
|
- **INBOUND** (`inbound: "cloudflare"`, `--inbound cloudflare`): Cloudflare Email Routing receives the reply-to
|
|
411
476
|
domain's mail (else the checked domain's). Every MX record must be under `mx.cloudflare.net`
|
|
412
477
|
(`unexpected-target` otherwise, `null-mx`, `missing`), and the domain must publish exactly one SPF record
|
|
413
|
-
(`multiple`) that includes `_spf.mx.cloudflare.net` (`missing-include`)
|
|
414
|
-
the
|
|
478
|
+
(`multiple`) that includes `_spf.mx.cloudflare.net` (`missing-include`) and ends in `-all` or `~all` (`weak`
|
|
479
|
+
otherwise: `?all`, `+all`, a bare `all` or no `all` lets anyone send as the domain; the first `all` decides, and a
|
|
480
|
+
`redirect=` is not followed). The report's `inbound` holds the MX and the SPF check, `[]` when not asked.
|
|
415
481
|
|
|
416
482
|
It cannot see whether the provider signs with the published key. The function takes `resolveTxt`, `resolveMx` and
|
|
417
483
|
`resolveCname` for tests; by default it asks the system resolver.
|
|
@@ -422,7 +488,8 @@ It cannot see whether the provider signs with the published key. The function ta
|
|
|
422
488
|
npm install @softure-ai/mailing @softure-ai/core @softure-ai/db @softure-ai/ui drizzle-orm
|
|
423
489
|
```
|
|
424
490
|
|
|
425
|
-
Peer dependencies: `drizzle-orm`,
|
|
491
|
+
Peer dependencies: `drizzle-orm`, `next` and `react` for the `/next` adapter, and `@softure-ai/jobs` (optional)
|
|
492
|
+
for the `/jobs` entry (`npm install @softure-ai/jobs`). The module needs a
|
|
426
493
|
database (its suppression list). The `/testing` entry imports `node:fs`.
|
|
427
494
|
|
|
428
495
|
## 3. Configuration
|
|
@@ -590,7 +657,8 @@ classes or its own layout (section 4).
|
|
|
590
657
|
|
|
591
658
|
## 9. Copy
|
|
592
659
|
|
|
593
|
-
`src/messages/
|
|
660
|
+
`src/messages/en.ts`: `errors.mailing.{invalid_input,rejected,unavailable,suppressed,provider_refused,quota_exceeded,delivery_in_flight,delivery_uncertain}`
|
|
661
|
+
(the last two are job codes, for an admin screen that shows a delivery job's state),
|
|
594
662
|
`footer.{text,htmlLead,htmlLink}` (the list-mail footer, in the app's locale) and
|
|
595
663
|
`unsubscribe.*` (the page). `getMailingErrorMessage(messages, code)` returns the copy for a code;
|
|
596
664
|
override any text with `mailing({ messages: { en: { footer: { text: "…" } } } })`.
|
|
@@ -687,7 +755,8 @@ personal data. So the module neither exports nor deletes per user (`privacy: { e
|
|
|
687
755
|
|
|
688
756
|
- One recipient per call; no cc, bcc or attachments.
|
|
689
757
|
- `sendMail` does not retry: the caller retries `unavailable` with the same `idempotencyKey` (Resend
|
|
690
|
-
keeps keys for 24 hours),
|
|
758
|
+
keeps keys for 24 hours), uses `deliverOnce`, which keeps the state between runs, or enqueues it with
|
|
759
|
+
`enqueueDelivery`, which retries it from a job.
|
|
691
760
|
- Exactly-once ends where the provider's idempotency window ends: a sender that dies after the
|
|
692
761
|
provider accepted a mail and before the outcome was written leaves an `uncertain` claim once
|
|
693
762
|
`uncertainClaimMs` passes; retaking it (`retakeUncertain`) may send that mail twice. No delivery,
|
package/dist/contract.d.ts
CHANGED
|
@@ -16,6 +16,12 @@ export type MailingErrorCode =
|
|
|
16
16
|
| "mailing.provider_refused"
|
|
17
17
|
/** The account's sending quota is spent (HTTP 429 that is not a rate limit). Stop sending; retry when it renews. */
|
|
18
18
|
| "mailing.quota_exceeded";
|
|
19
|
+
/** The codes a delivery job fails or retries with that are not a provider's answer (`@softure-ai/mailing/jobs`). */
|
|
20
|
+
export type DeliveryJobErrorCode =
|
|
21
|
+
/** Another sender holds a fresh claim on the delivery; the job looks again once that claim could be stale. */
|
|
22
|
+
"mailing.delivery_in_flight"
|
|
23
|
+
/** A claim older than the provider keeps its idempotency key is open: the mail may have gone out. Not retried. */
|
|
24
|
+
| "mailing.delivery_uncertain";
|
|
19
25
|
/** The codes that are about the sending account, not the mail: a run of sends stops at the first one. */
|
|
20
26
|
export declare const HALTING_ERROR_CODES: ReadonlySet<MailingErrorCode>;
|
|
21
27
|
/** Why an unsubscribe link was refused: missing, malformed, or not signed by a current secret. */
|
|
@@ -137,9 +143,13 @@ export interface SentMail {
|
|
|
137
143
|
/** The provider's name, e.g. `resend`. */
|
|
138
144
|
readonly provider: string;
|
|
139
145
|
}
|
|
140
|
-
/**
|
|
146
|
+
/**
|
|
147
|
+
* A failed send: the code, the provider's HTTP status when it answered with one, and how many seconds it asked to
|
|
148
|
+
* wait before the next try when it said so (`Retry-After`).
|
|
149
|
+
*/
|
|
141
150
|
export type SendMailFailure = Err<MailingErrorCode> & {
|
|
142
151
|
readonly httpStatus?: number;
|
|
152
|
+
readonly retryAfterSeconds?: number;
|
|
143
153
|
};
|
|
144
154
|
export type SendMailResult = Ok<SentMail> | SendMailFailure;
|
|
145
155
|
/** What a provider receives: a validated mail with the sender from the configuration and the mail's or the configured reply-to. */
|
|
@@ -156,6 +166,7 @@ export interface ProviderMessage {
|
|
|
156
166
|
/**
|
|
157
167
|
* What a provider answers. `httpStatus` is logged and returned with the failure; the mail never is. `refused` is the
|
|
158
168
|
* provider refusing the sender (key, account), `quota_exceeded` a spent sending quota; `rejected` is about this mail.
|
|
169
|
+
* `retryAfterSeconds` is the wait the provider asked for (`Retry-After`), a whole number of seconds, 0 or more.
|
|
159
170
|
*/
|
|
160
171
|
export type ProviderOutcome = {
|
|
161
172
|
readonly status: "sent";
|
|
@@ -163,6 +174,7 @@ export type ProviderOutcome = {
|
|
|
163
174
|
} | {
|
|
164
175
|
readonly status: ProviderFailureStatus;
|
|
165
176
|
readonly httpStatus?: number;
|
|
177
|
+
readonly retryAfterSeconds?: number;
|
|
166
178
|
};
|
|
167
179
|
export type ProviderFailureStatus = "rejected" | "unavailable" | "refused" | "quota_exceeded";
|
|
168
180
|
/**
|
package/dist/contract.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"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"}
|
package/dist/contract.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contract.js","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"contract.js","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AA6BA,yGAAyG;AACzG,MAAM,CAAC,MAAM,mBAAmB,GAAkC,IAAI,GAAG,CAAC,CAAC,0BAA0B,EAAE,wBAAwB,CAAC,CAAC,CAAC;AAKlI,mGAAmG;AACnG,MAAM,CAAC,MAAM,kBAAkB,GAAG,eAAe,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -15,6 +15,8 @@ export declare const mailing: import("@softure-ai/core").ModuleFactory<{
|
|
|
15
15
|
suppressed: string;
|
|
16
16
|
provider_refused: string;
|
|
17
17
|
quota_exceeded: string;
|
|
18
|
+
delivery_in_flight: string;
|
|
19
|
+
delivery_uncertain: string;
|
|
18
20
|
};
|
|
19
21
|
};
|
|
20
22
|
footer: {
|
|
@@ -62,9 +64,9 @@ export declare const mailing: import("@softure-ai/core").ModuleFactory<{
|
|
|
62
64
|
}, import("zod/v4/core").$strict>>;
|
|
63
65
|
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";
|
|
64
66
|
export { HALTING_ERROR_CODES, TRANSACTIONAL_KIND } from "./contract.js";
|
|
65
|
-
export type { CampaignRecipient, CampaignRecipientFilter, CampaignRecipientSource, LegacyUnsubscribe, LegacyUnsubscribeParams, MailingErrorCode, MailProvider, OnUnsubscribedHook, OutgoingMail, ProviderFailureStatus, ProviderMessage, ProviderOutcome, SendMailFailure, SendMailOptions, SendMailResult, SentMail, SuppressionSource, UnsubscribeErrorCode, UnsubscribeEvent, VerifiedUnsubscribeLink, } from "./contract.js";
|
|
67
|
+
export type { CampaignRecipient, CampaignRecipientFilter, CampaignRecipientSource, DeliveryJobErrorCode, LegacyUnsubscribe, LegacyUnsubscribeParams, MailingErrorCode, MailProvider, OnUnsubscribedHook, OutgoingMail, ProviderFailureStatus, ProviderMessage, ProviderOutcome, SendMailFailure, SendMailOptions, SendMailResult, SentMail, SuppressionSource, UnsubscribeErrorCode, UnsubscribeEvent, VerifiedUnsubscribeLink, } from "./contract.js";
|
|
66
68
|
export { getMailingErrorMessage, mailingMessages, type MailingMessages } from "./messages/index.js";
|
|
67
69
|
export { DEFAULT_STALE_CLAIM_MS, DEFAULT_TIMEOUT_MS, DEFAULT_UNCERTAIN_CLAIM_MS, type MailingOptions, type MailingOptionsInput } from "./options.js";
|
|
68
|
-
export { resend, RESEND_API_KEY_ENV, RESEND_ENDPOINT, type ResendOptions } from "./providers/resend.js";
|
|
70
|
+
export { readRetryAfter, resend, RESEND_API_KEY_ENV, RESEND_ENDPOINT, type ResendOptions } from "./providers/resend.js";
|
|
69
71
|
export { campaigns, deliveries, mailingSchema, suppressions, type DeliveryStatus } from "./schema.js";
|
|
70
72
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +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
|
|
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,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACxE,YAAY,EACV,iBAAiB,EACjB,uBAAuB,EACvB,uBAAuB,EACvB,oBAAoB,EACpB,iBAAiB,EACjB,uBAAuB,EACvB,gBAAgB,EAChB,YAAY,EACZ,kBAAkB,EAClB,YAAY,EACZ,qBAAqB,EACrB,eAAe,EACf,eAAe,EACf,eAAe,EACf,eAAe,EACf,cAAc,EACd,QAAQ,EACR,iBAAiB,EACjB,oBAAoB,EACpB,gBAAgB,EAChB,uBAAuB,GACxB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,sBAAsB,EAAE,eAAe,EAAE,KAAK,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACpG,OAAO,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,0BAA0B,EAAE,KAAK,cAAc,EAAE,KAAK,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACrJ,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,kBAAkB,EAAE,eAAe,EAAE,KAAK,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACxH,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,aAAa,EAAE,YAAY,EAAE,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -15,8 +15,8 @@ export const MODULE_ID = "mailing";
|
|
|
15
15
|
export const mailing = defineModule({
|
|
16
16
|
manifest: {
|
|
17
17
|
id: MODULE_ID,
|
|
18
|
-
version: "0.1
|
|
19
|
-
dependsOn: {},
|
|
18
|
+
version: "0.2.1",
|
|
19
|
+
dependsOn: { jobs: "*?" },
|
|
20
20
|
dbSchema: "mailing",
|
|
21
21
|
tables: ["suppressions", "campaigns", "deliveries"],
|
|
22
22
|
env: [
|
|
@@ -54,6 +54,6 @@ export { isMailbox, isMailKind, isSingleAddress, LIST_MAIL_HEADERS, MAX_ADDRESS_
|
|
|
54
54
|
export { HALTING_ERROR_CODES, TRANSACTIONAL_KIND } from "./contract.js";
|
|
55
55
|
export { getMailingErrorMessage, mailingMessages } from "./messages/index.js";
|
|
56
56
|
export { DEFAULT_STALE_CLAIM_MS, DEFAULT_TIMEOUT_MS, DEFAULT_UNCERTAIN_CLAIM_MS } from "./options.js";
|
|
57
|
-
export { resend, RESEND_API_KEY_ENV, RESEND_ENDPOINT } from "./providers/resend.js";
|
|
57
|
+
export { readRetryAfter, resend, RESEND_API_KEY_ENV, RESEND_ENDPOINT } from "./providers/resend.js";
|
|
58
58
|
export { campaigns, deliveries, mailingSchema, suppressions } from "./schema.js";
|
|
59
59
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +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,
|
|
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,IAAI,EAAE,IAAI,EAAE;QACzB,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,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAwBxE,OAAO,EAAE,sBAAsB,EAAE,eAAe,EAAwB,MAAM,qBAAqB,CAAC;AACpG,OAAO,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,0BAA0B,EAAiD,MAAM,cAAc,CAAC;AACrJ,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,kBAAkB,EAAE,eAAe,EAAsB,MAAM,uBAAuB,CAAC;AACxH,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,aAAa,EAAE,YAAY,EAAuB,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { type Ok } from "@softure-ai/core";
|
|
2
|
+
import { type JobFailure, type JobHandler, type JobHandlerContext } from "@softure-ai/jobs";
|
|
3
|
+
import { type EnqueuedJob, type JobsContext } from "@softure-ai/jobs/server";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import type { OutgoingMail } from "../contract.js";
|
|
6
|
+
export type { DeliveryJobErrorCode } from "../contract.js";
|
|
7
|
+
/** What a delivery job returns: done, or a failure the jobs worker retries or fails. */
|
|
8
|
+
export type DeliveryJobResult = Ok<undefined> | JobFailure;
|
|
9
|
+
import { type DeliverOptions, type DeliveryOutcome } from "../server/deliveries.js";
|
|
10
|
+
export interface EnqueueDeliveryInput<TPayload> {
|
|
11
|
+
/** A queue defined in `jobs({ queues })` whose handler is a `defineDeliveryJobHandler`. */
|
|
12
|
+
readonly queue: string;
|
|
13
|
+
/**
|
|
14
|
+
* The ledger scope of the mail (`deliverOnce`'s `scope`), e.g. `auth.password-reset:<request id>`. Also the job's
|
|
15
|
+
* dedup key: while a job for this scope is pending or running, a second enqueue returns it.
|
|
16
|
+
*/
|
|
17
|
+
readonly scope: string;
|
|
18
|
+
/** The app's ids the handler builds the mail from. JSON; no addresses, names or tokens. */
|
|
19
|
+
readonly payload: TPayload;
|
|
20
|
+
/** When the job becomes due; default now. Not together with `delaySeconds`. */
|
|
21
|
+
readonly runAt?: Date;
|
|
22
|
+
readonly delaySeconds?: number;
|
|
23
|
+
/** Overrides the queue's `maxAttempts` for this job. */
|
|
24
|
+
readonly maxAttempts?: number;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Enqueues a delivery. Pass the caller's transaction as `ctx.db`, so the job exists exactly when the caller's own
|
|
28
|
+
* change commits. Throws for a malformed scope, and like `enqueueJob` (an unknown queue, a database failure).
|
|
29
|
+
*/
|
|
30
|
+
export declare function enqueueDelivery<TPayload>(ctx: JobsContext, input: EnqueueDeliveryInput<TPayload>): Promise<EnqueuedJob>;
|
|
31
|
+
/** What `buildMail` receives besides the app's payload: the job's context and the delivery's scope. */
|
|
32
|
+
export interface DeliveryJobContext extends JobHandlerContext {
|
|
33
|
+
readonly scope: string;
|
|
34
|
+
}
|
|
35
|
+
export interface DeliveryJobHandlerOptions<TSchema extends z.ZodType> {
|
|
36
|
+
/** The schema of the app's payload (the `payload` given to `enqueueDelivery`). */
|
|
37
|
+
readonly payload: TSchema;
|
|
38
|
+
/**
|
|
39
|
+
* Builds the mail from the app's data. Called on every attempt, before the ledger is read, so it must have no side
|
|
40
|
+
* effects and return the same mail each time: a different body under the same idempotency key is refused by the
|
|
41
|
+
* provider. Return `null` when there is nothing to send any more (the account is gone); the job then succeeds.
|
|
42
|
+
*/
|
|
43
|
+
readonly buildMail: (payload: z.output<TSchema>, context: DeliveryJobContext) => Promise<OutgoingMail | null>;
|
|
44
|
+
/** The ledger's claim windows; `maxAttempts` is the job's, so the ledger does not count attempts here. */
|
|
45
|
+
readonly deliverOptions?: Omit<DeliverOptions, "maxAttempts">;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* A jobs handler that sends one delivery through the ledger (`deliverOnce`) and turns its outcome into the job's
|
|
49
|
+
* (`getDeliveryJobResult`). Register it for the queue `enqueueDelivery` writes to:
|
|
50
|
+
*
|
|
51
|
+
* export const handlers: JobHandlers = {
|
|
52
|
+
* "mail.welcome": defineDeliveryJobHandler({ payload: z.object({ userId: z.string() }), buildMail: buildWelcomeMail }),
|
|
53
|
+
* };
|
|
54
|
+
*/
|
|
55
|
+
export declare function defineDeliveryJobHandler<TSchema extends z.ZodType>(options: DeliveryJobHandlerOptions<TSchema>): JobHandler<{
|
|
56
|
+
scope: string;
|
|
57
|
+
payload: z.output<TSchema>;
|
|
58
|
+
}>;
|
|
59
|
+
/**
|
|
60
|
+
* Turns a `deliverOnce` outcome into a job result, for an app that writes its own handler:
|
|
61
|
+
*
|
|
62
|
+
* - `sent`, `done`: done.
|
|
63
|
+
* - `rejected` (invalid, suppressed, refused mail): failed at once with its reason.
|
|
64
|
+
* - `retry-later`, `halted` (the provider is down, the account refused or out of quota): retried after the provider's
|
|
65
|
+
* `Retry-After`, else after the queue's backoff.
|
|
66
|
+
* - `in-flight`: retried once the other sender's claim could be stale (`staleClaimMs`).
|
|
67
|
+
* - `uncertain`: failed with `mailing.delivery_uncertain`; the operator decides whether to send it again.
|
|
68
|
+
*/
|
|
69
|
+
export declare function getDeliveryJobResult(outcome: DeliveryOutcome, options: {
|
|
70
|
+
readonly staleClaimMs: number;
|
|
71
|
+
}): DeliveryJobResult;
|
|
72
|
+
//# sourceMappingURL=delivery-job.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delivery-job.d.ts","sourceRoot":"","sources":["../../src/jobs/delivery-job.ts"],"names":[],"mappings":"AAIA,OAAO,EAAW,KAAK,EAAE,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAA4C,KAAK,UAAU,EAAE,KAAK,UAAU,EAAE,KAAK,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACtI,OAAO,EAAc,KAAK,WAAW,EAAE,KAAK,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACzF,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACnD,YAAY,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAE3D,wFAAwF;AACxF,MAAM,MAAM,iBAAiB,GAAG,EAAE,CAAC,SAAS,CAAC,GAAG,UAAU,CAAC;AAC3D,OAAO,EAAgC,KAAK,cAAc,EAAE,KAAK,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAGlH,MAAM,WAAW,oBAAoB,CAAC,QAAQ;IAC5C,2FAA2F;IAC3F,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,2FAA2F;IAC3F,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC;IAC3B,+EAA+E;IAC/E,QAAQ,CAAC,KAAK,CAAC,EAAE,IAAI,CAAC;IACtB,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,wDAAwD;IACxD,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED;;;GAGG;AACH,wBAAsB,eAAe,CAAC,QAAQ,EAAE,GAAG,EAAE,WAAW,EAAE,KAAK,EAAE,oBAAoB,CAAC,QAAQ,CAAC,GAAG,OAAO,CAAC,WAAW,CAAC,CAY7H;AAED,uGAAuG;AACvG,MAAM,WAAW,kBAAmB,SAAQ,iBAAiB;IAC3D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,yBAAyB,CAAC,OAAO,SAAS,CAAC,CAAC,OAAO;IAClE,kFAAkF;IAClF,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B;;;;OAIG;IACH,QAAQ,CAAC,SAAS,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,kBAAkB,KAAK,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;IAC9G,0GAA0G;IAC1G,QAAQ,CAAC,cAAc,CAAC,EAAE,IAAI,CAAC,cAAc,EAAE,aAAa,CAAC,CAAC;CAC/D;AAED;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,SAAS,CAAC,CAAC,OAAO,EAChE,OAAO,EAAE,yBAAyB,CAAC,OAAO,CAAC,GAC1C,UAAU,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;CAAE,CAAC,CAgB3D;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,eAAe,EAAE,OAAO,EAAE;IAAE,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GAAG,iBAAiB,CAiB5H"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
// Deliveries through @softure-ai/jobs: the app enqueues a delivery in its own transaction and the jobs worker sends
|
|
2
|
+
// it, retrying while the provider is unavailable. The job runs at least once; the delivery ledger and its idempotency
|
|
3
|
+
// key make the mail go out once. The job payload holds the ledger scope and the app's ids, never the mail: the
|
|
4
|
+
// handler builds the mail from the app's data when it runs.
|
|
5
|
+
import { err, ok } from "@softure-ai/core";
|
|
6
|
+
import { defineJobHandler, failJob, retryJobLater } from "@softure-ai/jobs";
|
|
7
|
+
import { enqueueJob } from "@softure-ai/jobs/server";
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
import { deliverOnce, isDeliveryScope } from "../server/deliveries.js";
|
|
10
|
+
import { getMailingOptions } from "../server/options.js";
|
|
11
|
+
/**
|
|
12
|
+
* Enqueues a delivery. Pass the caller's transaction as `ctx.db`, so the job exists exactly when the caller's own
|
|
13
|
+
* change commits. Throws for a malformed scope, and like `enqueueJob` (an unknown queue, a database failure).
|
|
14
|
+
*/
|
|
15
|
+
export async function enqueueDelivery(ctx, input) {
|
|
16
|
+
if (!isDeliveryScope(input.scope)) {
|
|
17
|
+
throw new Error(`@softure-ai/mailing: enqueueDelivery scope "${input.scope}" must be lowercase letters, digits and ._:- (at most 128 characters)`);
|
|
18
|
+
}
|
|
19
|
+
return enqueueJob(ctx, {
|
|
20
|
+
queue: input.queue,
|
|
21
|
+
payload: { scope: input.scope, payload: input.payload },
|
|
22
|
+
dedupKey: input.scope,
|
|
23
|
+
...(input.runAt === undefined ? {} : { runAt: input.runAt }),
|
|
24
|
+
...(input.delaySeconds === undefined ? {} : { delaySeconds: input.delaySeconds }),
|
|
25
|
+
...(input.maxAttempts === undefined ? {} : { maxAttempts: input.maxAttempts }),
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* A jobs handler that sends one delivery through the ledger (`deliverOnce`) and turns its outcome into the job's
|
|
30
|
+
* (`getDeliveryJobResult`). Register it for the queue `enqueueDelivery` writes to:
|
|
31
|
+
*
|
|
32
|
+
* export const handlers: JobHandlers = {
|
|
33
|
+
* "mail.welcome": defineDeliveryJobHandler({ payload: z.object({ userId: z.string() }), buildMail: buildWelcomeMail }),
|
|
34
|
+
* };
|
|
35
|
+
*/
|
|
36
|
+
export function defineDeliveryJobHandler(options) {
|
|
37
|
+
// The cast keeps the payload's own output type, which zod's object inference widens for a generic schema.
|
|
38
|
+
const schema = z.object({
|
|
39
|
+
scope: z.string().refine(isDeliveryScope),
|
|
40
|
+
payload: options.payload,
|
|
41
|
+
});
|
|
42
|
+
return defineJobHandler({
|
|
43
|
+
payload: schema,
|
|
44
|
+
run: async ({ scope, payload }, context) => {
|
|
45
|
+
const mail = await options.buildMail(payload, { ...context, scope });
|
|
46
|
+
if (mail === null)
|
|
47
|
+
return ok();
|
|
48
|
+
const outcome = await deliverOnce(context, { scope, mail }, { ...options.deliverOptions, maxAttempts: null });
|
|
49
|
+
const staleClaimMs = options.deliverOptions?.staleClaimMs ?? getMailingOptions(context.config).staleClaimMs;
|
|
50
|
+
return getDeliveryJobResult(outcome, { staleClaimMs });
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Turns a `deliverOnce` outcome into a job result, for an app that writes its own handler:
|
|
56
|
+
*
|
|
57
|
+
* - `sent`, `done`: done.
|
|
58
|
+
* - `rejected` (invalid, suppressed, refused mail): failed at once with its reason.
|
|
59
|
+
* - `retry-later`, `halted` (the provider is down, the account refused or out of quota): retried after the provider's
|
|
60
|
+
* `Retry-After`, else after the queue's backoff.
|
|
61
|
+
* - `in-flight`: retried once the other sender's claim could be stale (`staleClaimMs`).
|
|
62
|
+
* - `uncertain`: failed with `mailing.delivery_uncertain`; the operator decides whether to send it again.
|
|
63
|
+
*/
|
|
64
|
+
export function getDeliveryJobResult(outcome, options) {
|
|
65
|
+
switch (outcome.status) {
|
|
66
|
+
case "sent":
|
|
67
|
+
case "done":
|
|
68
|
+
return ok();
|
|
69
|
+
case "rejected":
|
|
70
|
+
return failJob(outcome.reason);
|
|
71
|
+
case "retry-later":
|
|
72
|
+
return retryAfter("mailing.unavailable", outcome.retryAfterSeconds);
|
|
73
|
+
case "halted":
|
|
74
|
+
return retryAfter(outcome.reason, outcome.retryAfterSeconds);
|
|
75
|
+
case "in-flight":
|
|
76
|
+
// One second past the window: a claim exactly `staleClaimMs` old is still fresh.
|
|
77
|
+
return retryJobLater("mailing.delivery_in_flight", Math.ceil(options.staleClaimMs / 1000) + 1);
|
|
78
|
+
case "uncertain":
|
|
79
|
+
return failJob("mailing.delivery_uncertain");
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
function retryAfter(code, seconds) {
|
|
83
|
+
return seconds === undefined ? err(code) : retryJobLater(code, seconds);
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=delivery-job.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delivery-job.js","sourceRoot":"","sources":["../../src/jobs/delivery-job.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/jobs/index.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,wBAAwB,EACxB,eAAe,EACf,oBAAoB,EACpB,KAAK,kBAAkB,EACvB,KAAK,oBAAoB,EACzB,KAAK,yBAAyB,EAC9B,KAAK,iBAAiB,EACtB,KAAK,oBAAoB,GAC1B,MAAM,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
// The jobs adapter of @softure-ai/mailing: deliveries sent by the @softure-ai/jobs worker, with retries (an optional
|
|
2
|
+
// peer dependency; only apps that import this entry need it).
|
|
3
|
+
export { defineDeliveryJobHandler, enqueueDelivery, getDeliveryJobResult, } from "./delivery-job.js";
|
|
4
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/jobs/index.ts"],"names":[],"mappings":"AAAA,qHAAqH;AACrH,8DAA8D;AAC9D,OAAO,EACL,wBAAwB,EACxB,eAAe,EACf,oBAAoB,GAMrB,MAAM,mBAAmB,CAAC"}
|
package/dist/messages/en.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"en.d.ts","sourceRoot":"","sources":["../../src/messages/en.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,EAAE
|
|
1
|
+
{"version":3,"file":"en.d.ts","sourceRoot":"","sources":["../../src/messages/en.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4Bd,CAAC"}
|
package/dist/messages/en.js
CHANGED
|
@@ -7,6 +7,8 @@ export const en = {
|
|
|
7
7
|
suppressed: "This address unsubscribed from these emails, so nothing was sent.",
|
|
8
8
|
provider_refused: "The mail service refused our account, so nothing was sent. We need to fix it on our side.",
|
|
9
9
|
quota_exceeded: "We reached our sending limit for now. Try again later.",
|
|
10
|
+
delivery_in_flight: "This message is being sent right now.",
|
|
11
|
+
delivery_uncertain: "This message may already have been sent. Check before sending it again.",
|
|
10
12
|
},
|
|
11
13
|
},
|
|
12
14
|
footer: {
|
package/dist/messages/en.js.map
CHANGED
|
@@ -1 +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;YAC/E,gBAAgB,EAAE,2FAA2F;YAC7G,cAAc,EAAE,wDAAwD;
|
|
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;YAC/E,gBAAgB,EAAE,2FAA2F;YAC7G,cAAc,EAAE,wDAAwD;YACxE,kBAAkB,EAAE,uCAAuC;YAC3D,kBAAkB,EAAE,yEAAyE;SAC9F;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"}
|
package/dist/messages/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { MailingErrorCode } from "../contract.js";
|
|
1
|
+
import type { DeliveryJobErrorCode, MailingErrorCode } from "../contract.js";
|
|
2
2
|
import { en } from "./en.js";
|
|
3
|
-
/**
|
|
3
|
+
/** The built-in English dictionary; an app passes partial overrides or its own translation per locale. */
|
|
4
4
|
export declare const mailingMessages: {
|
|
5
5
|
en: {
|
|
6
6
|
errors: {
|
|
@@ -11,33 +11,8 @@ export declare const mailingMessages: {
|
|
|
11
11
|
suppressed: string;
|
|
12
12
|
provider_refused: string;
|
|
13
13
|
quota_exceeded: string;
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
footer: {
|
|
17
|
-
text: string;
|
|
18
|
-
htmlLead: string;
|
|
19
|
-
htmlLink: string;
|
|
20
|
-
};
|
|
21
|
-
unsubscribe: {
|
|
22
|
-
title: string;
|
|
23
|
-
lead: string;
|
|
24
|
-
submit: string;
|
|
25
|
-
doneTitle: string;
|
|
26
|
-
doneBody: string;
|
|
27
|
-
invalidTitle: string;
|
|
28
|
-
invalidBody: string;
|
|
29
|
-
failed: string;
|
|
30
|
-
};
|
|
31
|
-
};
|
|
32
|
-
pl: {
|
|
33
|
-
errors: {
|
|
34
|
-
mailing: {
|
|
35
|
-
invalid_input: string;
|
|
36
|
-
rejected: string;
|
|
37
|
-
unavailable: string;
|
|
38
|
-
suppressed: string;
|
|
39
|
-
provider_refused: string;
|
|
40
|
-
quota_exceeded: string;
|
|
14
|
+
delivery_in_flight: string;
|
|
15
|
+
delivery_uncertain: string;
|
|
41
16
|
};
|
|
42
17
|
};
|
|
43
18
|
footer: {
|
|
@@ -59,5 +34,5 @@ export declare const mailingMessages: {
|
|
|
59
34
|
};
|
|
60
35
|
export type MailingMessages = typeof en;
|
|
61
36
|
/** The copy for a mailing error code; a code the dictionary does not know reads as `unavailable`. */
|
|
62
|
-
export declare function getMailingErrorMessage(messages: MailingMessages, code: MailingErrorCode): string;
|
|
37
|
+
export declare function getMailingErrorMessage(messages: MailingMessages, code: MailingErrorCode | DeliveryJobErrorCode): string;
|
|
63
38
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/messages/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/messages/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAC7E,OAAO,EAAE,EAAE,EAAE,MAAM,SAAS,CAAC;AAE7B,0GAA0G;AAC1G,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAAS,CAAC;AAEtC,MAAM,MAAM,eAAe,GAAG,OAAO,EAAE,CAAC;AAExC,qGAAqG;AACrG,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,eAAe,EAAE,IAAI,EAAE,gBAAgB,GAAG,oBAAoB,GAAG,MAAM,CAIvH"}
|
package/dist/messages/index.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { en } from "./en.js";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
export const mailingMessages = { en, pl };
|
|
2
|
+
/** The built-in English dictionary; an app passes partial overrides or its own translation per locale. */
|
|
3
|
+
export const mailingMessages = { en };
|
|
5
4
|
/** The copy for a mailing error code; a code the dictionary does not know reads as `unavailable`. */
|
|
6
5
|
export function getMailingErrorMessage(messages, code) {
|
|
7
6
|
const group = messages.errors.mailing;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/messages/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,EAAE,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/messages/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,EAAE,EAAE,MAAM,SAAS,CAAC;AAE7B,0GAA0G;AAC1G,MAAM,CAAC,MAAM,eAAe,GAAG,EAAE,EAAE,EAAE,CAAC;AAItC,qGAAqG;AACrG,MAAM,UAAU,sBAAsB,CAAC,QAAyB,EAAE,IAA6C;IAC7G,MAAM,KAAK,GAAqC,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC;IACxE,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;IAC3C,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC;AACvG,CAAC"}
|
package/dist/next/messages.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type SoftureConfig } from "@softure-ai/core";
|
|
2
2
|
import type { MailingMessages } from "../messages/index.js";
|
|
3
3
|
/** The module's copy in the app's locale, with the app's overrides applied. */
|
|
4
4
|
export declare function getMailingMessages(config: SoftureConfig): MailingMessages;
|