@reddoorla/maintenance 0.42.0 → 0.43.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/cli/bin.js +81 -9
- package/dist/cli/bin.js.map +1 -1
- package/dist/index.d.ts +17 -0
- package/dist/index.js +217 -38
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/bin.js
CHANGED
|
@@ -575,6 +575,39 @@ var init_write_audits_to_airtable = __esm({
|
|
|
575
575
|
}
|
|
576
576
|
});
|
|
577
577
|
|
|
578
|
+
// src/reports/checklist.ts
|
|
579
|
+
function checklistFor(type) {
|
|
580
|
+
return type === "Maintenance" ? MAINTENANCE_CHECKLIST : type === "Testing" ? TESTING_CHECKLIST : [];
|
|
581
|
+
}
|
|
582
|
+
function isChecklistComplete(report) {
|
|
583
|
+
return checklistFor(report.reportType).every((i) => report.checklist[i.field] === true);
|
|
584
|
+
}
|
|
585
|
+
var MAINTENANCE_CHECKLIST, TESTING_CHECKLIST, ALL_CHECKLIST_FIELDS;
|
|
586
|
+
var init_checklist = __esm({
|
|
587
|
+
"src/reports/checklist.ts"() {
|
|
588
|
+
"use strict";
|
|
589
|
+
MAINTENANCE_CHECKLIST = [
|
|
590
|
+
{ key: "logs", label: "Reviewed Logs", field: "Maint: Reviewed Logs" },
|
|
591
|
+
{ key: "cms", label: "CMS Checked", field: "Maint: CMS Checked" },
|
|
592
|
+
{ key: "dns", label: "DNS Checked", field: "Maint: DNS Checked" },
|
|
593
|
+
{ key: "google", label: "Google Indexed", field: "Maint: Google Indexed" },
|
|
594
|
+
{ key: "cert", label: "Reviewed Certificate", field: "Maint: Reviewed Certificate" },
|
|
595
|
+
{ key: "security", label: "Security Updates", field: "Maint: Security Updates" }
|
|
596
|
+
];
|
|
597
|
+
TESTING_CHECKLIST = [
|
|
598
|
+
{ key: "desktop", label: "Desktop Browsers", field: "Test: Desktop Browsers" },
|
|
599
|
+
{ key: "mobile", label: "Mobile Browsers", field: "Test: Mobile Browsers" },
|
|
600
|
+
{ key: "packages", label: "Package Updates", field: "Test: Package Updates" },
|
|
601
|
+
{ key: "bottle", label: "Bottlenecks", field: "Test: Bottlenecks" },
|
|
602
|
+
{ key: "forms", label: "Form Functionality", field: "Test: Form Functionality" },
|
|
603
|
+
{ key: "animation", label: "Animation Functionality", field: "Test: Animation Functionality" }
|
|
604
|
+
];
|
|
605
|
+
ALL_CHECKLIST_FIELDS = [...MAINTENANCE_CHECKLIST, ...TESTING_CHECKLIST].map(
|
|
606
|
+
(i) => i.field
|
|
607
|
+
);
|
|
608
|
+
}
|
|
609
|
+
});
|
|
610
|
+
|
|
578
611
|
// src/reports/airtable/reports.ts
|
|
579
612
|
function toReportType(raw) {
|
|
580
613
|
if (raw && REPORT_TYPES.includes(raw)) return raw;
|
|
@@ -613,7 +646,8 @@ function mapRow2(rec) {
|
|
|
613
646
|
approvedBy: f["Approved By"] ?? null,
|
|
614
647
|
deliveryStatus: f["Delivery status"] ?? "pending",
|
|
615
648
|
renderedHtmlAttachment: html,
|
|
616
|
-
resendMessageId: f["Resend message ID"] ?? null
|
|
649
|
+
resendMessageId: f["Resend message ID"] ?? null,
|
|
650
|
+
checklist: Object.fromEntries(ALL_CHECKLIST_FIELDS.map((name) => [name, Boolean(f[name])]))
|
|
617
651
|
};
|
|
618
652
|
}
|
|
619
653
|
function lighthouseFromFields(f) {
|
|
@@ -717,6 +751,7 @@ var REPORTS_TABLE, REPORT_TYPES;
|
|
|
717
751
|
var init_reports = __esm({
|
|
718
752
|
"src/reports/airtable/reports.ts"() {
|
|
719
753
|
"use strict";
|
|
754
|
+
init_checklist();
|
|
720
755
|
REPORTS_TABLE = "Reports";
|
|
721
756
|
REPORT_TYPES = ["Maintenance", "Testing", "Launch", "Announcement"];
|
|
722
757
|
}
|
|
@@ -779,13 +814,16 @@ var init_copy = __esm({
|
|
|
779
814
|
"Continuous integration + automatic dependency updates",
|
|
780
815
|
"Analytics and uptime monitoring"
|
|
781
816
|
],
|
|
782
|
-
announceHeading: "YOUR
|
|
783
|
-
announceBody: "We've
|
|
817
|
+
announceHeading: "YOUR ONGOING SITE CARE",
|
|
818
|
+
announceBody: "We've completed a full test of your site and set it up for ongoing care to keep it fast, secure, and healthy. Here's what you can expect from us going forward:",
|
|
819
|
+
announceCadenceHeading: "WHAT TO EXPECT",
|
|
820
|
+
announceTestingLabel: "Full site testing",
|
|
821
|
+
announceMaintenanceLabel: "Routine maintenance",
|
|
784
822
|
announceMonitorItems: ["Performance", "Accessibility", "Security", "Uptime"],
|
|
785
|
-
announcePreviewLabel: "
|
|
823
|
+
announcePreviewLabel: "From your latest full site test:",
|
|
786
824
|
announceImprovementResend: "Your contact forms now deliver straight to your inbox through reliable infrastructure, so no inquiry slips through the cracks.",
|
|
787
825
|
announceImprovementSvelte5: "We've modernized your site to the latest framework \u2014 it's faster, more secure, and built to last.",
|
|
788
|
-
announceCadence: "
|
|
826
|
+
announceCadence: "After each one we'll send you a short report like this \u2014 there's nothing you need to do.",
|
|
789
827
|
announceOpenDoor: "And if you'd ever like to expand the scope, add features, or freshen anything up, just reply \u2014 we'd love to help."
|
|
790
828
|
};
|
|
791
829
|
}
|
|
@@ -1140,6 +1178,23 @@ function buildAnnouncementMjml(data) {
|
|
|
1140
1178
|
).join("")}
|
|
1141
1179
|
</mj-column>
|
|
1142
1180
|
</mj-section>` : "";
|
|
1181
|
+
const cad = data.cadence;
|
|
1182
|
+
const cadenceLines = [];
|
|
1183
|
+
if (cad && cad.testing !== "None")
|
|
1184
|
+
cadenceLines.push(`${copy.announceTestingLabel} \u2014 ${FREQ_PHRASE[cad.testing]}`);
|
|
1185
|
+
if (cad && cad.maintenance !== "None")
|
|
1186
|
+
cadenceLines.push(`${copy.announceMaintenanceLabel} \u2014 ${FREQ_PHRASE[cad.maintenance]}`);
|
|
1187
|
+
const cadenceSection = cadenceLines.length > 0 ? `
|
|
1188
|
+
<mj-section background-color="white">
|
|
1189
|
+
<mj-column>
|
|
1190
|
+
<mj-text color="${RED2}" font-size="20px" font-weight="700" padding-top="36px">${escapeXml(copy.announceCadenceHeading)}</mj-text>
|
|
1191
|
+
${cadenceLines.map(
|
|
1192
|
+
(line) => `
|
|
1193
|
+
<mj-text color="${GREY2}" font-family="helvetica, sans-serif" font-size="16px" font-weight="300" line-height="24px" padding-top="4px" padding-bottom="4px">\u2022 ${escapeXml(line)}</mj-text>`
|
|
1194
|
+
).join("")}
|
|
1195
|
+
<mj-text color="${GREY2}" font-family="helvetica, sans-serif" font-size="16px" font-weight="300" line-height="24px" padding-top="12px">${escapeXml(copy.announceCadence)}</mj-text>
|
|
1196
|
+
</mj-column>
|
|
1197
|
+
</mj-section>` : "";
|
|
1143
1198
|
const monitorRows = copy.announceMonitorItems.map(
|
|
1144
1199
|
(item) => `
|
|
1145
1200
|
<mj-text color="${GREY2}" font-family="helvetica, sans-serif" font-size="16px" font-weight="300" line-height="24px" padding-top="4px" padding-bottom="4px">\u2022 ${escapeXml(item)}</mj-text>`
|
|
@@ -1178,6 +1233,7 @@ function buildAnnouncementMjml(data) {
|
|
|
1178
1233
|
<mj-text color="${GREY2}" font-family="helvetica, sans-serif" font-size="16px" font-weight="300" line-height="24px" padding-top="8px">${escapeXml(copy.announceBody)}</mj-text>
|
|
1179
1234
|
</mj-column>
|
|
1180
1235
|
</mj-section>
|
|
1236
|
+
${cadenceSection}
|
|
1181
1237
|
${improvementsSection}
|
|
1182
1238
|
<mj-section background-color="white">
|
|
1183
1239
|
<mj-column>
|
|
@@ -1193,8 +1249,7 @@ function buildAnnouncementMjml(data) {
|
|
|
1193
1249
|
</mj-section>
|
|
1194
1250
|
<mj-section background-color="white">
|
|
1195
1251
|
<mj-column>
|
|
1196
|
-
<mj-text color="${GREY2}" font-family="helvetica, sans-serif" font-size="16px" font-weight="300" line-height="24px" padding-top="36px">${escapeXml(copy.
|
|
1197
|
-
<mj-text color="${GREY2}" font-family="helvetica, sans-serif" font-size="16px" font-weight="300" line-height="24px" padding-top="8px">${escapeXml(copy.announceOpenDoor)}</mj-text>
|
|
1252
|
+
<mj-text color="${GREY2}" font-family="helvetica, sans-serif" font-size="16px" font-weight="300" line-height="24px" padding-top="36px">${escapeXml(copy.announceOpenDoor)}</mj-text>
|
|
1198
1253
|
</mj-column>
|
|
1199
1254
|
</mj-section>
|
|
1200
1255
|
<mj-section background-color="white">
|
|
@@ -1211,12 +1266,17 @@ function buildAnnouncementMjml(data) {
|
|
|
1211
1266
|
</mj-body>
|
|
1212
1267
|
</mjml>`;
|
|
1213
1268
|
}
|
|
1214
|
-
var RED2, GREY2, SCORE_PREVIEW;
|
|
1269
|
+
var FREQ_PHRASE, RED2, GREY2, SCORE_PREVIEW;
|
|
1215
1270
|
var init_template3 = __esm({
|
|
1216
1271
|
"src/reports/announcement-email/template.ts"() {
|
|
1217
1272
|
"use strict";
|
|
1218
1273
|
init_copy();
|
|
1219
1274
|
init_template();
|
|
1275
|
+
FREQ_PHRASE = {
|
|
1276
|
+
Monthly: "every month",
|
|
1277
|
+
Quarterly: "every quarter",
|
|
1278
|
+
Yearly: "every year"
|
|
1279
|
+
};
|
|
1220
1280
|
RED2 = "#C00";
|
|
1221
1281
|
GREY2 = "#757575";
|
|
1222
1282
|
SCORE_PREVIEW = [
|
|
@@ -1827,6 +1887,13 @@ async function sendApprovedReports(options = {}) {
|
|
|
1827
1887
|
return { output: lines.join("\n"), code: anyFailed ? 1 : 0 };
|
|
1828
1888
|
}
|
|
1829
1889
|
async function sendOne(client, base, site, report) {
|
|
1890
|
+
if (!isChecklistComplete(report)) {
|
|
1891
|
+
const items = checklistFor(report.reportType);
|
|
1892
|
+
const done = items.filter((i) => report.checklist[i.field] === true).length;
|
|
1893
|
+
throw new Error(
|
|
1894
|
+
`Report ${report.reportId} checklist incomplete \u2014 ${done}/${items.length} items checked`
|
|
1895
|
+
);
|
|
1896
|
+
}
|
|
1830
1897
|
if (!site.headerImage) {
|
|
1831
1898
|
throw new Error(`Site '${site.name}' has no Header image set on the Websites row`);
|
|
1832
1899
|
}
|
|
@@ -1970,6 +2037,7 @@ var init_orchestrate = __esm({
|
|
|
1970
2037
|
init_header_image();
|
|
1971
2038
|
init_resend();
|
|
1972
2039
|
init_idempotency();
|
|
2040
|
+
init_checklist();
|
|
1973
2041
|
FROM_ADDRESS2 = "Reddoor Reports <reports@reddoorla.com>";
|
|
1974
2042
|
REPLY_TO = "info@reddoorla.com";
|
|
1975
2043
|
MONTHS2 = [
|
|
@@ -6373,6 +6441,10 @@ async function announce(deps) {
|
|
|
6373
6441
|
commentary: null,
|
|
6374
6442
|
copy: resolveCopy(w),
|
|
6375
6443
|
headerImageCid: `${slug}-header`,
|
|
6444
|
+
// The client's go-forward pace, read straight off the Websites row — the email
|
|
6445
|
+
// states each cadence ("Full site testing — every quarter", etc.); a "None" pace
|
|
6446
|
+
// is omitted so we never claim a cadence the site isn't on.
|
|
6447
|
+
cadence: { maintenance: w.maintenanceFreq, testing: w.testingFreq },
|
|
6376
6448
|
// Default-on fleet-wide for v1: both recent-improvement callouts render for every
|
|
6377
6449
|
// site. Operator review (the draft never auto-sends) is the relevance backstop;
|
|
6378
6450
|
// per-site conditioning of these flags is a future lever, not a v1 requirement.
|
|
@@ -6426,7 +6498,7 @@ function draftInputFor2(w, scores, now, period) {
|
|
|
6426
6498
|
completedOn: now,
|
|
6427
6499
|
lighthouse: scores,
|
|
6428
6500
|
lastTestedDate: null,
|
|
6429
|
-
subjectOverride: `Your
|
|
6501
|
+
subjectOverride: `Your testing & maintenance schedule for ${w.name}`
|
|
6430
6502
|
};
|
|
6431
6503
|
}
|
|
6432
6504
|
|