@dbx-tools/email 0.3.43 → 0.4.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/lib/index.d.ts +26 -0
- package/lib/index.js +24 -0
- package/lib/src/brand.d.ts +47 -0
- package/lib/src/brand.js +44 -0
- package/lib/src/config.d.ts +143 -0
- package/lib/src/config.js +212 -0
- package/lib/src/defaults.d.ts +60 -0
- package/lib/src/defaults.js +64 -0
- package/lib/src/email-html.d.ts +51 -0
- package/lib/src/email-html.js +146 -0
- package/lib/src/markdown.d.ts +22 -0
- package/lib/src/markdown.js +81 -0
- package/lib/src/outbox.d.ts +22 -0
- package/lib/src/outbox.js +60 -0
- package/lib/src/plugin.d.ts +165 -0
- package/lib/src/plugin.js +294 -0
- package/lib/src/sender.d.ts +71 -0
- package/lib/src/sender.js +152 -0
- package/lib/src/tool.d.ts +68 -0
- package/lib/src/tool.js +82 -0
- package/lib/src/transport.d.ts +103 -0
- package/lib/src/transport.js +303 -0
- package/lib/tsconfig.tsbuildinfo +1 -0
- package/package.json +11 -7
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export * as brand from "./src/brand.js";
|
|
2
|
+
export * as config from "./src/config.js";
|
|
3
|
+
export * as defaults from "./src/defaults.js";
|
|
4
|
+
export * as emailHtml from "./src/email-html.js";
|
|
5
|
+
export * as markdown from "./src/markdown.js";
|
|
6
|
+
export * as outbox from "./src/outbox.js";
|
|
7
|
+
export * as plugin from "./src/plugin.js";
|
|
8
|
+
export * as sender from "./src/sender.js";
|
|
9
|
+
export * as tool from "./src/tool.js";
|
|
10
|
+
export * as transport from "./src/transport.js";
|
|
11
|
+
export { emailBrandFromContext, defaultEmailBrand } from "./src/brand.js";
|
|
12
|
+
export type { EmailBrand } from "./src/brand.js";
|
|
13
|
+
export { DEFAULT_SMTP_PORT, IMPLICIT_TLS_SMTP_PORT, EMAIL_CONFIG_SCHEMA, resolveEmailConfig } from "./src/config.js";
|
|
14
|
+
export type { SenderPolicy, SmtpConfig, EmailPluginConfig, ResolvedSmtpConfig, ResolvedFileConfig, ResolvedEmailConfig } from "./src/config.js";
|
|
15
|
+
export { SEND_TIMEOUT_MS, VERIFY_TIMEOUT_MS, VERIFY_ATTEMPTS, MAX_ATTACHMENT_BYTES, MAX_ATTACHMENTS_TOTAL_BYTES, MAX_ATTACHMENT_COUNT, MAX_BODY_CHARS, EMAIL_SEND_SETTINGS, EMAIL_VERIFY_SETTINGS, EMAIL_SENDERS_SETTINGS } from "./src/defaults.js";
|
|
16
|
+
export type { EmailExecuteConfig, EmailExecutionSettings } from "./src/defaults.js";
|
|
17
|
+
export { escapeHtml, renderEmailHtml } from "./src/email-html.js";
|
|
18
|
+
export type { EmailHtmlOptions } from "./src/email-html.js";
|
|
19
|
+
export { normalizeMarkdown, markdownToHtml } from "./src/markdown.js";
|
|
20
|
+
export { writeOutboxEmail } from "./src/outbox.js";
|
|
21
|
+
export { EmailPlugin, email } from "./src/plugin.js";
|
|
22
|
+
export { deriveSenderAddress, parseAllowedSenders, isSenderAllowed, assertSenderAllowed, resolveSenderAddress, listSenderOptions } from "./src/sender.js";
|
|
23
|
+
export { SEND_EMAIL_DESCRIPTION, emailTool } from "./src/tool.js";
|
|
24
|
+
export type { EmailToolOptions } from "./src/tool.js";
|
|
25
|
+
export { getEmailRuntime, setEmailExecutor, resetEmailRuntime, executeWrite, verifyEmailTransport, sendEmail } from "./src/transport.js";
|
|
26
|
+
export type { EmailExecutor, EmailRuntime } from "./src/transport.js";
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// GENERATED by projen watch - DO NOT EDIT.
|
|
2
|
+
// Regenerated from the exporting modules in ./src.
|
|
3
|
+
// Hand edits are overwritten on the next watch; this file is read-only.
|
|
4
|
+
export * as brand from "./src/brand.js";
|
|
5
|
+
export * as config from "./src/config.js";
|
|
6
|
+
export * as defaults from "./src/defaults.js";
|
|
7
|
+
export * as emailHtml from "./src/email-html.js";
|
|
8
|
+
export * as markdown from "./src/markdown.js";
|
|
9
|
+
export * as outbox from "./src/outbox.js";
|
|
10
|
+
export * as plugin from "./src/plugin.js";
|
|
11
|
+
export * as sender from "./src/sender.js";
|
|
12
|
+
export * as tool from "./src/tool.js";
|
|
13
|
+
export * as transport from "./src/transport.js";
|
|
14
|
+
export { emailBrandFromContext, defaultEmailBrand } from "./src/brand.js";
|
|
15
|
+
export { DEFAULT_SMTP_PORT, IMPLICIT_TLS_SMTP_PORT, EMAIL_CONFIG_SCHEMA, resolveEmailConfig } from "./src/config.js";
|
|
16
|
+
export { SEND_TIMEOUT_MS, VERIFY_TIMEOUT_MS, VERIFY_ATTEMPTS, MAX_ATTACHMENT_BYTES, MAX_ATTACHMENTS_TOTAL_BYTES, MAX_ATTACHMENT_COUNT, MAX_BODY_CHARS, EMAIL_SEND_SETTINGS, EMAIL_VERIFY_SETTINGS, EMAIL_SENDERS_SETTINGS } from "./src/defaults.js";
|
|
17
|
+
export { escapeHtml, renderEmailHtml } from "./src/email-html.js";
|
|
18
|
+
export { normalizeMarkdown, markdownToHtml } from "./src/markdown.js";
|
|
19
|
+
export { writeOutboxEmail } from "./src/outbox.js";
|
|
20
|
+
export { EmailPlugin, email } from "./src/plugin.js";
|
|
21
|
+
export { deriveSenderAddress, parseAllowedSenders, isSenderAllowed, assertSenderAllowed, resolveSenderAddress, listSenderOptions } from "./src/sender.js";
|
|
22
|
+
export { SEND_EMAIL_DESCRIPTION, emailTool } from "./src/tool.js";
|
|
23
|
+
export { getEmailRuntime, setEmailExecutor, resetEmailRuntime, executeWrite, verifyEmailTransport, sendEmail } from "./src/transport.js";
|
|
24
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwyQ0FBMkM7QUFDM0MsbURBQW1EO0FBQ25ELHdFQUF3RTtBQUV4RSxPQUFPLEtBQUssS0FBSyxNQUFNLGFBQWEsQ0FBQztBQUNyQyxPQUFPLEtBQUssTUFBTSxNQUFNLGNBQWMsQ0FBQztBQUN2QyxPQUFPLEtBQUssUUFBUSxNQUFNLGdCQUFnQixDQUFDO0FBQzNDLE9BQU8sS0FBSyxTQUFTLE1BQU0sa0JBQWtCLENBQUM7QUFDOUMsT0FBTyxLQUFLLFFBQVEsTUFBTSxnQkFBZ0IsQ0FBQztBQUMzQyxPQUFPLEtBQUssTUFBTSxNQUFNLGNBQWMsQ0FBQztBQUN2QyxPQUFPLEtBQUssTUFBTSxNQUFNLGNBQWMsQ0FBQztBQUN2QyxPQUFPLEtBQUssTUFBTSxNQUFNLGNBQWMsQ0FBQztBQUN2QyxPQUFPLEtBQUssSUFBSSxNQUFNLFlBQVksQ0FBQztBQUNuQyxPQUFPLEtBQUssU0FBUyxNQUFNLGlCQUFpQixDQUFDO0FBQzdDLE9BQU8sRUFBRSxxQkFBcUIsRUFBRSxpQkFBaUIsRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUV2RSxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsc0JBQXNCLEVBQUUsbUJBQW1CLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSxjQUFjLENBQUM7QUFFbEgsT0FBTyxFQUFFLGVBQWUsRUFBRSxpQkFBaUIsRUFBRSxlQUFlLEVBQUUsb0JBQW9CLEVBQUUsMkJBQTJCLEVBQUUsb0JBQW9CLEVBQUUsY0FBYyxFQUFFLG1CQUFtQixFQUFFLHFCQUFxQixFQUFFLHNCQUFzQixFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFFbFAsT0FBTyxFQUFFLFVBQVUsRUFBRSxlQUFlLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUUvRCxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsY0FBYyxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFDbkUsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sY0FBYyxDQUFDO0FBQ2hELE9BQU8sRUFBRSxXQUFXLEVBQUUsS0FBSyxFQUFFLE1BQU0sY0FBYyxDQUFDO0FBQ2xELE9BQU8sRUFBRSxtQkFBbUIsRUFBRSxtQkFBbUIsRUFBRSxlQUFlLEVBQUUsbUJBQW1CLEVBQUUsb0JBQW9CLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSxjQUFjLENBQUM7QUFDdkosT0FBTyxFQUFFLHNCQUFzQixFQUFFLFNBQVMsRUFBRSxNQUFNLFlBQVksQ0FBQztBQUUvRCxPQUFPLEVBQUUsZUFBZSxFQUFFLGdCQUFnQixFQUFFLGlCQUFpQixFQUFFLFlBQVksRUFBRSxvQkFBb0IsRUFBRSxTQUFTLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8vIEdFTkVSQVRFRCBieSBwcm9qZW4gd2F0Y2ggLSBETyBOT1QgRURJVC5cbi8vIFJlZ2VuZXJhdGVkIGZyb20gdGhlIGV4cG9ydGluZyBtb2R1bGVzIGluIC4vc3JjLlxuLy8gSGFuZCBlZGl0cyBhcmUgb3ZlcndyaXR0ZW4gb24gdGhlIG5leHQgd2F0Y2g7IHRoaXMgZmlsZSBpcyByZWFkLW9ubHkuXG5cbmV4cG9ydCAqIGFzIGJyYW5kIGZyb20gXCIuL3NyYy9icmFuZFwiO1xuZXhwb3J0ICogYXMgY29uZmlnIGZyb20gXCIuL3NyYy9jb25maWdcIjtcbmV4cG9ydCAqIGFzIGRlZmF1bHRzIGZyb20gXCIuL3NyYy9kZWZhdWx0c1wiO1xuZXhwb3J0ICogYXMgZW1haWxIdG1sIGZyb20gXCIuL3NyYy9lbWFpbC1odG1sXCI7XG5leHBvcnQgKiBhcyBtYXJrZG93biBmcm9tIFwiLi9zcmMvbWFya2Rvd25cIjtcbmV4cG9ydCAqIGFzIG91dGJveCBmcm9tIFwiLi9zcmMvb3V0Ym94XCI7XG5leHBvcnQgKiBhcyBwbHVnaW4gZnJvbSBcIi4vc3JjL3BsdWdpblwiO1xuZXhwb3J0ICogYXMgc2VuZGVyIGZyb20gXCIuL3NyYy9zZW5kZXJcIjtcbmV4cG9ydCAqIGFzIHRvb2wgZnJvbSBcIi4vc3JjL3Rvb2xcIjtcbmV4cG9ydCAqIGFzIHRyYW5zcG9ydCBmcm9tIFwiLi9zcmMvdHJhbnNwb3J0XCI7XG5leHBvcnQgeyBlbWFpbEJyYW5kRnJvbUNvbnRleHQsIGRlZmF1bHRFbWFpbEJyYW5kIH0gZnJvbSBcIi4vc3JjL2JyYW5kXCI7XG5leHBvcnQgdHlwZSB7IEVtYWlsQnJhbmQgfSBmcm9tIFwiLi9zcmMvYnJhbmRcIjtcbmV4cG9ydCB7IERFRkFVTFRfU01UUF9QT1JULCBJTVBMSUNJVF9UTFNfU01UUF9QT1JULCBFTUFJTF9DT05GSUdfU0NIRU1BLCByZXNvbHZlRW1haWxDb25maWcgfSBmcm9tIFwiLi9zcmMvY29uZmlnXCI7XG5leHBvcnQgdHlwZSB7IFNlbmRlclBvbGljeSwgU210cENvbmZpZywgRW1haWxQbHVnaW5Db25maWcsIFJlc29sdmVkU210cENvbmZpZywgUmVzb2x2ZWRGaWxlQ29uZmlnLCBSZXNvbHZlZEVtYWlsQ29uZmlnIH0gZnJvbSBcIi4vc3JjL2NvbmZpZ1wiO1xuZXhwb3J0IHsgU0VORF9USU1FT1VUX01TLCBWRVJJRllfVElNRU9VVF9NUywgVkVSSUZZX0FUVEVNUFRTLCBNQVhfQVRUQUNITUVOVF9CWVRFUywgTUFYX0FUVEFDSE1FTlRTX1RPVEFMX0JZVEVTLCBNQVhfQVRUQUNITUVOVF9DT1VOVCwgTUFYX0JPRFlfQ0hBUlMsIEVNQUlMX1NFTkRfU0VUVElOR1MsIEVNQUlMX1ZFUklGWV9TRVRUSU5HUywgRU1BSUxfU0VOREVSU19TRVRUSU5HUyB9IGZyb20gXCIuL3NyYy9kZWZhdWx0c1wiO1xuZXhwb3J0IHR5cGUgeyBFbWFpbEV4ZWN1dGVDb25maWcsIEVtYWlsRXhlY3V0aW9uU2V0dGluZ3MgfSBmcm9tIFwiLi9zcmMvZGVmYXVsdHNcIjtcbmV4cG9ydCB7IGVzY2FwZUh0bWwsIHJlbmRlckVtYWlsSHRtbCB9IGZyb20gXCIuL3NyYy9lbWFpbC1odG1sXCI7XG5leHBvcnQgdHlwZSB7IEVtYWlsSHRtbE9wdGlvbnMgfSBmcm9tIFwiLi9zcmMvZW1haWwtaHRtbFwiO1xuZXhwb3J0IHsgbm9ybWFsaXplTWFya2Rvd24sIG1hcmtkb3duVG9IdG1sIH0gZnJvbSBcIi4vc3JjL21hcmtkb3duXCI7XG5leHBvcnQgeyB3cml0ZU91dGJveEVtYWlsIH0gZnJvbSBcIi4vc3JjL291dGJveFwiO1xuZXhwb3J0IHsgRW1haWxQbHVnaW4sIGVtYWlsIH0gZnJvbSBcIi4vc3JjL3BsdWdpblwiO1xuZXhwb3J0IHsgZGVyaXZlU2VuZGVyQWRkcmVzcywgcGFyc2VBbGxvd2VkU2VuZGVycywgaXNTZW5kZXJBbGxvd2VkLCBhc3NlcnRTZW5kZXJBbGxvd2VkLCByZXNvbHZlU2VuZGVyQWRkcmVzcywgbGlzdFNlbmRlck9wdGlvbnMgfSBmcm9tIFwiLi9zcmMvc2VuZGVyXCI7XG5leHBvcnQgeyBTRU5EX0VNQUlMX0RFU0NSSVBUSU9OLCBlbWFpbFRvb2wgfSBmcm9tIFwiLi9zcmMvdG9vbFwiO1xuZXhwb3J0IHR5cGUgeyBFbWFpbFRvb2xPcHRpb25zIH0gZnJvbSBcIi4vc3JjL3Rvb2xcIjtcbmV4cG9ydCB7IGdldEVtYWlsUnVudGltZSwgc2V0RW1haWxFeGVjdXRvciwgcmVzZXRFbWFpbFJ1bnRpbWUsIGV4ZWN1dGVXcml0ZSwgdmVyaWZ5RW1haWxUcmFuc3BvcnQsIHNlbmRFbWFpbCB9IGZyb20gXCIuL3NyYy90cmFuc3BvcnRcIjtcbmV4cG9ydCB0eXBlIHsgRW1haWxFeGVjdXRvciwgRW1haWxSdW50aW1lIH0gZnJvbSBcIi4vc3JjL3RyYW5zcG9ydFwiO1xuIl19
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Optional brand styling for outbound email. {@link EmailBrand} is the
|
|
3
|
+
* small, email-safe slice of a brand a message actually needs - an accent
|
|
4
|
+
* color, a font stack, a display name, and an optional logo image - and
|
|
5
|
+
* {@link emailBrandFromContext} derives it from the portable `BrandContext`
|
|
6
|
+
* shared across the UI and libraries.
|
|
7
|
+
*
|
|
8
|
+
* Email can't use the `[data-brand]` CSS bridge the browser UI uses (mail
|
|
9
|
+
* clients strip `<style>` blocks and ignore `var()`), so branding is applied
|
|
10
|
+
* by inlining these values at render time. A logo is only emitted when it's
|
|
11
|
+
* a fetchable `http(s):` or `data:` URL - a package-export path (the default
|
|
12
|
+
* asset form) can't resolve in an inbox, so it's dropped rather than shown
|
|
13
|
+
* as a broken image.
|
|
14
|
+
*
|
|
15
|
+
* @module
|
|
16
|
+
*/
|
|
17
|
+
import { type BrandContext } from "@dbx-tools/shared-core";
|
|
18
|
+
/** Email-safe brand values inlined into the message layout at render time. */
|
|
19
|
+
export interface EmailBrand {
|
|
20
|
+
/** Header-band background and link color. */
|
|
21
|
+
accent: string;
|
|
22
|
+
/** Text and logo color rendered on the accent band. Defaults to white. */
|
|
23
|
+
onAccent?: string;
|
|
24
|
+
/** Body font stack. */
|
|
25
|
+
fontFamily: string;
|
|
26
|
+
/** Product/display name, used as the header text and the logo `alt`. */
|
|
27
|
+
name?: string;
|
|
28
|
+
/**
|
|
29
|
+
* Logo image rendered in the header band. Only an `http(s):` or `data:`
|
|
30
|
+
* URL renders; other values (e.g. a package-export path) are ignored,
|
|
31
|
+
* since they can't load in a mail client.
|
|
32
|
+
*/
|
|
33
|
+
logoUrl?: string;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Derive the email-safe {@link EmailBrand} from a full brand context: the
|
|
37
|
+
* primary color as the accent, the sans font stack, the display name, and
|
|
38
|
+
* the dark-surface logo (the header band is dark) when it's a renderable URL.
|
|
39
|
+
*/
|
|
40
|
+
export declare function emailBrandFromContext(context: BrandContext): EmailBrand;
|
|
41
|
+
/**
|
|
42
|
+
* The dbx-tools brand as an {@link EmailBrand}, ready to pass to the email
|
|
43
|
+
* plugin (`email({ brand: defaultEmailBrand })`). Convenience so a consumer
|
|
44
|
+
* needs only `@dbx-tools/email`, not the shared brand context, for the
|
|
45
|
+
* common case of the default brand.
|
|
46
|
+
*/
|
|
47
|
+
export declare const defaultEmailBrand: EmailBrand;
|
package/lib/src/brand.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Optional brand styling for outbound email. {@link EmailBrand} is the
|
|
3
|
+
* small, email-safe slice of a brand a message actually needs - an accent
|
|
4
|
+
* color, a font stack, a display name, and an optional logo image - and
|
|
5
|
+
* {@link emailBrandFromContext} derives it from the portable `BrandContext`
|
|
6
|
+
* shared across the UI and libraries.
|
|
7
|
+
*
|
|
8
|
+
* Email can't use the `[data-brand]` CSS bridge the browser UI uses (mail
|
|
9
|
+
* clients strip `<style>` blocks and ignore `var()`), so branding is applied
|
|
10
|
+
* by inlining these values at render time. A logo is only emitted when it's
|
|
11
|
+
* a fetchable `http(s):` or `data:` URL - a package-export path (the default
|
|
12
|
+
* asset form) can't resolve in an inbox, so it's dropped rather than shown
|
|
13
|
+
* as a broken image.
|
|
14
|
+
*
|
|
15
|
+
* @module
|
|
16
|
+
*/
|
|
17
|
+
import { brand } from "@dbx-tools/shared-core";
|
|
18
|
+
/** Whether `value` is an image reference a mail client can actually load. */
|
|
19
|
+
function isRenderableImageUrl(value) {
|
|
20
|
+
return typeof value === "string" && /^(?:https?:|data:)/i.test(value);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Derive the email-safe {@link EmailBrand} from a full brand context: the
|
|
24
|
+
* primary color as the accent, the sans font stack, the display name, and
|
|
25
|
+
* the dark-surface logo (the header band is dark) when it's a renderable URL.
|
|
26
|
+
*/
|
|
27
|
+
export function emailBrandFromContext(context) {
|
|
28
|
+
const logo = context.assets.logo.dark ?? context.assets.logo.light;
|
|
29
|
+
return {
|
|
30
|
+
accent: context.colors.primary,
|
|
31
|
+
onAccent: "#ffffff",
|
|
32
|
+
fontFamily: context.typography.sans,
|
|
33
|
+
name: context.name,
|
|
34
|
+
...(isRenderableImageUrl(logo) ? { logoUrl: logo } : {}),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* The dbx-tools brand as an {@link EmailBrand}, ready to pass to the email
|
|
39
|
+
* plugin (`email({ brand: defaultEmailBrand })`). Convenience so a consumer
|
|
40
|
+
* needs only `@dbx-tools/email`, not the shared brand context, for the
|
|
41
|
+
* common case of the default brand.
|
|
42
|
+
*/
|
|
43
|
+
export const defaultEmailBrand = emailBrandFromContext(brand.defaultBrandContext);
|
|
44
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYnJhbmQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvYnJhbmQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7OztHQWVHO0FBQ0gsT0FBTyxFQUFFLEtBQUssRUFBcUIsTUFBTSx3QkFBd0IsQ0FBQztBQW9CbEUsNkVBQTZFO0FBQzdFLFNBQVMsb0JBQW9CLENBQUMsS0FBeUI7SUFDckQsT0FBTyxPQUFPLEtBQUssS0FBSyxRQUFRLElBQUkscUJBQXFCLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDO0FBQ3hFLENBQUM7QUFFRDs7OztHQUlHO0FBQ0gsTUFBTSxVQUFVLHFCQUFxQixDQUFDLE9BQXFCO0lBQ3pELE1BQU0sSUFBSSxHQUFHLE9BQU8sQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLElBQUksSUFBSSxPQUFPLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUM7SUFDbkUsT0FBTztRQUNMLE1BQU0sRUFBRSxPQUFPLENBQUMsTUFBTSxDQUFDLE9BQU87UUFDOUIsUUFBUSxFQUFFLFNBQVM7UUFDbkIsVUFBVSxFQUFFLE9BQU8sQ0FBQyxVQUFVLENBQUMsSUFBSTtRQUNuQyxJQUFJLEVBQUUsT0FBTyxDQUFDLElBQUk7UUFDbEIsR0FBRyxDQUFDLG9CQUFvQixDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFFLE9BQU8sRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO0tBQ3pELENBQUM7QUFDSixDQUFDO0FBRUQ7Ozs7O0dBS0c7QUFDSCxNQUFNLENBQUMsTUFBTSxpQkFBaUIsR0FBZSxxQkFBcUIsQ0FBQyxLQUFLLENBQUMsbUJBQW1CLENBQUMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogT3B0aW9uYWwgYnJhbmQgc3R5bGluZyBmb3Igb3V0Ym91bmQgZW1haWwuIHtAbGluayBFbWFpbEJyYW5kfSBpcyB0aGVcbiAqIHNtYWxsLCBlbWFpbC1zYWZlIHNsaWNlIG9mIGEgYnJhbmQgYSBtZXNzYWdlIGFjdHVhbGx5IG5lZWRzIC0gYW4gYWNjZW50XG4gKiBjb2xvciwgYSBmb250IHN0YWNrLCBhIGRpc3BsYXkgbmFtZSwgYW5kIGFuIG9wdGlvbmFsIGxvZ28gaW1hZ2UgLSBhbmRcbiAqIHtAbGluayBlbWFpbEJyYW5kRnJvbUNvbnRleHR9IGRlcml2ZXMgaXQgZnJvbSB0aGUgcG9ydGFibGUgYEJyYW5kQ29udGV4dGBcbiAqIHNoYXJlZCBhY3Jvc3MgdGhlIFVJIGFuZCBsaWJyYXJpZXMuXG4gKlxuICogRW1haWwgY2FuJ3QgdXNlIHRoZSBgW2RhdGEtYnJhbmRdYCBDU1MgYnJpZGdlIHRoZSBicm93c2VyIFVJIHVzZXMgKG1haWxcbiAqIGNsaWVudHMgc3RyaXAgYDxzdHlsZT5gIGJsb2NrcyBhbmQgaWdub3JlIGB2YXIoKWApLCBzbyBicmFuZGluZyBpcyBhcHBsaWVkXG4gKiBieSBpbmxpbmluZyB0aGVzZSB2YWx1ZXMgYXQgcmVuZGVyIHRpbWUuIEEgbG9nbyBpcyBvbmx5IGVtaXR0ZWQgd2hlbiBpdCdzXG4gKiBhIGZldGNoYWJsZSBgaHR0cChzKTpgIG9yIGBkYXRhOmAgVVJMIC0gYSBwYWNrYWdlLWV4cG9ydCBwYXRoICh0aGUgZGVmYXVsdFxuICogYXNzZXQgZm9ybSkgY2FuJ3QgcmVzb2x2ZSBpbiBhbiBpbmJveCwgc28gaXQncyBkcm9wcGVkIHJhdGhlciB0aGFuIHNob3duXG4gKiBhcyBhIGJyb2tlbiBpbWFnZS5cbiAqXG4gKiBAbW9kdWxlXG4gKi9cbmltcG9ydCB7IGJyYW5kLCB0eXBlIEJyYW5kQ29udGV4dCB9IGZyb20gXCJAZGJ4LXRvb2xzL3NoYXJlZC1jb3JlXCI7XG5cbi8qKiBFbWFpbC1zYWZlIGJyYW5kIHZhbHVlcyBpbmxpbmVkIGludG8gdGhlIG1lc3NhZ2UgbGF5b3V0IGF0IHJlbmRlciB0aW1lLiAqL1xuZXhwb3J0IGludGVyZmFjZSBFbWFpbEJyYW5kIHtcbiAgLyoqIEhlYWRlci1iYW5kIGJhY2tncm91bmQgYW5kIGxpbmsgY29sb3IuICovXG4gIGFjY2VudDogc3RyaW5nO1xuICAvKiogVGV4dCBhbmQgbG9nbyBjb2xvciByZW5kZXJlZCBvbiB0aGUgYWNjZW50IGJhbmQuIERlZmF1bHRzIHRvIHdoaXRlLiAqL1xuICBvbkFjY2VudD86IHN0cmluZztcbiAgLyoqIEJvZHkgZm9udCBzdGFjay4gKi9cbiAgZm9udEZhbWlseTogc3RyaW5nO1xuICAvKiogUHJvZHVjdC9kaXNwbGF5IG5hbWUsIHVzZWQgYXMgdGhlIGhlYWRlciB0ZXh0IGFuZCB0aGUgbG9nbyBgYWx0YC4gKi9cbiAgbmFtZT86IHN0cmluZztcbiAgLyoqXG4gICAqIExvZ28gaW1hZ2UgcmVuZGVyZWQgaW4gdGhlIGhlYWRlciBiYW5kLiBPbmx5IGFuIGBodHRwKHMpOmAgb3IgYGRhdGE6YFxuICAgKiBVUkwgcmVuZGVyczsgb3RoZXIgdmFsdWVzIChlLmcuIGEgcGFja2FnZS1leHBvcnQgcGF0aCkgYXJlIGlnbm9yZWQsXG4gICAqIHNpbmNlIHRoZXkgY2FuJ3QgbG9hZCBpbiBhIG1haWwgY2xpZW50LlxuICAgKi9cbiAgbG9nb1VybD86IHN0cmluZztcbn1cblxuLyoqIFdoZXRoZXIgYHZhbHVlYCBpcyBhbiBpbWFnZSByZWZlcmVuY2UgYSBtYWlsIGNsaWVudCBjYW4gYWN0dWFsbHkgbG9hZC4gKi9cbmZ1bmN0aW9uIGlzUmVuZGVyYWJsZUltYWdlVXJsKHZhbHVlOiBzdHJpbmcgfCB1bmRlZmluZWQpOiB2YWx1ZSBpcyBzdHJpbmcge1xuICByZXR1cm4gdHlwZW9mIHZhbHVlID09PSBcInN0cmluZ1wiICYmIC9eKD86aHR0cHM/OnxkYXRhOikvaS50ZXN0KHZhbHVlKTtcbn1cblxuLyoqXG4gKiBEZXJpdmUgdGhlIGVtYWlsLXNhZmUge0BsaW5rIEVtYWlsQnJhbmR9IGZyb20gYSBmdWxsIGJyYW5kIGNvbnRleHQ6IHRoZVxuICogcHJpbWFyeSBjb2xvciBhcyB0aGUgYWNjZW50LCB0aGUgc2FucyBmb250IHN0YWNrLCB0aGUgZGlzcGxheSBuYW1lLCBhbmRcbiAqIHRoZSBkYXJrLXN1cmZhY2UgbG9nbyAodGhlIGhlYWRlciBiYW5kIGlzIGRhcmspIHdoZW4gaXQncyBhIHJlbmRlcmFibGUgVVJMLlxuICovXG5leHBvcnQgZnVuY3Rpb24gZW1haWxCcmFuZEZyb21Db250ZXh0KGNvbnRleHQ6IEJyYW5kQ29udGV4dCk6IEVtYWlsQnJhbmQge1xuICBjb25zdCBsb2dvID0gY29udGV4dC5hc3NldHMubG9nby5kYXJrID8/IGNvbnRleHQuYXNzZXRzLmxvZ28ubGlnaHQ7XG4gIHJldHVybiB7XG4gICAgYWNjZW50OiBjb250ZXh0LmNvbG9ycy5wcmltYXJ5LFxuICAgIG9uQWNjZW50OiBcIiNmZmZmZmZcIixcbiAgICBmb250RmFtaWx5OiBjb250ZXh0LnR5cG9ncmFwaHkuc2FucyxcbiAgICBuYW1lOiBjb250ZXh0Lm5hbWUsXG4gICAgLi4uKGlzUmVuZGVyYWJsZUltYWdlVXJsKGxvZ28pID8geyBsb2dvVXJsOiBsb2dvIH0gOiB7fSksXG4gIH07XG59XG5cbi8qKlxuICogVGhlIGRieC10b29scyBicmFuZCBhcyBhbiB7QGxpbmsgRW1haWxCcmFuZH0sIHJlYWR5IHRvIHBhc3MgdG8gdGhlIGVtYWlsXG4gKiBwbHVnaW4gKGBlbWFpbCh7IGJyYW5kOiBkZWZhdWx0RW1haWxCcmFuZCB9KWApLiBDb252ZW5pZW5jZSBzbyBhIGNvbnN1bWVyXG4gKiBuZWVkcyBvbmx5IGBAZGJ4LXRvb2xzL2VtYWlsYCwgbm90IHRoZSBzaGFyZWQgYnJhbmQgY29udGV4dCwgZm9yIHRoZVxuICogY29tbW9uIGNhc2Ugb2YgdGhlIGRlZmF1bHQgYnJhbmQuXG4gKi9cbmV4cG9ydCBjb25zdCBkZWZhdWx0RW1haWxCcmFuZDogRW1haWxCcmFuZCA9IGVtYWlsQnJhbmRGcm9tQ29udGV4dChicmFuZC5kZWZhdWx0QnJhbmRDb250ZXh0KTtcbiJdfQ==
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { type BasePluginConfig } from "@databricks/appkit";
|
|
2
|
+
import type { JSONSchema7 } from "json-schema";
|
|
3
|
+
import type { EmailBrand } from "./brand.js";
|
|
4
|
+
/** SMTP submission port used when none is configured. */
|
|
5
|
+
export declare const DEFAULT_SMTP_PORT = 587;
|
|
6
|
+
/** SMTP port that implies a TLS-on-connect socket rather than STARTTLS. */
|
|
7
|
+
export declare const IMPLICIT_TLS_SMTP_PORT = 465;
|
|
8
|
+
/**
|
|
9
|
+
* How the sender (`From`) address is restricted.
|
|
10
|
+
*
|
|
11
|
+
* `"allowlist"` is the default and the deny-by-default posture: a send is
|
|
12
|
+
* permitted only from an address the configuration names, either through
|
|
13
|
+
* {@link EmailPluginConfig.allowedSenders} or, when that is empty, through
|
|
14
|
+
* the configured sender source ({@link EmailPluginConfig.from} as an exact
|
|
15
|
+
* address, {@link EmailPluginConfig.domain} as a `*@domain` wildcard).
|
|
16
|
+
*
|
|
17
|
+
* `"unrestricted"` is the explicit opt-out: any `From` a caller supplies is
|
|
18
|
+
* accepted. Only reach for it when an upstream system already vets the
|
|
19
|
+
* sender.
|
|
20
|
+
*/
|
|
21
|
+
export type SenderPolicy = "allowlist" | "unrestricted";
|
|
22
|
+
/** SMTP connection + credentials. All fields fall back to env when unset. */
|
|
23
|
+
export interface SmtpConfig {
|
|
24
|
+
/** SMTP server hostname (`SMTP_HOST`). */
|
|
25
|
+
host?: string;
|
|
26
|
+
/** SMTP server port (`SMTP_PORT`). Defaults to {@link DEFAULT_SMTP_PORT}. */
|
|
27
|
+
port?: number;
|
|
28
|
+
/**
|
|
29
|
+
* Use a TLS-on-connect socket (`SMTP_SECURE`). Defaults to
|
|
30
|
+
* `port === ` {@link IMPLICIT_TLS_SMTP_PORT}.
|
|
31
|
+
*/
|
|
32
|
+
secure?: boolean;
|
|
33
|
+
/** SMTP auth username (`SMTP_USER`). */
|
|
34
|
+
user?: string;
|
|
35
|
+
/** SMTP auth password / API key (`SMTP_PASSWORD`). */
|
|
36
|
+
password?: string;
|
|
37
|
+
}
|
|
38
|
+
/** AppKit config accepted by the email plugin. */
|
|
39
|
+
export interface EmailPluginConfig extends BasePluginConfig {
|
|
40
|
+
/** SMTP connection + credentials. Omit to run in file/outbox mode. */
|
|
41
|
+
smtp?: SmtpConfig;
|
|
42
|
+
/**
|
|
43
|
+
* Domain used to build the sender address from the on-behalf-of user
|
|
44
|
+
* (`<local-part>@<domain>`). Falls back to `EMAIL_DOMAIN`. Required in
|
|
45
|
+
* SMTP mode unless {@link from} is set; optional in file mode (the
|
|
46
|
+
* outbox falls back to the user's own email address).
|
|
47
|
+
*/
|
|
48
|
+
domain?: string;
|
|
49
|
+
/**
|
|
50
|
+
* Explicit `From` address. When set, the sender is used verbatim and
|
|
51
|
+
* the per-user derivation is skipped. Falls back to `EMAIL_FROM`.
|
|
52
|
+
*/
|
|
53
|
+
from?: string;
|
|
54
|
+
/**
|
|
55
|
+
* Directory for the file/outbox fallback. Falls back to
|
|
56
|
+
* `EMAIL_OUTBOX_DIR`, then `<cwd>/tmp`. Only used when SMTP
|
|
57
|
+
* credentials are absent.
|
|
58
|
+
*/
|
|
59
|
+
outDir?: string;
|
|
60
|
+
/**
|
|
61
|
+
* Allow-list restricting the sender (`From`) address. Each entry is an
|
|
62
|
+
* exact address (`user@domain.com`), a domain wildcard (`*@domain.com`
|
|
63
|
+
* or the bare `domain.com`, matching any local part on that domain), or
|
|
64
|
+
* `*` (any). A resolved / chosen sender that matches no entry is
|
|
65
|
+
* rejected at send time. Accepts a `string[]` or a single comma- /
|
|
66
|
+
* whitespace-separated string; falls back to `EMAIL_ALLOWED_SENDERS`.
|
|
67
|
+
* When empty, the {@link senderPolicy} decides what is permitted.
|
|
68
|
+
*/
|
|
69
|
+
allowedSenders?: string | string[];
|
|
70
|
+
/**
|
|
71
|
+
* How the sender address is restricted when {@link allowedSenders} is
|
|
72
|
+
* empty (`EMAIL_SENDER_POLICY`). Defaults to `"allowlist"`, which
|
|
73
|
+
* narrows sends to the configured sender source. See
|
|
74
|
+
* {@link SenderPolicy}.
|
|
75
|
+
*/
|
|
76
|
+
senderPolicy?: SenderPolicy;
|
|
77
|
+
/**
|
|
78
|
+
* Optional brand styling (accent, font, header logo) applied to the
|
|
79
|
+
* rendered HTML of every message. Omit for the neutral default layout.
|
|
80
|
+
* Pass {@link emailBrandFromContext} to derive it from a shared
|
|
81
|
+
* `BrandContext`.
|
|
82
|
+
*/
|
|
83
|
+
brand?: EmailBrand;
|
|
84
|
+
}
|
|
85
|
+
/** Config shared by both resolved modes. */
|
|
86
|
+
interface ResolvedSender {
|
|
87
|
+
/** Sender domain; present whenever {@link from} is absent. */
|
|
88
|
+
domain?: string;
|
|
89
|
+
/** Explicit sender override; present skips per-user derivation. */
|
|
90
|
+
from?: string;
|
|
91
|
+
/**
|
|
92
|
+
* Effective sender allow-list: the configured patterns when any were
|
|
93
|
+
* given, else the patterns implied by the sender source under an
|
|
94
|
+
* `"allowlist"` policy. Empty means unenforceable (an `"unrestricted"`
|
|
95
|
+
* policy, or an outbox with no sender source to narrow to).
|
|
96
|
+
*/
|
|
97
|
+
allowedSenders: string[];
|
|
98
|
+
/** The restriction mode the allow-list was resolved under. */
|
|
99
|
+
senderPolicy: SenderPolicy;
|
|
100
|
+
/** Brand styling applied to rendered HTML; absent for the default layout. */
|
|
101
|
+
brand?: EmailBrand;
|
|
102
|
+
}
|
|
103
|
+
/** Resolved config for real SMTP delivery. */
|
|
104
|
+
export interface ResolvedSmtpConfig extends ResolvedSender {
|
|
105
|
+
mode: "smtp";
|
|
106
|
+
host: string;
|
|
107
|
+
port: number;
|
|
108
|
+
secure: boolean;
|
|
109
|
+
auth: {
|
|
110
|
+
user: string;
|
|
111
|
+
pass: string;
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
/** Resolved config for the file/outbox fallback (no SMTP credentials). */
|
|
115
|
+
export interface ResolvedFileConfig extends ResolvedSender {
|
|
116
|
+
mode: "file";
|
|
117
|
+
/** Absolute directory messages are written under. */
|
|
118
|
+
outDir: string;
|
|
119
|
+
}
|
|
120
|
+
/** Concrete, validated config the runtime dispatches through. */
|
|
121
|
+
export type ResolvedEmailConfig = ResolvedSmtpConfig | ResolvedFileConfig;
|
|
122
|
+
/** JSON Schema published on the manifest's `config.schema`. */
|
|
123
|
+
export declare const EMAIL_CONFIG_SCHEMA: JSONSchema7;
|
|
124
|
+
/**
|
|
125
|
+
* Resolve plugin config over environment defaults.
|
|
126
|
+
*
|
|
127
|
+
* When SMTP host + user + password are all present, returns `mode:
|
|
128
|
+
* "smtp"` for real delivery (and throws if no sender source - domain or
|
|
129
|
+
* from - is configured, since SMTP can't derive one from nothing).
|
|
130
|
+
* When all three are absent and `EMAIL_OUTBOX_MODE` is enabled, returns
|
|
131
|
+
* `mode: "file"` so the runtime writes messages to the outbox directory
|
|
132
|
+
* for local testing; in that mode a sender source is optional (the
|
|
133
|
+
* outbox falls back to the OBO user's own address). Partial SMTP
|
|
134
|
+
* configuration or a send with no credentials and no outbox opt-in
|
|
135
|
+
* throws.
|
|
136
|
+
*
|
|
137
|
+
* The sender allow-list is resolved here too: under the default
|
|
138
|
+
* `"allowlist"` policy an empty list is filled in from the sender source,
|
|
139
|
+
* so a deployment that only sets `EMAIL_DOMAIN` still rejects a `From` on
|
|
140
|
+
* any other domain. See {@link SenderPolicy}.
|
|
141
|
+
*/
|
|
142
|
+
export declare function resolveEmailConfig(config?: EmailPluginConfig): ResolvedEmailConfig;
|
|
143
|
+
export {};
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SMTP configuration for the email plugin: the typed
|
|
3
|
+
* {@link EmailPluginConfig} (the plugin's slice of AppKit config), the
|
|
4
|
+
* JSON Schema the manifest publishes for it, and {@link resolveEmailConfig}
|
|
5
|
+
* which layers that config over environment defaults into the concrete
|
|
6
|
+
* {@link ResolvedEmailConfig} the runtime needs.
|
|
7
|
+
*
|
|
8
|
+
* Two modes fall out of the resolution. When SMTP credentials (host +
|
|
9
|
+
* user + password) are all present it resolves to `mode: "smtp"` and
|
|
10
|
+
* mail is sent for real. When they are absent and `EMAIL_OUTBOX_MODE`
|
|
11
|
+
* is explicitly enabled it resolves to `mode: "file"` (an "outbox")
|
|
12
|
+
* and each message is written to disk as HTML instead of sent. Any
|
|
13
|
+
* partial SMTP configuration or a send attempt with no credentials and
|
|
14
|
+
* no outbox opt-in throws.
|
|
15
|
+
*
|
|
16
|
+
* Precedence per field: explicit plugin config wins, then the matching
|
|
17
|
+
* environment variable. Env names are unprefixed because the app talks
|
|
18
|
+
* to a single SMTP server (e.g. SMTP2GO): `SMTP_HOST`, `SMTP_PORT`,
|
|
19
|
+
* `SMTP_SECURE`, `SMTP_USER`, `SMTP_PASSWORD`, plus `EMAIL_DOMAIN` for
|
|
20
|
+
* the derived sender's domain, `EMAIL_FROM` for an explicit override,
|
|
21
|
+
* `EMAIL_SENDER_POLICY` for the sender restriction mode, and
|
|
22
|
+
* `EMAIL_OUTBOX_DIR` for the outbox directory.
|
|
23
|
+
*
|
|
24
|
+
* @module
|
|
25
|
+
*/
|
|
26
|
+
import { resolve } from "node:path";
|
|
27
|
+
import { ConfigurationError, ValidationError } from "@databricks/appkit";
|
|
28
|
+
import { object } from "@dbx-tools/shared-core";
|
|
29
|
+
import { parseAllowedSenders } from "./sender.js";
|
|
30
|
+
/** SMTP submission port used when none is configured. */
|
|
31
|
+
export const DEFAULT_SMTP_PORT = 587;
|
|
32
|
+
/** SMTP port that implies a TLS-on-connect socket rather than STARTTLS. */
|
|
33
|
+
export const IMPLICIT_TLS_SMTP_PORT = 465;
|
|
34
|
+
/** JSON Schema published on the manifest's `config.schema`. */
|
|
35
|
+
export const EMAIL_CONFIG_SCHEMA = {
|
|
36
|
+
type: "object",
|
|
37
|
+
properties: {
|
|
38
|
+
smtp: {
|
|
39
|
+
type: "object",
|
|
40
|
+
description: "SMTP connection and credentials (env fallbacks: SMTP_*).",
|
|
41
|
+
properties: {
|
|
42
|
+
host: { type: "string", description: "SMTP server hostname (SMTP_HOST)." },
|
|
43
|
+
port: {
|
|
44
|
+
type: "number",
|
|
45
|
+
description: `SMTP server port (SMTP_PORT). Defaults to ${DEFAULT_SMTP_PORT}.`,
|
|
46
|
+
},
|
|
47
|
+
secure: {
|
|
48
|
+
type: "boolean",
|
|
49
|
+
description: `TLS-on-connect socket (SMTP_SECURE). Defaults to port === ${IMPLICIT_TLS_SMTP_PORT}.`,
|
|
50
|
+
},
|
|
51
|
+
user: { type: "string", description: "SMTP auth username (SMTP_USER)." },
|
|
52
|
+
password: {
|
|
53
|
+
type: "string",
|
|
54
|
+
description: "SMTP auth password / API key (SMTP_PASSWORD).",
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
domain: {
|
|
59
|
+
type: "string",
|
|
60
|
+
description: "Domain for the derived sender address (<user-local-part>@<domain>). Falls back to EMAIL_DOMAIN.",
|
|
61
|
+
},
|
|
62
|
+
from: {
|
|
63
|
+
type: "string",
|
|
64
|
+
description: "Explicit From address; skips per-user derivation. Falls back to EMAIL_FROM.",
|
|
65
|
+
},
|
|
66
|
+
outDir: {
|
|
67
|
+
type: "string",
|
|
68
|
+
description: "Directory for the file/outbox fallback when SMTP is unconfigured. Falls back to EMAIL_OUTBOX_DIR, then <cwd>/tmp.",
|
|
69
|
+
},
|
|
70
|
+
allowedSenders: {
|
|
71
|
+
type: "array",
|
|
72
|
+
items: { type: "string" },
|
|
73
|
+
description: 'Allow-list of permitted sender (From) patterns: exact addresses ("user@domain.com"), domain wildcards ("*@domain.com"), or "*". Also accepts a comma/space-separated string. Falls back to EMAIL_ALLOWED_SENDERS. When empty, senderPolicy decides.',
|
|
74
|
+
},
|
|
75
|
+
senderPolicy: {
|
|
76
|
+
type: "string",
|
|
77
|
+
enum: ["allowlist", "unrestricted"],
|
|
78
|
+
description: 'How the sender is restricted when allowedSenders is empty (EMAIL_SENDER_POLICY). "allowlist" (default) narrows sends to the configured sender source; "unrestricted" accepts any From.',
|
|
79
|
+
},
|
|
80
|
+
brand: {
|
|
81
|
+
type: "object",
|
|
82
|
+
description: "Brand styling inlined into every rendered message. Omit for the neutral default layout.",
|
|
83
|
+
properties: {
|
|
84
|
+
accent: {
|
|
85
|
+
type: "string",
|
|
86
|
+
description: 'Header-band background and body link color (e.g. "#FF3621").',
|
|
87
|
+
},
|
|
88
|
+
onAccent: {
|
|
89
|
+
type: "string",
|
|
90
|
+
description: "Text and logo color rendered on the accent band. Defaults to white.",
|
|
91
|
+
},
|
|
92
|
+
fontFamily: {
|
|
93
|
+
type: "string",
|
|
94
|
+
description: 'Body font stack (e.g. "Inter, ui-sans-serif, system-ui, sans-serif").',
|
|
95
|
+
},
|
|
96
|
+
name: {
|
|
97
|
+
type: "string",
|
|
98
|
+
description: "Product / display name used as the header text and the logo alt text.",
|
|
99
|
+
},
|
|
100
|
+
logoUrl: {
|
|
101
|
+
type: "string",
|
|
102
|
+
description: "Logo image for the header band. Only an http(s): or data: URL renders; other values are dropped because a mail client cannot load them.",
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
required: ["accent", "fontFamily"],
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
/** Parse the `SMTP_SECURE` env / config flag, defaulting by port. */
|
|
110
|
+
function resolveSecure(flag, port) {
|
|
111
|
+
if (typeof flag === "boolean")
|
|
112
|
+
return flag;
|
|
113
|
+
return object.toBoolean(process.env.SMTP_SECURE) ?? port === IMPLICIT_TLS_SMTP_PORT;
|
|
114
|
+
}
|
|
115
|
+
/** Parse the `EMAIL_SENDER_POLICY` env / config value, defaulting to deny-by-default. */
|
|
116
|
+
function resolveSenderPolicy(policy) {
|
|
117
|
+
const raw = policy ?? process.env.EMAIL_SENDER_POLICY?.trim().toLowerCase();
|
|
118
|
+
if (raw === "unrestricted")
|
|
119
|
+
return "unrestricted";
|
|
120
|
+
if (raw === undefined || raw === "" || raw === "allowlist")
|
|
121
|
+
return "allowlist";
|
|
122
|
+
throw ValidationError.invalidValue("senderPolicy", raw, '"allowlist" or "unrestricted"');
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Effective allow-list for an `"allowlist"` policy with no explicit patterns:
|
|
126
|
+
* the sender source itself, so a configured domain or fixed address is the
|
|
127
|
+
* boundary rather than "anything goes". Yields nothing when neither is set,
|
|
128
|
+
* which only happens in outbox mode (where the sender falls back to the
|
|
129
|
+
* on-behalf-of user's own address and cannot be enumerated up front).
|
|
130
|
+
*/
|
|
131
|
+
function impliedSenderPatterns(domain, from) {
|
|
132
|
+
const patterns = [];
|
|
133
|
+
if (from)
|
|
134
|
+
patterns.push(from.trim().toLowerCase());
|
|
135
|
+
if (domain)
|
|
136
|
+
patterns.push(`*@${domain.trim().toLowerCase()}`);
|
|
137
|
+
return patterns;
|
|
138
|
+
}
|
|
139
|
+
/** Whether `EMAIL_OUTBOX_MODE` explicitly opts into the file/outbox fallback. */
|
|
140
|
+
function isOutboxModeEnabled() {
|
|
141
|
+
return object.toBoolean(process.env.EMAIL_OUTBOX_MODE) ?? false;
|
|
142
|
+
}
|
|
143
|
+
const SMTP_REQUIRED_FIELDS = ["SMTP_HOST", "SMTP_USER", "SMTP_PASSWORD"];
|
|
144
|
+
/** List env keys for SMTP fields that are unset in the resolved credential set. */
|
|
145
|
+
function missingSmtpFields(host, user, pass) {
|
|
146
|
+
const values = [host, user, pass];
|
|
147
|
+
return SMTP_REQUIRED_FIELDS.filter((_, index) => !values[index]);
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Resolve plugin config over environment defaults.
|
|
151
|
+
*
|
|
152
|
+
* When SMTP host + user + password are all present, returns `mode:
|
|
153
|
+
* "smtp"` for real delivery (and throws if no sender source - domain or
|
|
154
|
+
* from - is configured, since SMTP can't derive one from nothing).
|
|
155
|
+
* When all three are absent and `EMAIL_OUTBOX_MODE` is enabled, returns
|
|
156
|
+
* `mode: "file"` so the runtime writes messages to the outbox directory
|
|
157
|
+
* for local testing; in that mode a sender source is optional (the
|
|
158
|
+
* outbox falls back to the OBO user's own address). Partial SMTP
|
|
159
|
+
* configuration or a send with no credentials and no outbox opt-in
|
|
160
|
+
* throws.
|
|
161
|
+
*
|
|
162
|
+
* The sender allow-list is resolved here too: under the default
|
|
163
|
+
* `"allowlist"` policy an empty list is filled in from the sender source,
|
|
164
|
+
* so a deployment that only sets `EMAIL_DOMAIN` still rejects a `From` on
|
|
165
|
+
* any other domain. See {@link SenderPolicy}.
|
|
166
|
+
*/
|
|
167
|
+
export function resolveEmailConfig(config = {}) {
|
|
168
|
+
const smtp = config.smtp ?? {};
|
|
169
|
+
const host = smtp.host ?? process.env.SMTP_HOST;
|
|
170
|
+
const user = smtp.user ?? process.env.SMTP_USER;
|
|
171
|
+
const pass = smtp.password ?? process.env.SMTP_PASSWORD;
|
|
172
|
+
const domain = config.domain ?? process.env.EMAIL_DOMAIN;
|
|
173
|
+
const from = config.from ?? process.env.EMAIL_FROM;
|
|
174
|
+
const senderPolicy = resolveSenderPolicy(config.senderPolicy);
|
|
175
|
+
const configuredSenders = parseAllowedSenders(config.allowedSenders ?? process.env.EMAIL_ALLOWED_SENDERS);
|
|
176
|
+
const allowedSenders = configuredSenders.length > 0 || senderPolicy === "unrestricted"
|
|
177
|
+
? configuredSenders
|
|
178
|
+
: impliedSenderPatterns(domain, from);
|
|
179
|
+
const sender = {
|
|
180
|
+
...(domain ? { domain } : {}),
|
|
181
|
+
...(from ? { from } : {}),
|
|
182
|
+
allowedSenders,
|
|
183
|
+
senderPolicy,
|
|
184
|
+
...(config.brand ? { brand: config.brand } : {}),
|
|
185
|
+
};
|
|
186
|
+
const hasAllSmtp = Boolean(host && user && pass);
|
|
187
|
+
const hasAnySmtp = Boolean(host || user || pass);
|
|
188
|
+
if (hasAnySmtp && !hasAllSmtp) {
|
|
189
|
+
throw ValidationError.missingEnvVars(missingSmtpFields(host, user, pass));
|
|
190
|
+
}
|
|
191
|
+
if (hasAllSmtp) {
|
|
192
|
+
if (!domain && !from) {
|
|
193
|
+
throw ConfigurationError.resourceNotFound("Email sender source", "Set EMAIL_DOMAIN to derive <user-local-part>@<domain>, or EMAIL_FROM for a fixed address.");
|
|
194
|
+
}
|
|
195
|
+
const portRaw = smtp.port ?? Number(process.env.SMTP_PORT);
|
|
196
|
+
const port = Number.isFinite(portRaw) && portRaw ? Number(portRaw) : DEFAULT_SMTP_PORT;
|
|
197
|
+
return {
|
|
198
|
+
mode: "smtp",
|
|
199
|
+
host: host,
|
|
200
|
+
port,
|
|
201
|
+
secure: resolveSecure(smtp.secure, port),
|
|
202
|
+
auth: { user: user, pass: pass },
|
|
203
|
+
...sender,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
if (!isOutboxModeEnabled()) {
|
|
207
|
+
throw ConfigurationError.invalidConnection("SMTP", `Set ${SMTP_REQUIRED_FIELDS.join(", ")}, or EMAIL_OUTBOX_MODE=1 to write messages to a local outbox instead.`);
|
|
208
|
+
}
|
|
209
|
+
const outDir = resolve(config.outDir ?? process.env.EMAIL_OUTBOX_DIR ?? resolve(process.cwd(), "tmp"));
|
|
210
|
+
return { mode: "file", outDir, ...sender };
|
|
211
|
+
}
|
|
212
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29uZmlnLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2NvbmZpZy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBd0JHO0FBQ0gsT0FBTyxFQUFFLE9BQU8sRUFBRSxNQUFNLFdBQVcsQ0FBQztBQUNwQyxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsZUFBZSxFQUF5QixNQUFNLG9CQUFvQixDQUFDO0FBQ2hHLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUdoRCxPQUFPLEVBQUUsbUJBQW1CLEVBQUUsTUFBTSxVQUFVLENBQUM7QUFFL0MseURBQXlEO0FBQ3pELE1BQU0sQ0FBQyxNQUFNLGlCQUFpQixHQUFHLEdBQUcsQ0FBQztBQUVyQywyRUFBMkU7QUFDM0UsTUFBTSxDQUFDLE1BQU0sc0JBQXNCLEdBQUcsR0FBRyxDQUFDO0FBd0gxQywrREFBK0Q7QUFDL0QsTUFBTSxDQUFDLE1BQU0sbUJBQW1CLEdBQWdCO0lBQzlDLElBQUksRUFBRSxRQUFRO0lBQ2QsVUFBVSxFQUFFO1FBQ1YsSUFBSSxFQUFFO1lBQ0osSUFBSSxFQUFFLFFBQVE7WUFDZCxXQUFXLEVBQUUsMERBQTBEO1lBQ3ZFLFVBQVUsRUFBRTtnQkFDVixJQUFJLEVBQUUsRUFBRSxJQUFJLEVBQUUsUUFBUSxFQUFFLFdBQVcsRUFBRSxtQ0FBbUMsRUFBRTtnQkFDMUUsSUFBSSxFQUFFO29CQUNKLElBQUksRUFBRSxRQUFRO29CQUNkLFdBQVcsRUFBRSw2Q0FBNkMsaUJBQWlCLEdBQUc7aUJBQy9FO2dCQUNELE1BQU0sRUFBRTtvQkFDTixJQUFJLEVBQUUsU0FBUztvQkFDZixXQUFXLEVBQUUsNkRBQTZELHNCQUFzQixHQUFHO2lCQUNwRztnQkFDRCxJQUFJLEVBQUUsRUFBRSxJQUFJLEVBQUUsUUFBUSxFQUFFLFdBQVcsRUFBRSxpQ0FBaUMsRUFBRTtnQkFDeEUsUUFBUSxFQUFFO29CQUNSLElB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bmZpZ3VyYXRpb24gZm9yIHRoZSBlbWFpbCBwbHVnaW46IHRoZSB0eXBlZFxuICoge0BsaW5rIEVtYWlsUGx1Z2luQ29uZmlnfSAodGhlIHBsdWdpbidzIHNsaWNlIG9mIEFwcEtpdCBjb25maWcpLCB0aGVcbiAqIEpTT04gU2NoZW1hIHRoZSBtYW5pZmVzdCBwdWJsaXNoZXMgZm9yIGl0LCBhbmQge0BsaW5rIHJlc29sdmVFbWFpbENvbmZpZ31cbiAqIHdoaWNoIGxheWVycyB0aGF0IGNvbmZpZyBvdmVyIGVudmlyb25tZW50IGRlZmF1bHRzIGludG8gdGhlIGNvbmNyZXRlXG4gKiB7QGxpbmsgUmVzb2x2ZWRFbWFpbENvbmZpZ30gdGhlIHJ1bnRpbWUgbmVlZHMuXG4gKlxuICogVHdvIG1vZGVzIGZhbGwgb3V0IG9mIHRoZSByZXNvbHV0aW9uLiBXaGVuIFNNVFAgY3JlZGVudGlhbHMgKGhvc3QgK1xuICogdXNlciArIHBhc3N3b3JkKSBhcmUgYWxsIHByZXNlbnQgaXQgcmVzb2x2ZXMgdG8gYG1vZGU6IFwic210cFwiYCBhbmRcbiAqIG1haWwgaXMgc2VudCBmb3IgcmVhbC4gV2hlbiB0aGV5IGFyZSBhYnNlbnQgYW5kIGBFTUFJTF9PVVRCT1hfTU9ERWBcbiAqIGlzIGV4cGxpY2l0bHkgZW5hYmxlZCBpdCByZXNvbHZlcyB0byBgbW9kZTogXCJmaWxlXCJgIChhbiBcIm91dGJveFwiKVxuICogYW5kIGVhY2ggbWVzc2FnZSBpcyB3cml0dGVuIHRvIGRpc2sgYXMgSFRNTCBpbnN0ZWFkIG9mIHNlbnQuIEFueVxuICogcGFydGlhbCBTTVRQIGNvbmZpZ3VyYXRpb24gb3IgYSBzZW5kIGF0dGVtcHQgd2l0aCBubyBjcmVkZW50aWFscyBhbmRcbiAqIG5vIG91dGJveCBvcHQtaW4gdGhyb3dzLlxuICpcbiAqIFByZWNlZGVuY2UgcGVyIGZpZWxkOiBleHBsaWNpdCBwbHVnaW4gY29uZmlnIHdpbnMsIHRoZW4gdGhlIG1hdGNoaW5nXG4gKiBlbnZpcm9ubWVudCB2YXJpYWJsZS4gRW52IG5hbWVzIGFyZSB1bnByZWZpeGVkIGJlY2F1c2UgdGhlIGFwcCB0YWxrc1xuICogdG8gYSBzaW5nbGUgU01UUCBzZXJ2ZXIgKGUuZy4gU01UUDJHTyk6IGBTTVRQX0hPU1RgLCBgU01UUF9QT1JUYCxcbiAqIGBTTVRQX1NFQ1VSRWAsIGBTTVRQX1VTRVJgLCBgU01UUF9QQVNTV09SRGAsIHBsdXMgYEVNQUlMX0RPTUFJTmAgZm9yXG4gKiB0aGUgZGVyaXZlZCBzZW5kZXIncyBkb21haW4sIGBFTUFJTF9GUk9NYCBmb3IgYW4gZXhwbGljaXQgb3ZlcnJpZGUsXG4gKiBgRU1BSUxfU0VOREVSX1BPTElDWWAgZm9yIHRoZSBzZW5kZXIgcmVzdHJpY3Rpb24gbW9kZSwgYW5kXG4gKiBgRU1BSUxfT1VUQk9YX0RJUmAgZm9yIHRoZSBvdXRib3ggZGlyZWN0b3J5LlxuICpcbiAqIEBtb2R1bGVcbiAqL1xuaW1wb3J0IHsgcmVzb2x2ZSB9IGZyb20gXCJub2RlOnBhdGhcIjtcbmltcG9ydCB7IENvbmZpZ3VyYXRpb25FcnJvciwgVmFsaWRhdGlvbkVycm9yLCB0eXBlIEJhc2VQbHVnaW5Db25maWcgfSBmcm9tIFwiQGRhdGFicmlja3MvYXBwa2l0XCI7XG5pbXBvcnQgeyBvYmplY3QgfSBmcm9tIFwiQGRieC10b29scy9zaGFyZWQtY29yZVwiO1xuaW1wb3J0IHR5cGUgeyBKU09OU2NoZW1hNyB9IGZyb20gXCJqc29uLXNjaGVtYVwiO1xuaW1wb3J0IHR5cGUgeyBFbWFpbEJyYW5kIH0gZnJvbSBcIi4vYnJhbmRcIjtcbmltcG9ydCB7IHBhcnNlQWxsb3dlZFNlbmRlcnMgfSBmcm9tIFwiLi9zZW5kZXJcIjtcblxuLyoqIFNNVFAgc3VibWlzc2lvbiBwb3J0IHVzZWQgd2hlbiBub25lIGlzIGNvbmZpZ3VyZWQuICovXG5leHBvcnQgY29uc3QgREVGQVVMVF9TTVRQX1BPUlQgPSA1ODc7XG5cbi8qKiBTTVRQIHBvcnQgdGhhdCBpbXBsaWVzIGEgVExTLW9uLWNvbm5lY3Qgc29ja2V0IHJhdGhlciB0aGFuIFNUQVJUVExTLiAqL1xuZXhwb3J0IGNvbnN0IElNUExJQ0lUX1RMU19TTVRQX1BPUlQgPSA0NjU7XG5cbi8qKlxuICogSG93IHRoZSBzZW5kZXIgKGBGcm9tYCkgYWRkcmVzcyBpcyByZXN0cmljdGVkLlxuICpcbiAqIGBcImFsbG93bGlzdFwiYCBpcyB0aGUgZGVmYXVsdCBhbmQgdGhlIGRlbnktYnktZGVmYXVsdCBwb3N0dXJlOiBhIHNlbmQgaXNcbiAqIHBlcm1pdHRlZCBvbmx5IGZyb20gYW4gYWRkcmVzcyB0aGUgY29uZmlndXJhdGlvbiBuYW1lcywgZWl0aGVyIHRocm91Z2hcbiAqIHtAbGluayBFbWFpbFBsdWdpbkNvbmZpZy5hbGxvd2VkU2VuZGVyc30gb3IsIHdoZW4gdGhhdCBpcyBlbXB0eSwgdGhyb3VnaFxuICogdGhlIGNvbmZpZ3VyZWQgc2VuZGVyIHNvdXJjZSAoe0BsaW5rIEVtYWlsUGx1Z2luQ29uZmlnLmZyb219IGFzIGFuIGV4YWN0XG4gKiBhZGRyZXNzLCB7QGxpbmsgRW1haWxQbHVnaW5Db25maWcuZG9tYWlufSBhcyBhIGAqQGRvbWFpbmAgd2lsZGNhcmQpLlxuICpcbiAqIGBcInVucmVzdHJpY3RlZFwiYCBpcyB0aGUgZXhwbGljaXQgb3B0LW91dDogYW55IGBGcm9tYCBhIGNhbGxlciBzdXBwbGllcyBpc1xuICogYWNjZXB0ZWQuIE9ubHkgcmVhY2ggZm9yIGl0IHdoZW4gYW4gdXBzdHJlYW0gc3lzdGVtIGFscmVhZHkgdmV0cyB0aGVcbiAqIHNlbmRlci5cbiAqL1xuZXhwb3J0IHR5cGUgU2VuZGVyUG9saWN5ID0gXCJhbGxvd2xpc3RcIiB8IFwidW5yZXN0cmljdGVkXCI7XG5cbi8qKiBTTVRQIGNvbm5lY3Rpb24gKyBjcmVkZW50aWFscy4gQWxsIGZpZWxkcyBmYWxsIGJhY2sgdG8gZW52IHdoZW4gdW5zZXQuICovXG5leHBvcnQgaW50ZXJmYWNlIFNtdHBDb25maWcge1xuICAvKiogU01UUCBzZXJ2ZXIgaG9zdG5hbWUgKGBTTVRQX0hPU1RgKS4gKi9cbiAgaG9zdD86IHN0cmluZztcbiAgLyoqIFNNVFAgc2VydmVyIHBvcnQgKGBTTVRQX1BPUlRgKS4gRGVmYXVsdHMgdG8ge0BsaW5rIERFRkFVTFRfU01UUF9QT1JUfS4gKi9cbiAgcG9ydD86IG51bWJlcjtcbiAgLyoqXG4gICAqIFVzZSBhIFRMUy1vbi1jb25uZWN0IHNvY2tldCAoYFNNVFBfU0VDVVJFYCkuIERlZmF1bHRzIHRvXG4gICAqIGBwb3J0ID09PSBgIHtAbGluayBJTVBMSUNJVF9UTFNfU01UUF9QT1JUfS5cbiAgICovXG4gIHNlY3VyZT86IGJvb2xlYW47XG4gIC8qKiBTTVRQIGF1dGggdXNlcm5hbWUgKGBTTVRQX1VTRVJgKS4gKi9cbiAgdXNlcj86IHN0cmluZztcbiAgLyoqIFNNVFAgYXV0aCBwYXNzd29yZCAvIEFQSSBrZXkgKGBTTVRQX1BBU1NXT1JEYCkuICovXG4gIHBhc3N3b3JkPzogc3RyaW5nO1xufVxuXG4vKiogQXBwS2l0IGNvbmZpZyBhY2NlcHRlZCBieSB0aGUgZW1haWwgcGx1Z2luLiAqL1xuZXhwb3J0IGludGVyZmFjZSBFbWFpbFBsdWdpbkNvbmZpZyBleHRlbmRzIEJhc2VQbHVnaW5Db25maWcge1xuICAvKiogU01UUCBjb25uZWN0aW9uICsgY3JlZGVudGlhbHMuIE9taXQgdG8gcnVuIGluIGZpbGUvb3V0Ym94IG1vZGUuICovXG4gIHNtdHA/OiBTbXRwQ29uZmlnO1xuICAvKipcbiAgICogRG9tYWluIHVzZWQgdG8gYnVpbGQgdGhlIHNlbmRlciBhZGRyZXNzIGZyb20gdGhlIG9uLWJlaGFsZi1vZiB1c2VyXG4gICAqIChgPGxvY2FsLXBhcnQ+QDxkb21haW4+YCkuIEZhbGxzIGJhY2sgdG8gYEVNQUlMX0RPTUFJTmAuIFJlcXVpcmVkIGluXG4gICAqIFNNVFAgbW9kZSB1bmxlc3Mge0BsaW5rIGZyb219IGlzIHNldDsgb3B0aW9uYWwgaW4gZmlsZSBtb2RlICh0aGVcbiAgICogb3V0Ym94IGZhbGxzIGJhY2sgdG8gdGhlIHVzZXIncyBvd24gZW1haWwgYWRkcmVzcykuXG4gICAqL1xuICBkb21haW4/OiBzdHJpbmc7XG4gIC8qKlxuICAgKiBFeHBsaWNpdCBgRnJvbWAgYWRkcmVzcy4gV2hlbiBzZXQsIHRoZSBzZW5kZXIgaXMgdXNlZCB2ZXJiYXRpbSBhbmRcbiAgICogdGhlIHBlci11c2VyIGRlcml2YXRpb24gaXMgc2tpcHBlZC4gRmFsbHMgYmFjayB0byBgRU1BSUxfRlJPTWAuXG4gICAqL1xuICBmcm9tPzogc3RyaW5nO1xuICAvKipcbiAgICogRGlyZWN0b3J5IGZvciB0aGUgZmlsZS9vdXRib3ggZmFsbGJhY2suIEZhbGxzIGJhY2sgdG9cbiAgICogYEVNQUlMX09VVEJPWF9ESVJgLCB0aGVuIGA8Y3dkPi90bXBgLiBPbmx5IHVzZWQgd2hlbiBTTVRQXG4gICAqIGNyZWRlbnRpYWxzIGFyZSBhYnNlbnQuXG4gICAqL1xuICBvdXREaXI/OiBzdHJpbmc7XG4gIC8qKlxuICAgKiBBbGxvdy1saXN0IHJlc3RyaWN0aW5nIHRoZSBzZW5kZXIgKGBGcm9tYCkgYWRkcmVzcy4gRWFjaCBlbnRyeSBpcyBhblxuICAgKiBleGFjdCBhZGRyZXNzIChgdXNlckBkb21haW4uY29tYCksIGEgZG9tYWluIHdpbGRjYXJkIChgKkBkb21haW4uY29tYFxuICAgKiBvciB0aGUgYmFyZSBgZG9tYWluLmNvbWAsIG1hdGNoaW5nIGFueSBsb2NhbCBwYXJ0IG9uIHRoYXQgZG9tYWluKSwgb3JcbiAgICogYCpgIChhbnkpLiBBIHJlc29sdmVkIC8gY2hvc2VuIHNlbmRlciB0aGF0IG1hdGNoZXMgbm8gZW50cnkgaXNcbiAgICogcmVqZWN0ZWQgYXQgc2VuZCB0aW1lLiBBY2NlcHRzIGEgYHN0cmluZ1tdYCBvciBhIHNpbmdsZSBjb21tYS0gL1xuICAgKiB3aGl0ZXNwYWNlLXNlcGFyYXRlZCBzdHJpbmc7IGZhbGxzIGJhY2sgdG8gYEVNQUlMX0FMTE9XRURfU0VOREVSU2AuXG4gICAqIFdoZW4gZW1wdHksIHRoZSB7QGxpbmsgc2VuZGVyUG9saWN5fSBkZWNpZGVzIHdoYXQgaXMgcGVybWl0dGVkLlxuICAgKi9cbiAgYWxsb3dlZFNlbmRlcnM/OiBzdHJpbmcgfCBzdHJpbmdbXTtcbiAgLyoqXG4gICAqIEhvdyB0aGUgc2VuZGVyIGFkZHJlc3MgaXMgcmVzdHJpY3RlZCB3aGVuIHtAbGluayBhbGxvd2VkU2VuZGVyc30gaXNcbiAgICogZW1wdHkgKGBFTUFJTF9TRU5ERVJfUE9MSUNZYCkuIERlZmF1bHRzIHRvIGBcImFsbG93bGlzdFwiYCwgd2hpY2hcbiAgICogbmFycm93cyBzZW5kcyB0byB0aGUgY29uZmlndXJlZCBzZW5kZXIgc291cmNlLiBTZWVcbiAgICoge0BsaW5rIFNlbmRlclBvbGljeX0uXG4gICAqL1xuICBzZW5kZXJQb2xpY3k/OiBTZW5kZXJQb2xpY3k7XG4gIC8qKlxuICAgKiBPcHRpb25hbCBicmFuZCBzdHlsaW5nIChhY2NlbnQsIGZvbnQsIGhlYWRlciBsb2dvKSBhcHBsaWVkIHRvIHRoZVxuICAgKiByZW5kZXJlZCBIVE1MIG9mIGV2ZXJ5IG1lc3NhZ2UuIE9taXQgZm9yIHRoZSBuZXV0cmFsIGRlZmF1bHQgbGF5b3V0LlxuICAgKiBQYXNzIHtAbGluayBlbWFpbEJyYW5kRnJvbUNvbnRleHR9IHRvIGRlcml2ZSBpdCBmcm9tIGEgc2hhcmVkXG4gICAqIGBCcmFuZENvbnRleHRgLlxuICAgKi9cbiAgYnJhbmQ/OiBFbWFpbEJyYW5kO1xufVxuXG4vKiogQ29uZmlnIHNoYXJlZCBieSBib3RoIHJlc29sdmVkIG1vZGVzLiAqL1xuaW50ZXJmYWNlIFJlc29sdmVkU2VuZGVyIHtcbiAgLyoqIFNlbmRlciBkb21haW47IHByZXNlbnQgd2hlbmV2ZXIge0BsaW5rIGZyb219IGlzIGFic2VudC4gKi9cbiAgZG9tYWluPzogc3RyaW5nO1xuICAvKiogRXhwbGljaXQgc2VuZGVyIG92ZXJyaWRlOyBwcmVzZW50IHNraXBzIHBlci11c2VyIGRlcml2YXRpb24uICovXG4gIGZyb20/OiBzdHJpbmc7XG4gIC8qKlxuICAgKiBFZmZlY3RpdmUgc2VuZGVyIGFsbG93LWxpc3Q6IHRoZSBjb25maWd1cmVkIHBhdHRlcm5zIHdoZW4gYW55IHdlcmVcbiAgICogZ2l2ZW4sIGVsc2UgdGhlIHBhdHRlcm5zIGltcGxpZWQgYnkgdGhlIHNlbmRlciBzb3VyY2UgdW5kZXIgYW5cbiAgICogYFwiYWxsb3dsaXN0XCJgIHBvbGljeS4gRW1wdHkgbWVhbnMgdW5lbmZvcmNlYWJsZSAoYW4gYFwidW5yZXN0cmljdGVkXCJgXG4gICAqIHBvbGljeSwgb3IgYW4gb3V0Ym94IHdpdGggbm8gc2VuZGVyIHNvdXJjZSB0byBuYXJyb3cgdG8pLlxuICAgKi9cbiAgYWxsb3dlZFNlbmRlcnM6IHN0cmluZ1tdO1xuICAvKiogVGhlIHJlc3RyaWN0aW9uIG1vZGUgdGhlIGFsbG93LWxpc3Qgd2FzIHJlc29sdmVkIHVuZGVyLiAqL1xuICBzZW5kZXJQb2xpY3k6IFNlbmRlclBvbGljeTtcbiAgLyoqIEJyYW5kIHN0eWxpbmcgYXBwbGllZCB0byByZW5kZXJlZCBIVE1MOyBhYnNlbnQgZm9yIHRoZSBkZWZhdWx0IGxheW91dC4gKi9cbiAgYnJhbmQ/OiBFbWFpbEJyYW5kO1xufVxuXG4vKiogUmVzb2x2ZWQgY29uZmlnIGZvciByZWFsIFNNVFAgZGVsaXZlcnkuICovXG5leHBvcnQgaW50ZXJmYWNlIFJlc29sdmVkU210cENvbmZpZyBleHRlbmRzIFJlc29sdmVkU2VuZGVyIHtcbiAgbW9kZTogXCJzbXRwXCI7XG4gIGhvc3Q6IHN0cmluZztcbiAgcG9ydDogbnVtYmVyO1xuICBzZWN1cmU6IGJvb2xlYW47XG4gIGF1dGg6IHsgdXNlcjogc3RyaW5nOyBwYXNzOiBzdHJpbmcgfTtcbn1cblxuLyoqIFJlc29sdmVkIGNvbmZpZyBmb3IgdGhlIGZpbGUvb3V0Ym94IGZhbGxiYWNrIChubyBTTVRQIGNyZWRlbnRpYWxzKS4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgUmVzb2x2ZWRGaWxlQ29uZmlnIGV4dGVuZHMgUmVzb2x2ZWRTZW5kZXIge1xuICBtb2RlOiBcImZpbGVcIjtcbiAgLyoqIEFic29sdXRlIGRpcmVjdG9yeSBtZXNzYWdlcyBhcmUgd3JpdHRlbiB1bmRlci4gKi9cbiAgb3V0RGlyOiBzdHJpbmc7XG59XG5cbi8qKiBDb25jcmV0ZSwgdmFsaWRhdGVkIGNvbmZpZyB0aGUgcnVudGltZSBkaXNwYXRjaGVzIHRocm91Z2guICovXG5leHBvcnQgdHlwZSBSZXNvbHZlZEVtYWlsQ29uZmlnID0gUmVzb2x2ZWRTbXRwQ29uZmlnIHwgUmVzb2x2ZWRGaWxlQ29uZmlnO1xuXG4vKiogSlNPTiBTY2hlbWEgcHVibGlzaGVkIG9uIHRoZSBtYW5pZmVzdCdzIGBjb25maWcuc2NoZW1hYC4gKi9cbmV4cG9ydCBjb25zdCBFTUFJTF9DT05GSUdfU0NIRU1BOiBKU09OU2NoZW1hNyA9IHtcbiAgdHlwZTogXCJvYmplY3RcIixcbiAgcHJvcGVydGllczoge1xuICAgIHNtdHA6IHtcbiAgICAgIHR5cGU6IFwib2JqZWN0XCIsXG4gICAgICBkZXNjcmlwdGlvbjogXCJTTVRQIGNvbm5lY3Rpb24gYW5kIGNyZWRlbnRpYWxzIChlbnYgZmFsbGJhY2tzOiBTTVRQXyopLlwiLFxuICAgICAgcHJvcGVydGllczoge1xuICAgICAgICBob3N0OiB7IHR5cGU6IFwic3RyaW5nXCIsIGRlc2NyaXB0aW9uOiBcIlNNVFAgc2VydmVyIGhvc3RuYW1lIChTTVRQX0hPU1QpLlwiIH0sXG4gICAgICAgIHBvcnQ6IHtcbiAgICAgICAgICB0eXBlOiBcIm51bWJlclwiLFxuICAgICAgICAgIGRlc2NyaXB0aW9uOiBgU01UUCBzZXJ2ZXIgcG9ydCAoU01UUF9QT1JUKS4gRGVmYXVsdHMgdG8gJHtERUZBVUxUX1NNVFBfUE9SVH0uYCxcbiAgICAgICAgfSxcbiAgICAgICAgc2VjdXJlOiB7XG4gICAgICAgICAgdHlwZTogXCJib29sZWFuXCIsXG4gICAgICAgICAgZGVzY3JpcHRpb246IGBUTFMtb24tY29ubmVjdCBzb2NrZXQgKFNNVFBfU0VDVVJFKS4gRGVmYXVsdHMgdG8gcG9ydCA9PT0gJHtJTVBMSUNJVF9UTFNfU01UUF9QT1JUfS5gLFxuICAgICAgICB9LFxuICAgICAgICB1c2VyOiB7IHR5cGU6IFwic3RyaW5nXCIsIGRlc2NyaXB0aW9uOiBcIlNNVFAgYXV0aCB1c2VybmFtZSAoU01UUF9VU0VSKS5cIiB9LFxuICAgICAgICBwYXNzd29yZDoge1xuICAgICAgICAgIHR5cGU6IFwic3RyaW5nXCIsXG4gICAgICAgICAgZGVzY3JpcHRpb246IFwiU01UUCBhdXRoIHBhc3N3b3JkIC8gQVBJIGtleSAoU01UUF9QQVNTV09SRCkuXCIsXG4gICAgICAgIH0sXG4gICAgICB9LFxuICAgIH0sXG4gICAgZG9tYWluOiB7XG4gICAgICB0eXBlOiBcInN0cmluZ1wiLFxuICAgICAgZGVzY3JpcHRpb246XG4gICAgICAgIFwiRG9tYWluIGZvciB0aGUgZGVyaXZlZCBzZW5kZXIgYWRkcmVzcyAoPHVzZXItbG9jYWwtcGFydD5APGRvbWFpbj4pLiBGYWxscyBiYWNrIHRvIEVNQUlMX0RPTUFJTi5cIixcbiAgICB9LFxuICAgIGZyb206IHtcbiAgICAgIHR5cGU6IFwic3RyaW5nXCIsXG4gICAgICBkZXNjcmlwdGlvbjogXCJFeHBsaWNpdCBGcm9tIGFkZHJlc3M7IHNraXBzIHBlci11c2VyIGRlcml2YXRpb24uIEZhbGxzIGJhY2sgdG8gRU1BSUxfRlJPTS5cIixcbiAgICB9LFxuICAgIG91dERpcjoge1xuICAgICAgdHlwZTogXCJzdHJpbmdcIixcbiAgICAgIGRlc2NyaXB0aW9uOlxuICAgICAgICBcIkRpcmVjdG9yeSBmb3IgdGhlIGZpbGUvb3V0Ym94IGZhbGxiYWNrIHdoZW4gU01UUCBpcyB1bmNvbmZpZ3VyZWQuIEZhbGxzIGJhY2sgdG8gRU1BSUxfT1VUQk9YX0RJUiwgdGhlbiA8Y3dkPi90bXAuXCIsXG4gICAgfSxcbiAgICBhbGxvd2VkU2VuZGVyczoge1xuICAgICAgdHlwZTogXCJhcnJheVwiLFxuICAgICAgaXRlbXM6IHsgdHlwZTogXCJzdHJpbmdcIiB9LFxuICAgICAgZGVzY3JpcHRpb246XG4gICAgICAgICdBbGxvdy1saXN0IG9mIHBlcm1pdHRlZCBzZW5kZXIgKEZyb20pIHBhdHRlcm5zOiBleGFjdCBhZGRyZXNzZXMgKFwidXNlckBkb21haW4uY29tXCIpLCBkb21haW4gd2lsZGNhcmRzIChcIipAZG9tYWluLmNvbVwiKSwgb3IgXCIqXCIuIEFsc28gYWNjZXB0cyBhIGNvbW1hL3NwYWNlLXNlcGFyYXRlZCBzdHJpbmcuIEZhbGxzIGJhY2sgdG8gRU1BSUxfQUxMT1dFRF9TRU5ERVJTLiBXaGVuIGVtcHR5LCBzZW5kZXJQb2xpY3kgZGVjaWRlcy4nLFxuICAgIH0sXG4gICAgc2VuZGVyUG9saWN5OiB7XG4gICAgICB0eXBlOiBcInN0cmluZ1wiLFxuICAgICAgZW51bTogW1wiYWxsb3dsaXN0XCIsIFwidW5yZXN0cmljdGVkXCJdLFxuICAgICAgZGVzY3JpcHRpb246XG4gICAgICAgICdIb3cgdGhlIHNlbmRlciBpcyByZXN0cmljdGVkIHdoZW4gYWxsb3dlZFNlbmRlcnMgaXMgZW1wdHkgKEVNQUlMX1NFTkRFUl9QT0xJQ1kpLiBcImFsbG93bGlzdFwiIChkZWZhdWx0KSBuYXJyb3dzIHNlbmRzIHRvIHRoZSBjb25maWd1cmVkIHNlbmRlciBzb3VyY2U7IFwidW5yZXN0cmljdGVkXCIgYWNjZXB0cyBhbnkgRnJvbS4nLFxuICAgIH0sXG4gICAgYnJhbmQ6IHtcbiAgICAgIHR5cGU6IFwib2JqZWN0XCIsXG4gICAgICBkZXNjcmlwdGlvbjpcbiAgICAgICAgXCJCcmFuZCBzdHlsaW5nIGlubGluZWQgaW50byBldmVyeSByZW5kZXJlZCBtZXNzYWdlLiBPbWl0IGZvciB0aGUgbmV1dHJhbCBkZWZhdWx0IGxheW91dC5cIixcbiAgICAgIHByb3BlcnRpZXM6IHtcbiAgICAgICAgYWNjZW50OiB7XG4gICAgICAgICAgdHlwZTogXCJzdHJpbmdcIixcbiAgICAgICAgICBkZXNjcmlwdGlvbjogJ0hlYWRlci1iYW5kIGJhY2tncm91bmQgYW5kIGJvZHkgbGluayBjb2xvciAoZS5nLiBcIiNGRjM2MjFcIikuJyxcbiAgICAgICAgfSxcbiAgICAgICAgb25BY2NlbnQ6IHtcbiAgICAgICAgICB0eXBlOiBcInN0cmluZ1wiLFxuICAgICAgICAgIGRlc2NyaXB0aW9uOiBcIlRleHQgYW5kIGxvZ28gY29sb3IgcmVuZGVyZWQgb24gdGhlIGFjY2VudCBiYW5kLiBEZWZhdWx0cyB0byB3aGl0ZS5cIixcbiAgICAgICAgfSxcbiAgICAgICAgZm9udEZhbWlseToge1xuICAgICAgICAgIHR5cGU6IFwic3RyaW5nXCIsXG4gICAgICAgICAgZGVzY3JpcHRpb246ICdCb2R5IGZvbnQgc3RhY2sgKGUuZy4gXCJJbnRlciwgdWktc2Fucy1zZXJpZiwgc3lzdGVtLXVpLCBzYW5zLXNlcmlmXCIpLicsXG4gICAgICAgIH0sXG4gICAgICAgIG5hbWU6IHtcbiAgICAgICAgICB0eXBlOiBcInN0cmluZ1wiLFxuICAgICAgICAgIGRlc2NyaXB0aW9uOiBcIlByb2R1Y3QgLyBkaXNwbGF5IG5hbWUgdXNlZCBhcyB0aGUgaGVhZGVyIHRleHQgYW5kIHRoZSBsb2dvIGFsdCB0ZXh0LlwiLFxuICAgICAgICB9LFxuICAgICAgICBsb2dvVXJsOiB7XG4gICAgICAgICAgdHlwZTogXCJzdHJpbmdcIixcbiAgICAgICAgICBkZXNjcmlwdGlvbjpcbiAgICAgICAgICAgIFwiTG9nbyBpbWFnZSBmb3IgdGhlIGhlYWRlciBiYW5kLiBPbmx5IGFuIGh0dHAocyk6IG9yIGRhdGE6IFVSTCByZW5kZXJzOyBvdGhlciB2YWx1ZXMgYXJlIGRyb3BwZWQgYmVjYXVzZSBhIG1haWwgY2xpZW50IGNhbm5vdCBsb2FkIHRoZW0uXCIsXG4gICAgICAgIH0sXG4gICAgICB9LFxuICAgICAgcmVxdWlyZWQ6IFtcImFjY2VudFwiLCBcImZvbnRGYW1pbHlcIl0sXG4gICAgfSxcbiAgfSxcbn07XG5cbi8qKiBQYXJzZSB0aGUgYFNNVFBfU0VDVVJFYCBlbnYgLyBjb25maWcgZmxhZywgZGVmYXVsdGluZyBieSBwb3J0LiAqL1xuZnVuY3Rpb24gcmVzb2x2ZVNlY3VyZShmbGFnOiBib29sZWFuIHwgdW5kZWZpbmVkLCBwb3J0OiBudW1iZXIpOiBib29sZWFuIHtcbiAgaWYgKHR5cGVvZiBmbGFnID09PSBcImJvb2xlYW5cIikgcmV0dXJuIGZsYWc7XG4gIHJldHVybiBvYmplY3QudG9Cb29sZWFuKHByb2Nlc3MuZW52LlNNVFBfU0VDVVJFKSA/PyBwb3J0ID09PSBJTVBMSUNJVF9UTFNfU01UUF9QT1JUO1xufVxuXG4vKiogUGFyc2UgdGhlIGBFTUFJTF9TRU5ERVJfUE9MSUNZYCBlbnYgLyBjb25maWcgdmFsdWUsIGRlZmF1bHRpbmcgdG8gZGVueS1ieS1kZWZhdWx0LiAqL1xuZnVuY3Rpb24gcmVzb2x2ZVNlbmRlclBvbGljeShwb2xpY3k6IFNlbmRlclBvbGljeSB8IHVuZGVmaW5lZCk6IFNlbmRlclBvbGljeSB7XG4gIGNvbnN0IHJhdyA9IHBvbGljeSA/PyBwcm9jZXNzLmVudi5FTUFJTF9TRU5ERVJfUE9MSUNZPy50cmltKCkudG9Mb3dlckNhc2UoKTtcbiAgaWYgKHJhdyA9PT0gXCJ1bnJlc3RyaWN0ZWRcIikgcmV0dXJuIFwidW5yZXN0cmljdGVkXCI7XG4gIGlmIChyYXcgPT09IHVuZGVmaW5lZCB8fCByYXcgPT09IFwiXCIgfHwgcmF3ID09PSBcImFsbG93bGlzdFwiKSByZXR1cm4gXCJhbGxvd2xpc3RcIjtcbiAgdGhyb3cgVmFsaWRhdGlvbkVycm9yLmludmFsaWRWYWx1ZShcInNlbmRlclBvbGljeVwiLCByYXcsICdcImFsbG93bGlzdFwiIG9yIFwidW5yZXN0cmljdGVkXCInKTtcbn1cblxuLyoqXG4gKiBFZmZlY3RpdmUgYWxsb3ctbGlzdCBmb3IgYW4gYFwiYWxsb3dsaXN0XCJgIHBvbGljeSB3aXRoIG5vIGV4cGxpY2l0IHBhdHRlcm5zOlxuICogdGhlIHNlbmRlciBzb3VyY2UgaXRzZWxmLCBzbyBhIGNvbmZpZ3VyZWQgZG9tYWluIG9yIGZpeGVkIGFkZHJlc3MgaXMgdGhlXG4gKiBib3VuZGFyeSByYXRoZXIgdGhhbiBcImFueXRoaW5nIGdvZXNcIi4gWWllbGRzIG5vdGhpbmcgd2hlbiBuZWl0aGVyIGlzIHNldCxcbiAqIHdoaWNoIG9ubHkgaGFwcGVucyBpbiBvdXRib3ggbW9kZSAod2hlcmUgdGhlIHNlbmRlciBmYWxscyBiYWNrIHRvIHRoZVxuICogb24tYmVoYWxmLW9mIHVzZXIncyBvd24gYWRkcmVzcyBhbmQgY2Fubm90IGJlIGVudW1lcmF0ZWQgdXAgZnJvbnQpLlxuICovXG5mdW5jdGlvbiBpbXBsaWVkU2VuZGVyUGF0dGVybnMoZG9tYWluOiBzdHJpbmcgfCB1bmRlZmluZWQsIGZyb206IHN0cmluZyB8IHVuZGVmaW5lZCk6IHN0cmluZ1tdIHtcbiAgY29uc3QgcGF0dGVybnM6IHN0cmluZ1tdID0gW107XG4gIGlmIChmcm9tKSBwYXR0ZXJucy5wdXNoKGZyb20udHJpbSgpLnRvTG93ZXJDYXNlKCkpO1xuICBpZiAoZG9tYWluKSBwYXR0ZXJucy5wdXNoKGAqQCR7ZG9tYWluLnRyaW0oKS50b0xvd2VyQ2FzZSgpfWApO1xuICByZXR1cm4gcGF0dGVybnM7XG59XG5cbi8qKiBXaGV0aGVyIGBFTUFJTF9PVVRCT1hfTU9ERWAgZXhwbGljaXRseSBvcHRzIGludG8gdGhlIGZpbGUvb3V0Ym94IGZhbGxiYWNrLiAqL1xuZnVuY3Rpb24gaXNPdXRib3hNb2RlRW5hYmxlZCgpOiBib29sZWFuIHtcbiAgcmV0dXJuIG9iamVjdC50b0Jvb2xlYW4ocHJvY2Vzcy5lbnYuRU1BSUxfT1VUQk9YX01PREUpID8/IGZhbHNlO1xufVxuXG5jb25zdCBTTVRQX1JFUVVJUkVEX0ZJRUxEUyA9IFtcIlNNVFBfSE9TVFwiLCBcIlNNVFBfVVNFUlwiLCBcIlNNVFBfUEFTU1dPUkRcIl0gYXMgY29uc3Q7XG5cbi8qKiBMaXN0IGVudiBrZXlzIGZvciBTTVRQIGZpZWxkcyB0aGF0IGFyZSB1bnNldCBpbiB0aGUgcmVzb2x2ZWQgY3JlZGVudGlhbCBzZXQuICovXG5mdW5jdGlvbiBtaXNzaW5nU210cEZpZWxkcyhcbiAgaG9zdDogc3RyaW5nIHwgdW5kZWZpbmVkLFxuICB1c2VyOiBzdHJpbmcgfCB1bmRlZmluZWQsXG4gIHBhc3M6IHN0cmluZyB8IHVuZGVmaW5lZCxcbik6IHN0cmluZ1tdIHtcbiAgY29uc3QgdmFsdWVzID0gW2hvc3QsIHVzZXIsIHBhc3NdIGFzIGNvbnN0O1xuICByZXR1cm4gU01UUF9SRVFVSVJFRF9GSUVMRFMuZmlsdGVyKChfLCBpbmRleCkgPT4gIXZhbHVlc1tpbmRleF0pO1xufVxuXG4vKipcbiAqIFJlc29sdmUgcGx1Z2luIGNvbmZpZyBvdmVyIGVudmlyb25tZW50IGRlZmF1bHRzLlxuICpcbiAqIFdoZW4gU01UUCBob3N0ICsgdXNlciArIHBhc3N3b3JkIGFyZSBhbGwgcHJlc2VudCwgcmV0dXJucyBgbW9kZTpcbiAqIFwic210cFwiYCBmb3IgcmVhbCBkZWxpdmVyeSAoYW5kIHRocm93cyBpZiBubyBzZW5kZXIgc291cmNlIC0gZG9tYWluIG9yXG4gKiBmcm9tIC0gaXMgY29uZmlndXJlZCwgc2luY2UgU01UUCBjYW4ndCBkZXJpdmUgb25lIGZyb20gbm90aGluZykuXG4gKiBXaGVuIGFsbCB0aHJlZSBhcmUgYWJzZW50IGFuZCBgRU1BSUxfT1VUQk9YX01PREVgIGlzIGVuYWJsZWQsIHJldHVybnNcbiAqIGBtb2RlOiBcImZpbGVcImAgc28gdGhlIHJ1bnRpbWUgd3JpdGVzIG1lc3NhZ2VzIHRvIHRoZSBvdXRib3ggZGlyZWN0b3J5XG4gKiBmb3IgbG9jYWwgdGVzdGluZzsgaW4gdGhhdCBtb2RlIGEgc2VuZGVyIHNvdXJjZSBpcyBvcHRpb25hbCAodGhlXG4gKiBvdXRib3ggZmFsbHMgYmFjayB0byB0aGUgT0JPIHVzZXIncyBvd24gYWRkcmVzcykuIFBhcnRpYWwgU01UUFxuICogY29uZmlndXJhdGlvbiBvciBhIHNlbmQgd2l0aCBubyBjcmVkZW50aWFscyBhbmQgbm8gb3V0Ym94IG9wdC1pblxuICogdGhyb3dzLlxuICpcbiAqIFRoZSBzZW5kZXIgYWxsb3ctbGlzdCBpcyByZXNvbHZlZCBoZXJlIHRvbzogdW5kZXIgdGhlIGRlZmF1bHRcbiAqIGBcImFsbG93bGlzdFwiYCBwb2xpY3kgYW4gZW1wdHkgbGlzdCBpcyBmaWxsZWQgaW4gZnJvbSB0aGUgc2VuZGVyIHNvdXJjZSxcbiAqIHNvIGEgZGVwbG95bWVudCB0aGF0IG9ubHkgc2V0cyBgRU1BSUxfRE9NQUlOYCBzdGlsbCByZWplY3RzIGEgYEZyb21gIG9uXG4gKiBhbnkgb3RoZXIgZG9tYWluLiBTZWUge0BsaW5rIFNlbmRlclBvbGljeX0uXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiByZXNvbHZlRW1haWxDb25maWcoY29uZmlnOiBFbWFpbFBsdWdpbkNvbmZpZyA9IHt9KTogUmVzb2x2ZWRFbWFpbENvbmZpZyB7XG4gIGNvbnN0IHNtdHAgPSBjb25maWcuc210cCA/PyB7fTtcbiAgY29uc3QgaG9zdCA9IHNtdHAuaG9zdCA/PyBwcm9jZXNzLmVudi5TTVRQX0hPU1Q7XG4gIGNvbnN0IHVzZXIgPSBzbXRwLnVzZXIgPz8gcHJvY2Vzcy5lbnYuU01UUF9VU0VSO1xuICBjb25zdCBwYXNzID0gc210cC5wYXNzd29yZCA/PyBwcm9jZXNzLmVudi5TTVRQX1BBU1NXT1JEO1xuICBjb25zdCBkb21haW4gPSBjb25maWcuZG9tYWluID8/IHByb2Nlc3MuZW52LkVNQUlMX0RPTUFJTjtcbiAgY29uc3QgZnJvbSA9IGNvbmZpZy5mcm9tID8/IHByb2Nlc3MuZW52LkVNQUlMX0ZST007XG4gIGNvbnN0IHNlbmRlclBvbGljeSA9IHJlc29sdmVTZW5kZXJQb2xpY3koY29uZmlnLnNlbmRlclBvbGljeSk7XG4gIGNvbnN0IGNvbmZpZ3VyZWRTZW5kZXJzID0gcGFyc2VBbGxvd2VkU2VuZGVycyhcbiAgICBjb25maWcuYWxsb3dlZFNlbmRlcnMgPz8gcHJvY2Vzcy5lbnYuRU1BSUxfQUxMT1dFRF9TRU5ERVJTLFxuICApO1xuICBjb25zdCBhbGxvd2VkU2VuZGVycyA9XG4gICAgY29uZmlndXJlZFNlbmRlcnMubGVuZ3RoID4gMCB8fCBzZW5kZXJQb2xpY3kgPT09IFwidW5yZXN0cmljdGVkXCJcbiAgICAgID8gY29uZmlndXJlZFNlbmRlcnNcbiAgICAgIDogaW1wbGllZFNlbmRlclBhdHRlcm5zKGRvbWFpbiwgZnJvbSk7XG4gIGNvbnN0IHNlbmRlcjogUmVzb2x2ZWRTZW5kZXIgPSB7XG4gICAgLi4uKGRvbWFpbiA/IHsgZG9tYWluIH0gOiB7fSksXG4gICAgLi4uKGZyb20gPyB7IGZyb20gfSA6IHt9KSxcbiAgICBhbGxvd2VkU2VuZGVycyxcbiAgICBzZW5kZXJQb2xpY3ksXG4gICAgLi4uKGNvbmZpZy5icmFuZCA/IHsgYnJhbmQ6IGNvbmZpZy5icmFuZCB9IDoge30pLFxuICB9O1xuXG4gIGNvbnN0IGhhc0FsbFNtdHAgPSBCb29sZWFuKGhvc3QgJiYgdXNlciAmJiBwYXNzKTtcbiAgY29uc3QgaGFzQW55U210cCA9IEJvb2xlYW4oaG9zdCB8fCB1c2VyIHx8IHBhc3MpO1xuXG4gIGlmIChoYXNBbnlTbXRwICYmICFoYXNBbGxTbXRwKSB7XG4gICAgdGhyb3cgVmFsaWRhdGlvbkVycm9yLm1pc3NpbmdFbnZWYXJzKG1pc3NpbmdTbXRwRmllbGRzKGhvc3QsIHVzZXIsIHBhc3MpKTtcbiAgfVxuXG4gIGlmIChoYXNBbGxTbXRwKSB7XG4gICAgaWYgKCFkb21haW4gJiYgIWZyb20pIHtcbiAgICAgIHRocm93IENvbmZpZ3VyYXRpb25FcnJvci5yZXNvdXJjZU5vdEZvdW5kKFxuICAgICAgICBcIkVtYWlsIHNlbmRlciBzb3VyY2VcIixcbiAgICAgICAgXCJTZXQgRU1BSUxfRE9NQUlOIHRvIGRlcml2ZSA8dXNlci1sb2NhbC1wYXJ0PkA8ZG9tYWluPiwgb3IgRU1BSUxfRlJPTSBmb3IgYSBmaXhlZCBhZGRyZXNzLlwiLFxuICAgICAgKTtcbiAgICB9XG4gICAgY29uc3QgcG9ydFJhdyA9IHNtdHAucG9ydCA/PyBOdW1iZXIocHJvY2Vzcy5lbnYuU01UUF9QT1JUKTtcbiAgICBjb25zdCBwb3J0ID0gTnVtYmVyLmlzRmluaXRlKHBvcnRSYXcpICYmIHBvcnRSYXcgPyBOdW1iZXIocG9ydFJhdykgOiBERUZBVUxUX1NNVFBfUE9SVDtcbiAgICByZXR1cm4ge1xuICAgICAgbW9kZTogXCJzbXRwXCIsXG4gICAgICBob3N0OiBob3N0ISxcbiAgICAgIHBvcnQsXG4gICAgICBzZWN1cmU6IHJlc29sdmVTZWN1cmUoc210cC5zZWN1cmUsIHBvcnQpLFxuICAgICAgYXV0aDogeyB1c2VyOiB1c2VyISwgcGFzczogcGFzcyEgfSxcbiAgICAgIC4uLnNlbmRlcixcbiAgICB9O1xuICB9XG5cbiAgaWYgKCFpc091dGJveE1vZGVFbmFibGVkKCkpIHtcbiAgICB0aHJvdyBDb25maWd1cmF0aW9uRXJyb3IuaW52YWxpZENvbm5lY3Rpb24oXG4gICAgICBcIlNNVFBcIixcbiAgICAgIGBTZXQgJHtTTVRQX1JFUVVJUkVEX0ZJRUxEUy5qb2luKFwiLCBcIil9LCBvciBFTUFJTF9PVVRCT1hfTU9ERT0xIHRvIHdyaXRlIG1lc3NhZ2VzIHRvIGEgbG9jYWwgb3V0Ym94IGluc3RlYWQuYCxcbiAgICApO1xuICB9XG5cbiAgY29uc3Qgb3V0RGlyID0gcmVzb2x2ZShcbiAgICBjb25maWcub3V0RGlyID8/IHByb2Nlc3MuZW52LkVNQUlMX09VVEJPWF9ESVIgPz8gcmVzb2x2ZShwcm9jZXNzLmN3ZCgpLCBcInRtcFwiKSxcbiAgKTtcbiAgcmV0dXJuIHsgbW9kZTogXCJmaWxlXCIsIG91dERpciwgLi4uc2VuZGVyIH07XG59XG4iXX0=
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interceptor defaults and hard payload caps for the email plugin.
|
|
3
|
+
*
|
|
4
|
+
* The execution settings are what the runtime's executor hands
|
|
5
|
+
* `Plugin.execute()`, kept here rather than at the call sites so the
|
|
6
|
+
* caching / retry / timeout posture of every outbound operation is
|
|
7
|
+
* reviewable in one place. The caps bound the one unbounded input the
|
|
8
|
+
* plugin accepts: a model-drafted message with attachments.
|
|
9
|
+
*
|
|
10
|
+
* @module
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* The `PluginExecuteConfig` slice this package sets. Mirrored structurally
|
|
14
|
+
* because AppKit's `PluginExecuteConfig` lives behind a subpath its `exports`
|
|
15
|
+
* map does not publish, so the nominal type cannot be imported. Written as a
|
|
16
|
+
* type alias rather than an interface so it stays assignable to the nominal
|
|
17
|
+
* type's index signature.
|
|
18
|
+
*/
|
|
19
|
+
export type EmailExecuteConfig = {
|
|
20
|
+
cache?: {
|
|
21
|
+
enabled?: boolean;
|
|
22
|
+
ttl?: number;
|
|
23
|
+
cacheKey?: (string | number | object)[];
|
|
24
|
+
};
|
|
25
|
+
retry?: {
|
|
26
|
+
enabled?: boolean;
|
|
27
|
+
attempts?: number;
|
|
28
|
+
initialDelay?: number;
|
|
29
|
+
maxDelay?: number;
|
|
30
|
+
};
|
|
31
|
+
timeout?: number;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* The `PluginExecutionSettings` shape accepted by AppKit's `Plugin.execute()`.
|
|
35
|
+
* Mirrored structurally for the same reason as {@link EmailExecuteConfig}.
|
|
36
|
+
*/
|
|
37
|
+
export type EmailExecutionSettings = {
|
|
38
|
+
default: EmailExecuteConfig;
|
|
39
|
+
user?: EmailExecuteConfig;
|
|
40
|
+
};
|
|
41
|
+
/** Ceiling on how long a single SMTP conversation may take. */
|
|
42
|
+
export declare const SEND_TIMEOUT_MS = 30000;
|
|
43
|
+
/** Ceiling on the SMTP handshake performed at plugin setup. */
|
|
44
|
+
export declare const VERIFY_TIMEOUT_MS = 15000;
|
|
45
|
+
/** Attempts allowed for the setup-time SMTP handshake, including the first. */
|
|
46
|
+
export declare const VERIFY_ATTEMPTS = 3;
|
|
47
|
+
/** Largest single attachment accepted, in decoded bytes (10 MiB). */
|
|
48
|
+
export declare const MAX_ATTACHMENT_BYTES = 10485760;
|
|
49
|
+
/** Largest combined attachment payload accepted, in decoded bytes (20 MiB). */
|
|
50
|
+
export declare const MAX_ATTACHMENTS_TOTAL_BYTES = 20971520;
|
|
51
|
+
/** Largest number of attachments accepted on one message. */
|
|
52
|
+
export declare const MAX_ATTACHMENT_COUNT = 20;
|
|
53
|
+
/** Largest markdown body accepted, in characters. */
|
|
54
|
+
export declare const MAX_BODY_CHARS = 200000;
|
|
55
|
+
/** Execution settings for a send (SMTP dispatch or an outbox write). */
|
|
56
|
+
export declare const EMAIL_SEND_SETTINGS: EmailExecutionSettings;
|
|
57
|
+
/** Execution settings for the setup-time SMTP connectivity check. */
|
|
58
|
+
export declare const EMAIL_VERIFY_SETTINGS: EmailExecutionSettings;
|
|
59
|
+
/** Execution settings for the sender-options lookup. */
|
|
60
|
+
export declare const EMAIL_SENDERS_SETTINGS: EmailExecutionSettings;
|