@push.rocks/smartmta 8.1.0 → 8.2.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.
@@ -9,6 +9,20 @@ export interface IStorageManager {
9
9
  get(key: string): Promise<string | null>;
10
10
  set(key: string, value: string): Promise<void>;
11
11
  list(prefix: string): Promise<string[]>;
12
+ /**
13
+ * Return at most `limit` keys from one backend-bounded page.
14
+ *
15
+ * Continuation tokens are opaque, scoped to the supplied prefix, and must
16
+ * only be passed back to the same storage implementation. A returned token
17
+ * must advance a non-overlapping traversal, remain valid after any keys in
18
+ * its page are deleted, and eventually exhaust an otherwise stable prefix.
19
+ * Offset-based tokens do not satisfy this deletion-stability requirement.
20
+ *
21
+ * This optional method preserves compatibility for existing callers;
22
+ * features which need bounded cleanup must require it explicitly and must
23
+ * never fall back to list().
24
+ */
25
+ listPage?(prefix: string, options: IStorageListPageOptions): Promise<IStorageListPageResult>;
12
26
  delete(key: string): Promise<void>;
13
27
  }
14
28
  /** Blob storage for queued messages and template attachments. */
@@ -16,22 +30,42 @@ export interface IBlobStorageManager {
16
30
  get(key: string): Promise<Buffer | null>;
17
31
  set(key: string, value: Buffer): Promise<void>;
18
32
  list(prefix: string): Promise<string[]>;
33
+ /** Backend-bounded counterpart to list(); see IStorageManager.listPage. */
34
+ listPage?(prefix: string, options: IStorageListPageOptions): Promise<IStorageListPageResult>;
19
35
  delete(key: string): Promise<void>;
20
36
  }
37
+ export interface IStorageListPageOptions {
38
+ /** Positive maximum number of keys the backend may return. */
39
+ readonly limit: number;
40
+ /** Opaque token returned by the immediately preceding page. */
41
+ readonly continuationToken?: string;
42
+ }
43
+ export interface IStorageListPageResult {
44
+ /** Unique keys under the requested prefix; length must not exceed limit. */
45
+ readonly keys: string[];
46
+ /** Omitted when the prefix has been exhausted. */
47
+ readonly continuationToken?: string;
48
+ }
49
+ /** Hard upper bound for one SmartMTA storage listing page. */
50
+ export declare const SMARTMTA_STORAGE_LIST_PAGE_MAX_LIMIT = 1000;
21
51
  /** In-memory small-state store for tests and explicitly non-durable runtimes. */
22
52
  export declare class MemoryStorageManager implements IStorageManager {
23
53
  private readonly entries;
54
+ private readonly sortedKeys;
24
55
  get(key: string): Promise<string | null>;
25
56
  set(key: string, value: string): Promise<void>;
26
57
  list(prefix: string): Promise<string[]>;
58
+ listPage(prefix: string, options: IStorageListPageOptions): Promise<IStorageListPageResult>;
27
59
  delete(key: string): Promise<void>;
28
60
  }
29
61
  /** In-memory blob store for tests and explicitly non-durable runtimes. */
30
62
  export declare class MemoryBlobStorageManager implements IBlobStorageManager {
31
63
  private readonly entries;
64
+ private readonly sortedKeys;
32
65
  get(key: string): Promise<Buffer | null>;
33
66
  set(key: string, value: Buffer): Promise<void>;
34
67
  list(prefix: string): Promise<string[]>;
68
+ listPage(prefix: string, options: IStorageListPageOptions): Promise<IStorageListPageResult>;
35
69
  delete(key: string): Promise<void>;
36
70
  }
37
71
  /** @deprecated Use IStorageManager. */
