@bumail/queue 0.1.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.
Files changed (87) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +243 -0
  3. package/dist/chunks/index-p4k4rjb7.js +15 -0
  4. package/dist/chunks/index-p4k4rjb7.js.map +10 -0
  5. package/dist/chunks/index-xhsw4xcz.js +119 -0
  6. package/dist/chunks/index-xhsw4xcz.js.map +10 -0
  7. package/dist/contract/checks.d.ts +27 -0
  8. package/dist/contract/checks.d.ts.map +1 -0
  9. package/dist/contract/queue-store.d.ts +60 -0
  10. package/dist/contract/queue-store.d.ts.map +1 -0
  11. package/dist/contract/types.d.ts +102 -0
  12. package/dist/contract/types.d.ts.map +1 -0
  13. package/dist/dsn/build.d.ts +35 -0
  14. package/dist/dsn/build.d.ts.map +1 -0
  15. package/dist/dsn/content.d.ts +18 -0
  16. package/dist/dsn/content.d.ts.map +1 -0
  17. package/dist/dsn/fields.d.ts +38 -0
  18. package/dist/dsn/fields.d.ts.map +1 -0
  19. package/dist/errors.d.ts +17 -0
  20. package/dist/errors.d.ts.map +1 -0
  21. package/dist/index.d.ts +8 -0
  22. package/dist/index.d.ts.map +1 -0
  23. package/dist/index.js +1089 -0
  24. package/dist/index.js.map +30 -0
  25. package/dist/memory/index.d.ts +2 -0
  26. package/dist/memory/index.d.ts.map +1 -0
  27. package/dist/memory/index.js +121 -0
  28. package/dist/memory/index.js.map +10 -0
  29. package/dist/memory/store.d.ts +21 -0
  30. package/dist/memory/store.d.ts.map +1 -0
  31. package/dist/queue/deliver.d.ts +20 -0
  32. package/dist/queue/deliver.d.ts.map +1 -0
  33. package/dist/queue/envelope.d.ts +26 -0
  34. package/dist/queue/envelope.d.ts.map +1 -0
  35. package/dist/queue/events.d.ts +48 -0
  36. package/dist/queue/events.d.ts.map +1 -0
  37. package/dist/queue/limiter.d.ts +14 -0
  38. package/dist/queue/limiter.d.ts.map +1 -0
  39. package/dist/queue/notify.d.ts +19 -0
  40. package/dist/queue/notify.d.ts.map +1 -0
  41. package/dist/queue/options.d.ts +111 -0
  42. package/dist/queue/options.d.ts.map +1 -0
  43. package/dist/queue/outcome.d.ts +26 -0
  44. package/dist/queue/outcome.d.ts.map +1 -0
  45. package/dist/queue/queue.d.ts +43 -0
  46. package/dist/queue/queue.d.ts.map +1 -0
  47. package/dist/queue/retry.d.ts +16 -0
  48. package/dist/queue/retry.d.ts.map +1 -0
  49. package/dist/queue/route.d.ts +10 -0
  50. package/dist/queue/route.d.ts.map +1 -0
  51. package/dist/queue/settings/dsn.d.ts +8 -0
  52. package/dist/queue/settings/dsn.d.ts.map +1 -0
  53. package/dist/queue/settings/index.d.ts +25 -0
  54. package/dist/queue/settings/index.d.ts.map +1 -0
  55. package/dist/queue/settings/limits.d.ts +18 -0
  56. package/dist/queue/settings/limits.d.ts.map +1 -0
  57. package/dist/queue/settings/numbers.d.ts +7 -0
  58. package/dist/queue/settings/numbers.d.ts.map +1 -0
  59. package/dist/queue/settings/routes.d.ts +3 -0
  60. package/dist/queue/settings/routes.d.ts.map +1 -0
  61. package/dist/queue/settings/sessions.d.ts +5 -0
  62. package/dist/queue/settings/sessions.d.ts.map +1 -0
  63. package/dist/queue/settle.d.ts +21 -0
  64. package/dist/queue/settle.d.ts.map +1 -0
  65. package/dist/queue/worker.d.ts +27 -0
  66. package/dist/queue/worker.d.ts.map +1 -0
  67. package/dist/sqlite/index.d.ts +2 -0
  68. package/dist/sqlite/index.d.ts.map +1 -0
  69. package/dist/sqlite/index.js +288 -0
  70. package/dist/sqlite/index.js.map +13 -0
  71. package/dist/sqlite/open.d.ts +8 -0
  72. package/dist/sqlite/open.d.ts.map +1 -0
  73. package/dist/sqlite/options.d.ts +16 -0
  74. package/dist/sqlite/options.d.ts.map +1 -0
  75. package/dist/sqlite/rows.d.ts +28 -0
  76. package/dist/sqlite/rows.d.ts.map +1 -0
  77. package/dist/sqlite/schema.d.ts +14 -0
  78. package/dist/sqlite/schema.d.ts.map +1 -0
  79. package/dist/sqlite/store.d.ts +30 -0
  80. package/dist/sqlite/store.d.ts.map +1 -0
  81. package/dist/text.d.ts +14 -0
  82. package/dist/text.d.ts.map +1 -0
  83. package/docs/README.md +10 -0
  84. package/docs/guide.md +461 -0
  85. package/docs/roadmap.md +67 -0
  86. package/docs/troubleshooting.md +394 -0
  87. package/package.json +71 -0
