@bumail/queue 0.9.0 → 0.11.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 +30 -2
- package/dist/chunks/{index-9yp15arf.js → index-t6q7zxgj.js} +27 -4
- package/dist/chunks/index-t6q7zxgj.js.map +14 -0
- package/dist/contract/checks.d.ts +4 -0
- package/dist/contract/checks.d.ts.map +1 -1
- package/dist/contract/dsn-checks.d.ts.map +1 -1
- package/dist/contract/queue-store.d.ts +15 -7
- package/dist/contract/queue-store.d.ts.map +1 -1
- package/dist/contract/types.d.ts +5 -0
- package/dist/contract/types.d.ts.map +1 -1
- package/dist/dsn/delivered.d.ts +2 -16
- package/dist/dsn/delivered.d.ts.map +1 -1
- package/dist/dsn/failed.d.ts +29 -0
- package/dist/dsn/failed.d.ts.map +1 -0
- package/dist/dsn/for.d.ts +26 -0
- package/dist/dsn/for.d.ts.map +1 -0
- package/dist/dsn/policy.d.ts +2 -0
- package/dist/dsn/policy.d.ts.map +1 -1
- package/dist/errors.d.ts +3 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +99 -42
- package/dist/index.js.map +10 -8
- package/dist/memory/index.js +10 -6
- package/dist/memory/index.js.map +3 -3
- package/dist/memory/store.d.ts +2 -2
- package/dist/memory/store.d.ts.map +1 -1
- package/dist/postgres/complete.d.ts +13 -0
- package/dist/postgres/complete.d.ts.map +1 -0
- package/dist/postgres/index.js +85 -74
- package/dist/postgres/index.js.map +6 -5
- package/dist/postgres/store.d.ts +2 -2
- package/dist/postgres/store.d.ts.map +1 -1
- package/dist/queue/deliver.d.ts.map +1 -1
- package/dist/queue/notify.d.ts +35 -6
- package/dist/queue/notify.d.ts.map +1 -1
- package/dist/queue/settings/limits.d.ts.map +1 -1
- package/dist/redis/complete.d.ts +11 -0
- package/dist/redis/complete.d.ts.map +1 -0
- package/dist/redis/index.js +133 -110
- package/dist/redis/index.js.map +7 -6
- package/dist/redis/scripts.d.ts +8 -5
- package/dist/redis/scripts.d.ts.map +1 -1
- package/dist/redis/store.d.ts +2 -2
- package/dist/redis/store.d.ts.map +1 -1
- package/dist/sqlite/index.js +29 -20
- package/dist/sqlite/index.js.map +5 -4
- package/dist/sqlite/insert.d.ts +5 -0
- package/dist/sqlite/insert.d.ts.map +1 -0
- package/dist/sqlite/store.d.ts +2 -2
- package/dist/sqlite/store.d.ts.map +1 -1
- package/docs/guide.md +59 -0
- package/docs/roadmap.md +36 -10
- package/docs/troubleshooting.md +67 -3
- package/package.json +4 -4
- package/dist/chunks/index-9yp15arf.js.map +0 -14
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
QueueError2,
|
|
7
7
|
invalid,
|
|
8
8
|
checkOwner
|
|
9
|
-
} from "./chunks/index-
|
|
9
|
+
} from "./chunks/index-t6q7zxgj.js";
|
|
10
10
|
|
|
11
11
|
// src/dsn/build.ts
|
|
12
12
|
import {
|
|
@@ -306,31 +306,48 @@ function asked(kind, notify) {
|
|
|
306
306
|
return notify.includes(event[kind]);
|
|
307
307
|
}
|
|
308
308
|
var returnOf = (ret, fallback) => ret === "HDRS" ? "headers" : ret === "FULL" ? "full" : fallback;
|
|
309
|
+
var DEFAULT_MAX_RETURN = 64 * 1024;
|
|
309
310
|
|
|
310
|
-
// src/dsn/
|
|
311
|
-
function
|
|
312
|
-
|
|
313
|
-
if (input.to === "" || wanted.length === 0)
|
|
311
|
+
// src/dsn/for.ts
|
|
312
|
+
function dsnFor(kind, input, recipients, returnContent) {
|
|
313
|
+
if (input.to === "" || recipients.length === 0)
|
|
314
314
|
return;
|
|
315
|
-
const date = input.date ?? new Date;
|
|
316
315
|
return buildDsn({
|
|
317
|
-
kind
|
|
316
|
+
kind,
|
|
318
317
|
reportingMta: input.reportingMta,
|
|
319
318
|
from: input.from,
|
|
320
319
|
to: input.to,
|
|
321
320
|
arrival: input.arrival,
|
|
322
|
-
date,
|
|
321
|
+
date: input.date ?? new Date,
|
|
323
322
|
...input.envid === undefined ? {} : { envid: input.envid },
|
|
324
|
-
recipients
|
|
325
|
-
address: r.address,
|
|
326
|
-
lastAttempt: date,
|
|
327
|
-
...r.orcpt ? { orcpt: r.orcpt } : {}
|
|
328
|
-
})),
|
|
323
|
+
recipients,
|
|
329
324
|
...input.original ? { original: input.original } : {},
|
|
330
|
-
returnContent
|
|
331
|
-
maxReturn: input.maxReturn ??
|
|
325
|
+
returnContent,
|
|
326
|
+
maxReturn: input.maxReturn ?? DEFAULT_MAX_RETURN
|
|
332
327
|
});
|
|
333
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
|
+
}
|
|
334
351
|
// src/queue/envelope.ts
|
|
335
352
|
import { isMailbox } from "@bumail/smtp/client";
|
|
336
353
|
var ADDRESS = /^[^\s<>]{1,64}@[^\s<>@]{1,253}$/u;
|
|
@@ -504,7 +521,7 @@ function limitsOf(options) {
|
|
|
504
521
|
maxRecipients: numberOf("limits.maxRecipients", l.maxRecipients, 100, 1),
|
|
505
522
|
maxItems: l.maxItems === undefined ? undefined : numberOf("limits.maxItems", l.maxItems, 0, 1),
|
|
506
523
|
maxReplyText: numberOf("limits.maxReplyText", l.maxReplyText, 512, 64, 900),
|
|
507
|
-
maxDsnReturn: numberOf("limits.maxDsnReturn", l.maxDsnReturn,
|
|
524
|
+
maxDsnReturn: numberOf("limits.maxDsnReturn", l.maxDsnReturn, DEFAULT_MAX_RETURN, 0)
|
|
508
525
|
};
|
|
509
526
|
}
|
|
510
527
|
|
|
@@ -921,12 +938,12 @@ function reported(r, kind) {
|
|
|
921
938
|
text: [reply.text, ...said].filter((t) => t !== "").join(" ")
|
|
922
939
|
};
|
|
923
940
|
}
|
|
924
|
-
|
|
941
|
+
function dsnsFor(ctx, item, message, kind, recipients, now, failures) {
|
|
925
942
|
const states = new Map(item.recipients.map((r) => [r.address, r]));
|
|
926
943
|
const wanted = recipients.filter((r) => asked(kind, states.get(r.address)?.notify));
|
|
927
944
|
if (item.from === "" || wanted.length === 0)
|
|
928
|
-
return;
|
|
929
|
-
const { settings
|
|
945
|
+
return [];
|
|
946
|
+
const { settings } = ctx;
|
|
930
947
|
try {
|
|
931
948
|
const lastAttempt = new Date(now);
|
|
932
949
|
const dsn = buildDsn({
|
|
@@ -952,21 +969,61 @@ async function notify(ctx, item, message, kind, recipients, now) {
|
|
|
952
969
|
returnContent: kind === "relayed" ? "headers" : returnOf(item.ret, settings.dsn.returnContent),
|
|
953
970
|
maxReturn: settings.limits.maxDsnReturn
|
|
954
971
|
});
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
972
|
+
return [
|
|
973
|
+
{
|
|
974
|
+
item: { from: "", to: [item.from], message: dsn, createdAt: now },
|
|
975
|
+
kind,
|
|
976
|
+
to: item.from,
|
|
977
|
+
recipients: wanted.map((r) => r.address)
|
|
978
|
+
}
|
|
979
|
+
];
|
|
980
|
+
} catch (error) {
|
|
981
|
+
failures.push(error);
|
|
982
|
+
return [];
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
function dsnsOf(ctx, item, message, settled, now) {
|
|
986
|
+
const failures = [];
|
|
987
|
+
const build = (kind, recipients) => dsnsFor(ctx, item, message, kind, recipients, now, failures);
|
|
988
|
+
const pending = [
|
|
989
|
+
...build("failed", settled.failed),
|
|
990
|
+
...build("relayed", settled.delivered.filter((u) => u.dsnOffered === false)),
|
|
991
|
+
...settled.delayDsn ? build("delayed", settled.deferred) : []
|
|
992
|
+
];
|
|
993
|
+
return { pending, failures };
|
|
994
|
+
}
|
|
995
|
+
async function tellDsns(ctx, item, pending, completed) {
|
|
996
|
+
const { store, events } = ctx;
|
|
997
|
+
const stored = completed.dsns;
|
|
998
|
+
const outdated = !Array.isArray(stored);
|
|
999
|
+
if (outdated && pending.length > 0) {
|
|
1000
|
+
const error = new QueueError2("INVALID", "Queue store outdated: complete returned the item alone, not { item, dsns }; its DSNs are added one by one, outside the step that records the outcome");
|
|
1001
|
+
events.emit("error", { error, id: item.id });
|
|
1002
|
+
}
|
|
1003
|
+
for (const [index, dsn] of pending.entries()) {
|
|
1004
|
+
let id;
|
|
1005
|
+
if (outdated) {
|
|
1006
|
+
try {
|
|
1007
|
+
id = (await store.add(dsn.item)).id;
|
|
1008
|
+
} catch (error) {
|
|
1009
|
+
events.emit("error", { error, id: item.id });
|
|
1010
|
+
continue;
|
|
1011
|
+
}
|
|
1012
|
+
} else {
|
|
1013
|
+
id = stored[index]?.id;
|
|
1014
|
+
if (id === undefined) {
|
|
1015
|
+
const error = new QueueError2("INVALID", `The queue store stored ${stored.length} of the ${pending.length} DSNs of ${item.id}: a ${dsn.kind} DSN to ${dsn.to} is lost`);
|
|
1016
|
+
events.emit("error", { error, id: item.id });
|
|
1017
|
+
continue;
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
961
1020
|
events.emit("dsn", {
|
|
962
|
-
kind,
|
|
963
|
-
id
|
|
1021
|
+
kind: dsn.kind,
|
|
1022
|
+
id,
|
|
964
1023
|
of: item.id,
|
|
965
|
-
to:
|
|
966
|
-
recipients:
|
|
1024
|
+
to: dsn.to,
|
|
1025
|
+
recipients: dsn.recipients
|
|
967
1026
|
});
|
|
968
|
-
} catch (error) {
|
|
969
|
-
events.emit("error", { error, id: item.id });
|
|
970
1027
|
}
|
|
971
1028
|
}
|
|
972
1029
|
|
|
@@ -1099,9 +1156,10 @@ async function record(ctx, item, updates, skipped, message, used) {
|
|
|
1099
1156
|
const { settings, store, events } = ctx;
|
|
1100
1157
|
const now = settings.now();
|
|
1101
1158
|
const settled = settle(item, updates, skipped, now, settings);
|
|
1102
|
-
const
|
|
1159
|
+
const { pending, failures } = dsnsOf(ctx, item, message, settled, now);
|
|
1160
|
+
const completed = await store.complete(item.id, settings.owner, settled.result, pending.map((p) => p.item));
|
|
1103
1161
|
ctx.recorded();
|
|
1104
|
-
if (!
|
|
1162
|
+
if (!completed && await store.get(item.id) === undefined) {
|
|
1105
1163
|
const lapse = await ctx.leaseLapsed(now);
|
|
1106
1164
|
if (lapse) {
|
|
1107
1165
|
events.emit("error", { error: goneAfter(lapse, item.id), id: item.id });
|
|
@@ -1112,7 +1170,7 @@ async function record(ctx, item, updates, skipped, message, used) {
|
|
|
1112
1170
|
emitOutcomes(events, item, settled, used);
|
|
1113
1171
|
return;
|
|
1114
1172
|
}
|
|
1115
|
-
if (!
|
|
1173
|
+
if (!completed) {
|
|
1116
1174
|
const error = new QueueError2("LEASE_LOST", `The lease on ${item.id} was lost before its outcome was recorded; another worker will try it again`);
|
|
1117
1175
|
events.emit("error", { error, id: item.id });
|
|
1118
1176
|
return;
|
|
@@ -1121,12 +1179,9 @@ async function record(ctx, item, updates, skipped, message, used) {
|
|
|
1121
1179
|
events.emit("error", { error: unreadableError(item.id), id: item.id });
|
|
1122
1180
|
}
|
|
1123
1181
|
emitOutcomes(events, item, settled, used);
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
await
|
|
1127
|
-
if (settled.delayDsn) {
|
|
1128
|
-
await notify(ctx, item, message, "delayed", settled.deferred, now);
|
|
1129
|
-
}
|
|
1182
|
+
for (const error of failures)
|
|
1183
|
+
events.emit("error", { error, id: item.id });
|
|
1184
|
+
await tellDsns(ctx, item, pending, completed);
|
|
1130
1185
|
}
|
|
1131
1186
|
|
|
1132
1187
|
// src/queue/lease.ts
|
|
@@ -1414,10 +1469,12 @@ function createQueue(options) {
|
|
|
1414
1469
|
};
|
|
1415
1470
|
}
|
|
1416
1471
|
export {
|
|
1472
|
+
DEFAULT_MAX_RETURN,
|
|
1417
1473
|
QueueError2 as QueueError,
|
|
1418
1474
|
createQueue,
|
|
1419
|
-
deliveredDsn
|
|
1475
|
+
deliveredDsn,
|
|
1476
|
+
failedDsn
|
|
1420
1477
|
};
|
|
1421
1478
|
|
|
1422
|
-
//# debugId=
|
|
1479
|
+
//# debugId=9190A6EB36D3FC0664756E2164756E21
|
|
1423
1480
|
//# sourceMappingURL=index.js.map
|