@@ -1,34 +1,120 @@
1
+ /** Hard upper bound for one SmartMTA storage listing page. */
2
+ export const SMARTMTA_STORAGE_LIST_PAGE_MAX_LIMIT = 1_000;
3
+ function assertValidListPageOptions(options) {
4
+ if (!Number.isInteger(options.limit)
5
+ || options.limit < 1
6
+ || options.limit > SMARTMTA_STORAGE_LIST_PAGE_MAX_LIMIT) {
7
+ throw new Error(`Storage list page limit must be an integer from 1 to ${SMARTMTA_STORAGE_LIST_PAGE_MAX_LIMIT}`);
8
+ }
9
+ if (options.continuationToken !== undefined
10
+ && (typeof options.continuationToken !== 'string'
11
+ || options.continuationToken.length === 0)) {
12
+ throw new Error('Storage list page continuation token must be a non-empty string');
13
+ }
14
+ }
15
+ function lowerBound(sortedKeys, target) {
16
+ let low = 0;
17
+ let high = sortedKeys.length;
18
+ while (low < high) {
19
+ const middle = low + Math.floor((high - low) / 2);
20
+ if (sortedKeys[middle] < target)
21
+ low = middle + 1;
22
+ else
23
+ high = middle;
24
+ }
25
+ return low;
26
+ }
27
+ function upperBound(sortedKeys, target) {
28
+ let low = 0;
29
+ let high = sortedKeys.length;
30
+ while (low < high) {
31
+ const middle = low + Math.floor((high - low) / 2);
32
+ if (sortedKeys[middle] <= target)
33
+ low = middle + 1;
34
+ else
35
+ high = middle;
36
+ }
37
+ return low;
38
+ }
39
+ function insertSortedKey(sortedKeys, key) {
40
+ sortedKeys.splice(lowerBound(sortedKeys, key), 0, key);
41
+ }
42
+ function removeSortedKey(sortedKeys, key) {
43
+ const index = lowerBound(sortedKeys, key);
44
+ if (sortedKeys[index] === key)
45
+ sortedKeys.splice(index, 1);
46
+ }
47
+ function listSortedPrefixPage(sortedKeys, prefix, options) {
48
+ assertValidListPageOptions(options);
49
+ if (options.continuationToken !== undefined
50
+ && !options.continuationToken.startsWith(prefix)) {
51
+ throw new Error('Storage list page continuation token does not belong to the prefix');
52
+ }
53
+ let index = options.continuationToken === undefined
54
+ ? lowerBound(sortedKeys, prefix)
55
+ : Math.max(lowerBound(sortedKeys, prefix), upperBound(sortedKeys, options.continuationToken));
56
+ const keys = [];
57
+ while (index < sortedKeys.length && keys.length < options.limit) {
58
+ const key = sortedKeys[index];
59
+ if (!key.startsWith(prefix))
60
+ break;
61
+ keys.push(key);
62
+ index++;
63
+ }
64
+ const hasMore = (index < sortedKeys.length
65
+ && sortedKeys[index].startsWith(prefix));
66
+ return {
67
+ keys,
68
+ ...(hasMore && keys.length > 0
69
+ ? { continuationToken: keys[keys.length - 1] }
70
+ : {}),
71
+ };
72
+ }
1
73
  /** In-memory small-state store for tests and explicitly non-durable runtimes. */
2
74
  export class MemoryStorageManager {
3
75
  entries = new Map();
76
+ sortedKeys = [];
4
77
  async get(key) {
5
78
  return this.entries.get(key) ?? null;
6
79
  }
7
80
  async set(key, value) {
81
+ if (!this.entries.has(key))
82
+ insertSortedKey(this.sortedKeys, key);
8
83
  this.entries.set(key, value);
9
84
  }
10
85
  async list(prefix) {
11
86
  return [...this.entries.keys()].filter((key) => key.startsWith(prefix)).sort();
12
87
  }
88
+ async listPage(prefix, options) {
89
+ return listSortedPrefixPage(this.sortedKeys, prefix, options);
90
+ }
13
91
  async delete(key) {
14
- this.entries.delete(key);
92
+ if (this.entries.delete(key))
93
+ removeSortedKey(this.sortedKeys, key);
15
94
  }
16
95
  }
17
96
  /** In-memory blob store for tests and explicitly non-durable runtimes. */
