@aglyn/plugins-outreach 1.0.0-beta.264 → 1.0.0-beta.266
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aglyn/plugins-outreach",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.266",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"homepage": "https://aglyn.com",
|
|
6
6
|
"repository": {
|
|
@@ -25,19 +25,19 @@
|
|
|
25
25
|
"./package.json": "./package.json"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@aglyn/aglyn": "1.0.0-beta.
|
|
29
|
-
"@aglyn/shared-data-mdi": "1.0.0-beta.
|
|
30
|
-
"@aglyn/shared-ui-email-campaigns": "1.0.0-beta.
|
|
31
|
-
"@aglyn/shared-ui-jsx": "1.0.0-beta.
|
|
32
|
-
"@aglyn/shared-ui-next": "1.0.0-beta.
|
|
33
|
-
"@aglyn/shared-ui-snackstack": "1.0.0-beta.
|
|
34
|
-
"@aglyn/shared-util-email": "1.0.0-beta.
|
|
35
|
-
"@aglyn/shared-util-http": "1.0.0-beta.
|
|
36
|
-
"@aglyn/shared-util-timestamp": "1.0.0-beta.
|
|
37
|
-
"@aglyn/shared-util-tools": "1.0.0-beta.
|
|
38
|
-
"@aglyn/tenant-data-admin": "1.0.0-beta.
|
|
39
|
-
"@aglyn/tenant-feature-instance": "1.0.0-beta.
|
|
40
|
-
"@aglyn/tenant-runtime": "1.0.0-beta.
|
|
28
|
+
"@aglyn/aglyn": "1.0.0-beta.266",
|
|
29
|
+
"@aglyn/shared-data-mdi": "1.0.0-beta.266",
|
|
30
|
+
"@aglyn/shared-ui-email-campaigns": "1.0.0-beta.266",
|
|
31
|
+
"@aglyn/shared-ui-jsx": "1.0.0-beta.266",
|
|
32
|
+
"@aglyn/shared-ui-next": "1.0.0-beta.266",
|
|
33
|
+
"@aglyn/shared-ui-snackstack": "1.0.0-beta.266",
|
|
34
|
+
"@aglyn/shared-util-email": "1.0.0-beta.266",
|
|
35
|
+
"@aglyn/shared-util-http": "1.0.0-beta.266",
|
|
36
|
+
"@aglyn/shared-util-timestamp": "1.0.0-beta.266",
|
|
37
|
+
"@aglyn/shared-util-tools": "1.0.0-beta.266",
|
|
38
|
+
"@aglyn/tenant-data-admin": "1.0.0-beta.266",
|
|
39
|
+
"@aglyn/tenant-feature-instance": "1.0.0-beta.266",
|
|
40
|
+
"@aglyn/tenant-runtime": "1.0.0-beta.266",
|
|
41
41
|
"@swc/helpers": "0.5.23"
|
|
42
42
|
},
|
|
43
43
|
"peerDependencies": {
|
|
@@ -35,6 +35,15 @@ export interface OutreachMailboxNotice {
|
|
|
35
35
|
export interface OutreachMailboxNoticeEmail {
|
|
36
36
|
subject: string;
|
|
37
37
|
text: string;
|
|
38
|
+
/** What happened and what to do, without the link: the designed email's body. */
|
|
39
|
+
lead: string;
|
|
40
|
+
/** "Label: value" lines for the designed email's details panel. */
|
|
41
|
+
details: string;
|
|
42
|
+
/** The Mailboxes page, when there is one to link. */
|
|
43
|
+
url: string | null;
|
|
44
|
+
urlLabel: string;
|
|
45
|
+
/** Why this reader is getting it. */
|
|
46
|
+
reason: string;
|
|
38
47
|
}
|
|
39
48
|
/** The mailbox as the notice names it: `Avery Quinn <avery@example.com>`, the send-as beside it when it differs. */
|
|
40
49
|
export declare function outreachMailboxNoticeName(mailbox: OutreachMailboxNotice['mailbox']): string;
|
|
@@ -52,16 +52,35 @@ function waitingSentence(waiting) {
|
|
|
52
52
|
const subject = paused ? `Sequences paused your mailbox ${notice.mailbox.email}` : `Your mailbox ${notice.mailbox.email} needs reconnecting`;
|
|
53
53
|
const action = paused ? 'Re-check the addresses in the sequences it sends, then open Mailboxes and select Resume.' : 'Open Mailboxes and select Reconnect to connect it again.';
|
|
54
54
|
const where = options.mailboxesUrl ? `${action}\n\n${options.mailboxesUrl}` : `${action} Mailboxes is under Sequences in your workspace’s console.`;
|
|
55
|
+
const reason = 'You are getting this because the mailbox is yours, or you manage the organization it belongs to.';
|
|
56
|
+
const opening = paused ? `Sequences paused the mailbox ${name}.` : `The mailbox ${name} needs reconnecting.`;
|
|
57
|
+
const message = String((_notice_message = notice.message) != null ? _notice_message : '').trim();
|
|
55
58
|
const text = [
|
|
56
|
-
|
|
57
|
-
|
|
59
|
+
opening,
|
|
60
|
+
message,
|
|
58
61
|
waitingSentence(notice.waiting),
|
|
59
62
|
where,
|
|
60
|
-
|
|
63
|
+
reason
|
|
61
64
|
].filter(Boolean).join('\n\n');
|
|
65
|
+
const waiting = Math.max(0, Math.floor(Number(notice.waiting) || 0));
|
|
66
|
+
const details = [
|
|
67
|
+
`Mailbox: ${notice.mailbox.email}`,
|
|
68
|
+
`Status: ${paused ? 'Paused by Sequences' : 'Needs reconnecting'}`,
|
|
69
|
+
`Enrollments waiting: ${waiting.toLocaleString('en-US')}`
|
|
70
|
+
].join('\n');
|
|
71
|
+
const lead = [
|
|
72
|
+
message || opening,
|
|
73
|
+
waitingSentence(notice.waiting),
|
|
74
|
+
action
|
|
75
|
+
].join(' ');
|
|
62
76
|
return {
|
|
63
77
|
subject,
|
|
64
|
-
text
|
|
78
|
+
text,
|
|
79
|
+
lead,
|
|
80
|
+
details,
|
|
81
|
+
url: options.mailboxesUrl,
|
|
82
|
+
urlLabel: paused ? 'Open Mailboxes to resume' : 'Open Mailboxes to reconnect',
|
|
83
|
+
reason
|
|
65
84
|
};
|
|
66
85
|
}
|
|
67
86
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/outreach/src/lib/engine/mailbox-notice.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*==========================================\n * WHAT THE MAILBOX'S OWNER IS TOLD (AGL-3244).\n *\n * A mailbox that pauses itself, or that Google stops accepting, holds every\n * enrollment on it until a person acts — and the person is the member whose\n * mailbox it is, who is not watching the Mailboxes page. So the moment the\n * runtime judges it, they are emailed: the engine's own sentence for the\n * pause, the mailbox by name, how many enrollments are waiting on it, and\n * the link to the page where Resume and Reconnect live.\n *\n * Plain text, one screen long, and the reason in the first line: it is read\n * on a phone between meetings.\n *==========================================*/\n\nexport type OutreachMailboxNoticeKind = 'auto_pause' | 'reconnect_required'\n\nexport interface OutreachMailboxNotice {\n kind: OutreachMailboxNoticeKind\n mailbox: {\n /** The account's own address. */\n email: string\n /** The address mail goes out as, when it is not the account's. */\n sendAs: string\n displayName: string\n }\n /**\n * For a pause, the engine's own sentence — `autoPause.message`; for a\n * reconnect, {@link outreachReconnectRequiredSentence}.\n */\n message: string\n /** Enrollments active on the mailbox, waiting for it to send again. */\n waiting: number\n}\n\nexport interface OutreachMailboxNoticeEmail {\n subject: string\n text: string\n}\n\n/** The mailbox as the notice names it: `Avery Quinn <avery@example.com>`, the send-as beside it when it differs. */\nexport function outreachMailboxNoticeName(mailbox: OutreachMailboxNotice['mailbox']): string {\n const name = String(mailbox.displayName ?? '').trim()\n const own = name ? `${name} <${mailbox.email}>` : mailbox.email\n const sendAs = String(mailbox.sendAs ?? '')\n .trim()\n .toLowerCase()\n return sendAs && sendAs !== String(mailbox.email).trim().toLowerCase() ? `${own}, sending as ${sendAs}` : own\n}\n\n/**\n * What the notice says when the provider stopped accepting the mailbox's\n * connection: Google's, or Microsoft's for a Microsoft 365 mailbox (AGL-3489).\n */\nexport function outreachReconnectRequiredSentence(\n errorCode: string | null | undefined,\n provider: string | null | undefined = 'google',\n): string {\n const code = String(errorCode ?? '').trim()\n const name = provider === 'microsoft' ? 'Microsoft' : 'Google'\n return (\n `${name} stopped accepting this mailbox’s connection` +\n (code ? ` (${code})` : '') +\n ` — the password changed, access was removed from the ${name} account, or an administrator revoked it. ` +\n 'Nothing sends from it until it is connected again.'\n )\n}\n\nfunction waitingSentence(waiting: number): string {\n const count = Math.max(0, Math.floor(Number(waiting) || 0))\n if (count === 0) return 'No enrollment is waiting on it right now.'\n return count === 1\n ? '1 enrollment is active on it and waiting; its next email goes out once the mailbox sends again.'\n : `${count} enrollments are active on it and waiting; their next emails go out once the mailbox sends again.`\n}\n\n/** The email, composed — see the module note. */\nexport function composeOutreachMailboxNotice(\n notice: OutreachMailboxNotice,\n options: { mailboxesUrl: string | null },\n): OutreachMailboxNoticeEmail {\n const name = outreachMailboxNoticeName(notice.mailbox)\n const paused = notice.kind === 'auto_pause'\n const subject = paused\n ? `Sequences paused your mailbox ${notice.mailbox.email}`\n : `Your mailbox ${notice.mailbox.email} needs reconnecting`\n const action = paused\n ? 'Re-check the addresses in the sequences it sends, then open Mailboxes and select Resume.'\n : 'Open Mailboxes and select Reconnect to connect it again.'\n const where = options.mailboxesUrl\n ? `${action}\\n\\n${options.mailboxesUrl}`\n : `${action} Mailboxes is under Sequences in your workspace’s console.`\n const
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/outreach/src/lib/engine/mailbox-notice.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*==========================================\n * WHAT THE MAILBOX'S OWNER IS TOLD (AGL-3244).\n *\n * A mailbox that pauses itself, or that Google stops accepting, holds every\n * enrollment on it until a person acts — and the person is the member whose\n * mailbox it is, who is not watching the Mailboxes page. So the moment the\n * runtime judges it, they are emailed: the engine's own sentence for the\n * pause, the mailbox by name, how many enrollments are waiting on it, and\n * the link to the page where Resume and Reconnect live.\n *\n * Plain text, one screen long, and the reason in the first line: it is read\n * on a phone between meetings.\n *==========================================*/\n\nexport type OutreachMailboxNoticeKind = 'auto_pause' | 'reconnect_required'\n\nexport interface OutreachMailboxNotice {\n kind: OutreachMailboxNoticeKind\n mailbox: {\n /** The account's own address. */\n email: string\n /** The address mail goes out as, when it is not the account's. */\n sendAs: string\n displayName: string\n }\n /**\n * For a pause, the engine's own sentence — `autoPause.message`; for a\n * reconnect, {@link outreachReconnectRequiredSentence}.\n */\n message: string\n /** Enrollments active on the mailbox, waiting for it to send again. */\n waiting: number\n}\n\nexport interface OutreachMailboxNoticeEmail {\n subject: string\n text: string\n /** What happened and what to do, without the link: the designed email's body. */\n lead: string\n /** \"Label: value\" lines for the designed email's details panel. */\n details: string\n /** The Mailboxes page, when there is one to link. */\n url: string | null\n urlLabel: string\n /** Why this reader is getting it. */\n reason: string\n}\n\n/** The mailbox as the notice names it: `Avery Quinn <avery@example.com>`, the send-as beside it when it differs. */\nexport function outreachMailboxNoticeName(mailbox: OutreachMailboxNotice['mailbox']): string {\n const name = String(mailbox.displayName ?? '').trim()\n const own = name ? `${name} <${mailbox.email}>` : mailbox.email\n const sendAs = String(mailbox.sendAs ?? '')\n .trim()\n .toLowerCase()\n return sendAs && sendAs !== String(mailbox.email).trim().toLowerCase() ? `${own}, sending as ${sendAs}` : own\n}\n\n/**\n * What the notice says when the provider stopped accepting the mailbox's\n * connection: Google's, or Microsoft's for a Microsoft 365 mailbox (AGL-3489).\n */\nexport function outreachReconnectRequiredSentence(\n errorCode: string | null | undefined,\n provider: string | null | undefined = 'google',\n): string {\n const code = String(errorCode ?? '').trim()\n const name = provider === 'microsoft' ? 'Microsoft' : 'Google'\n return (\n `${name} stopped accepting this mailbox’s connection` +\n (code ? ` (${code})` : '') +\n ` — the password changed, access was removed from the ${name} account, or an administrator revoked it. ` +\n 'Nothing sends from it until it is connected again.'\n )\n}\n\nfunction waitingSentence(waiting: number): string {\n const count = Math.max(0, Math.floor(Number(waiting) || 0))\n if (count === 0) return 'No enrollment is waiting on it right now.'\n return count === 1\n ? '1 enrollment is active on it and waiting; its next email goes out once the mailbox sends again.'\n : `${count} enrollments are active on it and waiting; their next emails go out once the mailbox sends again.`\n}\n\n/** The email, composed — see the module note. */\nexport function composeOutreachMailboxNotice(\n notice: OutreachMailboxNotice,\n options: { mailboxesUrl: string | null },\n): OutreachMailboxNoticeEmail {\n const name = outreachMailboxNoticeName(notice.mailbox)\n const paused = notice.kind === 'auto_pause'\n const subject = paused\n ? `Sequences paused your mailbox ${notice.mailbox.email}`\n : `Your mailbox ${notice.mailbox.email} needs reconnecting`\n const action = paused\n ? 'Re-check the addresses in the sequences it sends, then open Mailboxes and select Resume.'\n : 'Open Mailboxes and select Reconnect to connect it again.'\n const where = options.mailboxesUrl\n ? `${action}\\n\\n${options.mailboxesUrl}`\n : `${action} Mailboxes is under Sequences in your workspace’s console.`\n const reason = 'You are getting this because the mailbox is yours, or you manage the organization it belongs to.'\n const opening = paused ? `Sequences paused the mailbox ${name}.` : `The mailbox ${name} needs reconnecting.`\n const message = String(notice.message ?? '').trim()\n const text = [opening, message, waitingSentence(notice.waiting), where, reason].filter(Boolean).join('\\n\\n')\n const waiting = Math.max(0, Math.floor(Number(notice.waiting) || 0))\n const details = [\n `Mailbox: ${notice.mailbox.email}`,\n `Status: ${paused ? 'Paused by Sequences' : 'Needs reconnecting'}`,\n `Enrollments waiting: ${waiting.toLocaleString('en-US')}`,\n ].join('\\n')\n const lead = [message || opening, waitingSentence(notice.waiting), action].join(' ')\n return {\n subject,\n text,\n lead,\n details,\n url: options.mailboxesUrl,\n urlLabel: paused ? 'Open Mailboxes to resume' : 'Open Mailboxes to reconnect',\n reason,\n }\n}\n"],"names":["outreachMailboxNoticeName","mailbox","name","String","displayName","trim","own","email","sendAs","toLowerCase","outreachReconnectRequiredSentence","errorCode","provider","code","waitingSentence","waiting","count","Math","max","floor","Number","composeOutreachMailboxNotice","notice","options","paused","kind","subject","action","where","mailboxesUrl","reason","opening","message","text","filter","Boolean","join","details","toLocaleString","lead","url","urlLabel"],"mappings":"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"}
|
|
@@ -176,6 +176,11 @@ import { createOutreachUnsubscribeRoute } from "./unsubscribe-route.js";
|
|
|
176
176
|
includeAdmins: true,
|
|
177
177
|
subject: email.subject,
|
|
178
178
|
text: email.text,
|
|
179
|
+
lead: email.lead,
|
|
180
|
+
details: email.details,
|
|
181
|
+
url: email.url,
|
|
182
|
+
urlLabel: email.urlLabel,
|
|
183
|
+
reason: email.reason,
|
|
179
184
|
context: 'outreach-mailbox-notice'
|
|
180
185
|
});
|
|
181
186
|
if (!result.sent) console.warn(`[outreach] the mailbox owner was not emailed: ${result.reason}`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/outreach/src/lib/runtime/platform-runtime-deps.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { SALES_TOPIC_ID } from '../model/sales-topic'\nimport { platformMailDnsResolver } from '@aglyn/tenant-data-admin/server/email-deliverability'\nimport { checkEntitlement } from '@aglyn/aglyn/app-utils/plan-entitlements'\nimport { isPluginEnabled } from '@aglyn/aglyn/plugin-manager/enabled-plugins'\nimport { pluginRecordTimelineWriter } from '@aglyn/aglyn/plugin-manager/plugin-record-timeline'\nimport {\n creditConversion,\n creditConversionOutcome,\n recordConversionClick,\n} from '@aglyn/aglyn/plugin-manager/plugin-conversion-credit'\nimport { suppressEmail } from '@aglyn/tenant-data-admin/server/email-suppression'\nimport { recordTopicOptOut } from '@aglyn/tenant-data-admin/server/topic-subscriptions'\nimport { firebaseAdmin } from '@aglyn/tenant-data-admin/server/firebase-admin'\nimport { getLockdownVerdict } from '@aglyn/tenant-data-admin/server/lockdown'\nimport { sendOrgMemberNotice } from '@aglyn/tenant-data-admin/server/org-member-notice'\nimport { logOrgActivity } from '@aglyn/tenant-data-admin/server/organizations'\nimport { filterEnabledPluginsByReleaseFlags } from '@aglyn/tenant-data-admin/server/release-flags'\nimport { resolveTrackingLinkOrigin } from '@aglyn/tenant-data-admin/server/tracking-hosts'\nimport { sendingWorkspaceFor } from '@aglyn/tenant-data-admin/server/outbound-send-review'\nimport { screenTenantMessage } from '@aglyn/shared-util-email/outbound-screen-gate'\nimport { stampRecordEmailState } from '@aglyn/aglyn/plugin-manager/plugin-record-email-state'\nimport { OUTREACH_PLUGIN_ID } from '../constants/bundle-common'\nimport { composeOutreachMailboxNotice } from '../engine/mailbox-notice'\nimport { openOutreachMailboxClient } from '../mailboxes/mailbox-transport'\nimport { canonicalConsoleOrigin } from '../mailboxes/oauth-redirect'\nimport type { PluginWebApiHandler } from '@aglyn/aglyn/server'\nimport type { PluginPersonEraser } from '@aglyn/aglyn/plugin-manager/plugin-person-erasure'\nimport { outreachShortLinkUrl } from './click-link'\nimport { createOutreachClickRoute, createOutreachShortLinkRoute } from './click-route'\nimport { createOutreachPersonEraser } from './person-erasure'\nimport type { OutreachCampaignCredit, OutreachRuntimeDeps } from './runtime-deps'\nimport { outreachUnsubscribeUrl } from './unsubscribe-link'\nimport { createOutreachUnsubscribeRoute } from './unsubscribe-route'\n\n/**\n * What a sequence produced (AGL-3254), handed to whichever plugin credits\n * outcomes (`plugin-conversion-credit`): the same contract every form,\n * booking and campaign email is credited through, so a sequence's numbers\n * and a campaign send's are two readings of one rule. A workspace whose\n * plugins credit nothing credits nothing here either; the seam never throws.\n *\n * The campaign a sequence is filed under is what its mail is credited to,\n * and the sequence and the enrollment ride with it as the mail's own facts.\n */\nexport function platformOutreachCampaignCredit(): OutreachCampaignCredit {\n return {\n async credit({ hostId, orgId, campaignIds, outcome, atMs }) {\n await creditConversionOutcome({ hostId, orgId, containerIds: campaignIds, outcome, atMs })\n },\n async attributeRecord({ hostId, kind, refId, campaignId, sequenceId, enrollmentId, atMs }) {\n await creditConversion({\n hostId,\n kind,\n refId,\n click: { hostId, creditTo: campaignId, atMs, via: { sequenceId, enrollmentId } },\n convertedAtMs: atMs,\n })\n },\n async recordTouch({ hostId, email, campaignId, sequenceId, enrollmentId, atMs }) {\n await recordConversionClick({\n email,\n hostId,\n creditTo: campaignId,\n atMs,\n via: { sequenceId, enrollmentId },\n })\n },\n }\n}\n\n/**\n * The platform's own reach for the sending runtime (AGL-2981). Loaded only\n * when a job runs or the unsubscribe route answers — never at boot — so the\n * console pays for these modules when Outreach does work.\n */\nexport function platformOutreachRuntimeDeps(): OutreachRuntimeDeps {\n const firestore = () => firebaseAdmin.app().firestore()\n return {\n firestore,\n now: Date.now,\n random: Math.random,\n campaignCredit: platformOutreachCampaignCredit(),\n openMailbox: (mailboxId) => openOutreachMailboxClient(firestore(), { mailboxId }),\n // The domain's MX (AGL-3326) and, for a domain with none, its address\n // record — through the platform's pinned resolver (AGL-3328), which\n // asks twice before it says a domain takes no mail.\n resolveMx: (domain) => platformMailDnsResolver().resolveMx(domain),\n resolveAddress: (domain) => platformMailDnsResolver().resolveAddress?.(domain) ?? Promise.resolve(false),\n async orgRefusal(orgId, org) {\n if (!isPluginEnabled(org as { enabledPlugins?: string[] }, OUTREACH_PLUGIN_ID)) return 'plugin-disabled'\n if (!checkEntitlement(org, 'outreach')) return 'not-entitled'\n const released = await filterEnabledPluginsByReleaseFlags([OUTREACH_PLUGIN_ID], { orgId })\n return released.length ? null : 'not-released'\n },\n async sendRefusal({ org, uid }) {\n const locked = await getLockdownVerdict({ org, uid, intent: 'write' })\n return locked ? `locked:${locked.scope}` : null\n },\n timeline: () => pluginRecordTimelineWriter()?.writer ?? null,\n logOrgActivity: (orgId, actor, action, target) => logOrgActivity(orgId, actor, action, target),\n async optOutOfSalesTopic({ hostId, email }) {\n await recordTopicOptOut(hostId, email, SALES_TOPIC_ID, { firestore: firestore() })\n },\n async suppressBouncedEmail({ email, hostId }) {\n // The runtime stamps the record itself, with the richer verdict — the\n // domain block, the enrollment — so the list's own stamp stands down.\n await suppressEmail({ email, reason: 'bounce', context: 'outreach', hostId, stampRecord: false, firestore: firestore() })\n },\n async stampRecordEmailState(stamp) {\n await stampRecordEmailState(stamp)\n },\n async notifyMailboxOwner(notice) {\n // The Mailboxes page, by the organization's slug — the page Resume and\n // Reconnect live on (AGL-3244). Without a slug or a console origin the\n // notice still goes, and says where the page is instead.\n const org = await firestore().collection('orgs').doc(notice.orgId).get()\n const slug = typeof org.get('slug') === 'string' ? String(org.get('slug')) : ''\n const origin = canonicalConsoleOrigin()\n const mailboxesUrl = slug && origin ? `${origin}/${encodeURIComponent(slug)}/outreach/mailboxes` : null\n const email = composeOutreachMailboxNotice(notice, { mailboxesUrl })\n const result = await sendOrgMemberNotice({\n orgId: notice.orgId,\n uids: [notice.connectedByUid],\n includeAdmins: true,\n subject: email.subject,\n text: email.text,\n context: 'outreach-mailbox-notice',\n })\n if (!result.sent) console.warn(`[outreach] the mailbox owner was not emailed: ${result.reason}`)\n },\n unsubscribeUrl: (target) => outreachUnsubscribeUrl({ origin: canonicalConsoleOrigin(), target }),\n clickLinkUrl: (linkId, trackingOrigin) =>\n outreachShortLinkUrl({ origin: canonicalConsoleOrigin(), linkId, trackingOrigin }),\n // The sending domain's own `links.` host once the organization has\n // verified it (AGL-3306). A static import like every other\n // `tenant-data-admin` module here: a dynamic one marks the whole library\n // lazy-loaded, and `@nx/enforce-module-boundaries` then refuses every\n // static import of it across the plugin and the console.\n clickLinkOrigin({ orgId, senderAddress }) {\n return resolveTrackingLinkOrigin(firestore(), orgId, senderAddress)\n },\n // The one phishing screen every tenant message passes (AGL-3356). The\n // review module's import installs its store, so a hold files the same\n // abuse-queue row a held newsletter or campaign does.\n async screenMessage({ orgId, org, hostId, host, subject, text, fromName, fromAddress }) {\n const refusal = await screenTenantMessage(\n {\n workspace: sendingWorkspaceFor({ hostId, orgId, org, host }),\n subject,\n fromName,\n fromAddress: fromAddress ?? null,\n bodies: [text],\n context: 'outreach sequence',\n },\n 'outreach email',\n )\n return refusal?.detail ?? null\n },\n }\n}\n\n/** The unsubscribe route on the platform's own reach. */\nexport function platformOutreachUnsubscribeRoute(): PluginWebApiHandler {\n return createOutreachUnsubscribeRoute(platformOutreachRuntimeDeps())\n}\n\n/** The click-tracking redirect on the platform's own reach (AGL-3239). */\nexport function platformOutreachClickRoute(): PluginWebApiHandler {\n return createOutreachClickRoute(platformOutreachRuntimeDeps())\n}\n\n/** The short tracking link's redirect on the platform's own reach (AGL-3297). */\nexport function platformOutreachShortLinkRoute(): PluginWebApiHandler {\n return createOutreachShortLinkRoute(platformOutreachRuntimeDeps())\n}\n\n/** The person eraser on the platform's own Firestore. */\nexport function platformOutreachPersonEraser(): PluginPersonEraser {\n return createOutreachPersonEraser({ firestore: () => firebaseAdmin.app().firestore() })\n}\n"],"names":["SALES_TOPIC_ID","platformMailDnsResolver","checkEntitlement","isPluginEnabled","pluginRecordTimelineWriter","creditConversion","creditConversionOutcome","recordConversionClick","suppressEmail","recordTopicOptOut","firebaseAdmin","getLockdownVerdict","sendOrgMemberNotice","logOrgActivity","filterEnabledPluginsByReleaseFlags","resolveTrackingLinkOrigin","sendingWorkspaceFor","screenTenantMessage","stampRecordEmailState","OUTREACH_PLUGIN_ID","composeOutreachMailboxNotice","openOutreachMailboxClient","canonicalConsoleOrigin","outreachShortLinkUrl","createOutreachClickRoute","createOutreachShortLinkRoute","createOutreachPersonEraser","outreachUnsubscribeUrl","createOutreachUnsubscribeRoute","platformOutreachCampaignCredit","credit","hostId","orgId","campaignIds","outcome","atMs","containerIds","attributeRecord","kind","refId","campaignId","sequenceId","enrollmentId","click","creditTo","via","convertedAtMs","recordTouch","email","platformOutreachRuntimeDeps","firestore","app","now","Date","random","Math","campaignCredit","openMailbox","mailboxId","resolveMx","domain","resolveAddress","Promise","resolve","orgRefusal","org","released","length","sendRefusal","uid","locked","intent","scope","timeline","writer","actor","action","target","optOutOfSalesTopic","suppressBouncedEmail","reason","context","stampRecord","stamp","notifyMailboxOwner","notice","collection","doc","get","slug","String","origin","mailboxesUrl","encodeURIComponent","result","uids","connectedByUid","includeAdmins","subject","text","sent","console","warn","unsubscribeUrl","clickLinkUrl","linkId","trackingOrigin","clickLinkOrigin","senderAddress","screenMessage","host","fromName","fromAddress","refusal","workspace","bodies","detail","platformOutreachUnsubscribeRoute","platformOutreachClickRoute","platformOutreachShortLinkRoute","platformOutreachPersonEraser"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/outreach/src/lib/runtime/platform-runtime-deps.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { SALES_TOPIC_ID } from '../model/sales-topic'\nimport { platformMailDnsResolver } from '@aglyn/tenant-data-admin/server/email-deliverability'\nimport { checkEntitlement } from '@aglyn/aglyn/app-utils/plan-entitlements'\nimport { isPluginEnabled } from '@aglyn/aglyn/plugin-manager/enabled-plugins'\nimport { pluginRecordTimelineWriter } from '@aglyn/aglyn/plugin-manager/plugin-record-timeline'\nimport {\n creditConversion,\n creditConversionOutcome,\n recordConversionClick,\n} from '@aglyn/aglyn/plugin-manager/plugin-conversion-credit'\nimport { suppressEmail } from '@aglyn/tenant-data-admin/server/email-suppression'\nimport { recordTopicOptOut } from '@aglyn/tenant-data-admin/server/topic-subscriptions'\nimport { firebaseAdmin } from '@aglyn/tenant-data-admin/server/firebase-admin'\nimport { getLockdownVerdict } from '@aglyn/tenant-data-admin/server/lockdown'\nimport { sendOrgMemberNotice } from '@aglyn/tenant-data-admin/server/org-member-notice'\nimport { logOrgActivity } from '@aglyn/tenant-data-admin/server/organizations'\nimport { filterEnabledPluginsByReleaseFlags } from '@aglyn/tenant-data-admin/server/release-flags'\nimport { resolveTrackingLinkOrigin } from '@aglyn/tenant-data-admin/server/tracking-hosts'\nimport { sendingWorkspaceFor } from '@aglyn/tenant-data-admin/server/outbound-send-review'\nimport { screenTenantMessage } from '@aglyn/shared-util-email/outbound-screen-gate'\nimport { stampRecordEmailState } from '@aglyn/aglyn/plugin-manager/plugin-record-email-state'\nimport { OUTREACH_PLUGIN_ID } from '../constants/bundle-common'\nimport { composeOutreachMailboxNotice } from '../engine/mailbox-notice'\nimport { openOutreachMailboxClient } from '../mailboxes/mailbox-transport'\nimport { canonicalConsoleOrigin } from '../mailboxes/oauth-redirect'\nimport type { PluginWebApiHandler } from '@aglyn/aglyn/server'\nimport type { PluginPersonEraser } from '@aglyn/aglyn/plugin-manager/plugin-person-erasure'\nimport { outreachShortLinkUrl } from './click-link'\nimport { createOutreachClickRoute, createOutreachShortLinkRoute } from './click-route'\nimport { createOutreachPersonEraser } from './person-erasure'\nimport type { OutreachCampaignCredit, OutreachRuntimeDeps } from './runtime-deps'\nimport { outreachUnsubscribeUrl } from './unsubscribe-link'\nimport { createOutreachUnsubscribeRoute } from './unsubscribe-route'\n\n/**\n * What a sequence produced (AGL-3254), handed to whichever plugin credits\n * outcomes (`plugin-conversion-credit`): the same contract every form,\n * booking and campaign email is credited through, so a sequence's numbers\n * and a campaign send's are two readings of one rule. A workspace whose\n * plugins credit nothing credits nothing here either; the seam never throws.\n *\n * The campaign a sequence is filed under is what its mail is credited to,\n * and the sequence and the enrollment ride with it as the mail's own facts.\n */\nexport function platformOutreachCampaignCredit(): OutreachCampaignCredit {\n return {\n async credit({ hostId, orgId, campaignIds, outcome, atMs }) {\n await creditConversionOutcome({ hostId, orgId, containerIds: campaignIds, outcome, atMs })\n },\n async attributeRecord({ hostId, kind, refId, campaignId, sequenceId, enrollmentId, atMs }) {\n await creditConversion({\n hostId,\n kind,\n refId,\n click: { hostId, creditTo: campaignId, atMs, via: { sequenceId, enrollmentId } },\n convertedAtMs: atMs,\n })\n },\n async recordTouch({ hostId, email, campaignId, sequenceId, enrollmentId, atMs }) {\n await recordConversionClick({\n email,\n hostId,\n creditTo: campaignId,\n atMs,\n via: { sequenceId, enrollmentId },\n })\n },\n }\n}\n\n/**\n * The platform's own reach for the sending runtime (AGL-2981). Loaded only\n * when a job runs or the unsubscribe route answers — never at boot — so the\n * console pays for these modules when Outreach does work.\n */\nexport function platformOutreachRuntimeDeps(): OutreachRuntimeDeps {\n const firestore = () => firebaseAdmin.app().firestore()\n return {\n firestore,\n now: Date.now,\n random: Math.random,\n campaignCredit: platformOutreachCampaignCredit(),\n openMailbox: (mailboxId) => openOutreachMailboxClient(firestore(), { mailboxId }),\n // The domain's MX (AGL-3326) and, for a domain with none, its address\n // record — through the platform's pinned resolver (AGL-3328), which\n // asks twice before it says a domain takes no mail.\n resolveMx: (domain) => platformMailDnsResolver().resolveMx(domain),\n resolveAddress: (domain) => platformMailDnsResolver().resolveAddress?.(domain) ?? Promise.resolve(false),\n async orgRefusal(orgId, org) {\n if (!isPluginEnabled(org as { enabledPlugins?: string[] }, OUTREACH_PLUGIN_ID)) return 'plugin-disabled'\n if (!checkEntitlement(org, 'outreach')) return 'not-entitled'\n const released = await filterEnabledPluginsByReleaseFlags([OUTREACH_PLUGIN_ID], { orgId })\n return released.length ? null : 'not-released'\n },\n async sendRefusal({ org, uid }) {\n const locked = await getLockdownVerdict({ org, uid, intent: 'write' })\n return locked ? `locked:${locked.scope}` : null\n },\n timeline: () => pluginRecordTimelineWriter()?.writer ?? null,\n logOrgActivity: (orgId, actor, action, target) => logOrgActivity(orgId, actor, action, target),\n async optOutOfSalesTopic({ hostId, email }) {\n await recordTopicOptOut(hostId, email, SALES_TOPIC_ID, { firestore: firestore() })\n },\n async suppressBouncedEmail({ email, hostId }) {\n // The runtime stamps the record itself, with the richer verdict — the\n // domain block, the enrollment — so the list's own stamp stands down.\n await suppressEmail({ email, reason: 'bounce', context: 'outreach', hostId, stampRecord: false, firestore: firestore() })\n },\n async stampRecordEmailState(stamp) {\n await stampRecordEmailState(stamp)\n },\n async notifyMailboxOwner(notice) {\n // The Mailboxes page, by the organization's slug — the page Resume and\n // Reconnect live on (AGL-3244). Without a slug or a console origin the\n // notice still goes, and says where the page is instead.\n const org = await firestore().collection('orgs').doc(notice.orgId).get()\n const slug = typeof org.get('slug') === 'string' ? String(org.get('slug')) : ''\n const origin = canonicalConsoleOrigin()\n const mailboxesUrl = slug && origin ? `${origin}/${encodeURIComponent(slug)}/outreach/mailboxes` : null\n const email = composeOutreachMailboxNotice(notice, { mailboxesUrl })\n const result = await sendOrgMemberNotice({\n orgId: notice.orgId,\n uids: [notice.connectedByUid],\n includeAdmins: true,\n subject: email.subject,\n text: email.text,\n lead: email.lead,\n details: email.details,\n url: email.url,\n urlLabel: email.urlLabel,\n reason: email.reason,\n context: 'outreach-mailbox-notice',\n })\n if (!result.sent) console.warn(`[outreach] the mailbox owner was not emailed: ${result.reason}`)\n },\n unsubscribeUrl: (target) => outreachUnsubscribeUrl({ origin: canonicalConsoleOrigin(), target }),\n clickLinkUrl: (linkId, trackingOrigin) =>\n outreachShortLinkUrl({ origin: canonicalConsoleOrigin(), linkId, trackingOrigin }),\n // The sending domain's own `links.` host once the organization has\n // verified it (AGL-3306). A static import like every other\n // `tenant-data-admin` module here: a dynamic one marks the whole library\n // lazy-loaded, and `@nx/enforce-module-boundaries` then refuses every\n // static import of it across the plugin and the console.\n clickLinkOrigin({ orgId, senderAddress }) {\n return resolveTrackingLinkOrigin(firestore(), orgId, senderAddress)\n },\n // The one phishing screen every tenant message passes (AGL-3356). The\n // review module's import installs its store, so a hold files the same\n // abuse-queue row a held newsletter or campaign does.\n async screenMessage({ orgId, org, hostId, host, subject, text, fromName, fromAddress }) {\n const refusal = await screenTenantMessage(\n {\n workspace: sendingWorkspaceFor({ hostId, orgId, org, host }),\n subject,\n fromName,\n fromAddress: fromAddress ?? null,\n bodies: [text],\n context: 'outreach sequence',\n },\n 'outreach email',\n )\n return refusal?.detail ?? null\n },\n }\n}\n\n/** The unsubscribe route on the platform's own reach. */\nexport function platformOutreachUnsubscribeRoute(): PluginWebApiHandler {\n return createOutreachUnsubscribeRoute(platformOutreachRuntimeDeps())\n}\n\n/** The click-tracking redirect on the platform's own reach (AGL-3239). */\nexport function platformOutreachClickRoute(): PluginWebApiHandler {\n return createOutreachClickRoute(platformOutreachRuntimeDeps())\n}\n\n/** The short tracking link's redirect on the platform's own reach (AGL-3297). */\nexport function platformOutreachShortLinkRoute(): PluginWebApiHandler {\n return createOutreachShortLinkRoute(platformOutreachRuntimeDeps())\n}\n\n/** The person eraser on the platform's own Firestore. */\nexport function platformOutreachPersonEraser(): PluginPersonEraser {\n return createOutreachPersonEraser({ firestore: () => firebaseAdmin.app().firestore() })\n}\n"],"names":["SALES_TOPIC_ID","platformMailDnsResolver","checkEntitlement","isPluginEnabled","pluginRecordTimelineWriter","creditConversion","creditConversionOutcome","recordConversionClick","suppressEmail","recordTopicOptOut","firebaseAdmin","getLockdownVerdict","sendOrgMemberNotice","logOrgActivity","filterEnabledPluginsByReleaseFlags","resolveTrackingLinkOrigin","sendingWorkspaceFor","screenTenantMessage","stampRecordEmailState","OUTREACH_PLUGIN_ID","composeOutreachMailboxNotice","openOutreachMailboxClient","canonicalConsoleOrigin","outreachShortLinkUrl","createOutreachClickRoute","createOutreachShortLinkRoute","createOutreachPersonEraser","outreachUnsubscribeUrl","createOutreachUnsubscribeRoute","platformOutreachCampaignCredit","credit","hostId","orgId","campaignIds","outcome","atMs","containerIds","attributeRecord","kind","refId","campaignId","sequenceId","enrollmentId","click","creditTo","via","convertedAtMs","recordTouch","email","platformOutreachRuntimeDeps","firestore","app","now","Date","random","Math","campaignCredit","openMailbox","mailboxId","resolveMx","domain","resolveAddress","Promise","resolve","orgRefusal","org","released","length","sendRefusal","uid","locked","intent","scope","timeline","writer","actor","action","target","optOutOfSalesTopic","suppressBouncedEmail","reason","context","stampRecord","stamp","notifyMailboxOwner","notice","collection","doc","get","slug","String","origin","mailboxesUrl","encodeURIComponent","result","uids","connectedByUid","includeAdmins","subject","text","lead","details","url","urlLabel","sent","console","warn","unsubscribeUrl","clickLinkUrl","linkId","trackingOrigin","clickLinkOrigin","senderAddress","screenMessage","host","fromName","fromAddress","refusal","workspace","bodies","detail","platformOutreachUnsubscribeRoute","platformOutreachClickRoute","platformOutreachShortLinkRoute","platformOutreachPersonEraser"],"mappings":"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"}
|