@odla-ai/chapter 0.59.1 → 0.60.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/README.md +17 -0
- package/dist/{chunk-SDGVPIXF.js → chunk-D5UR5H6R.js} +2 -2
- package/dist/{chunk-P6X3W2XP.js → chunk-J7ES2OWN.js} +12 -2
- package/dist/{chunk-P6X3W2XP.js.map → chunk-J7ES2OWN.js.map} +1 -1
- package/dist/{chunk-GADLTNJF.js → chunk-QNP3UEIL.js} +2 -1
- package/dist/chunk-QNP3UEIL.js.map +1 -0
- package/dist/{copy-context-BuJBQafl.d.ts → copy-context-BP3G1iBd.d.ts} +1 -1
- package/dist/index.cjs +141 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +54 -2
- package/dist/index.d.ts +54 -2
- package/dist/index.js +141 -3
- package/dist/index.js.map +1 -1
- package/dist/ui/admin/index.d.ts +2 -2
- package/dist/ui/admin/index.js +2 -2
- package/dist/ui/index.d.ts +1 -1
- package/dist/ui/index.js +3 -3
- package/dist/ui/member/index.d.ts +2 -2
- package/dist/ui/member/index.js +2 -2
- package/dist/worker/index.cjs +143 -5
- package/dist/worker/index.cjs.map +1 -1
- package/dist/worker/index.d.cts +1 -1
- package/dist/worker/index.d.ts +1 -1
- package/dist/worker/index.js +143 -5
- package/dist/worker/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-GADLTNJF.js.map +0 -1
- /package/dist/{chunk-SDGVPIXF.js.map → chunk-D5UR5H6R.js.map} +0 -0
package/dist/ui/admin/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import * as preact from 'preact';
|
|
2
2
|
import { ComponentChildren, JSX } from 'preact';
|
|
3
3
|
import { CrmClient, Crm, CrmRecord, FieldType } from '@odla-ai/crm';
|
|
4
|
-
import { a as Chapter, b as ChapterCopy, M as MemberContentDocument, E as EffectiveMemberContent, c as MemberContentTierAuthority } from '../../copy-context-
|
|
5
|
-
export { C as ChapterCopyProvider, u as useChapterCopy } from '../../copy-context-
|
|
4
|
+
import { a as Chapter, b as ChapterCopy, M as MemberContentDocument, E as EffectiveMemberContent, c as MemberContentTierAuthority } from '../../copy-context-BP3G1iBd.js';
|
|
5
|
+
export { C as ChapterCopyProvider, u as useChapterCopy } from '../../copy-context-BP3G1iBd.js';
|
|
6
6
|
import { CrmLifecycleAdapter, CrmWorkspaceMasterContext, CrmWorkspaceRecordContext, RecordPanelTab } from '@odla-ai/crm/ui';
|
|
7
7
|
|
|
8
8
|
/** Normalized public auth configuration consumed by the Clerk gate. */
|
package/dist/ui/admin/index.js
CHANGED
|
@@ -44,11 +44,11 @@ import {
|
|
|
44
44
|
networkRecordsWorkspace,
|
|
45
45
|
peopleSection,
|
|
46
46
|
useAdminResource
|
|
47
|
-
} from "../../chunk-
|
|
47
|
+
} from "../../chunk-J7ES2OWN.js";
|
|
48
48
|
import {
|
|
49
49
|
ChapterCopyProvider,
|
|
50
50
|
useChapterCopy
|
|
51
|
-
} from "../../chunk-
|
|
51
|
+
} from "../../chunk-QNP3UEIL.js";
|
|
52
52
|
export {
|
|
53
53
|
APPLICATION_STAGES,
|
|
54
54
|
AdminNote,
|
package/dist/ui/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { APPLICATION_STAGES, AdminAccountMenuProps, AdminBuilderLibraryData, AdminChrome, AdminMemberContentData, AdminNote, AdminPage, AdminResource, AdminRouteState, AdminRouteTarget, AdminRouting, AdminSection, AdminSectionContext, AdminWorkspace, AdminWorkspaceFrameProps, AdminWorkspaceNavProps, AdmissionGrantsWorkspace, AvailabilitySectionOptions, BillingSectionOptions, BuilderLibraryAdmin, CandidateStagePanel, CandidateStagePanelProps, ChapterAdmin, ChapterAdminAuthAdapter, ChapterAdminAuthConfig, ChapterAdminAuthLoadContext, ChapterAdminProps, ChapterAdminUser, CollectionSectionOptions, DISPUTE_STATUS_WORDS, DashboardSectionOptions, EmailSectionOptions, LeaderAdmissionAction, LeaderAdmissionActionProps, LeaderNetworkOverview, MeetingsSectionOptions, MemberContentAdmin, MembershipDisputeRow, MembershipDisputes, MembershipPolicyVersions, MembershipPolicyVersionsProps, NamedSeatRecordPanel, NamedSeatRecordPanelProps, NetworkRecordEditor, NetworkRecordsWorkspace, NetworkSeatBilling, NetworkShareActions, NetworkShareActionsProps, Panel, PaymentRecoveryQueue, PeopleSectionOptions, RecordActions, RecordActionsProps, RecordAdmissionPanel, RecordAdmissionPanelProps, ThemeWarning, adminFetch, adminRouteFromUrl, adminRouteHref, adminSectionFromUrl, adminSectionHref, admissionGrantsWorkspace, applicationLifecycleAdapter, availabilitySection, billingSection, builderLibrarySection, collectionSection, dashboardSection, defaultAdminSections, defaultAdminWorkspaces, emailSection, loadChapterAdminConfig, loadChapterAdminUser, meetingsSection, memberContentSection, networkRecordsWorkspace, peopleSection, useAdminResource } from './admin/index.js';
|
|
2
|
-
export { C as ChapterCopyProvider, u as useChapterCopy } from '../copy-context-
|
|
2
|
+
export { C as ChapterCopyProvider, u as useChapterCopy } from '../copy-context-BP3G1iBd.js';
|
|
3
3
|
export { ADDITIONAL_MEMBER_COPY, AdditionalMemberCopy, AdditionalMemberDraft, AdditionalMemberEditor, AdditionalMemberEditorOptions, AdditionalMemberEditorProps, AdditionalMemberFieldNames, AdditionalMemberFields, AdditionalMemberFieldsProps, AdditionalMemberOffer, AdditionalMemberOptions, ApiFn, BrandStyle, BrandStyleProps, CheckoutSeatEditor, CheckoutSeatEditorContext, GiftCodeRedeemResult, GiftCodeRedeemer, GiftCodeRedeemerCopy, GiftCodeRedeemerProps, GiftOfferCards, GiftOfferCardsMode, GiftOfferCardsProps, GiftPurchases, GiftPurchasesProps, JoinBookingPreload, JoinBookingSimulation, JoinConfig, JoinFlowState, JoinIsland, JoinIslandProps, JoinSimulation, JoinStepRenderContext, JoinSubmitButton, JoinSubmitRenderContext, JoinTier, JoinTierRenderContext, JourneyFrame, MEMBERSHIP_CARD_TOKEN_DEFAULTS, MemberContentArea, MemberContentAreaProps, MemberGifts, MemberProvisionalRenderContext, MemberRestart, MembersArea, MembersAreaProps, MembershipCardTokenName, MembershipCards, MembershipCardsMode, MembershipCardsProps, NAMED_SEAT_RECIPIENT_WORDS, NamedSeatInvitationLink, NamedSeatInvitationLinkProps, NamedSeatPurchase, NamedSeatRecipientLine, PaymentPriceLines, PaymentStep, PaymentStepProps, PaymentStepSimulation, RescheduleProps, Rescheduler, Slot, SlotPicker, SlotPickerClasses, SlotPickerProps, SubResponse, TierPlaceholder, additionalMemberSelection, dayKey, dayLabel, fmtDate, fmtMoney, fullLabel, groupSlotsByDay, loadJoinResume, membershipApplicationHref, namedSeatInvitationMessage, namedSeatInvitationShareable, namedSeatInvitationUrl, tierNeedsPayment, timeLabel, tzShort, useTierSelection } from './member/index.js';
|
|
4
4
|
import 'preact';
|
|
5
5
|
import '@odla-ai/crm';
|
package/dist/ui/index.js
CHANGED
|
@@ -38,7 +38,7 @@ import {
|
|
|
38
38
|
timeLabel,
|
|
39
39
|
tzShort,
|
|
40
40
|
useTierSelection
|
|
41
|
-
} from "../chunk-
|
|
41
|
+
} from "../chunk-D5UR5H6R.js";
|
|
42
42
|
import {
|
|
43
43
|
APPLICATION_STAGES,
|
|
44
44
|
AdminNote,
|
|
@@ -85,12 +85,12 @@ import {
|
|
|
85
85
|
networkRecordsWorkspace,
|
|
86
86
|
peopleSection,
|
|
87
87
|
useAdminResource
|
|
88
|
-
} from "../chunk-
|
|
88
|
+
} from "../chunk-J7ES2OWN.js";
|
|
89
89
|
import {
|
|
90
90
|
BrandStyle,
|
|
91
91
|
ChapterCopyProvider,
|
|
92
92
|
useChapterCopy
|
|
93
|
-
} from "../chunk-
|
|
93
|
+
} from "../chunk-QNP3UEIL.js";
|
|
94
94
|
import "../chunk-IVXVECKR.js";
|
|
95
95
|
export {
|
|
96
96
|
ADDITIONAL_MEMBER_COPY,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as preact from 'preact';
|
|
2
2
|
import { ComponentChildren, JSX } from 'preact';
|
|
3
|
-
import { d as MemberContentProjection, e as MemberContentPageProjection, b as ChapterCopy, f as ChapterBrand } from '../../copy-context-
|
|
4
|
-
export { C as ChapterCopyProvider, u as useChapterCopy } from '../../copy-context-
|
|
3
|
+
import { d as MemberContentProjection, e as MemberContentPageProjection, b as ChapterCopy, f as ChapterBrand } from '../../copy-context-BP3G1iBd.js';
|
|
4
|
+
export { C as ChapterCopyProvider, u as useChapterCopy } from '../../copy-context-BP3G1iBd.js';
|
|
5
5
|
import { FieldType, FieldConditions } from '@odla-ai/crm';
|
|
6
6
|
|
|
7
7
|
/** A bookable slot: a start instant in epoch milliseconds. */
|
package/dist/ui/member/index.js
CHANGED
|
@@ -38,12 +38,12 @@ import {
|
|
|
38
38
|
timeLabel,
|
|
39
39
|
tzShort,
|
|
40
40
|
useTierSelection
|
|
41
|
-
} from "../../chunk-
|
|
41
|
+
} from "../../chunk-D5UR5H6R.js";
|
|
42
42
|
import {
|
|
43
43
|
BrandStyle,
|
|
44
44
|
ChapterCopyProvider,
|
|
45
45
|
useChapterCopy
|
|
46
|
-
} from "../../chunk-
|
|
46
|
+
} from "../../chunk-QNP3UEIL.js";
|
|
47
47
|
import "../../chunk-IVXVECKR.js";
|
|
48
48
|
export {
|
|
49
49
|
ADDITIONAL_MEMBER_COPY,
|
package/dist/worker/index.cjs
CHANGED
|
@@ -1786,6 +1786,47 @@ function planDelivery(input) {
|
|
|
1786
1786
|
return { deliver: true, transport, to, subject, text: text10, redirected: redirect };
|
|
1787
1787
|
}
|
|
1788
1788
|
|
|
1789
|
+
// src/email-html.ts
|
|
1790
|
+
var URL_RE = /https?:\/\/[^\s<>"')\]]+[^\s<>"').,;:!?\]]/g;
|
|
1791
|
+
function escapeHtml(s) {
|
|
1792
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
1793
|
+
}
|
|
1794
|
+
function lineHtml(line) {
|
|
1795
|
+
let out = "", last = 0;
|
|
1796
|
+
for (const match of line.matchAll(URL_RE)) {
|
|
1797
|
+
out += escapeHtml(line.slice(last, match.index));
|
|
1798
|
+
const url = escapeHtml(match[0]);
|
|
1799
|
+
out += `<a href="${url}">${url}</a>`;
|
|
1800
|
+
last = match.index + match[0].length;
|
|
1801
|
+
}
|
|
1802
|
+
return out + escapeHtml(line.slice(last));
|
|
1803
|
+
}
|
|
1804
|
+
function textToHtml(text10) {
|
|
1805
|
+
const paragraphs = text10.replace(/\r\n/g, "\n").trim().split(/\n{2,}/).filter((paragraph) => paragraph.trim());
|
|
1806
|
+
const body = paragraphs.map((paragraph) => `<p>${paragraph.split("\n").map(lineHtml).join("<br>\n")}</p>`).join("\n");
|
|
1807
|
+
return `<!doctype html>
|
|
1808
|
+
<html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width"></head>
|
|
1809
|
+
<body style="margin:0;padding:24px 16px;background:#ffffff;color:#1a1a1a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Helvetica,Arial,sans-serif;font-size:16px;line-height:1.5">
|
|
1810
|
+
${body}
|
|
1811
|
+
</body></html>`;
|
|
1812
|
+
}
|
|
1813
|
+
|
|
1814
|
+
// src/email-deliverability.ts
|
|
1815
|
+
var ADMINISTRATOR_AUDIENCE = /^(?:administrator|admin|team|operator|owner|staff)s?$/i;
|
|
1816
|
+
function isRecipientFacing(event2) {
|
|
1817
|
+
return !event2 || !ADMINISTRATOR_AUDIENCE.test(event2.audience.trim());
|
|
1818
|
+
}
|
|
1819
|
+
function listUnsubscribeHeader(replyTo) {
|
|
1820
|
+
const address = replyTo.trim();
|
|
1821
|
+
if (!/^[^\s@,;<>?]+@[^\s@,;<>?]+\.[^\s@,;<>?]+$/.test(address)) return void 0;
|
|
1822
|
+
return `<mailto:${address}?subject=${encodeURIComponent("unsubscribe")}>`;
|
|
1823
|
+
}
|
|
1824
|
+
function deliverabilityHeaders(input) {
|
|
1825
|
+
if (!isRecipientFacing(input.event)) return void 0;
|
|
1826
|
+
const value = listUnsubscribeHeader(input.replyTo);
|
|
1827
|
+
return value ? { "List-Unsubscribe": value } : void 0;
|
|
1828
|
+
}
|
|
1829
|
+
|
|
1789
1830
|
// src/notify.ts
|
|
1790
1831
|
var import_crm7 = require("@odla-ai/crm");
|
|
1791
1832
|
|
|
@@ -2057,11 +2098,16 @@ async function sendTemplated(deps, input) {
|
|
|
2057
2098
|
if (decision.transport === "log-only") return { sent: true };
|
|
2058
2099
|
let messageId;
|
|
2059
2100
|
try {
|
|
2101
|
+
const text10 = String(row2.body);
|
|
2102
|
+
const html = deps.renderHtml === false ? void 0 : (deps.renderHtml ?? textToHtml)(text10);
|
|
2103
|
+
const headers = deliverabilityHeaders({ event: event2, replyTo: input.group.replyTo });
|
|
2060
2104
|
const response = await deps.sender.send({
|
|
2061
2105
|
from: fromAddress(deps.from, input.group.fromName),
|
|
2062
2106
|
to: [decision.to],
|
|
2063
2107
|
subject: String(row2.subject),
|
|
2064
|
-
text:
|
|
2108
|
+
text: text10,
|
|
2109
|
+
...html ? { html } : {},
|
|
2110
|
+
...headers ? { headers } : {},
|
|
2065
2111
|
replyTo: input.group.replyTo
|
|
2066
2112
|
});
|
|
2067
2113
|
if (!response || typeof response.messageId !== "string" || !response.messageId) throw new Error("missing delivery receipt");
|
|
@@ -12530,6 +12576,84 @@ var import_crm16 = require("@odla-ai/crm");
|
|
|
12530
12576
|
|
|
12531
12577
|
// src/email-readiness.ts
|
|
12532
12578
|
var import_crm15 = require("@odla-ai/crm");
|
|
12579
|
+
|
|
12580
|
+
// src/email-spam-signals.ts
|
|
12581
|
+
var TRIGGER_PHRASES = [
|
|
12582
|
+
"act now",
|
|
12583
|
+
"click here",
|
|
12584
|
+
"limited time",
|
|
12585
|
+
"guaranteed",
|
|
12586
|
+
"risk-free",
|
|
12587
|
+
"risk free",
|
|
12588
|
+
"no obligation",
|
|
12589
|
+
"100% free",
|
|
12590
|
+
"winner",
|
|
12591
|
+
"congratulations",
|
|
12592
|
+
"urgent",
|
|
12593
|
+
"double your",
|
|
12594
|
+
"earn money",
|
|
12595
|
+
"make money",
|
|
12596
|
+
"cash bonus",
|
|
12597
|
+
"once in a lifetime",
|
|
12598
|
+
"exclusive deal",
|
|
12599
|
+
"order now",
|
|
12600
|
+
"buy now",
|
|
12601
|
+
"this is not spam",
|
|
12602
|
+
"dear friend",
|
|
12603
|
+
"dear customer"
|
|
12604
|
+
];
|
|
12605
|
+
var SUBJECT_TRIGGERS = ["free", ...TRIGGER_PHRASES];
|
|
12606
|
+
var DEV_HOSTS = /(?:^|\.)(?:workers\.dev|pages\.dev|localhost|ngrok(?:-free)?\.app|trycloudflare\.com)$/i;
|
|
12607
|
+
var SHORTENERS = /^(?:bit\.ly|tinyurl\.com|t\.co|goo\.gl|ow\.ly|is\.gd|buff\.ly|rebrand\.ly|cutt\.ly|tiny\.cc|shorturl\.at|rb\.gy)$/i;
|
|
12608
|
+
var URL_RE2 = /https?:\/\/[^\s<>"')\]]+/g;
|
|
12609
|
+
var strip = (s) => s.replace(/\{\{\w+\}\}/g, " ");
|
|
12610
|
+
var words = (s) => strip(s).split(/\s+/).filter(Boolean);
|
|
12611
|
+
var phraseIn = (phrases, haystack) => phrases.find((phrase) => new RegExp(`(?:^|[^a-z0-9])${phrase.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?![a-z0-9])`, "i").test(haystack));
|
|
12612
|
+
function emailSpamSignals(copy) {
|
|
12613
|
+
const subject = typeof copy.subject === "string" ? copy.subject : "";
|
|
12614
|
+
const text10 = typeof copy.text === "string" ? copy.text : "";
|
|
12615
|
+
const out = [];
|
|
12616
|
+
const letters = strip(subject).replace(/[^A-Za-z]/g, "");
|
|
12617
|
+
const shouting = letters.length >= 6 && letters.replace(/[^A-Z]/g, "").length / letters.length > 0.5 || words(subject).some((word) => /^[A-Z]{4,}$/.test(word.replace(/[^A-Za-z]/g, "")));
|
|
12618
|
+
if (shouting) out.push({ code: "subject-caps", message: "The subject shouts in capital letters. Filters read that as a spam signal; use sentence case." });
|
|
12619
|
+
if (/[!?]{2,}|!\s*$/.test(subject)) out.push({ code: "subject-punctuation", message: "Repeated or trailing exclamation marks in the subject are a classic spam signal." });
|
|
12620
|
+
if (/^\s*(?:re|fw|fwd)\s*:/i.test(subject)) out.push({ code: "subject-fake-reply", message: "A subject that starts like a reply or forward reads as a spoof when the recipient never wrote first." });
|
|
12621
|
+
const subjectHit = phraseIn(SUBJECT_TRIGGERS, strip(subject));
|
|
12622
|
+
if (subjectHit) out.push({ code: "subject-trigger-words", message: `The subject uses "${subjectHit}", a word filters weight against the sender.` });
|
|
12623
|
+
const bodyHit = phraseIn(TRIGGER_PHRASES, strip(text10));
|
|
12624
|
+
if (bodyHit) out.push({ code: "body-trigger-words", message: `The body uses "${bodyHit}", a phrase filters weight against the sender.` });
|
|
12625
|
+
if (/(?:^|\s)[A-Z]{3,}(?:\s+[A-Z]{3,}){2,}(?=\s|$)/.test(strip(text10))) out.push({ code: "body-caps", message: "The body shouts a run of capitalised words; use sentence case." });
|
|
12626
|
+
if (/!{2,}/.test(text10)) out.push({ code: "body-punctuation", message: "Repeated exclamation marks in the body are a classic spam signal." });
|
|
12627
|
+
const urls = [...text10.matchAll(URL_RE2)].map((match) => match[0]);
|
|
12628
|
+
const hosts = urls.map((url) => {
|
|
12629
|
+
try {
|
|
12630
|
+
return new URL(url).hostname;
|
|
12631
|
+
} catch {
|
|
12632
|
+
return "";
|
|
12633
|
+
}
|
|
12634
|
+
});
|
|
12635
|
+
if (urls.some((url) => /^http:\/\//i.test(url))) out.push({ code: "link-insecure", message: "A link uses http:// instead of https://. Filters flag insecure links." });
|
|
12636
|
+
if (hosts.some((host) => DEV_HOSTS.test(host) || /^\d+\.\d+\.\d+\.\d+$/.test(host))) out.push({ code: "link-origin", message: "A link points at a development host (workers.dev, pages.dev, localhost or a raw IP). Link to the site's own domain." });
|
|
12637
|
+
if (hosts.some((host) => SHORTENERS.test(host))) out.push({ code: "link-shortener", message: "A link uses a URL shortener, which hides the destination and is heavily abused. Link to the real address." });
|
|
12638
|
+
if (new Set(urls).size > 5) out.push({ code: "link-many", message: "The body has more than five links. Keep lifecycle mail to the one or two the reader needs." });
|
|
12639
|
+
if (urls.length && words(text10).length < 20) out.push({ code: "body-link-only", message: "The body is little more than a link. Say what the reader is being sent and why before the address." });
|
|
12640
|
+
return out;
|
|
12641
|
+
}
|
|
12642
|
+
function emailSenderWarnings(input) {
|
|
12643
|
+
const out = [];
|
|
12644
|
+
const from = (input.fromEmail ?? "").trim().toLowerCase().replace(/^.*<([^>]+)>.*$/, "$1");
|
|
12645
|
+
const reply = (input.replyTo ?? "").trim().toLowerCase();
|
|
12646
|
+
const domain = (address) => address.split("@")[1] ?? "";
|
|
12647
|
+
if (/^(?:no-?reply|do-?not-?reply|donotreply)(?:[+.-][^@]*)?@/.test(from)) {
|
|
12648
|
+
out.push({ code: "sender-no-reply", message: "Mail is sent from a no-reply address. Gmail's sender guidelines advise against it; send from a monitored address so replies and unsubscribe requests reach someone." });
|
|
12649
|
+
}
|
|
12650
|
+
if (domain(from) && domain(reply) && domain(from) !== domain(reply)) {
|
|
12651
|
+
out.push({ code: "reply-to-domain", message: `Replies go to ${domain(reply)} while mail is sent from ${domain(from)}. A Reply-To on another domain is a mild spam signal; prefer an address on the sending domain.` });
|
|
12652
|
+
}
|
|
12653
|
+
return out;
|
|
12654
|
+
}
|
|
12655
|
+
|
|
12656
|
+
// src/email-readiness.ts
|
|
12533
12657
|
function validateEmailReadiness(input) {
|
|
12534
12658
|
const issues = [];
|
|
12535
12659
|
for (const key3 of Object.keys(input.templates)) if (!input.events[key3]) {
|
|
@@ -12554,9 +12678,15 @@ ${template.text ?? ""}`.matchAll(/\{\{(\w+)\}\}/g)].map((match) => match[1]);
|
|
|
12554
12678
|
}
|
|
12555
12679
|
if (event2.requiresTier && input.tiersUnavailable) problems.push({ code: "tiers-unavailable", message: "Tier names are unavailable; delivery checks will block unresolved memberships." });
|
|
12556
12680
|
else if (event2.requiresTier && noTierNames) problems.push({ code: "tiers-missing", message: "Configure the membership tier names needed by this event." });
|
|
12557
|
-
return {
|
|
12681
|
+
return {
|
|
12682
|
+
...event2,
|
|
12683
|
+
key: key3,
|
|
12684
|
+
status: template?.enabled === false ? "disabled" : problems.length ? "blocked" : "ready",
|
|
12685
|
+
issues: problems,
|
|
12686
|
+
warnings: template ? emailSpamSignals(template) : []
|
|
12687
|
+
};
|
|
12558
12688
|
});
|
|
12559
|
-
return { checkedAt: input.now ?? Date.now(), events, issues };
|
|
12689
|
+
return { checkedAt: input.now ?? Date.now(), events, issues, warnings: emailSenderWarnings(input.sender ?? {}) };
|
|
12560
12690
|
}
|
|
12561
12691
|
|
|
12562
12692
|
// src/email-admin-config.ts
|
|
@@ -12582,7 +12712,14 @@ async function emailSettings(db, ctx, env) {
|
|
|
12582
12712
|
}
|
|
12583
12713
|
}
|
|
12584
12714
|
const groupValues = Object.fromEntries(["refundPolicyText", "commitmentText", "normsText"].map((key3) => [key3, str2(group?.[key3])]));
|
|
12585
|
-
const validation = validateEmailReadiness({
|
|
12715
|
+
const validation = validateEmailReadiness({
|
|
12716
|
+
events,
|
|
12717
|
+
templates: stored,
|
|
12718
|
+
groupValues,
|
|
12719
|
+
tiers: hub ? void 0 : tiers,
|
|
12720
|
+
tiersUnavailable,
|
|
12721
|
+
sender: { fromEmail: env.EMAIL_FROM ?? null, replyTo: str2(group?.replyTo) }
|
|
12722
|
+
});
|
|
12586
12723
|
return { group, events, data: {
|
|
12587
12724
|
groupId: ctx.chapter.id,
|
|
12588
12725
|
name: ctx.chapter.name,
|
|
@@ -12636,7 +12773,8 @@ async function updateEmailSettings(db, ctx, env, body) {
|
|
|
12636
12773
|
{ t: "update", ns: "groups", id: id7, attrs: { commitmentText, normsText, notificationEmail, replyTo, debugEmail, fromName } }
|
|
12637
12774
|
]);
|
|
12638
12775
|
}
|
|
12639
|
-
|
|
12776
|
+
const warnings = Object.fromEntries(Object.entries(clean).map(([key3, template]) => [key3, emailSpamSignals(template)]).filter(([, found]) => found.length));
|
|
12777
|
+
return json({ ok: true, warnings });
|
|
12640
12778
|
}
|
|
12641
12779
|
|
|
12642
12780
|
// src/worker-routes-admin-comms.ts
|