@serve.zone/dcrouter 15.0.0 → 15.0.2

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.
Files changed (53) hide show
  1. package/deno.json +1 -1
  2. package/dist_ts/00_commitinfo_data.js +1 -1
  3. package/dist_ts/acme/classes.smartacme-lifecycle.d.ts +25 -0
  4. package/dist_ts/acme/classes.smartacme-lifecycle.js +144 -0
  5. package/dist_ts/acme/index.d.ts +1 -0
  6. package/dist_ts/acme/index.js +2 -1
  7. package/dist_ts/classes.dcrouter.d.ts +21 -139
  8. package/dist_ts/classes.dcrouter.js +71 -1585
  9. package/dist_ts/dns/classes.dns-server-runtime.d.ts +37 -0
  10. package/dist_ts/dns/classes.dns-server-runtime.js +449 -0
  11. package/dist_ts/dns/index.d.ts +1 -0
  12. package/dist_ts/dns/index.js +2 -1
  13. package/dist_ts/email/classes.accepted-email-spool.d.ts +55 -0
  14. package/dist_ts/email/classes.accepted-email-spool.js +345 -0
  15. package/dist_ts/email/classes.email-route-builder.d.ts +28 -0
  16. package/dist_ts/email/classes.email-route-builder.js +260 -0
  17. package/dist_ts/email/index.d.ts +2 -0
  18. package/dist_ts/email/index.js +3 -1
  19. package/dist_ts/opsserver/handlers/gatewayclient.handler.js +10 -8
  20. package/dist_ts/remoteingress/classes.hub-lifecycle.d.ts +27 -0
  21. package/dist_ts/remoteingress/classes.hub-lifecycle.js +241 -0
  22. package/dist_ts/remoteingress/classes.remoteingress-manager.d.ts +1 -2
  23. package/dist_ts/remoteingress/index.d.ts +1 -0
  24. package/dist_ts/remoteingress/index.js +2 -1
  25. package/dist_ts/security/classes.route-policy-augmenter.d.ts +22 -0
  26. package/dist_ts/security/classes.route-policy-augmenter.js +120 -0
  27. package/dist_ts/security/index.d.ts +1 -0
  28. package/dist_ts/security/index.js +2 -1
  29. package/dist_ts/vpn/classes.vpn-access-resolver.d.ts +34 -0
  30. package/dist_ts/vpn/classes.vpn-access-resolver.js +101 -0
  31. package/dist_ts/vpn/index.d.ts +1 -0
  32. package/dist_ts/vpn/index.js +2 -1
  33. package/dist_ts_migrations/index.js +92 -9
  34. package/dist_ts_web/00_commitinfo_data.js +1 -1
  35. package/package.json +2 -2
  36. package/ts/00_commitinfo_data.ts +1 -1
  37. package/ts/acme/classes.smartacme-lifecycle.ts +155 -0
  38. package/ts/acme/index.ts +1 -0
  39. package/ts/classes.dcrouter.ts +118 -1919
  40. package/ts/dns/classes.dns-server-runtime.ts +525 -0
  41. package/ts/dns/index.ts +1 -0
  42. package/ts/email/classes.accepted-email-spool.ts +434 -0
  43. package/ts/email/classes.email-route-builder.ts +312 -0
  44. package/ts/email/index.ts +2 -0
  45. package/ts/opsserver/handlers/gatewayclient.handler.ts +9 -7
  46. package/ts/remoteingress/classes.hub-lifecycle.ts +278 -0
  47. package/ts/remoteingress/classes.remoteingress-manager.ts +1 -1
  48. package/ts/remoteingress/index.ts +1 -0
  49. package/ts/security/classes.route-policy-augmenter.ts +140 -0
  50. package/ts/security/index.ts +1 -0
  51. package/ts/vpn/classes.vpn-access-resolver.ts +126 -0
  52. package/ts/vpn/index.ts +1 -0
  53. package/ts_web/00_commitinfo_data.ts +1 -1
