mail-time 4.1.0 → 5.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.
- package/README.md +76 -19
- package/adapters/redis.d.ts +19 -1
- package/adapters/redis.js +337 -8
- package/helpers.js +19 -0
- package/index.cjs +778 -127
- package/index.d.cts +50 -7
- package/index.d.ts +50 -7
- package/index.js +423 -118
- package/package.json +21 -4
- package/presets.js +2 -2
- package/scripts/migrate-redis-queue-to-cluster.mjs +108 -0
package/README.md
CHANGED
|
@@ -19,7 +19,7 @@ Bulletproof email queue for [horizontally scaled](#sending-emails-from-a-cluster
|
|
|
19
19
|
|
|
20
20
|
`MailTime` runs in one of two modes:
|
|
21
21
|
|
|
22
|
-
- **`server`** — drains the queue and sends emails via SMTP.
|
|
22
|
+
- **`server`** — drains the queue and sends emails via SMTP. Per-row leases let one worker claim each attempt even when many are running.
|
|
23
23
|
- **`client`** — only enqueues emails. Use for app servers in a "dedicated mail micro-service" topology.
|
|
24
24
|
|
|
25
25
|
Many clients + one or more servers coexist behind the same `prefix` in the same store.
|
|
@@ -29,7 +29,7 @@ Many clients + one or more servers coexist behind the same `prefix` in the same
|
|
|
29
29
|
- 🏢 **Horizontally scaled** — synchronize one queue across N processes/hosts/DCs.
|
|
30
30
|
- 🔁 **Multi-SMTP rotation** — `backup` (failover) and `balancer` (round-robin) strategies.
|
|
31
31
|
- 💪 **Built-in retries** — storage-backed retry with per-letter transport pinning.
|
|
32
|
-
- 🎯 **Per-recipient retries** — when a multi-`to` send is partially rejected,
|
|
32
|
+
- 🎯 **Per-recipient retries** — when a multi-`to` send is partially rejected, later attempts exclude accepted addresses.
|
|
33
33
|
- 📮 **Email concatenation** — fold same-`to` emails arriving inside a window into one letter.
|
|
34
34
|
- 🎛️ **One-line setup** — built-in [presets](#settings-presets) for `transactional`, `otp`, `newsletter`, `marketing`, `notifications`, `alerts`.
|
|
35
35
|
- 🛢️ **Three first-party storages** — MongoDB, Redis, PostgreSQL. Plus a [custom-adapter contract](https://github.com/veliovgroup/mail-time/blob/master/docs/queue-api.md).
|
|
@@ -84,7 +84,7 @@ MailTime keeps every letter in the queue until SMTP confirms delivery.
|
|
|
84
84
|
|
|
85
85
|
### Sending emails from a cluster
|
|
86
86
|
|
|
87
|
-
Most apps schedule recurring emails (daily digest, weekly summary). On a single server this is trivial. In a cluster, every node would otherwise send the same email N times. MailTime's lease prevents
|
|
87
|
+
Most apps schedule recurring emails (daily digest, weekly summary). On a single server this is trivial. In a cluster, every node would otherwise send the same email N times. MailTime's lease prevents concurrent duplicate claims. SMTP acceptance and queue completion cannot be one atomic transaction: a worker crash between them can still cause a later retry. See [reliability boundary](https://github.com/veliovgroup/mail-time/blob/master/docs/tuning.md#reliability-boundary).
|
|
88
88
|
|
|
89
89
|
```ascii
|
|
90
90
|
|===================THE=CLUSTER===================| |=QUEUE=|
|
|
@@ -271,9 +271,7 @@ process.on('SIGTERM', async () => {
|
|
|
271
271
|
});
|
|
272
272
|
```
|
|
273
273
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
`destroy()` is idempotent and stops the scheduler. Always call it from tests; pair with `drain()` when iterate-driven sends ran.
|
|
274
|
+
`destroy()` without `{ drain: true }` stops immediately: in-flight completions make no storage writes and their claims recover after `sendingTimeout`. Use it for forced teardown, not graceful shutdown.
|
|
277
275
|
|
|
278
276
|
#### Pause / resume a server (backpressure)
|
|
279
277
|
|
|
@@ -300,7 +298,7 @@ Queue storage and scheduler storage can be the same store or different ones. Use
|
|
|
300
298
|
|
|
301
299
|
| Queue | Scheduler (`josk`) | Best for |
|
|
302
300
|
| -------- | ------------------ | -------------------------------------------- |
|
|
303
|
-
| Postgres | Postgres | Multi-DC, mixed clocks,
|
|
301
|
+
| Postgres | Postgres | Multi-DC, mixed clocks, strongest consistency. |
|
|
304
302
|
| Redis | Redis | High-throughput single-region. |
|
|
305
303
|
| Mongo | Mongo | Apps already on Mongo (especially Meteor). |
|
|
306
304
|
| Mongo | Redis | Durable letter storage + sub-second polling. |
|
|
@@ -338,11 +336,11 @@ const mailTime = new MailTime(mailTimePreset('otp', {
|
|
|
338
336
|
| Preset | Shape | Best for |
|
|
339
337
|
| --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- |
|
|
340
338
|
| `transactional` | `retries: 30`, `retryDelay: 10s`, `concatEmails: false`, `concurrency: 1`, `josk.zombieTime: 120s` | Receipts, password resets, account changes, welcome emails. |
|
|
341
|
-
| `otp` | `retries: 5`, `retryDelay: 2s`, snappy `revolvingInterval: 1024` + jitter `256/1024`, `concurrency: 4`, `sendingTimeout:
|
|
339
|
+
| `otp` | `retries: 5`, `retryDelay: 2s`, snappy `revolvingInterval: 1024` + jitter `256/1024`, `concurrency: 4`, `sendingTimeout: 2min` | Sign-in codes, 2FA, verification codes — stale OTPs aren't worth resending forever. |
|
|
342
340
|
| `newsletter` | `concatEmails: true` with a 5-minute fold window, `concatSubject: 'Your updates'`, `retries: 5`, `retryDelay: 60s`, `concurrency: 2`, `sendingTimeout: 10min`, `josk.zombieTime: 5min` | Scheduled digests, weekly summaries, "what's new" emails. |
|
|
343
341
|
| `marketing` | `retries: 10`, `retryDelay: 30s`, `concatEmails: false`, `concurrency: 5`, `josk.zombieTime: 3min` | Promotional / campaign blasts where each letter is unique. |
|
|
344
342
|
| `notifications` | `concatEmails: true` with a 60-second fold window, `concatSubject: 'New activity'`, `retries: 8`, `retryDelay: 30s`, `concurrency: 3`, `josk.zombieTime: 3min` | App / social activity (likes, mentions, follows) where bursts collapse into one letter. |
|
|
345
|
-
| `alerts` | `retries: 20`, `retryDelay: 5s`, snappy `revolvingInterval: 1024` + jitter `256/1024`, `concurrency: 2`, `sendingTimeout:
|
|
343
|
+
| `alerts` | `retries: 20`, `retryDelay: 5s`, snappy `revolvingInterval: 1024` + jitter `256/1024`, `concurrency: 2`, `sendingTimeout: 2min` | Ops / admin alerts: monitoring, error reports, escalations. |
|
|
346
344
|
|
|
347
345
|
Presets are equally useful on `type: 'client'` instances — keys that don't apply to the client role are simply ignored.
|
|
348
346
|
|
|
@@ -371,8 +369,12 @@ Defaults fit moderate traffic in a single region. Reach for a [preset](#settings
|
|
|
371
369
|
| Option | Default | Change when |
|
|
372
370
|
| ---------------------------------------------- | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
|
|
373
371
|
| `mode` | `'batch'` | `'one'` to claim a single row per tick (fairness over throughput across cluster nodes) |
|
|
374
|
-
| `concurrency` | `1` | Raise to send N emails in parallel per instance.
|
|
375
|
-
| `sendingTimeout` | `300000` (5 min) | Stale-lock recovery window. Must exceed worst-case SMTP roundtrip;
|
|
372
|
+
| `concurrency` | `1` | Raise to send N emails in parallel per instance. CAS blocks concurrent claims; SMTP rate limits still apply. |
|
|
373
|
+
| `sendingTimeout` | `300000` (5 min) | Stale-lock recovery window. Must exceed worst-case SMTP roundtrip; MailTime logs a warning below `120000`. |
|
|
374
|
+
| `renewClaim` | `sendingTimeout / 3` | Set `false` to go back to a single stamp at claim time. Lower it if your storage round-trip is slow relative to `sendingTimeout`. |
|
|
375
|
+
| `maxRenewals` | `10` | Renewal-attempt ceiling. Recovery begins `sendingTimeout` after last successful stamp; with responsive storage this is roughly `sendingTimeout + maxRenewals × renewClaim`. |
|
|
376
|
+
| `shouldFailOver` | — | Set when your transport can tell "never delivered" from "may have been delivered" — see [Transport fail-over](#transport-fail-over). |
|
|
377
|
+
| `strictPayload` | `false` | Turn on when anything other than your own app can write to the queue storage. |
|
|
376
378
|
| `revolvingInterval` | `1536` ms | Lower → faster pickup; higher → less scheduler I/O |
|
|
377
379
|
| `josk.minRevolvingDelay` / `maxRevolvingDelay` | `512` / `2048` | Lower both → snappier polls, more storage load |
|
|
378
380
|
| `josk.zombieTime` | `60000` | **Never below 60s.** Iterate releases the JoSk lease as soon as scanning ends — only a stalled storage scan can blow this. |
|
|
@@ -385,12 +387,54 @@ Defaults fit moderate traffic in a single region. Reach for a [preset](#settings
|
|
|
385
387
|
|
|
386
388
|
For deeper JoSk semantics (lease lifecycle, scheduler adapters, recurring tasks), install the JoSk skill: **`npx skills add veliovgroup/josk`**.
|
|
387
389
|
|
|
390
|
+
## Transport fail-over
|
|
391
|
+
|
|
392
|
+
`backup` rotates after `failsToNext` failures. Veto rotation when an MTA may already hold the message:
|
|
393
|
+
|
|
394
|
+
```js
|
|
395
|
+
new MailTime({
|
|
396
|
+
// ...
|
|
397
|
+
shouldFailOver: (error) => error?.code !== 'EMESSAGE', // nodemailer: rejected after DATA
|
|
398
|
+
});
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
Transports may also set `error.mayFailOver = false`. Retry timing stays unchanged. Edge cases: [v4.1 → v5 migration guide](https://github.com/veliovgroup/mail-time/blob/master/docs/migration-v4.1-v5.md#shouldfailovererror-info-email).
|
|
402
|
+
|
|
403
|
+
## Queue payload trust
|
|
404
|
+
|
|
405
|
+
`raw` is always refused. Use `strictPayload` when queue writers should not control attachments, URLs, envelope, DKIM, or other nodemailer capabilities:
|
|
406
|
+
|
|
407
|
+
```js
|
|
408
|
+
new MailTime({
|
|
409
|
+
// ...
|
|
410
|
+
strictPayload: true,
|
|
411
|
+
allowedMailFields: ['attachments'], // optional allowlist extension
|
|
412
|
+
});
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
`strictPayload` also forces `disableFileAccess` and `disableUrlAccess`. Threat model and built-in allowlist: [v4.1 → v5 migration guide](https://github.com/veliovgroup/mail-time/blob/master/docs/migration-v4.1-v5.md#strictpayload--allowedmailfields).
|
|
416
|
+
|
|
417
|
+
## Resolving the sender
|
|
418
|
+
|
|
419
|
+
`from` receives selected transport plus `{ index, from }`:
|
|
420
|
+
|
|
421
|
+
```js
|
|
422
|
+
new MailTime({
|
|
423
|
+
// ...
|
|
424
|
+
from: (transport, details) => `"Acme" <${details.from}>`,
|
|
425
|
+
});
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
Use `details.from`, not `transport.options.from`, for class-instance transports. `MailTime.transportFrom(transport)` exposes same resolution. Details: [v4.1 → v5 migration guide](https://github.com/veliovgroup/mail-time/blob/master/docs/migration-v4.1-v5.md#fromtransport-details-and-mailtimetransportfrom).
|
|
429
|
+
|
|
388
430
|
## Templates
|
|
389
431
|
|
|
390
432
|
Two Mustache-like placeholder forms:
|
|
391
433
|
|
|
392
|
-
- `{{key}}` — string interpolation
|
|
393
|
-
- `{{{key}}}` — raw
|
|
434
|
+
- `{{key}}` — string interpolation. **HTML-escaped** in HTML contexts (`html`, `template`, `concatDelimiter`); verbatim in `text` bodies and subject headers, which are not HTML.
|
|
435
|
+
- `{{{key}}}` — raw interpolation, never escaped. Only use it for values you produced yourself.
|
|
436
|
+
|
|
437
|
+
Migration behavior: [v4.1 → v5 template escaping](https://github.com/veliovgroup/mail-time/blob/master/docs/migration-v4.1-v5.md#2-key-escapes-html-instead-of-stripping-tags).
|
|
394
438
|
|
|
395
439
|
Every `sendMail` option is available inside `text`, `html`, and the wrapping template:
|
|
396
440
|
|
|
@@ -441,12 +485,17 @@ await mailQueue.sendMail({
|
|
|
441
485
|
| `concatDelay` | `number` (ms) | `60000` | Fold window. |
|
|
442
486
|
| `revolvingInterval` | `number` (ms) | `1536` | Queue iteration interval. |
|
|
443
487
|
| `mode` | `'one' \| 'batch'` | `'batch'` | `'batch'` claims every due row per tick; `'one'` claims a single row per tick. |
|
|
444
|
-
| `concurrency` | `number` | `1` | Parallel SMTPs per instance.
|
|
445
|
-
| `sendingTimeout` | `number` (ms) | `300000` | Window after which a stuck `isSending=true` row becomes eligible again. Must exceed worst-case SMTP roundtrip.
|
|
488
|
+
| `concurrency` | `number` | `1` | Parallel SMTPs per instance. CAS blocks concurrent claims for one row. |
|
|
489
|
+
| `sendingTimeout` | `number` (ms) | `300000` | Window after which a stuck `isSending=true` row becomes eligible again. Must exceed worst-case SMTP roundtrip; values below `120000` log a warning. |
|
|
490
|
+
| `renewClaim` | `boolean \| number` (ms) | `sendingTimeout / 3` | Re-stamp `sendingAt` on the claimed row while its SMTP roundtrip runs, so a slow-but-healthy send never loses its lock to a recovery worker. `false` restores the v4 single-stamp behaviour. |
|
|
491
|
+
| `maxRenewals` | `number` | `10` | Renewal-attempt budget per send. Recovery begins `sendingTimeout` after last successful stamp; slow renewal writes can extend elapsed time beyond the nominal interval calculation. |
|
|
492
|
+
| `shouldFailOver` | `(error, info, email) => boolean` | — | Veto rotating to the next transport for this failure. Default: rotate unless the error carries `mayFailOver === false`. See [Transport fail-over](#transport-fail-over). |
|
|
493
|
+
| `strictPayload` | `boolean` | `false` | Narrow every queued letter to `allowedMailFields` and force nodemailer's `disableFileAccess` / `disableUrlAccess`. See [Queue payload trust](#queue-payload-trust). |
|
|
494
|
+
| `allowedMailFields` | `string[]` | — | Extra field names permitted under `strictPayload` (added to the built-in allowlist). |
|
|
446
495
|
| `verifyTransports` | `boolean` | `true` | Probe each transport via `transport.verify()` once at `ready()`. Failing transports are marked unusable, surfaced through `onError(error, null, { transportIndex, phase: 'verify' })`, and skipped during rotation/fallback. Throws from `ready()` if **every** transport fails. Transports without a `verify()` method are treated as healthy. |
|
|
447
496
|
| `template` | `string` | `'{{{html}}}'` | Default envelope. |
|
|
448
497
|
| `prefix` | `string` | `''` | Queue namespace. **Same** on every `client` and `server` for one logical queue; **different** per email class. Inherited by the queue adapter; JoSk scheduler uses `mailTimeQueue<prefix>`. |
|
|
449
|
-
| `from` | `string \| (transport) => string`
|
|
498
|
+
| `from` | `string \| (transport, details) => string` | — | Strongly recommended for spam-passing `From:` formatting. `details` is `{ index, from }` — see [Resolving the sender](#resolving-the-sender). |
|
|
450
499
|
| `debug` | `boolean` | `false` | Verbose logs. |
|
|
451
500
|
| `onSent(email, info)` | `function` | — | Called once the task is fully delivered. `email.mailOptions[i].accepted` lists every address that got through (across all attempts). |
|
|
452
501
|
| `onError(error, email, info)` | `function` | — | Called once the retry budget is exhausted with at least one un-accepted recipient. `email.mailOptions[i].rejected` lists each un-delivered address with its last error. Also fires once per transport that fails `verify()` at startup with `email === null` and `info = { transportIndex, phase: 'verify' }`. |
|
|
@@ -461,6 +510,7 @@ await mailQueue.sendMail({
|
|
|
461
510
|
| `minRevolvingDelay` | `512` | Lower bound of poll window. |
|
|
462
511
|
| `maxRevolvingDelay` | `2048` | Upper bound. |
|
|
463
512
|
| `zombieTime` | `60000` | Re-claim if `queue.iterate()` runs longer than this. **Do not drop below 60s.** |
|
|
513
|
+
| `lockLeaseTime` | `30000` | JoSk scheduler lease TTL. JoSk floors it at `2 * maxRevolvingDelay + 1000`; increase for slow storage claim batches. Separate from `zombieTime`, which controls task recovery. |
|
|
464
514
|
| `execute` | `'batch'` | JoSk scheduler batching; low impact for MailTime (one interval task per instance). |
|
|
465
515
|
| `concurrency` | `Infinity` | Cap overlapping JoSk handler runs on **this** process (`1` if ticks pile up). |
|
|
466
516
|
| `autoClear` | `false` | Remove orphan tasks from storage. |
|
|
@@ -478,21 +528,25 @@ For deeper JoSk semantics, install the JoSk skill: **`npx skills add veliovgroup
|
|
|
478
528
|
- `cancel(uuid)` — alias of `cancelMail`.
|
|
479
529
|
- `ping()` → `Promise<{status, code, statusCode, paused?, error?}>`. Pings scheduler then queue; `paused` reflects `isPaused`.
|
|
480
530
|
- `ready()` → `Promise<MailTime>`. Awaits all startup work; rejects with `.cause` on storage failure.
|
|
481
|
-
- `destroy(opts?)` → `boolean` or `Promise<boolean>` when `{ drain: true }`. Stops scheduler. Idempotent. Use `destroy({ drain: true })`
|
|
531
|
+
- `destroy(opts?)` → `boolean` or `Promise<boolean>` when `{ drain: true }`. Stops scheduler. Idempotent. Use `await destroy({ drain: true })` for graceful shutdown; plain `destroy()` aborts completion writes.
|
|
482
532
|
- `drain()` → `Promise<void>`. Resolves once every in-flight SMTP attempt finishes. Useful in tests and graceful-shutdown paths.
|
|
483
|
-
- `pause()` / `resume()` → `boolean`. Server-only reversible backpressure; no-ops on `client` or after `destroy()`. See [
|
|
533
|
+
- `pause()` / `resume()` → `boolean`. Server-only reversible backpressure; no-ops on `client` or after `destroy()`. See [Shutdown](#6-shutdown).
|
|
484
534
|
- `isPaused` → `boolean`. Read-only; always `false` on `client`.
|
|
485
535
|
|
|
486
536
|
### Queue constructors
|
|
487
537
|
|
|
488
538
|
| Constructor | Required option | Optional |
|
|
489
539
|
| ---------------------------------------- | ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
|
|
490
|
-
| `new RedisQueue({ client, prefix? })`
|
|
540
|
+
| `new RedisQueue({ client, prefix?, useHashTags? })` | connected `redis@^4/^5` standalone or Cluster client | `prefix` — inherited from `MailTime` when omitted. Set `useHashTags: true` on Redis/KeyDB Cluster; set same option on JoSk Redis adapter. |
|
|
491
541
|
| `new MongoQueue({ db, prefix? })` | `db` from `MongoClient#db()` | `prefix` — inherited from `MailTime` when omitted. Indexes auto-created on first `ready()`. |
|
|
492
542
|
| `new PostgresQueue({ client, prefix? })` | `pg.Pool` (recommended) or `pg.Client` | `prefix` — inherited from `MailTime` when omitted. `mail_time_queue` table auto-migrated on first `ready()`. |
|
|
493
543
|
|
|
494
544
|
For custom adapters see [docs/queue-api.md](https://github.com/veliovgroup/mail-time/blob/master/docs/queue-api.md).
|
|
495
545
|
|
|
546
|
+
### Redis Cluster
|
|
547
|
+
|
|
548
|
+
Set `useHashTags: true` on both `RedisQueue` and its JoSk Redis adapter. Queue state then uses `mailtime:{prefix}:letters` plus a tagged sorted-set schedule, so node-redis `createCluster()` can route every Lua operation to one slot. One prefix is one slot; shard high-volume traffic across prefixes. Tagged `iterate` returns at most 100 due rows per tick. Existing standalone keys are not read in this mode. Follow [Redis Cluster migration](https://github.com/veliovgroup/mail-time/blob/master/docs/migration-v5-v5.1.md) before cutover.
|
|
549
|
+
|
|
496
550
|
### Module functions
|
|
497
551
|
|
|
498
552
|
- `mailTimePreset(name, overrides?)` → fresh MailTime constructor config. Deep-clones the named [preset](#settings-presets) and deep-merges your overrides (scalars win, nested `josk` composes). Throws on unknown `name` or non-object `overrides`.
|
|
@@ -502,6 +556,7 @@ For custom adapters see [docs/queue-api.md](https://github.com/veliovgroup/mail-
|
|
|
502
556
|
### Static
|
|
503
557
|
|
|
504
558
|
- `MailTime.Template` — get/set the default HTML envelope template.
|
|
559
|
+
- `MailTime.transportFrom(transport)` → `string | undefined`. Resolves a transport's sender address across `options.from`, `transporter.options.from`, `_defaults.from`, and `_options.from`, accepting both `'a@b.c'` and `{ name, address }`. Returns the first *usable* address; `undefined` when none resolves. Same value the `from(transport, details)` callback receives as `details.from`.
|
|
505
560
|
|
|
506
561
|
## Migrations
|
|
507
562
|
|
|
@@ -511,6 +566,8 @@ Upgrade checklists, adapter contract changes, and rollout notes live in the docs
|
|
|
511
566
|
|------|-----|------------|
|
|
512
567
|
| 3.x | 4.1 | [v3 → v4](https://github.com/veliovgroup/mail-time/blob/master/docs/migration-v3-v4.md) |
|
|
513
568
|
| 4.0 | 4.1 | [v4.0 → v4.1](https://github.com/veliovgroup/mail-time/blob/master/docs/migration-v4-v4.1.md) |
|
|
569
|
+
| 4.1 | 5.0 | [v4.1 → v5](https://github.com/veliovgroup/mail-time/blob/master/docs/migration-v4.1-v5.md) |
|
|
570
|
+
| 5.0 | 5.1 | [v5 → v5.1 Redis Cluster](https://github.com/veliovgroup/mail-time/blob/master/docs/migration-v5-v5.1.md) |
|
|
514
571
|
|
|
515
572
|
New in v4 (opt-in): `mailTimePreset`, `concurrency`/`mode`, `sendingTimeout`, `drain()`/`pause()`/`resume()`, per-recipient handling, AI skills bundle.
|
|
516
573
|
|
package/adapters/redis.d.ts
CHANGED
|
@@ -4,7 +4,17 @@ export type RedisClient = {
|
|
|
4
4
|
set: (key: string, value: string, options?: object) => Promise<unknown>;
|
|
5
5
|
del: (key: string | string[]) => Promise<number>;
|
|
6
6
|
ping: () => Promise<string>;
|
|
7
|
-
scanIterator
|
|
7
|
+
scanIterator?: ((options: object) => AsyncIterable<string | string[]>) | undefined;
|
|
8
|
+
hGet?: ((key: string, field: string) => Promise<string | null>) | undefined;
|
|
9
|
+
eval?: ((script: string, options: {
|
|
10
|
+
keys: string[];
|
|
11
|
+
arguments: string[];
|
|
12
|
+
}) => Promise<unknown>) | undefined;
|
|
13
|
+
evalSha?: ((sha: string, options: {
|
|
14
|
+
keys: string[];
|
|
15
|
+
arguments: string[];
|
|
16
|
+
}) => Promise<unknown>) | undefined;
|
|
17
|
+
scriptLoad?: ((script: string) => Promise<string>) | undefined;
|
|
8
18
|
watch?: ((key: string) => Promise<unknown>) | undefined;
|
|
9
19
|
unwatch?: (() => Promise<unknown>) | undefined;
|
|
10
20
|
multi?: (() => object) | undefined;
|
|
@@ -12,6 +22,10 @@ export type RedisClient = {
|
|
|
12
22
|
export type RedisQueueOption = {
|
|
13
23
|
client: RedisClient;
|
|
14
24
|
prefix?: string | undefined;
|
|
25
|
+
/**
|
|
26
|
+
* - Use Redis Cluster hash-tag keys (`mailtime:{prefix}:*`). Default keeps existing standalone keys.
|
|
27
|
+
*/
|
|
28
|
+
useHashTags?: boolean | undefined;
|
|
15
29
|
};
|
|
16
30
|
/** Class representing Redis Queue for MailTime */
|
|
17
31
|
export class RedisQueue {
|
|
@@ -22,8 +36,12 @@ export class RedisQueue {
|
|
|
22
36
|
constructor(opts: RedisQueueOption);
|
|
23
37
|
name: string;
|
|
24
38
|
client: RedisClient;
|
|
39
|
+
useHashTags: boolean;
|
|
25
40
|
prefix: any;
|
|
26
41
|
uniqueName: string | undefined;
|
|
42
|
+
lettersKey: string | undefined;
|
|
43
|
+
scheduleKey: string | undefined;
|
|
44
|
+
concatKeysKey: string | undefined;
|
|
27
45
|
/**
|
|
28
46
|
* @async
|
|
29
47
|
* @memberOf RedisQueue
|