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/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?: object, recipients?: MailTimeRecipientResult[], summary?: MailTimeRecipientSummary) => void | Promise<void>;
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?: object, recipients?: MailTimeRecipientResult[], summary?: MailTimeRecipientSummary) => void | Promise<void>;
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 a scheduler handler exceeds `schedulerTimeout` (default 10000ms) or JoSk shutdown throws (logged); never rejects. The timeout does not bound SMTP drain time.
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<void>}
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<void>;
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?: object, recipients?: MailTimeRecipientResult[], summary?: MailTimeRecipientSummary) => void | Promise<void>;
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?: object, recipients?: MailTimeRecipientResult[], summary?: MailTimeRecipientSummary) => void | Promise<void>;
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 a scheduler handler exceeds `schedulerTimeout` (default 10000ms) or JoSk shutdown throws (logged); never rejects. The timeout does not bound SMTP drain time.
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<void>}
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<void>;
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;