@reddoorla/maintenance 0.91.0 → 0.93.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/dist/{bump-deps-KGH5UYSF.js → bump-deps-GI7PHMOC.js} +6 -6
- package/dist/{chunk-A6T2R63Z.js → chunk-3HS6LPFX.js} +2 -2
- package/dist/{chunk-SJOXWJ5Y.js → chunk-4763KQ6O.js} +184 -9
- package/dist/chunk-4763KQ6O.js.map +1 -0
- package/dist/{chunk-UXJZU23T.js → chunk-4NUKMPOO.js} +9 -9
- package/dist/{chunk-BQRFQSGY.js → chunk-4R3YAQNV.js} +2 -2
- package/dist/{chunk-XIDUYLSR.js → chunk-7CJ3DVZW.js} +2 -2
- package/dist/{chunk-Z3KYFUN6.js → chunk-B6NUZ2ZX.js} +2 -2
- package/dist/{chunk-WVVT6SJK.js → chunk-BJQCIJOY.js} +169 -17
- package/dist/chunk-BJQCIJOY.js.map +1 -0
- package/dist/{chunk-Y53WOQIJ.js → chunk-CVJF2IC5.js} +2 -2
- package/dist/chunk-I7NHLVVI.js +89 -0
- package/dist/chunk-I7NHLVVI.js.map +1 -0
- package/dist/{chunk-QD427NIO.js → chunk-JB4Q3C3M.js} +7 -7
- package/dist/{chunk-IYSEC543.js → chunk-LS6QPANO.js} +2 -2
- package/dist/{chunk-7RJ2HMMJ.js → chunk-NTLSMJHW.js} +2 -2
- package/dist/{chunk-2NJQLSEU.js → chunk-PPIZHDJL.js} +2 -2
- package/dist/{chunk-JRHIFA5R.js → chunk-QF6IQQFP.js} +10 -4
- package/dist/chunk-QF6IQQFP.js.map +1 -0
- package/dist/{chunk-RQLXEMEE.js → chunk-T7ZUUOZ2.js} +2 -2
- package/dist/chunk-T7ZUUOZ2.js.map +1 -0
- package/dist/{chunk-TVL5MVUF.js → chunk-TCF6S7AD.js} +4 -4
- package/dist/{chunk-GSWTT2J6.js → chunk-WHWVYL7B.js} +4 -4
- package/dist/{chunk-3G25KIWW.js → chunk-ZTPPCHDB.js} +4 -4
- package/dist/cli/bin.js +17 -17
- package/dist/cli/commands/audit.js +10 -10
- package/dist/{convert-to-pnpm-NWB5FCQ2.js → convert-to-pnpm-ONFP3JKF.js} +6 -6
- package/dist/{db-IMMQSRAB.js → db-HSZ6D5DU.js} +3 -3
- package/dist/digest-EOA4JLTV.js +1 -1
- package/dist/digest-collectors-ZA3BTS3R.js +1 -1
- package/dist/{email-PLPDJUDH.js → email-I4BCETI4.js} +16 -65
- package/dist/email-I4BCETI4.js.map +1 -0
- package/dist/forms/index.d.ts +5 -1
- package/dist/forms/index.js +1 -1
- package/dist/forms/index.js.map +1 -1
- package/dist/forms/prismic.d.ts +92 -0
- package/dist/forms/prismic.js +105 -0
- package/dist/forms/prismic.js.map +1 -0
- package/dist/{forms-notify-target-YCZUAHTY.js → forms-notify-target-RMZPKT6S.js} +4 -3
- package/dist/{forms-notify-target-YCZUAHTY.js.map → forms-notify-target-RMZPKT6S.js.map} +1 -1
- package/dist/{health-endpoint-UVX6R5N5.js → health-endpoint-DHKJUPRM.js} +6 -6
- package/dist/index.js +30 -23
- package/dist/index.js.map +1 -1
- package/dist/{init-NXZ4NXTL.js → init-ZRKWYCKA.js} +15 -15
- package/dist/{launch-WPEQ3TY2.js → launch-6X5OXPF2.js} +11 -11
- package/dist/{notify-4HP4GZ2H.js → notify-IISTHNBR.js} +4 -3
- package/dist/{onboard-BKCYBAVW.js → onboard-6NHGCKGB.js} +6 -6
- package/dist/orchestrate-KMQIUP3H.js +1 -1
- package/dist/{pipeline-HQJYZ443.js → pipeline-AQBFG5WM.js} +5 -3
- package/dist/preflight-VPHQYWTY.js +1 -1
- package/dist/{prismic-ci-6YGRIOHT.js → prismic-ci-A2HI7NNJ.js} +8 -8
- package/dist/{prismic-models-FPDSUXDO.js → prismic-models-QTKIMFBC.js} +4 -4
- package/dist/prospect/types.d.ts +17 -0
- package/dist/prospect/types.js +1 -1
- package/dist/{prospect-audit-TFIXSTE5.js → prospect-audit-RKUVSTLN.js} +4 -4
- package/dist/recipes/sync-configs.js +3 -3
- package/dist/{chunk-H6BLTEFN.js → render-7G43BSK5.js} +3 -3
- package/dist/{chunk-H6BLTEFN.js.map → render-7G43BSK5.js.map} +1 -1
- package/dist/{replay-4S6ENYCH.js → replay-PCSQOGCZ.js} +3 -2
- package/dist/{replay-4S6ENYCH.js.map → replay-PCSQOGCZ.js.map} +1 -1
- package/dist/{self-updating-VQIW24RZ.js → self-updating-D5XOWS7V.js} +5 -5
- package/dist/selftest-IFGSJP4X.js +1 -1
- package/dist/{smoke-suite-4JFZZYVW.js → smoke-suite-GXC5JZPH.js} +6 -6
- package/dist/{svelte-codemods-QAPDZC6F.js → svelte-codemods-A3EARSRP.js} +6 -6
- package/dist/{sync-configs-PVRLZRKL.js → sync-configs-KSESOHFF.js} +6 -6
- package/dist/{upgrade-IR2OO3KP.js → upgrade-ZLNVEIEA.js} +6 -6
- package/dist/{webflow-624FEBFD.js → webflow-IRKOIT7S.js} +4 -4
- package/package.json +5 -1
- package/dist/chunk-JRHIFA5R.js.map +0 -1
- package/dist/chunk-RQLXEMEE.js.map +0 -1
- package/dist/chunk-SJOXWJ5Y.js.map +0 -1
- package/dist/chunk-WVVT6SJK.js.map +0 -1
- package/dist/email-PLPDJUDH.js.map +0 -1
- package/dist/render-3GYW2J34.js +0 -11
- package/dist/render-3GYW2J34.js.map +0 -1
- /package/dist/{bump-deps-KGH5UYSF.js.map → bump-deps-GI7PHMOC.js.map} +0 -0
- /package/dist/{chunk-A6T2R63Z.js.map → chunk-3HS6LPFX.js.map} +0 -0
- /package/dist/{chunk-UXJZU23T.js.map → chunk-4NUKMPOO.js.map} +0 -0
- /package/dist/{chunk-BQRFQSGY.js.map → chunk-4R3YAQNV.js.map} +0 -0
- /package/dist/{chunk-XIDUYLSR.js.map → chunk-7CJ3DVZW.js.map} +0 -0
- /package/dist/{chunk-Z3KYFUN6.js.map → chunk-B6NUZ2ZX.js.map} +0 -0
- /package/dist/{chunk-Y53WOQIJ.js.map → chunk-CVJF2IC5.js.map} +0 -0
- /package/dist/{chunk-QD427NIO.js.map → chunk-JB4Q3C3M.js.map} +0 -0
- /package/dist/{chunk-IYSEC543.js.map → chunk-LS6QPANO.js.map} +0 -0
- /package/dist/{chunk-7RJ2HMMJ.js.map → chunk-NTLSMJHW.js.map} +0 -0
- /package/dist/{chunk-2NJQLSEU.js.map → chunk-PPIZHDJL.js.map} +0 -0
- /package/dist/{chunk-TVL5MVUF.js.map → chunk-TCF6S7AD.js.map} +0 -0
- /package/dist/{chunk-GSWTT2J6.js.map → chunk-WHWVYL7B.js.map} +0 -0
- /package/dist/{chunk-3G25KIWW.js.map → chunk-ZTPPCHDB.js.map} +0 -0
- /package/dist/{convert-to-pnpm-NWB5FCQ2.js.map → convert-to-pnpm-ONFP3JKF.js.map} +0 -0
- /package/dist/{db-IMMQSRAB.js.map → db-HSZ6D5DU.js.map} +0 -0
- /package/dist/{health-endpoint-UVX6R5N5.js.map → health-endpoint-DHKJUPRM.js.map} +0 -0
- /package/dist/{init-NXZ4NXTL.js.map → init-ZRKWYCKA.js.map} +0 -0
- /package/dist/{launch-WPEQ3TY2.js.map → launch-6X5OXPF2.js.map} +0 -0
- /package/dist/{notify-4HP4GZ2H.js.map → notify-IISTHNBR.js.map} +0 -0
- /package/dist/{onboard-BKCYBAVW.js.map → onboard-6NHGCKGB.js.map} +0 -0
- /package/dist/{pipeline-HQJYZ443.js.map → pipeline-AQBFG5WM.js.map} +0 -0
- /package/dist/{prismic-ci-6YGRIOHT.js.map → prismic-ci-A2HI7NNJ.js.map} +0 -0
- /package/dist/{prismic-models-FPDSUXDO.js.map → prismic-models-QTKIMFBC.js.map} +0 -0
- /package/dist/{prospect-audit-TFIXSTE5.js.map → prospect-audit-RKUVSTLN.js.map} +0 -0
- /package/dist/{self-updating-VQIW24RZ.js.map → self-updating-D5XOWS7V.js.map} +0 -0
- /package/dist/{smoke-suite-4JFZZYVW.js.map → smoke-suite-GXC5JZPH.js.map} +0 -0
- /package/dist/{svelte-codemods-QAPDZC6F.js.map → svelte-codemods-A3EARSRP.js.map} +0 -0
- /package/dist/{sync-configs-PVRLZRKL.js.map → sync-configs-KSESOHFF.js.map} +0 -0
- /package/dist/{upgrade-IR2OO3KP.js.map → upgrade-ZLNVEIEA.js.map} +0 -0
- /package/dist/{webflow-624FEBFD.js.map → webflow-IRKOIT7S.js.map} +0 -0
|
@@ -5,19 +5,19 @@ import {
|
|
|
5
5
|
appendSkipNotice,
|
|
6
6
|
fleetWorkdir,
|
|
7
7
|
prepareFleetSites
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-TCF6S7AD.js";
|
|
9
9
|
import {
|
|
10
10
|
resolveSites
|
|
11
11
|
} from "./chunk-Q47DU255.js";
|
|
12
12
|
import {
|
|
13
13
|
bumpDeps
|
|
14
|
-
} from "./chunk-
|
|
15
|
-
import "./chunk-
|
|
14
|
+
} from "./chunk-PPIZHDJL.js";
|
|
15
|
+
import "./chunk-ZTPPCHDB.js";
|
|
16
16
|
import "./chunk-Z3JXLULD.js";
|
|
17
|
-
import "./chunk-RARREDQE.js";
|
|
18
|
-
import "./chunk-XTK5VIQB.js";
|
|
19
17
|
import "./chunk-LBYOLBW7.js";
|
|
18
|
+
import "./chunk-RARREDQE.js";
|
|
20
19
|
import "./chunk-XXTZBPUY.js";
|
|
20
|
+
import "./chunk-XTK5VIQB.js";
|
|
21
21
|
|
|
22
22
|
// src/cli/commands/bump-deps.ts
|
|
23
23
|
import { resolve } from "path";
|
|
@@ -57,4 +57,4 @@ async function runBumpDepsCommand(site, opts) {
|
|
|
57
57
|
export {
|
|
58
58
|
runBumpDepsCommand
|
|
59
59
|
};
|
|
60
|
-
//# sourceMappingURL=bump-deps-
|
|
60
|
+
//# sourceMappingURL=bump-deps-GI7PHMOC.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
withRecipe
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-ZTPPCHDB.js";
|
|
4
4
|
import {
|
|
5
5
|
ALL_TEMPLATES,
|
|
6
6
|
RENOVATE_ACTION_CONFIG,
|
|
@@ -229,4 +229,4 @@ export {
|
|
|
229
229
|
planTemplateDiffs,
|
|
230
230
|
syncConfigs
|
|
231
231
|
};
|
|
232
|
-
//# sourceMappingURL=chunk-
|
|
232
|
+
//# sourceMappingURL=chunk-3HS6LPFX.js.map
|
|
@@ -1,13 +1,172 @@
|
|
|
1
1
|
import {
|
|
2
2
|
hostsMatch
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-QF6IQQFP.js";
|
|
4
4
|
import {
|
|
5
5
|
operatorEmail
|
|
6
6
|
} from "./chunk-5ML7FMBD.js";
|
|
7
|
+
import {
|
|
8
|
+
parseReplyCopy
|
|
9
|
+
} from "./chunk-I7NHLVVI.js";
|
|
7
10
|
import {
|
|
8
11
|
escapeHtml
|
|
9
12
|
} from "./chunk-BGTYPVLT.js";
|
|
10
13
|
|
|
14
|
+
// src/forms/ics.ts
|
|
15
|
+
var DEFAULT_DURATION_MS = 2 * 60 * 60 * 1e3;
|
|
16
|
+
function stamp(iso) {
|
|
17
|
+
return new Date(iso).toISOString().replace(/[-:]/g, "").replace(/\.\d{3}/, "");
|
|
18
|
+
}
|
|
19
|
+
function endStamp(e) {
|
|
20
|
+
if (e.end) return stamp(e.end);
|
|
21
|
+
return stamp(new Date(Date.parse(e.start) + DEFAULT_DURATION_MS).toISOString());
|
|
22
|
+
}
|
|
23
|
+
function esc(v) {
|
|
24
|
+
return v.replace(/\\/g, "\\\\").replace(/;/g, "\\;").replace(/,/g, "\\,").replace(/\r?\n/g, "\\n");
|
|
25
|
+
}
|
|
26
|
+
function uid(e) {
|
|
27
|
+
const slug = e.title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 40);
|
|
28
|
+
return `${stamp(e.start)}-${slug || "event"}@reddoorla.com`;
|
|
29
|
+
}
|
|
30
|
+
function buildIcs(e, now = /* @__PURE__ */ new Date()) {
|
|
31
|
+
const lines = [
|
|
32
|
+
"BEGIN:VCALENDAR",
|
|
33
|
+
"VERSION:2.0",
|
|
34
|
+
"PRODID:-//Reddoor Creative//Form Auto-Reply//EN",
|
|
35
|
+
"CALSCALE:GREGORIAN",
|
|
36
|
+
"METHOD:PUBLISH",
|
|
37
|
+
"BEGIN:VEVENT",
|
|
38
|
+
`UID:${uid(e)}`,
|
|
39
|
+
`DTSTAMP:${stamp(now.toISOString())}`,
|
|
40
|
+
`DTSTART:${stamp(e.start)}`,
|
|
41
|
+
`DTEND:${endStamp(e)}`,
|
|
42
|
+
`SUMMARY:${esc(e.title)}`
|
|
43
|
+
];
|
|
44
|
+
if (e.location) lines.push(`LOCATION:${esc(e.location)}`);
|
|
45
|
+
const description = [e.description, e.url].filter(Boolean).join("\n\n");
|
|
46
|
+
if (description) lines.push(`DESCRIPTION:${esc(description)}`);
|
|
47
|
+
if (e.url) lines.push(`URL:${e.url}`);
|
|
48
|
+
lines.push("END:VEVENT", "END:VCALENDAR");
|
|
49
|
+
return lines.join("\r\n") + "\r\n";
|
|
50
|
+
}
|
|
51
|
+
function googleCalendarUrl(e) {
|
|
52
|
+
const params = new URLSearchParams({
|
|
53
|
+
action: "TEMPLATE",
|
|
54
|
+
text: e.title,
|
|
55
|
+
dates: `${stamp(e.start)}/${endStamp(e)}`
|
|
56
|
+
});
|
|
57
|
+
if (e.location) params.set("location", e.location);
|
|
58
|
+
const details = [e.description, e.url].filter(Boolean).join("\n\n");
|
|
59
|
+
if (details) params.set("details", details);
|
|
60
|
+
return `https://calendar.google.com/calendar/render?${params.toString()}`;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// src/forms/rich-text.ts
|
|
64
|
+
var SAFE_HREF = /^(https:\/\/|mailto:)/i;
|
|
65
|
+
var HEADING_TAG = { heading2: "h2", heading3: "h3" };
|
|
66
|
+
function wrap(escaped, active) {
|
|
67
|
+
let out = escaped;
|
|
68
|
+
const link = active.find((s) => s.type === "link" && s.url && SAFE_HREF.test(s.url));
|
|
69
|
+
if (link) out = `<a href="${escapeHtml(link.url)}">${out}</a>`;
|
|
70
|
+
if (active.some((s) => s.type === "em")) out = `<em>${out}</em>`;
|
|
71
|
+
if (active.some((s) => s.type === "strong")) out = `<strong>${out}</strong>`;
|
|
72
|
+
return out;
|
|
73
|
+
}
|
|
74
|
+
function renderText(text, spans) {
|
|
75
|
+
const usable = (spans ?? []).map((s) => ({ ...s, start: Math.max(0, s.start), end: Math.min(text.length, s.end) })).filter((s) => Number.isFinite(s.start) && Number.isFinite(s.end) && s.start < s.end);
|
|
76
|
+
if (usable.length === 0) return escapeHtml(text);
|
|
77
|
+
const bounds = /* @__PURE__ */ new Set([0, text.length]);
|
|
78
|
+
for (const s of usable) {
|
|
79
|
+
bounds.add(s.start);
|
|
80
|
+
bounds.add(s.end);
|
|
81
|
+
}
|
|
82
|
+
const points = [...bounds].sort((a, b) => a - b);
|
|
83
|
+
let out = "";
|
|
84
|
+
for (let i = 0; i < points.length - 1; i++) {
|
|
85
|
+
const from = points[i];
|
|
86
|
+
const to = points[i + 1];
|
|
87
|
+
if (from === to) continue;
|
|
88
|
+
const active = usable.filter((s) => s.start <= from && s.end >= to);
|
|
89
|
+
out += wrap(escapeHtml(text.slice(from, to)), active);
|
|
90
|
+
}
|
|
91
|
+
return out;
|
|
92
|
+
}
|
|
93
|
+
var isListItem = (t) => t === "list-item" || t === "o-list-item";
|
|
94
|
+
function renderBlocks(blocks) {
|
|
95
|
+
const usable = blocks.filter((b) => b.text.trim() !== "");
|
|
96
|
+
let out = "";
|
|
97
|
+
let openList = null;
|
|
98
|
+
const closeList = () => {
|
|
99
|
+
if (openList) {
|
|
100
|
+
out += openList === "list-item" ? "</ul>" : "</ol>";
|
|
101
|
+
openList = null;
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
for (const block of usable) {
|
|
105
|
+
const inner = renderText(block.text, block.spans);
|
|
106
|
+
if (isListItem(block.type)) {
|
|
107
|
+
if (openList !== block.type) {
|
|
108
|
+
closeList();
|
|
109
|
+
out += block.type === "list-item" ? "<ul>" : "<ol>";
|
|
110
|
+
openList = block.type;
|
|
111
|
+
}
|
|
112
|
+
out += `<li>${inner}</li>`;
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
closeList();
|
|
116
|
+
const heading = HEADING_TAG[block.type];
|
|
117
|
+
out += heading ? `<${heading}>${inner}</${heading}>` : `<p>${inner}</p>`;
|
|
118
|
+
}
|
|
119
|
+
closeList();
|
|
120
|
+
return out;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// src/forms/default-replies.ts
|
|
124
|
+
var REPLIES = {
|
|
125
|
+
contact: (site) => ({
|
|
126
|
+
subject: "We got your message",
|
|
127
|
+
paragraphs: [
|
|
128
|
+
`Thanks for getting in touch with ${site}.`,
|
|
129
|
+
"We've received your message and someone will reply as soon as we can."
|
|
130
|
+
]
|
|
131
|
+
}),
|
|
132
|
+
inquiry: (site) => ({
|
|
133
|
+
subject: "Thanks for your inquiry",
|
|
134
|
+
paragraphs: [
|
|
135
|
+
`Thanks for your interest \u2014 your inquiry has reached the team at ${site}.`,
|
|
136
|
+
"Someone will be in touch shortly with the details you asked about."
|
|
137
|
+
]
|
|
138
|
+
}),
|
|
139
|
+
newsletter: (site) => ({
|
|
140
|
+
// "Subscribed", not "on the list" — that phrasing belongs to the RSVP, and
|
|
141
|
+
// a subject line that could mean either is worse than a plain one. Accurate
|
|
142
|
+
// for single opt-in, which is what the Mailchimp fan-out does today.
|
|
143
|
+
subject: "You're subscribed",
|
|
144
|
+
paragraphs: [
|
|
145
|
+
`Thanks for subscribing to updates from ${site}.`,
|
|
146
|
+
"You'll hear from us when there's something worth sharing, and you can unsubscribe from any email."
|
|
147
|
+
]
|
|
148
|
+
}),
|
|
149
|
+
rsvp: (site) => ({
|
|
150
|
+
// The event name, when the submission carries one, beats this in notify.ts.
|
|
151
|
+
subject: "You're on the list",
|
|
152
|
+
paragraphs: [
|
|
153
|
+
`Thanks for your RSVP \u2014 ${site} has you down.`,
|
|
154
|
+
"We'll be in touch if anything changes before the day."
|
|
155
|
+
]
|
|
156
|
+
}),
|
|
157
|
+
reserve: (site) => ({
|
|
158
|
+
subject: "We've got your reservation request",
|
|
159
|
+
paragraphs: [
|
|
160
|
+
`Thanks \u2014 ${site} has received your reservation request.`,
|
|
161
|
+
"We'll confirm the details with you shortly."
|
|
162
|
+
]
|
|
163
|
+
})
|
|
164
|
+
};
|
|
165
|
+
function defaultReply(formType, siteName) {
|
|
166
|
+
const build = REPLIES[formType] ?? REPLIES.contact;
|
|
167
|
+
return build(siteName);
|
|
168
|
+
}
|
|
169
|
+
|
|
11
170
|
// src/forms/notify.ts
|
|
12
171
|
var FORMS_FROM = "forms@reddoorla.com";
|
|
13
172
|
var FALLBACK_REPLY_TO = "info@reddoorla.com";
|
|
@@ -137,7 +296,7 @@ function parseExtraFields(raw) {
|
|
|
137
296
|
return {};
|
|
138
297
|
}
|
|
139
298
|
function extraFieldRows(raw) {
|
|
140
|
-
return Object.entries(parseExtraFields(raw)).filter(([, v]) => !(typeof v === "string" && v.trim() === "")).map(([k, v]) => [humanizeKey(k), formatValue(v)]);
|
|
299
|
+
return Object.entries(parseExtraFields(raw)).filter(([k]) => !k.startsWith("_")).filter(([, v]) => !(typeof v === "string" && v.trim() === "")).map(([k, v]) => [humanizeKey(k), formatValue(v)]);
|
|
141
300
|
}
|
|
142
301
|
function fieldsTable(submission) {
|
|
143
302
|
const rows = [
|
|
@@ -179,16 +338,32 @@ function buildAutoresponder(site, submission) {
|
|
|
179
338
|
} catch {
|
|
180
339
|
}
|
|
181
340
|
if (siteHost && hostsMatch(emailDomain, siteHost)) return null;
|
|
182
|
-
const
|
|
183
|
-
const
|
|
184
|
-
const
|
|
185
|
-
|
|
341
|
+
const extra = parseExtraFields(submission.extraFields);
|
|
342
|
+
const reply = parseReplyCopy(extra._reply);
|
|
343
|
+
const eventName = typeof extra.event === "string" ? extra.event.trim() : "";
|
|
344
|
+
const fallback = defaultReply(submission.formType, site.name);
|
|
345
|
+
const subject = reply?.subject ?? (eventName ? `You're on the list for ${eventName}` : fallback.subject);
|
|
346
|
+
const signature = reply?.signature ?? site.copyFooter ?? site.name;
|
|
347
|
+
const body = reply?.body ? renderBlocks(reply.body) : [site.copyIntro ?? fallback.paragraphs[0], site.copyContact ?? fallback.paragraphs[1]].map((p) => `<p>${escapeHtml(p)}</p>`).join("");
|
|
348
|
+
const calendar = reply?.calendar;
|
|
349
|
+
const calendarBlock = calendar ? `<p>Add it to your calendar: <a href="${escapeHtml(googleCalendarUrl(calendar))}">Google Calendar</a>. The attached invite works in Apple Calendar and Outlook.</p>` : "";
|
|
350
|
+
const input = {
|
|
186
351
|
from: `${displayName(site.name)} <${FORMS_FROM}>`,
|
|
187
352
|
to: [submission.email],
|
|
188
353
|
replyTo: resolveRecipients(site, submission)?.to[0] ?? FALLBACK_REPLY_TO,
|
|
189
|
-
subject
|
|
190
|
-
html:
|
|
354
|
+
subject,
|
|
355
|
+
html: `${body}${calendarBlock}<p>${escapeHtml(signature)}</p>`
|
|
191
356
|
};
|
|
357
|
+
if (calendar) {
|
|
358
|
+
input.attachments = [
|
|
359
|
+
{
|
|
360
|
+
filename: "event.ics",
|
|
361
|
+
content: Buffer.from(buildIcs(calendar), "utf8").toString("base64"),
|
|
362
|
+
contentType: "text/calendar"
|
|
363
|
+
}
|
|
364
|
+
];
|
|
365
|
+
}
|
|
366
|
+
return input;
|
|
192
367
|
}
|
|
193
368
|
async function notifySubmission(deps, site, submission) {
|
|
194
369
|
const poc = buildPocNotification(site, submission);
|
|
@@ -226,4 +401,4 @@ export {
|
|
|
226
401
|
notifySubmission,
|
|
227
402
|
makeNotify
|
|
228
403
|
};
|
|
229
|
-
//# sourceMappingURL=chunk-
|
|
404
|
+
//# sourceMappingURL=chunk-4763KQ6O.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/forms/ics.ts","../src/forms/rich-text.ts","../src/forms/default-replies.ts","../src/forms/notify.ts"],"sourcesContent":["/**\n * Calendar formats for the confirmation email: an RFC 5545 VEVENT and a Google\n * Calendar template URL. Kept apart from notify.ts because the escaping rules\n * are fiddly enough to deserve their own tests, and because a calendar format\n * has nothing to do with email.\n */\nimport type { ReplyCalendar } from \"./reply-copy.js\";\n\nconst DEFAULT_DURATION_MS = 2 * 60 * 60 * 1000;\n\n/** `2026-09-12T18:00:00-07:00` → `20260913T010000Z`. Callers have already proven\n * the string parses (parseReplyCopy), so this never sees NaN. */\nfunction stamp(iso: string): string {\n return new Date(iso)\n .toISOString()\n .replace(/[-:]/g, \"\")\n .replace(/\\.\\d{3}/, \"\");\n}\n\nfunction endStamp(e: ReplyCalendar): string {\n if (e.end) return stamp(e.end);\n return stamp(new Date(Date.parse(e.start) + DEFAULT_DURATION_MS).toISOString());\n}\n\n/** RFC 5545 §3.3.11. Backslash FIRST — escaping it after the others would\n * double-escape the backslashes they just introduced. */\nfunction esc(v: string): string {\n return v\n .replace(/\\\\/g, \"\\\\\\\\\")\n .replace(/;/g, \"\\\\;\")\n .replace(/,/g, \"\\\\,\")\n .replace(/\\r?\\n/g, \"\\\\n\");\n}\n\n/** Stable per event, so a guest who receives a second copy sees their calendar\n * entry UPDATE rather than gain a duplicate. Derived from the fields that\n * identify the event, never from the send. */\nfunction uid(e: ReplyCalendar): string {\n const slug = e.title\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-|-$/g, \"\")\n .slice(0, 40);\n return `${stamp(e.start)}-${slug || \"event\"}@reddoorla.com`;\n}\n\n/** A complete single-event calendar, CRLF-delimited per the spec. */\nexport function buildIcs(e: ReplyCalendar, now: Date = new Date()): string {\n const lines = [\n \"BEGIN:VCALENDAR\",\n \"VERSION:2.0\",\n \"PRODID:-//Reddoor Creative//Form Auto-Reply//EN\",\n \"CALSCALE:GREGORIAN\",\n \"METHOD:PUBLISH\",\n \"BEGIN:VEVENT\",\n `UID:${uid(e)}`,\n `DTSTAMP:${stamp(now.toISOString())}`,\n `DTSTART:${stamp(e.start)}`,\n `DTEND:${endStamp(e)}`,\n `SUMMARY:${esc(e.title)}`,\n ];\n if (e.location) lines.push(`LOCATION:${esc(e.location)}`);\n const description = [e.description, e.url].filter(Boolean).join(\"\\n\\n\");\n if (description) lines.push(`DESCRIPTION:${esc(description)}`);\n if (e.url) lines.push(`URL:${e.url}`);\n lines.push(\"END:VEVENT\", \"END:VCALENDAR\");\n return lines.join(\"\\r\\n\") + \"\\r\\n\";\n}\n\n/** The \"Add to Google Calendar\" link. Google reads the same UTC stamps. */\nexport function googleCalendarUrl(e: ReplyCalendar): string {\n const params = new URLSearchParams({\n action: \"TEMPLATE\",\n text: e.title,\n dates: `${stamp(e.start)}/${endStamp(e)}`,\n });\n if (e.location) params.set(\"location\", e.location);\n const details = [e.description, e.url].filter(Boolean).join(\"\\n\\n\");\n if (details) params.set(\"details\", details);\n return `https://calendar.google.com/calendar/render?${params.toString()}`;\n}\n","/**\n * Render the reply body's block/span AST to email-safe HTML.\n *\n * This is a WHITELIST renderer, and that is the entire point of the AST. The\n * envelope deliberately does not carry HTML: if it did, `parseReplyCopy` could\n * make no promise about what ends up in an email we send from a domain with\n * real sending reputation, and the day a site lets request data reach `_reply`\n * that becomes an injection vector in the worst possible place. Here the only\n * tags that can ever be emitted are the ones named below, so the worst a\n * hostile AST achieves is some bold text.\n *\n * Kept deliberately small for email clients: no images, no embeds, no classes,\n * no styles. Bold, italic, links, lists, two heading levels.\n */\nimport { escapeHtml } from \"../util/html.js\";\nimport type { ReplyBlock, ReplySpan } from \"./reply-copy.js\";\n\n/** Schemes allowed in an href. Everything else is dropped and the text kept —\n * a `javascript:` or `data:` URL arriving through a CMS field is not a link\n * anyone meant to write. */\nconst SAFE_HREF = /^(https:\\/\\/|mailto:)/i;\n\nconst HEADING_TAG: Record<string, string> = { heading2: \"h2\", heading3: \"h3\" };\n\n/** Wrap one already-escaped segment in the tags for the spans covering it.\n * Link innermost so nested emphasis reads naturally in every client. */\nfunction wrap(escaped: string, active: ReplySpan[]): string {\n let out = escaped;\n const link = active.find((s) => s.type === \"link\" && s.url && SAFE_HREF.test(s.url));\n if (link) out = `<a href=\"${escapeHtml(link.url as string)}\">${out}</a>`;\n if (active.some((s) => s.type === \"em\")) out = `<em>${out}</em>`;\n if (active.some((s) => s.type === \"strong\")) out = `<strong>${out}</strong>`;\n return out;\n}\n\n/**\n * Apply spans to text by OFFSET, then escape — in that order, per segment.\n *\n * The order is the whole trick. Offsets index the raw string, so escaping first\n * and slicing after silently shifts every span past the first `&`, `<` or `>` —\n * the classic way this kind of renderer goes subtly wrong, bolding the wrong\n * words only in copy that happens to contain an ampersand.\n */\nfunction renderText(text: string, spans: ReplySpan[] | undefined): string {\n const usable = (spans ?? [])\n // Clamp into range rather than trusting the CMS's arithmetic; drop anything\n // that still describes no characters.\n .map((s) => ({ ...s, start: Math.max(0, s.start), end: Math.min(text.length, s.end) }))\n .filter((s) => Number.isFinite(s.start) && Number.isFinite(s.end) && s.start < s.end);\n if (usable.length === 0) return escapeHtml(text);\n\n // Cut at every boundary, then decide which spans cover each piece. This is\n // what makes overlapping spans (bold across a range that a link only partly\n // covers) come out well-formed instead of interleaved.\n const bounds = new Set<number>([0, text.length]);\n for (const s of usable) {\n bounds.add(s.start);\n bounds.add(s.end);\n }\n const points = [...bounds].sort((a, b) => a - b);\n\n let out = \"\";\n for (let i = 0; i < points.length - 1; i++) {\n const from = points[i] as number;\n const to = points[i + 1] as number;\n if (from === to) continue;\n const active = usable.filter((s) => s.start <= from && s.end >= to);\n out += wrap(escapeHtml(text.slice(from, to)), active);\n }\n return out;\n}\n\nconst isListItem = (t: string): boolean => t === \"list-item\" || t === \"o-list-item\";\n\n/** Blocks → HTML. Empty blocks are dropped; consecutive list items of the same\n * kind collapse into a single list. */\nexport function renderBlocks(blocks: ReplyBlock[]): string {\n const usable = blocks.filter((b) => b.text.trim() !== \"\");\n let out = \"\";\n let openList: string | null = null;\n\n const closeList = () => {\n if (openList) {\n out += openList === \"list-item\" ? \"</ul>\" : \"</ol>\";\n openList = null;\n }\n };\n\n for (const block of usable) {\n const inner = renderText(block.text, block.spans);\n if (isListItem(block.type)) {\n if (openList !== block.type) {\n closeList();\n out += block.type === \"list-item\" ? \"<ul>\" : \"<ol>\";\n openList = block.type;\n }\n out += `<li>${inner}</li>`;\n continue;\n }\n closeList();\n const heading = HEADING_TAG[block.type];\n out += heading ? `<${heading}>${inner}</${heading}>` : `<p>${inner}</p>`;\n }\n closeList();\n return out;\n}\n","/**\n * What a confirmation email says when nobody has authored anything.\n *\n * Before this, every form type on every site sent one string — \"We got your\n * message\" over \"Thanks for reaching out to {site}.\" A newsletter signup and a\n * price-list inquiry are not the same event, and reading as though they are is\n * the tell that nothing behind the form is paying attention.\n *\n * These are the floor, not the ceiling: a site that authors copy in its CMS\n * overrides them, and a site with the legacy per-site columns set still uses\n * those. The aim is that a fleet site with NO configuration at all still sends\n * something a person would recognise as a real reply.\n *\n * Deliberately plain and brand-neutral — they stand in for a client's voice\n * without impersonating it, and they must read acceptably for a law firm, a\n * gallery and a dentist alike.\n */\nimport type { FormType } from \"./types.js\";\n\nexport type DefaultReply = {\n subject: string;\n /** Plain paragraphs. Our own text, so no formatting and nothing to sanitize\n * beyond the escaping every body gets. */\n paragraphs: [string, string];\n};\n\nconst REPLIES: Record<FormType, (site: string) => DefaultReply> = {\n contact: (site) => ({\n subject: \"We got your message\",\n paragraphs: [\n `Thanks for getting in touch with ${site}.`,\n \"We've received your message and someone will reply as soon as we can.\",\n ],\n }),\n inquiry: (site) => ({\n subject: \"Thanks for your inquiry\",\n paragraphs: [\n `Thanks for your interest — your inquiry has reached the team at ${site}.`,\n \"Someone will be in touch shortly with the details you asked about.\",\n ],\n }),\n newsletter: (site) => ({\n // \"Subscribed\", not \"on the list\" — that phrasing belongs to the RSVP, and\n // a subject line that could mean either is worse than a plain one. Accurate\n // for single opt-in, which is what the Mailchimp fan-out does today.\n subject: \"You're subscribed\",\n paragraphs: [\n `Thanks for subscribing to updates from ${site}.`,\n \"You'll hear from us when there's something worth sharing, and you can unsubscribe from any email.\",\n ],\n }),\n rsvp: (site) => ({\n // The event name, when the submission carries one, beats this in notify.ts.\n subject: \"You're on the list\",\n paragraphs: [\n `Thanks for your RSVP — ${site} has you down.`,\n \"We'll be in touch if anything changes before the day.\",\n ],\n }),\n reserve: (site) => ({\n subject: \"We've got your reservation request\",\n paragraphs: [\n `Thanks — ${site} has received your reservation request.`,\n \"We'll confirm the details with you shortly.\",\n ],\n }),\n};\n\n/** The default reply for a form type. An unrecognized type gets the contact\n * copy, which is the one wording that is true of any form: we received it. */\nexport function defaultReply(formType: string, siteName: string): DefaultReply {\n const build = REPLIES[formType as FormType] ?? REPLIES.contact;\n return build(siteName);\n}\n","import type { WebsiteRow } from \"../reports/airtable/websites.js\";\nimport type { SubmissionRow, NotifyStatus } from \"../reports/submission-row.js\";\nimport type { ResendSendInput } from \"../reports/send/resend.js\";\nimport { hostsMatch } from \"./ingest.js\";\nimport { escapeHtml } from \"../util/html.js\";\nimport { parseReplyCopy } from \"./reply-copy.js\";\nimport { buildIcs, googleCalendarUrl } from \"./ics.js\";\nimport { renderBlocks } from \"./rich-text.js\";\nimport { defaultReply } from \"./default-replies.js\";\nimport { operatorEmail } from \"../util/operator.js\";\n\nconst FORMS_FROM = \"forms@reddoorla.com\";\n// Reply-To only: a client replying to a lead notification should reach the\n// shared inbox. The RECIPIENT fallback is the operator's own — see util/operator.\nconst FALLBACK_REPLY_TO = \"info@reddoorla.com\";\n\n/** Strip characters that would break an RFC 5322 display name. */\nfunction displayName(raw: string): string {\n return raw.replace(/[\"\\r\\n]/g, \"\").trim() || \"Reddoor\";\n}\n\nfunction pocAddress(site: WebsiteRow): string | null {\n return site.pointOfContact ?? site.reportRecipientsTo ?? null;\n}\n\n/** Coerce a string|string[] recipient config into a clean, de-duped address list. */\nfunction normalizeRecipients(v: string | string[] | undefined): string[] {\n const arr = Array.isArray(v) ? v : v ? [v] : [];\n const seen = new Set<string>();\n const out: string[] = [];\n for (const raw of arr) {\n if (typeof raw !== \"string\") continue;\n const t = raw.trim();\n if (t && !seen.has(t)) {\n seen.add(t);\n out.push(t);\n }\n }\n return out;\n}\n\nexport type Recipients = { to: string[]; cc: string[] };\n\n/**\n * Where a submission notification goes.\n * - Pre-launch (status !== \"maintained\"): the operator only — no routing, no CC.\n * Preserves the verify guard (flip a site to \"launching\" to route tests to\n * yourself).\n * - Maintained + a `Notify Routing` config: address by the routing field's value\n * (`extraFields[field]`) → its matched route, else the config `default`; CC from\n * the config. If nothing resolves, fall through to the single POC.\n * - Maintained, no routing: the single site POC (pointOfContact ?? reportRecipientsTo).\n * Returns null only when nothing resolves — the lead is still persisted; notify skips.\n */\nexport function resolveRecipients(site: WebsiteRow, submission: SubmissionRow): Recipients | null {\n if (site.status !== \"maintained\") {\n return { to: [operatorEmail()], cc: [] };\n }\n const routing = site.notifyRouting;\n if (routing) {\n const value = parseExtraFields(submission.extraFields)[routing.field];\n const match = typeof value === \"string\" ? routing.routes[value] : undefined;\n const to = normalizeRecipients(match ?? routing.default);\n if (to.length > 0) return { to, cc: normalizeRecipients(routing.cc) };\n // routing matched nothing → fall through to the POC below\n }\n const poc = pocAddress(site);\n return poc ? { to: [poc], cc: [] } : null;\n}\n\n/** Who a submission to this site would notify, answered WITHOUT sending one. */\nexport type NotifyTarget = {\n /** `client` means a real point of contact receives it. `operator` means the\n * pre-launch guard is holding and only you do. `nobody` means nothing\n * resolves — the lead is still stored, but no notification goes out. */\n audience: \"client\" | \"operator\" | \"nobody\";\n /** Every address a submission could reach, across ALL routing branches. */\n to: string[];\n cc: string[];\n /** Plain-language reason, naming the field that decided it. */\n reason: string;\n};\n\n/** A submission that exists only to ask resolveRecipients a question. */\nfunction probeSubmission(extraFields: string | null): SubmissionRow {\n return {\n id: \"probe\",\n submissionId: null,\n siteId: \"probe\",\n formType: \"contact\" as SubmissionRow[\"formType\"],\n name: \"probe\",\n email: \"probe@example.com\",\n phone: null,\n message: null,\n extraFields,\n sourceUrl: null,\n utm: null,\n submittedAt: null,\n status: \"new\" as SubmissionRow[\"status\"],\n notifyStatus: \"pending\" as NotifyStatus,\n resendMessageId: null,\n };\n}\n\n/**\n * Resolve who a form submission would notify, for a site, right now.\n *\n * This exists because the pre-launch guard had no feedback. Its whole state is\n * one Airtable `Status` cell nobody is looking at while testing, the site\n * reports nothing about it, and the only way to learn the answer was to submit\n * a form and read the `resend_message_id` afterwards. On 2026-08-03 an intended\n * flip never landed and a real client received a test lead — an email cannot be\n * recalled. A guard whose state you cannot see is a guard you will eventually\n * assume is on.\n *\n * Every address here comes back out of `resolveRecipients` itself rather than\n * from a second reading of the same rules — for a routed site each branch is\n * probed with a synthetic submission and the results unioned. A copy of the\n * routing logic that drifted would be worse than no answer at all, because it\n * would be a confident wrong one.\n */\nexport function describeNotifyTarget(site: WebsiteRow): NotifyTarget {\n if (site.status !== \"maintained\") {\n const guarded = resolveRecipients(site, probeSubmission(null));\n return {\n audience: \"operator\",\n to: guarded?.to ?? [],\n cc: [],\n reason: `Status is ${site.status === null ? \"blank\" : `\"${site.status}\"`}, not \"maintained\" — the pre-launch guard routes every notification to the operator.`,\n };\n }\n\n const routing = site.notifyRouting;\n if (!routing) {\n const direct = resolveRecipients(site, probeSubmission(null));\n if (!direct || direct.to.length === 0) {\n return {\n audience: \"nobody\",\n to: [],\n cc: [],\n reason: `Status is \"maintained\" but the site has no Point of Contact and no Notify Routing — the lead is stored and no notification is sent.`,\n };\n }\n return {\n audience: \"client\",\n to: direct.to,\n cc: direct.cc,\n reason: `Status is \"maintained\" — notifications go to the site's Point of Contact.`,\n };\n }\n\n // Probe every declared route value, plus one value that matches none so the\n // `default` (and the POC fall-through behind it) is covered too.\n const to = new Set<string>();\n const cc = new Set<string>();\n const values = [...Object.keys(routing.routes), \"\u0000no-such-route\"];\n for (const value of values) {\n const r = resolveRecipients(site, probeSubmission(JSON.stringify({ [routing.field]: value })));\n r?.to.forEach((a) => to.add(a));\n r?.cc.forEach((a) => cc.add(a));\n }\n if (to.size === 0) {\n return {\n audience: \"nobody\",\n to: [],\n cc: [],\n reason: `Status is \"maintained\" with Notify Routing on \"${routing.field}\", but no route, default or Point of Contact resolves to an address.`,\n };\n }\n return {\n audience: \"client\",\n to: [...to],\n cc: [...cc],\n reason: `Status is \"maintained\" with Notify Routing on \"${routing.field}\" — the recipient depends on that field's value; every address it could reach is listed.`,\n };\n}\n\n/** Humanize an extraFields key for display: \"appointment_date\" → \"Appointment date\". */\nfunction humanizeKey(k: string): string {\n const spaced = k.replace(/[_-]+/g, \" \").trim();\n return spaced ? spaced.charAt(0).toUpperCase() + spaced.slice(1) : k;\n}\n\nfunction formatValue(v: unknown): string {\n if (v === null || v === undefined) return \"—\";\n if (typeof v === \"string\") return v;\n if (typeof v === \"number\" || typeof v === \"boolean\") return String(v);\n return JSON.stringify(v);\n}\n\n/** Parse the stored `extraFields` JSON into a plain object — bad JSON or a\n * non-object yields {} (never throws). Shared by the email renderer and routing. */\nfunction parseExtraFields(raw: string | null): Record<string, unknown> {\n if (!raw) return {};\n try {\n const parsed = JSON.parse(raw);\n if (parsed && typeof parsed === \"object\" && !Array.isArray(parsed)) {\n return parsed as Record<string, unknown>;\n }\n } catch {\n /* malformed JSON → no fields */\n }\n return {};\n}\n\nfunction extraFieldRows(raw: string | null): Array<[string, string]> {\n return (\n Object.entries(parseExtraFields(raw))\n // Underscore keys are reserved transport (see payload.ts), never lead data.\n // `_reply` is a whole confirmation email; rendering it here would put the\n // copy in the client's notification as an unreadable JSON row.\n .filter(([k]) => !k.startsWith(\"_\"))\n .filter(([, v]) => !(typeof v === \"string\" && v.trim() === \"\"))\n .map(([k, v]) => [humanizeKey(k), formatValue(v)] as [string, string])\n );\n}\n\nfunction fieldsTable(submission: SubmissionRow): string {\n const rows: Array<[string, string]> = [\n [\"Form\", submission.formType],\n [\"Name\", submission.name || \"—\"],\n [\"Email\", submission.email || \"—\"],\n ];\n if (submission.phone) rows.push([\"Phone\", submission.phone]);\n // Site-specific context (the artwork an inquiry is about, the event an rsvp is\n // for, etc.) lives in extraFields — surface it so the recipient sees what the\n // submitter was looking at, not just their name and email.\n rows.push(...extraFieldRows(submission.extraFields));\n if (submission.sourceUrl) rows.push([\"Page\", submission.sourceUrl]);\n if (submission.utm) rows.push([\"UTM\", submission.utm]);\n const body = rows\n .map(([k, v]) => `<tr><td><strong>${escapeHtml(k)}</strong></td><td>${escapeHtml(v)}</td></tr>`)\n .join(\"\");\n const message = submission.message\n ? `<p style=\"white-space:pre-wrap\">${escapeHtml(submission.message)}</p>`\n : \"\";\n return `<table>${body}</table>${message}`;\n}\n\n/** POC notification — the primary email; null when the site has no contact address. */\nexport function buildPocNotification(\n site: WebsiteRow,\n submission: SubmissionRow,\n): ResendSendInput | null {\n if (submission.status === \"spam_auto\" || submission.status === \"spam\") return null;\n const recipients = resolveRecipients(site, submission);\n if (!recipients || recipients.to.length === 0) return null;\n const input: ResendSendInput = {\n from: `${displayName(site.name)} Forms <${FORMS_FROM}>`,\n to: recipients.to,\n subject: `New ${submission.formType} from ${site.name}`,\n html: `<h2>New ${escapeHtml(submission.formType)} submission — ${escapeHtml(\n site.name,\n )}</h2>${fieldsTable(submission)}`,\n };\n if (recipients.cc.length > 0) input.cc = recipients.cc;\n // Reply straight to the lead.\n if (submission.email) input.replyTo = submission.email;\n return input;\n}\n\n/** Autoresponder to the submitter — null when there's no submitter email. */\nexport function buildAutoresponder(\n site: WebsiteRow,\n submission: SubmissionRow,\n): ResendSendInput | null {\n if (submission.status === \"spam_auto\" || submission.status === \"spam\") return null;\n if (!submission.email) return null;\n // A submitter email on the site's OWN domain is never a real lead wanting a\n // confirmation — it is the spoofed-sender backscatter case (a bot writes the\n // site's info@ as its email and the \"We got your message\" lands in the\n // client's inbox). The POC notification still sends; only this email is\n // suppressed. Unparseable/blank site url → fail open and send, same\n // philosophy as turnstileHostnameAcceptable.\n const emailDomain = submission.email.split(\"@\").pop() ?? \"\";\n let siteHost = \"\";\n try {\n siteHost = new URL(site.url).hostname;\n } catch {\n /* fail open */\n }\n if (siteHost && hostsMatch(emailDomain, siteHost)) return null;\n const extra = parseExtraFields(submission.extraFields);\n const reply = parseReplyCopy(extra._reply);\n const eventName = typeof extra.event === \"string\" ? extra.event.trim() : \"\";\n\n // Subject, best available first. The middle tier is why an RSVP reads like a\n // confirmation even before anyone writes a word of copy: the event name is\n // already on every submission that has one.\n const fallback = defaultReply(submission.formType, site.name);\n const subject =\n reply?.subject ?? (eventName ? `You're on the list for ${eventName}` : fallback.subject);\n\n // Body. The per-site trio is the last-resort net for sites that author\n // nothing — it has no editor since the Airtable freeze, so anything a client\n // can actually change now comes through the envelope.\n const signature = reply?.signature ?? site.copyFooter ?? site.name;\n\n // The legacy per-site columns still win over the built-in default where a\n // site has them set — they were written for that client. Where they're blank\n // (every site created since the Airtable freeze) the per-form-type default\n // fills in, which is strictly better than the one generic line it replaces.\n const body = reply?.body\n ? renderBlocks(reply.body)\n : [site.copyIntro ?? fallback.paragraphs[0], site.copyContact ?? fallback.paragraphs[1]]\n .map((p) => `<p>${escapeHtml(p)}</p>`)\n .join(\"\");\n // The one block assembled rather than escaped wholesale, because it carries an\n // anchor we build ourselves. Its only interpolated value is the Google URL,\n // escaped on the way in — and parseReplyCopy already refused any non-https\n // `url` that feeds it.\n const calendar = reply?.calendar;\n const calendarBlock = calendar\n ? `<p>Add it to your calendar: <a href=\"${escapeHtml(googleCalendarUrl(calendar))}\">Google Calendar</a>. ` +\n `The attached invite works in Apple Calendar and Outlook.</p>`\n : \"\";\n\n const input: ResendSendInput = {\n from: `${displayName(site.name)} <${FORMS_FROM}>`,\n to: [submission.email],\n replyTo: resolveRecipients(site, submission)?.to[0] ?? FALLBACK_REPLY_TO,\n subject,\n html: `${body}${calendarBlock}<p>${escapeHtml(signature)}</p>`,\n };\n if (calendar) {\n // A bare Google link strands every Apple Mail reader; an .ics alone is a\n // file most people on a phone will not open. Both, once.\n input.attachments = [\n {\n filename: \"event.ics\",\n content: Buffer.from(buildIcs(calendar), \"utf8\").toString(\"base64\"),\n contentType: \"text/calendar\",\n },\n ];\n }\n return input;\n}\n\nexport type NotifyDeps = {\n send: (input: ResendSendInput) => Promise<{ messageId: string }>;\n};\n\nexport type NotifyOutcome = { status: NotifyStatus; messageId: string | null };\n\n/**\n * Send the POC notification (primary — drives notifyStatus) then the submitter\n * autoresponder (best-effort — logged, never changes the outcome). The submission\n * is already persisted before this runs, so a Resend outage degrades to\n * notifyStatus=\"failed\", never a lost lead.\n */\nexport async function notifySubmission(\n deps: NotifyDeps,\n site: WebsiteRow,\n submission: SubmissionRow,\n): Promise<NotifyOutcome> {\n const poc = buildPocNotification(site, submission);\n let outcome: NotifyOutcome;\n if (!poc) {\n outcome = { status: \"skipped\", messageId: null };\n } else {\n try {\n const { messageId } = await deps.send(poc);\n outcome = { status: \"sent\", messageId };\n } catch (err) {\n console.error(`[submissions] POC notification failed: ${String(err)}`);\n outcome = { status: \"failed\", messageId: null };\n }\n }\n const auto = buildAutoresponder(site, submission);\n if (auto) {\n try {\n await deps.send(auto);\n } catch (err) {\n console.error(`[submissions] autoresponder failed: ${String(err)}`);\n }\n }\n return outcome;\n}\n\n/**\n * Build the ingest `notify` dependency from a Resend send fn — or `null` when the\n * Resend client couldn't even be constructed (e.g. `RESEND_API_KEY` unset). A null\n * send marks the notification `failed` WITHOUT attempting it, so a Resend\n * misconfiguration degrades to a captured-but-unemailed lead rather than aborting\n * ingest and losing it. Mirrors the in-flight failure isolation in notifySubmission.\n */\nexport function makeNotify(\n send: NotifyDeps[\"send\"] | null,\n): (site: WebsiteRow, submission: SubmissionRow) => Promise<NotifyOutcome> {\n return (site, submission) =>\n send\n ? notifySubmission({ send }, site, submission)\n : Promise.resolve({ status: \"failed\", messageId: null });\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,27 +1,27 @@
|
|
|
1
1
|
import {
|
|
2
2
|
convertToPnpm
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-7CJ3DVZW.js";
|
|
4
4
|
import {
|
|
5
5
|
svelteCodemods
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-LS6QPANO.js";
|
|
7
7
|
import {
|
|
8
8
|
healthEndpoint
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-4R3YAQNV.js";
|
|
10
10
|
import {
|
|
11
11
|
smokeSuite
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-CVJF2IC5.js";
|
|
13
13
|
import {
|
|
14
14
|
onboard
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-NTLSMJHW.js";
|
|
16
16
|
import {
|
|
17
17
|
syncConfigs
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-3HS6LPFX.js";
|
|
19
19
|
import {
|
|
20
20
|
withRecipe
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-ZTPPCHDB.js";
|
|
22
22
|
import {
|
|
23
23
|
runAudits
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-JB4Q3C3M.js";
|
|
25
25
|
import {
|
|
26
26
|
siteLabel
|
|
27
27
|
} from "./chunk-XXTZBPUY.js";
|
|
@@ -143,4 +143,4 @@ export {
|
|
|
143
143
|
DEFAULT_INIT_STEPS,
|
|
144
144
|
init
|
|
145
145
|
};
|
|
146
|
-
//# sourceMappingURL=chunk-
|
|
146
|
+
//# sourceMappingURL=chunk-4NUKMPOO.js.map
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
} from "./chunk-R53VL3RI.js";
|
|
5
5
|
import {
|
|
6
6
|
withRecipe
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-ZTPPCHDB.js";
|
|
8
8
|
import {
|
|
9
9
|
defaultSpawn
|
|
10
10
|
} from "./chunk-LBYOLBW7.js";
|
|
@@ -142,4 +142,4 @@ async function healthEndpoint(site, deps = { spawn: defaultSpawn }) {
|
|
|
142
142
|
export {
|
|
143
143
|
healthEndpoint
|
|
144
144
|
};
|
|
145
|
-
//# sourceMappingURL=chunk-
|
|
145
|
+
//# sourceMappingURL=chunk-4R3YAQNV.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
withRecipe
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-ZTPPCHDB.js";
|
|
4
4
|
import {
|
|
5
5
|
readPackageJson,
|
|
6
6
|
writePackageJson
|
|
@@ -96,4 +96,4 @@ async function convertToPnpm(site, opts = {}) {
|
|
|
96
96
|
export {
|
|
97
97
|
convertToPnpm
|
|
98
98
|
};
|
|
99
|
-
//# sourceMappingURL=chunk-
|
|
99
|
+
//# sourceMappingURL=chunk-7CJ3DVZW.js.map
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
} from "./chunk-J7SZQUCW.js";
|
|
4
4
|
import {
|
|
5
5
|
withRecipe
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-ZTPPCHDB.js";
|
|
7
7
|
import {
|
|
8
8
|
bumpDep,
|
|
9
9
|
readPackageJson,
|
|
@@ -243,4 +243,4 @@ async function upgradeSvelte4to5(site, opts = {}) {
|
|
|
243
243
|
export {
|
|
244
244
|
upgradeSvelte4to5
|
|
245
245
|
};
|
|
246
|
-
//# sourceMappingURL=chunk-
|
|
246
|
+
//# sourceMappingURL=chunk-B6NUZ2ZX.js.map
|