@@ -0,0 +1,345 @@
1
+ import * as plugins from '../plugins.js';
2
+ import { logger } from '../logger.js';
3
+ import { CachedEmail } from '../db/index.js';
4
+ export const DCROUTER_CACHE_ID_HEADER = 'X-Dcrouter-Cached-Email-Id';
5
+ const ACCEPTED_EMAIL_SPOOL_INTERVAL_MS = 60_000;
6
+ const ACCEPTED_EMAIL_RETRY_DELAY_MS = 5 * 60_000;
7
+ const ACCEPTED_EMAIL_QUEUE_LEASE_MS = 30 * 60_000;
8
+ const ACCEPTED_EMAIL_SPOOL_BATCH_SIZE = 25;
9
+ const ACCEPTED_EMAIL_STOP_DRAIN_TIMEOUT_MS = 30_000;
10
+ /**
11
+ * Accept-then-spool pipeline for inbound SMTP messages: persists accepted
12
+ * messages as CachedEmail docs, replays them through SmartMTA on an interval,
13
+ * and mirrors SmartMTA delivery-queue outcomes back onto the cached docs.
14
+ */
15
+ export class AcceptedEmailSpool {
16
+ dcRouterRef;
17
+ spoolTimer;
18
+ spoolRun;
19
+ processing = false;
20
+ stopping = false;
21
+ queueUpdatePromises = new Set();
22
+ constructor(dcRouterRef) {
23
+ this.dcRouterRef = dcRouterRef;
24
+ }
25
+ async acceptMessage(context, processAfterAccept = true) {
26
+ if (!this.dcRouterRef.dcRouterDb?.isReady()) {
27
+ throw new Error('DcRouterDb is not available for email acceptance');
28
+ }
29
+ this.throwIfMessageAcceptanceAborted(context.abortSignal);
30
+ const rawMessage = context.rawMessage;
31
+ const session = context.session;
32
+ const envelope = session.envelope;
33
+ const envelopeRecipients = Array.isArray(envelope.rcptTo)
34
+ ? envelope.rcptTo.map((recipient) => recipient.address).filter(Boolean)
35
+ : [];
36
+ const email = context.email;
37
+ const headers = email.headers;
38
+ const cachedEmail = CachedEmail.createNew();
39
+ this.removeHeader(email.headers, DCROUTER_CACHE_ID_HEADER);
40
+ email.headers[DCROUTER_CACHE_ID_HEADER] = cachedEmail.id;
41
+ cachedEmail.messageId = headers['Message-ID'] || headers['message-id'] || cachedEmail.id;
42
+ cachedEmail.from = envelope.mailFrom?.address || email.from || '';
43
+ cachedEmail.to = envelopeRecipients.length > 0
44
+ ? envelopeRecipients
45
+ : Array.isArray(email.to) ? email.to : [];
46
+ cachedEmail.cc = Array.isArray(email.cc) ? email.cc : [];
47
+ cachedEmail.bcc = Array.isArray(email.bcc) ? email.bcc : [];
48
+ cachedEmail.subject = email.subject || '';
49
+ cachedEmail.rawContent = this.setDcRouterCacheIdHeader(rawMessage.toString('utf8'), cachedEmail.id);
50
+ cachedEmail.status = 'pending';
51
+ cachedEmail.nextAttempt = new Date();
52
+ cachedEmail.routeData = JSON.stringify({
53
+ acceptedAt: new Date().toISOString(),
54
+ session: {
55
+ id: session.id,
56
+ remoteAddress: session.remoteAddress,
57
+ clientHostname: session.clientHostname,
58
+ secure: !!session.secure,
59
+ authenticated: !!session.authenticated,
60
+ user: session.user,
61
+ envelope,
62
+ },
63
+ });
64
+ cachedEmail.updateSenderDomain();
65
+ await cachedEmail.save();
66
+ if (context.abortSignal?.aborted) {
67
+ cachedEmail.markFailed('Message acceptance aborted before SMTP success');
68
+ await cachedEmail.save();
69
+ throw new Error('Message acceptance aborted before SMTP success');
70
+ }
71
+ if (processAfterAccept) {
72
+ this.run();
73
+ }
74
+ else {
75
+ cachedEmail.markDelivered();
76
+ await cachedEmail.save();
77
+ }
78
+ return {
79
+ accepted: true,
80
+ smtpCode: 250,
81
+ smtpMessage: '2.0.0 Message accepted for delivery',
82
+ continueProcessing: false,
83
+ };
84
+ }
85
+ /** Start the interval-driven spool processor and trigger an immediate run. */
86
+ start() {
87
+ this.clearSpoolTimer();
88
+ this.stopping = false;
89
+ const runProcessor = () => {
90
+ this.run();
91
+ };
92
+ this.spoolTimer = setInterval(runProcessor, ACCEPTED_EMAIL_SPOOL_INTERVAL_MS);
93
+ this.spoolTimer.unref?.();
94
+ runProcessor();
95
+ }
96
+ /** Mark the spool as stopping and clear the interval without awaiting in-flight work. */
97
+ beginStop() {
98
+ this.stopping = true;
99
+ this.clearSpoolTimer();
100
+ }
101
+ /** Stop the spool and wait (bounded) for an in-flight run to settle. */
102
+ async stop() {
103
+ this.beginStop();
104
+ const spoolRun = this.spoolRun;
105
+ if (spoolRun) {
106
+ const settled = await this.waitForPromiseToSettleWithTimeout(spoolRun, ACCEPTED_EMAIL_STOP_DRAIN_TIMEOUT_MS);
107
+ if (!settled) {
108
+ logger.log('warn', 'Timed out waiting for accepted email spool processing to stop');
109
+ }
110
+ }
111
+ }
112
+ /** Kick off a spool run unless one is already in flight. */
113
+ run() {
114
+ if (this.spoolRun) {
115
+ return;
116
+ }
117
+ const run = this.processSpool().catch((error) => {
118
+ logger.log('warn', `Accepted email spool processing failed: ${error.message}`);
119
+ });
120
+ this.spoolRun = run;
121
+ void run.finally(() => {
122
+ if (this.spoolRun === run) {
123
+ this.spoolRun = undefined;
124
+ }
125
+ });
126
+ }
127
+ trackQueueUpdate(item, status, failureMessage) {
128
+ const updatePromise = this.updateAcceptedEmailFromQueueItem(item, status).catch((error) => {
129
+ logger.log('warn', `${failureMessage}: ${error.message}`);
130
+ });
131
+ this.queueUpdatePromises.add(updatePromise);
132
+ void updatePromise.finally(() => {
133
+ this.queueUpdatePromises.delete(updatePromise);
134
+ });
135
+ }
136
+ async drainQueueUpdates() {
137
+ const queueUpdates = [...this.queueUpdatePromises];
138
+ if (queueUpdates.length === 0) {
139
+ return;
140
+ }
141
+ const settled = await this.waitForPromiseToSettleWithTimeout(Promise.allSettled(queueUpdates).then(() => undefined), ACCEPTED_EMAIL_STOP_DRAIN_TIMEOUT_MS);
142
+ if (!settled) {
143
+ for (const queueUpdate of queueUpdates) {
144
+ this.queueUpdatePromises.delete(queueUpdate);
145
+ }
146
+ logger.log('warn', `Timed out waiting for ${queueUpdates.length} accepted email queue update(s) to settle`);
147
+ }
148
+ }
149
+ /** Requeue emails left in 'queued' state by a previous process as pending. */
150
+ async recoverQueuedEmails() {
151
+ while (true) {
152
+ const queuedEmails = await CachedEmail.findQueuedForRecovery(ACCEPTED_EMAIL_SPOOL_BATCH_SIZE);
153
+ if (queuedEmails.length === 0) {
154
+ return;
155
+ }
156
+ for (const queuedEmail of queuedEmails) {
157
+ if (this.isCachedEmailTerminal(queuedEmail)) {
158
+ continue;
159
+ }
160
+ queuedEmail.status = 'pending';
161
+ queuedEmail.nextAttempt = new Date();
162
+ await queuedEmail.save();
163
+ }
164
+ if (queuedEmails.length < ACCEPTED_EMAIL_SPOOL_BATCH_SIZE) {
165
+ return;
166
+ }
167
+ }
168
+ }
169
+ async processSpool() {
170
+ const emailServer = this.dcRouterRef.emailServer;
171
+ if (this.processing || !emailServer || !this.dcRouterRef.dcRouterDb?.isReady()) {
172
+ return;
173
+ }
174
+ this.processing = true;
175
+ try {
176
+ const cachedEmails = await CachedEmail.findPendingForDelivery(ACCEPTED_EMAIL_SPOOL_BATCH_SIZE);
177
+ for (const cachedEmail of cachedEmails) {
178
+ if (this.stopping || this.dcRouterRef.emailServer !== emailServer) {
179
+ break;
180
+ }
181
+ const session = this.buildCachedEmailSession(cachedEmail);
182
+ const rawMessage = plugins.buffer.Buffer.from(cachedEmail.rawContent || '', 'utf8');
183
+ await this.processAcceptedCachedEmail(cachedEmail, rawMessage, session, emailServer);
184
+ }
185
+ }
186
+ finally {
187
+ this.processing = false;
188
+ }
189
+ }
190
+ async processAcceptedCachedEmail(cachedEmail, emailData, session, emailServer) {
191
+ cachedEmail.status = 'processing';
192
+ cachedEmail.nextAttempt = new Date(Date.now() + ACCEPTED_EMAIL_QUEUE_LEASE_MS);
193
+ await cachedEmail.save();
194
+ try {
195
+ await emailServer.processEmailByMode(emailData, session);
196
+ if (session.matchedRoute?.action.type === 'forward') {
197
+ cachedEmail.markDelivered();
198
+ }
199
+ else {
200
+ const currentCachedEmail = await CachedEmail.findById(cachedEmail.id) || cachedEmail;
201
+ if (this.isCachedEmailTerminal(currentCachedEmail)) {
202
+ return;
203
+ }
204
+ currentCachedEmail.status = 'queued';
205
+ currentCachedEmail.nextAttempt = new Date(Date.now() + ACCEPTED_EMAIL_QUEUE_LEASE_MS);
206
+ await currentCachedEmail.save();
207
+ return;
208
+ }
209
+ await cachedEmail.save();
210
+ }
211
+ catch (error) {
212
+ cachedEmail.scheduleRetry(ACCEPTED_EMAIL_RETRY_DELAY_MS);
213
+ cachedEmail.lastError = error.message;
214
+ await cachedEmail.save();
215
+ logger.log('warn', `Accepted email ${cachedEmail.id} deferred after SmartMTA handoff failure: ${error.message}`);
216
+ }
217
+ }
218
+ buildCachedEmailSession(cachedEmail) {
219
+ const routeData = this.parseCachedEmailRouteData(cachedEmail);
220
+ const storedSession = routeData.session || {};
221
+ const storedEnvelope = storedSession.envelope || {};
222
+ const storedRcptTo = Array.isArray(storedEnvelope.rcptTo) && storedEnvelope.rcptTo.length > 0
223
+ ? storedEnvelope.rcptTo
224
+ : cachedEmail.to.map((address) => ({ address, args: {} }));
225
+ const rcptTo = storedRcptTo
226
+ .filter((recipient) => !!recipient.address)
227
+ .map((recipient) => ({ address: recipient.address, args: recipient.args || {} }));
228
+ const mailFrom = storedEnvelope.mailFrom
229
+ ? { address: storedEnvelope.mailFrom.address, args: storedEnvelope.mailFrom.args || {} }
230
+ : { address: cachedEmail.from || '', args: {} };
231
+ const session = {
232
+ id: `${storedSession.id || cachedEmail.id}-replay-${Date.now()}`,
233
+ state: 'DATA',
234
+ clientHostname: storedSession.clientHostname || '',
235
+ mailFrom: mailFrom.address,
236
+ rcptTo: rcptTo.map((recipient) => recipient.address),
237
+ emailData: cachedEmail.rawContent || '',
238
+ useTLS: !!storedSession.secure,
239
+ connectionEnded: false,
240
+ remoteAddress: storedSession.remoteAddress || '127.0.0.1',
241
+ secure: !!storedSession.secure,
242
+ authenticated: !!storedSession.authenticated,
243
+ envelope: {
244
+ mailFrom,
245
+ rcptTo,
246
+ },
247
+ };
248
+ if (storedSession.user) {
249
+ session.user = storedSession.user;
250
+ }
251
+ return session;
252
+ }
253
+ parseCachedEmailRouteData(cachedEmail) {
254
+ try {
255
+ return cachedEmail.routeData ? JSON.parse(cachedEmail.routeData) : {};
256
+ }
257
+ catch {
258
+ return {};
259
+ }
260
+ }
261
+ async updateAcceptedEmailFromQueueItem(item, status) {
262
+ const cachedEmailId = this.getCachedEmailIdFromQueueItem(item);
263
+ if (!cachedEmailId || !this.dcRouterRef.dcRouterDb?.isReady()) {
264
+ return;
265
+ }
266
+ const cachedEmail = await CachedEmail.findById(cachedEmailId);
267
+ if (!cachedEmail) {
268
+ return;
269
+ }
270
+ if (this.isCachedEmailTerminal(cachedEmail) && status !== 'delivered') {
271
+ return;
272
+ }
273
+ cachedEmail.attempts = Math.max(cachedEmail.attempts || 0, item.attempts || 0);
274
+ if (status === 'delivered') {
275
+ cachedEmail.markDelivered();
276
+ }
277
+ else if (status === 'failed') {
278
+ cachedEmail.markFailed(item.lastError || 'SmartMTA delivery failed');
279
+ }
280
+ else {
281
+ cachedEmail.status = 'queued';
282
+ cachedEmail.nextAttempt = new Date(Date.now() + ACCEPTED_EMAIL_QUEUE_LEASE_MS);
283
+ }
284
+ await cachedEmail.save();
285
+ }
286
+ async waitForPromiseToSettleWithTimeout(promise, timeoutMs) {
287
+ let timeout;
288
+ return await new Promise((resolve) => {
289
+ let settled = false;
290
+ const settle = (didSettle) => {
291
+ if (settled) {
292
+ return;
293
+ }
294
+ settled = true;
295
+ if (timeout) {
296
+ clearTimeout(timeout);
297
+ }
298
+ resolve(didSettle);
299
+ };
300
+ timeout = setTimeout(() => settle(false), timeoutMs);
301
+ timeout.unref?.();
302
+ promise.then(() => settle(true), () => settle(true));
303
+ });
304
+ }
305
+ clearSpoolTimer() {
306
+ if (this.spoolTimer) {
307
+ clearInterval(this.spoolTimer);
308
+ this.spoolTimer = undefined;
309
+ }
310
+ }
311
+ setDcRouterCacheIdHeader(rawContent, cachedEmailId) {
312
+ const headerRegex = new RegExp(`^${DCROUTER_CACHE_ID_HEADER}:.*(?:\r?\n[\t ].*)*\r?\n?`, 'gim');
313
+ const sanitizedContent = rawContent.replace(headerRegex, '');
314
+ return `${DCROUTER_CACHE_ID_HEADER}: ${cachedEmailId}\r\n${sanitizedContent}`;
315
+ }
316
+ isCachedEmailTerminal(cachedEmail) {
317
+ return cachedEmail.status === 'delivered' || cachedEmail.status === 'failed';
318
+ }
319
+ getCachedEmailIdFromQueueItem(item) {
320
+ return this.getHeaderValue(item.processingResult?.headers, DCROUTER_CACHE_ID_HEADER)
321
+ || this.getHeaderValue(item.processingResult?.email?.headers, DCROUTER_CACHE_ID_HEADER);
322
+ }
323
+ getHeaderValue(headers, headerName) {
324
+ if (!headers) {
325
+ return undefined;
326
+ }
327
+ const normalizedHeaderName = headerName.toLowerCase();
328
+ const matchingHeaderName = Object.keys(headers).find((key) => key.toLowerCase() === normalizedHeaderName);
329
+ return matchingHeaderName ? headers[matchingHeaderName] : undefined;
330
+ }
331
+ removeHeader(headers, headerName) {
332
+ const normalizedHeaderName = headerName.toLowerCase();
333
+ for (const key of Object.keys(headers)) {
334
+ if (key.toLowerCase() === normalizedHeaderName) {
335
+ delete headers[key];
336
+ }
337
+ }
338
+ }
339
+ throwIfMessageAcceptanceAborted(abortSignal) {
340
+ if (abortSignal?.aborted) {
341
+ throw new Error('Message acceptance aborted before SMTP success');
342
+ }
343
+ }
344
+ }
345
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5hY2NlcHRlZC1lbWFpbC1zcG9vbC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3RzL2VtYWlsL2NsYXNzZXMuYWNjZXB0ZWQtZW1haWwtc3Bvb2wudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSxlQUFlLENBQUM7QUFDekMsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLGNBQWMsQ0FBQztBQUN0QyxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFVN0MsTUFBTSxDQUFDLE1BQU0sd0JBQXdCLEdBQUcsNEJBQTRCLENBQUM7QUFDckUsTUFBTSxnQ0FBZ0MsR0FBRyxNQUFNLENBQUM7QUFDaEQsTUFBTSw2QkFBNkIsR0FBRyxDQUFDLEdBQUcsTUFBTSxDQUFDO0FBQ2pELE1BQU0sNkJBQTZCLEdBQUcsRUFBRSxHQUFHLE1BQU0sQ0FBQztBQUNsRCxNQUFNLCtCQUErQixHQUFHLEVBQUUsQ0FBQztBQUMzQyxNQUFNLG9DQUFvQyxHQUFHLE1BQU0sQ0FBQztBQW1DcEQ7Ozs7R0FJRztBQUNILE1BQU0sT0FBTyxrQkFBa0I7SUFPVDtJQU5aLFVBQVUsQ0FBMkQ7SUFDckUsUUFBUSxDQUFpQjtJQUN6QixVQUFVLEdBQUcsS0FBSyxDQUFDO0lBQ25CLFFBQVEsR0FBRyxLQUFLLENBQUM7SUFDakIsbUJBQW1CLEdBQUcsSUFBSSxHQUFHLEVBQWlCLENBQUM7SUFFdkQsWUFBb0IsV0FBcUI7UUFBckIsZ0JBQVcsR0FBWCxXQUFXLENBQVU7SUFBRyxDQUFDO0lBRXRDLEtBQUssQ0FBQyxhQUFhLENBQ3hCLE9BQWtDLEVBQ2xDLGtCQUFrQixHQUFHLElBQUk7UUFFekIsSUFBSSxDQUFDLElBQUksQ0FBQyxXQUFXLENBQUMsVUFBVSxFQUFFLE9BQU8sRUFBRSxFQUFFLENBQUM7WUFDNUMsTUFBTSxJQUFJLEtBQUssQ0FBQyxrREFBa0QsQ0FBQyxDQUFDO1FBQ3RFLENB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
@@ -0,0 +1,28 @@
1
+ import * as plugins from '../plugins.js';
2
+ import type { IUnifiedEmailServerOptions } from '@push.rocks/smartmta';
3
+ import type { IRoute } from '../../dist_ts_interfaces/data/route-management.js';
4
+ import type { IDcRouterRouteConfig } from '../../dist_ts_interfaces/data/remoteingress.js';
5
+ import type { DcRouter } from '../classes.dcrouter.js';
6
+ /**
7
+ * Generates SmartProxy routes for the email ports and hydrates persisted
8
+ * email routes into runtime routes: server-first SMTP ports get a raw
9
+ * socket-handler proxy that injects PROXY protocol toward the backend.
10
+ */
11
+ export declare class EmailRouteBuilder {
12
+ private dcRouterRef;
13
+ constructor(dcRouterRef: DcRouter);
14
+ generateEmailRoutes(emailConfig: IUnifiedEmailServerOptions): IDcRouterRouteConfig[];
15
+ getRuntimeEmailRoutes(emailRoutes: IDcRouterRouteConfig[]): plugins.smartproxy.IRouteConfig[];
16
+ /**
17
+ * Hydrate a persisted route into its runtime form: generated email routes
18
+ * get the server-first socket-handler treatment, DoH routes get the DNS
19
+ * socket handler. Returns undefined when the stored route runs as-is.
20
+ */
21
+ hydrateStoredRouteForRuntime(storedRoute: IRoute): plugins.smartproxy.IRouteConfig | undefined;
22
+ private getCurrentGeneratedEmailRouteNames;
23
+ private shouldHydrateGeneratedEmailRoute;
24
+ private createServerFirstEmailRuntimeRoute;
25
+ private getRemoteIngressEmailInboundProxyPolicy;
26
+ private createEmailSocketProxyHandler;
27
+ private createProxyProtocolV1Header;
28
+ }