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.
@@ -0,0 +1,118 @@
1
+ export type MongoCollection = {
2
+ collectionName?: string | undefined;
3
+ createIndex: (keys: object, opts?: object) => Promise<unknown>;
4
+ indexes: () => Promise<{
5
+ name: string;
6
+ key: Record<string, unknown>;
7
+ }[]>;
8
+ dropIndex: (name: string) => Promise<unknown>;
9
+ find: (query: object, opts?: object) => unknown;
10
+ findOne: (query: object, opts?: object) => Promise<object | null>;
11
+ insertOne: (doc: object) => Promise<unknown>;
12
+ deleteOne: (query: object) => Promise<{
13
+ deletedCount?: number;
14
+ }>;
15
+ updateOne: (query: object, update: object) => Promise<{
16
+ modifiedCount?: number;
17
+ }>;
18
+ };
19
+ export type Db = {
20
+ collection: (name: string) => MongoCollection;
21
+ command: (cmd: object) => Promise<{
22
+ ok?: number;
23
+ }>;
24
+ };
25
+ export type MongoQueueOption = {
26
+ db: Db;
27
+ prefix?: string | undefined;
28
+ };
29
+ /** Class representing MongoDB Queue for MailTime */
30
+ export class MongoQueue {
31
+ /**
32
+ * Create a MongoQueue instance
33
+ * @param {MongoQueueOption} opts - configuration object
34
+ */
35
+ constructor(opts: MongoQueueOption);
36
+ name: string;
37
+ db: Db;
38
+ prefix: any;
39
+ collection: MongoCollection | undefined;
40
+ /**
41
+ * @async
42
+ * @memberOf MongoQueue
43
+ * @name ready
44
+ * @description Wait until indexes are created
45
+ * @returns {Promise<void 0>}
46
+ */
47
+ ready(): Promise<void>;
48
+ /**
49
+ * @async
50
+ * @memberOf MongoQueue
51
+ * @name ping
52
+ * @description Check connection to Storage
53
+ * @returns {Promise<object>}
54
+ */
55
+ ping(): Promise<object>;
56
+ /**
57
+ * @memberOf MongoQueue
58
+ * @name iterate
59
+ * @description iterate over queued tasks passing each to `mailTimeInstance.___dispatch` (the bounded send pool)
60
+ * @param {{ limit?: number, sendingTimeout?: number }} [opts] - iteration options
61
+ * @returns {Promise<void>}
62
+ */
63
+ iterate(opts?: {
64
+ limit?: number;
65
+ sendingTimeout?: number;
66
+ }): Promise<void>;
67
+ /**
68
+ * @async
69
+ * @memberOf MongoQueue
70
+ * @name getPendingTo
71
+ * @description get queued task by `to` field (addressee)
72
+ * @param to {string} - email address
73
+ * @param sendAt {number} - timestamp
74
+ * @returns {Promise<object|null>}
75
+ */
76
+ getPendingTo(to: string, sendAt: number): Promise<object | null>;
77
+ /**
78
+ * @async
79
+ * @memberOf MongoQueue
80
+ * @name push
81
+ * @description push task to the queue/storage
82
+ * @param task {object} - task's object
83
+ * @returns {Promise<void 0>}
84
+ */
85
+ push(task: object): Promise<void>;
86
+ /**
87
+ * @async
88
+ * @memberOf MongoQueue
89
+ * @name cancel
90
+ * @description cancel scheduled email
91
+ * @param uuid {string} - email's uuid
92
+ * @returns {Promise<boolean>} returns `true` if cancelled or `false` if not found, was sent, or was cancelled previously
93
+ */
94
+ cancel(uuid: string): Promise<boolean>;
95
+ /**
96
+ * @async
97
+ * @memberOf MongoQueue
98
+ * @name remove
99
+ * @description remove task from queue
100
+ * @param task {object} - task's object
101
+ * @param {{ leaseTries: number, leaseSendingAt: number }} [opts] - lease guard: only remove if this worker still holds the lease (tries + sendingAt match, row not cancelled/failed)
102
+ * @returns {Promise<boolean>} returns `true` if removed or `false` if not found
103
+ */
104
+ remove(task: object, opts?: {
105
+ leaseTries: number;
106
+ leaseSendingAt: number;
107
+ }): Promise<boolean>;
108
+ /**
109
+ * @async
110
+ * @memberOf MongoQueue
111
+ * @name update
112
+ * @description update task in queue
113
+ * @param task {object} - task's object
114
+ * @param updateObj {object} - fields with new values to update
115
+ * @returns {Promise<boolean>} returns `true` if updated or `false` if not found or no changes was made
116
+ */
117
+ update(task: object, updateObj: object): Promise<boolean>;
118
+ }
package/adapters/mongo.js CHANGED
@@ -1,37 +1,62 @@
1
- import { logError } from '../helpers.js';
1
+ import {
2
+ debug,
3
+ logError,
4
+ isSendClaimUpdate,
5
+ isSendLeaseGuardedUpdate,
6
+ isAppendMailOptionUpdate,
7
+ stripInternalUpdateMeta,
8
+ isSendLeaseRemove,
9
+ } from '../helpers.js';
10
+
11
+ const DEFAULT_PREFIX = '';
12
+
13
+ /**
14
+ * @typedef {object} MongoCollection
15
+ * @property {string} [collectionName]
16
+ * @property {(keys: object, opts?: object) => Promise<unknown>} createIndex
17
+ * @property {() => Promise<{ name: string, key: Record<string, unknown> }[]>} indexes
18
+ * @property {(name: string) => Promise<unknown>} dropIndex
19
+ * @property {(query: object, opts?: object) => unknown} find
20
+ * @property {(query: object, opts?: object) => Promise<object|null>} findOne
21
+ * @property {(doc: object) => Promise<unknown>} insertOne
22
+ * @property {(query: object) => Promise<{ deletedCount?: number }>} deleteOne
23
+ * @property {(query: object, update: object) => Promise<{ modifiedCount?: number }>} updateOne
24
+ */
25
+
26
+ /**
27
+ * @typedef {object} Db
28
+ * @property {(name: string) => MongoCollection} collection
29
+ * @property {(cmd: object) => Promise<{ ok?: number }>} command
30
+ */
2
31
 
