@reddoorla/maintenance 0.46.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 +111 -25
- package/dist/cli/bin.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +77 -11
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -288,6 +288,7 @@ type ResolvedCopy = {
|
|
|
288
288
|
announceTestingLabel: string;
|
|
289
289
|
announceMaintenanceLabel: string;
|
|
290
290
|
announcePreviewLabel: string;
|
|
291
|
+
announceScoreNote: string;
|
|
291
292
|
announceImprovementResend: string;
|
|
292
293
|
announceImprovementSvelte5: string;
|
|
293
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.",
|
|
@@ -2789,6 +2789,7 @@ var DEFAULT_COPY = {
|
|
|
2789
2789
|
announceTestingLabel: "Full site testing",
|
|
2790
2790
|
announceMaintenanceLabel: "Routine maintenance",
|
|
2791
2791
|
announcePreviewLabel: "From your latest full site test:",
|
|
2792
|
+
announceScoreNote: "These are independent Google Lighthouse scores, each out of 100 \u2014 higher is better.",
|
|
2792
2793
|
announceImprovementResend: "Your contact forms now deliver straight to your inbox through reliable infrastructure, so no inquiry slips through the cracks.",
|
|
2793
2794
|
announceImprovementSvelte5: "We've modernized your site to the latest framework \u2014 it's faster, more secure, and built to last.",
|
|
2794
2795
|
announceCadence: "After each one we'll send you a short report like this \u2014 there's nothing you need to do.",
|
|
@@ -3030,7 +3031,7 @@ function buildMjml(data) {
|
|
|
3030
3031
|
<mj-text color="#C00" font-size="44px" font-weight="400" padding-top="0px">${data.lighthouse.performance}</mj-text>
|
|
3031
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>
|
|
3032
3033
|
<mj-divider border-width="1px" border-style="solid" border-color="#CCCCCC" padding="0" />
|
|
3033
|
-
<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>
|
|
3034
3035
|
<mj-text color="#C00" font-size="44px" font-weight="400" padding-top="0px">${data.lighthouse.accessibility}</mj-text>
|
|
3035
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>
|
|
3036
3037
|
<mj-divider border-width="1px" border-style="solid" border-color="#CCCCCC" padding="0" />
|
|
@@ -3135,9 +3136,26 @@ var FREQ_PHRASE = {
|
|
|
3135
3136
|
};
|
|
3136
3137
|
var RED2 = "#C00";
|
|
3137
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
|
+
}
|
|
3138
3156
|
var SCORE_PREVIEW = [
|
|
3139
3157
|
{ label: "Performance", key: "performance" },
|
|
3140
|
-
{ label: "Readability", key: "accessibility" },
|
|
3158
|
+
{ label: "Readability (A11y)", key: "accessibility" },
|
|
3141
3159
|
{ label: "Best Practices", key: "bestPractices" },
|
|
3142
3160
|
{ label: "Site Structure", key: "seo" }
|
|
3143
3161
|
];
|
|
@@ -3175,8 +3193,10 @@ function buildAnnouncementMjml(data) {
|
|
|
3175
3193
|
<mj-text color="${RED2}" font-size="20px" font-weight="700" padding-top="36px">${escapeXml(copy.announceCadenceHeading)}</mj-text>
|
|
3176
3194
|
${cadenceBlocks.map(
|
|
3177
3195
|
(b) => `
|
|
3178
|
-
<mj-text color="${GREY2}" font-family="helvetica, sans-serif" font-size="16px" font-weight="400" line-height="24px" padding-top="12px" padding-bottom="
|
|
3179
|
-
|
|
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("")}`
|
|
3180
3200
|
).join("")}
|
|
3181
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>
|
|
3182
3202
|
</mj-column>
|
|
@@ -3186,6 +3206,23 @@ function buildAnnouncementMjml(data) {
|
|
|
3186
3206
|
<mj-text color="${RED2}" font-size="20px" font-weight="300" padding-top="25px">${label}</mj-text>
|
|
3187
3207
|
<mj-text color="${RED2}" font-size="44px" font-weight="400" padding-top="0px">${data.lighthouse[key]}</mj-text>`
|
|
3188
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>` : "";
|
|
3189
3226
|
const contactRows = copy.contact.map(
|
|
3190
3227
|
(line) => `
|
|
3191
3228
|
<mj-text font-family="helvetica, sans-serif" font-size="24px" font-weight="300" line-height="30px">${escapeXml(line)}</mj-text>`
|
|
@@ -3220,9 +3257,10 @@ function buildAnnouncementMjml(data) {
|
|
|
3220
3257
|
<mj-section background-color="#F4F4F4">
|
|
3221
3258
|
<mj-column>
|
|
3222
3259
|
<mj-text color="${RED2}" font-size="20px" font-weight="700" padding-top="55px">${escapeXml(copy.announcePreviewLabel)}</mj-text>
|
|
3223
|
-
${scoreRows}
|
|
3260
|
+
${scoreRows}${scoreNote}
|
|
3224
3261
|
</mj-column>
|
|
3225
3262
|
</mj-section>
|
|
3263
|
+
${trafficSection}
|
|
3226
3264
|
<mj-section background-color="white">
|
|
3227
3265
|
<mj-column>
|
|
3228
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>
|
|
@@ -3270,7 +3308,10 @@ var TESTING_CHECKLIST = [
|
|
|
3270
3308
|
label: "Interactions & Animations",
|
|
3271
3309
|
field: "Test: Interactions & Animations"
|
|
3272
3310
|
},
|
|
3273
|
-
|
|
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" }
|
|
3274
3315
|
];
|
|
3275
3316
|
var ALL_CHECKLIST_FIELDS = [...MAINTENANCE_CHECKLIST, ...TESTING_CHECKLIST].map(
|
|
3276
3317
|
(i) => i.field
|
|
@@ -3511,6 +3552,7 @@ import { JWT as JWT2 } from "google-auth-library";
|
|
|
3511
3552
|
var WEBMASTERS_READONLY = "https://www.googleapis.com/auth/webmasters.readonly";
|
|
3512
3553
|
var SC_BASE = "https://searchconsole.googleapis.com/webmasters/v3";
|
|
3513
3554
|
var PAGE_1_MAX_POSITION = 10;
|
|
3555
|
+
var BRAND_QUERY_ROW_LIMIT = 100;
|
|
3514
3556
|
function bareHost(s) {
|
|
3515
3557
|
return s.trim().replace(/^sc-domain:/i, "").replace(/^https?:\/\//i, "").split("/")[0].replace(/^www\./i, "").toLowerCase();
|
|
3516
3558
|
}
|
|
@@ -3524,6 +3566,25 @@ function resolvePropertyCandidates(entries, host) {
|
|
|
3524
3566
|
function ymd3(d) {
|
|
3525
3567
|
return d.toISOString().slice(0, 10);
|
|
3526
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
|
+
}
|
|
3527
3588
|
async function fetchSearchPresence(q, periodStart, periodEnd) {
|
|
3528
3589
|
const key = JSON.parse(readFileSync4(q.keyPath, "utf8"));
|
|
3529
3590
|
const jwt = new JWT2({
|
|
@@ -3555,14 +3616,14 @@ async function fetchSearchPresence(q, periodStart, periodEnd) {
|
|
|
3555
3616
|
dimensionFilterGroups: [
|
|
3556
3617
|
{
|
|
3557
3618
|
filters: [
|
|
3558
|
-
{ dimension: "query", operator: "
|
|
3619
|
+
{ dimension: "query", operator: "contains", expression: q.query.toLowerCase() }
|
|
3559
3620
|
]
|
|
3560
3621
|
}
|
|
3561
3622
|
],
|
|
3562
|
-
rowLimit:
|
|
3623
|
+
rowLimit: BRAND_QUERY_ROW_LIMIT
|
|
3563
3624
|
}
|
|
3564
3625
|
});
|
|
3565
|
-
const pos = res.data.rows
|
|
3626
|
+
const pos = selectBrandPosition(res.data.rows ?? [], q.query.toLowerCase());
|
|
3566
3627
|
if (typeof pos === "number") {
|
|
3567
3628
|
return { foundOnPage1: pos <= PAGE_1_MAX_POSITION, position: Math.max(1, Math.round(pos)) };
|
|
3568
3629
|
}
|
|
@@ -3910,7 +3971,12 @@ async function sendOne(client, base, site, report) {
|
|
|
3910
3971
|
headerImageCid: cidName,
|
|
3911
3972
|
headerWidth: header.displayWidth,
|
|
3912
3973
|
headerHeight: header.displayHeight,
|
|
3913
|
-
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) : {}
|
|
3914
3980
|
});
|
|
3915
3981
|
const reportDate = report.completedOn ? new Date(report.completedOn) : /* @__PURE__ */ new Date();
|
|
3916
3982
|
const subject = report.subjectOverride ?? `${site.name} \u2014 ${monthYear(reportDate)} ${report.reportType} Report`;
|