@softure-ai/mailing 0.2.0 → 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 +27 -0
- package/README.md +73 -5
- 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 +4 -2
- package/dist/messages/index.d.ts.map +1 -1
- package/dist/messages/index.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/send-mail.d.ts +2 -1
- package/dist/server/send-mail.d.ts.map +1 -1
- package/dist/server/send-mail.js +17 -5
- package/dist/server/send-mail.js.map +1 -1
- package/module.json +2 -2
- package/package.json +12 -1
- 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 +2 -2
- package/src/providers/resend.ts +22 -3
- package/src/server/deliveries.ts +9 -5
- package/src/server/send-mail.ts +18 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resend.js","sourceRoot":"","sources":["../../src/providers/resend.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"resend.js","sourceRoot":"","sources":["../../src/providers/resend.ts"],"names":[],"mappings":"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"}
|
|
@@ -75,10 +75,14 @@ export type DeliveryOutcome =
|
|
|
75
75
|
| {
|
|
76
76
|
readonly status: "uncertain";
|
|
77
77
|
}
|
|
78
|
-
/**
|
|
78
|
+
/**
|
|
79
|
+
* The provider was unavailable; the claim is released for a later run. `retryAfterSeconds` is the wait the provider
|
|
80
|
+
* asked for, when it named one.
|
|
81
|
+
*/
|
|
79
82
|
| {
|
|
80
83
|
readonly status: "retry-later";
|
|
81
84
|
readonly httpStatus?: number;
|
|
85
|
+
readonly retryAfterSeconds?: number;
|
|
82
86
|
}
|
|
83
87
|
/**
|
|
84
88
|
* The provider refused the account or its quota is spent: the claim is released, its attempt given back. Stop
|
|
@@ -88,6 +92,7 @@ export type DeliveryOutcome =
|
|
|
88
92
|
readonly status: "halted";
|
|
89
93
|
readonly reason: HaltingErrorCode;
|
|
90
94
|
readonly httpStatus?: number;
|
|
95
|
+
readonly retryAfterSeconds?: number;
|
|
91
96
|
};
|
|
92
97
|
/**
|
|
93
98
|
* Sends `delivery.mail` unless the ledger already has an outcome (or a fresh claim) for its scope
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deliveries.d.ts","sourceRoot":"","sources":["../../src/server/deliveries.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAGhD,OAAO,EAA2C,KAAK,gBAAgB,EAAE,KAAK,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACnH,OAAO,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,0BAA0B,EAAE,MAAM,eAAe,CAAC;AAQzG,MAAM,MAAM,eAAe,GAAG,aAAa,CAAC,SAAS,CAAC,CAAC;AAEvD,OAAO,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,0BAA0B,EAAE,CAAC;AAKpF,MAAM,WAAW,QAAQ;IACvB;;;;OAIG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,8FAA8F;IAC9F,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAC5B,oFAAoF;IACpF,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,cAAc;IAC7B,yDAAyD;IACzD,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,2DAA2D;IAC3D,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC;;;OAGG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,OAAO,CAAC;IACnC;;;OAGG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACtC;AAED,oFAAoF;AACpF,MAAM,MAAM,gBAAgB,GAAG,0BAA0B,GAAG,wBAAwB,CAAC;AAErF;;;;GAIG;AACH,MAAM,MAAM,aAAa,GAAG,gBAAgB,GAAG,qBAAqB,CAAC;AAErE,iFAAiF;AACjF,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,kHAAkH;AAClH,wBAAgB,UAAU,CAAC,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,OAAO,GAAG,OAAO,GAAG,IAAI,CAI9F;AAED,MAAM,MAAM,eAAe;AACzB,mDAAmD;AACjD;IAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE;AAClD,4FAA4F;GAC1F;IAAE,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE;AAClG,qEAAqE;GACnE;IAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU,CAAA;CAAE;AACpE,4DAA4D;GAC1D;IAAE,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAA;CAAE;AAClC;;;GAGG;GACD;IAAE,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAA;CAAE;AAClC
|
|
1
|
+
{"version":3,"file":"deliveries.d.ts","sourceRoot":"","sources":["../../src/server/deliveries.ts"],"names":[],"mappings":"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"}
|
|
@@ -50,13 +50,14 @@ export async function deliverOnce(ctx, delivery, options = {}) {
|
|
|
50
50
|
}
|
|
51
51
|
const providerStatus = result.httpStatus ?? null;
|
|
52
52
|
const httpStatus = result.httpStatus === undefined ? {} : { httpStatus: result.httpStatus };
|
|
53
|
+
const retryAfter = result.retryAfterSeconds === undefined ? {} : { retryAfterSeconds: result.retryAfterSeconds };
|
|
53
54
|
if (isHaltingCode(result.error)) {
|
|
54
55
|
await releaseDelivery(ctx, fence, { providerStatus, giveAttemptBack: true });
|
|
55
|
-
return { status: "halted", reason: result.error, ...httpStatus };
|
|
56
|
+
return { status: "halted", reason: result.error, ...httpStatus, ...retryAfter };
|
|
56
57
|
}
|
|
57
58
|
if (result.error === "mailing.unavailable" && (maxAttempts === null || attempt < maxAttempts)) {
|
|
58
59
|
await releaseDelivery(ctx, fence, { providerStatus, giveAttemptBack: false });
|
|
59
|
-
return { status: "retry-later", ...httpStatus };
|
|
60
|
+
return { status: "retry-later", ...httpStatus, ...retryAfter };
|
|
60
61
|
}
|
|
61
62
|
await closeDelivery(ctx, fence, { status: "rejected", providerMessageId: null, reason: result.error, providerStatus });
|
|
62
63
|
return { status: "rejected", reason: result.error, ...httpStatus };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deliveries.js","sourceRoot":"","sources":["../../src/server/deliveries.ts"],"names":[],"mappings":"AAAA,kGAAkG;AAClG,mGAAmG;AACnG,mGAAmG;AACnG,kGAAkG;AAClG,iGAAiG;AACjG,8FAA8F;AAC9F,8FAA8F;AAC9F,OAAO,EAAsB,MAAM,kBAAkB,CAAC;AAEtD,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,EAAY,MAAM,aAAa,CAAC;AAClE,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAA4C,MAAM,gBAAgB,CAAC;AACnH,OAAO,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,0BAA0B,EAAE,MAAM,eAAe,CAAC;AACzG,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAIlD,OAAO,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,0BAA0B,EAAE,CAAC;AAEpF,MAAM,gBAAgB,GAAG,GAAG,CAAC;AAC7B,MAAM,KAAK,GAAG,yBAAyB,CAAC;AAgDxC,kHAAkH;AAClH,MAAM,UAAU,UAAU,CAAC,OAAwB,EAAE,gBAAyB;IAC5E,IAAI,OAAO,CAAC,MAAM,KAAK,QAAQ,IAAI,CAAC,CAAC,gBAAgB,IAAI,OAAO,CAAC,MAAM,KAAK,aAAa,CAAC;QAAE,OAAO,IAAI,CAAC;IACxG,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,qBAAqB,CAAC;IACpF,OAAO,OAAO,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,CAAC,UAAU,EAAE,CAAC;AACpG,CAAC;
|
|
1
|
+
{"version":3,"file":"deliveries.js","sourceRoot":"","sources":["../../src/server/deliveries.ts"],"names":[],"mappings":"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"}
|
|
@@ -15,7 +15,8 @@ export interface MailContext {
|
|
|
15
15
|
* `invalid_input` (fix the input), `rejected` (do not retry), `unavailable` (retry later with the
|
|
16
16
|
* same `idempotencyKey`), `suppressed` (a list mail to someone who unsubscribed; do not retry),
|
|
17
17
|
* `provider_refused` / `quota_exceeded` (the account cannot send: stop, fix or wait, then retry). A
|
|
18
|
-
* failure the provider answered with an HTTP status carries it as `httpStatus
|
|
18
|
+
* failure the provider answered with an HTTP status carries it as `httpStatus`, and the wait it asked for
|
|
19
|
+
* (`Retry-After`) as `retryAfterSeconds`.
|
|
19
20
|
* Throws only when the module is not enabled, or for list mail without `context.db`.
|
|
20
21
|
*/
|
|
21
22
|
export declare function sendMail(context: MailContext, mail: OutgoingMail, options?: SendMailOptions): Promise<SendMailResult>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"send-mail.d.ts","sourceRoot":"","sources":["../../src/server/send-mail.ts"],"names":[],"mappings":"AAKA,OAAO,EAAO,KAAK,GAAG,EAAM,KAAK,EAAE,EAAkB,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAClG,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAIL,KAAK,YAAY,EAEjB,KAAK,eAAe,EAEpB,KAAK,eAAe,EACpB,KAAK,cAAc,EACpB,MAAM,gBAAgB,CAAC;AAKxB,OAAO,EAAwG,KAAK,GAAG,EAAE,MAAM,uBAAuB,CAAC;AAGvJ;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B,QAAQ,CAAC,EAAE,CAAC,EAAE,SAAS,CAAC;CACzB;AAED
|
|
1
|
+
{"version":3,"file":"send-mail.d.ts","sourceRoot":"","sources":["../../src/server/send-mail.ts"],"names":[],"mappings":"AAKA,OAAO,EAAO,KAAK,GAAG,EAAM,KAAK,EAAE,EAAkB,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAClG,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAIL,KAAK,YAAY,EAEjB,KAAK,eAAe,EAEpB,KAAK,eAAe,EACpB,KAAK,cAAc,EACpB,MAAM,gBAAgB,CAAC;AAKxB,OAAO,EAAwG,KAAK,GAAG,EAAE,MAAM,uBAAuB,CAAC;AAGvJ;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B,QAAQ,CAAC,EAAE,CAAC,EAAE,SAAS,CAAC;CACzB;AAED;;;;;;;;GAQG;AACH,wBAAsB,QAAQ,CAAC,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,cAAc,CAAC,CA6B/H;AAED,gHAAgH;AAChH,MAAM,MAAM,kBAAkB,GAC1B,CAAC,GAAG,CAAC,uBAAuB,CAAC,GAAG;IAAE,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,CAAC,GACvE,CAAC,GAAG,CAAC,qBAAqB,CAAC,GAAG;IAAE,QAAQ,CAAC,KAAK,EAAE,uBAAuB,CAAA;CAAE,CAAC,CAAC;AAE/E,MAAM,MAAM,iBAAiB,GAAG,EAAE,CAAC,eAAe,CAAC,GAAG,kBAAkB,CAAC;AAEzE,MAAM,WAAW,kBAAmB,SAAQ,eAAe;IACzD,oEAAoE;IACpE,QAAQ,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC;CACpB;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,aAAa,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,GAAE,kBAAuB,GAAG,iBAAiB,CAO1H"}
|
package/dist/server/send-mail.js
CHANGED
|
@@ -15,7 +15,8 @@ import { validateMail } from "./validate-mail.js";
|
|
|
15
15
|
* `invalid_input` (fix the input), `rejected` (do not retry), `unavailable` (retry later with the
|
|
16
16
|
* same `idempotencyKey`), `suppressed` (a list mail to someone who unsubscribed; do not retry),
|
|
17
17
|
* `provider_refused` / `quota_exceeded` (the account cannot send: stop, fix or wait, then retry). A
|
|
18
|
-
* failure the provider answered with an HTTP status carries it as `httpStatus
|
|
18
|
+
* failure the provider answered with an HTTP status carries it as `httpStatus`, and the wait it asked for
|
|
19
|
+
* (`Retry-After`) as `retryAfterSeconds`.
|
|
19
20
|
* Throws only when the module is not enabled, or for list mail without `context.db`.
|
|
20
21
|
*/
|
|
21
22
|
export async function sendMail(context, mail, options = {}) {
|
|
@@ -40,7 +41,11 @@ export async function sendMail(context, mail, options = {}) {
|
|
|
40
41
|
}
|
|
41
42
|
const code = FAILURE_CODES[outcome.status];
|
|
42
43
|
logFailure(provider, code, { status: outcome.httpStatus });
|
|
43
|
-
return
|
|
44
|
+
return {
|
|
45
|
+
...err(code),
|
|
46
|
+
...(outcome.httpStatus === undefined ? {} : { httpStatus: outcome.httpStatus }),
|
|
47
|
+
...(outcome.retryAfterSeconds === undefined ? {} : { retryAfterSeconds: outcome.retryAfterSeconds }),
|
|
48
|
+
};
|
|
44
49
|
}
|
|
45
50
|
/**
|
|
46
51
|
* Exactly what `sendMail` would hand the provider for `mail`: the sender, the reply-to, and for list mail the
|
|
@@ -133,16 +138,23 @@ async function callProvider(provider, message, timeoutMs) {
|
|
|
133
138
|
function readOutcome(outcome) {
|
|
134
139
|
if (typeof outcome !== "object" || outcome === null)
|
|
135
140
|
return { status: "unavailable" };
|
|
136
|
-
const { status, id, httpStatus } = outcome;
|
|
141
|
+
const { status, id, httpStatus, retryAfterSeconds } = outcome;
|
|
137
142
|
if (status === "sent") {
|
|
138
143
|
return typeof id === "string" && id !== "" ? { status, id } : { status: "unavailable" };
|
|
139
144
|
}
|
|
140
145
|
if (typeof status === "string" && Object.hasOwn(FAILURE_CODES, status)) {
|
|
141
|
-
|
|
142
|
-
|
|
146
|
+
return {
|
|
147
|
+
status: status,
|
|
148
|
+
...(isHttpStatus(httpStatus) ? { httpStatus } : {}),
|
|
149
|
+
...(isRetryAfter(retryAfterSeconds) ? { retryAfterSeconds } : {}),
|
|
150
|
+
};
|
|
143
151
|
}
|
|
144
152
|
return { status: "unavailable" };
|
|
145
153
|
}
|
|
154
|
+
/** A provider's `Retry-After` in whole seconds; anything else is dropped. */
|
|
155
|
+
function isRetryAfter(value) {
|
|
156
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
157
|
+
}
|
|
146
158
|
function isHttpStatus(value) {
|
|
147
159
|
return typeof value === "number" && Number.isInteger(value) && value >= 100 && value <= 599;
|
|
148
160
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"send-mail.js","sourceRoot":"","sources":["../../src/server/send-mail.ts"],"names":[],"mappings":"AAAA,iGAAiG;AACjG,oGAAoG;AACpG,mGAAmG;AACnG,mHAAmH;AACnH,8FAA8F;AAC9F,OAAO,EAAE,GAAG,EAAY,EAAE,EAAW,cAAc,EAAsB,MAAM,kBAAkB,CAAC;AAElG,OAAO,EACL,kBAAkB,GASnB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,yBAAyB,EAAE,MAAM,gBAAgB,CAAC;AACzF,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACnE,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,qBAAqB,EAAE,6BAA6B,EAAE,sBAAsB,EAAE,sBAAsB,EAAY,MAAM,uBAAuB,CAAC;AACvJ,OAAO,EAAE,YAAY,EAAkB,MAAM,oBAAoB,CAAC;AAWlE
|
|
1
|
+
{"version":3,"file":"send-mail.js","sourceRoot":"","sources":["../../src/server/send-mail.ts"],"names":[],"mappings":"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"}
|
package/module.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@softure-ai/mailing",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "SOFTURE AI mailing module: sendMail over a provider adapter (Resend first) with plain-text and HTML bodies, sender and reply-to from config, idempotency keys, a timeout and a typed result; signed one-click unsubscribe (RFC 8058) with a suppression list for list mail; an exactly-once delivery ledger, campaigns sent from a content file by the softure-mail command, and a sender DNS check; a fake provider for tests.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -54,6 +54,11 @@
|
|
|
54
54
|
"@softure-ai/source": "./src/cli/index.ts",
|
|
55
55
|
"types": "./dist/cli/index.d.ts",
|
|
56
56
|
"default": "./dist/cli/index.js"
|
|
57
|
+
},
|
|
58
|
+
"./jobs": {
|
|
59
|
+
"@softure-ai/source": "./src/jobs/index.ts",
|
|
60
|
+
"types": "./dist/jobs/index.d.ts",
|
|
61
|
+
"default": "./dist/jobs/index.js"
|
|
57
62
|
}
|
|
58
63
|
},
|
|
59
64
|
"scripts": {
|
|
@@ -65,11 +70,17 @@
|
|
|
65
70
|
"zod": "^4.6.5"
|
|
66
71
|
},
|
|
67
72
|
"peerDependencies": {
|
|
73
|
+
"@softure-ai/jobs": "^0.1.0",
|
|
68
74
|
"@softure-ai/ui": "^0.3.0",
|
|
69
75
|
"drizzle-orm": "^0.45.2",
|
|
70
76
|
"next": "^16.0.0",
|
|
71
77
|
"react": "^19.0.0"
|
|
72
78
|
},
|
|
79
|
+
"peerDependenciesMeta": {
|
|
80
|
+
"@softure-ai/jobs": {
|
|
81
|
+
"optional": true
|
|
82
|
+
}
|
|
83
|
+
},
|
|
73
84
|
"devDependencies": {
|
|
74
85
|
"@softure-ai/ui": "^0.3.0"
|
|
75
86
|
}
|
package/src/contract.ts
CHANGED
|
@@ -20,6 +20,13 @@ export type MailingErrorCode =
|
|
|
20
20
|
/** The account's sending quota is spent (HTTP 429 that is not a rate limit). Stop sending; retry when it renews. */
|
|
21
21
|
| "mailing.quota_exceeded";
|
|
22
22
|
|
|
23
|
+
/** The codes a delivery job fails or retries with that are not a provider's answer (`@softure-ai/mailing/jobs`). */
|
|
24
|
+
export type DeliveryJobErrorCode =
|
|
25
|
+
/** Another sender holds a fresh claim on the delivery; the job looks again once that claim could be stale. */
|
|
26
|
+
| "mailing.delivery_in_flight"
|
|
27
|
+
/** A claim older than the provider keeps its idempotency key is open: the mail may have gone out. Not retried. */
|
|
28
|
+
| "mailing.delivery_uncertain";
|
|
29
|
+
|
|
23
30
|
/** The codes that are about the sending account, not the mail: a run of sends stops at the first one. */
|
|
24
31
|
export const HALTING_ERROR_CODES: ReadonlySet<MailingErrorCode> = new Set(["mailing.provider_refused", "mailing.quota_exceeded"]);
|
|
25
32
|
|
|
@@ -149,8 +156,11 @@ export interface SentMail {
|
|
|
149
156
|
readonly provider: string;
|
|
150
157
|
}
|
|
151
158
|
|
|
152
|
-
/**
|
|
153
|
-
|
|
159
|
+
/**
|
|
160
|
+
* A failed send: the code, the provider's HTTP status when it answered with one, and how many seconds it asked to
|
|
161
|
+
* wait before the next try when it said so (`Retry-After`).
|
|
162
|
+
*/
|
|
163
|
+
export type SendMailFailure = Err<MailingErrorCode> & { readonly httpStatus?: number; readonly retryAfterSeconds?: number };
|
|
154
164
|
|
|
155
165
|
export type SendMailResult = Ok<SentMail> | SendMailFailure;
|
|
156
166
|
|
|
@@ -169,10 +179,11 @@ export interface ProviderMessage {
|
|
|
169
179
|
/**
|
|
170
180
|
* What a provider answers. `httpStatus` is logged and returned with the failure; the mail never is. `refused` is the
|
|
171
181
|
* provider refusing the sender (key, account), `quota_exceeded` a spent sending quota; `rejected` is about this mail.
|
|
182
|
+
* `retryAfterSeconds` is the wait the provider asked for (`Retry-After`), a whole number of seconds, 0 or more.
|
|
172
183
|
*/
|
|
173
184
|
export type ProviderOutcome =
|
|
174
185
|
| { readonly status: "sent"; readonly id: string }
|
|
175
|
-
| { readonly status: ProviderFailureStatus; readonly httpStatus?: number };
|
|
186
|
+
| { readonly status: ProviderFailureStatus; readonly httpStatus?: number; readonly retryAfterSeconds?: number };
|
|
176
187
|
|
|
177
188
|
export type ProviderFailureStatus = "rejected" | "unavailable" | "refused" | "quota_exceeded";
|
|
178
189
|
|
package/src/index.ts
CHANGED
|
@@ -17,8 +17,8 @@ export const MODULE_ID = "mailing";
|
|
|
17
17
|
export const mailing = defineModule({
|
|
18
18
|
manifest: {
|
|
19
19
|
id: MODULE_ID,
|
|
20
|
-
version: "0.2.
|
|
21
|
-
dependsOn: {},
|
|
20
|
+
version: "0.2.1",
|
|
21
|
+
dependsOn: { jobs: "*?" },
|
|
22
22
|
dbSchema: "mailing",
|
|
23
23
|
tables: ["suppressions", "campaigns", "deliveries"],
|
|
24
24
|
env: [
|
|
@@ -69,6 +69,7 @@ export type {
|
|
|
69
69
|
CampaignRecipient,
|
|
70
70
|
CampaignRecipientFilter,
|
|
71
71
|
CampaignRecipientSource,
|
|
72
|
+
DeliveryJobErrorCode,
|
|
72
73
|
LegacyUnsubscribe,
|
|
73
74
|
LegacyUnsubscribeParams,
|
|
74
75
|
MailingErrorCode,
|
|
@@ -89,5 +90,5 @@ export type {
|
|
|
89
90
|
} from "./contract.js";
|
|
90
91
|
export { getMailingErrorMessage, mailingMessages, type MailingMessages } from "./messages/index.js";
|
|
91
92
|
export { DEFAULT_STALE_CLAIM_MS, DEFAULT_TIMEOUT_MS, DEFAULT_UNCERTAIN_CLAIM_MS, type MailingOptions, type MailingOptionsInput } from "./options.js";
|
|
92
|
-
export { resend, RESEND_API_KEY_ENV, RESEND_ENDPOINT, type ResendOptions } from "./providers/resend.js";
|
|
93
|
+
export { readRetryAfter, resend, RESEND_API_KEY_ENV, RESEND_ENDPOINT, type ResendOptions } from "./providers/resend.js";
|
|
93
94
|
export { campaigns, deliveries, mailingSchema, suppressions, type DeliveryStatus } from "./schema.js";
|
|
@@ -0,0 +1,129 @@
|
|
|
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, type Ok } from "@softure-ai/core";
|
|
6
|
+
import { defineJobHandler, failJob, retryJobLater, type JobFailure, type JobHandler, type JobHandlerContext } from "@softure-ai/jobs";
|
|
7
|
+
import { enqueueJob, type EnqueuedJob, type JobsContext } from "@softure-ai/jobs/server";
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
import type { OutgoingMail } from "../contract.js";
|
|
10
|
+
export type { DeliveryJobErrorCode } from "../contract.js";
|
|
11
|
+
|
|
12
|
+
/** What a delivery job returns: done, or a failure the jobs worker retries or fails. */
|
|
13
|
+
export type DeliveryJobResult = Ok<undefined> | JobFailure;
|
|
14
|
+
import { deliverOnce, isDeliveryScope, type DeliverOptions, type DeliveryOutcome } from "../server/deliveries.js";
|
|
15
|
+
import { getMailingOptions } from "../server/options.js";
|
|
16
|
+
|
|
17
|
+
export interface EnqueueDeliveryInput<TPayload> {
|
|
18
|
+
/** A queue defined in `jobs({ queues })` whose handler is a `defineDeliveryJobHandler`. */
|
|
19
|
+
readonly queue: string;
|
|
20
|
+
/**
|
|
21
|
+
* The ledger scope of the mail (`deliverOnce`'s `scope`), e.g. `auth.password-reset:<request id>`. Also the job's
|
|
22
|
+
* dedup key: while a job for this scope is pending or running, a second enqueue returns it.
|
|
23
|
+
*/
|
|
24
|
+
readonly scope: string;
|
|
25
|
+
/** The app's ids the handler builds the mail from. JSON; no addresses, names or tokens. */
|
|
26
|
+
readonly payload: TPayload;
|
|
27
|
+
/** When the job becomes due; default now. Not together with `delaySeconds`. */
|
|
28
|
+
readonly runAt?: Date;
|
|
29
|
+
readonly delaySeconds?: number;
|
|
30
|
+
/** Overrides the queue's `maxAttempts` for this job. */
|
|
31
|
+
readonly maxAttempts?: number;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Enqueues a delivery. Pass the caller's transaction as `ctx.db`, so the job exists exactly when the caller's own
|
|
36
|
+
* change commits. Throws for a malformed scope, and like `enqueueJob` (an unknown queue, a database failure).
|
|
37
|
+
*/
|
|
38
|
+
export async function enqueueDelivery<TPayload>(ctx: JobsContext, input: EnqueueDeliveryInput<TPayload>): Promise<EnqueuedJob> {
|
|
39
|
+
if (!isDeliveryScope(input.scope)) {
|
|
40
|
+
throw new Error(`@softure-ai/mailing: enqueueDelivery scope "${input.scope}" must be lowercase letters, digits and ._:- (at most 128 characters)`);
|
|
41
|
+
}
|
|
42
|
+
return enqueueJob(ctx, {
|
|
43
|
+
queue: input.queue,
|
|
44
|
+
payload: { scope: input.scope, payload: input.payload },
|
|
45
|
+
dedupKey: input.scope,
|
|
46
|
+
...(input.runAt === undefined ? {} : { runAt: input.runAt }),
|
|
47
|
+
...(input.delaySeconds === undefined ? {} : { delaySeconds: input.delaySeconds }),
|
|
48
|
+
...(input.maxAttempts === undefined ? {} : { maxAttempts: input.maxAttempts }),
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** What `buildMail` receives besides the app's payload: the job's context and the delivery's scope. */
|
|
53
|
+
export interface DeliveryJobContext extends JobHandlerContext {
|
|
54
|
+
readonly scope: string;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface DeliveryJobHandlerOptions<TSchema extends z.ZodType> {
|
|
58
|
+
/** The schema of the app's payload (the `payload` given to `enqueueDelivery`). */
|
|
59
|
+
readonly payload: TSchema;
|
|
60
|
+
/**
|
|
61
|
+
* Builds the mail from the app's data. Called on every attempt, before the ledger is read, so it must have no side
|
|
62
|
+
* effects and return the same mail each time: a different body under the same idempotency key is refused by the
|
|
63
|
+
* provider. Return `null` when there is nothing to send any more (the account is gone); the job then succeeds.
|
|
64
|
+
*/
|
|
65
|
+
readonly buildMail: (payload: z.output<TSchema>, context: DeliveryJobContext) => Promise<OutgoingMail | null>;
|
|
66
|
+
/** The ledger's claim windows; `maxAttempts` is the job's, so the ledger does not count attempts here. */
|
|
67
|
+
readonly deliverOptions?: Omit<DeliverOptions, "maxAttempts">;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* A jobs handler that sends one delivery through the ledger (`deliverOnce`) and turns its outcome into the job's
|
|
72
|
+
* (`getDeliveryJobResult`). Register it for the queue `enqueueDelivery` writes to:
|
|
73
|
+
*
|
|
74
|
+
* export const handlers: JobHandlers = {
|
|
75
|
+
* "mail.welcome": defineDeliveryJobHandler({ payload: z.object({ userId: z.string() }), buildMail: buildWelcomeMail }),
|
|
76
|
+
* };
|
|
77
|
+
*/
|
|
78
|
+
export function defineDeliveryJobHandler<TSchema extends z.ZodType>(
|
|
79
|
+
options: DeliveryJobHandlerOptions<TSchema>,
|
|
80
|
+
): JobHandler<{ scope: string; payload: z.output<TSchema> }> {
|
|
81
|
+
// The cast keeps the payload's own output type, which zod's object inference widens for a generic schema.
|
|
82
|
+
const schema = z.object({
|
|
83
|
+
scope: z.string().refine(isDeliveryScope),
|
|
84
|
+
payload: options.payload,
|
|
85
|
+
}) as unknown as z.ZodType<{ scope: string; payload: z.output<TSchema> }>;
|
|
86
|
+
return defineJobHandler({
|
|
87
|
+
payload: schema,
|
|
88
|
+
run: async ({ scope, payload }, context) => {
|
|
89
|
+
const mail = await options.buildMail(payload, { ...context, scope });
|
|
90
|
+
if (mail === null) return ok();
|
|
91
|
+
const outcome = await deliverOnce(context, { scope, mail }, { ...options.deliverOptions, maxAttempts: null });
|
|
92
|
+
const staleClaimMs = options.deliverOptions?.staleClaimMs ?? getMailingOptions(context.config).staleClaimMs;
|
|
93
|
+
return getDeliveryJobResult(outcome, { staleClaimMs });
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Turns a `deliverOnce` outcome into a job result, for an app that writes its own handler:
|
|
100
|
+
*
|
|
101
|
+
* - `sent`, `done`: done.
|
|
102
|
+
* - `rejected` (invalid, suppressed, refused mail): failed at once with its reason.
|
|
103
|
+
* - `retry-later`, `halted` (the provider is down, the account refused or out of quota): retried after the provider's
|
|
104
|
+
* `Retry-After`, else after the queue's backoff.
|
|
105
|
+
* - `in-flight`: retried once the other sender's claim could be stale (`staleClaimMs`).
|
|
106
|
+
* - `uncertain`: failed with `mailing.delivery_uncertain`; the operator decides whether to send it again.
|
|
107
|
+
*/
|
|
108
|
+
export function getDeliveryJobResult(outcome: DeliveryOutcome, options: { readonly staleClaimMs: number }): DeliveryJobResult {
|
|
109
|
+
switch (outcome.status) {
|
|
110
|
+
case "sent":
|
|
111
|
+
case "done":
|
|
112
|
+
return ok();
|
|
113
|
+
case "rejected":
|
|
114
|
+
return failJob(outcome.reason);
|
|
115
|
+
case "retry-later":
|
|
116
|
+
return retryAfter("mailing.unavailable", outcome.retryAfterSeconds);
|
|
117
|
+
case "halted":
|
|
118
|
+
return retryAfter(outcome.reason, outcome.retryAfterSeconds);
|
|
119
|
+
case "in-flight":
|
|
120
|
+
// One second past the window: a claim exactly `staleClaimMs` old is still fresh.
|
|
121
|
+
return retryJobLater("mailing.delivery_in_flight", Math.ceil(options.staleClaimMs / 1000) + 1);
|
|
122
|
+
case "uncertain":
|
|
123
|
+
return failJob("mailing.delivery_uncertain");
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function retryAfter(code: "mailing.unavailable" | "mailing.provider_refused" | "mailing.quota_exceeded", seconds: number | undefined): DeliveryJobResult {
|
|
128
|
+
return seconds === undefined ? err(code) : retryJobLater(code, seconds);
|
|
129
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
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 {
|
|
4
|
+
defineDeliveryJobHandler,
|
|
5
|
+
enqueueDelivery,
|
|
6
|
+
getDeliveryJobResult,
|
|
7
|
+
type DeliveryJobContext,
|
|
8
|
+
type DeliveryJobErrorCode,
|
|
9
|
+
type DeliveryJobHandlerOptions,
|
|
10
|
+
type DeliveryJobResult,
|
|
11
|
+
type EnqueueDeliveryInput,
|
|
12
|
+
} from "./delivery-job.js";
|
package/src/messages/en.ts
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/src/messages/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { MailingErrorCode } from "../contract.js";
|
|
1
|
+
import type { DeliveryJobErrorCode, MailingErrorCode } from "../contract.js";
|
|
2
2
|
import { en } from "./en.js";
|
|
3
3
|
|
|
4
4
|
/** The built-in English dictionary; an app passes partial overrides or its own translation per locale. */
|
|
@@ -7,7 +7,7 @@ export const mailingMessages = { en };
|
|
|
7
7
|
export type MailingMessages = typeof en;
|
|
8
8
|
|
|
9
9
|
/** The copy for a mailing error code; a code the dictionary does not know reads as `unavailable`. */
|
|
10
|
-
export function getMailingErrorMessage(messages: MailingMessages, code: MailingErrorCode): string {
|
|
10
|
+
export function getMailingErrorMessage(messages: MailingMessages, code: MailingErrorCode | DeliveryJobErrorCode): string {
|
|
11
11
|
const group: Readonly<Record<string, string>> = messages.errors.mailing;
|
|
12
12
|
const name = code.slice("mailing.".length);
|
|
13
13
|
return (Object.hasOwn(group, name) ? group[name] : undefined) ?? messages.errors.mailing.unavailable;
|
package/src/providers/resend.ts
CHANGED
|
@@ -77,10 +77,29 @@ async function readOutcome(response: Response): Promise<ProviderOutcome> {
|
|
|
77
77
|
// Only the error's `name` (an enum) is read: its `message` can echo the address back.
|
|
78
78
|
const name = readString(payload, "name");
|
|
79
79
|
const httpStatus = response.status;
|
|
80
|
-
|
|
81
|
-
|
|
80
|
+
const retryAfterSeconds = readRetryAfter(response.headers.get("retry-after"), Date.now());
|
|
81
|
+
const failure = retryAfterSeconds === null ? { httpStatus } : { httpStatus, retryAfterSeconds };
|
|
82
|
+
if (REFUSED_STATUSES.has(httpStatus)) return { status: "refused", ...failure };
|
|
83
|
+
if (httpStatus === 429) return { status: name === RATE_LIMIT_EXCEEDED ? "unavailable" : "quota_exceeded", ...failure };
|
|
82
84
|
const isRetryable = httpStatus >= 500 || httpStatus === 408 || (httpStatus === 409 && name === CONCURRENT_IDEMPOTENT_REQUESTS);
|
|
83
|
-
return { status: isRetryable ? "unavailable" : "rejected",
|
|
85
|
+
return { status: isRetryable ? "unavailable" : "rejected", ...failure };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const DELAY_SECONDS = /^\d{1,10}$/;
|
|
89
|
+
/** The IMF-fixdate form of an HTTP date, the one senders must produce (RFC 9110 §5.6.7). */
|
|
90
|
+
const HTTP_DATE = /^[A-Z][a-z]{2}, \d{2} [A-Z][a-z]{2} \d{4} \d{2}:\d{2}:\d{2} GMT$/;
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Reads a `Retry-After` header (RFC 9110 §10.2.3): a whole number of seconds, or an HTTP date (IMF-fixdate) turned into the seconds
|
|
94
|
+
* from `nowMs` to it (0 for a date in the past). `null` when there is none or it is malformed.
|
|
95
|
+
*/
|
|
96
|
+
export function readRetryAfter(value: string | null, nowMs: number): number | null {
|
|
97
|
+
const text = value?.trim() ?? "";
|
|
98
|
+
if (text === "") return null;
|
|
99
|
+
if (DELAY_SECONDS.test(text)) return Number(text);
|
|
100
|
+
const at = HTTP_DATE.test(text) ? Date.parse(text) : Number.NaN;
|
|
101
|
+
if (Number.isNaN(at)) return null;
|
|
102
|
+
return Math.max(0, Math.ceil((at - nowMs) / 1000));
|
|
84
103
|
}
|
|
85
104
|
|
|
86
105
|
function readString(payload: unknown, key: string): string | null {
|
package/src/server/deliveries.ts
CHANGED
|
@@ -92,13 +92,16 @@ export type DeliveryOutcome =
|
|
|
92
92
|
* `retakeUncertain` sends it again.
|
|
93
93
|
*/
|
|
94
94
|
| { readonly status: "uncertain" }
|
|
95
|
-
/**
|
|
96
|
-
|
|
95
|
+
/**
|
|
96
|
+
* The provider was unavailable; the claim is released for a later run. `retryAfterSeconds` is the wait the provider
|
|
97
|
+
* asked for, when it named one.
|
|
98
|
+
*/
|
|
99
|
+
| { readonly status: "retry-later"; readonly httpStatus?: number; readonly retryAfterSeconds?: number }
|
|
97
100
|
/**
|
|
98
101
|
* The provider refused the account or its quota is spent: the claim is released, its attempt given back. Stop
|
|
99
102
|
* sending (every other mail fails the same way) and run again once the account can send.
|
|
100
103
|
*/
|
|
101
|
-
| { readonly status: "halted"; readonly reason: HaltingErrorCode; readonly httpStatus?: number };
|
|
104
|
+
| { readonly status: "halted"; readonly reason: HaltingErrorCode; readonly httpStatus?: number; readonly retryAfterSeconds?: number };
|
|
102
105
|
|
|
103
106
|
/**
|
|
104
107
|
* Sends `delivery.mail` unless the ledger already has an outcome (or a fresh claim) for its scope
|
|
@@ -125,13 +128,14 @@ export async function deliverOnce(ctx: DeliveryContext, delivery: Delivery, opti
|
|
|
125
128
|
}
|
|
126
129
|
const providerStatus = result.httpStatus ?? null;
|
|
127
130
|
const httpStatus = result.httpStatus === undefined ? {} : { httpStatus: result.httpStatus };
|
|
131
|
+
const retryAfter = result.retryAfterSeconds === undefined ? {} : { retryAfterSeconds: result.retryAfterSeconds };
|
|
128
132
|
if (isHaltingCode(result.error)) {
|
|
129
133
|
await releaseDelivery(ctx, fence, { providerStatus, giveAttemptBack: true });
|
|
130
|
-
return { status: "halted", reason: result.error, ...httpStatus };
|
|
134
|
+
return { status: "halted", reason: result.error, ...httpStatus, ...retryAfter };
|
|
131
135
|
}
|
|
132
136
|
if (result.error === "mailing.unavailable" && (maxAttempts === null || attempt < maxAttempts)) {
|
|
133
137
|
await releaseDelivery(ctx, fence, { providerStatus, giveAttemptBack: false });
|
|
134
|
-
return { status: "retry-later", ...httpStatus };
|
|
138
|
+
return { status: "retry-later", ...httpStatus, ...retryAfter };
|
|
135
139
|
}
|
|
136
140
|
await closeDelivery(ctx, fence, { status: "rejected", providerMessageId: null, reason: result.error, providerStatus });
|
|
137
141
|
return { status: "rejected", reason: result.error, ...httpStatus };
|