mail-time 5.2.0 → 5.3.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 +22 -11
- package/adapters/mongo.js +1 -1
- package/adapters/postgres.js +3 -0
- package/adapters/redis.js +6 -0
- package/index.cjs +556 -141
- package/index.d.cts +21 -77
- package/index.d.ts +21 -77
- package/index.js +342 -99
- package/package.json +5 -4
- package/presets.js +15 -15
- package/recipient-policy-lease.js +40 -8
- package/recipient-policy.js +151 -20
package/index.d.cts
CHANGED
|
@@ -199,6 +199,18 @@ export type MailTimeFromDetails = {
|
|
|
199
199
|
index: number;
|
|
200
200
|
from: string | undefined;
|
|
201
201
|
};
|
|
202
|
+
export type MailTimeDrainResult = {
|
|
203
|
+
failedWrites: number;
|
|
204
|
+
};
|
|
205
|
+
/**
|
|
206
|
+
* `details` passed to `onError`. `phase` is `'verify'` (transport verification, `email === null`), `'complete'` (a storage write recording a send outcome), or `'checkpoint'` (recipient-policy results written after SMTP). Without `phase` it is the SMTP `info` of the failed attempt.
|
|
207
|
+
*/
|
|
208
|
+
export type MailTimeErrorDetails = {
|
|
209
|
+
phase?: "verify" | "complete" | "checkpoint";
|
|
210
|
+
transportIndex?: number;
|
|
211
|
+
attempt?: number;
|
|
212
|
+
[key: string]: unknown;
|
|
213
|
+
};
|
|
202
214
|
export type MailTimeOptions = {
|
|
203
215
|
queue: RedisQueue | MongoQueue | PostgresQueue | CustomQueue;
|
|
204
216
|
type?: "server" | "client";
|
|
@@ -226,86 +238,17 @@ export type MailTimeOptions = {
|
|
|
226
238
|
strictPayload?: boolean;
|
|
227
239
|
allowedMailFields?: string[];
|
|
228
240
|
verifyTransports?: boolean;
|
|
241
|
+
verifyTimeout?: number;
|
|
229
242
|
template?: string;
|
|
230
243
|
prefix?: string;
|
|
231
244
|
debug?: boolean;
|
|
232
245
|
josk?: MailTimeJoSkOptions;
|
|
233
246
|
recipientPolicies?: MailTimeRecipientPolicy[];
|
|
234
|
-
onError?: (error: unknown, email: MailTimeTask | null, details?:
|
|
247
|
+
onError?: (error: unknown, email: MailTimeTask | null, details?: MailTimeErrorDetails, recipients?: MailTimeRecipientResult[], summary?: MailTimeRecipientSummary) => void | Promise<void>;
|
|
235
248
|
onSent?: (email: MailTimeTask, details?: object, recipients?: MailTimeRecipientResult[], summary?: MailTimeRecipientSummary) => void | Promise<void>;
|
|
236
249
|
onSuppressed?: (email: MailTimeTask, recipients: MailTimeRecipientResult[], summary: MailTimeRecipientSummary) => void | Promise<void>;
|
|
237
250
|
onRejected?: (email: MailTimeTask, recipients: MailTimeRecipientResult[], summary: MailTimeRecipientSummary) => void | Promise<void>;
|
|
238
251
|
};
|
|
239
|
-
/**
|
|
240
|
-
* @typedef {import('./presets.js').MailTimePresetName} MailTimePresetName
|
|
241
|
-
*/
|
|
242
|
-
/**
|
|
243
|
-
* @typedef {import('./presets.js').MailTimePresetConfig} MailTimePresetConfig
|
|
244
|
-
*/
|
|
245
|
-
/**
|
|
246
|
-
* @typedef {{ status: string, code: number, statusCode: number, paused?: boolean, error?: unknown }} MailTimePingResult
|
|
247
|
-
*/
|
|
248
|
-
/**
|
|
249
|
-
* @typedef {{ [key: string]: any, query?: (queryText: string, values?: unknown[]) => Promise<{ rows?: unknown[], rowCount?: number | null }> }} MailTimeStorageClient
|
|
250
|
-
*/
|
|
251
|
-
/**
|
|
252
|
-
* @typedef {{ [key: string]: any }} MailTimeMongoDb
|
|
253
|
-
*/
|
|
254
|
-
/**
|
|
255
|
-
* @typedef {{ [key: string]: any }} MailTimeTransport
|
|
256
|
-
*/
|
|
257
|
-
/**
|
|
258
|
-
* @typedef {{ [key: string]: any, type?: 'mongo' | 'redis' | 'postgres', client?: MailTimeStorageClient, db?: MailTimeMongoDb, prefix?: string, resetOnInit?: boolean, useHashTags?: boolean }} MailTimeJoSkAdapterOptions
|
|
259
|
-
*/
|
|
260
|
-
/**
|
|
261
|
-
* @typedef {{ [key: string]: any, adapter: MailTimeJoSkAdapterOptions | object, debug?: boolean, autoClear?: boolean, zombieTime?: number, lockLeaseTime?: number, minRevolvingDelay?: number, maxRevolvingDelay?: number, execute?: 'batch' | 'one', concurrency?: number, lockOwnerId?: string, resetOnInit?: boolean, onError?: (title: string, details: object) => void, onExecuted?: (uid: string, details: object) => void }} MailTimeJoSkOptions
|
|
262
|
-
*/
|
|
263
|
-
/**
|
|
264
|
-
* @typedef {{ [key: string]: any, ping: () => Promise<MailTimePingResult>, setInterval: (func: (...args: any[]) => unknown, delay: number, uid: string) => Promise<string>, destroy: () => boolean, shutdown: (opts?: { timeout?: number }) => Promise<boolean>, pause: (timerId?: string) => boolean, resume: (timerId?: string) => boolean }} MailTimeScheduler
|
|
265
|
-
*/
|
|
266
|
-
/**
|
|
267
|
-
* @typedef {string | { address: string, name?: string }} MailTimeMailbox
|
|
268
|
-
*/
|
|
269
|
-
/**
|
|
270
|
-
* @typedef {{ address: string, sources: Array<'envelope' | 'to' | 'cc' | 'bcc'> }} MailTimePolicyRecipient
|
|
271
|
-
* @typedef {{ address: string, status: 'suppressed' | 'rejected' | 'retry', reason: string }} MailTimePolicyDecision
|
|
272
|
-
* @typedef {{ decisions?: MailTimePolicyDecision[] }} MailTimePolicyResult
|
|
273
|
-
* @typedef {{ index: number, name?: string }} MailTimePolicyTransport
|
|
274
|
-
* @typedef {{ from?: string, to: string[] }} MailTimePolicyEnvelope
|
|
275
|
-
* @typedef {{ task: MailTimeTask, attempt: number, transport: MailTimePolicyTransport, envelope: MailTimePolicyEnvelope, recipients: MailTimePolicyRecipient[] }} MailTimePolicyContext
|
|
276
|
-
* @typedef {MailTimePolicyContext} MailTimeBeforeSendPolicyContext
|
|
277
|
-
* @typedef {{ address: string | null, command?: string, responseCode?: number, response?: string, message?: string, transportIndex: number, transportName?: string }} MailTimeStructuredRejection
|
|
278
|
-
* @typedef {MailTimePolicyContext & { error?: unknown, info?: unknown, rejections: MailTimeStructuredRejection[] }} MailTimeRejectionPolicyContext
|
|
279
|
-
* @typedef {{ address: string, status: 'sent' | 'error' | 'suppressed' | 'rejected', sources?: Array<'envelope' | 'to' | 'cc' | 'bcc'>, reasons: Array<{ provider: string, reason: string }>, attempt: number, transportIndex?: number, transportName?: string, command?: string, responseCode?: number, response?: string, message?: string }} MailTimeRecipientResult
|
|
280
|
-
* @typedef {MailTimeRejectionPolicyContext & { decisions: MailTimeRecipientResult[] }} MailTimeRecipientAttemptContext
|
|
281
|
-
* @typedef {{ uuid: string, tries: number, isSettled: boolean, recipients: { sent: MailTimeRecipientResult[], error: MailTimeRecipientResult[], suppressed: MailTimeRecipientResult[], rejected: MailTimeRecipientResult[] } }} MailTimeRecipientSummary
|
|
282
|
-
* @typedef {{ name: string, failureMode?: 'retry' | 'continue', beforeSend?: (context: MailTimeBeforeSendPolicyContext) => void | MailTimePolicyResult | Promise<void | MailTimePolicyResult>, classifyRejections?: (context: MailTimeRejectionPolicyContext) => void | MailTimePolicyResult | Promise<void | MailTimePolicyResult>, observeAttempt?: (context: MailTimeRecipientAttemptContext) => void | Promise<void> }} MailTimeRecipientPolicy
|
|
283
|
-
* @description Policy contexts are read-only by contract. Providers must bound their own I/O; renewal budgets do not limit hook duration.
|
|
284
|
-
*/
|
|
285
|
-
/**
|
|
286
|
-
* @typedef {{ uuid: string, to?: MailTimeMailbox | MailTimeMailbox[], tries: number, sendAt: number, isSent: boolean, isSettled?: boolean, recipientResults?: MailTimeRecipientResult[], isCancelled: boolean, isFailed: boolean, isSending?: boolean, sendingAt?: number, template?: string | false, transport: number, concatSubject?: string | false, mailOptions: MailTimeMailOptions[] }} MailTimeTask
|
|
287
|
-
*/
|
|
288
|
-
/**
|
|
289
|
-
* @typedef {{ limit?: number, sendingTimeout?: number }} MailTimeIterateOptions
|
|
290
|
-
*/
|
|
291
|
-
/**
|
|
292
|
-
* @typedef {{ ping: () => Promise<MailTimePingResult>, iterate: (opts?: MailTimeIterateOptions) => Promise<void> | void, getPendingTo: (to: string, sendAt: number) => Promise<MailTimeTask | object | null>, push: (email: MailTimeTask) => Promise<void> | void, cancel: (uuid: string) => Promise<boolean>, remove: (email: MailTimeTask | object, opts?: { leaseTries: number, leaseSendingAt: number }) => Promise<boolean>, update: (email: MailTimeTask | object, updateObj: object) => Promise<boolean>, ready?: () => Promise<void>, supportsRecipientPolicies?: boolean }} CustomQueue
|
|
293
|
-
*/
|
|
294
|
-
/**
|
|
295
|
-
* @typedef {{ address: string, error: string }} MailTimeRejectedRecipient
|
|
296
|
-
*/
|
|
297
|
-
/**
|
|
298
|
-
* @typedef {{ [key: string]: any, to: MailTimeMailbox | MailTimeMailbox[], cc?: MailTimeMailbox | MailTimeMailbox[], bcc?: MailTimeMailbox | MailTimeMailbox[], sendAt?: Date | number, template?: string, concatSubject?: string, text?: string | false, html?: string | false, subject?: string, accepted?: string[], rejected?: MailTimeRejectedRecipient[] }} MailTimeMailOptions
|
|
299
|
-
*/
|
|
300
|
-
/**
|
|
301
|
-
* @typedef {{ subject?: string }} MailTimeConcatEmailsOptions
|
|
302
|
-
*/
|
|
303
|
-
/**
|
|
304
|
-
* @typedef {{ index: number, from: string | undefined }} MailTimeFromDetails
|
|
305
|
-
*/
|
|
306
|
-
/**
|
|
307
|
-
* @typedef {{ queue: RedisQueue | MongoQueue | PostgresQueue | CustomQueue, type?: 'server' | 'client', from?: string | ((transport: MailTimeTransport, details: MailTimeFromDetails) => string), transports?: MailTimeTransport[], strategy?: 'backup' | 'balancer', failsToNext?: number, shouldFailOver?: (error: unknown, info: object | undefined, email: MailTimeTask) => boolean, retries?: number, maxTries?: number, retryDelay?: number, interval?: number, keepHistory?: boolean, concatEmails?: boolean | MailTimeConcatEmailsOptions, concatSubject?: string, concatDelimiter?: string, concatDelay?: number, concatThrottling?: number, revolvingInterval?: number, mode?: 'one' | 'batch', concurrency?: number, sendingTimeout?: number, renewClaim?: boolean | number, maxRenewals?: number, strictPayload?: boolean, allowedMailFields?: string[], verifyTransports?: boolean, template?: string, prefix?: string, debug?: boolean, josk?: MailTimeJoSkOptions, recipientPolicies?: MailTimeRecipientPolicy[], onError?: (error: unknown, email: MailTimeTask | null, details?: object, recipients?: MailTimeRecipientResult[], summary?: MailTimeRecipientSummary) => void | Promise<void>, onSent?: (email: MailTimeTask, details?: object, recipients?: MailTimeRecipientResult[], summary?: MailTimeRecipientSummary) => void | Promise<void>, onSuppressed?: (email: MailTimeTask, recipients: MailTimeRecipientResult[], summary: MailTimeRecipientSummary) => void | Promise<void>, onRejected?: (email: MailTimeTask, recipients: MailTimeRecipientResult[], summary: MailTimeRecipientSummary) => void | Promise<void> }} MailTimeOptions
|
|
308
|
-
*/
|
|
309
252
|
/**
|
|
310
253
|
* Class of MailTime.
|
|
311
254
|
* With recipientPolicies, terminal callbacks receive grouped recipients and a complete summary after persistence.
|
|
@@ -343,7 +286,7 @@ export class MailTime {
|
|
|
343
286
|
template: string;
|
|
344
287
|
keepHistory: boolean;
|
|
345
288
|
onSent: (email: MailTimeTask, details?: object, recipients?: MailTimeRecipientResult[], summary?: MailTimeRecipientSummary) => void | Promise<void>;
|
|
346
|
-
onError: (error: unknown, email: MailTimeTask | null, details?:
|
|
289
|
+
onError: (error: unknown, email: MailTimeTask | null, details?: MailTimeErrorDetails, recipients?: MailTimeRecipientResult[], summary?: MailTimeRecipientSummary) => void | Promise<void>;
|
|
347
290
|
onSuppressed: (email: MailTimeTask, recipients: MailTimeRecipientResult[], summary: MailTimeRecipientSummary) => void | Promise<void>;
|
|
348
291
|
onRejected: (email: MailTimeTask, recipients: MailTimeRecipientResult[], summary: MailTimeRecipientSummary) => void | Promise<void>;
|
|
349
292
|
revolvingInterval: number;
|
|
@@ -360,6 +303,7 @@ export class MailTime {
|
|
|
360
303
|
transports: MailTimeTransport[];
|
|
361
304
|
transport: number;
|
|
362
305
|
verifyTransports: boolean;
|
|
306
|
+
verifyTimeout: number;
|
|
363
307
|
from: boolean | ((transport: MailTimeTransport, details: MailTimeFromDetails) => string);
|
|
364
308
|
/** @type {string} */
|
|
365
309
|
concatSubject: string;
|
|
@@ -404,7 +348,7 @@ export class MailTime {
|
|
|
404
348
|
/**
|
|
405
349
|
* @memberOf MailTime
|
|
406
350
|
* @name destroy
|
|
407
|
-
* @description Stop the scheduler and block future dispatches. Without `{ drain: true }`, in-flight SMTP attempts are neutralized and their claims recover after `sendingTimeout`. With `{ drain: true }`, await JoSk shutdown and in-flight SMTP; resolves false if
|
|
351
|
+
* @description Stop the scheduler and block future dispatches. Sends still waiting for a `concurrency` slot are dropped at once; their rows stay unclaimed for the next scan. Without `{ drain: true }`, in-flight SMTP attempts are neutralized and their claims recover after `sendingTimeout`. With `{ drain: true }`, await JoSk shutdown and in-flight SMTP; resolves false if the queue scan exceeds `schedulerTimeout` (default 10000ms) or JoSk shutdown throws (logged); never rejects. In-flight SMTP does not count against the timeout, and the timeout does not bound SMTP drain time.
|
|
408
352
|
* @param {{ drain?: boolean, schedulerTimeout?: number }} [opts] - schedulerTimeout must be finite and non-negative; used only with drain
|
|
409
353
|
* @returns {boolean | Promise<boolean>}
|
|
410
354
|
*/
|
|
@@ -416,14 +360,14 @@ export class MailTime {
|
|
|
416
360
|
* @async
|
|
417
361
|
* @memberOf MailTime
|
|
418
362
|
* @name drain
|
|
419
|
-
* @description Wait for all in-flight email send attempts to settle
|
|
420
|
-
* @returns {Promise<
|
|
363
|
+
* @description Wait for all in-flight email send attempts to settle. Resolves with `{ failedWrites }`, the count since this instance was created of failures while recording a send outcome: a storage write that threw, an exception in the completion path, or an outcome write lost after a claim-renewal error (message `outcome write lost (renewal outcome uncertain or lease taken over)`, after one retry with the renewal's stamp (no retry when the claim was already stale at renewal time)). Each is also reported once through `onError` with `details.phase` of `'complete'` or `'checkpoint'`, except after a plain `destroy()`, which still counts but suppresses the hook and the log. A row whose write failed can stay `sending` and be re-sent after `sendingTimeout`. Compare `failedWrites` before and after to scope a shutdown.
|
|
364
|
+
* @returns {Promise<MailTimeDrainResult>}
|
|
421
365
|
*/
|
|
422
|
-
drain(): Promise<
|
|
366
|
+
drain(): Promise<MailTimeDrainResult>;
|
|
423
367
|
/**
|
|
424
368
|
* @memberOf MailTime
|
|
425
369
|
* @name pause
|
|
426
|
-
* @description Pause this server instance from competing for the queue-drain lease. In-flight SMTP sends finish; peer server instances keep draining. Reversible (unlike `destroy()`). No-op on `client` instances or after `destroy()`. To stop scanning *and* wait for in-flight sends: `mailTime.pause(); await mailTime.drain();`.
|
|
370
|
+
* @description Pause this server instance from competing for the queue-drain lease. In-flight SMTP sends finish; sends still waiting for a `concurrency` slot are dropped and their rows stay unclaimed; peer server instances keep draining. Reversible (unlike `destroy()`). No-op on `client` instances or after `destroy()`. To stop scanning *and* wait for in-flight sends: `mailTime.pause(); await mailTime.drain();`.
|
|
427
371
|
* @returns {boolean} `true` if newly paused; `false` if already paused, a client instance, or destroyed
|
|
428
372
|
*/
|
|
429
373
|
pause(): boolean;
|
package/index.d.ts
CHANGED
|
@@ -199,6 +199,18 @@ export type MailTimeFromDetails = {
|
|
|
199
199
|
index: number;
|
|
200
200
|
from: string | undefined;
|
|
201
201
|
};
|
|
202
|
+
export type MailTimeDrainResult = {
|
|
203
|
+
failedWrites: number;
|
|
204
|
+
};
|
|
205
|
+
/**
|
|
206
|
+
* `details` passed to `onError`. `phase` is `'verify'` (transport verification, `email === null`), `'complete'` (a storage write recording a send outcome), or `'checkpoint'` (recipient-policy results written after SMTP). Without `phase` it is the SMTP `info` of the failed attempt.
|
|
207
|
+
*/
|
|
208
|
+
export type MailTimeErrorDetails = {
|
|
209
|
+
phase?: "verify" | "complete" | "checkpoint";
|
|
210
|
+
transportIndex?: number;
|
|
211
|
+
attempt?: number;
|
|
212
|
+
[key: string]: unknown;
|
|
213
|
+
};
|
|
202
214
|
export type MailTimeOptions = {
|
|
203
215
|
queue: RedisQueue | MongoQueue | PostgresQueue | CustomQueue;
|
|
204
216
|
type?: "server" | "client";
|
|
@@ -226,86 +238,17 @@ export type MailTimeOptions = {
|
|
|
226
238
|
strictPayload?: boolean;
|
|
227
239
|
allowedMailFields?: string[];
|
|
228
240
|
verifyTransports?: boolean;
|
|
241
|
+
verifyTimeout?: number;
|
|
229
242
|
template?: string;
|
|
230
243
|
prefix?: string;
|
|
231
244
|
debug?: boolean;
|
|
232
245
|
josk?: MailTimeJoSkOptions;
|
|
233
246
|
recipientPolicies?: MailTimeRecipientPolicy[];
|
|
234
|
-
onError?: (error: unknown, email: MailTimeTask | null, details?:
|
|
247
|
+
onError?: (error: unknown, email: MailTimeTask | null, details?: MailTimeErrorDetails, recipients?: MailTimeRecipientResult[], summary?: MailTimeRecipientSummary) => void | Promise<void>;
|
|
235
248
|
onSent?: (email: MailTimeTask, details?: object, recipients?: MailTimeRecipientResult[], summary?: MailTimeRecipientSummary) => void | Promise<void>;
|
|
236
249
|
onSuppressed?: (email: MailTimeTask, recipients: MailTimeRecipientResult[], summary: MailTimeRecipientSummary) => void | Promise<void>;
|
|
237
250
|
onRejected?: (email: MailTimeTask, recipients: MailTimeRecipientResult[], summary: MailTimeRecipientSummary) => void | Promise<void>;
|
|
238
251
|
};
|
|
239
|
-
/**
|
|
240
|
-
* @typedef {import('./presets.js').MailTimePresetName} MailTimePresetName
|
|
241
|
-
*/
|
|
242
|
-
/**
|
|
243
|
-
* @typedef {import('./presets.js').MailTimePresetConfig} MailTimePresetConfig
|
|
244
|
-
*/
|
|
245
|
-
/**
|
|
246
|
-
* @typedef {{ status: string, code: number, statusCode: number, paused?: boolean, error?: unknown }} MailTimePingResult
|
|
247
|
-
*/
|
|
248
|
-
/**
|
|
249
|
-
* @typedef {{ [key: string]: any, query?: (queryText: string, values?: unknown[]) => Promise<{ rows?: unknown[], rowCount?: number | null }> }} MailTimeStorageClient
|
|
250
|
-
*/
|
|
251
|
-
/**
|
|
252
|
-
* @typedef {{ [key: string]: any }} MailTimeMongoDb
|
|
253
|
-
*/
|
|
254
|
-
/**
|
|
255
|
-
* @typedef {{ [key: string]: any }} MailTimeTransport
|
|
256
|
-
*/
|
|
257
|
-
/**
|
|
258
|
-
* @typedef {{ [key: string]: any, type?: 'mongo' | 'redis' | 'postgres', client?: MailTimeStorageClient, db?: MailTimeMongoDb, prefix?: string, resetOnInit?: boolean, useHashTags?: boolean }} MailTimeJoSkAdapterOptions
|
|
259
|
-
*/
|
|
260
|
-
/**
|
|
261
|
-
* @typedef {{ [key: string]: any, adapter: MailTimeJoSkAdapterOptions | object, debug?: boolean, autoClear?: boolean, zombieTime?: number, lockLeaseTime?: number, minRevolvingDelay?: number, maxRevolvingDelay?: number, execute?: 'batch' | 'one', concurrency?: number, lockOwnerId?: string, resetOnInit?: boolean, onError?: (title: string, details: object) => void, onExecuted?: (uid: string, details: object) => void }} MailTimeJoSkOptions
|
|
262
|
-
*/
|
|
263
|
-
/**
|
|
264
|
-
* @typedef {{ [key: string]: any, ping: () => Promise<MailTimePingResult>, setInterval: (func: (...args: any[]) => unknown, delay: number, uid: string) => Promise<string>, destroy: () => boolean, shutdown: (opts?: { timeout?: number }) => Promise<boolean>, pause: (timerId?: string) => boolean, resume: (timerId?: string) => boolean }} MailTimeScheduler
|
|
265
|
-
*/
|
|
266
|
-
/**
|
|
267
|
-
* @typedef {string | { address: string, name?: string }} MailTimeMailbox
|
|
268
|
-
*/
|
|
269
|
-
/**
|
|
270
|
-
* @typedef {{ address: string, sources: Array<'envelope' | 'to' | 'cc' | 'bcc'> }} MailTimePolicyRecipient
|
|
271
|
-
* @typedef {{ address: string, status: 'suppressed' | 'rejected' | 'retry', reason: string }} MailTimePolicyDecision
|
|
272
|
-
* @typedef {{ decisions?: MailTimePolicyDecision[] }} MailTimePolicyResult
|
|
273
|
-
* @typedef {{ index: number, name?: string }} MailTimePolicyTransport
|
|
274
|
-
* @typedef {{ from?: string, to: string[] }} MailTimePolicyEnvelope
|
|
275
|
-
* @typedef {{ task: MailTimeTask, attempt: number, transport: MailTimePolicyTransport, envelope: MailTimePolicyEnvelope, recipients: MailTimePolicyRecipient[] }} MailTimePolicyContext
|
|
276
|
-
* @typedef {MailTimePolicyContext} MailTimeBeforeSendPolicyContext
|
|
277
|
-
* @typedef {{ address: string | null, command?: string, responseCode?: number, response?: string, message?: string, transportIndex: number, transportName?: string }} MailTimeStructuredRejection
|
|
278
|
-
* @typedef {MailTimePolicyContext & { error?: unknown, info?: unknown, rejections: MailTimeStructuredRejection[] }} MailTimeRejectionPolicyContext
|
|
279
|
-
* @typedef {{ address: string, status: 'sent' | 'error' | 'suppressed' | 'rejected', sources?: Array<'envelope' | 'to' | 'cc' | 'bcc'>, reasons: Array<{ provider: string, reason: string }>, attempt: number, transportIndex?: number, transportName?: string, command?: string, responseCode?: number, response?: string, message?: string }} MailTimeRecipientResult
|
|
280
|
-
* @typedef {MailTimeRejectionPolicyContext & { decisions: MailTimeRecipientResult[] }} MailTimeRecipientAttemptContext
|
|
281
|
-
* @typedef {{ uuid: string, tries: number, isSettled: boolean, recipients: { sent: MailTimeRecipientResult[], error: MailTimeRecipientResult[], suppressed: MailTimeRecipientResult[], rejected: MailTimeRecipientResult[] } }} MailTimeRecipientSummary
|
|
282
|
-
* @typedef {{ name: string, failureMode?: 'retry' | 'continue', beforeSend?: (context: MailTimeBeforeSendPolicyContext) => void | MailTimePolicyResult | Promise<void | MailTimePolicyResult>, classifyRejections?: (context: MailTimeRejectionPolicyContext) => void | MailTimePolicyResult | Promise<void | MailTimePolicyResult>, observeAttempt?: (context: MailTimeRecipientAttemptContext) => void | Promise<void> }} MailTimeRecipientPolicy
|
|
283
|
-
* @description Policy contexts are read-only by contract. Providers must bound their own I/O; renewal budgets do not limit hook duration.
|
|
284
|
-
*/
|
|
285
|
-
/**
|
|
286
|
-
* @typedef {{ uuid: string, to?: MailTimeMailbox | MailTimeMailbox[], tries: number, sendAt: number, isSent: boolean, isSettled?: boolean, recipientResults?: MailTimeRecipientResult[], isCancelled: boolean, isFailed: boolean, isSending?: boolean, sendingAt?: number, template?: string | false, transport: number, concatSubject?: string | false, mailOptions: MailTimeMailOptions[] }} MailTimeTask
|
|
287
|
-
*/
|
|
288
|
-
/**
|
|
289
|
-
* @typedef {{ limit?: number, sendingTimeout?: number }} MailTimeIterateOptions
|
|
290
|
-
*/
|
|
291
|
-
/**
|
|
292
|
-
* @typedef {{ ping: () => Promise<MailTimePingResult>, iterate: (opts?: MailTimeIterateOptions) => Promise<void> | void, getPendingTo: (to: string, sendAt: number) => Promise<MailTimeTask | object | null>, push: (email: MailTimeTask) => Promise<void> | void, cancel: (uuid: string) => Promise<boolean>, remove: (email: MailTimeTask | object, opts?: { leaseTries: number, leaseSendingAt: number }) => Promise<boolean>, update: (email: MailTimeTask | object, updateObj: object) => Promise<boolean>, ready?: () => Promise<void>, supportsRecipientPolicies?: boolean }} CustomQueue
|
|
293
|
-
*/
|
|
294
|
-
/**
|
|
295
|
-
* @typedef {{ address: string, error: string }} MailTimeRejectedRecipient
|
|
296
|
-
*/
|
|
297
|
-
/**
|
|
298
|
-
* @typedef {{ [key: string]: any, to: MailTimeMailbox | MailTimeMailbox[], cc?: MailTimeMailbox | MailTimeMailbox[], bcc?: MailTimeMailbox | MailTimeMailbox[], sendAt?: Date | number, template?: string, concatSubject?: string, text?: string | false, html?: string | false, subject?: string, accepted?: string[], rejected?: MailTimeRejectedRecipient[] }} MailTimeMailOptions
|
|
299
|
-
*/
|
|
300
|
-
/**
|
|
301
|
-
* @typedef {{ subject?: string }} MailTimeConcatEmailsOptions
|
|
302
|
-
*/
|
|
303
|
-
/**
|
|
304
|
-
* @typedef {{ index: number, from: string | undefined }} MailTimeFromDetails
|
|
305
|
-
*/
|
|
306
|
-
/**
|
|
307
|
-
* @typedef {{ queue: RedisQueue | MongoQueue | PostgresQueue | CustomQueue, type?: 'server' | 'client', from?: string | ((transport: MailTimeTransport, details: MailTimeFromDetails) => string), transports?: MailTimeTransport[], strategy?: 'backup' | 'balancer', failsToNext?: number, shouldFailOver?: (error: unknown, info: object | undefined, email: MailTimeTask) => boolean, retries?: number, maxTries?: number, retryDelay?: number, interval?: number, keepHistory?: boolean, concatEmails?: boolean | MailTimeConcatEmailsOptions, concatSubject?: string, concatDelimiter?: string, concatDelay?: number, concatThrottling?: number, revolvingInterval?: number, mode?: 'one' | 'batch', concurrency?: number, sendingTimeout?: number, renewClaim?: boolean | number, maxRenewals?: number, strictPayload?: boolean, allowedMailFields?: string[], verifyTransports?: boolean, template?: string, prefix?: string, debug?: boolean, josk?: MailTimeJoSkOptions, recipientPolicies?: MailTimeRecipientPolicy[], onError?: (error: unknown, email: MailTimeTask | null, details?: object, recipients?: MailTimeRecipientResult[], summary?: MailTimeRecipientSummary) => void | Promise<void>, onSent?: (email: MailTimeTask, details?: object, recipients?: MailTimeRecipientResult[], summary?: MailTimeRecipientSummary) => void | Promise<void>, onSuppressed?: (email: MailTimeTask, recipients: MailTimeRecipientResult[], summary: MailTimeRecipientSummary) => void | Promise<void>, onRejected?: (email: MailTimeTask, recipients: MailTimeRecipientResult[], summary: MailTimeRecipientSummary) => void | Promise<void> }} MailTimeOptions
|
|
308
|
-
*/
|
|
309
252
|
/**
|
|
310
253
|
* Class of MailTime.
|
|
311
254
|
* With recipientPolicies, terminal callbacks receive grouped recipients and a complete summary after persistence.
|
|
@@ -343,7 +286,7 @@ export class MailTime {
|
|
|
343
286
|
template: string;
|
|
344
287
|
keepHistory: boolean;
|
|
345
288
|
onSent: (email: MailTimeTask, details?: object, recipients?: MailTimeRecipientResult[], summary?: MailTimeRecipientSummary) => void | Promise<void>;
|
|
346
|
-
onError: (error: unknown, email: MailTimeTask | null, details?:
|
|
289
|
+
onError: (error: unknown, email: MailTimeTask | null, details?: MailTimeErrorDetails, recipients?: MailTimeRecipientResult[], summary?: MailTimeRecipientSummary) => void | Promise<void>;
|
|
347
290
|
onSuppressed: (email: MailTimeTask, recipients: MailTimeRecipientResult[], summary: MailTimeRecipientSummary) => void | Promise<void>;
|
|
348
291
|
onRejected: (email: MailTimeTask, recipients: MailTimeRecipientResult[], summary: MailTimeRecipientSummary) => void | Promise<void>;
|
|
349
292
|
revolvingInterval: number;
|
|
@@ -360,6 +303,7 @@ export class MailTime {
|
|
|
360
303
|
transports: MailTimeTransport[];
|
|
361
304
|
transport: number;
|
|
362
305
|
verifyTransports: boolean;
|
|
306
|
+
verifyTimeout: number;
|
|
363
307
|
from: boolean | ((transport: MailTimeTransport, details: MailTimeFromDetails) => string);
|
|
364
308
|
/** @type {string} */
|
|
365
309
|
concatSubject: string;
|
|
@@ -404,7 +348,7 @@ export class MailTime {
|
|
|
404
348
|
/**
|
|
405
349
|
* @memberOf MailTime
|
|
406
350
|
* @name destroy
|
|
407
|
-
* @description Stop the scheduler and block future dispatches. Without `{ drain: true }`, in-flight SMTP attempts are neutralized and their claims recover after `sendingTimeout`. With `{ drain: true }`, await JoSk shutdown and in-flight SMTP; resolves false if
|
|
351
|
+
* @description Stop the scheduler and block future dispatches. Sends still waiting for a `concurrency` slot are dropped at once; their rows stay unclaimed for the next scan. Without `{ drain: true }`, in-flight SMTP attempts are neutralized and their claims recover after `sendingTimeout`. With `{ drain: true }`, await JoSk shutdown and in-flight SMTP; resolves false if the queue scan exceeds `schedulerTimeout` (default 10000ms) or JoSk shutdown throws (logged); never rejects. In-flight SMTP does not count against the timeout, and the timeout does not bound SMTP drain time.
|
|
408
352
|
* @param {{ drain?: boolean, schedulerTimeout?: number }} [opts] - schedulerTimeout must be finite and non-negative; used only with drain
|
|
409
353
|
* @returns {boolean | Promise<boolean>}
|
|
410
354
|
*/
|
|
@@ -416,14 +360,14 @@ export class MailTime {
|
|
|
416
360
|
* @async
|
|
417
361
|
* @memberOf MailTime
|
|
418
362
|
* @name drain
|
|
419
|
-
* @description Wait for all in-flight email send attempts to settle
|
|
420
|
-
* @returns {Promise<
|
|
363
|
+
* @description Wait for all in-flight email send attempts to settle. Resolves with `{ failedWrites }`, the count since this instance was created of failures while recording a send outcome: a storage write that threw, an exception in the completion path, or an outcome write lost after a claim-renewal error (message `outcome write lost (renewal outcome uncertain or lease taken over)`, after one retry with the renewal's stamp (no retry when the claim was already stale at renewal time)). Each is also reported once through `onError` with `details.phase` of `'complete'` or `'checkpoint'`, except after a plain `destroy()`, which still counts but suppresses the hook and the log. A row whose write failed can stay `sending` and be re-sent after `sendingTimeout`. Compare `failedWrites` before and after to scope a shutdown.
|
|
364
|
+
* @returns {Promise<MailTimeDrainResult>}
|
|
421
365
|
*/
|
|
422
|
-
drain(): Promise<
|
|
366
|
+
drain(): Promise<MailTimeDrainResult>;
|
|
423
367
|
/**
|
|
424
368
|
* @memberOf MailTime
|
|
425
369
|
* @name pause
|
|
426
|
-
* @description Pause this server instance from competing for the queue-drain lease. In-flight SMTP sends finish; peer server instances keep draining. Reversible (unlike `destroy()`). No-op on `client` instances or after `destroy()`. To stop scanning *and* wait for in-flight sends: `mailTime.pause(); await mailTime.drain();`.
|
|
370
|
+
* @description Pause this server instance from competing for the queue-drain lease. In-flight SMTP sends finish; sends still waiting for a `concurrency` slot are dropped and their rows stay unclaimed; peer server instances keep draining. Reversible (unlike `destroy()`). No-op on `client` instances or after `destroy()`. To stop scanning *and* wait for in-flight sends: `mailTime.pause(); await mailTime.drain();`.
|
|
427
371
|
* @returns {boolean} `true` if newly paused; `false` if already paused, a client instance, or destroyed
|
|
428
372
|
*/
|
|
429
373
|
pause(): boolean;
|