18
97
  export class MemoryBlobStorageManager {
19
98
  entries = new Map();
99
+ sortedKeys = [];
20
100
  async get(key) {
21
101
  const value = this.entries.get(key);
22
102
  return value ? Buffer.from(value) : null;
23
103
  }
24
104
  async set(key, value) {
105
+ if (!this.entries.has(key))
106
+ insertSortedKey(this.sortedKeys, key);
25
107
  this.entries.set(key, Buffer.from(value));
26
108
  }
27
109
  async list(prefix) {
28
110
  return [...this.entries.keys()].filter((key) => key.startsWith(prefix)).sort();
29
111
  }
112
+ async listPage(prefix, options) {
113
+ return listSortedPrefixPage(this.sortedKeys, prefix, options);
114
+ }
30
115
  async delete(key) {
31
- this.entries.delete(key);
116
+ if (this.entries.delete(key))
117
+ removeSortedKey(this.sortedKeys, key);
32
118
  }
33
119
  }
34
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW50ZXJmYWNlcy5zdG9yYWdlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vdHMvbWFpbC9pbnRlcmZhY2VzLnN0b3JhZ2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBc0JBLGlGQUFpRjtBQUNqRixNQUFNLE9BQU8sb0JBQW9CO0lBQ2QsT0FBTyxHQUFHLElBQUksR0FBRyxFQUFrQixDQUFDO0lBRTlDLEtBQUssQ0FBQyxHQUFHLENBQUMsR0FBVztRQUMxQixPQUFPLElBQUksQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxJQUFJLElBQUksQ0FBQztJQUN2QyxDQUFDO0lBRU0sS0FBSyxDQUFDLEdBQUcsQ0FBQyxHQUFXLEVBQUUsS0FBYTtRQUN6QyxJQUFJLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxHQUFHLEVBQUUsS0FBSyxDQUFDLENBQUM7SUFDL0IsQ0FBQztJQUVNLEtBQUssQ0FBQyxJQUFJLENBQUMsTUFBYztRQUM5QixPQUFPLENBQUMsR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLElBQUksRUFBRSxDQUFDLENBQUMsTUFBTSxDQUFDLENBQUMsR0FBRyxFQUFFLEVBQUUsQ0FBQyxHQUFHLENBQUMsVUFBVSxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsSUFBSSxFQUFFLENBQUM7SUFDakYsQ0FBQztJQUVNLEtBQUssQ0FBQyxNQUFNLENBQUMsR0FBVztRQUM3QixJQUFJLENBQUMsT0FBTyxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQztJQUMzQixDQUFDO0NBQ0Y7QUFFRCwwRUFBMEU7QUFDMUUsTUFBTSxPQUFPLHdCQUF3QjtJQUNsQixPQUFPLEdBQUcsSUFBSSxHQUFHLEVBQWtCLENBQUM7SUFFOUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxHQUFXO1FBQzFCLE1BQU0sS0FBSyxHQUFHLElBQUksQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBQ3BDLE9BQU8sS0FBSyxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUM7SUFDM0MsQ0FBQztJQUVNLEtBQUssQ0FBQyxHQUFHLENBQUMsR0FBVyxFQUFFLEtBQWE7UUFDekMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsR0FBRyxFQUFFLE1BQU0sQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQztJQUM1QyxDQUFDO0lBRU0sS0FBSyxDQUFDLElBQUksQ0FBQyxNQUFjO1FBQzlCLE9BQU8sQ0FBQyxHQUFHLElBQUksQ0FBQyxPQUFPLENBQUMsSUFBSSxFQUFFLENBQUMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxHQUFHLEVBQUUsRUFBRSxDQUFDLEdBQUcsQ0FBQyxVQUFVLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQztJQUNqRixDQUFDO0lBRU0sS0FBSyxDQUFDLE1BQU0sQ0FBQyxHQUFXO1FBQzdCLElBQUksQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDO0lBQzNCLENBQUM7Q0FDRiJ9
120
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW50ZXJmYWNlcy5zdG9yYWdlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vdHMvbWFpbC9pbnRlcmZhY2VzLnN0b3JhZ2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBMERBLDhEQUE4RDtBQUM5RCxNQUFNLENBQUMsTUFBTSxvQ0FBb0MsR0FBRyxLQUFLLENBQUM7QUFFMUQsU0FBUywwQkFBMEIsQ0FBQyxPQUFnQztJQUNsRSxJQUNFLENBQUMsTUFBTSxDQUFDLFNBQVMsQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDO1dBQzdCLE9BQU8sQ0FBQyxLQUFLLEdBQUcsQ0FBQztXQUNqQixPQUFPLENBQUMsS0FBSyxHQUFHLG9DQUFvQyxFQUN2RCxDQUFDO1FBQ0QsTUFBTSxJQUFJLEtBQUssQ0FDYix3REFBd0Qsb0NBQW9DLEVBQUUsQ0FDL0YsQ0FBQztJQUNKLENBQUM7SUFDRCxJQUNFLE9BQU8sQ0FBQyxpQkFBaUIsS0FBSyxTQUFTO1dBQ3BDLENBQ0QsT0FBTyxPQUFPLENBQUMsaUJBQWlCLEtBQUssUUFBUTtlQUMxQyxPQUFPLENBQUMsaUJBQWlCLENBQUMsTUFBTSxLQUFLLENBQUMsQ0FDMUMsRUFDRCxDQUFDO1FBQ0QsTUFBTSxJQUFJLEtBQUssQ0FBQyxpRUFBaUUsQ0FBQyxDQUFDO0lBQ3JGLENBQUM7QUFDSCxDQUFDO0FBRUQsU0FBUyxVQUFVLENBQUMsVUFBNkIsRUFBRSxNQUFjO0lBQy9ELElBQUksR0FBRyxHQUFHLENBQUMsQ0FBQztJQUNaLElBQUksSUFBSSxHQUFHLFVBQVUsQ0FBQyxNQUFNLENBQUM7SUFDN0IsT0FBTyxHQUFHLEdBQUcsSUFBSSxFQUFFLENBQUM7UUFDbEIsTUFBTSxNQUFNLEdBQUcsR0FBRyxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQyxJQUFJLEdBQUcsR0FBRyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7UUFDbEQsSUFBSSxVQUFVLENBQUMsTUFBTSxDQUFDLEdBQUcsTUFBTTtZQUFF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
@@ -19,6 +19,20 @@ export interface IActionExecutorDeps {
19
19
  bounceManager: BounceManager;
20
20
  deliveryQueue: UnifiedDeliveryQueue;
21
21
  }
