@bumail/queue 0.8.0 → 0.10.0
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/README.md +27 -1
- package/dist/chunks/index-r8wb840a.js +24 -0
- package/dist/chunks/index-r8wb840a.js.map +10 -0
- package/dist/chunks/{index-e5wa9f3j.js → index-sz13a17b.js} +103 -21
- package/dist/chunks/index-sz13a17b.js.map +14 -0
- package/dist/contract/checks.d.ts +2 -6
- package/dist/contract/checks.d.ts.map +1 -1
- package/dist/contract/dsn-checks.d.ts +4 -0
- package/dist/contract/dsn-checks.d.ts.map +1 -0
- package/dist/contract/dsn-column.d.ts +12 -0
- package/dist/contract/dsn-column.d.ts.map +1 -0
- package/dist/contract/storable.d.ts +7 -0
- package/dist/contract/storable.d.ts.map +1 -0
- package/dist/contract/types.d.ts +42 -0
- package/dist/contract/types.d.ts.map +1 -1
- package/dist/dsn/build.d.ts +2 -0
- package/dist/dsn/build.d.ts.map +1 -1
- package/dist/dsn/delivered.d.ts +25 -0
- package/dist/dsn/delivered.d.ts.map +1 -0
- package/dist/dsn/failed.d.ts +29 -0
- package/dist/dsn/failed.d.ts.map +1 -0
- package/dist/dsn/fields.d.ts +11 -2
- package/dist/dsn/fields.d.ts.map +1 -1
- package/dist/dsn/for.d.ts +26 -0
- package/dist/dsn/for.d.ts.map +1 -0
- package/dist/dsn/human.d.ts +7 -0
- package/dist/dsn/human.d.ts.map +1 -0
- package/dist/dsn/policy.d.ts +19 -0
- package/dist/dsn/policy.d.ts.map +1 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +404 -255
- package/dist/index.js.map +21 -16
- package/dist/memory/index.js +1 -1
- package/dist/postgres/index.js +17 -9
- package/dist/postgres/index.js.map +6 -6
- package/dist/postgres/rows.d.ts +6 -1
- package/dist/postgres/rows.d.ts.map +1 -1
- package/dist/postgres/schema.d.ts.map +1 -1
- package/dist/postgres/statements.d.ts +9 -8
- package/dist/postgres/statements.d.ts.map +1 -1
- package/dist/postgres/store.d.ts.map +1 -1
- package/dist/queue/envelope.d.ts +24 -4
- package/dist/queue/envelope.d.ts.map +1 -1
- package/dist/queue/events.d.ts +2 -1
- package/dist/queue/events.d.ts.map +1 -1
- package/dist/queue/notify.d.ts +6 -3
- package/dist/queue/notify.d.ts.map +1 -1
- package/dist/queue/outcome.d.ts.map +1 -1
- package/dist/queue/queue.d.ts.map +1 -1
- package/dist/queue/route.d.ts +7 -0
- package/dist/queue/route.d.ts.map +1 -1
- package/dist/queue/session.d.ts.map +1 -1
- package/dist/queue/settings/limits.d.ts.map +1 -1
- package/dist/queue/settle.d.ts.map +1 -1
- package/dist/redis/index.js +26 -13
- package/dist/redis/index.js.map +5 -5
- package/dist/redis/items.d.ts +2 -0
- package/dist/redis/items.d.ts.map +1 -1
- package/dist/redis/scripts.d.ts +1 -1
- package/dist/redis/scripts.d.ts.map +1 -1
- package/dist/redis/store.d.ts.map +1 -1
- package/dist/sqlite/index.js +15 -8
- package/dist/sqlite/index.js.map +5 -5
- package/dist/sqlite/rows.d.ts +2 -1
- package/dist/sqlite/rows.d.ts.map +1 -1
- package/dist/sqlite/schema.d.ts.map +1 -1
- package/dist/sqlite/store.d.ts.map +1 -1
- package/docs/guide.md +102 -3
- package/docs/roadmap.md +8 -3
- package/docs/troubleshooting.md +42 -0
- package/package.json +3 -3
- package/dist/chunks/index-e5wa9f3j.js.map +0 -12
package/dist/index.js
CHANGED
|
@@ -1,12 +1,353 @@
|
|
|
1
1
|
import {
|
|
2
|
-
QueueError2,
|
|
3
|
-
invalid,
|
|
4
2
|
hasControl,
|
|
5
3
|
replaceControls,
|
|
6
4
|
cleanText,
|
|
7
5
|
asciiText,
|
|
6
|
+
QueueError2,
|
|
7
|
+
invalid,
|
|
8
8
|
checkOwner
|
|
9
|
-
} from "./chunks/index-
|
|
9
|
+
} from "./chunks/index-sz13a17b.js";
|
|
10
|
+
|
|
11
|
+
// src/dsn/build.ts
|
|
12
|
+
import {
|
|
13
|
+
encodeHeaderValue,
|
|
14
|
+
encodeQuotedPrintable,
|
|
15
|
+
foldHeader as foldHeader2,
|
|
16
|
+
formatDate as formatDate3
|
|
17
|
+
} from "@bumail/mime";
|
|
18
|
+
|
|
19
|
+
// src/dsn/content.ts
|
|
20
|
+
var CR = 13;
|
|
21
|
+
var LF = 10;
|
|
22
|
+
var MAX_LINE = 998;
|
|
23
|
+
var isContinuation = (byte) => byte !== undefined && byte >= 128 && byte < 192;
|
|
24
|
+
function crlf(bytes) {
|
|
25
|
+
const out = [];
|
|
26
|
+
let column = 0;
|
|
27
|
+
for (let i = 0;i < bytes.length; i++) {
|
|
28
|
+
const byte = bytes[i];
|
|
29
|
+
if (byte === CR || byte === LF) {
|
|
30
|
+
out.push(CR, LF);
|
|
31
|
+
column = 0;
|
|
32
|
+
if (byte === CR && bytes[i + 1] === LF)
|
|
33
|
+
i++;
|
|
34
|
+
} else if (column < MAX_LINE) {
|
|
35
|
+
out.push(byte);
|
|
36
|
+
column++;
|
|
37
|
+
} else if (column === MAX_LINE) {
|
|
38
|
+
if (isContinuation(byte)) {
|
|
39
|
+
while (isContinuation(out.at(-1))) {
|
|
40
|
+
out.pop();
|
|
41
|
+
}
|
|
42
|
+
if ((out.at(-1) ?? 0) >= 192)
|
|
43
|
+
out.pop();
|
|
44
|
+
}
|
|
45
|
+
column++;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return Uint8Array.from(out);
|
|
49
|
+
}
|
|
50
|
+
function headerEnd(bytes) {
|
|
51
|
+
for (let i = 0;i + 3 < bytes.length; i++) {
|
|
52
|
+
if (bytes[i] === CR && bytes[i + 1] === LF && bytes[i + 2] === CR && bytes[i + 3] === LF) {
|
|
53
|
+
return i + 2;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return bytes.length;
|
|
57
|
+
}
|
|
58
|
+
function wholeLines(bytes, max) {
|
|
59
|
+
if (bytes.length <= max)
|
|
60
|
+
return bytes;
|
|
61
|
+
const cut = bytes.subarray(0, max).lastIndexOf(LF);
|
|
62
|
+
return bytes.subarray(0, cut + 1);
|
|
63
|
+
}
|
|
64
|
+
function returned(original, mode, max) {
|
|
65
|
+
const message = crlf(original.subarray(0, max * 2 + 2));
|
|
66
|
+
const whole = original.length <= max && message.length <= max;
|
|
67
|
+
const body = whole && mode === "full" ? message : wholeLines(message.subarray(0, headerEnd(message)), max);
|
|
68
|
+
return {
|
|
69
|
+
type: whole && mode === "full" ? "message/rfc822" : "text/rfc822-headers",
|
|
70
|
+
body,
|
|
71
|
+
eightBit: body.some((byte) => byte > 127)
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// src/dsn/fields.ts
|
|
76
|
+
import { foldHeader, formatDate } from "@bumail/mime";
|
|
77
|
+
var strip = (text) => replaceControls(text, "");
|
|
78
|
+
var MAX_ESCAPED = 900;
|
|
79
|
+
function typedAddress(address) {
|
|
80
|
+
const clean = cleanAddress(address);
|
|
81
|
+
if (isAscii(clean))
|
|
82
|
+
return `rfc822; ${clean}`;
|
|
83
|
+
return `utf-8; ${escapedAddress(clean).slice(0, MAX_ESCAPED)}`;
|
|
84
|
+
}
|
|
85
|
+
var cleanAddress = (address) => strip(address).replace(/[\s\\]/g, "");
|
|
86
|
+
var isAscii = (text) => /^[\x21-\x7e]*$/.test(text);
|
|
87
|
+
function escapedAddress(clean) {
|
|
88
|
+
let escaped = "";
|
|
89
|
+
for (const c of clean) {
|
|
90
|
+
escaped += /[\x21-\x7e]/.test(c) ? c : `\\x{${(c.codePointAt(0) ?? 0).toString(16).toUpperCase()}}`;
|
|
91
|
+
}
|
|
92
|
+
return escaped;
|
|
93
|
+
}
|
|
94
|
+
var remoteMta = (host) => host && /^[A-Za-z0-9.:[\]-]{1,255}$/.test(host) ? `dns; ${host}` : undefined;
|
|
95
|
+
function statusOf(kind, reply) {
|
|
96
|
+
if (reply?.status)
|
|
97
|
+
return reply.status;
|
|
98
|
+
if (reply?.code !== undefined) {
|
|
99
|
+
if (reply.code < 400)
|
|
100
|
+
return "2.0.0";
|
|
101
|
+
return reply.code >= 500 ? "5.0.0" : "4.0.0";
|
|
102
|
+
}
|
|
103
|
+
if (kind === "relayed" || kind === "delivered")
|
|
104
|
+
return "2.0.0";
|
|
105
|
+
return kind === "failed" ? "5.0.0" : "4.0.0";
|
|
106
|
+
}
|
|
107
|
+
function originalRecipient(orcpt) {
|
|
108
|
+
const type = orcpt.type.toLowerCase();
|
|
109
|
+
if (type === "rfc822" || type === "utf-8") {
|
|
110
|
+
const clean = cleanAddress(orcpt.address);
|
|
111
|
+
const whole = isAscii(clean) ? clean : escapedAddress(clean);
|
|
112
|
+
return whole.length > MAX_ESCAPED ? undefined : typedAddress(orcpt.address);
|
|
113
|
+
}
|
|
114
|
+
const address = strip(orcpt.address).replace(/\s/g, "");
|
|
115
|
+
if (!/^[A-Za-z0-9-]{1,20}$/.test(orcpt.type) || !/^[\x21-\x7e]{1,900}$/.test(address)) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
return `${orcpt.type.toLowerCase()}; ${address}`;
|
|
119
|
+
}
|
|
120
|
+
function recipientFields(report, recipient) {
|
|
121
|
+
const { reply } = recipient;
|
|
122
|
+
const original = recipient.orcpt && originalRecipient(recipient.orcpt);
|
|
123
|
+
const fields = [
|
|
124
|
+
...original ? [["Original-Recipient", original]] : [],
|
|
125
|
+
["Final-Recipient", typedAddress(recipient.address)],
|
|
126
|
+
["Action", report.kind],
|
|
127
|
+
["Status", statusOf(report.kind, reply)]
|
|
128
|
+
];
|
|
129
|
+
const remote = remoteMta(reply?.host);
|
|
130
|
+
if (remote)
|
|
131
|
+
fields.push(["Remote-MTA", remote]);
|
|
132
|
+
if (reply) {
|
|
133
|
+
const text = asciiText(reply.text, 900);
|
|
134
|
+
fields.push(reply.code === undefined ? ["Diagnostic-Code", `X-Bumail; ${text}`] : ["Diagnostic-Code", `smtp; ${reply.code} ${text}`]);
|
|
135
|
+
}
|
|
136
|
+
fields.push(["Last-Attempt-Date", formatDate(recipient.lastAttempt)]);
|
|
137
|
+
if (report.kind === "delayed" && report.willRetryUntil) {
|
|
138
|
+
fields.push(["Will-Retry-Until", formatDate(report.willRetryUntil)]);
|
|
139
|
+
}
|
|
140
|
+
return fields.map(([name, value]) => foldHeader(name, value));
|
|
141
|
+
}
|
|
142
|
+
function deliveryStatus(report) {
|
|
143
|
+
const envid = strip(report.envid ?? "").trim();
|
|
144
|
+
const message = [
|
|
145
|
+
...envid === "" ? [] : [foldHeader("Original-Envelope-Id", envid)],
|
|
146
|
+
foldHeader("Reporting-MTA", `dns; ${strip(report.reportingMta)}`),
|
|
147
|
+
foldHeader("Arrival-Date", formatDate(report.arrival))
|
|
148
|
+
].join(`\r
|
|
149
|
+
`);
|
|
150
|
+
const blocks = report.recipients.map((r) => recipientFields(report, r).join(`\r
|
|
151
|
+
`));
|
|
152
|
+
return `${[message, ...blocks].join(`\r
|
|
153
|
+
\r
|
|
154
|
+
`)}\r
|
|
155
|
+
`;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// src/dsn/human.ts
|
|
159
|
+
import { formatDate as formatDate2 } from "@bumail/mime";
|
|
160
|
+
var SUBJECTS = {
|
|
161
|
+
failed: "Undelivered Mail Returned to Sender",
|
|
162
|
+
delayed: "Delayed Mail (still being retried)",
|
|
163
|
+
relayed: "Mail Relayed (delivery not confirmed)",
|
|
164
|
+
delivered: "Mail Delivered"
|
|
165
|
+
};
|
|
166
|
+
function opening(input) {
|
|
167
|
+
switch (input.kind) {
|
|
168
|
+
case "failed":
|
|
169
|
+
return [
|
|
170
|
+
"Your message could not be delivered to the recipients below.",
|
|
171
|
+
"This is a permanent failure: the message will not be retried."
|
|
172
|
+
];
|
|
173
|
+
case "delayed":
|
|
174
|
+
return [
|
|
175
|
+
"Your message has not been delivered yet to the recipients below.",
|
|
176
|
+
"This is a warning only: the message is still being retried",
|
|
177
|
+
...input.willRetryUntil ? [`until ${formatDate2(input.willRetryUntil)}.`] : [],
|
|
178
|
+
"You do not need to send it again."
|
|
179
|
+
];
|
|
180
|
+
case "relayed":
|
|
181
|
+
return [
|
|
182
|
+
"Your message was passed on to the recipients below, whose server",
|
|
183
|
+
"does not report delivery status (RFC 3461 §5.2.2).",
|
|
184
|
+
"This server can say no more: no further notice will follow."
|
|
185
|
+
];
|
|
186
|
+
case "delivered":
|
|
187
|
+
return ["Your message was delivered to the recipients below."];
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
function humanText(input) {
|
|
191
|
+
const lines = [
|
|
192
|
+
`This is the mail system at ${input.reportingMta}.`,
|
|
193
|
+
"",
|
|
194
|
+
...opening(input)
|
|
195
|
+
];
|
|
196
|
+
if (!input.original && input.kind !== "delivered") {
|
|
197
|
+
lines.push("", "Your message could not be read from the queue, so it is not returned.");
|
|
198
|
+
}
|
|
199
|
+
lines.push("");
|
|
200
|
+
for (const r of input.recipients) {
|
|
201
|
+
const said = r.reply ? `${r.reply.code ?? ""} ${cleanText(r.reply.text, 900)}`.trim() : statusOf(input.kind);
|
|
202
|
+
const why = r.reply?.status === "4.4.7" ? `delivery time expired, still failing: ${said}` : said;
|
|
203
|
+
lines.push(`<${cleanText(r.address, 254)}>: ${why}`);
|
|
204
|
+
}
|
|
205
|
+
return `${lines.join(`\r
|
|
206
|
+
`)}\r
|
|
207
|
+
`;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// src/dsn/build.ts
|
|
211
|
+
var encoder = new TextEncoder;
|
|
212
|
+
var strip2 = (text) => replaceControls(text, "").replace(/[\s<>]/g, "");
|
|
213
|
+
function headerFields(input, boundary) {
|
|
214
|
+
const subject = SUBJECTS[input.kind];
|
|
215
|
+
const domain = strip2(input.reportingMta);
|
|
216
|
+
return [
|
|
217
|
+
foldHeader2("Date", formatDate3(input.date)),
|
|
218
|
+
foldHeader2("From", `Mail Delivery System <${strip2(input.from)}>`),
|
|
219
|
+
foldHeader2("To", `<${strip2(input.to)}>`),
|
|
220
|
+
foldHeader2("Subject", encodeHeaderValue(subject)),
|
|
221
|
+
foldHeader2("Message-ID", `<${crypto.randomUUID()}@${domain}>`),
|
|
222
|
+
foldHeader2("Auto-Submitted", "auto-replied"),
|
|
223
|
+
foldHeader2("MIME-Version", "1.0"),
|
|
224
|
+
foldHeader2("Content-Type", `multipart/report; report-type=delivery-status; boundary="${boundary}"`)
|
|
225
|
+
];
|
|
226
|
+
}
|
|
227
|
+
var concat = (parts) => {
|
|
228
|
+
const out = new Uint8Array(parts.reduce((n, p) => n + p.length, 0));
|
|
229
|
+
let at = 0;
|
|
230
|
+
for (const part of parts) {
|
|
231
|
+
out.set(part, at);
|
|
232
|
+
at += part.length;
|
|
233
|
+
}
|
|
234
|
+
return out;
|
|
235
|
+
};
|
|
236
|
+
function boundaryFor(content) {
|
|
237
|
+
const text = new TextDecoder("latin1").decode(content);
|
|
238
|
+
for (;; ) {
|
|
239
|
+
const boundary = `=_dsn_${crypto.randomUUID()}`;
|
|
240
|
+
if (!text.includes(boundary))
|
|
241
|
+
return boundary;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
function buildDsn(input) {
|
|
245
|
+
const original = input.original ? returned(input.original, input.returnContent, input.maxReturn) : undefined;
|
|
246
|
+
const boundary = boundaryFor(original?.body ?? new Uint8Array);
|
|
247
|
+
const text = encodeQuotedPrintable(humanText(input));
|
|
248
|
+
const status = deliveryStatus({
|
|
249
|
+
kind: input.kind,
|
|
250
|
+
reportingMta: input.reportingMta,
|
|
251
|
+
arrival: input.arrival,
|
|
252
|
+
...input.envid === undefined ? {} : { envid: input.envid },
|
|
253
|
+
recipients: input.recipients,
|
|
254
|
+
...input.willRetryUntil ? { willRetryUntil: input.willRetryUntil } : {}
|
|
255
|
+
});
|
|
256
|
+
const parts = [
|
|
257
|
+
...headerFields(input, boundary),
|
|
258
|
+
"",
|
|
259
|
+
"This is a MIME-formatted delivery status notification.",
|
|
260
|
+
"",
|
|
261
|
+
`--${boundary}`,
|
|
262
|
+
"Content-Type: text/plain; charset=utf-8",
|
|
263
|
+
"Content-Transfer-Encoding: quoted-printable",
|
|
264
|
+
"",
|
|
265
|
+
text,
|
|
266
|
+
`--${boundary}`,
|
|
267
|
+
"Content-Type: message/delivery-status",
|
|
268
|
+
"",
|
|
269
|
+
status
|
|
270
|
+
];
|
|
271
|
+
const end = `--${boundary}--\r
|
|
272
|
+
`;
|
|
273
|
+
if (!original)
|
|
274
|
+
return encoder.encode(`${parts.join(`\r
|
|
275
|
+
`)}\r
|
|
276
|
+
${end}`);
|
|
277
|
+
const head = [
|
|
278
|
+
...parts,
|
|
279
|
+
`--${boundary}`,
|
|
280
|
+
`Content-Type: ${original.type}`,
|
|
281
|
+
...original.eightBit ? ["Content-Transfer-Encoding: 8bit"] : [],
|
|
282
|
+
"",
|
|
283
|
+
""
|
|
284
|
+
].join(`\r
|
|
285
|
+
`);
|
|
286
|
+
return concat([
|
|
287
|
+
encoder.encode(head),
|
|
288
|
+
original.body,
|
|
289
|
+
encoder.encode(`\r
|
|
290
|
+
${end}`)
|
|
291
|
+
]);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// src/dsn/policy.ts
|
|
295
|
+
function asked(kind, notify) {
|
|
296
|
+
if (notify === "NEVER")
|
|
297
|
+
return false;
|
|
298
|
+
if (notify === undefined)
|
|
299
|
+
return kind === "failed" || kind === "delayed";
|
|
300
|
+
const event = {
|
|
301
|
+
failed: "FAILURE",
|
|
302
|
+
delayed: "DELAY",
|
|
303
|
+
relayed: "SUCCESS",
|
|
304
|
+
delivered: "SUCCESS"
|
|
305
|
+
};
|
|
306
|
+
return notify.includes(event[kind]);
|
|
307
|
+
}
|
|
308
|
+
var returnOf = (ret, fallback) => ret === "HDRS" ? "headers" : ret === "FULL" ? "full" : fallback;
|
|
309
|
+
var DEFAULT_MAX_RETURN = 64 * 1024;
|
|
310
|
+
|
|
311
|
+
// src/dsn/for.ts
|
|
312
|
+
function dsnFor(kind, input, recipients, returnContent) {
|
|
313
|
+
if (input.to === "" || recipients.length === 0)
|
|
314
|
+
return;
|
|
315
|
+
return buildDsn({
|
|
316
|
+
kind,
|
|
317
|
+
reportingMta: input.reportingMta,
|
|
318
|
+
from: input.from,
|
|
319
|
+
to: input.to,
|
|
320
|
+
arrival: input.arrival,
|
|
321
|
+
date: input.date ?? new Date,
|
|
322
|
+
...input.envid === undefined ? {} : { envid: input.envid },
|
|
323
|
+
recipients,
|
|
324
|
+
...input.original ? { original: input.original } : {},
|
|
325
|
+
returnContent,
|
|
326
|
+
maxReturn: input.maxReturn ?? DEFAULT_MAX_RETURN
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// src/dsn/delivered.ts
|
|
331
|
+
function deliveredDsn(input) {
|
|
332
|
+
const date = input.date ?? new Date;
|
|
333
|
+
const wanted = input.recipients.filter((r) => asked("delivered", r.notify));
|
|
334
|
+
return dsnFor("delivered", { ...input, date }, wanted.map((r) => ({
|
|
335
|
+
address: r.address,
|
|
336
|
+
lastAttempt: date,
|
|
337
|
+
...r.orcpt ? { orcpt: r.orcpt } : {}
|
|
338
|
+
})), "headers");
|
|
339
|
+
}
|
|
340
|
+
// src/dsn/failed.ts
|
|
341
|
+
function failedDsn(input) {
|
|
342
|
+
const date = input.date ?? new Date;
|
|
343
|
+
const wanted = input.recipients.filter((r) => asked("failed", r.notify));
|
|
344
|
+
return dsnFor("failed", { ...input, date }, wanted.map((r) => ({
|
|
345
|
+
address: r.address,
|
|
346
|
+
reply: r.reply,
|
|
347
|
+
lastAttempt: date,
|
|
348
|
+
...r.orcpt ? { orcpt: r.orcpt } : {}
|
|
349
|
+
})), returnOf(input.ret, "headers"));
|
|
350
|
+
}
|
|
10
351
|
// src/queue/envelope.ts
|
|
11
352
|
import { isMailbox } from "@bumail/smtp/client";
|
|
12
353
|
var ADDRESS = /^[^\s<>]{1,64}@[^\s<>@]{1,253}$/u;
|
|
@@ -39,15 +380,27 @@ function checkEnvelope(envelope, maxRecipients) {
|
|
|
39
380
|
if (to.length > maxRecipients) {
|
|
40
381
|
throw new QueueError2("TOO_MANY_RECIPIENTS", `The message has ${to.length} recipients; the limit is ${maxRecipients}`);
|
|
41
382
|
}
|
|
42
|
-
|
|
383
|
+
const keyOf = (a) => `${a.slice(0, a.lastIndexOf("@"))}@${domainOf(a)}`;
|
|
384
|
+
const kept = new Map(to.map((a) => [keyOf(a), a]));
|
|
385
|
+
const recipients = Array.isArray(envelope.recipients) ? envelope.recipients.map((r) => {
|
|
386
|
+
const target = typeof r?.address === "string" && list.includes(r.address) ? kept.get(keyOf(r.address)) : undefined;
|
|
387
|
+
return target === undefined ? r : { ...r, address: target };
|
|
388
|
+
}).filter((r, i, all) => all.findIndex((o) => o?.address === r?.address) === i) : envelope.recipients;
|
|
389
|
+
return {
|
|
390
|
+
from,
|
|
391
|
+
to,
|
|
392
|
+
...envelope.ret === undefined ? {} : { ret: envelope.ret },
|
|
393
|
+
...envelope.envid === undefined ? {} : { envid: envelope.envid },
|
|
394
|
+
...recipients === undefined ? {} : { recipients }
|
|
395
|
+
};
|
|
43
396
|
}
|
|
44
397
|
var tooBig = (max) => new QueueError2("MESSAGE_TOO_BIG", `The message is larger than the limit of ${max} bytes`);
|
|
45
|
-
var
|
|
46
|
-
var
|
|
398
|
+
var CR2 = 13;
|
|
399
|
+
var LF2 = 10;
|
|
47
400
|
function checkLineEnds(bytes) {
|
|
48
401
|
for (let i = 0;i < bytes.length; i++) {
|
|
49
402
|
const byte = bytes[i];
|
|
50
|
-
const bare = byte ===
|
|
403
|
+
const bare = byte === CR2 ? bytes[i + 1] !== LF2 : byte === LF2 && (i === 0 || bytes[i - 1] !== CR2);
|
|
51
404
|
if (bare) {
|
|
52
405
|
throw invalid(`The message has a bare CR or LF at byte ${i}: every line must end in CRLF`);
|
|
53
406
|
}
|
|
@@ -168,7 +521,7 @@ function limitsOf(options) {
|
|
|
168
521
|
maxRecipients: numberOf("limits.maxRecipients", l.maxRecipients, 100, 1),
|
|
169
522
|
maxItems: l.maxItems === undefined ? undefined : numberOf("limits.maxItems", l.maxItems, 0, 1),
|
|
170
523
|
maxReplyText: numberOf("limits.maxReplyText", l.maxReplyText, 512, 64, 900),
|
|
171
|
-
maxDsnReturn: numberOf("limits.maxDsnReturn", l.maxDsnReturn,
|
|
524
|
+
maxDsnReturn: numberOf("limits.maxDsnReturn", l.maxDsnReturn, DEFAULT_MAX_RETURN, 0)
|
|
172
525
|
};
|
|
173
526
|
}
|
|
174
527
|
|
|
@@ -337,7 +690,8 @@ function outcomesOf(result, group, max) {
|
|
|
337
690
|
updates.set(recipient, {
|
|
338
691
|
address: recipient,
|
|
339
692
|
status: "delivered",
|
|
340
|
-
reply: diagnosticOf(result.reply, max, host)
|
|
693
|
+
reply: diagnosticOf(result.reply, max, host),
|
|
694
|
+
...result.dsnOffered === false ? { dsnOffered: false } : {}
|
|
341
695
|
});
|
|
342
696
|
}
|
|
343
697
|
for (const { recipient, reply } of result.rejected) {
|
|
@@ -403,6 +757,19 @@ function routeOf(settings, domain) {
|
|
|
403
757
|
function hostOf(route) {
|
|
404
758
|
return route === "mx" ? undefined : route.host;
|
|
405
759
|
}
|
|
760
|
+
function dsnOptionsOf(item, group) {
|
|
761
|
+
const only = new Set(group);
|
|
762
|
+
const recipients = item.recipients.filter((r) => only.has(r.address) && (r.notify !== undefined || r.orcpt !== undefined)).map((r) => ({
|
|
763
|
+
address: r.address,
|
|
764
|
+
...r.notify === undefined ? {} : { notify: r.notify },
|
|
765
|
+
...r.orcpt === undefined ? {} : { orcpt: r.orcpt }
|
|
766
|
+
}));
|
|
767
|
+
return {
|
|
768
|
+
...item.ret === undefined ? {} : { ret: item.ret },
|
|
769
|
+
...item.envid === undefined ? {} : { envid: item.envid },
|
|
770
|
+
...recipients.length === 0 ? {} : { recipients }
|
|
771
|
+
};
|
|
772
|
+
}
|
|
406
773
|
function sendOptionsOf(settings, domain, from, to, override) {
|
|
407
774
|
const { options, hostname } = settings;
|
|
408
775
|
const common = {
|
|
@@ -449,7 +816,10 @@ async function session(ctx, item, message, domain, group, route, override) {
|
|
|
449
816
|
const { settings } = ctx;
|
|
450
817
|
const max = settings.limits.maxReplyText;
|
|
451
818
|
try {
|
|
452
|
-
const options =
|
|
819
|
+
const options = {
|
|
820
|
+
...sendOptionsOf(settings, domain, item.from, group, override),
|
|
821
|
+
...dsnOptionsOf(item, group)
|
|
822
|
+
};
|
|
453
823
|
const result = await settings.send(message, options);
|
|
454
824
|
return { updates: outcomesOf(result, group, max) };
|
|
455
825
|
} catch (error) {
|
|
@@ -538,241 +908,6 @@ async function attemptGroup(ctx, item, message, domain, recipients, used) {
|
|
|
538
908
|
return [...kept, ...updates, ...bad.map(badAddress)];
|
|
539
909
|
}
|
|
540
910
|
|
|
541
|
-
// src/dsn/build.ts
|
|
542
|
-
import {
|
|
543
|
-
encodeHeaderValue,
|
|
544
|
-
encodeQuotedPrintable,
|
|
545
|
-
foldHeader as foldHeader2,
|
|
546
|
-
formatDate as formatDate2
|
|
547
|
-
} from "@bumail/mime";
|
|
548
|
-
|
|
549
|
-
// src/dsn/content.ts
|
|
550
|
-
var CR2 = 13;
|
|
551
|
-
var LF2 = 10;
|
|
552
|
-
var MAX_LINE = 998;
|
|
553
|
-
var isContinuation = (byte) => byte !== undefined && byte >= 128 && byte < 192;
|
|
554
|
-
function crlf(bytes) {
|
|
555
|
-
const out = [];
|
|
556
|
-
let column = 0;
|
|
557
|
-
for (let i = 0;i < bytes.length; i++) {
|
|
558
|
-
const byte = bytes[i];
|
|
559
|
-
if (byte === CR2 || byte === LF2) {
|
|
560
|
-
out.push(CR2, LF2);
|
|
561
|
-
column = 0;
|
|
562
|
-
if (byte === CR2 && bytes[i + 1] === LF2)
|
|
563
|
-
i++;
|
|
564
|
-
} else if (column < MAX_LINE) {
|
|
565
|
-
out.push(byte);
|
|
566
|
-
column++;
|
|
567
|
-
} else if (column === MAX_LINE) {
|
|
568
|
-
if (isContinuation(byte)) {
|
|
569
|
-
while (isContinuation(out.at(-1))) {
|
|
570
|
-
out.pop();
|
|
571
|
-
}
|
|
572
|
-
if ((out.at(-1) ?? 0) >= 192)
|
|
573
|
-
out.pop();
|
|
574
|
-
}
|
|
575
|
-
column++;
|
|
576
|
-
}
|
|
577
|
-
}
|
|
578
|
-
return Uint8Array.from(out);
|
|
579
|
-
}
|
|
580
|
-
function headerEnd(bytes) {
|
|
581
|
-
for (let i = 0;i + 3 < bytes.length; i++) {
|
|
582
|
-
if (bytes[i] === CR2 && bytes[i + 1] === LF2 && bytes[i + 2] === CR2 && bytes[i + 3] === LF2) {
|
|
583
|
-
return i + 2;
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
return bytes.length;
|
|
587
|
-
}
|
|
588
|
-
function wholeLines(bytes, max) {
|
|
589
|
-
if (bytes.length <= max)
|
|
590
|
-
return bytes;
|
|
591
|
-
const cut = bytes.subarray(0, max).lastIndexOf(LF2);
|
|
592
|
-
return bytes.subarray(0, cut + 1);
|
|
593
|
-
}
|
|
594
|
-
function returned(original, mode, max) {
|
|
595
|
-
const message = crlf(original.subarray(0, max * 2 + 2));
|
|
596
|
-
const whole = original.length <= max && message.length <= max;
|
|
597
|
-
const body = whole && mode === "full" ? message : wholeLines(message.subarray(0, headerEnd(message)), max);
|
|
598
|
-
return {
|
|
599
|
-
type: whole && mode === "full" ? "message/rfc822" : "text/rfc822-headers",
|
|
600
|
-
body,
|
|
601
|
-
eightBit: body.some((byte) => byte > 127)
|
|
602
|
-
};
|
|
603
|
-
}
|
|
604
|
-
|
|
605
|
-
// src/dsn/fields.ts
|
|
606
|
-
import { foldHeader, formatDate } from "@bumail/mime";
|
|
607
|
-
var strip = (text) => replaceControls(text, "");
|
|
608
|
-
var MAX_ESCAPED = 900;
|
|
609
|
-
function typedAddress(address) {
|
|
610
|
-
const clean = strip(address).replace(/[\s\\]/g, "");
|
|
611
|
-
if (/^[\x21-\x7e]*$/.test(clean))
|
|
612
|
-
return `rfc822; ${clean}`;
|
|
613
|
-
let escaped = "";
|
|
614
|
-
for (const c of clean) {
|
|
615
|
-
const piece = /[\x21-\x7e]/.test(c) ? c : `\\x{${(c.codePointAt(0) ?? 0).toString(16).toUpperCase()}}`;
|
|
616
|
-
if (escaped.length + piece.length > MAX_ESCAPED)
|
|
617
|
-
break;
|
|
618
|
-
escaped += piece;
|
|
619
|
-
}
|
|
620
|
-
return `utf-8; ${escaped}`;
|
|
621
|
-
}
|
|
622
|
-
var remoteMta = (host) => host && /^[A-Za-z0-9.:[\]-]{1,255}$/.test(host) ? `dns; ${host}` : undefined;
|
|
623
|
-
function statusOf(kind, reply) {
|
|
624
|
-
if (reply?.status)
|
|
625
|
-
return reply.status;
|
|
626
|
-
if (reply?.code !== undefined) {
|
|
627
|
-
return reply.code >= 500 ? "5.0.0" : "4.0.0";
|
|
628
|
-
}
|
|
629
|
-
return kind === "failed" ? "5.0.0" : "4.0.0";
|
|
630
|
-
}
|
|
631
|
-
function recipientFields(report, recipient) {
|
|
632
|
-
const { reply } = recipient;
|
|
633
|
-
const fields = [
|
|
634
|
-
["Final-Recipient", typedAddress(recipient.address)],
|
|
635
|
-
["Action", report.kind],
|
|
636
|
-
["Status", statusOf(report.kind, reply)]
|
|
637
|
-
];
|
|
638
|
-
const remote = remoteMta(reply?.host);
|
|
639
|
-
if (remote)
|
|
640
|
-
fields.push(["Remote-MTA", remote]);
|
|
641
|
-
if (reply) {
|
|
642
|
-
const text = asciiText(reply.text, 900);
|
|
643
|
-
fields.push(reply.code === undefined ? ["Diagnostic-Code", `X-Bumail; ${text}`] : ["Diagnostic-Code", `smtp; ${reply.code} ${text}`]);
|
|
644
|
-
}
|
|
645
|
-
fields.push(["Last-Attempt-Date", formatDate(recipient.lastAttempt)]);
|
|
646
|
-
if (report.kind === "delayed" && report.willRetryUntil) {
|
|
647
|
-
fields.push(["Will-Retry-Until", formatDate(report.willRetryUntil)]);
|
|
648
|
-
}
|
|
649
|
-
return fields.map(([name, value]) => foldHeader(name, value));
|
|
650
|
-
}
|
|
651
|
-
function deliveryStatus(report) {
|
|
652
|
-
const message = [
|
|
653
|
-
foldHeader("Reporting-MTA", `dns; ${strip(report.reportingMta)}`),
|
|
654
|
-
foldHeader("Arrival-Date", formatDate(report.arrival))
|
|
655
|
-
].join(`\r
|
|
656
|
-
`);
|
|
657
|
-
const blocks = report.recipients.map((r) => recipientFields(report, r).join(`\r
|
|
658
|
-
`));
|
|
659
|
-
return `${[message, ...blocks].join(`\r
|
|
660
|
-
\r
|
|
661
|
-
`)}\r
|
|
662
|
-
`;
|
|
663
|
-
}
|
|
664
|
-
|
|
665
|
-
// src/dsn/build.ts
|
|
666
|
-
var encoder = new TextEncoder;
|
|
667
|
-
var strip2 = (text) => replaceControls(text, "").replace(/[\s<>]/g, "");
|
|
668
|
-
function humanText(input) {
|
|
669
|
-
const failed = input.kind === "failed";
|
|
670
|
-
const lines = failed ? [
|
|
671
|
-
`This is the mail system at ${input.reportingMta}.`,
|
|
672
|
-
"",
|
|
673
|
-
"Your message could not be delivered to the recipients below.",
|
|
674
|
-
"This is a permanent failure: the message will not be retried."
|
|
675
|
-
] : [
|
|
676
|
-
`This is the mail system at ${input.reportingMta}.`,
|
|
677
|
-
"",
|
|
678
|
-
"Your message has not been delivered yet to the recipients below.",
|
|
679
|
-
"This is a warning only: the message is still being retried",
|
|
680
|
-
...input.willRetryUntil ? [`until ${formatDate2(input.willRetryUntil)}.`] : [],
|
|
681
|
-
"You do not need to send it again."
|
|
682
|
-
];
|
|
683
|
-
if (!input.original) {
|
|
684
|
-
lines.push("", "Your message could not be read from the queue, so it is not returned.");
|
|
685
|
-
}
|
|
686
|
-
lines.push("");
|
|
687
|
-
for (const r of input.recipients) {
|
|
688
|
-
const said = r.reply ? `${r.reply.code ?? ""} ${cleanText(r.reply.text, 900)}`.trim() : statusOf(input.kind);
|
|
689
|
-
const why = r.reply?.status === "4.4.7" ? `delivery time expired, still failing: ${said}` : said;
|
|
690
|
-
lines.push(`<${cleanText(r.address, 254)}>: ${why}`);
|
|
691
|
-
}
|
|
692
|
-
return `${lines.join(`\r
|
|
693
|
-
`)}\r
|
|
694
|
-
`;
|
|
695
|
-
}
|
|
696
|
-
function headerFields(input, boundary) {
|
|
697
|
-
const subject = input.kind === "failed" ? "Undelivered Mail Returned to Sender" : "Delayed Mail (still being retried)";
|
|
698
|
-
const domain = strip2(input.reportingMta);
|
|
699
|
-
return [
|
|
700
|
-
foldHeader2("Date", formatDate2(input.date)),
|
|
701
|
-
foldHeader2("From", `Mail Delivery System <${strip2(input.from)}>`),
|
|
702
|
-
foldHeader2("To", `<${strip2(input.to)}>`),
|
|
703
|
-
foldHeader2("Subject", encodeHeaderValue(subject)),
|
|
704
|
-
foldHeader2("Message-ID", `<${crypto.randomUUID()}@${domain}>`),
|
|
705
|
-
foldHeader2("Auto-Submitted", "auto-replied"),
|
|
706
|
-
foldHeader2("MIME-Version", "1.0"),
|
|
707
|
-
foldHeader2("Content-Type", `multipart/report; report-type=delivery-status; boundary="${boundary}"`)
|
|
708
|
-
];
|
|
709
|
-
}
|
|
710
|
-
var concat = (parts) => {
|
|
711
|
-
const out = new Uint8Array(parts.reduce((n, p) => n + p.length, 0));
|
|
712
|
-
let at = 0;
|
|
713
|
-
for (const part of parts) {
|
|
714
|
-
out.set(part, at);
|
|
715
|
-
at += part.length;
|
|
716
|
-
}
|
|
717
|
-
return out;
|
|
718
|
-
};
|
|
719
|
-
function boundaryFor(content) {
|
|
720
|
-
const text = new TextDecoder("latin1").decode(content);
|
|
721
|
-
for (;; ) {
|
|
722
|
-
const boundary = `=_dsn_${crypto.randomUUID()}`;
|
|
723
|
-
if (!text.includes(boundary))
|
|
724
|
-
return boundary;
|
|
725
|
-
}
|
|
726
|
-
}
|
|
727
|
-
function buildDsn(input) {
|
|
728
|
-
const original = input.original ? returned(input.original, input.returnContent, input.maxReturn) : undefined;
|
|
729
|
-
const boundary = boundaryFor(original?.body ?? new Uint8Array);
|
|
730
|
-
const text = encodeQuotedPrintable(humanText(input));
|
|
731
|
-
const status = deliveryStatus({
|
|
732
|
-
kind: input.kind,
|
|
733
|
-
reportingMta: input.reportingMta,
|
|
734
|
-
arrival: input.arrival,
|
|
735
|
-
recipients: input.recipients,
|
|
736
|
-
...input.willRetryUntil ? { willRetryUntil: input.willRetryUntil } : {}
|
|
737
|
-
});
|
|
738
|
-
const parts = [
|
|
739
|
-
...headerFields(input, boundary),
|
|
740
|
-
"",
|
|
741
|
-
"This is a MIME-formatted delivery status notification.",
|
|
742
|
-
"",
|
|
743
|
-
`--${boundary}`,
|
|
744
|
-
"Content-Type: text/plain; charset=utf-8",
|
|
745
|
-
"Content-Transfer-Encoding: quoted-printable",
|
|
746
|
-
"",
|
|
747
|
-
text,
|
|
748
|
-
`--${boundary}`,
|
|
749
|
-
"Content-Type: message/delivery-status",
|
|
750
|
-
"",
|
|
751
|
-
status
|
|
752
|
-
];
|
|
753
|
-
const end = `--${boundary}--\r
|
|
754
|
-
`;
|
|
755
|
-
if (!original)
|
|
756
|
-
return encoder.encode(`${parts.join(`\r
|
|
757
|
-
`)}\r
|
|
758
|
-
${end}`);
|
|
759
|
-
const head = [
|
|
760
|
-
...parts,
|
|
761
|
-
`--${boundary}`,
|
|
762
|
-
`Content-Type: ${original.type}`,
|
|
763
|
-
...original.eightBit ? ["Content-Transfer-Encoding: 8bit"] : [],
|
|
764
|
-
"",
|
|
765
|
-
""
|
|
766
|
-
].join(`\r
|
|
767
|
-
`);
|
|
768
|
-
return concat([
|
|
769
|
-
encoder.encode(head),
|
|
770
|
-
original.body,
|
|
771
|
-
encoder.encode(`\r
|
|
772
|
-
${end}`)
|
|
773
|
-
]);
|
|
774
|
-
}
|
|
775
|
-
|
|
776
911
|
// src/queue/retry.ts
|
|
777
912
|
function retryDelay(attempts, retry, random) {
|
|
778
913
|
const exponent = Math.max(0, attempts - 1);
|
|
@@ -804,7 +939,9 @@ function reported(r, kind) {
|
|
|
804
939
|
};
|
|
805
940
|
}
|
|
806
941
|
async function notify(ctx, item, message, kind, recipients, now) {
|
|
807
|
-
|
|
942
|
+
const states = new Map(item.recipients.map((r) => [r.address, r]));
|
|
943
|
+
const wanted = recipients.filter((r) => asked(kind, states.get(r.address)?.notify));
|
|
944
|
+
if (item.from === "" || wanted.length === 0)
|
|
808
945
|
return;
|
|
809
946
|
const { settings, store, events } = ctx;
|
|
810
947
|
try {
|
|
@@ -816,13 +953,20 @@ async function notify(ctx, item, message, kind, recipients, now) {
|
|
|
816
953
|
to: item.from,
|
|
817
954
|
arrival: new Date(item.createdAt),
|
|
818
955
|
date: lastAttempt,
|
|
819
|
-
|
|
956
|
+
...item.envid === undefined ? {} : { envid: item.envid },
|
|
957
|
+
recipients: wanted.map((r) => {
|
|
820
958
|
const reply = reported(r, kind);
|
|
821
|
-
|
|
959
|
+
const orcpt = states.get(r.address)?.orcpt;
|
|
960
|
+
return {
|
|
961
|
+
address: r.address,
|
|
962
|
+
...reply ? { reply } : {},
|
|
963
|
+
lastAttempt,
|
|
964
|
+
...orcpt ? { orcpt } : {}
|
|
965
|
+
};
|
|
822
966
|
}),
|
|
823
967
|
...kind === "delayed" ? { willRetryUntil: new Date(giveUpAt(item.createdAt, settings.retry)) } : {},
|
|
824
968
|
...message ? { original: message } : {},
|
|
825
|
-
returnContent: settings.dsn.returnContent,
|
|
969
|
+
returnContent: kind === "relayed" ? "headers" : returnOf(item.ret, settings.dsn.returnContent),
|
|
826
970
|
maxReturn: settings.limits.maxDsnReturn
|
|
827
971
|
});
|
|
828
972
|
const added = await store.add({
|
|
@@ -836,7 +980,7 @@ async function notify(ctx, item, message, kind, recipients, now) {
|
|
|
836
980
|
id: added.id,
|
|
837
981
|
of: item.id,
|
|
838
982
|
to: item.from,
|
|
839
|
-
recipients:
|
|
983
|
+
recipients: wanted.map((r) => r.address)
|
|
840
984
|
});
|
|
841
985
|
} catch (error) {
|
|
842
986
|
events.emit("error", { error, id: item.id });
|
|
@@ -869,7 +1013,7 @@ function settle(item, updates, skipped, now, settings) {
|
|
|
869
1013
|
}).map((u) => u.status === "deferred" && next === undefined ? expired(u) : u);
|
|
870
1014
|
const deferred = all.filter((u) => u.status === "deferred");
|
|
871
1015
|
const { delayAfter } = settings.dsn;
|
|
872
|
-
const delayDsn = !item.delayNotified && item.from !== "" && delayAfter !== false && deferred.length > 0 && now - item.createdAt >= delayAfter;
|
|
1016
|
+
const delayDsn = !item.delayNotified && deferred.some((u) => asked("delayed", was.get(u.address)?.notify)) && item.from !== "" && delayAfter !== false && deferred.length > 0 && now - item.createdAt >= delayAfter;
|
|
873
1017
|
return {
|
|
874
1018
|
result: {
|
|
875
1019
|
now,
|
|
@@ -995,6 +1139,8 @@ async function record(ctx, item, updates, skipped, message, used) {
|
|
|
995
1139
|
}
|
|
996
1140
|
emitOutcomes(events, item, settled, used);
|
|
997
1141
|
await notify(ctx, item, message, "failed", settled.failed, now);
|
|
1142
|
+
const relayed = settled.delivered.filter((u) => u.dsnOffered === false);
|
|
1143
|
+
await notify(ctx, item, message, "relayed", relayed, now);
|
|
998
1144
|
if (settled.delayDsn) {
|
|
999
1145
|
await notify(ctx, item, message, "delayed", settled.deferred, now);
|
|
1000
1146
|
}
|
|
@@ -1258,10 +1404,10 @@ function createQueue(options) {
|
|
|
1258
1404
|
return {
|
|
1259
1405
|
owner: settings.owner,
|
|
1260
1406
|
async enqueue(message, envelope) {
|
|
1261
|
-
const { from, to } = checkEnvelope(envelope, limits.maxRecipients);
|
|
1407
|
+
const { from, to, ...dsn } = checkEnvelope(envelope, limits.maxRecipients);
|
|
1262
1408
|
const bytes = await readMessage(message, limits.maxMessageSize);
|
|
1263
1409
|
checkLineEnds(bytes);
|
|
1264
|
-
const item = await store.add({ from, to, message: bytes, createdAt: settings.now() }, limits.maxItems === undefined ? {} : { maxItems: limits.maxItems });
|
|
1410
|
+
const item = await store.add({ from, to, message: bytes, createdAt: settings.now(), ...dsn }, limits.maxItems === undefined ? {} : { maxItems: limits.maxItems });
|
|
1265
1411
|
worker.wake();
|
|
1266
1412
|
return item;
|
|
1267
1413
|
},
|
|
@@ -1285,9 +1431,12 @@ function createQueue(options) {
|
|
|
1285
1431
|
};
|
|
1286
1432
|
}
|
|
1287
1433
|
export {
|
|
1434
|
+
DEFAULT_MAX_RETURN,
|
|
1288
1435
|
QueueError2 as QueueError,
|
|
1289
|
-
createQueue
|
|
1436
|
+
createQueue,
|
|
1437
|
+
deliveredDsn,
|
|
1438
|
+
failedDsn
|
|
1290
1439
|
};
|
|
1291
1440
|
|
|
1292
|
-
//# debugId=
|
|
1441
|
+
//# debugId=9F6E913A6461985264756E2164756E21
|
|
1293
1442
|
//# sourceMappingURL=index.js.map
|