@dbx-tools/email 0.3.44 → 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
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AppKit plugin (registered name: `email`) that owns the SMTP runtime
|
|
3
|
+
* for outbound mail. Registering it validates the SMTP configuration
|
|
4
|
+
* and verifies connectivity at startup, so a bad host / credential
|
|
5
|
+
* surfaces in the boot logs instead of on the first approved send, and
|
|
6
|
+
* installs this plugin's `execute()` as the runtime's executor so every
|
|
7
|
+
* send picks up AppKit's retry / timeout / telemetry chain.
|
|
8
|
+
*
|
|
9
|
+
* The plugin is also a `ToolProvider`, so an AppKit agent can reach an
|
|
10
|
+
* `email.send` tool directly; the {@link emailTool} export is the same
|
|
11
|
+
* capability for a Mastra agent. Both share the transport primed here,
|
|
12
|
+
* and {@link sendEmail} is available to non-agent callers.
|
|
13
|
+
*
|
|
14
|
+
* Configuration is the manifest-published {@link EmailPluginConfig}
|
|
15
|
+
* (SMTP host/port/credentials, sender domain or explicit `from`, the
|
|
16
|
+
* sender policy, and an optional `allowedSenders` restriction), with
|
|
17
|
+
* unprefixed `SMTP_*` / `EMAIL_*` environment fallbacks.
|
|
18
|
+
*
|
|
19
|
+
* The plugin mounts one route under its base path (`/api/email`):
|
|
20
|
+
* `GET /senders` returns the permitted `From` options for the calling
|
|
21
|
+
* user, so a compose UI can offer them in a dropdown.
|
|
22
|
+
*
|
|
23
|
+
* @module
|
|
24
|
+
*/
|
|
25
|
+
import { Plugin, type IAppRouter } from "@databricks/appkit";
|
|
26
|
+
import { type AgentToolDefinition, type ToolProvider } from "@databricks/appkit/beta";
|
|
27
|
+
import { type EmailMessage, type EmailResult, type EmailSenders } from "@dbx-tools/shared-email";
|
|
28
|
+
import { type EmailPluginConfig } from "./config.js";
|
|
29
|
+
/**
|
|
30
|
+
* AppKit plugin that configures and verifies the SMTP transport used by
|
|
31
|
+
* the `send_email` tool, and exposes sending as an AppKit agent tool.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```ts
|
|
35
|
+
* import { createApp, server } from "@databricks/appkit";
|
|
36
|
+
* import { plugin as emailPlugin } from "@dbx-tools/email";
|
|
37
|
+
*
|
|
38
|
+
* await createApp({
|
|
39
|
+
* plugins: [
|
|
40
|
+
* server(),
|
|
41
|
+
* emailPlugin.email({
|
|
42
|
+
* smtp: { host: "smtp.example.com", user: "apikey", password: process.env.SMTP_KEY },
|
|
43
|
+
* domain: "mail.example.com",
|
|
44
|
+
* }),
|
|
45
|
+
* ],
|
|
46
|
+
* });
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
export declare class EmailPlugin extends Plugin<EmailPluginConfig> implements ToolProvider {
|
|
50
|
+
static manifest: {
|
|
51
|
+
name: "email";
|
|
52
|
+
displayName: string;
|
|
53
|
+
description: string;
|
|
54
|
+
stability: "beta";
|
|
55
|
+
resources: {
|
|
56
|
+
required: never[];
|
|
57
|
+
optional: never[];
|
|
58
|
+
};
|
|
59
|
+
config: {
|
|
60
|
+
schema: import("json-schema").JSONSchema7;
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* The tool this plugin offers to an AppKit agent.
|
|
65
|
+
*
|
|
66
|
+
* Not `autoInheritable`: a send is irreversible and leaves the workspace,
|
|
67
|
+
* so it must only appear in an agent that asked for it and accepted the
|
|
68
|
+
* sender policy that comes with it.
|
|
69
|
+
*
|
|
70
|
+
* `execute` re-parses its arguments with the local schema: AppKit validates
|
|
71
|
+
* against the same schema first, but re-parsing is what gives the body typed
|
|
72
|
+
* arguments instead of `unknown`.
|
|
73
|
+
*/
|
|
74
|
+
private readonly tools;
|
|
75
|
+
/**
|
|
76
|
+
* Prime the shared runtime from this plugin's config (over env), route the
|
|
77
|
+
* tools' sends through this plugin's interceptor chain, and log the
|
|
78
|
+
* effective sender policy so an active restriction is obvious at boot. In
|
|
79
|
+
* SMTP mode, fail setup when the transport cannot be verified: a bad host
|
|
80
|
+
* or credential is a deploy-time mistake and should stop the app rather
|
|
81
|
+
* than wait for a user to approve a send that cannot work. With no SMTP
|
|
82
|
+
* credentials the runtime is in file/outbox mode (only when
|
|
83
|
+
* `EMAIL_OUTBOX_MODE` is set), logged loudly here so it is obvious mail
|
|
84
|
+
* is being written to disk rather than sent.
|
|
85
|
+
*/
|
|
86
|
+
setup(): Promise<void>;
|
|
87
|
+
/** Close the SMTP connection pool. Idempotent. */
|
|
88
|
+
shutdown(): Promise<void>;
|
|
89
|
+
/**
|
|
90
|
+
* Abort in-flight work. AppKit's graceful shutdown only invokes this hook -
|
|
91
|
+
* it never calls {@link shutdown} - so the SMTP pool is closed from here or
|
|
92
|
+
* it leaks at SIGTERM. The teardown is synchronous and idempotent, so the
|
|
93
|
+
* un-awaited call costs nothing.
|
|
94
|
+
*/
|
|
95
|
+
abortActiveOperations(): void;
|
|
96
|
+
/**
|
|
97
|
+
* Expose the sender-options lookup so UI compose views can populate a
|
|
98
|
+
* `From` dropdown from the configured allow-list. Mounted under the
|
|
99
|
+
* plugin base path, i.e. `GET /api/email/senders`. Runs in the OBO
|
|
100
|
+
* user scope so domain wildcards resolve against the caller's own
|
|
101
|
+
* local part.
|
|
102
|
+
*/
|
|
103
|
+
injectRoutes(router: IAppRouter): void;
|
|
104
|
+
exports(): {
|
|
105
|
+
/**
|
|
106
|
+
* Send a message immediately from `from` through the shared
|
|
107
|
+
* transport, bypassing the approval flow. For agent-driven sends
|
|
108
|
+
* use {@link emailTool} instead.
|
|
109
|
+
*/
|
|
110
|
+
sendEmail: (message: EmailMessage, from: string, signal?: AbortSignal) => Promise<EmailResult>;
|
|
111
|
+
/**
|
|
112
|
+
* Sender options for the current user (the `GET /senders` payload).
|
|
113
|
+
* AppKit wraps this with `asUser(req)` for OBO scoping.
|
|
114
|
+
*/
|
|
115
|
+
listSenders: () => Promise<EmailSenders>;
|
|
116
|
+
};
|
|
117
|
+
/** AppKit `ToolProvider`: the tool definitions offered to an agent. */
|
|
118
|
+
getAgentTools(): AgentToolDefinition[];
|
|
119
|
+
/**
|
|
120
|
+
* AppKit `ToolProvider`: run one tool call. Arguments are validated against
|
|
121
|
+
* the tool's schema first, and a validation failure comes back as an
|
|
122
|
+
* LLM-friendly string so the model can correct itself on the next turn.
|
|
123
|
+
*/
|
|
124
|
+
executeAgentTool(name: string, args: unknown, signal?: AbortSignal): Promise<unknown>;
|
|
125
|
+
/**
|
|
126
|
+
* Send one message, resolving the sender for the caller in scope when
|
|
127
|
+
* `from` is not pinned. The interceptor chain is applied inside
|
|
128
|
+
* {@link sendEmail} through the executor installed at setup, so this must
|
|
129
|
+
* not wrap it again.
|
|
130
|
+
*/
|
|
131
|
+
private send;
|
|
132
|
+
/** Run the sender-options lookup through the plugin's interceptor chain. */
|
|
133
|
+
private executeListSenders;
|
|
134
|
+
/**
|
|
135
|
+
* Compute the `From` options offered to the current user: the concrete
|
|
136
|
+
* addresses the effective allow-list permits (domain wildcards expanded
|
|
137
|
+
* against the OBO user's local part), the default among them, and
|
|
138
|
+
* whether the list is an enforced restriction. See
|
|
139
|
+
* {@link listSenderOptions}.
|
|
140
|
+
*/
|
|
141
|
+
private listSenders;
|
|
142
|
+
/** The `From` a send defaults to for the caller in scope. */
|
|
143
|
+
private resolveSender;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Register the email plugin.
|
|
147
|
+
*
|
|
148
|
+
* @example
|
|
149
|
+
* ```ts
|
|
150
|
+
* import { createApp, server } from "@databricks/appkit";
|
|
151
|
+
* import { brand, plugin as emailPlugin } from "@dbx-tools/email";
|
|
152
|
+
*
|
|
153
|
+
* await createApp({
|
|
154
|
+
* plugins: [
|
|
155
|
+
* server(),
|
|
156
|
+
* emailPlugin.email({
|
|
157
|
+
* domain: "mail.example.com",
|
|
158
|
+
* allowedSenders: ["*@mail.example.com"],
|
|
159
|
+
* brand: brand.defaultEmailBrand,
|
|
160
|
+
* }),
|
|
161
|
+
* ],
|
|
162
|
+
* });
|
|
163
|
+
* ```
|
|
164
|
+
*/
|
|
165
|
+
export declare const email: import("@databricks/appkit").ToPlugin<typeof EmailPlugin, EmailPluginConfig, "email">;
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AppKit plugin (registered name: `email`) that owns the SMTP runtime
|
|
3
|
+
* for outbound mail. Registering it validates the SMTP configuration
|
|
4
|
+
* and verifies connectivity at startup, so a bad host / credential
|
|
5
|
+
* surfaces in the boot logs instead of on the first approved send, and
|
|
6
|
+
* installs this plugin's `execute()` as the runtime's executor so every
|
|
7
|
+
* send picks up AppKit's retry / timeout / telemetry chain.
|
|
8
|
+
*
|
|
9
|
+
* The plugin is also a `ToolProvider`, so an AppKit agent can reach an
|
|
10
|
+
* `email.send` tool directly; the {@link emailTool} export is the same
|
|
11
|
+
* capability for a Mastra agent. Both share the transport primed here,
|
|
12
|
+
* and {@link sendEmail} is available to non-agent callers.
|
|
13
|
+
*
|
|
14
|
+
* Configuration is the manifest-published {@link EmailPluginConfig}
|
|
15
|
+
* (SMTP host/port/credentials, sender domain or explicit `from`, the
|
|
16
|
+
* sender policy, and an optional `allowedSenders` restriction), with
|
|
17
|
+
* unprefixed `SMTP_*` / `EMAIL_*` environment fallbacks.
|
|
18
|
+
*
|
|
19
|
+
* The plugin mounts one route under its base path (`/api/email`):
|
|
20
|
+
* `GET /senders` returns the permitted `From` options for the calling
|
|
21
|
+
* user, so a compose UI can offer them in a dropdown.
|
|
22
|
+
*
|
|
23
|
+
* @module
|
|
24
|
+
*/
|
|
25
|
+
import { AuthenticationError, ConfigurationError, ConnectionError, ExecutionError, getExecutionContext, Plugin, toPlugin, ValidationError, } from "@databricks/appkit";
|
|
26
|
+
import { defineTool, executeFromRegistry, toolsFromRegistry, } from "@databricks/appkit/beta";
|
|
27
|
+
import { log } from "@dbx-tools/shared-core";
|
|
28
|
+
import { email as emailWire, } from "@dbx-tools/shared-email";
|
|
29
|
+
import { EMAIL_CONFIG_SCHEMA } from "./config.js";
|
|
30
|
+
import { EMAIL_SENDERS_SETTINGS, EMAIL_VERIFY_SETTINGS } from "./defaults.js";
|
|
31
|
+
import { isSenderAllowed, listSenderOptions, resolveSenderAddress } from "./sender.js";
|
|
32
|
+
import { SEND_EMAIL_DESCRIPTION } from "./tool.js";
|
|
33
|
+
import { getEmailRuntime, resetEmailRuntime, sendEmail, setEmailExecutor, verifyEmailTransport, } from "./transport.js";
|
|
34
|
+
/** Mount-relative route (under `/api/email`) for the sender-options lookup. */
|
|
35
|
+
const SENDERS_ROUTE = "/senders";
|
|
36
|
+
/** Registry key of the agent tool, which agents address as `email.send`. */
|
|
37
|
+
const SEND_TOOL = "send";
|
|
38
|
+
const logger = log.logger("email");
|
|
39
|
+
/**
|
|
40
|
+
* AppKit plugin that configures and verifies the SMTP transport used by
|
|
41
|
+
* the `send_email` tool, and exposes sending as an AppKit agent tool.
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```ts
|
|
45
|
+
* import { createApp, server } from "@databricks/appkit";
|
|
46
|
+
* import { plugin as emailPlugin } from "@dbx-tools/email";
|
|
47
|
+
*
|
|
48
|
+
* await createApp({
|
|
49
|
+
* plugins: [
|
|
50
|
+
* server(),
|
|
51
|
+
* emailPlugin.email({
|
|
52
|
+
* smtp: { host: "smtp.example.com", user: "apikey", password: process.env.SMTP_KEY },
|
|
53
|
+
* domain: "mail.example.com",
|
|
54
|
+
* }),
|
|
55
|
+
* ],
|
|
56
|
+
* });
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
export class EmailPlugin extends Plugin {
|
|
60
|
+
static manifest = {
|
|
61
|
+
name: "email",
|
|
62
|
+
displayName: "Email",
|
|
63
|
+
description: "Sends approval-gated email over SMTP, with the sender derived from " +
|
|
64
|
+
"the on-behalf-of user's address on a configured domain.",
|
|
65
|
+
stability: "beta",
|
|
66
|
+
resources: {
|
|
67
|
+
required: [],
|
|
68
|
+
optional: [],
|
|
69
|
+
},
|
|
70
|
+
config: { schema: EMAIL_CONFIG_SCHEMA },
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* The tool this plugin offers to an AppKit agent.
|
|
74
|
+
*
|
|
75
|
+
* Not `autoInheritable`: a send is irreversible and leaves the workspace,
|
|
76
|
+
* so it must only appear in an agent that asked for it and accepted the
|
|
77
|
+
* sender policy that comes with it.
|
|
78
|
+
*
|
|
79
|
+
* `execute` re-parses its arguments with the local schema: AppKit validates
|
|
80
|
+
* against the same schema first, but re-parsing is what gives the body typed
|
|
81
|
+
* arguments instead of `unknown`.
|
|
82
|
+
*/
|
|
83
|
+
tools = {
|
|
84
|
+
[SEND_TOOL]: defineTool({
|
|
85
|
+
description: SEND_EMAIL_DESCRIPTION,
|
|
86
|
+
schema: emailWire.emailMessageSchema,
|
|
87
|
+
annotations: { effect: "write", requiresUserContext: true },
|
|
88
|
+
autoInheritable: false,
|
|
89
|
+
execute: async (args, signal) => this.send(emailWire.emailMessageSchema.parse(args), undefined, signal),
|
|
90
|
+
}),
|
|
91
|
+
};
|
|
92
|
+
/**
|
|
93
|
+
* Prime the shared runtime from this plugin's config (over env), route the
|
|
94
|
+
* tools' sends through this plugin's interceptor chain, and log the
|
|
95
|
+
* effective sender policy so an active restriction is obvious at boot. In
|
|
96
|
+
* SMTP mode, fail setup when the transport cannot be verified: a bad host
|
|
97
|
+
* or credential is a deploy-time mistake and should stop the app rather
|
|
98
|
+
* than wait for a user to approve a send that cannot work. With no SMTP
|
|
99
|
+
* credentials the runtime is in file/outbox mode (only when
|
|
100
|
+
* `EMAIL_OUTBOX_MODE` is set), logged loudly here so it is obvious mail
|
|
101
|
+
* is being written to disk rather than sent.
|
|
102
|
+
*/
|
|
103
|
+
async setup() {
|
|
104
|
+
const { transporter, config } = getEmailRuntime(this.config);
|
|
105
|
+
setEmailExecutor((fn, settings) => this.execute(fn, settings));
|
|
106
|
+
const policy = {
|
|
107
|
+
mode: config.mode,
|
|
108
|
+
senderPolicy: config.senderPolicy,
|
|
109
|
+
restricted: config.allowedSenders.length > 0,
|
|
110
|
+
...(config.allowedSenders.length > 0 ? { allowedSenders: config.allowedSenders } : {}),
|
|
111
|
+
};
|
|
112
|
+
if (config.mode === "file") {
|
|
113
|
+
logger.warn("outbox:enabled", {
|
|
114
|
+
dir: config.outDir,
|
|
115
|
+
reason: "no SMTP credentials configured; emails are written to disk instead of sent",
|
|
116
|
+
});
|
|
117
|
+
logger.info("ready", policy);
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
const verified = await this.execute(async (signal) => verifyEmailTransport(transporter, signal), EMAIL_VERIFY_SETTINGS);
|
|
121
|
+
if (!verified.ok) {
|
|
122
|
+
logger.error("smtp:unverified", {
|
|
123
|
+
host: config.host,
|
|
124
|
+
port: config.port,
|
|
125
|
+
status: verified.status,
|
|
126
|
+
error: verified.message,
|
|
127
|
+
});
|
|
128
|
+
throw ConfigurationError.invalidConnection("SMTP", `Could not verify ${config.host}:${config.port}. Check SMTP_HOST, SMTP_PORT, SMTP_SECURE, and the credentials, or set EMAIL_OUTBOX_MODE=1 for local outbox testing.`);
|
|
129
|
+
}
|
|
130
|
+
logger.info("ready", {
|
|
131
|
+
...policy,
|
|
132
|
+
host: config.host,
|
|
133
|
+
port: config.port,
|
|
134
|
+
secure: config.secure,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
/** Close the SMTP connection pool. Idempotent. */
|
|
138
|
+
async shutdown() {
|
|
139
|
+
resetEmailRuntime();
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Abort in-flight work. AppKit's graceful shutdown only invokes this hook -
|
|
143
|
+
* it never calls {@link shutdown} - so the SMTP pool is closed from here or
|
|
144
|
+
* it leaks at SIGTERM. The teardown is synchronous and idempotent, so the
|
|
145
|
+
* un-awaited call costs nothing.
|
|
146
|
+
*/
|
|
147
|
+
abortActiveOperations() {
|
|
148
|
+
super.abortActiveOperations();
|
|
149
|
+
void this.shutdown();
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Expose the sender-options lookup so UI compose views can populate a
|
|
153
|
+
* `From` dropdown from the configured allow-list. Mounted under the
|
|
154
|
+
* plugin base path, i.e. `GET /api/email/senders`. Runs in the OBO
|
|
155
|
+
* user scope so domain wildcards resolve against the caller's own
|
|
156
|
+
* local part.
|
|
157
|
+
*/
|
|
158
|
+
injectRoutes(router) {
|
|
159
|
+
this.route(router, {
|
|
160
|
+
name: "listSenders",
|
|
161
|
+
method: "get",
|
|
162
|
+
path: SENDERS_ROUTE,
|
|
163
|
+
handler: async (req, res) => {
|
|
164
|
+
const result = await this.asUser(req).executeListSenders();
|
|
165
|
+
if (!result.ok) {
|
|
166
|
+
res.status(result.status).json({ error: result.message });
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
res.json(result.data);
|
|
170
|
+
},
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
exports() {
|
|
174
|
+
return {
|
|
175
|
+
/**
|
|
176
|
+
* Send a message immediately from `from` through the shared
|
|
177
|
+
* transport, bypassing the approval flow. For agent-driven sends
|
|
178
|
+
* use {@link emailTool} instead.
|
|
179
|
+
*/
|
|
180
|
+
sendEmail: (message, from, signal) => this.send(message, from, signal),
|
|
181
|
+
/**
|
|
182
|
+
* Sender options for the current user (the `GET /senders` payload).
|
|
183
|
+
* AppKit wraps this with `asUser(req)` for OBO scoping.
|
|
184
|
+
*/
|
|
185
|
+
listSenders: async () => unwrap(await this.executeListSenders()),
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
/** AppKit `ToolProvider`: the tool definitions offered to an agent. */
|
|
189
|
+
getAgentTools() {
|
|
190
|
+
return toolsFromRegistry(this.tools);
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* AppKit `ToolProvider`: run one tool call. Arguments are validated against
|
|
194
|
+
* the tool's schema first, and a validation failure comes back as an
|
|
195
|
+
* LLM-friendly string so the model can correct itself on the next turn.
|
|
196
|
+
*/
|
|
197
|
+
async executeAgentTool(name, args, signal) {
|
|
198
|
+
return executeFromRegistry(this.tools, name, args, signal);
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Send one message, resolving the sender for the caller in scope when
|
|
202
|
+
* `from` is not pinned. The interceptor chain is applied inside
|
|
203
|
+
* {@link sendEmail} through the executor installed at setup, so this must
|
|
204
|
+
* not wrap it again.
|
|
205
|
+
*/
|
|
206
|
+
async send(message, from, signal) {
|
|
207
|
+
return sendEmail(message, from ?? this.resolveSender(), signal);
|
|
208
|
+
}
|
|
209
|
+
/** Run the sender-options lookup through the plugin's interceptor chain. */
|
|
210
|
+
async executeListSenders() {
|
|
211
|
+
return this.execute(async () => this.listSenders(), EMAIL_SENDERS_SETTINGS);
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Compute the `From` options offered to the current user: the concrete
|
|
215
|
+
* addresses the effective allow-list permits (domain wildcards expanded
|
|
216
|
+
* against the OBO user's local part), the default among them, and
|
|
217
|
+
* whether the list is an enforced restriction. See
|
|
218
|
+
* {@link listSenderOptions}.
|
|
219
|
+
*/
|
|
220
|
+
async listSenders() {
|
|
221
|
+
const { config } = getEmailRuntime();
|
|
222
|
+
const senders = listSenderOptions(config, currentUserEmail());
|
|
223
|
+
// Prefer the address a send would actually default to; fall back to
|
|
224
|
+
// the first offered option when that can't be resolved / permitted.
|
|
225
|
+
let defaultSender = senders[0];
|
|
226
|
+
try {
|
|
227
|
+
const resolved = this.resolveSender().toLowerCase();
|
|
228
|
+
if (isSenderAllowed(resolved, config.allowedSenders))
|
|
229
|
+
defaultSender = resolved;
|
|
230
|
+
}
|
|
231
|
+
catch {
|
|
232
|
+
// Keep the first offered option (or none) as the default.
|
|
233
|
+
}
|
|
234
|
+
return {
|
|
235
|
+
senders,
|
|
236
|
+
...(defaultSender ? { defaultSender } : {}),
|
|
237
|
+
restricted: config.allowedSenders.length > 0,
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
/** The `From` a send defaults to for the caller in scope. */
|
|
241
|
+
resolveSender() {
|
|
242
|
+
return resolveSenderAddress(getEmailRuntime().config, currentUserEmail());
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
/** The OBO user's address, or undefined outside a user context. */
|
|
246
|
+
function currentUserEmail() {
|
|
247
|
+
const ctx = getExecutionContext();
|
|
248
|
+
return "isUserContext" in ctx ? ctx.userEmail : undefined;
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Re-raise a failed execution as the AppKit error class that already carries
|
|
252
|
+
* the status AppKit resolved, so a programmatic caller sees the same 400 /
|
|
253
|
+
* 401 / 503 an HTTP caller would.
|
|
254
|
+
*/
|
|
255
|
+
function toAppKitError(status, message) {
|
|
256
|
+
if (status === 400)
|
|
257
|
+
return new ValidationError(message);
|
|
258
|
+
if (status === 401)
|
|
259
|
+
return new AuthenticationError(message);
|
|
260
|
+
if (status === 503)
|
|
261
|
+
return new ConnectionError(message);
|
|
262
|
+
return new ExecutionError(message);
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Surface a failed {@link ExecutionResult} to a programmatic caller as a
|
|
266
|
+
* throw. HTTP handlers map `status` onto the response instead.
|
|
267
|
+
*/
|
|
268
|
+
function unwrap(result) {
|
|
269
|
+
if (result.ok)
|
|
270
|
+
return result.data;
|
|
271
|
+
throw toAppKitError(result.status, result.message);
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Register the email plugin.
|
|
275
|
+
*
|
|
276
|
+
* @example
|
|
277
|
+
* ```ts
|
|
278
|
+
* import { createApp, server } from "@databricks/appkit";
|
|
279
|
+
* import { brand, plugin as emailPlugin } from "@dbx-tools/email";
|
|
280
|
+
*
|
|
281
|
+
* await createApp({
|
|
282
|
+
* plugins: [
|
|
283
|
+
* server(),
|
|
284
|
+
* emailPlugin.email({
|
|
285
|
+
* domain: "mail.example.com",
|
|
286
|
+
* allowedSenders: ["*@mail.example.com"],
|
|
287
|
+
* brand: brand.defaultEmailBrand,
|
|
288
|
+
* }),
|
|
289
|
+
* ],
|
|
290
|
+
* });
|
|
291
|
+
* ```
|
|
292
|
+
*/
|
|
293
|
+
export const email = toPlugin(EmailPlugin);
|
|
294
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGx1Z2luLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3BsdWdpbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0F1Qkc7QUFFSCxPQUFPLEVBQ0wsbUJBQW1CLEVBQ25CLGtCQUFrQixFQUNsQixlQUFlLEVBQ2YsY0FBYyxFQUNkLG1CQUFtQixFQUNuQixNQUFNLEVBQ04sUUFBUSxFQUNSLGVBQWUsR0FLaEIsTUFBTSxvQkFBb0IsQ0FBQztBQUM1QixPQUFPLEVBQ0wsVUFBVSxFQUNWLG1CQUFtQixFQUNuQixpQkFBaUIsR0FJbEIsTUFBTSx5QkFBeUIsQ0FBQztBQUNqQyxPQUFPLEVBQUUsR0FBRyxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDN0MsT0FBTyxFQUNMLEtBQUssSUFBSSxTQUFTLEdBSW5CLE1BQU0seUJBQXlCLENBQUM7QUFDakMsT0FBTyxFQUFFLG1CQUFtQixFQUEwQixNQUFNLFVBQVUsQ0FBQztBQUN2RSxPQUFPLEVBQUUsc0JBQXNCLEVBQUUscUJBQXFCLEVBQUUsTUFBTSxZQUFZLENBQUM7QUFDM0UsT0FBTyxFQUFFLGVBQWUsRUFBRSxpQkFBaUIsRUFBRSxvQkFBb0IsRUFBRSxNQUFNLFVBQVUsQ0FBQztBQUNwRixPQUFPLEVBQUUsc0JBQXNCLEVBQUUsTUFBTSxRQUFRLENBQUM7QUFDaEQsT0FBTyxFQUNMLGVBQWUsRUFDZixpQkFBaUIsRUFDakIsU0FBUyxFQUNULGdCQUFnQixFQUNoQixvQkFBb0IsR0FDckIsTUFBTSxhQUFhLENBQUM7QUFFckIsK0VBQStFO0FBQy9FLE1BQU0sYUFBYSxHQUFHLFVBQVUsQ0FBQztBQUVqQyw0RUFBNEU7QUFDNUUsTUFBTSxTQUFTLEdBQUcsTUFBTSxDQUFDO0FBRXpCLE1BQU0sTUFBTSxHQUFHLEdBQUcsQ0FBQyxNQUFNLENBQUMsT0FBTyxDQUFDLENBQUM7QUFFbkM7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FtQkc7QUFD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QyxPQUFPLENBQUMsQ0FBQztJQUM1RCxJQUFJLE1BQU0sS0FBSyxHQUFHO1FBQUUsT0FBTyxJQUFJLGVBQWUsQ0FBQyxPQUFPLENBQUMsQ0FBQztJQUN4RCxPQUFPLElBQUksY0FBYyxDQUFDLE9BQU8sQ0FBQyxDQUFDO0FBQ3JDLENBQUM7QUFFRDs7O0dBR0c7QUFDSCxTQUFTLE1BQU0sQ0FBSSxNQUEwQjtJQUMzQyxJQUFJLE1BQU0sQ0FBQyxFQUFFO1FBQUUsT0FBTyxNQUFNLENBQUMsSUFBSSxDQUFDO0lBQ2xDLE1BQU0sYUFBYSxDQUFDLE1BQU0sQ0FBQyxNQUFNLEVBQUUsTUFBTSxDQUFDLE9BQU8sQ0FBQyxDQUFDO0FBQ3JELENBQUM7QUFFRDs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQW1CRztBQUNILE1BQU0sQ0FBQyxNQUFNLEtBQUssR0FBRyxRQUFRLENBQUMsV0FBVyxDQUFDLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIEFwcEtpdCBwbHVnaW4gKHJlZ2lzdGVyZWQgbmFtZTogYGVtYWlsYCkgdGhhdCBvd25zIHRoZSBTTVRQIHJ1bnRpbWVcbiAqIGZvciBvdXRib3VuZCBtYWlsLiBSZWdpc3RlcmluZyBpdCB2YWxpZGF0ZXMgdGhlIFNNVFAgY29uZmlndXJhdGlvblxuICogYW5kIHZlcmlmaWVzIGNvbm5lY3Rpdml0eSBhdCBzdGFydHVwLCBzbyBhIGJhZCBob3N0IC8gY3JlZGVudGlhbFxuICogc3VyZmFjZXMgaW4gdGhlIGJvb3QgbG9ncyBpbnN0ZWFkIG9mIG9uIHRoZSBmaXJzdCBhcHByb3ZlZCBzZW5kLCBhbmRcbiAqIGluc3RhbGxzIHRoaXMgcGx1Z2luJ3MgYGV4ZWN1dGUoKWAgYXMgdGhlIHJ1bnRpbWUncyBleGVjdXRvciBzbyBldmVyeVxuICogc2VuZCBwaWNrcyB1cCBBcHBLaXQncyByZXRyeSAvIHRpbWVvdXQgLyB0ZWxlbWV0cnkgY2hhaW4uXG4gKlxuICogVGhlIHBsdWdpbiBpcyBhbHNvIGEgYFRvb2xQcm92aWRlcmAsIHNvIGFuIEFwcEtpdCBhZ2VudCBjYW4gcmVhY2ggYW5cbiAqIGBlbWFpbC5zZW5kYCB0b29sIGRpcmVjdGx5OyB0aGUge0BsaW5rIGVtYWlsVG9vbH0gZXhwb3J0IGlzIHRoZSBzYW1lXG4gKiBjYXBhYmlsaXR5IGZvciBhIE1hc3RyYSBhZ2VudC4gQm90aCBzaGFyZSB0aGUgdHJhbnNwb3J0IHByaW1lZCBoZXJlLFxuICogYW5kIHtAbGluayBzZW5kRW1haWx9IGlzIGF2YWlsYWJsZSB0byBub24tYWdlbnQgY2FsbGVycy5cbiAqXG4gKiBDb25maWd1cmF0aW9uIGlzIHRoZSBtYW5pZmVzdC1wdWJsaXNoZWQge0BsaW5rIEVtYWlsUGx1Z2luQ29uZmlnfVxuICogKFNNVFAgaG9zdC9wb3J0L2NyZWRlbnRpYWxzLCBzZW5kZXIgZG9tYWluIG9yIGV4cGxpY2l0IGBmcm9tYCwgdGhlXG4gKiBzZW5kZXIgcG9saWN5LCBhbmQgYW4gb3B0aW9uYWwgYGFsbG93ZWRTZW5kZXJzYCByZXN0cmljdGlvbiksIHdpdGhcbiAqIHVucHJlZml4ZWQgYFNNVFBfKmAgLyBgRU1BSUxfKmAgZW52aXJvbm1lbnQgZmFsbGJhY2tzLlxuICpcbiAqIFRoZSBwbHVnaW4gbW91bnRzIG9uZSByb3V0ZSB1bmRlciBpdHMgYmFzZSBwYXRoIChgL2FwaS9lbWFpbGApOlxuICogYEdFVCAvc2VuZGVyc2AgcmV0dXJucyB0aGUgcGVybWl0dGVkIGBGcm9tYCBvcHRpb25zIGZvciB0aGUgY2FsbGluZ1xuICogdXNlciwgc28gYSBjb21wb3NlIFVJIGNhbiBvZmZlciB0aGVtIGluIGEgZHJvcGRvd24uXG4gKlxuICogQG1vZHVsZVxuICovXG5cbmltcG9ydCB7XG4gIEF1dGhlbnRpY2F0aW9uRXJyb3IsXG4gIENvbmZpZ3VyYXRpb25FcnJvcixcbiAgQ29ubmVjdGlvbkVycm9yLFxuICBFeGVjdXRpb25FcnJvcixcbiAgZ2V0RXhlY3V0aW9uQ29udGV4dCxcbiAgUGx1Z2luLFxuICB0b1BsdWdpbixcbiAgVmFsaWRhdGlvbkVycm9yLFxuICB0eXBlIEFwcEtpdEVycm9yLFxuICB0eXBlIEV4ZWN1dGlvblJlc3VsdCxcbiAgdHlwZSBJQXBwUm91dGVyLFxuICB0eXBlIFBsdWdpbk1hbmlmZXN0LFxufSBmcm9tIFwiQGRhdGFicmlja3MvYXBwa2l0XCI7XG5pbXBvcnQge1xuICBkZWZpbmVUb29sLFxuICBleGVjdXRlRnJvbVJlZ2lzdHJ5LFxuICB0b29sc0Zyb21SZWdpc3RyeSxcbiAgdHlwZSBBZ2VudFRvb2xEZWZpbml0aW9uLFxuICB0eXBlIFRvb2xQcm92aWRlcixcbiAgdHlwZSBUb29sUmVnaXN0cnksXG59IGZyb20gXCJAZGF0YWJyaWNrcy9hcHBraXQvYmV0YVwiO1xuaW1wb3J0IHsgbG9nIH0gZnJvbSBcIkBkYngtdG9vbHMvc2hhcmVkLWNvcmVcIjtcbmltcG9ydCB7XG4gIGVtYWlsIGFzIGVtYWlsV2lyZSxcbiAgdHlwZSBFbWFpbE1lc3NhZ2UsXG4gIHR5cGUgRW1haWxSZXN1bHQsXG4gIHR5cGUgRW1haWxTZW5kZXJzLFxufSBmcm9tIFwiQGRieC10b29scy9zaGFyZWQtZW1haWxcIjtcbmltcG9ydCB7IEVNQUlMX0NPTkZJR19TQ0hFTUEsIHR5cGUgRW1haWxQbHVnaW5Db25maWcgfSBmcm9tIFwiLi9jb25maWdcIjtcbmltcG9ydCB7IEVNQUlMX1NFTkRFUlNfU0VUVElOR1MsIEVNQUlMX1ZFUklGWV9TRVRUSU5HUyB9IGZyb20gXCIuL2RlZmF1bHRzXCI7XG5pbXBvcnQgeyBpc1NlbmRlckFsbG93ZWQsIGxpc3RTZW5kZXJPcHRpb25zLCByZXNvbHZlU2VuZGVyQWRkcmVzcyB9IGZyb20gXCIuL3NlbmRlclwiO1xuaW1wb3J0IHsgU0VORF9FTUFJTF9ERVNDUklQVElPTiB9IGZyb20gXCIuL3Rvb2xcIjtcbmltcG9ydCB7XG4gIGdldEVtYWlsUnVudGltZSxcbiAgcmVzZXRFbWFpbFJ1bnRpbWUsXG4gIHNlbmRFbWFpbCxcbiAgc2V0RW1haWxFeGVjdXRvcixcbiAgdmVyaWZ5RW1haWxUcmFuc3BvcnQsXG59IGZyb20gXCIuL3RyYW5zcG9ydFwiO1xuXG4vKiogTW91bnQtcmVsYXRpdmUgcm91dGUgKHVuZGVyIGAvYXBpL2VtYWlsYCkgZm9yIHRoZSBzZW5kZXItb3B0aW9ucyBsb29rdXAuICovXG5jb25zdCBTRU5ERVJTX1JPVVRFID0gXCIvc2VuZGVyc1wiO1xuXG4vKiogUmVnaXN0cnkga2V5IG9mIHRoZSBhZ2VudCB0b29sLCB3aGljaCBhZ2VudHMgYWRkcmVzcyBhcyBgZW1haWwuc2VuZGAuICovXG5jb25zdCBTRU5EX1RPT0wgPSBcInNlbmRcIjtcblxuY29uc3QgbG9nZ2VyID0gbG9nLmxvZ2dlcihcImVtYWlsXCIpO1xuXG4vKipcbiAqIEFwcEtpdCBwbHVnaW4gdGhhdCBjb25maWd1cmVzIGFuZCB2ZXJpZmllcyB0aGUgU01UUCB0cmFuc3BvcnQgdXNlZCBieVxuICogdGhlIGBzZW5kX2VtYWlsYCB0b29sLCBhbmQgZXhwb3NlcyBzZW5kaW5nIGFzIGFuIEFwcEtpdCBhZ2VudCB0b29sLlxuICpcbiAqIEBleGFtcGxlXG4gKiBgYGB0c1xuICogaW1wb3J0IHsgY3JlYXRlQXBwLCBzZXJ2ZXIgfSBmcm9tIFwiQGRhdGFicmlja3MvYXBwa2l0XCI7XG4gKiBpbXBvcnQgeyBwbHVnaW4gYXMgZW1haWxQbHVnaW4gfSBmcm9tIFwiQGRieC10b29scy9lbWFpbFwiO1xuICpcbiAqIGF3YWl0IGNyZWF0ZUFwcCh7XG4gKiAgIHBsdWdpbnM6IFtcbiAqICAgICBzZXJ2ZXIoKSxcbiAqICAgICBlbWFpbFBsdWdpbi5lbWFpbCh7XG4gKiAgICAgICBzbXRwOiB7IGhvc3Q6IFwic210cC5leGFtcGxlLmNvbVwiLCB1c2VyOiBcImFwaWtleVwiLCBwYXNzd29yZDogcHJvY2Vzcy5lbnYuU01UUF9LRVkgfSxcbiAqICAgICAgIGRvbWFpbjogXCJtYWlsLmV4YW1wbGUuY29tXCIsXG4gKiAgICAgfSksXG4gKiAgIF0sXG4gKiB9KTtcbiAqIGBgYFxuICovXG5leHBvcnQgY2xhc3MgRW1haWxQbHVnaW4gZXh0ZW5kcyBQbHVnaW48RW1haWxQbHVnaW5Db25maWc+IGltcGxlbWVudHMgVG9vbFByb3ZpZGVyIHtcbiAgc3RhdGljIG1hbmlmZXN0ID0ge1xuICAgIG5hbWU6IFwiZW1haWxcIixcbiAgICBkaXNwbGF5TmFtZTogXCJFbWFpbFwiLFxuICAgIGRlc2NyaXB0aW9uOlxuICAgICAgXCJTZW5kcyBhcHByb3ZhbC1nYXRlZCBlbWFpbCBvdmVyIFNNVFAsIHdpdGggdGhlIHNlbmRlciBkZXJpdmVkIGZyb20gXCIgK1xuICAgICAgXCJ0aGUgb24tYmVoYWxmLW9mIHVzZXIncyBhZGRyZXNzIG9uIGEgY29uZmlndXJlZCBkb21haW4uXCIsXG4gICAgc3RhYmlsaXR5OiBcImJldGFcIixcbiAgICByZXNvdXJjZXM6IHtcbiAgICAgIHJlcXVpcmVkOiBbXSxcbiAgICAgIG9wdGlvbmFsOiBbXSxcbiAgICB9LFxuICAgIGNvbmZpZzogeyBzY2hlbWE6IEVNQUlMX0NPTkZJR19TQ0hFTUEgfSxcbiAgfSBzYXRpc2ZpZXMgUGx1Z2luTWFuaWZlc3Q8XCJlbWFpbFwiPjtcblxuICAvKipcbiAgICogVGhlIHRvb2wgdGhpcyBwbHVnaW4gb2ZmZXJzIHRvIGFuIEFwcEtpdCBhZ2VudC5cbiAgICpcbiAgICogTm90IGBhdXRvSW5oZXJpdGFibGVgOiBhIHNlbmQgaXMgaXJyZXZlcnNpYmxlIGFuZCBsZWF2ZXMgdGhlIHdvcmtzcGFjZSxcbiAgICogc28gaXQgbXVzdCBvbmx5IGFwcGVhciBpbiBhbiBhZ2VudCB0aGF0IGFza2VkIGZvciBpdCBhbmQgYWNjZXB0ZWQgdGhlXG4gICAqIHNlbmRlciBwb2xpY3kgdGhhdCBjb21lcyB3aXRoIGl0LlxuICAgKlxuICAgKiBgZXhlY3V0ZWAgcmUtcGFyc2VzIGl0cyBhcmd1bWVudHMgd2l0aCB0aGUgbG9jYWwgc2NoZW1hOiBBcHBLaXQgdmFsaWRhdGVzXG4gICAqIGFnYWluc3QgdGhlIHNhbWUgc2NoZW1hIGZpcnN0LCBidXQgcmUtcGFyc2luZyBpcyB3aGF0IGdpdmVzIHRoZSBib2R5IHR5cGVkXG4gICAqIGFyZ3VtZW50cyBpbnN0ZWFkIG9mIGB1bmtub3duYC5cbiAgICovXG4gIHByaXZhdGUgcmVhZG9ubHkgdG9vbHM6IFRvb2xSZWdpc3RyeSA9IHtcbiAgICBbU0VORF9UT09MXTogZGVmaW5lVG9vbCh7XG4gICAgICBkZXNjcmlwdGlvbjogU0VORF9FTUFJTF9ERVNDUklQVElPTixcbiAgICAgIHNjaGVtYTogZW1haWxXaXJlLmVtYWlsTWVzc2FnZVNjaGVtYSxcbiAgICAgIGFubm90YXRpb25zOiB7IGVmZmVjdDogXCJ3cml0ZVwiLCByZXF1aXJlc1VzZXJDb250ZXh0OiB0cnVlIH0sXG4gICAgICBhdXRvSW5oZXJpdGFibGU6IGZhbHNlLFxuICAgICAgZXhlY3V0ZTogYXN5bmMgKGFyZ3MsIHNpZ25hbCkgPT5cbiAgICAgICAgdGhpcy5zZW5kKGVtYWlsV2lyZS5lbWFpbE1lc3NhZ2VTY2hlbWEucGFyc2UoYXJncyksIHVuZGVmaW5lZCwgc2lnbmFsKSxcbiAgICB9KSxcbiAgfTtcblxuICAvKipcbiAgICogUHJpbWUgdGhlIHNoYXJlZCBydW50aW1lIGZyb20gdGhpcyBwbHVnaW4ncyBjb25maWcgKG92ZXIgZW52KSwgcm91dGUgdGhlXG4gICAqIHRvb2xzJyBzZW5kcyB0aHJvdWdoIHRoaXMgcGx1Z2luJ3MgaW50ZXJjZXB0b3IgY2hhaW4sIGFuZCBsb2cgdGhlXG4gICAqIGVmZmVjdGl2ZSBzZW5kZXIgcG9saWN5IHNvIGFuIGFjdGl2ZSByZXN0cmljdGlvbiBpcyBvYnZpb3VzIGF0IGJvb3QuIEluXG4gICAqIFNNVFAgbW9kZSwgZmFpbCBzZXR1cCB3aGVuIHRoZSB0cmFuc3BvcnQgY2Fubm90IGJlIHZlcmlmaWVkOiBhIGJhZCBob3N0XG4gICAqIG9yIGNyZWRlbnRpYWwgaXMgYSBkZXBsb3ktdGltZSBtaXN0YWtlIGFuZCBzaG91bGQgc3RvcCB0aGUgYXBwIHJhdGhlclxuICAgKiB0aGFuIHdhaXQgZm9yIGEgdXNlciB0byBhcHByb3ZlIGEgc2VuZCB0aGF0IGNhbm5vdCB3b3JrLiBXaXRoIG5vIFNNVFBcbiAgICogY3JlZGVudGlhbHMgdGhlIHJ1bnRpbWUgaXMgaW4gZmlsZS9vdXRib3ggbW9kZSAob25seSB3aGVuXG4gICAqIGBFTUFJTF9PVVRCT1hfTU9ERWAgaXMgc2V0KSwgbG9nZ2VkIGxvdWRseSBoZXJlIHNvIGl0IGlzIG9idmlvdXMgbWFpbFxuICAgKiBpcyBiZWluZyB3cml0dGVuIHRvIGRpc2sgcmF0aGVyIHRoYW4gc2VudC5cbiAgICovXG4gIG92ZXJyaWRlIGFzeW5jIHNldHVwKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IHsgdHJhbnNwb3J0ZXIsIGNvbmZpZyB9ID0gZ2V0RW1haWxSdW50aW1lKHRoaXMuY29uZmlnKTtcbiAgICBzZXRFbWFpbEV4ZWN1dG9yKChmbiwgc2V0dGluZ3MpID0+IHRoaXMuZXhlY3V0ZShmbiwgc2V0dGluZ3MpKTtcbiAgICBjb25zdCBwb2xpY3kgPSB7XG4gICAgICBtb2RlOiBjb25maWcubW9kZSxcbiAgICAgIHNlbmRlclBvbGljeTogY29uZmlnLnNlbmRlclBvbGljeSxcbiAgICAgIHJlc3RyaWN0ZWQ6IGNvbmZpZy5hbGxvd2VkU2VuZGVycy5sZW5ndGggPiAwLFxuICAgICAgLi4uKGNvbmZpZy5hbGxvd2VkU2VuZGVycy5sZW5ndGggPiAwID8geyBhbGxvd2VkU2VuZGVyczogY29uZmlnLmFsbG93ZWRTZW5kZXJzIH0gOiB7fSksXG4gICAgfTtcbiAgICBpZiAoY29uZmlnLm1vZGUgPT09IFwiZmlsZVwiKSB7XG4gICAgICBsb2dnZXIud2FybihcIm91dGJveDplbmFibGVkXCIsIHtcbiAgICAgICAgZGlyOiBjb25maWcub3V0RGlyLFxuICAgICAgICByZWFzb246IFwibm8gU01UUCBjcmVkZW50aWFscyBjb25maWd1cmVkOyBlbWFpbHMgYXJlIHdyaXR0ZW4gdG8gZGlzayBpbnN0ZWFkIG9mIHNlbnRcIixcbiAgICAgIH0pO1xuICAgICAgbG9nZ2VyLmluZm8oXCJyZWFkeVwiLCBwb2xpY3kpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBjb25zdCB2ZXJpZmllZCA9IGF3YWl0IHRoaXMuZXhlY3V0ZShcbiAgICAgIGFzeW5jIChzaWduYWwpID0+IHZlcmlmeUVtYWlsVHJhbnNwb3J0KHRyYW5zcG9ydGVyLCBzaWduYWwpLFxuICAgICAgRU1BSUxfVkVSSUZZX1NFVFRJTkdTLFxuICAgICk7XG4gICAgaWYgKCF2ZXJpZmllZC5vaykge1xuICAgICAgbG9nZ2VyLmVycm9yKFwic210cDp1bnZlcmlmaWVkXCIsIHtcbiAgICAgICAgaG9zdDogY29uZmlnLmhvc3QsXG4gICAgICAgIHBvcnQ6IGNvbmZpZy5wb3J0LFxuICAgICAgICBzdGF0dXM6IHZlcmlmaWVkLnN0YXR1cyxcbiAgICAgICAgZXJyb3I6IHZlcmlmaWVkLm1lc3NhZ2UsXG4gICAgICB9KTtcbiAgICAgIHRocm93IENvbmZpZ3VyYXRpb25FcnJvci5pbnZhbGlkQ29ubmVjdGlvbihcbiAgICAgICAgXCJTTVRQXCIsXG4gICAgICAgIGBDb3VsZCBub3QgdmVyaWZ5ICR7Y29uZmlnLmhvc3R9OiR7Y29uZmlnLnBvcnR9LiBDaGVjayBTTVRQX0hPU1QsIFNNVFBfUE9SVCwgU01UUF9TRUNVUkUsIGFuZCB0aGUgY3JlZGVudGlhbHMsIG9yIHNldCBFTUFJTF9PVVRCT1hfTU9ERT0xIGZvciBsb2NhbCBvdXRib3ggdGVzdGluZy5gLFxuICAgICAgKTtcbiAgICB9XG4gICAgbG9nZ2VyLmluZm8oXCJyZWFkeVwiLCB7XG4gICAgICAuLi5wb2xpY3ksXG4gICAgICBob3N0OiBjb25maWcuaG9zdCxcbiAgICAgIHBvcnQ6IGNvbmZpZy5wb3J0LFxuICAgICAgc2VjdXJlOiBjb25maWcuc2VjdXJlLFxuICAgIH0pO1xuICB9XG5cbiAgLyoqIENsb3NlIHRoZSBTTVRQIGNvbm5lY3Rpb24gcG9vbC4gSWRlbXBvdGVudC4gKi9cbiAgYXN5bmMgc2h1dGRvd24oKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgcmVzZXRFbWFpbFJ1bnRpbWUoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBBYm9ydCBpbi1mbGlnaHQgd29yay4gQXBwS2l0J3MgZ3JhY2VmdWwgc2h1dGRvd24gb25seSBpbnZva2VzIHRoaXMgaG9vayAtXG4gICAqIGl0IG5ldmVyIGNhbGxzIHtAbGluayBzaHV0ZG93bn0gLSBzbyB0aGUgU01UUCBwb29sIGlzIGNsb3NlZCBmcm9tIGhlcmUgb3JcbiAgICogaXQgbGVha3MgYXQgU0lHVEVSTS4gVGhlIHRlYXJkb3duIGlzIHN5bmNocm9ub3VzIGFuZCBpZGVtcG90ZW50LCBzbyB0aGVcbiAgICogdW4tYXdhaXRlZCBjYWxsIGNvc3RzIG5vdGhpbmcuXG4gICAqL1xuICBvdmVycmlkZSBhYm9ydEFjdGl2ZU9wZXJhdGlvbnMoKTogdm9pZCB7XG4gICAgc3VwZXIuYWJvcnRBY3RpdmVPcGVyYXRpb25zKCk7XG4gICAgdm9pZCB0aGlzLnNodXRkb3duKCk7XG4gIH1cblxuICAvKipcbiAgICogRXhwb3NlIHRoZSBzZW5kZXItb3B0aW9ucyBsb29rdXAgc28gVUkgY29tcG9zZSB2aWV3cyBjYW4gcG9wdWxhdGUgYVxuICAgKiBgRnJvbWAgZHJvcGRvd24gZnJvbSB0aGUgY29uZmlndXJlZCBhbGxvdy1saXN0LiBNb3VudGVkIHVuZGVyIHRoZVxuICAgKiBwbHVnaW4gYmFzZSBwYXRoLCBpLmUuIGBHRVQgL2FwaS9lbWFpbC9zZW5kZXJzYC4gUnVucyBpbiB0aGUgT0JPXG4gICAqIHVzZXIgc2NvcGUgc28gZG9tYWluIHdpbGRjYXJkcyByZXNvbHZlIGFnYWluc3QgdGhlIGNhbGxlcidzIG93blxuICAgKiBsb2NhbCBwYXJ0LlxuICAgKi9cbiAgb3ZlcnJpZGUgaW5qZWN0Um91dGVzKHJvdXRlcjogSUFwcFJvdXRlcik6IHZvaWQge1xuICAgIHRoaXMucm91dGUocm91dGVyLCB7XG4gICAgICBuYW1lOiBcImxpc3RTZW5kZXJzXCIsXG4gICAgICBtZXRob2Q6IFwiZ2V0XCIsXG4gICAgICBwYXRoOiBTRU5ERVJTX1JPVVRFLFxuICAgICAgaGFuZGxlcjogYXN5bmMgKHJlcSwgcmVzKSA9PiB7XG4gICAgICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHRoaXMuYXNVc2VyKHJlcSkuZXhlY3V0ZUxpc3RTZW5kZXJzKCk7XG4gICAgICAgIGlmICghcmVzdWx0Lm9rKSB7XG4gICAgICAgICAgcmVzLnN0YXR1cyhyZXN1bHQuc3RhdHVzKS5qc29uKHsgZXJyb3I6IHJlc3VsdC5tZXNzYWdlIH0pO1xuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICByZXMuanNvbihyZXN1bHQuZGF0YSk7XG4gICAgICB9LFxuICAgIH0pO1xuICB9XG5cbiAgb3ZlcnJpZGUgZXhwb3J0cygpIHtcbiAgICByZXR1cm4ge1xuICAgICAgLyoqXG4gICAgICAgKiBTZW5kIGEgbWVzc2FnZSBpbW1lZGlhdGVseSBmcm9tIGBmcm9tYCB0aHJvdWdoIHRoZSBzaGFyZWRcbiAgICAgICAqIHRyYW5zcG9ydCwgYnlwYXNzaW5nIHRoZSBhcHByb3ZhbCBmbG93LiBGb3IgYWdlbnQtZHJpdmVuIHNlbmRzXG4gICAgICAgKiB1c2Uge0BsaW5rIGVtYWlsVG9vbH0gaW5zdGVhZC5cbiAgICAgICAqL1xuICAgICAgc2VuZEVtYWlsOiAoXG4gICAgICAgIG1lc3NhZ2U6IEVtYWlsTWVzc2FnZSxcbiAgICAgICAgZnJvbTogc3RyaW5nLFxuICAgICAgICBzaWduYWw/OiBBYm9ydFNpZ25hbCxcbiAgICAgICk6IFByb21pc2U8RW1haWxSZXN1bHQ+ID0+IHRoaXMuc2VuZChtZXNzYWdlLCBmcm9tLCBzaWduYWwpLFxuICAgICAgLyoqXG4gICAgICAgKiBTZW5kZXIgb3B0aW9ucyBmb3IgdGhlIGN1cnJlbnQgdXNlciAodGhlIGBHRVQgL3NlbmRlcnNgIHBheWxvYWQpLlxuICAgICAgICogQXBwS2l0IHdyYXBzIHRoaXMgd2l0aCBgYXNVc2VyKHJlcSlgIGZvciBPQk8gc2NvcGluZy5cbiAgICAgICAqL1xuICAgICAgbGlzdFNlbmRlcnM6IGFzeW5jICgpOiBQcm9taXNlPEVtYWlsU2VuZGVycz4gPT4gdW53cmFwKGF3YWl0IHRoaXMuZXhlY3V0ZUxpc3RTZW5kZXJzKCkpLFxuICAgIH07XG4gIH1cblxuICAvKiogQXBwS2l0IGBUb29sUHJvdmlkZXJgOiB0aGUgdG9vbCBkZWZpbml0aW9ucyBvZmZlcmVkIHRvIGFuIGFnZW50LiAqL1xuICBnZXRBZ2VudFRvb2xzKCk6IEFnZW50VG9vbERlZmluaXRpb25bXSB7XG4gICAgcmV0dXJuIHRvb2xzRnJvbVJlZ2lzdHJ5KHRoaXMudG9vbHMpO1xuICB9XG5cbiAgLyoqXG4gICAqIEFwcEtpdCBgVG9vbFByb3ZpZGVyYDogcnVuIG9uZSB0b29sIGNhbGwuIEFyZ3VtZW50cyBhcmUgdmFsaWRhdGVkIGFnYWluc3RcbiAgICogdGhlIHRvb2wncyBzY2hlbWEgZmlyc3QsIGFuZCBhIHZhbGlkYXRpb24gZmFpbHVyZSBjb21lcyBiYWNrIGFzIGFuXG4gICAqIExMTS1mcmllbmRseSBzdHJpbmcgc28gdGhlIG1vZGVsIGNhbiBjb3JyZWN0IGl0c2VsZiBvbiB0aGUgbmV4dCB0dXJuLlxuICAgKi9cbiAgYXN5bmMgZXhlY3V0ZUFnZW50VG9vbChuYW1lOiBzdHJpbmcsIGFyZ3M6IHVua25vd24sIHNpZ25hbD86IEFib3J0U2lnbmFsKTogUHJvbWlzZTx1bmtub3duPiB7XG4gICAgcmV0dXJuIGV4ZWN1dGVGcm9tUmVnaXN0cnkodGhpcy50b29scywgbmFtZSwgYXJncywgc2lnbmFsKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTZW5kIG9uZSBtZXNzYWdlLCByZXNvbHZpbmcgdGhlIHNlbmRlciBmb3IgdGhlIGNhbGxlciBpbiBzY29wZSB3aGVuXG4gICAqIGBmcm9tYCBpcyBub3QgcGlubmVkLiBUaGUgaW50ZXJjZXB0b3IgY2hhaW4gaXMgYXBwbGllZCBpbnNpZGVcbiAgICoge0BsaW5rIHNlbmRFbWFpbH0gdGhyb3VnaCB0aGUgZXhlY3V0b3IgaW5zdGFsbGVkIGF0IHNldHVwLCBzbyB0aGlzIG11c3RcbiAgICogbm90IHdyYXAgaXQgYWdhaW4uXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIHNlbmQoXG4gICAgbWVzc2FnZTogRW1haWxNZXNzYWdlLFxuICAgIGZyb206IHN0cmluZyB8IHVuZGVmaW5lZCxcbiAgICBzaWduYWw/OiBBYm9ydFNpZ25hbCxcbiAgKTogUHJvbWlzZTxFbWFpbFJlc3VsdD4ge1xuICAgIHJldHVybiBzZW5kRW1haWwobWVzc2FnZSwgZnJvbSA/PyB0aGlzLnJlc29sdmVTZW5kZXIoKSwgc2lnbmFsKTtcbiAgfVxuXG4gIC8qKiBSdW4gdGhlIHNlbmRlci1vcHRpb25zIGxvb2t1cCB0aHJvdWdoIHRoZSBwbHVnaW4ncyBpbnRlcmNlcHRvciBjaGFpbi4gKi9cbiAgcHJpdmF0ZSBhc3luYyBleGVjdXRlTGlzdFNlbmRlcnMoKTogUHJvbWlzZTxFeGVjdXRpb25SZXN1bHQ8RW1haWxTZW5kZXJzPj4ge1xuICAgIHJldHVybiB0aGlzLmV4ZWN1dGUoYXN5bmMgKCkgPT4gdGhpcy5saXN0U2VuZGVycygpLCBFTUFJTF9TRU5ERVJTX1NFVFRJTkdTKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDb21wdXRlIHRoZSBgRnJvbWAgb3B0aW9ucyBvZmZlcmVkIHRvIHRoZSBjdXJyZW50IHVzZXI6IHRoZSBjb25jcmV0ZVxuICAgKiBhZGRyZXNzZXMgdGhlIGVmZmVjdGl2ZSBhbGxvdy1saXN0IHBlcm1pdHMgKGRvbWFpbiB3aWxkY2FyZHMgZXhwYW5kZWRcbiAgICogYWdhaW5zdCB0aGUgT0JPIHVzZXIncyBsb2NhbCBwYXJ0KSwgdGhlIGRlZmF1bHQgYW1vbmcgdGhlbSwgYW5kXG4gICAqIHdoZXRoZXIgdGhlIGxpc3QgaXMgYW4gZW5mb3JjZWQgcmVzdHJpY3Rpb24uIFNlZVxuICAgKiB7QGxpbmsgbGlzdFNlbmRlck9wdGlvbnN9LlxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyBsaXN0U2VuZGVycygpOiBQcm9taXNlPEVtYWlsU2VuZGVycz4ge1xuICAgIGNvbnN0IHsgY29uZmlnIH0gPSBnZXRFbWFpbFJ1bnRpbWUoKTtcbiAgICBjb25zdCBzZW5kZXJzID0gbGlzdFNlbmRlck9wdGlvbnMoY29uZmlnLCBjdXJyZW50VXNlckVtYWlsKCkpO1xuICAgIC8vIFByZWZlciB0aGUgYWRkcmVzcyBhIHNlbmQgd291bGQgYWN0dWFsbHkgZGVmYXVsdCB0bzsgZmFsbCBiYWNrIHRvXG4gICAgLy8gdGhlIGZpcnN0IG9mZmVyZWQgb3B0aW9uIHdoZW4gdGhhdCBjYW4ndCBiZSByZXNvbHZlZCAvIHBlcm1pdHRlZC5cbiAgICBsZXQgZGVmYXVsdFNlbmRlciA9IHNlbmRlcnNbMF07XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHJlc29sdmVkID0gdGhpcy5yZXNvbHZlU2VuZGVyKCkudG9Mb3dlckNhc2UoKTtcbiAgICAgIGlmIChpc1NlbmRlckFsbG93ZWQocmVzb2x2ZWQsIGNvbmZpZy5hbGxvd2VkU2VuZGVycykpIGRlZmF1bHRTZW5kZXIgPSByZXNvbHZlZDtcbiAgICB9IGNhdGNoIHtcbiAgICAgIC8vIEtlZXAgdGhlIGZpcnN0IG9mZmVyZWQgb3B0aW9uIChvciBub25lKSBhcyB0aGUgZGVmYXVsdC5cbiAgICB9XG4gICAgcmV0dXJuIHtcbiAgICAgIHNlbmRlcnMsXG4gICAgICAuLi4oZGVmYXVsdFNlbmRlciA/IHsgZGVmYXVsdFNlbmRlciB9IDoge30pLFxuICAgICAgcmVzdHJpY3RlZDogY29uZmlnLmFsbG93ZWRTZW5kZXJzLmxlbmd0aCA+IDAsXG4gICAgfTtcbiAgfVxuXG4gIC8qKiBUaGUgYEZyb21gIGEgc2VuZCBkZWZhdWx0cyB0byBmb3IgdGhlIGNhbGxlciBpbiBzY29wZS4gKi9cbiAgcHJpdmF0ZSByZXNvbHZlU2VuZGVyKCk6IHN0cmluZyB7XG4gICAgcmV0dXJuIHJlc29sdmVTZW5kZXJBZGRyZXNzKGdldEVtYWlsUnVudGltZSgpLmNvbmZpZywgY3VycmVudFVzZXJFbWFpbCgpKTtcbiAgfVxufVxuXG4vKiogVGhlIE9CTyB1c2VyJ3MgYWRkcmVzcywgb3IgdW5kZWZpbmVkIG91dHNpZGUgYSB1c2VyIGNvbnRleHQuICovXG5mdW5jdGlvbiBjdXJyZW50VXNlckVtYWlsKCk6IHN0cmluZyB8IHVuZGVmaW5lZCB7XG4gIGNvbnN0IGN0eCA9IGdldEV4ZWN1dGlvbkNvbnRleHQoKTtcbiAgcmV0dXJuIFwiaXNVc2VyQ29udGV4dFwiIGluIGN0eCA/IGN0eC51c2VyRW1haWwgOiB1bmRlZmluZWQ7XG59XG5cbi8qKlxuICogUmUtcmFpc2UgYSBmYWlsZWQgZXhlY3V0aW9uIGFzIHRoZSBBcHBLaXQgZXJyb3IgY2xhc3MgdGhhdCBhbHJlYWR5IGNhcnJpZXNcbiAqIHRoZSBzdGF0dXMgQXBwS2l0IHJlc29sdmVkLCBzbyBhIHByb2dyYW1tYXRpYyBjYWxsZXIgc2VlcyB0aGUgc2FtZSA0MDAgL1xuICogNDAxIC8gNTAzIGFuIEhUVFAgY2FsbGVyIHdvdWxkLlxuICovXG5mdW5jdGlvbiB0b0FwcEtpdEVycm9yKHN0YXR1czogbnVtYmVyLCBtZXNzYWdlOiBzdHJpbmcpOiBBcHBLaXRFcnJvciB7XG4gIGlmIChzdGF0dXMgPT09IDQwMCkgcmV0dXJuIG5ldyBWYWxpZGF0aW9uRXJyb3IobWVzc2FnZSk7XG4gIGlmIChzdGF0dXMgPT09IDQwMSkgcmV0dXJuIG5ldyBBdXRoZW50aWNhdGlvbkVycm9yKG1lc3NhZ2UpO1xuICBpZiAoc3RhdHVzID09PSA1MDMpIHJldHVybiBuZXcgQ29ubmVjdGlvbkVycm9yKG1lc3NhZ2UpO1xuICByZXR1cm4gbmV3IEV4ZWN1dGlvbkVycm9yKG1lc3NhZ2UpO1xufVxuXG4vKipcbiAqIFN1cmZhY2UgYSBmYWlsZWQge0BsaW5rIEV4ZWN1dGlvblJlc3VsdH0gdG8gYSBwcm9ncmFtbWF0aWMgY2FsbGVyIGFzIGFcbiAqIHRocm93LiBIVFRQIGhhbmRsZXJzIG1hcCBgc3RhdHVzYCBvbnRvIHRoZSByZXNwb25zZSBpbnN0ZWFkLlxuICovXG5mdW5jdGlvbiB1bndyYXA8VD4ocmVzdWx0OiBFeGVjdXRpb25SZXN1bHQ8VD4pOiBUIHtcbiAgaWYgKHJlc3VsdC5vaykgcmV0dXJuIHJlc3VsdC5kYXRhO1xuICB0aHJvdyB0b0FwcEtpdEVycm9yKHJlc3VsdC5zdGF0dXMsIHJlc3VsdC5tZXNzYWdlKTtcbn1cblxuLyoqXG4gKiBSZWdpc3RlciB0aGUgZW1haWwgcGx1Z2luLlxuICpcbiAqIEBleGFtcGxlXG4gKiBgYGB0c1xuICogaW1wb3J0IHsgY3JlYXRlQXBwLCBzZXJ2ZXIgfSBmcm9tIFwiQGRhdGFicmlja3MvYXBwa2l0XCI7XG4gKiBpbXBvcnQgeyBicmFuZCwgcGx1Z2luIGFzIGVtYWlsUGx1Z2luIH0gZnJvbSBcIkBkYngtdG9vbHMvZW1haWxcIjtcbiAqXG4gKiBhd2FpdCBjcmVhdGVBcHAoe1xuICogICBwbHVnaW5zOiBbXG4gKiAgICAgc2VydmVyKCksXG4gKiAgICAgZW1haWxQbHVnaW4uZW1haWwoe1xuICogICAgICAgZG9tYWluOiBcIm1haWwuZXhhbXBsZS5jb21cIixcbiAqICAgICAgIGFsbG93ZWRTZW5kZXJzOiBbXCIqQG1haWwuZXhhbXBsZS5jb21cIl0sXG4gKiAgICAgICBicmFuZDogYnJhbmQuZGVmYXVsdEVtYWlsQnJhbmQsXG4gKiAgICAgfSksXG4gKiAgIF0sXG4gKiB9KTtcbiAqIGBgYFxuICovXG5leHBvcnQgY29uc3QgZW1haWwgPSB0b1BsdWdpbihFbWFpbFBsdWdpbik7XG4iXX0=
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sender-address policy: turn the on-behalf-of user's email into an
|
|
3
|
+
* outbound `From`, and (optionally) restrict which addresses may send.
|
|
4
|
+
*
|
|
5
|
+
* The default `From` re-homes the local part (everything before `@`) of
|
|
6
|
+
* the OBO email on the configured sending domain, so `alice@databricks.com`
|
|
7
|
+
* through a domain of `mail.example.com` goes out as
|
|
8
|
+
* `alice@mail.example.com`. An explicit `from` short-circuits that; the
|
|
9
|
+
* file/outbox fallback (no domain) keeps the user's address verbatim so
|
|
10
|
+
* test artifacts land under a recognizable folder.
|
|
11
|
+
*
|
|
12
|
+
* The resolved `From` is then constrained to the effective allow-list: a
|
|
13
|
+
* pattern is either an exact address (`user@domain.com`), a domain wildcard
|
|
14
|
+
* (`*@domain.com` or the bare `domain.com`, matching any local part on that
|
|
15
|
+
* domain), or `*` (any). This module only matches patterns; which patterns
|
|
16
|
+
* apply is decided by the configured sender policy in `./config`, which
|
|
17
|
+
* under the default `"allowlist"` mode fills an empty list in from the
|
|
18
|
+
* sender source. {@link listSenderOptions} expands the effective list into
|
|
19
|
+
* the concrete addresses a UI dropdown can offer for the current user.
|
|
20
|
+
*
|
|
21
|
+
* @module
|
|
22
|
+
*/
|
|
23
|
+
import type { ResolvedEmailConfig } from "./config.js";
|
|
24
|
+
/**
|
|
25
|
+
* Re-home the OBO user's local part on `domain`. Throws when no usable
|
|
26
|
+
* local part is available (e.g. a service-context call with no user).
|
|
27
|
+
*/
|
|
28
|
+
export declare function deriveSenderAddress(userEmail: string | undefined, domain: string): string;
|
|
29
|
+
/**
|
|
30
|
+
* Normalize a sender allow-list from config (a `string[]`) or an env var
|
|
31
|
+
* (a CSV / whitespace-separated string). Delegates to the shared
|
|
32
|
+
* {@link net.parseEmails} so allow-list patterns are read exactly
|
|
33
|
+
* like recipient lists elsewhere: entries are trimmed, lower-cased (so
|
|
34
|
+
* matching in {@link isSenderAllowed} is case-insensitive), and
|
|
35
|
+
* de-duplicated; empties are dropped. An empty result means "no
|
|
36
|
+
* restriction".
|
|
37
|
+
*/
|
|
38
|
+
export declare function parseAllowedSenders(raw: string | string[] | undefined): string[];
|
|
39
|
+
/**
|
|
40
|
+
* Whether `from` is permitted by the allow-list. An empty (or absent)
|
|
41
|
+
* allow-list permits everything: {@link resolveEmailConfig} is what turns
|
|
42
|
+
* the configured {@link SenderPolicy} into concrete patterns, so an empty
|
|
43
|
+
* list here means the policy had nothing to narrow to.
|
|
44
|
+
*/
|
|
45
|
+
export declare function isSenderAllowed(from: string, patterns: string[]): boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Throw when `from` is not permitted by the allow-list. No-op when the
|
|
48
|
+
* allow-list is empty. The single enforcement point for the restriction
|
|
49
|
+
* (called from {@link sendEmail}), so every send path is covered whether
|
|
50
|
+
* the address was derived server-side or chosen in a UI.
|
|
51
|
+
*/
|
|
52
|
+
export declare function assertSenderAllowed(from: string, patterns: string[]): void;
|
|
53
|
+
/**
|
|
54
|
+
* Resolve the `From` address for a send from the resolved config and the
|
|
55
|
+
* current OBO user: explicit `from` wins, then `<local>@<domain>`, then
|
|
56
|
+
* (file/outbox mode only) the user's email verbatim. Throws when none of
|
|
57
|
+
* those yield an address.
|
|
58
|
+
*/
|
|
59
|
+
export declare function resolveSenderAddress(config: ResolvedEmailConfig, userEmail: string | undefined): string;
|
|
60
|
+
/**
|
|
61
|
+
* Expand the resolved config's allow-list into the concrete `From`
|
|
62
|
+
* addresses offered to the current user - the data a UI sender dropdown
|
|
63
|
+
* renders. Exact-address patterns pass through; domain wildcards
|
|
64
|
+
* (`*@domain.com` / bare `domain.com`) are concretized as
|
|
65
|
+
* `<user-local>@<domain>` and dropped when no OBO user local part is
|
|
66
|
+
* available. When no allow-list is configured, the single default sender
|
|
67
|
+
* ({@link resolveSenderAddress}) is returned when it can be resolved,
|
|
68
|
+
* else an empty list. The default resolved sender, when permitted, is
|
|
69
|
+
* ordered first.
|
|
70
|
+
*/
|
|
71
|
+
export declare function listSenderOptions(config: ResolvedEmailConfig, userEmail: string | undefined): string[];
|