22
+ export interface IActionExecutionOptions {
23
+ /**
24
+ * Queue the action with a deterministic durable id instead of performing
25
+ * direct delivery. Used when replaying an already-accepted envelope.
26
+ */
27
+ durableQueue?: {
28
+ idempotencyKey: string;
29
+ fingerprint: string;
30
+ };
31
+ }
32
+ export interface IActionExecutionResult {
33
+ outcome: 'completed' | 'queued';
34
+ queueId?: string;
35
+ }
22
36
  /**
23
37
  * Executes email routing actions (forward, process, deliver, reject)
24
38
  */
@@ -27,6 +41,7 @@ export declare class EmailActionExecutor {
27
41
  constructor(deps: IActionExecutorDeps);
28
42
  private suppressPermanentRcptFailures;
29
43
  executeAction(action: IEmailAction, email: Email, context: IEmailContext): Promise<void>;
44
+ executeActionWithResult(action: IEmailAction, email: Email, context: IEmailContext, options?: IActionExecutionOptions): Promise<IActionExecutionResult>;
30
45
  private handleForwardAction;
31
46
  private handleProcessAction;
32
47
  private handleDeliverAction;
@@ -31,26 +31,26 @@ export class EmailActionExecutor {
31
31
  }
32
32
  }
33
33
  async executeAction(action, email, context) {
34
+ await this.executeActionWithResult(action, email, context);
35
+ }
36
+ async executeActionWithResult(action, email, context, options = {}) {
34
37
  switch (action.type) {
35
38
  case 'forward':
36
- await this.handleForwardAction(action, email, context);
37
- break;
39
+ return this.handleForwardAction(action, email, context, options);
38
40
  case 'process':
39
- await this.handleProcessAction(action, email, context);
40
- break;
41
+ return this.handleProcessAction(action, email, context, options);
41
42
  case 'deliver':
42
- await this.handleDeliverAction(action, email, context);
43
- break;
43
+ return this.handleDeliverAction(action, email, context, options);
44
44
  case 'store':
45
45
  throw new Error('Store actions require raw SMTP context and an awaited hooks.onAcceptEnvelope handler');
46
46
  case 'reject':
47
47
  await this.handleRejectAction(action, email, context);
48
- break;
48
+ throw new Error('Reject action unexpectedly returned');
49
49
  default:
50
50
  throw new Error(`Unknown action type: ${action.type}`);
51
51
  }
52
52
  }