3
32
  /**
4
- * Ensure (create) index on MongoDB collection, catch and log exception if thrown
5
- * @function ensureIndex
6
- * @param {Collection} collection - Mongo's driver Collection instance
7
- * @param {object} keys - Field and value pairs where the field is the index key and the value describes the type of index for that field
8
- * @param {object} opts - Set of options that controls the creation of the index
9
- * @returns {void 0}
33
+ * @typedef {object} MongoQueueOption
34
+ * @property {Db} db
35
+ * @property {string} [prefix]
10
36
  */
37
+
38
+ /** @internal */
11
39
  const ensureIndex = async (collection, keys, opts) => {
12
40
  try {
13
41
  await collection.createIndex(keys, opts);
14
42
  } catch (e) {
15
- if (e.code === 85) {
43
+ if (e?.code === 85) {
16
44
  let indexName;
17
45
  const indexes = await collection.indexes();
46
+ const keyNames = Object.keys(keys);
18
47
  for (const index of indexes) {
19
- let drop = true;
20
- for (const indexKey of Object.keys(keys)) {
21
- if (typeof index.key[indexKey] === 'undefined') {
22
- drop = false;
23
- break;
24
- }
48
+ const indexKeys = Object.keys(index.key);
49
+ if (indexKeys.length !== keyNames.length) {
50
+ continue;
25
51
  }
26
-
27
- for (const indexKey of Object.keys(index.key)) {
28
- if (typeof keys[indexKey] === 'undefined') {
29
- drop = false;
52
+ let match = true;
53
+ for (const k of keyNames) {
54
+ if (typeof index.key[k] === 'undefined') {
55
+ match = false;
30
56
  break;
31
57
  }
32
58
  }
33
-
34
- if (drop) {
59
+ if (match) {
35
60
  indexName = index.name;
36
61
  break;
37
62
  }
@@ -42,7 +67,7 @@ const ensureIndex = async (collection, keys, opts) => {
42
67
  await collection.createIndex(keys, opts);
43
68
  }
44
69
  } else {
45
- logError(`[ensureIndex] Can not set ${Object.keys(keys).join(' + ')} index on "${collection._name || 'MongoDB'}" collection`, { keys, opts, details: e });
70
+ logError(`[ensureIndex] Can not set ${Object.keys(keys).join(' + ')} index on "${collection?.collectionName || 'MongoDB'}" collection`, { keys, opts, details: e });
46
71
  }
47
72
  }
48
73
  };
@@ -51,47 +76,58 @@ const ensureIndex = async (collection, keys, opts) => {
51
76
  class MongoQueue {
52
77
  /**
53
78
  * Create a MongoQueue instance
54
- * @param {object} opts - configuration object
55
- * @param {Db} opts.db - Required, Mongo's `Db` instance, like one returned from `MongoClient#db()` method
56
- * @param {string} [opts.prefix] - Optional prefix for scope isolation; use when creating multiple MailTime instances within the single application
79
+ * @param {MongoQueueOption} opts - configuration object
57
80
  */
58
81
  constructor (opts) {
59
82
  this.name = 'mongo-queue';
60
- if (!opts || typeof opts !== 'object' || opts === null) {
83
+ if (!opts || typeof opts !== 'object') {
61
84
  throw new TypeError('[mail-time] Configuration object must be passed into MongoQueue constructor');
62
85
  }
63
86
 
64
87
  if (!opts.db) {
65
- throw new Error('[mail-time] [MongoQueue] requires MongoDB database {db} option, like returned from `MongoClient.connect`');
88
+ throw new Error('[mail-time] [MongoQueue] requires MongoDB database {db} option, like returned from `MongoClient#db()`');
66
89
  }
67
90
 
68
- this.prefix = (typeof opts.prefix === 'string') ? opts.prefix : '';
69
91
  this.db = opts.db;
70
- this.collection = opts.db.collection(`__mailTimeQueue__${this.prefix}`);
71
- ensureIndex(this.collection, { uuid: 1 }, { background: false });
72
- ensureIndex(this.collection, { isSent: 1, isFailed: 1, isCancelled: 1, to: 1, sendAt: 1 }, { background: false });
73
- ensureIndex(this.collection, { isSent: 1, isFailed: 1, isCancelled: 1, sendAt: 1, tries: 1 }, { background: false });
74
-
75
- // MongoDB Collection Schema:
76
- // _id
77
- // uuid {string}
78
- // to {string|[string]}
79
- // tries {number} - qty of send attempts
80
- // sendAt {number} - When letter should be sent
81
- // isSent {boolean} - Email status
82
- // isCancelled {boolean} - `true` if email was cancelled before it was sent
83
- // isFailed {boolean} - `true` if email has failed to send
84
- // template {string} - Template for this email
85
- // transport {number} - Last used transport
86
- // concatSubject {string|boolean} - Email concatenation subject
87
- // ---
88
- // mailOptions {[object]} - Array of nodeMailer's `mailOptions`
89
- // mailOptions.to {string|[string]} - [REQUIRED]
90
- // mailOptions.from {string}
91
- // mailOptions.text {string|boolean}
92
- // mailOptions.html {string}
93
- // mailOptions.subject {string}
94
- // mailOptions.Other nodeMailer `sendMail` options...
92
+ if (typeof opts.prefix === 'string') {
93
+ this.__applyPrefix(opts.prefix);
94
+ }
95
+ }
96
+
97
+ /** @internal */
98
+ __applyPrefix(prefix) {
99
+ this.prefix = prefix;
100
+ this.collection = this.db.collection(`__mailTimeQueue__${prefix}`);
101
+ this.__readyPromise = Promise.all([
102
+ ensureIndex(this.collection, { uuid: 1 }, { background: false }),
103
+ ensureIndex(this.collection, { isSent: 1, isFailed: 1, isCancelled: 1, to: 1, sendAt: 1 }, { background: false }),
104
+ ensureIndex(this.collection, { isSent: 1, isFailed: 1, isCancelled: 1, isSending: 1, sendingAt: 1, sendAt: 1, tries: 1 }, { background: false }),
105
+ ]).then(() => void 0);
106
+ }
107
+
108
+ /** @internal */
109
+ __ensurePrefix() {
110
+ if (typeof this.prefix !== 'string') {
111
+ this.__applyPrefix(this.mailTimeInstance?.prefix || DEFAULT_PREFIX);
112
+ }
113
+ }
114
+
115
+ /** @internal */
116
+ __debug(...args) {
117
+ debug(this.mailTimeInstance?.debug === true, `[${this.name}]`, ...args);
118
+ }
119
+
120
+ /**
121
+ * @async
122
+ * @memberOf MongoQueue
123
+ * @name ready
124
+ * @description Wait until indexes are created
125
+ * @returns {Promise<void 0>}
126
+ */
127
+ async ready() {
128
+ this.__ensurePrefix();
129
+ this.__debug('[ready]');
130
+ await this.__readyPromise;
95
131
  }
96
132
 
97
133
  /**
@@ -102,6 +138,7 @@ class MongoQueue {
102
138
  * @returns {Promise<object>}
103
139
  */
104
140
  async ping() {
141
+ this.__debug('[ping]');
105
142
  if (!this.mailTimeInstance) {
106
143
  return {
107
144
  status: 'Service Unavailable',
@@ -125,7 +162,7 @@ class MongoQueue {
125
162
  status: 'Internal Server Error',
126
163
  code: 500,
127
164
  statusCode: 500,
128
- error: pingError
165
+ error: pingError,
129
166
  };
130
167
  }
131
168
 
@@ -133,28 +170,43 @@ class MongoQueue {
133
170
  status: 'Service Unavailable',
134
171
  code: 503,
135
172
  statusCode: 503,
136
- error: new Error('Service Unavailable')
173
+ error: new Error('Service Unavailable'),
137
174
  };
138
175
  }
139
176
 
140
177
  /**
141
178
  * @memberOf MongoQueue
142
179
  * @name iterate
143
- * @description iterate over queued tasks passing to `mailTimeInstance.___send` method
144
- * @returns {void 0}
180
+ * @description iterate over queued tasks passing each to `mailTimeInstance.___dispatch` (the bounded send pool)
181
+ * @param {{ limit?: number, sendingTimeout?: number }} [opts] - iteration options
182
+ * @returns {Promise<void>}
145
183
  */
146
- async iterate() {
184
+ async iterate(opts) {
185
+ this.__debug('[iterate]', opts);
186
+ this.__ensurePrefix();
187
+ const now = Date.now();
188
+ const sendingTimeout = (opts && typeof opts.sendingTimeout === 'number' && opts.sendingTimeout > 0)
189
+ ? opts.sendingTimeout
190
+ : 300000;
191
+ const limit = (opts && typeof opts.limit === 'number' && Number.isFinite(opts.limit) && opts.limit > 0)
192
+ ? Math.floor(opts.limit)
193
+ : 0;
194
+
147
195
  try {
148
196
  const cursor = this.collection.find({
149
197
  isSent: false,
150
198
  isFailed: false,
151
199
  isCancelled: false,
152
200
  sendAt: {
153
- $lte: Date.now()
201
+ $lte: now,
154
202
  },
155
203
  tries: {
156
- $lt: this.mailTimeInstance.maxTries
157
- }
204
+ $lt: this.mailTimeInstance.maxTries,
205
+ },
206
+ $or: [
207
+ { isSending: { $ne: true } },
208
+ { sendingAt: { $lte: now - sendingTimeout } },
209
+ ],
158
210
  }, {
159
211
  projection: {
160
212
  _id: 1,
@@ -165,13 +217,19 @@ class MongoQueue {
165
217
  isSent: 1,
166
218
  isFailed: 1,
167
219
  isCancelled: 1,
220
+ isSending: 1,
221
+ sendingAt: 1,
168
222
  mailOptions: 1,
169
223
  concatSubject: 1,
170
- }
224
+ },
171
225
  });
172
226
 
227
+ if (limit > 0) {
228
+ cursor.limit(limit);
229
+ }
230
+
173
231
  while (await cursor.hasNext()) {
174
- await this.mailTimeInstance.___send(await cursor.next());
232
+ await this.mailTimeInstance.___dispatch(await cursor.next());
175
233
  }
176
234
  await cursor.close();
177
235
  } catch (iterateError) {
@@ -189,18 +247,22 @@ class MongoQueue {
189
247
  * @returns {Promise<object|null>}
190
248
  */
191
249
  async getPendingTo(to, sendAt) {
250
+ this.__debug('[getPendingTo]', to, sendAt);
192
251
  if (typeof to !== 'string' || typeof sendAt !== 'number') {
193
252
  return null;
194
253
  }
254
+ this.__ensurePrefix();
195
255
 
196
256
  return await this.collection.findOne({
197
- to: to,
257
+ to,
198
258
  isSent: false,
199
259
  isFailed: false,
200
260
  isCancelled: false,
261
+ isSending: { $ne: true },
262
+ tries: { $lt: this.mailTimeInstance.maxTries },
201
263
  sendAt: {
202
- $lte: sendAt
203
- }
264
+ $lte: sendAt,
265
+ },
204
266
  }, {
205
267
  projection: {
206
268
  _id: 1,
@@ -211,7 +273,7 @@ class MongoQueue {
211
273
  isFailed: 1,
212
274
  isCancelled: 1,
213
275
  mailOptions: 1,
214
- }
276
+ },
215
277
  });
216
278
  }
217
279
 
@@ -224,9 +286,11 @@ class MongoQueue {
224
286
  * @returns {Promise<void 0>}
225
287
  */
226
288
  async push(task) {
227
- if (typeof task !== 'object') {
289
+ this.__debug('[push]', task?.uuid);
290
+ if (!task || typeof task !== 'object') {
228
291
  return;
229
292
  }
293
+ this.__ensurePrefix();
230
294
 
231
295
  if (task.sendAt instanceof Date) {
232
296
  task.sendAt = +task.sendAt;
@@ -243,9 +307,11 @@ class MongoQueue {
243
307
  * @returns {Promise<boolean>} returns `true` if cancelled or `false` if not found, was sent, or was cancelled previously
244
308
  */
245
309
  async cancel(uuid) {
310
+ this.__debug('[cancel]', uuid);
246
311
  if (typeof uuid !== 'string') {
247
312
  return false;
248
313
  }
314
+ this.__ensurePrefix();
249
315
 
250
316
  const task = await this.collection.findOne({ uuid }, {
251
317
  projection: {
@@ -253,7 +319,7 @@ class MongoQueue {
253
319
  uuid: 1,
254
320
  isSent: 1,
255
321
  isCancelled: 1,
256
- }
322
+ },
257
323
  });
258
324
 
259
325
  if (!task || task.isSent === true || task.isCancelled === true) {
@@ -275,35 +341,90 @@ class MongoQueue {
275
341
  * @name remove
276
342
  * @description remove task from queue
277
343
  * @param task {object} - task's object
344
+ * @param {{ leaseTries: number, leaseSendingAt: number }} [opts] - lease guard: only remove if this worker still holds the lease (tries + sendingAt match, row not cancelled/failed)
278
345
  * @returns {Promise<boolean>} returns `true` if removed or `false` if not found
279
346
  */
280
- async remove(task) {
281
- if (typeof task !== 'object') {
347
+ async remove(task, opts) {
348
+ this.__debug('[remove]', task?.uuid);
349
+ if (!task || typeof task !== 'object') {
282
350
  return false;
283
351
  }
352
+ this.__ensurePrefix();
353
+
354
+ const query = { _id: task._id };
355
+ if (isSendLeaseRemove(opts)) {
356
+ query.tries = opts.leaseTries;
357
+ query.isSending = true;
358
+ query.sendingAt = opts.leaseSendingAt;
359
+ query.isCancelled = false;
360
+ query.isFailed = false;
361
+ }
284
362
 
285
- return (await this.collection.deleteOne({ _id: task._id }))?.deletedCount >= 1;
363
+ const res = await this.collection.deleteOne(query);
364
+ return (res?.deletedCount || 0) >= 1;
286
365
  }
287
366
 
288
367
  /**
289
368
  * @async
290
369
  * @memberOf MongoQueue
291
370
  * @name update
292
- * @description remove task from queue
371
+ * @description update task in queue
293
372
  * @param task {object} - task's object
294
373
  * @param updateObj {object} - fields with new values to update
295
374
  * @returns {Promise<boolean>} returns `true` if updated or `false` if not found or no changes was made
296
375
  */
297
376
  async update(task, updateObj) {
298
- if (typeof task !== 'object' || typeof updateObj !== 'object') {
377
+ this.__debug('[update]', task?.uuid);
378
+ if (!task || typeof task !== 'object' || !updateObj || typeof updateObj !== 'object') {
299
379
  return false;
300
380
  }
381
+ this.__ensurePrefix();
301
382
 
302
- return (await this.collection.updateOne({
303
- _id: task._id
304
- }, {
305
- $set: updateObj
306
- }))?.modifiedCount >= 1;
383
+ if (isAppendMailOptionUpdate(updateObj)) {
384
+ const res = await this.collection.updateOne({
385
+ _id: task._id,
386
+ isSent: false,
387
+ isFailed: false,
388
+ isCancelled: false,
389
+ isSending: { $ne: true },
390
+ }, {
391
+ $push: {
392
+ mailOptions: updateObj.appendMailOption,
393
+ },
394
+ });
395
+ return (res?.modifiedCount || 0) >= 1;
396
+ }
397
+
398
+ const query = {
399
+ _id: task._id,
400
+ };
401
+
402
+ if (isSendClaimUpdate(updateObj)) {
403
+ const now = typeof updateObj.sendingAt === 'number' ? updateObj.sendingAt : Date.now();
404
+ const sendingTimeout = this.mailTimeInstance?.sendingTimeout || 300000;
405
+ query.isSent = false;
406
+ query.isFailed = false;
407
+ query.isCancelled = false;
408
+ query.tries = task.tries;
409
+ query.$or = [
410
+ { isSending: { $ne: true } },
411
+ { sendingAt: { $lte: now - sendingTimeout } },
412
+ ];
413
+ } else if (isSendLeaseGuardedUpdate(updateObj)) {
414
+ query.tries = updateObj.leaseTries;
415
+ query.isSending = true;
416
+ query.sendingAt = updateObj.leaseSendingAt;
417
+ query.isCancelled = false;
418
+ query.isFailed = false;
419
+ }
420
+
421
+ const res = await this.collection.updateOne(query, {
422
+ $set: stripInternalUpdateMeta(updateObj),
423
+ });
424
+ if (isSendClaimUpdate(updateObj)) {
425
+ return (res?.modifiedCount || res?.matchedCount || 0) >= 1;
426
+ }
427
+ return (res?.modifiedCount || 0) >= 1;
307
428
  }
308
429
  }
309
430
 
@@ -0,0 +1,100 @@
1
+ export type PostgresQueryResult = {
2
+ rowCount?: number | null | undefined;
3
+ rows?: unknown[] | undefined;
4
+ };
5
+ export type PostgresClient = {
6
+ query: (queryText: string, values?: unknown[]) => Promise<PostgresQueryResult>;
7
+ };
8
+ export type PostgresQueueOption = {
9
+ client: PostgresClient;
10
+ prefix?: string | undefined;
11
+ };
12
+ /** Class representing PostgreSQL Queue for MailTime */
13
+ export class PostgresQueue {
14
+ /**
15
+ * Create a PostgresQueue instance
16
+ * @param {PostgresQueueOption} opts - configuration object
17
+ */
18
+ constructor(opts: PostgresQueueOption);
19
+ name: string;
20
+ client: PostgresClient;
21
+ prefix: any;
22
+ /**
23
+ * @async
24
+ * @memberOf PostgresQueue
25
+ * @name ready
26
+ * @description Wait until PostgreSQL schema is ready
27
+ * @returns {Promise<void 0>}
28
+ */
29
+ ready(): Promise<void>;
30
+ /**
31
+ * @async
32
+ * @memberOf PostgresQueue
33
+ * @name ping
34
+ * @description Check connection to Storage
35
+ * @returns {Promise<object>}
36
+ */
37
+ ping(): Promise<object>;
38
+ /**
39
+ * @memberOf PostgresQueue
40
+ * @name iterate
41
+ * @description iterate over queued tasks passing each to `mailTimeInstance.___dispatch` (the bounded send pool). Postgres reads buffer the full result, so each tick is bounded by `opts.limit` (or 1000 when caller passes `Infinity` / no limit) to keep memory predictable; high-throughput deployments should shard prefixes.
42
+ * @param {{ limit?: number, sendingTimeout?: number }} [opts] - iteration options
43
+ * @returns {Promise<void>}
44
+ */
45
+ iterate(opts?: {
46
+ limit?: number;
47
+ sendingTimeout?: number;
48
+ }): Promise<void>;
49
+ /**
50
+ * @async
51
+ * @memberOf PostgresQueue
52
+ * @name getPendingTo
53
+ * @description get queued task by `to` field (addressee)
54
+ * @param to {string} - email address
55
+ * @param sendAt {number} - timestamp
56
+ * @returns {Promise<object|null>}
57
+ */
58
+ getPendingTo(to: string, sendAt: number): Promise<object | null>;
59
+ /**
60
+ * @async
61
+ * @memberOf PostgresQueue
62
+ * @name push
63
+ * @description push task to the queue/storage
64
+ * @param task {object} - task's object
65
+ * @returns {Promise<void 0>}
66
+ */
67
+ push(task: object): Promise<void>;
68
+ /**
69
+ * @async
70
+ * @memberOf PostgresQueue
71
+ * @name cancel
72
+ * @description cancel scheduled email
73
+ * @param uuid {string} - email's uuid
74
+ * @returns {Promise<boolean>} returns `true` if cancelled or `false` if not found, was sent, or was cancelled previously
75
+ */
76
+ cancel(uuid: string): Promise<boolean>;
77
+ /**
78
+ * @async
79
+ * @memberOf PostgresQueue
80
+ * @name remove
81
+ * @description remove task from queue
82
+ * @param task {object} - task's object
83
+ * @param {{ leaseTries: number, leaseSendingAt: number }} [opts] - lease guard: only remove if this worker still holds the lease (tries + sendingAt match, row not cancelled/failed)
84
+ * @returns {Promise<boolean>} returns `true` if removed or `false` if not found
85
+ */
86
+ remove(task: object, opts?: {
87
+ leaseTries: number;
88
+ leaseSendingAt: number;
89
+ }): Promise<boolean>;
90
+ /**
91
+ * @async
92
+ * @memberOf PostgresQueue
93
+ * @name update
94
+ * @description update task in queue
95
+ * @param task {object} - task's object
96
+ * @param updateObj {object} - fields with new values to update
97
+ * @returns {Promise<boolean>} returns `true` if updated or `false` if not found or no changes was made
98
+ */
99
+ update(task: object, updateObj: object): Promise<boolean>;
100
+ }