@reddoorla/maintenance 0.45.0 → 0.47.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 +124 -42
- package/dist/cli/bin.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +90 -28
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -287,8 +287,8 @@ type ResolvedCopy = {
|
|
|
287
287
|
announceCadenceHeading: string;
|
|
288
288
|
announceTestingLabel: string;
|
|
289
289
|
announceMaintenanceLabel: string;
|
|
290
|
-
announceMonitorItems: string[];
|
|
291
290
|
announcePreviewLabel: string;
|
|
291
|
+
announceScoreNote: string;
|
|
292
292
|
announceImprovementResend: string;
|
|
293
293
|
announceImprovementSvelte5: string;
|
|
294
294
|
announceCadence: string;
|
package/dist/index.js
CHANGED
|
@@ -2769,7 +2769,7 @@ var DEFAULT_COPY = {
|
|
|
2769
2769
|
"Links & Navigation",
|
|
2770
2770
|
"Form Functionality",
|
|
2771
2771
|
"Interactions & Animations",
|
|
2772
|
-
"
|
|
2772
|
+
"Tested After Updates"
|
|
2773
2773
|
],
|
|
2774
2774
|
notesHeader: "NOTES",
|
|
2775
2775
|
seoCta: "Contact us if you are interested in more in-depth data or have questions about SEO.",
|
|
@@ -2788,8 +2788,8 @@ var DEFAULT_COPY = {
|
|
|
2788
2788
|
announceCadenceHeading: "WHAT TO EXPECT",
|
|
2789
2789
|
announceTestingLabel: "Full site testing",
|
|
2790
2790
|
announceMaintenanceLabel: "Routine maintenance",
|
|
2791
|
-
announceMonitorItems: ["Performance", "Accessibility", "Security", "Uptime"],
|
|
2792
2791
|
announcePreviewLabel: "From your latest full site test:",
|
|
2792
|
+
announceScoreNote: "These are independent Google Lighthouse scores, each out of 100 \u2014 higher is better.",
|
|
2793
2793
|
announceImprovementResend: "Your contact forms now deliver straight to your inbox through reliable infrastructure, so no inquiry slips through the cracks.",
|
|
2794
2794
|
announceImprovementSvelte5: "We've modernized your site to the latest framework \u2014 it's faster, more secure, and built to last.",
|
|
2795
2795
|
announceCadence: "After each one we'll send you a short report like this \u2014 there's nothing you need to do.",
|
|
@@ -3031,7 +3031,7 @@ function buildMjml(data) {
|
|
|
3031
3031
|
<mj-text color="#C00" font-size="44px" font-weight="400" padding-top="0px">${data.lighthouse.performance}</mj-text>
|
|
3032
3032
|
<mj-text color="#757575" font-family="helvetica, sans-serif" font-size="12px" font-weight="300" padding-top="0px" padding-bottom="36px">Acceptable 50\u201389 // Ideal 90\u2013100</mj-text>
|
|
3033
3033
|
<mj-divider border-width="1px" border-style="solid" border-color="#CCCCCC" padding="0" />
|
|
3034
|
-
<mj-text color="#C00" font-size="20px" font-weight="300" padding-top="25px">Readability</mj-text>
|
|
3034
|
+
<mj-text color="#C00" font-size="20px" font-weight="300" padding-top="25px">Readability (A11y)</mj-text>
|
|
3035
3035
|
<mj-text color="#C00" font-size="44px" font-weight="400" padding-top="0px">${data.lighthouse.accessibility}</mj-text>
|
|
3036
3036
|
<mj-text color="#757575" font-family="helvetica, sans-serif" font-size="12px" font-weight="300" padding-top="0px" padding-bottom="36px">Acceptable 80\u201399 // Ideal 100</mj-text>
|
|
3037
3037
|
<mj-divider border-width="1px" border-style="solid" border-color="#CCCCCC" padding="0" />
|
|
@@ -3136,9 +3136,26 @@ var FREQ_PHRASE = {
|
|
|
3136
3136
|
};
|
|
3137
3137
|
var RED2 = "#C00";
|
|
3138
3138
|
var GREY2 = "#757575";
|
|
3139
|
+
var CHECK_PNG2 = `cid:${CHECK_CID}`;
|
|
3140
|
+
function fmtVisitors(n) {
|
|
3141
|
+
return n.toLocaleString("en-US");
|
|
3142
|
+
}
|
|
3143
|
+
function visitorTrend(cur, prev) {
|
|
3144
|
+
if (cur === void 0 || prev === void 0 || prev === 0) return null;
|
|
3145
|
+
const pct = Math.round((cur - prev) / prev * 100);
|
|
3146
|
+
if (pct > 0) return `\u25B2 ${pct}% vs the previous month`;
|
|
3147
|
+
if (pct < 0) return `\u25BC ${Math.abs(pct)}% vs the previous month`;
|
|
3148
|
+
return "No change vs the previous month";
|
|
3149
|
+
}
|
|
3150
|
+
function announcementSiteExtras(site) {
|
|
3151
|
+
return {
|
|
3152
|
+
cadence: { maintenance: site.maintenanceFreq, testing: site.testingFreq },
|
|
3153
|
+
improvements: { resendForms: true, svelte5: true }
|
|
3154
|
+
};
|
|
3155
|
+
}
|
|
3139
3156
|
var SCORE_PREVIEW = [
|
|
3140
3157
|
{ label: "Performance", key: "performance" },
|
|
3141
|
-
{ label: "Readability", key: "accessibility" },
|
|
3158
|
+
{ label: "Readability (A11y)", key: "accessibility" },
|
|
3142
3159
|
{ label: "Best Practices", key: "bestPractices" },
|
|
3143
3160
|
{ label: "Site Structure", key: "seo" }
|
|
3144
3161
|
];
|
|
@@ -3159,31 +3176,53 @@ function buildAnnouncementMjml(data) {
|
|
|
3159
3176
|
</mj-column>
|
|
3160
3177
|
</mj-section>` : "";
|
|
3161
3178
|
const cad = data.cadence;
|
|
3162
|
-
const
|
|
3179
|
+
const cadenceBlocks = [];
|
|
3163
3180
|
if (cad && cad.testing !== "None")
|
|
3164
|
-
|
|
3181
|
+
cadenceBlocks.push({
|
|
3182
|
+
line: `${copy.announceTestingLabel} \u2014 ${FREQ_PHRASE[cad.testing]}`,
|
|
3183
|
+
checks: copy.testingChecklist
|
|
3184
|
+
});
|
|
3165
3185
|
if (cad && cad.maintenance !== "None")
|
|
3166
|
-
|
|
3167
|
-
|
|
3186
|
+
cadenceBlocks.push({
|
|
3187
|
+
line: `${copy.announceMaintenanceLabel} \u2014 ${FREQ_PHRASE[cad.maintenance]}`,
|
|
3188
|
+
checks: copy.maintenanceChecks
|
|
3189
|
+
});
|
|
3190
|
+
const cadenceSection = cadenceBlocks.length > 0 ? `
|
|
3168
3191
|
<mj-section background-color="white">
|
|
3169
3192
|
<mj-column>
|
|
3170
3193
|
<mj-text color="${RED2}" font-size="20px" font-weight="700" padding-top="36px">${escapeXml(copy.announceCadenceHeading)}</mj-text>
|
|
3171
|
-
${
|
|
3172
|
-
(
|
|
3173
|
-
<mj-text color="${GREY2}" font-family="helvetica, sans-serif" font-size="16px" font-weight="
|
|
3194
|
+
${cadenceBlocks.map(
|
|
3195
|
+
(b) => `
|
|
3196
|
+
<mj-text color="${GREY2}" font-family="helvetica, sans-serif" font-size="16px" font-weight="400" line-height="24px" padding-top="12px" padding-bottom="2px">\u2022 ${escapeXml(b.line)}</mj-text>${b.checks.map(
|
|
3197
|
+
(c) => `
|
|
3198
|
+
<mj-text color="${GREY2}" font-family="helvetica, sans-serif" font-size="14px" font-weight="300" line-height="22px" padding-top="1px" padding-bottom="0px" padding-left="16px">${escapeXml(c)} <img src="${CHECK_PNG2}" alt="\u2713" width="14" height="14" style="vertical-align:middle;display:inline-block;margin-left:2px;" /></mj-text>`
|
|
3199
|
+
).join("")}`
|
|
3174
3200
|
).join("")}
|
|
3175
|
-
<mj-text color="${GREY2}" font-family="helvetica, sans-serif" font-size="16px" font-weight="300" line-height="24px" padding-top="
|
|
3201
|
+
<mj-text color="${GREY2}" font-family="helvetica, sans-serif" font-size="16px" font-weight="300" line-height="24px" padding-top="14px">${escapeXml(copy.announceCadence)}</mj-text>
|
|
3176
3202
|
</mj-column>
|
|
3177
3203
|
</mj-section>` : "";
|
|
3178
|
-
const monitorRows = copy.announceMonitorItems.map(
|
|
3179
|
-
(item) => `
|
|
3180
|
-
<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>`
|
|
3181
|
-
).join("");
|
|
3182
3204
|
const scoreRows = SCORE_PREVIEW.map(
|
|
3183
3205
|
({ label, key }) => `
|
|
3184
3206
|
<mj-text color="${RED2}" font-size="20px" font-weight="300" padding-top="25px">${label}</mj-text>
|
|
3185
3207
|
<mj-text color="${RED2}" font-size="44px" font-weight="400" padding-top="0px">${data.lighthouse[key]}</mj-text>`
|
|
3186
3208
|
).join("");
|
|
3209
|
+
const scoreNote = copy.announceScoreNote ? `
|
|
3210
|
+
<mj-text color="${GREY2}" font-family="helvetica, sans-serif" font-size="12px" font-weight="300" font-style="italic" line-height="18px" padding-top="16px">${escapeXml(copy.announceScoreNote)}</mj-text>` : "";
|
|
3211
|
+
const trend = visitorTrend(data.gaUsersCurrent, data.gaUsersPrevious);
|
|
3212
|
+
const trafficRows = [];
|
|
3213
|
+
if (data.gaUsersCurrent !== void 0)
|
|
3214
|
+
trafficRows.push(`
|
|
3215
|
+
<mj-text color="${GREY2}" font-family="helvetica, sans-serif" font-size="16px" font-weight="300" line-height="24px" padding-top="8px"><span style="color:${RED2};font-size:22px;font-weight:400;">${escapeXml(fmtVisitors(data.gaUsersCurrent))}</span> visitors in the last month${trend ? ` \u2014 ${escapeXml(trend)}` : ""}</mj-text>`);
|
|
3216
|
+
if (data.searchPosition !== void 0)
|
|
3217
|
+
trafficRows.push(`
|
|
3218
|
+
<mj-text color="${GREY2}" font-family="helvetica, sans-serif" font-size="16px" font-weight="300" line-height="24px" padding-top="4px">Page 1 Google result (#${data.searchPosition}) for your brand search</mj-text>`);
|
|
3219
|
+
const trafficSection = trafficRows.length > 0 ? `
|
|
3220
|
+
<mj-section background-color="white">
|
|
3221
|
+
<mj-column>
|
|
3222
|
+
<mj-text color="${RED2}" font-size="20px" font-weight="700" padding-top="36px">TRAFFIC & SEARCH</mj-text>
|
|
3223
|
+
${trafficRows.join("")}
|
|
3224
|
+
</mj-column>
|
|
3225
|
+
</mj-section>` : "";
|
|
3187
3226
|
const contactRows = copy.contact.map(
|
|
3188
3227
|
(line) => `
|
|
3189
3228
|
<mj-text font-family="helvetica, sans-serif" font-size="24px" font-weight="300" line-height="30px">${escapeXml(line)}</mj-text>`
|
|
@@ -3215,18 +3254,13 @@ function buildAnnouncementMjml(data) {
|
|
|
3215
3254
|
</mj-section>
|
|
3216
3255
|
${cadenceSection}
|
|
3217
3256
|
${improvementsSection}
|
|
3218
|
-
<mj-section background-color="white">
|
|
3219
|
-
<mj-column>
|
|
3220
|
-
<mj-text color="${RED2}" font-size="20px" font-weight="700" padding-top="36px">WHAT WE MONITOR</mj-text>
|
|
3221
|
-
${monitorRows}
|
|
3222
|
-
</mj-column>
|
|
3223
|
-
</mj-section>
|
|
3224
3257
|
<mj-section background-color="#F4F4F4">
|
|
3225
3258
|
<mj-column>
|
|
3226
3259
|
<mj-text color="${RED2}" font-size="20px" font-weight="700" padding-top="55px">${escapeXml(copy.announcePreviewLabel)}</mj-text>
|
|
3227
|
-
${scoreRows}
|
|
3260
|
+
${scoreRows}${scoreNote}
|
|
3228
3261
|
</mj-column>
|
|
3229
3262
|
</mj-section>
|
|
3263
|
+
${trafficSection}
|
|
3230
3264
|
<mj-section background-color="white">
|
|
3231
3265
|
<mj-column>
|
|
3232
3266
|
<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>
|
|
@@ -3274,7 +3308,10 @@ var TESTING_CHECKLIST = [
|
|
|
3274
3308
|
label: "Interactions & Animations",
|
|
3275
3309
|
field: "Test: Interactions & Animations"
|
|
3276
3310
|
},
|
|
3277
|
-
|
|
3311
|
+
// `field` keeps its original Airtable column name ("…Verified After Updates") even though
|
|
3312
|
+
// the client-facing label is now "Tested After Updates" — the column holds operator data,
|
|
3313
|
+
// so renaming the label is display-only and avoids a live-base column migration.
|
|
3314
|
+
{ key: "updates", label: "Tested After Updates", field: "Test: Verified After Updates" }
|
|
3278
3315
|
];
|
|
3279
3316
|
var ALL_CHECKLIST_FIELDS = [...MAINTENANCE_CHECKLIST, ...TESTING_CHECKLIST].map(
|
|
3280
3317
|
(i) => i.field
|
|
@@ -3515,6 +3552,7 @@ import { JWT as JWT2 } from "google-auth-library";
|
|
|
3515
3552
|
var WEBMASTERS_READONLY = "https://www.googleapis.com/auth/webmasters.readonly";
|
|
3516
3553
|
var SC_BASE = "https://searchconsole.googleapis.com/webmasters/v3";
|
|
3517
3554
|
var PAGE_1_MAX_POSITION = 10;
|
|
3555
|
+
var BRAND_QUERY_ROW_LIMIT = 100;
|
|
3518
3556
|
function bareHost(s) {
|
|
3519
3557
|
return s.trim().replace(/^sc-domain:/i, "").replace(/^https?:\/\//i, "").split("/")[0].replace(/^www\./i, "").toLowerCase();
|
|
3520
3558
|
}
|
|
@@ -3528,6 +3566,25 @@ function resolvePropertyCandidates(entries, host) {
|
|
|
3528
3566
|
function ymd3(d) {
|
|
3529
3567
|
return d.toISOString().slice(0, 10);
|
|
3530
3568
|
}
|
|
3569
|
+
function pickBrandQuery(rows) {
|
|
3570
|
+
let best;
|
|
3571
|
+
for (const r of rows) {
|
|
3572
|
+
if (typeof r.position !== "number") continue;
|
|
3573
|
+
const impressions = r.impressions ?? 0;
|
|
3574
|
+
if (best === void 0 || impressions > best.impressions || impressions === best.impressions && r.position < best.position) {
|
|
3575
|
+
best = { position: r.position, impressions };
|
|
3576
|
+
}
|
|
3577
|
+
}
|
|
3578
|
+
return best?.position;
|
|
3579
|
+
}
|
|
3580
|
+
function selectBrandPosition(rows, exactQuery) {
|
|
3581
|
+
const want = exactQuery.toLowerCase();
|
|
3582
|
+
const exact = rows.find(
|
|
3583
|
+
(r) => typeof r.position === "number" && (r.keys?.[0] ?? "").toLowerCase() === want
|
|
3584
|
+
);
|
|
3585
|
+
if (exact) return exact.position;
|
|
3586
|
+
return pickBrandQuery(rows);
|
|
3587
|
+
}
|
|
3531
3588
|
async function fetchSearchPresence(q, periodStart, periodEnd) {
|
|
3532
3589
|
const key = JSON.parse(readFileSync4(q.keyPath, "utf8"));
|
|
3533
3590
|
const jwt = new JWT2({
|
|
@@ -3559,14 +3616,14 @@ async function fetchSearchPresence(q, periodStart, periodEnd) {
|
|
|
3559
3616
|
dimensionFilterGroups: [
|
|
3560
3617
|
{
|
|
3561
3618
|
filters: [
|
|
3562
|
-
{ dimension: "query", operator: "
|
|
3619
|
+
{ dimension: "query", operator: "contains", expression: q.query.toLowerCase() }
|
|
3563
3620
|
]
|
|
3564
3621
|
}
|
|
3565
3622
|
],
|
|
3566
|
-
rowLimit:
|
|
3623
|
+
rowLimit: BRAND_QUERY_ROW_LIMIT
|
|
3567
3624
|
}
|
|
3568
3625
|
});
|
|
3569
|
-
const pos = res.data.rows
|
|
3626
|
+
const pos = selectBrandPosition(res.data.rows ?? [], q.query.toLowerCase());
|
|
3570
3627
|
if (typeof pos === "number") {
|
|
3571
3628
|
return { foundOnPage1: pos <= PAGE_1_MAX_POSITION, position: Math.max(1, Math.round(pos)) };
|
|
3572
3629
|
}
|
|
@@ -3914,7 +3971,12 @@ async function sendOne(client, base, site, report) {
|
|
|
3914
3971
|
headerImageCid: cidName,
|
|
3915
3972
|
headerWidth: header.displayWidth,
|
|
3916
3973
|
headerHeight: header.displayHeight,
|
|
3917
|
-
headerBgColor: header.placeholderColor
|
|
3974
|
+
headerBgColor: header.placeholderColor,
|
|
3975
|
+
// Announcement-only: re-derive cadence + improvements from the site row so the SENT email
|
|
3976
|
+
// keeps its WHAT TO EXPECT section + improvement callouts. Without this the send-time
|
|
3977
|
+
// re-render drops them entirely (they're not stored on the Reports row). Ignored by the
|
|
3978
|
+
// other report templates.
|
|
3979
|
+
...report.reportType === "Announcement" ? announcementSiteExtras(site) : {}
|
|
3918
3980
|
});
|
|
3919
3981
|
const reportDate = report.completedOn ? new Date(report.completedOn) : /* @__PURE__ */ new Date();
|
|
3920
3982
|
const subject = report.subjectOverride ?? `${site.name} \u2014 ${monthYear(reportDate)} ${report.reportType} Report`;
|