53
- async handleForwardAction(action, email, context) {
53
+ async handleForwardAction(action, email, context, options) {
54
54
  if (!action.forward) {
55
55
  throw new Error('Forward action requires forward configuration');
56
56
  }
@@ -66,6 +66,14 @@ export class EmailActionExecutor {
66
66
  email.headers['X-Forwarded-For'] = context.session.remoteAddress || 'unknown';
67
67
  email.headers['X-Forwarded-To'] = email.to.join(', ');
68
68
  email.headers['X-Forwarded-Date'] = new Date().toISOString();
69
+ if (options.durableQueue) {
70
+ const queueId = await this.deps.deliveryQueue.enqueue(email, 'forward', context.session.matchedRoute, {
71
+ idempotencyKey: options.durableQueue.idempotencyKey,
72
+ idempotencyFingerprint: options.durableQueue.fingerprint,
73
+ requireDurable: true,
74
+ });
75
+ return { outcome: 'queued', queueId };
76
+ }
69
77
  try {
70
78
  // Send email via Rust SMTP client
71
79
  const result = await this.deps.sendOutboundEmail(host, port, email, {
@@ -87,6 +95,7 @@ export class EmailActionExecutor {
87
95
  },
88
96
  success: true
89
97
  }));
98
+ return { outcome: 'completed' };
90
99
  }
91
100
  catch (error) {
92
101
  const errorMessage = getErrorMessage(error);
@@ -123,7 +132,7 @@ export class EmailActionExecutor {
123
132
  throw error;
124
133
  }
125
134
  }
126
- async handleProcessAction(action, email, context) {
135
+ async handleProcessAction(action, email, context, options) {
127
136
  logger.log('info', `Processing email with action options`);
128
137
  // Apply scanning if requested
129
138
  if (action.process?.scan) {
@@ -131,14 +140,28 @@ export class EmailActionExecutor {
131
140
  }
132
141
  // Queue for delivery
133
142
  const queue = action.process?.queue || 'normal';
134
- await this.deps.deliveryQueue.enqueue(email, 'process', context.session.matchedRoute);
143
+ const queueId = await this.deps.deliveryQueue.enqueue(email, 'process', context.session.matchedRoute, options.durableQueue
144
+ ? {
145
+ idempotencyKey: options.durableQueue.idempotencyKey,
146
+ idempotencyFingerprint: options.durableQueue.fingerprint,
147
+ requireDurable: true,
148
+ }
149
+ : {});
135
150
  logger.log('info', `Email queued for delivery in ${queue} queue`);
151
+ return { outcome: 'queued', queueId };
136
152
  }
137
- async handleDeliverAction(_action, email, context) {
153
+ async handleDeliverAction(_action, email, context, options) {
138
154
  logger.log('info', `Delivering email locally`);
139
155
  // Queue for local delivery
140
- await this.deps.deliveryQueue.enqueue(email, 'mta', context.session.matchedRoute);
156
+ const queueId = await this.deps.deliveryQueue.enqueue(email, 'mta', context.session.matchedRoute, options.durableQueue
157
+ ? {
158
+ idempotencyKey: options.durableQueue.idempotencyKey,
159
+ idempotencyFingerprint: options.durableQueue.fingerprint,
160
+ requireDurable: true,
161
+ }
162
+ : {});
141
163
  logger.log('info', 'Email queued for local delivery');
164
+ return { outcome: 'queued', queueId };
142
165
  }
143
166
  async handleRejectAction(action, _email, context) {
144
167
  const code = action.reject?.code || 550;
@@ -165,4 +188,4 @@ export class EmailActionExecutor {
165
188
  throw error;
166
189
  }
167
190
  }
168
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5lbWFpbC5hY3Rpb24uZXhlY3V0b3IuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9tYWlsL3JvdXRpbmcvY2xhc3Nlcy5lbWFpbC5hY3Rpb24uZXhlY3V0b3IudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBQ3pDLE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQztBQUM1RCxPQUFPLEVBQ0wsY0FBYyxFQUNkLGdCQUFnQixFQUNoQixpQkFBaUIsRUFDbEIsTUFBTSx5QkFBeUIsQ0FBQztBQUVqQyxPQUFPLEVBQUUsS0FBSyxFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFDakQsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLGtDQUFrQyxDQUFDO0FBQ2pFLE9BQU8sRUFBRSxvQkFBb0IsRUFBRSxNQUFNLHVDQUF1QyxDQUFDO0FBQzdFLE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxNQUFNLHlDQUF5QyxDQUFDO0FBQzdFLE9BQU8sRUFDTCxpQkFBaUIsR0FHbEIsTUFBTSw4Q0FBOEMsQ0FBQztBQWdCdEQ7O0dBRUc7QUFDSCxNQUFNLE9BQU8sbUJBQW1CO0lBQ1Y7SUFBcEIsWUFBb0IsSUFBeUI7UUFBekIsU0FBSSxHQUFKLElBQUksQ0FBcUI7SUFBRyxDQUFDO0lBRXpDLEtBQUssQ0FBQyw2QkFBNkIsQ0FDekMsS0FBWSxFQUNaLE9BQStCO1FBRS9CLEtBQUssTUFBTSxNQUFNLElBQUksT0FBTyxFQUFFLENBQUM7WUFDN0IsSUFBSSxNQUFNLENBQUMsUUFBUSxJQUFJLE1BQU0sQ0FBQyxZQUFZLEdBQUcsR0FBRztnQkFBRSxTQUFTO1lBQzNELElBQUksQ0FBQztnQkFDSCxNQUFNLElBQUksQ0FBQyxJQUFJLENBQUMsYUFBYSxDQUFDLGtCQUFrQixDQUM5QyxNQUFNLENBQUMsU0FBUyxFQUNoQixnQ0FBZ0MsTUFBTSxDQUFDLFlB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
191
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5lbWFpbC5hY3Rpb24uZXhlY3V0b3IuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9tYWlsL3JvdXRpbmcvY2xhc3Nlcy5lbWFpbC5hY3Rpb24uZXhlY3V0b3IudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBQ3pDLE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQztBQUM1RCxPQUFPLEVBQ0wsY0FBYyxFQUNkLGdCQUFnQixFQUNoQixpQkFBaUIsRUFDbEIsTUFBTSx5QkFBeUIsQ0FBQztBQUVqQyxPQUFPLEVBQUUsS0FBSyxFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFDakQsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLGtDQUFrQyxDQUFDO0FBQ2pFLE9BQU8sRUFBRSxvQkFBb0IsRUFBRSxNQUFNLHVDQUF1QyxDQUFDO0FBQzdFLE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxNQUFNLHlDQUF5QyxDQUFDO0FBQzdFLE9BQU8sRUFDTCxpQkFBaUIsR0FHbEIsTUFBTSw4Q0FBOEMsQ0FBQztBQWdDdEQ7O0dBRUc7QUFDSCxNQUFNLE9BQU8sbUJBQW1CO0lBQ1Y7SUFBcEIsWUFBb0IsSUFBeUI7UUFBekIsU0FBSSxHQUFKLElBQUksQ0FBcUI7SUFBRyxDQUFDO0lBRXpDLEtBQUssQ0FBQyw2QkFBNkIsQ0FDekMsS0FBWSxFQUNaLE9BQStCO1FBRS9CLEtBQUssTUFBTSxNQUFNLElBQUksT0FBTyxFQUFFLENBQUM7WUFDN0IsSUFBSSxNQUFNLENBQUMsUUFBUSxJQUFJLE1BQU0sQ0FBQyxZQUFZLEdBQUcsR0FBRztnQkFBRSxTQUFTO1lBQzNELElBQUksQ0FBQztnQkFDSCxNQUFNLElBQUksQ0FBQyxJQUFJLENBQUMsYUFBYSxDQUFDLGtCQUFrQixDQUM5QyxNQUFNLENBQUMsU0FBUyxFQUNoQixnQ0FBZ0MsTUFBTSxDQUFDLFlB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
@@ -2,7 +2,7 @@ import { EventEmitter } from 'events';
2
2
  import { DKIMCreator } from '../security/classes.dkimcreator.js';
3
3
  import type { IStorageManager } from '../interfaces.storage.js';
4
4
  import type { IInboundEmailSecurityResult } from '../../security/classes.rustsecuritybridge.js';
5
- import type { IEmailRoute, IEmailDomainConfig } from './interfaces.js';
5
+ import type { IEmailRoute, IEmailAction, IEmailDomainConfig } from './interfaces.js';
6
6
  import { Email } from '../core/classes.email.js';
7
7
  import { DomainRegistry } from './classes.domain.registry.js';
8
8
  import { BounceType, BounceCategory } from '../core/classes.bouncemanager.js';
@@ -75,6 +75,16 @@ export interface IMessageAcceptanceContext {
75
75
  resolvedRecipientRoutes: IResolvedRecipientRoute[];
76
76
  abortSignal?: AbortSignal;
77
77
  }
78
+ /**
79
+ * Throw from `hooks.onAcceptEnvelope` to REJECT the envelope with a permanent
80
+ * SMTP status (e.g. DMARC policy enforcement) instead of the generic 451
81
+ * temporary failure used for infrastructure errors.
82
+ */
83
+ export declare class AcceptEnvelopeRejectionError extends Error {
84
+ readonly smtpCode: number;
85
+ readonly smtpMessage: string;
86
+ constructor(smtpCode: number, smtpMessage: string);
87
+ }
78
88
  /**
79
89
  * Terminal durable acceptance context for an envelope containing at least one `store` route.
80
90
  * The consumer owns persistence and all recipient fan-out before this hook resolves.
@@ -96,6 +106,53 @@ export interface IAcceptEnvelopeContext {
96
106
  /** Rust-computed inbound verdicts, available before durable acceptance. */
97
107
  securityResults: IInboundEmailSecurityResult | null;
98
108
  }
109
+ /**
110
+ * JSON-safe, immutable snapshot of one accepted envelope recipient's exact
111
+ * routing decision. The snapshot is safe to persist and replay without
112
+ * consulting the live route table.
113
+ */
114
+ export interface IAcceptedEnvelopeRecipientPlan {
115
+ readonly recipient: string;
116
+ readonly localDomain: string | null;
117
+ readonly routeName: string;
118
+ readonly source: TRecipientRouteSource;
119
+ readonly action: IEmailAction;
120
+ readonly idempotencyKey: string;
121
+ }
122
+ /** Serializable SMTP metadata needed to recreate an isolated action session. */
123
+ export interface IAcceptedEnvelopeDispatchMetadata {
124
+ readonly mailFrom: string;
125
+ readonly session?: {
126
+ readonly id?: string;
127
+ readonly remoteAddress?: string;
128
+ readonly clientHostname?: string;
129
+ readonly secure?: boolean;
130
+ readonly authenticated?: boolean;
131
+ readonly authenticatedUser?: string | null;
132
+ readonly user?: Readonly<{
133
+ username: string;
134
+ [key: string]: unknown;
135
+ }>;
136
+ };
137
+ }
138
+ export type TAcceptedEnvelopeRecipientDispatchStatus = 'stored' | 'queued' | 'rejected' | 'failed';
139
+ export interface IAcceptedEnvelopeRecipientDispatchResult {
140
+ recipient: string;
141
+ localDomain: string | null;
142
+ routeName: string;
143
+ source: TRecipientRouteSource;
144
+ actionType: IEmailAction['type'];
145
+ idempotencyKey: string;
146
+ status: TAcceptedEnvelopeRecipientDispatchStatus;
147
+ replayed: boolean;
148
+ queueId?: string;
149
+ smtpCode?: number;
150
+ message?: string;
151
+ }
152
+ export interface IAcceptedEnvelopeDispatchResult {
153
+ complete: boolean;
154
+ results: IAcceptedEnvelopeRecipientDispatchResult[];
155
+ }
99
156
  export interface IMessageAcceptanceDecision extends ISmtpDecision {
100
157
  /** Continue SmartMTA's built-in route processing after the consumer accepted responsibility. */
101
158
  continueProcessing?: boolean;
@@ -280,6 +337,9 @@ export declare class UnifiedEmailServer extends EventEmitter {
280
337
  private bridgeEmailReceivedHandler?;
281
338
  private bridgeAuthRequestHandler?;
282
339
  private bridgeScramCredentialRequestHandler?;
340
+ private acceptedEnvelopeDispatchCleanupTimer?;
341
+ private acceptedEnvelopeDispatchCleanupPromise?;
342
+ private acceptedEnvelopeDispatchCleanupCursor?;
283
343
  constructor(dcRouter: DcRouter, options: IUnifiedEmailServerOptions);
284
344
  onRecipientRejected(listener: (event: IRecipientRejectedEvent) => Promise<void> | void): this;
285
345
  offRecipientRejected(listener: (event: IRecipientRejectedEvent) => Promise<void> | void): this;
@@ -346,6 +406,26 @@ export declare class UnifiedEmailServer extends EventEmitter {
346
406
  private buildDataPolicyContext;
347
407
  private resolveDataRecipientRoutes;
348
408
  private acceptDurableEnvelope;
409
+ private cloneJsonSafe;
410
+ private stableJson;
411
+ private validateAcceptedEnvelopeAction;
412
+ private normalizeAcceptedEnvelopePlan;
413
+ /**
414
+ * Snapshot the already-resolved acceptance plan for durable consumer storage.
415
+ * No live route lookup is performed when the returned plan is dispatched.
416
+ */
417
+ createAcceptedEnvelopeDispatchPlan(context: IAcceptEnvelopeContext, idempotencyKeys: readonly string[]): readonly IAcceptedEnvelopeRecipientPlan[];
418
+ private acceptedEnvelopeDispatchStorageKey;
419
+ private startAcceptedEnvelopeDispatchCleanup;
420
+ private cleanupExpiredAcceptedEnvelopeDispatchResults;
421
+ private readAcceptedEnvelopeDispatchResult;
422
+ private writeAcceptedEnvelopeDispatchResult;
423
+ /**
424
+ * Dispatch a durably accepted raw envelope using only its persisted exact
425
+ * action snapshots. Each recipient receives an isolated Email and session.
426
+ * Queue-backed actions require managed queue storage and are replay-safe.
427
+ */
428
+ dispatchAcceptedEnvelope(rawMessage: Uint8Array, planArg: readonly IAcceptedEnvelopeRecipientPlan[], metadataArg: IAcceptedEnvelopeDispatchMetadata): Promise<IAcceptedEnvelopeDispatchResult>;
349
429
  /**
350
430
  * Handle an emailReceived event from the Rust SMTP server.
351
431
  */