mail-time 3.0.0 → 4.0.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/package.json CHANGED
@@ -1,22 +1,64 @@
1
1
  {
2
2
  "name": "mail-time",
3
- "version": "3.0.0",
4
- "description": "📮 Email queue extending NodeMailer with multi SMTP transports and horizontally scaled apps support",
3
+ "version": "4.0.0",
4
+ "description": "📮 Email queue extending nodemailer with multi-SMTP transports and horizontally scaled apps support",
5
5
  "main": "index.js",
6
+ "types": "./index.d.ts",
6
7
  "type": "module",
7
8
  "exports": {
8
- "import": "./index.js",
9
- "require": "./index.cjs"
9
+ ".": {
10
+ "import": {
11
+ "types": "./index.d.ts",
12
+ "default": "./index.js"
13
+ },
14
+ "require": {
15
+ "types": "./index.d.cts",
16
+ "default": "./index.cjs"
17
+ }
18
+ },
19
+ "./presets": {
20
+ "import": {
21
+ "types": "./presets.d.ts",
22
+ "default": "./presets.js"
23
+ }
24
+ },
25
+ "./adapters/mongo": {
26
+ "import": {
27
+ "types": "./adapters/mongo.d.ts",
28
+ "default": "./adapters/mongo.js"
29
+ }
30
+ },
31
+ "./adapters/redis": {
32
+ "import": {
33
+ "types": "./adapters/redis.d.ts",
34
+ "default": "./adapters/redis.js"
35
+ }
36
+ },
37
+ "./adapters/postgres": {
38
+ "import": {
39
+ "types": "./adapters/postgres.d.ts",
40
+ "default": "./adapters/postgres.js"
41
+ }
42
+ },
43
+ "./package.json": "./package.json"
10
44
  },
11
45
  "scripts": {
12
- "prepublishOnly": "rollup --config rollup.config.js",
13
- "test": "mocha ./test/npm.js"
46
+ "prepublishOnly": "rollup --config rollup.config.js && rm -rf ./.types-tmp && tsc --emitDeclarationOnly --declaration --outDir ./.types-tmp --rootDir ./ && node ./scripts/strip-internal-dts.mjs ./.types-tmp/index.d.ts ./.types-tmp/presets.d.ts ./.types-tmp/adapters/mongo.d.ts ./.types-tmp/adapters/redis.d.ts ./.types-tmp/adapters/postgres.d.ts && cp ./.types-tmp/index.d.ts ./index.d.ts && cp ./.types-tmp/index.d.ts ./index.d.cts && cp ./.types-tmp/presets.d.ts ./presets.d.ts && cp ./.types-tmp/adapters/*.d.ts ./adapters/ && rm -rf ./.types-tmp",
47
+ "test": "npm run test:jest && npm run test:mocha && npm run test:types",
48
+ "test:jest": "node --experimental-vm-modules ./node_modules/jest/bin/jest.js --config jest.config.mjs",
49
+ "test:mocha": "mocha ./test/npm.js",
50
+ "test:types": "tsc -p ./tsconfig.types.json",
51
+ "test:redis": "mocha ./test/npm-redis-redis.js",
52
+ "test:mongo": "mocha ./test/npm-mongo-mongo.js",
53
+ "test:postgres": "mocha ./test/npm-postgres.js",
54
+ "test:bun": "bun test ./test/jest"
14
55
  },
15
56
  "keywords": [
16
57
  "backup",
17
58
  "balanced",
18
59
  "balancer",
19
60
  "bulletproof",
61
+ "bun",
20
62
  "cluster",
21
63
  "email",
22
64
  "highload",
@@ -29,7 +71,9 @@
29
71
  "microservice",
30
72
  "micro-service",
31
73
  "nodemailer",
74
+ "postgres",
32
75
  "queue",
76
+ "scheduler",
33
77
  "template"
34
78
  ],
35
79
  "tags": [
@@ -37,6 +81,7 @@
37
81
  "balanced",
38
82
  "balancer",
39
83
  "bulletproof",
84
+ "bun",
40
85
  "cluster",
41
86
  "email",
42
87
  "highload",
@@ -49,7 +94,9 @@
49
94
  "microservice",
50
95
  "micro-service",
51
96
  "nodemailer",
97
+ "postgres",
52
98
  "queue",
99
+ "scheduler",
53
100
  "template"
54
101
  ],
55
102
  "repository": {
@@ -63,20 +110,22 @@
63
110
  "author": "dr.dimitru (https://veliovgroup.com)",
64
111
  "license": "BSD-3-Clause",
65
112
  "engines": {
66
- "node": ">=14.20.0"
113
+ "node": ">=20.9.0"
67
114
  },
68
115
  "dependencies": {
69
- "deepmerge": "^4.3.1",
70
- "josk": "^5.0.0"
116
+ "josk": "^6.1.0"
71
117
  },
72
118
  "devDependencies": {
73
- "bson": "^6.6.0",
74
- "bson-ext": "^4.0.3",
75
- "chai": "^5.1.0",
76
- "mocha": "^10.4.0",
77
- "mongodb": "^6.5.0",
78
- "nodemailer": "^6.9.12",
79
- "nodemailer-direct-transport": "^3.3.2",
80
- "redis": "^4.6.13"
119
+ "@jest/globals": "^30.4.1",
120
+ "@types/node": "^25.9.0",
121
+ "chai": "^6.2.2",
122
+ "jest": "^30.4.2",
123
+ "mocha": "^11.7.5",
124
+ "mongodb": "^7.2.0",
125
+ "nodemailer": "^8.0.7",
126
+ "pg": "^8.21.0",
127
+ "redis": "^5.12.1",
128
+ "rollup": "^4.60.4",
129
+ "typescript": "^6.0.3"
81
130
  }
82
131
  }
package/presets.d.ts ADDED
@@ -0,0 +1,87 @@
1
+ export type MailTimePresetConfig = {
2
+ concatEmails?: boolean | undefined;
3
+ concatDelay?: number | undefined;
4
+ concatSubject?: string | undefined;
5
+ retries?: number | undefined;
6
+ retryDelay?: number | undefined;
7
+ revolvingInterval?: number | undefined;
8
+ sendingTimeout?: number | undefined;
9
+ mode?: "one" | "batch" | undefined;
10
+ concurrency?: number | undefined;
11
+ onError?: ((error: unknown, email: object, details?: object) => void) | undefined;
12
+ josk?: object | undefined;
13
+ };
14
+ export type MailTimePresetName = keyof typeof PRESETS;
15
+ /**
16
+ * Materialize a MailTime constructor config from a built-in preset.
17
+ * The named preset is deep-cloned (so the result is freely mutable) and
18
+ * `overrides` is deep-merged on top — overrides win for scalar keys, and
19
+ * nested objects like `josk` are merged so the preset's defaults compose
20
+ * with the caller's `adapter`, `lockOwnerId`, `onError`, etc.
21
+ *
22
+ * ```js
23
+ * import { MailTime, RedisQueue, mailTimePreset } from 'mail-time';
24
+ * const mailTime = new MailTime(mailTimePreset('otp', {
25
+ * prefix: 'otp',
26
+ * queue: new RedisQueue({ client }),
27
+ * transports: [otpTransport],
28
+ * josk: { adapter: { type: 'redis', client } },
29
+ * }));
30
+ * ```
31
+ *
32
+ * @param {MailTimePresetName} name - one of `presetNames`
33
+ * @param {object} [overrides] - additional MailTime constructor options
34
+ * @returns {object} fresh, mutable MailTime constructor options
35
+ * @throws {Error} when `name` is unknown
36
+ * @throws {TypeError} when `overrides` is provided but not a plain object
37
+ */
38
+ export function mailTimePreset(name: MailTimePresetName, overrides?: object): object;
39
+ /**
40
+ * Read-only map of preset name → partial MailTime config. Use
41
+ * `mailTimePreset(name, overrides)` to materialize a merged copy; use
42
+ * this object directly only when you want to introspect or compose
43
+ * presets manually.
44
+ */
45
+ export const presets: Readonly<Record<"transactional" | "otp" | "newsletter" | "marketing" | "notifications" | "alerts", Readonly<MailTimePresetConfig>>>;
46
+ /**
47
+ * @typedef {keyof typeof PRESETS} MailTimePresetName
48
+ */
49
+ /**
50
+ * Names of every built-in preset.
51
+ * @type {ReadonlyArray<MailTimePresetName>}
52
+ */
53
+ export const presetNames: ReadonlyArray<MailTimePresetName>;
54
+ /**
55
+ * @typedef {object} MailTimePresetConfig
56
+ * @property {boolean} [concatEmails]
57
+ * @property {number} [concatDelay]
58
+ * @property {string} [concatSubject]
59
+ * @property {number} [retries]
60
+ * @property {number} [retryDelay]
61
+ * @property {number} [revolvingInterval]
62
+ * @property {number} [sendingTimeout]
63
+ * @property {'one' | 'batch'} [mode]
64
+ * @property {number} [concurrency]
65
+ * @property {(error: unknown, email: object, details?: object) => void} [onError]
66
+ * @property {object} [josk]
67
+ */
68
+ /**
69
+ * Built-in MailTime presets keyed by use-case. Each value is a partial
70
+ * MailTime constructor options object — pass it through `mailTimePreset`
71
+ * (or spread directly) and supply your own `queue` / `transports` /
72
+ * `josk.adapter` / `prefix`. Presets only set the knobs that differ from
73
+ * MailTime defaults so the rest of the constructor stays in your hands.
74
+ *
75
+ * | Preset | Shape | Best for |
76
+ * |-----------------|-------|----------|
77
+ * | `transactional` | High retries, single SMTP per instance, no concat | Receipts, password resets, account changes, welcome emails. |
78
+ * | `otp` | Few retries, fast retryDelay, snappy polling, parallel SMTPs | Sign-in codes, 2FA, verification codes — stale OTPs aren't worth resending forever. |
79
+ * | `newsletter` | `concatEmails: true` with 5-min fold window | Scheduled digests / weekly updates / "what's new" emails. |
80
+ * | `marketing` | High concurrency, moderate retries, no concat | Promotional / campaign blasts where each letter is unique. |
81
+ * | `notifications` | `concatEmails: true` with 60-s fold window | App / social activity (likes, mentions) where bursts collapse into one letter. |
82
+ * | `alerts` | Many retries, fast retryDelay, modest concurrency | Ops / admin alerts: monitoring, error reports, escalations. |
83
+ *
84
+ * @type {Readonly<Record<'transactional' | 'otp' | 'newsletter' | 'marketing' | 'notifications' | 'alerts', Readonly<MailTimePresetConfig>>>}
85
+ */
86
+ declare const PRESETS: Readonly<Record<"transactional" | "otp" | "newsletter" | "marketing" | "notifications" | "alerts", Readonly<MailTimePresetConfig>>>;
87
+ export {};
package/presets.js ADDED
@@ -0,0 +1,193 @@
1
+ import { deepMerge, isPlainObject, logError } from './helpers.js';
2
+
3
+ /**
4
+ * Default `onError` hook used by every built-in preset. Logs via the
5
+ * shared `logError` helper and tags the line with the MailTime instance
6
+ * `prefix` (or `'default'` when unset) so multi-queue deployments can
7
+ * tell their streams apart. Defined as a regular function so `this`
8
+ * resolves to the MailTime instance at call time — `this.onError(...)`
9
+ * in `index.js` binds the receiver. Users override by passing their own
10
+ * `onError` through `mailTimePreset(name, { onError })` or the
11
+ * constructor.
12
+ */
13
+ function defaultPresetOnError(error, email, info) {
14
+ logError(`[${this?.prefix || 'default'}] [onError]`, { error, email, info });
15
+ }
16
+
17
+ /**
18
+ * @typedef {object} MailTimePresetConfig
19
+ * @property {boolean} [concatEmails]
20
+ * @property {number} [concatDelay]
21
+ * @property {string} [concatSubject]
22
+ * @property {number} [retries]
23
+ * @property {number} [retryDelay]
24
+ * @property {number} [revolvingInterval]
25
+ * @property {number} [sendingTimeout]
26
+ * @property {'one' | 'batch'} [mode]
27
+ * @property {number} [concurrency]
28
+ * @property {(error: unknown, email: object, details?: object) => void} [onError]
29
+ * @property {object} [josk]
30
+ */
31
+
32
+ /**
33
+ * Built-in MailTime presets keyed by use-case. Each value is a partial
34
+ * MailTime constructor options object — pass it through `mailTimePreset`
35
+ * (or spread directly) and supply your own `queue` / `transports` /
36
+ * `josk.adapter` / `prefix`. Presets only set the knobs that differ from
37
+ * MailTime defaults so the rest of the constructor stays in your hands.
38
+ *
39
+ * | Preset | Shape | Best for |
40
+ * |-----------------|-------|----------|
41
+ * | `transactional` | High retries, single SMTP per instance, no concat | Receipts, password resets, account changes, welcome emails. |
42
+ * | `otp` | Few retries, fast retryDelay, snappy polling, parallel SMTPs | Sign-in codes, 2FA, verification codes — stale OTPs aren't worth resending forever. |
43
+ * | `newsletter` | `concatEmails: true` with 5-min fold window | Scheduled digests / weekly updates / "what's new" emails. |
44
+ * | `marketing` | High concurrency, moderate retries, no concat | Promotional / campaign blasts where each letter is unique. |
45
+ * | `notifications` | `concatEmails: true` with 60-s fold window | App / social activity (likes, mentions) where bursts collapse into one letter. |
46
+ * | `alerts` | Many retries, fast retryDelay, modest concurrency | Ops / admin alerts: monitoring, error reports, escalations. |
47
+ *
48
+ * @type {Readonly<Record<'transactional' | 'otp' | 'newsletter' | 'marketing' | 'notifications' | 'alerts', Readonly<MailTimePresetConfig>>>}
49
+ */
50
+ // Every preset pins `mode: 'batch'` explicitly. `'one'` would trade per-tick
51
+ // throughput for cluster-wide fairness across pods on the same `prefix`, but:
52
+ // - urgent classes (`otp`, `alerts`, `transactional`) want all due rows claimed
53
+ // immediately, not spread one-per-tick;
54
+ // - bulk classes (`newsletter`, `marketing`, `notifications`) are bursty and
55
+ // need fast drains during the send window;
56
+ // - multiple `server` pods on the same `prefix` is already an anti-pattern in
57
+ // this library (one JoSk lease per prefix), so the fairness payoff is moot.
58
+ // If you do want `'one'`, pass it via `mailTimePreset(name, { mode: 'one' })`.
59
+ const PRESETS = Object.freeze({
60
+ transactional: Object.freeze({
61
+ concatEmails: false,
62
+ retries: 30,
63
+ retryDelay: 10_000,
64
+ mode: 'batch',
65
+ concurrency: 1,
66
+ onError: defaultPresetOnError,
67
+ josk: Object.freeze({
68
+ zombieTime: 120_000,
69
+ }),
70
+ }),
71
+ otp: Object.freeze({
72
+ concatEmails: false,
73
+ retries: 5,
74
+ retryDelay: 2000,
75
+ revolvingInterval: 1024,
76
+ sendingTimeout: 60_000,
77
+ mode: 'batch',
78
+ concurrency: 4,
79
+ onError: defaultPresetOnError,
80
+ josk: Object.freeze({
81
+ minRevolvingDelay: 256,
82
+ maxRevolvingDelay: 1024,
83
+ zombieTime: 60_000,
84
+ }),
85
+ }),
86
+ newsletter: Object.freeze({
87
+ concatEmails: true,
88
+ concatDelay: 5 * 60_000,
89
+ concatSubject: 'Your updates',
90
+ retries: 5,
91
+ retryDelay: 60_000,
92
+ sendingTimeout: 600_000,
93
+ mode: 'batch',
94
+ concurrency: 2,
95
+ onError: defaultPresetOnError,
96
+ josk: Object.freeze({
97
+ zombieTime: 300_000,
98
+ }),
99
+ }),
100
+ marketing: Object.freeze({
101
+ concatEmails: false,
102
+ retries: 10,
103
+ retryDelay: 30_000,
104
+ mode: 'batch',
105
+ concurrency: 5,
106
+ onError: defaultPresetOnError,
107
+ josk: Object.freeze({
108
+ zombieTime: 180_000,
109
+ }),
110
+ }),
111
+ notifications: Object.freeze({
112
+ concatEmails: true,
113
+ concatDelay: 60_000,
114
+ concatSubject: 'New activity',
115
+ retries: 8,
116
+ retryDelay: 30_000,
117
+ mode: 'batch',
118
+ concurrency: 3,
119
+ onError: defaultPresetOnError,
120
+ josk: Object.freeze({
121
+ zombieTime: 180_000,
122
+ }),
123
+ }),
124
+ alerts: Object.freeze({
125
+ concatEmails: false,
126
+ retries: 20,
127
+ retryDelay: 5000,
128
+ revolvingInterval: 1024,
129
+ sendingTimeout: 60_000,
130
+ mode: 'batch',
131
+ concurrency: 2,
132
+ onError: defaultPresetOnError,
133
+ josk: Object.freeze({
134
+ minRevolvingDelay: 256,
135
+ maxRevolvingDelay: 1024,
136
+ zombieTime: 60_000,
137
+ }),
138
+ }),
139
+ });
140
+
141
+ /**
142
+ * Read-only map of preset name → partial MailTime config. Use
143
+ * `mailTimePreset(name, overrides)` to materialize a merged copy; use
144
+ * this object directly only when you want to introspect or compose
145
+ * presets manually.
146
+ */
147
+ const presets = PRESETS;
148
+
149
+ /**
150
+ * @typedef {keyof typeof PRESETS} MailTimePresetName
151
+ */
152
+
153
+ /**
154
+ * Names of every built-in preset.
155
+ * @type {ReadonlyArray<MailTimePresetName>}
156
+ */
157
+ const presetNames = Object.freeze(/** @type {MailTimePresetName[]} */ (Object.keys(PRESETS)));
158
+
159
+ /**
160
+ * Materialize a MailTime constructor config from a built-in preset.
161
+ * The named preset is deep-cloned (so the result is freely mutable) and
162
+ * `overrides` is deep-merged on top — overrides win for scalar keys, and
163
+ * nested objects like `josk` are merged so the preset's defaults compose
164
+ * with the caller's `adapter`, `lockOwnerId`, `onError`, etc.
165
+ *
166
+ * ```js
167
+ * import { MailTime, RedisQueue, mailTimePreset } from 'mail-time';
168
+ * const mailTime = new MailTime(mailTimePreset('otp', {
169
+ * prefix: 'otp',
170
+ * queue: new RedisQueue({ client }),
171
+ * transports: [otpTransport],
172
+ * josk: { adapter: { type: 'redis', client } },
173
+ * }));
174
+ * ```
175
+ *
176
+ * @param {MailTimePresetName} name - one of `presetNames`
177
+ * @param {object} [overrides] - additional MailTime constructor options
178
+ * @returns {object} fresh, mutable MailTime constructor options
179
+ * @throws {Error} when `name` is unknown
180
+ * @throws {TypeError} when `overrides` is provided but not a plain object
181
+ */
182
+ const mailTimePreset = (name, overrides) => {
183
+ if (typeof name !== 'string' || !Object.hasOwn(PRESETS, name)) {
184
+ throw new Error(`[mail-time] [mailTimePreset] unknown preset "${name}". Available: ${presetNames.join(', ')}`);
185
+ }
186
+ if (overrides !== void 0 && !isPlainObject(overrides)) {
187
+ throw new TypeError('[mail-time] [mailTimePreset] {overrides} must be a plain object when provided');
188
+ }
189
+ const cloned = deepMerge({}, PRESETS[name]);
190
+ return overrides ? deepMerge(cloned, overrides) : cloned;
191
+ };
192
+
193
+ export { mailTimePreset, presets, presetNames };
@@ -1,230 +0,0 @@
1
- import { logError } from '../helpers.js';
2
-
3
- /** Class representing Example Queue for MailTime */
4
- class BlankQueue {
5
- /**
6
- * Create a BlankQueue instance
7
- * @param {object} opts - configuration object
8
- * @param {object} opts.requiredOption - Required option description
9
- * @param {string} [opts.prefix] - Optional prefix for scope isolation; use when creating multiple MailTime instances within the single application
10
- */
11
- constructor (opts) {
12
- this.name = 'queue-name';
13
- if (!opts || typeof opts !== 'object' || opts === null) {
14
- throw new TypeError('[mail-time] Configuration object must be passed into BlankQueue constructor');
15
- }
16
-
17
- if (!opts.requiredOption) {
18
- throw new Error('[mail-time] [BlankQueue] required {requiredOption} option is missing');
19
- }
20
-
21
- this.prefix = (typeof opts.prefix === 'string') ? opts.prefix : 'default';
22
- this.uniqueName = `mailtime:${this.prefix}`;
23
- this.requiredOption = opts.requiredOption;
24
-
25
- // Stored JSON structure:
26
- // uuid {string}
27
- // to {string|[string]}
28
- // tries {number} - qty of send attempts
29
- // sendAt {number} - When letter should be sent
30
- // isSent {boolean} - Email status
31
- // isCancelled {boolean} - `true` if email was cancelled before it was sent
32
- // isFailed {boolean} - `true` if email has failed to send
33
- // template {string} - Template for this email
34
- // transport {number} - Last used transport
35
- // concatSubject {string|boolean} - Email concatenation subject
36
- // ---
37
- // mailOptions {[object]} - Array of nodeMailer's `mailOptions`
38
- // mailOptions.to {string|[string]} - [REQUIRED]
39
- // mailOptions.from {string}
40
- // mailOptions.text {string|boolean}
41
- // mailOptions.html {string}
42
- // mailOptions.subject {string}
43
- // mailOptions.Other nodeMailer `sendMail` options...
44
- }
45
-
46
- /**
47
- * @async
48
- * @memberOf BlankQueue
49
- * @name ping
50
- * @description Check connection to Storage
51
- * @returns {Promise<object>}
52
- */
53
- async ping() {
54
- // LEAVE THIS if BLOCK UNCHANGED
55
- if (!this.mailTimeInstance) {
56
- return {
57
- status: 'Service Unavailable',
58
- code: 503,
59
- statusCode: 503,
60
- error: new Error('MailTime instance not yet assigned to {mailTimeInstance} of Queue Adapter context'),
61
- };
62
- }
63
-
64
- try {
65
- const ping = await this.requiredOption.ping();
66
- if (ping === 'PONG') {
67
- return {
68
- status: 'OK',
69
- code: 200,
70
- statusCode: 200,
71
- };
72
- }
73
-
74
- throw new Error(`Unexpected response from Storage#ping received: ${ping}`);
75
- } catch (pingError) {
76
- return {
77
- status: 'Internal Server Error',
78
- code: 500,
79
- statusCode: 500,
80
- error: pingError
81
- };
82
- }
83
- }
84
-
85
- /**
86
- * @memberOf BlankQueue
87
- * @name iterate
88
- * @description iterate over queued emails passing to `mailTimeInstance.___send` method
89
- * @returns {void 0}
90
- */
91
- async iterate() {
92
- // GET EMAILS WITHIN this.uniqueName SCOPE!
93
- // AND ONLY WHERE sendAt <= now
94
- // RUN ONE BY ONE VIA this.mailTimeInstance.___send() METHOD
95
- const cursorWithEmails = this.requiredOption.getEmails({
96
- scope: this.uniqueName,
97
- isSent: false,
98
- isFailed: false,
99
- isCancelled: false,
100
- sendAt: {
101
- $lte: Date.now() // Number (timestamp)
102
- }
103
- });
104
-
105
- for await (const emailObj of cursorWithEmails) {
106
- await this.mailTimeInstance.___send(emailObj);
107
- }
108
- }
109
-
110
- /**
111
- * @async
112
- * @memberOf BlankQueue
113
- * @name getPendingTo
114
- * @description get queued email by `to` field (addressee)
115
- * @param to {string} - email address
116
- * @param sendAt {number} - timestamp
117
- * @returns {Promise<object|null>}
118
- */
119
- async getPendingTo(to, sendAt) {
120
- if (typeof to !== 'string' || typeof sendAt !== 'number') {
121
- return null;
122
- }
123
-
124
- const email = await this.requiredOption.get({
125
- to: to,
126
- isSent: false,
127
- isFailed: false,
128
- isCancelled: false,
129
- sendAt: {
130
- $lte: sendAt // Number (timestamp)
131
- }
132
- });
133
-
134
- if (!email || email.isSent === true || email.isCancelled === true || email.isFailed === true) {
135
- return null;
136
- }
137
-
138
- return email;
139
- }
140
-
141
- /**
142
- * @async
143
- * @memberOf BlankQueue
144
- * @name push
145
- * @description push email to the queue/storage
146
- * @param email {object} - email's object
147
- * @returns {Promise<void 0>}
148
- */
149
- async push(email) {
150
- if (typeof email !== 'object') {
151
- return;
152
- }
153
-
154
- if (email.sendAt instanceof Date) {
155
- email.sendAt = +email.sendAt;
156
- }
157
-
158
- await this.requiredOption.save(email);
159
- }
160
-
161
- /**
162
- * @async
163
- * @memberOf BlankQueue
164
- * @name cancel
165
- * @description cancel scheduled email
166
- * @param uuid {string} - email's uuid
167
- * @returns {Promise<boolean>} returns `true` if cancelled or `false` if not found, was sent, or was cancelled previously
168
- */
169
- async cancel(uuid) {
170
- if (typeof uuid !== 'string') {
171
- return false;
172
- }
173
-
174
- const email = JSON.parse(await this.requiredOption.get({
175
- uuid: uuid
176
- }));
177
-
178
- if (!email || email.isSent === true || email.isCancelled === true) {
179
- return false;
180
- }
181
-
182
- if (!this.mailTimeInstance.keepHistory) {
183
- return await this.remove(email);
184
- }
185
-
186
- return await this.update(email, {
187
- isCancelled: true,
188
- });
189
- }
190
-
191
- /**
192
- * @async
193
- * @memberOf BlankQueue
194
- * @name remove
195
- * @description remove email from queue
196
- * @param email {object} - email's object
197
- * @returns {Promise<boolean>} returns `true` if removed or `false` if not found
198
- */
199
- async remove(email) {
200
- if (typeof email !== 'object' || typeof email.uuid !== 'string') {
201
- return false;
202
- }
203
-
204
- return await this.requiredOption.remove({
205
- uuid: email.uuid
206
- });
207
- }
208
-
209
- /**
210
- * @async
211
- * @memberOf BlankQueue
212
- * @name update
213
- * @description remove email from queue
214
- * @param email {object} - email's object
215
- * @param updateObj {object} - fields with new values to update
216
- * @returns {Promise<boolean>} returns `true` if updated or `false` if not found or no changes was made
217
- */
218
- async update(email, updateObj) {
219
- if (typeof email !== 'object' || typeof email.uuid !== 'string' || typeof updateObj !== 'object') {
220
- return false;
221
- }
222
-
223
- const updatedEmail = { ...email, ...updateObj };
224
- return await this.requiredOption.update({
225
- uuid: email.uuid
226
- }, updatedEmail);
227
- }
228
- }
229
-
230
- export { BlankQueue };