@@ -0,0 +1,111 @@
1
+ import type { MxResolver, SendMailAuth, SendMailOptions, SendMailResult, SendMailTimeouts, TlsMode } from '@bumail/smtp/client';
2
+ import type { QueueStore } from '../contract/queue-store';
3
+ /** What delivers one message to one destination: `sendMail` of `@bumail/smtp/client`, or a fake in a spec. */
4
+ export type Sender = (message: Uint8Array, options: SendMailOptions) => Promise<SendMailResult>;
5
+ /** What the queue reads the time from, in milliseconds since the epoch: `Date.now` but in a spec. */
6
+ export interface Clock {
7
+ now(): number;
8
+ }
9
+ /** A host that takes every message for a route: an ISP's relay, a provider's submission port. */
10
+ export interface Smarthost {
11
+ readonly host: string;
12
+ /** Default 465 with `secure`, else 25 (587 for submission is yours to give). */
13
+ readonly port?: number;
14
+ /** TLS from the first byte (port 465). */
15
+ readonly secure?: boolean;
16
+ /** Default `required` with `auth` or `secure`, else `opportunistic`. */
17
+ readonly tls?: TlsMode;
18
+ /** Sent only once TLS checked the host's certificate. */
19
+ readonly auth?: SendMailAuth;
20
+ /** Certificates to trust besides the system's, PEM. */
21
+ readonly ca?: string | readonly string[];
22
+ }
23
+ /** `'mx'`: the recipient domain's own mail hosts, by its MX records. Otherwise, a smarthost. */
24
+ export type Route = 'mx' | Smarthost;
25
+ /**
26
+ * When to try a deferred recipient again: `first` after the first
27
+ * attempt, then each wait `factor` times the one before, at most `max`,
28
+ * each plus up to `jitter` of itself at random. Past `giveUpAfter` since
29
+ * the message was enqueued, a recipient still deferred fails. Milliseconds.
30
+ */
31
+ export interface RetrySchedule {
32
+ /** Default 30 minutes, RFC 5321 §4.5.4.1's least. */
33
+ readonly first?: number;
34
+ /** Default 2. */
35
+ readonly factor?: number;
36
+ /** Default 4 hours. */
37
+ readonly max?: number;
38
+ /** A fraction from 0 to 1. Default 0.1. */
39
+ readonly jitter?: number;
40
+ /** Default 5 days (RFC 5321 §4.5.4.1: "at least 4-5 days"). */
41
+ readonly giveUpAfter?: number;
42
+ }
43
+ /** The bounds of what the queue takes and keeps. */
44
+ export interface QueueLimits {
45
+ /** Bytes. Default 25 MiB. */
46
+ readonly maxMessageSize?: number;
47
+ /** Recipients per message. Default 100 (RFC 5321 §4.5.3.1.8). */
48
+ readonly maxRecipients?: number;
49
+ /** Items the store may hold; `enqueue` refuses past it. Default: no limit. */
50
+ readonly maxItems?: number;
51
+ /** Characters of a reply kept per recipient, from 64 to 900. Default 512. */
52
+ readonly maxReplyText?: number;
53
+ /** Bytes of the original message a DSN returns. Default 64 KiB. */
54
+ readonly maxDsnReturn?: number;
55
+ }
56
+ /** Delivery status notifications (RFC 3464), sent back to the sender. */
57
+ export interface DsnOptions {
58
+ /** The DSN's From address. Default `postmaster@<hostname>`. */
59
+ readonly from?: string;
60
+ /**
61
+ * Milliseconds after enqueuing before a "delayed" DSN for recipients
62
+ * still deferred; `false` for none. Default 4 hours.
63
+ */
64
+ readonly delayAfter?: number | false;
65
+ /**
66
+ * `headers`: the original's header fields (`text/rfc822-headers`).
67
+ * `full`: the whole original (`message/rfc822`) when it fits
68
+ * `maxDsnReturn`, its headers otherwise. Default `headers`.
69
+ */
70
+ readonly returnContent?: 'headers' | 'full';
71
+ }
72
+ export interface QueueOptions {
73
+ /** Where items are kept: `MemoryQueueStore`, `SqliteQueueStore`, or your own. */
74
+ readonly store: QueueStore;
75
+ /** This server's public name: given in EHLO, as the DSN's Reporting-MTA, and in the DSN's From. */
76
+ readonly hostname: string;
77
+ /** The default route. Default `'mx'`. */
78
+ readonly route?: Route;
79
+ /** A route per recipient domain, over `route`. */
80
+ readonly routes?: Readonly<Record<string, Route>>;
81
+ /** Needed by the `'mx'` route: `@bumail/dns`'s resolver, or any of its shape. */
82
+ readonly resolver?: MxResolver;
83
+ /** The port of MX hosts. Default 25; another one is for a test. */
84
+ readonly mxPort?: number;
85
+ /** TLS to MX hosts. Default `opportunistic`. */
86
+ readonly mxTls?: TlsMode;
87
+ /** Each step's timeout, in seconds, as `sendMail` takes them. */
88
+ readonly timeouts?: SendMailTimeouts;
89
+ /** Seconds one session may take, DNS included. Default 1800. */
90
+ readonly deadline?: number;
91
+ readonly retry?: RetrySchedule;
92
+ readonly dsn?: DsnOptions;
93
+ readonly limits?: QueueLimits;
94
+ /** Items delivered at once, by this worker, each with one session per recipient domain. Default 20. */
95
+ readonly concurrency?: number;
96
+ /** Sessions at once to one recipient domain, by this worker. Default 2. */
97
+ readonly perDomain?: number;
98
+ /** How long a claim holds an item, renewed while it is delivered; milliseconds, 1000 to 6 442 450 941. Default 10 minutes. */
99
+ readonly leaseMs?: number;
100
+ /** How often `start()` looks for due items; milliseconds, at most 2 147 483 647. Default 5000. */
101
+ readonly pollInterval?: number;
102
+ /** This worker's name on its leases. Default a random UUID. */
103
+ readonly owner?: string;
104
+ /** Default `Date.now`. */
105
+ readonly clock?: Clock;
106
+ /** Default `sendMail` of `@bumail/smtp/client`. */
107
+ readonly send?: Sender;
108
+ /** For the jitter. Default `Math.random`. */
109
+ readonly random?: () => number;
110
+ }
111
+ //# sourceMappingURL=options.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../../src/queue/options.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import type { Reply, SendMailResult, SmtpError } from '@bumail/smtp/client';
2
+ import type { Diagnostic, RecipientUpdate } from '../contract/types';
3
+ /**
4
+ * An `SmtpError`, read by its `name`: a second copy of `@bumail/smtp` (a
5
+ * different version installed for the app) throws errors of another
6
+ * class, which `instanceof` would take for unknown ones.
7
+ */
8
+ export declare const isSmtpError: (error: unknown) => error is SmtpError;
9
+ /**
10
+ * `sendMail` refused its options: the route is misconfigured (a smarthost
11
+ * it cannot use, a bad timeout), not the recipient. Retried, never bounced
12
+ * at once: fixing the configuration and restarting delivers the message.
13
+ */
14
+ export declare const isRouteError: (error: unknown) => boolean;
15
+ /** What a reply says, bounded and cleaned. */
16
+ export declare function diagnosticOf(reply: Reply, max: number, host?: string): Diagnostic;
17
+ /** Each recipient of a delivery `sendMail` resolved: taken, or refused one by one. */
18
+ export declare function outcomesOf(result: SendMailResult, group: readonly string[], max: number): RecipientUpdate[];
19
+ /**
20
+ * Each recipient of a delivery `sendMail` rejected: by its own reply when
21
+ * the server refused every one, else all alike — deferred when the error
22
+ * is temporary (a 4xx, the network, a timeout) or the route's own
23
+ * (`INVALID_OPTION`, X.3.5), failed otherwise.
24
+ */
25
+ export declare function outcomesOfError(error: unknown, group: readonly string[], max: number, host?: string): RecipientUpdate[];
26
+ //# sourceMappingURL=outcome.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"outcome.d.ts","sourceRoot":"","sources":["../../src/queue/outcome.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,KAAK,EACL,cAAc,EACd,SAAS,EAET,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAGrE;;;;GAIG;AACH,eAAO,MAAM,WAAW,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,SAGC,CAAC;AAExD;;;;GAIG;AACH,eAAO,MAAM,YAAY,GAAI,OAAO,OAAO,KAAG,OACQ,CAAC;AAKvD,8CAA8C;AAC9C,wBAAgB,YAAY,CAC3B,KAAK,EAAE,KAAK,EACZ,GAAG,EAAE,MAAM,EACX,IAAI,CAAC,EAAE,MAAM,GACX,UAAU,CAWZ;AAsCD,sFAAsF;AACtF,wBAAgB,UAAU,CACzB,MAAM,EAAE,cAAc,EACtB,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,GAAG,EAAE,MAAM,GACT,eAAe,EAAE,CA0BnB;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAC9B,KAAK,EAAE,OAAO,EACd,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,GAAG,EAAE,MAAM,EACX,IAAI,CAAC,EAAE,MAAM,GACX,eAAe,EAAE,CAuBnB"}
@@ -0,0 +1,43 @@
1
+ import type { QueueItem, QueueListOptions } from '../contract/types';
2
+ import { type MessageSource, type QueueEnvelope } from './envelope';
3
+ import { type QueueEvents, type QueueListener } from './events';
4
+ import type { QueueOptions } from './options';
5
+ /** The outbound queue: what the app enqueues, delivered across restarts. */
6
+ export interface Queue {
7
+ /** This worker's name on its leases. */
8
+ readonly owner: string;
9
+ /**
10
+ * Takes a message to send — already signed, as it will leave — with its
11
+ * envelope, and keeps it in the store, every recipient pending, due at
12
+ * once. Refuses with `INVALID`, `MESSAGE_TOO_BIG`,
13
+ * `TOO_MANY_RECIPIENTS` or `QUEUE_FULL`. The queue sends whatever it is
14
+ * given: authenticate the submitter before you enqueue.
15
+ */
16
+ enqueue(message: MessageSource, envelope: QueueEnvelope): Promise<QueueItem>;
17
+ /** One pass by hand: delivers every item due now; resolves with how many it claimed. */
18
+ deliverDue(): Promise<number>;
19
+ /** Delivers in the background: a pass every `pollInterval`, and at once after `enqueue`. */
20
+ start(): void;
21
+ /** Claims nothing more, lets the deliveries under way end, and gives back the rest. */
22
+ stop(): Promise<void>;
23
+ /** Listens to an event; returns what stops listening. */
24
+ on<E extends keyof QueueEvents>(event: E, listener: QueueListener<E>): () => void;
25
+ /** The items in the queue, the next due first. */
26
+ list(options?: QueueListOptions): Promise<QueueItem[]>;
27
+ get(id: string): Promise<QueueItem | undefined>;
28
+ /**
29
+ * Makes an item due now, and wakes the worker. False when there is no
30
+ * such item, or when a worker is delivering it: its outcome sets the
31
+ * next attempt, so asking now would change nothing.
32
+ */
33
+ retryNow(id: string): Promise<boolean>;
34
+ /** Drops an item, its message and its recipients, with no DSN; the item as it stood, or `undefined`. */
35
+ cancel(id: string): Promise<QueueItem | undefined>;
36
+ }
37
+ /**
38
+ * Creates the queue over a store. Nothing is delivered until `start()`,
39
+ * or `deliverDue()` by hand. Several queues — in one process or several —
40
+ * may share a store: each claims items under its own `owner`.
41
+ */
42
+ export declare function createQueue(options: QueueOptions): Queue;
43
+ //# sourceMappingURL=queue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queue.d.ts","sourceRoot":"","sources":["../../src/queue/queue.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrE,OAAO,EAGN,KAAK,aAAa,EAClB,KAAK,aAAa,EAElB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAU,KAAK,WAAW,EAAE,KAAK,aAAa,EAAE,MAAM,UAAU,CAAC;AACxE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAI9C,4EAA4E;AAC5E,MAAM,WAAW,KAAK;IACrB,wCAAwC;IACxC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB;;;;;;OAMG;IACH,OAAO,CAAC,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,aAAa,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;IAC7E,wFAAwF;IACxF,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IAC9B,4FAA4F;IAC5F,KAAK,IAAI,IAAI,CAAC;IACd,uFAAuF;IACvF,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,yDAAyD;IACzD,EAAE,CAAC,CAAC,SAAS,MAAM,WAAW,EAC7B,KAAK,EAAE,CAAC,EACR,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC,GACxB,MAAM,IAAI,CAAC;IACd,kDAAkD;IAClD,IAAI,CAAC,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;IACvD,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC,CAAC;IAChD;;;;OAIG;IACH,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACvC,wGAAwG;IACxG,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC,CAAC;CACnD;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,KAAK,CAmCxD"}
@@ -0,0 +1,16 @@
1
+ import type { RetrySettings } from './settings';
2
+ /**
3
+ * The wait after the `attempts`-th attempt failed for now: `first`, then
4
+ * `factor` times the wait before, at most `max`, plus up to `jitter` of
5
+ * itself — never less, so RFC 5321 §4.5.4.1's 30 minutes stay a floor.
6
+ */
7
+ export declare function retryDelay(attempts: number, retry: RetrySettings, random: () => number): number;
8
+ /** When a message's deferred recipients give up: `giveUpAfter` after it was enqueued. */
9
+ export declare const giveUpAt: (createdAt: number, retry: RetrySettings) => number;
10
+ /**
11
+ * The next attempt after the `attempts`-th, at `now`: the back-off, but no
12
+ * later than the moment of giving up, so the last try falls on it.
13
+ * `undefined` when that moment has come: what is still deferred fails.
14
+ */
15
+ export declare function nextAttemptAt(createdAt: number, now: number, attempts: number, retry: RetrySettings, random: () => number): number | undefined;
16
+ //# sourceMappingURL=retry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retry.d.ts","sourceRoot":"","sources":["../../src/queue/retry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD;;;;GAIG;AACH,wBAAgB,UAAU,CACzB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,aAAa,EACpB,MAAM,EAAE,MAAM,MAAM,GAClB,MAAM,CAIR;AAED,yFAAyF;AACzF,eAAO,MAAM,QAAQ,GAAI,WAAW,MAAM,EAAE,OAAO,aAAa,WAClC,CAAC;AAE/B;;;;GAIG;AACH,wBAAgB,aAAa,CAC5B,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,aAAa,EACpB,MAAM,EAAE,MAAM,MAAM,GAClB,MAAM,GAAG,SAAS,CAIpB"}
@@ -0,0 +1,10 @@
1
+ import type { SendMailOptions } from '@bumail/smtp/client';
2
+ import type { Route } from './options';
3
+ import type { Settings } from './settings';
4
+ /** The route for a recipient domain: its own, else the default. */
5
+ export declare function routeOf(settings: Settings, domain: string): Route;
6
+ /** The host an attempt goes to, as far as the route says: the smarthost, or nothing for MX. */
7
+ export declare function hostOf(route: Route): string | undefined;
8
+ /** What `sendMail` takes to send one domain's recipients of a message. */
9
+ export declare function sendOptionsOf(settings: Settings, domain: string, from: string, to: readonly string[]): SendMailOptions;
10
+ //# sourceMappingURL=route.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../src/queue/route.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C,mEAAmE;AACnE,wBAAgB,OAAO,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAG,KAAK,CAEjE;AAED,+FAA+F;AAC/F,wBAAgB,MAAM,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,GAAG,SAAS,CAEvD;AAED,0EAA0E;AAC1E,wBAAgB,aAAa,CAC5B,QAAQ,EAAE,QAAQ,EAClB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,SAAS,MAAM,EAAE,GACnB,eAAe,CA8BjB"}
@@ -0,0 +1,8 @@
1
+ import type { QueueOptions } from '../options';
2
+ export interface DsnSettings {
3
+ readonly from: string;
4
+ readonly delayAfter: number | false;
5
+ readonly returnContent: 'headers' | 'full';
6
+ }
7
+ export declare function dsnOf(options: QueueOptions, hostname: string): DsnSettings;
8
+ //# sourceMappingURL=dsn.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dsn.d.ts","sourceRoot":"","sources":["../../../src/queue/settings/dsn.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAG/C,MAAM,WAAW,WAAW;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,KAAK,CAAC;IACpC,QAAQ,CAAC,aAAa,EAAE,SAAS,GAAG,MAAM,CAAC;CAC3C;AAED,wBAAgB,KAAK,CAAC,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,GAAG,WAAW,CAc1E"}
@@ -0,0 +1,25 @@
1
+ import type { QueueOptions, Route, Sender } from '../options';
2
+ import { type DsnSettings } from './dsn';
3
+ import { type LimitSettings, type RetrySettings } from './limits';
4
+ export type { DsnSettings, LimitSettings, RetrySettings };
5
+ /** Every option checked, with its default. */
6
+ export interface Settings {
7
+ readonly options: QueueOptions;
8
+ readonly hostname: string;
9
+ /** The routes by domain, in lowercase. */
10
+ readonly routes: ReadonlyMap<string, Route>;
11
+ readonly retry: RetrySettings;
12
+ readonly limits: LimitSettings;
13
+ readonly dsn: DsnSettings;
14
+ readonly concurrency: number;
15
+ readonly perDomain: number;
16
+ readonly leaseMs: number;
17
+ readonly pollInterval: number;
18
+ readonly owner: string;
19
+ readonly now: () => number;
20
+ readonly send: Sender;
21
+ readonly random: () => number;
22
+ }
23
+ /** The options, checked, with their defaults; `INVALID` names the first wrong one. */
24
+ export declare function settingsOf(options: QueueOptions): Settings;
25
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/queue/settings/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAC9D,OAAO,EAAE,KAAK,WAAW,EAAS,MAAM,OAAO,CAAC;AAChD,OAAO,EACN,KAAK,aAAa,EAElB,KAAK,aAAa,EAElB,MAAM,UAAU,CAAC;AAKlB,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,aAAa,EAAE,CAAC;AAE1D,8CAA8C;AAC9C,MAAM,WAAW,QAAQ;IACxB,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAC;IAC/B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,0CAA0C;IAC1C,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAC5C,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;IAC9B,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B,QAAQ,CAAC,GAAG,EAAE,WAAW,CAAC;IAC1B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,MAAM,CAAC;CAC9B;AAKD,sFAAsF;AACtF,wBAAgB,UAAU,CAAC,OAAO,EAAE,YAAY,GAAG,QAAQ,CAqD1D"}
@@ -0,0 +1,18 @@
1
+ import type { QueueOptions } from '../options';
2
+ export interface RetrySettings {
3
+ readonly first: number;
4
+ readonly factor: number;
5
+ readonly max: number;
6
+ readonly jitter: number;
7
+ readonly giveUpAfter: number;
8
+ }
9
+ export interface LimitSettings {
10
+ readonly maxMessageSize: number;
11
+ readonly maxRecipients: number;
12
+ readonly maxItems: number | undefined;
13
+ readonly maxReplyText: number;
14
+ readonly maxDsnReturn: number;
15
+ }
16
+ export declare function retryOf(options: QueueOptions): RetrySettings;
17
+ export declare function limitsOf(options: QueueOptions): LimitSettings;
18
+ //# sourceMappingURL=limits.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"limits.d.ts","sourceRoot":"","sources":["../../../src/queue/settings/limits.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAG/C,MAAM,WAAW,aAAa;IAC7B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,aAAa;IAC7B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC9B;AAED,wBAAgB,OAAO,CAAC,OAAO,EAAE,YAAY,GAAG,aAAa,CAU5D;AAED,wBAAgB,QAAQ,CAAC,OAAO,EAAE,YAAY,GAAG,aAAa,CAiB7D"}
@@ -0,0 +1,7 @@
1
+ export declare const MINUTE = 60000;
2
+ export declare const HOUR: number;
3
+ /** The longest delay a timer takes: past it, Bun fires after 1 ms. */
4
+ export declare const MAX_TIMER = 2147483647;
5
+ /** A number in `[min, max]`, integer when asked, or its default. */
6
+ export declare function numberOf(name: string, value: unknown, fallback: number, min: number, max?: number, integer?: boolean): number;
7
+ //# sourceMappingURL=numbers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"numbers.d.ts","sourceRoot":"","sources":["../../../src/queue/settings/numbers.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,MAAM,QAAS,CAAC;AAC7B,eAAO,MAAM,IAAI,QAAc,CAAC;AAChC,sEAAsE;AACtE,eAAO,MAAM,SAAS,aAAgB,CAAC;AAEvC,oEAAoE;AACpE,wBAAgB,QAAQ,CACvB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,OAAO,EACd,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,EACX,GAAG,SAA0B,EAC7B,OAAO,UAAO,GACZ,MAAM,CAgBR"}
@@ -0,0 +1,3 @@
1
+ import type { QueueOptions } from '../options';
2
+ export declare function checkRoutes(options: QueueOptions): void;
3
+ //# sourceMappingURL=routes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../../src/queue/settings/routes.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAoB,MAAM,YAAY,CAAC;AAoDjE,wBAAgB,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI,CAYvD"}
@@ -0,0 +1,5 @@
1
+ import type { QueueOptions } from '../options';
2
+ export declare function checkTls(name: string, tls: unknown): void;
3
+ /** What every session takes, whatever its route: the MX port and TLS, the timeouts. */
4
+ export declare function checkSessions(options: QueueOptions): void;
5
+ //# sourceMappingURL=sessions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessions.d.ts","sourceRoot":"","sources":["../../../src/queue/settings/sessions.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAoB/C,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,GAAG,IAAI,CAWzD;AAED,uFAAuF;AACvF,wBAAgB,aAAa,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI,CAazD"}
@@ -0,0 +1,21 @@
1
+ import type { AttemptResult, QueueItem, RecipientUpdate } from '../contract/types';
2
+ import type { Settings } from './settings';
3
+ /** An attempt's outcomes, sorted, and what to record and send for them. */
4
+ export interface Settled {
5
+ readonly result: AttemptResult;
6
+ readonly delivered: readonly RecipientUpdate[];
7
+ /** Failed in this attempt: a 5xx, or given up. */
8
+ readonly failed: readonly RecipientUpdate[];
9
+ /** Still deferred, to try again at `result.nextAttemptAt`. */
10
+ readonly deferred: readonly RecipientUpdate[];
11
+ /** A "delayed" DSN is due for the deferred recipients. */
12
+ readonly delayDsn: boolean;
13
+ }
14
+ /**
15
+ * What an attempt comes to: the count, the next attempt (RFC 5321
16
+ * §4.5.4.1's back-off, or now when some recipients were left out because
17
+ * the worker is stopping), the deferred recipients that gave up, and
18
+ * whether the "delayed" DSN is due — once, for a sender that is not null.
19
+ */
20
+ export declare function settle(item: QueueItem, updates: readonly RecipientUpdate[], skipped: boolean, now: number, settings: Settings): Settled;
21
+ //# sourceMappingURL=settle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settle.d.ts","sourceRoot":"","sources":["../../src/queue/settle.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,aAAa,EACb,SAAS,EACT,eAAe,EACf,MAAM,mBAAmB,CAAC;AAE3B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C,2EAA2E;AAC3E,MAAM,WAAW,OAAO;IACvB,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B,QAAQ,CAAC,SAAS,EAAE,SAAS,eAAe,EAAE,CAAC;IAC/C,kDAAkD;IAClD,QAAQ,CAAC,MAAM,EAAE,SAAS,eAAe,EAAE,CAAC;IAC5C,8DAA8D;IAC9D,QAAQ,CAAC,QAAQ,EAAE,SAAS,eAAe,EAAE,CAAC;IAC9C,0DAA0D;IAC1D,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;CAC3B;AAeD;;;;;GAKG;AACH,wBAAgB,MAAM,CACrB,IAAI,EAAE,SAAS,EACf,OAAO,EAAE,SAAS,eAAe,EAAE,EACnC,OAAO,EAAE,OAAO,EAChB,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,QAAQ,GAChB,OAAO,CAoCT"}
@@ -0,0 +1,27 @@
1
+ import type { NotifyContext } from './notify';
2
+ /**
3
+ * Claims due items and delivers them, at most `concurrency` at once,
4
+ * renewing each lease while its item is delivered. `start()` runs passes
5
+ * every `pollInterval`, or at once when woken; `stop()` lets every
6
+ * delivery under way finish and gives back what it had not started.
7
+ */
8
+ export declare class Worker {
9
+ #private;
10
+ constructor(ctx: NotifyContext);
11
+ /**
12
+ * One pass: claims and delivers every item due, `concurrency` at a time,
13
+ * claiming again as each one ends — so a DSN enqueued meanwhile goes in
14
+ * the same pass. Resolves once all are done, with how many it claimed.
15
+ */
16
+ deliverDue(): Promise<number>;
17
+ /** Wakes a sleeping `start()` loop, or spares it its next sleep: something was enqueued. */
18
+ wake(): void;
19
+ start(): void;
20
+ /**
21
+ * Claims nothing more, lets every delivery under way end, and gives back
22
+ * what was claimed but not started, due at once for the next worker. A
23
+ * `start()` called meanwhile starts again once this ends.
24
+ */
25
+ stop(): Promise<void>;
26
+ }
27
+ //# sourceMappingURL=worker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../../src/queue/worker.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAE9C;;;;;GAKG;AACH,qBAAa,MAAM;;gBAaN,GAAG,EAAE,aAAa;IAsE9B;;;;OAIG;IACG,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC;IAgCnC,4FAA4F;IAC5F,IAAI,IAAI,IAAI;IAKZ,KAAK,IAAI,IAAI;IAqCb;;;;OAIG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAa3B"}
@@ -0,0 +1,2 @@
1
+ export { SqliteQueueStore, type SqliteQueueStoreOptions } from './store';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sqlite/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,KAAK,uBAAuB,EAAE,MAAM,SAAS,CAAC"}