@orion-studios/payload-seo-audit 1.0.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 +127 -0
- package/bin/init.js +267 -0
- package/dist/api/backlinks-import.d.ts +4 -0
- package/dist/api/backlinks-import.d.ts.map +1 -0
- package/dist/api/backlinks-import.js +182 -0
- package/dist/api/cron.d.ts +4 -0
- package/dist/api/cron.d.ts.map +1 -0
- package/dist/api/cron.js +89 -0
- package/dist/api/index.d.ts +10 -0
- package/dist/api/index.d.ts.map +1 -0
- package/dist/api/index.js +21 -0
- package/dist/api/page-result.d.ts +4 -0
- package/dist/api/page-result.d.ts.map +1 -0
- package/dist/api/page-result.js +93 -0
- package/dist/api/page-results.d.ts +4 -0
- package/dist/api/page-results.d.ts.map +1 -0
- package/dist/api/page-results.js +83 -0
- package/dist/api/run-stream.d.ts +4 -0
- package/dist/api/run-stream.d.ts.map +1 -0
- package/dist/api/run-stream.js +273 -0
- package/dist/api/run.d.ts +4 -0
- package/dist/api/run.d.ts.map +1 -0
- package/dist/api/run.js +102 -0
- package/dist/api/snapshot-report.d.ts +4 -0
- package/dist/api/snapshot-report.d.ts.map +1 -0
- package/dist/api/snapshot-report.js +130 -0
- package/dist/api/snapshots.d.ts +4 -0
- package/dist/api/snapshots.d.ts.map +1 -0
- package/dist/api/snapshots.js +138 -0
- package/dist/api/trend.d.ts +4 -0
- package/dist/api/trend.d.ts.map +1 -0
- package/dist/api/trend.js +71 -0
- package/dist/collections/SeoAuthoritySnapshots.d.ts +3 -0
- package/dist/collections/SeoAuthoritySnapshots.d.ts.map +1 -0
- package/dist/collections/SeoAuthoritySnapshots.js +83 -0
- package/dist/collections/SeoKeywordVisibility.d.ts +3 -0
- package/dist/collections/SeoKeywordVisibility.d.ts.map +1 -0
- package/dist/collections/SeoKeywordVisibility.js +65 -0
- package/dist/collections/SeoPageResults.d.ts +3 -0
- package/dist/collections/SeoPageResults.d.ts.map +1 -0
- package/dist/collections/SeoPageResults.js +170 -0
- package/dist/collections/SeoSnapshots.d.ts +3 -0
- package/dist/collections/SeoSnapshots.d.ts.map +1 -0
- package/dist/collections/SeoSnapshots.js +131 -0
- package/dist/components/hooks/useSeoApi.d.ts +7 -0
- package/dist/components/hooks/useSeoApi.d.ts.map +1 -0
- package/dist/components/hooks/useSeoApi.js +31 -0
- package/dist/components/hooks/useSeoPageResults.d.ts +19 -0
- package/dist/components/hooks/useSeoPageResults.d.ts.map +1 -0
- package/dist/components/hooks/useSeoPageResults.js +62 -0
- package/dist/components/hooks/useSeoSnapshot.d.ts +8 -0
- package/dist/components/hooks/useSeoSnapshot.d.ts.map +1 -0
- package/dist/components/hooks/useSeoSnapshot.js +39 -0
- package/dist/components/hooks/useSeoTrend.d.ts +8 -0
- package/dist/components/hooks/useSeoTrend.d.ts.map +1 -0
- package/dist/components/hooks/useSeoTrend.js +38 -0
- package/dist/components/layout/SeoReportHeader.d.ts +10 -0
- package/dist/components/layout/SeoReportHeader.d.ts.map +1 -0
- package/dist/components/layout/SeoReportHeader.js +18 -0
- package/dist/components/layout/SeoReportShell.d.ts +9 -0
- package/dist/components/layout/SeoReportShell.d.ts.map +1 -0
- package/dist/components/layout/SeoReportShell.js +17 -0
- package/dist/components/pdf/PdfDownloadButton.d.ts +9 -0
- package/dist/components/pdf/PdfDownloadButton.d.ts.map +1 -0
- package/dist/components/pdf/PdfDownloadButton.js +80 -0
- package/dist/components/tables/IssueTable.d.ts +11 -0
- package/dist/components/tables/IssueTable.d.ts.map +1 -0
- package/dist/components/tables/IssueTable.js +121 -0
- package/dist/components/tables/PageResultsTable.d.ts +18 -0
- package/dist/components/tables/PageResultsTable.d.ts.map +1 -0
- package/dist/components/tables/PageResultsTable.js +96 -0
- package/dist/components/types.d.ts +107 -0
- package/dist/components/types.d.ts.map +1 -0
- package/dist/components/types.js +22 -0
- package/dist/components/utils/formatters.d.ts +15 -0
- package/dist/components/utils/formatters.d.ts.map +1 -0
- package/dist/components/utils/formatters.js +98 -0
- package/dist/components/utils/scoreHelpers.d.ts +17 -0
- package/dist/components/utils/scoreHelpers.d.ts.map +1 -0
- package/dist/components/utils/scoreHelpers.js +139 -0
- package/dist/components/views/SeoDashboard.d.ts +3 -0
- package/dist/components/views/SeoDashboard.d.ts.map +1 -0
- package/dist/components/views/SeoDashboard.js +239 -0
- package/dist/components/views/SeoPageReport.d.ts +3 -0
- package/dist/components/views/SeoPageReport.d.ts.map +1 -0
- package/dist/components/views/SeoPageReport.js +234 -0
- package/dist/components/views/SeoSnapshotReport.d.ts +3 -0
- package/dist/components/views/SeoSnapshotReport.d.ts.map +1 -0
- package/dist/components/views/SeoSnapshotReport.js +224 -0
- package/dist/components/visualization/CategoryScoreCard.d.ts +11 -0
- package/dist/components/visualization/CategoryScoreCard.d.ts.map +1 -0
- package/dist/components/visualization/CategoryScoreCard.js +17 -0
- package/dist/components/visualization/CategoryScoreGrid.d.ts +9 -0
- package/dist/components/visualization/CategoryScoreGrid.d.ts.map +1 -0
- package/dist/components/visualization/CategoryScoreGrid.js +32 -0
- package/dist/components/visualization/IssueCategoryChart.d.ts +8 -0
- package/dist/components/visualization/IssueCategoryChart.d.ts.map +1 -0
- package/dist/components/visualization/IssueCategoryChart.js +47 -0
- package/dist/components/visualization/MetricCard.d.ts +11 -0
- package/dist/components/visualization/MetricCard.d.ts.map +1 -0
- package/dist/components/visualization/MetricCard.js +17 -0
- package/dist/components/visualization/MetricCardRow.d.ts +7 -0
- package/dist/components/visualization/MetricCardRow.d.ts.map +1 -0
- package/dist/components/visualization/MetricCardRow.js +12 -0
- package/dist/components/visualization/ScoreBar.d.ts +11 -0
- package/dist/components/visualization/ScoreBar.d.ts.map +1 -0
- package/dist/components/visualization/ScoreBar.js +34 -0
- package/dist/components/visualization/ScoreGauge.d.ts +11 -0
- package/dist/components/visualization/ScoreGauge.d.ts.map +1 -0
- package/dist/components/visualization/ScoreGauge.js +28 -0
- package/dist/components/visualization/ScoreTrendChart.d.ts +9 -0
- package/dist/components/visualization/ScoreTrendChart.d.ts.map +1 -0
- package/dist/components/visualization/ScoreTrendChart.js +43 -0
- package/dist/components/visualization/SeverityBadge.d.ts +8 -0
- package/dist/components/visualization/SeverityBadge.d.ts.map +1 -0
- package/dist/components/visualization/SeverityBadge.js +14 -0
- package/dist/config.d.ts +38 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +36 -0
- package/dist/exports/components.d.ts +4 -0
- package/dist/exports/components.d.ts.map +1 -0
- package/dist/exports/components.js +9 -0
- package/dist/globals/SeoDashboard.d.ts +3 -0
- package/dist/globals/SeoDashboard.d.ts.map +1 -0
- package/dist/globals/SeoDashboard.js +25 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +39 -0
- package/dist/utilities/access.d.ts +8 -0
- package/dist/utilities/access.d.ts.map +1 -0
- package/dist/utilities/access.js +11 -0
- package/dist/utilities/auth.d.ts +7 -0
- package/dist/utilities/auth.d.ts.map +1 -0
- package/dist/utilities/auth.js +28 -0
- package/dist/utilities/checks.d.ts +3 -0
- package/dist/utilities/checks.d.ts.map +1 -0
- package/dist/utilities/checks.js +255 -0
- package/dist/utilities/crawler.d.ts +14 -0
- package/dist/utilities/crawler.d.ts.map +1 -0
- package/dist/utilities/crawler.js +152 -0
- package/dist/utilities/gsc.d.ts +15 -0
- package/dist/utilities/gsc.d.ts.map +1 -0
- package/dist/utilities/gsc.js +69 -0
- package/dist/utilities/helpers.d.ts +7 -0
- package/dist/utilities/helpers.d.ts.map +1 -0
- package/dist/utilities/helpers.js +44 -0
- package/dist/utilities/pagespeed.d.ts +3 -0
- package/dist/utilities/pagespeed.d.ts.map +1 -0
- package/dist/utilities/pagespeed.js +49 -0
- package/dist/utilities/providers.d.ts +3 -0
- package/dist/utilities/providers.d.ts.map +1 -0
- package/dist/utilities/providers.js +18 -0
- package/dist/utilities/runAudit.d.ts +14 -0
- package/dist/utilities/runAudit.d.ts.map +1 -0
- package/dist/utilities/runAudit.js +224 -0
- package/dist/utilities/scoring.d.ts +3 -0
- package/dist/utilities/scoring.d.ts.map +1 -0
- package/dist/utilities/scoring.js +45 -0
- package/dist/utilities/triggers.d.ts +3 -0
- package/dist/utilities/triggers.d.ts.map +1 -0
- package/dist/utilities/triggers.js +39 -0
- package/dist/utilities/types.d.ts +87 -0
- package/dist/utilities/types.d.ts.map +1 -0
- package/dist/utilities/types.js +2 -0
- package/package.json +63 -0
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.crawlSiteForSEO = void 0;
|
|
7
|
+
const helpers_1 = require("../utilities/helpers");
|
|
8
|
+
const https_1 = __importDefault(require("https"));
|
|
9
|
+
const extractSitemapURLs = (xml) => {
|
|
10
|
+
const matches = [...xml.matchAll(/<loc>(.*?)<\/loc>/gi)];
|
|
11
|
+
return matches.map((match) => match[1]?.trim()).filter(Boolean);
|
|
12
|
+
};
|
|
13
|
+
const extractInternalLinks = (html, baseOrigin) => {
|
|
14
|
+
const links = [...html.matchAll(/<a\s+[^>]*href=["']([^"']+)["'][^>]*>/gi)]
|
|
15
|
+
.map((match) => match[1]?.trim())
|
|
16
|
+
.filter(Boolean);
|
|
17
|
+
return links
|
|
18
|
+
.map((link) => (0, helpers_1.safeUrl)(link, baseOrigin))
|
|
19
|
+
.filter((link) => Boolean(link))
|
|
20
|
+
.map((link) => (0, helpers_1.normalizeURL)(link))
|
|
21
|
+
.filter((link) => {
|
|
22
|
+
try {
|
|
23
|
+
return new URL(link).origin === baseOrigin;
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
};
|
|
30
|
+
const matchesPatterns = (url, patterns) => {
|
|
31
|
+
if (!patterns?.length)
|
|
32
|
+
return true;
|
|
33
|
+
return patterns.some((entry) => {
|
|
34
|
+
const pattern = entry?.pattern?.trim();
|
|
35
|
+
if (!pattern)
|
|
36
|
+
return false;
|
|
37
|
+
return url.includes(pattern);
|
|
38
|
+
});
|
|
39
|
+
};
|
|
40
|
+
const isExcluded = (url, patterns) => {
|
|
41
|
+
if (!patterns?.length)
|
|
42
|
+
return false;
|
|
43
|
+
return patterns.some((entry) => {
|
|
44
|
+
const pattern = entry?.pattern?.trim();
|
|
45
|
+
if (!pattern)
|
|
46
|
+
return false;
|
|
47
|
+
return url.includes(pattern);
|
|
48
|
+
});
|
|
49
|
+
};
|
|
50
|
+
const httpsAgent = process.env.NODE_ENV === 'development'
|
|
51
|
+
? new https_1.default.Agent({
|
|
52
|
+
rejectUnauthorized: false,
|
|
53
|
+
})
|
|
54
|
+
: undefined;
|
|
55
|
+
const fetchWithTimeout = async (url, timeoutMs) => {
|
|
56
|
+
const controller = new AbortController();
|
|
57
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
58
|
+
try {
|
|
59
|
+
return await fetch(url, {
|
|
60
|
+
signal: controller.signal,
|
|
61
|
+
headers: {
|
|
62
|
+
'user-agent': 'OrionStudiosSEOAudit/1.0',
|
|
63
|
+
},
|
|
64
|
+
cache: 'no-store',
|
|
65
|
+
...(httpsAgent && { agent: httpsAgent }),
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
finally {
|
|
69
|
+
clearTimeout(timer);
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
const crawlSiteForSEO = async (site) => {
|
|
73
|
+
const canonicalHost = site.canonicalHost?.trim();
|
|
74
|
+
const sitemapURL = site.sitemapURL?.trim();
|
|
75
|
+
if (!canonicalHost || !sitemapURL) {
|
|
76
|
+
return { pages: [], visitedCount: 0 };
|
|
77
|
+
}
|
|
78
|
+
const origin = new URL(canonicalHost).origin;
|
|
79
|
+
const maxPages = Math.max(10, Number(site.crawlSettings?.maxPages || 120));
|
|
80
|
+
const maxDepth = Math.max(0, Number(site.crawlSettings?.maxDepth || 2));
|
|
81
|
+
const timeoutMs = Math.max(1000, Number(site.crawlSettings?.requestTimeoutMs || 10000));
|
|
82
|
+
const includePatterns = site.crawlSettings?.includePatterns;
|
|
83
|
+
const excludePatterns = site.crawlSettings?.excludePatterns;
|
|
84
|
+
const queued = new Set();
|
|
85
|
+
const visited = new Set();
|
|
86
|
+
const queue = [];
|
|
87
|
+
const pages = [];
|
|
88
|
+
const enqueue = (value, depth) => {
|
|
89
|
+
const normalized = (0, helpers_1.normalizeURL)(value);
|
|
90
|
+
if (queued.has(normalized) || visited.has(normalized))
|
|
91
|
+
return;
|
|
92
|
+
if (isExcluded(normalized, excludePatterns))
|
|
93
|
+
return;
|
|
94
|
+
if (!matchesPatterns(normalized, includePatterns))
|
|
95
|
+
return;
|
|
96
|
+
queued.add(normalized);
|
|
97
|
+
queue.push({ url: normalized, depth });
|
|
98
|
+
};
|
|
99
|
+
enqueue(canonicalHost, 0);
|
|
100
|
+
(site.keyURLs || []).forEach((entry) => {
|
|
101
|
+
if (entry?.url) {
|
|
102
|
+
const absolute = (0, helpers_1.safeUrl)(entry.url, canonicalHost);
|
|
103
|
+
if (absolute)
|
|
104
|
+
enqueue(absolute, 0);
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
try {
|
|
108
|
+
const sitemapResponse = await fetchWithTimeout(sitemapURL, timeoutMs);
|
|
109
|
+
if (sitemapResponse.ok) {
|
|
110
|
+
const sitemapXML = await sitemapResponse.text();
|
|
111
|
+
extractSitemapURLs(sitemapXML).forEach((url) => {
|
|
112
|
+
const absolute = (0, helpers_1.safeUrl)(url, canonicalHost);
|
|
113
|
+
if (absolute)
|
|
114
|
+
enqueue(absolute, 0);
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
console.warn('SEO audit sitemap fetch failed:', error);
|
|
120
|
+
}
|
|
121
|
+
while (queue.length > 0 && pages.length < maxPages) {
|
|
122
|
+
const current = queue.shift();
|
|
123
|
+
if (!current)
|
|
124
|
+
continue;
|
|
125
|
+
if (visited.has(current.url))
|
|
126
|
+
continue;
|
|
127
|
+
visited.add(current.url);
|
|
128
|
+
try {
|
|
129
|
+
const response = await fetchWithTimeout(current.url, timeoutMs);
|
|
130
|
+
const contentType = response.headers.get('content-type') || '';
|
|
131
|
+
if (!contentType.toLowerCase().includes('text/html'))
|
|
132
|
+
continue;
|
|
133
|
+
const html = await response.text();
|
|
134
|
+
const parsed = new URL(current.url);
|
|
135
|
+
pages.push({
|
|
136
|
+
url: current.url,
|
|
137
|
+
path: parsed.pathname || '/',
|
|
138
|
+
statusCode: response.status,
|
|
139
|
+
html,
|
|
140
|
+
});
|
|
141
|
+
if (current.depth < maxDepth) {
|
|
142
|
+
const links = extractInternalLinks(html, origin);
|
|
143
|
+
links.forEach((link) => enqueue(link, current.depth + 1));
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
console.warn(`SEO audit fetch failed for ${current.url}:`, error);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return { pages, visitedCount: visited.size };
|
|
151
|
+
};
|
|
152
|
+
exports.crawlSiteForSEO = crawlSiteForSEO;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Payload } from 'payload';
|
|
2
|
+
import type { SEOSiteRecord } from '../utilities/types';
|
|
3
|
+
export declare const syncGSCKeywordVisibility: ({ payload, site, }: {
|
|
4
|
+
payload: Payload;
|
|
5
|
+
site: SEOSiteRecord;
|
|
6
|
+
}) => Promise<{
|
|
7
|
+
imported: number;
|
|
8
|
+
enabled: boolean;
|
|
9
|
+
skipped?: undefined;
|
|
10
|
+
} | {
|
|
11
|
+
imported: number;
|
|
12
|
+
enabled: boolean;
|
|
13
|
+
skipped: string;
|
|
14
|
+
}>;
|
|
15
|
+
//# sourceMappingURL=gsc.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gsc.d.ts","sourceRoot":"","sources":["../../src/utilities/gsc.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AACtC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AASvD,eAAO,MAAM,wBAAwB,GAAU,oBAG5C;IACD,OAAO,EAAE,OAAO,CAAA;IAChB,IAAI,EAAE,aAAa,CAAA;CACpB;;;;;;;;EA+DA,CAAA"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.syncGSCKeywordVisibility = void 0;
|
|
4
|
+
const googleapis_1 = require("googleapis");
|
|
5
|
+
const requiredEnv = () => ({
|
|
6
|
+
clientID: process.env.SEO_GSC_CLIENT_ID,
|
|
7
|
+
clientSecret: process.env.SEO_GSC_CLIENT_SECRET,
|
|
8
|
+
redirectURI: process.env.SEO_GSC_REDIRECT_URI,
|
|
9
|
+
refreshToken: process.env.SEO_GSC_REFRESH_TOKEN,
|
|
10
|
+
});
|
|
11
|
+
const syncGSCKeywordVisibility = async ({ payload, site, }) => {
|
|
12
|
+
if (site.integrations?.enableSearchConsole !== true) {
|
|
13
|
+
return { imported: 0, enabled: false };
|
|
14
|
+
}
|
|
15
|
+
const env = requiredEnv();
|
|
16
|
+
if (!env.clientID || !env.clientSecret || !env.redirectURI || !env.refreshToken || !site.canonicalHost) {
|
|
17
|
+
return { imported: 0, enabled: true, skipped: 'missing-config' };
|
|
18
|
+
}
|
|
19
|
+
try {
|
|
20
|
+
const oauth2Client = new googleapis_1.google.auth.OAuth2(env.clientID, env.clientSecret, env.redirectURI);
|
|
21
|
+
oauth2Client.setCredentials({
|
|
22
|
+
refresh_token: env.refreshToken,
|
|
23
|
+
});
|
|
24
|
+
const searchConsole = googleapis_1.google.searchconsole({ version: 'v1', auth: oauth2Client });
|
|
25
|
+
const endDate = new Date();
|
|
26
|
+
const startDate = new Date();
|
|
27
|
+
startDate.setDate(endDate.getDate() - 7);
|
|
28
|
+
const toISODate = (value) => value.toISOString().slice(0, 10);
|
|
29
|
+
const response = await searchConsole.searchanalytics.query({
|
|
30
|
+
siteUrl: site.canonicalHost,
|
|
31
|
+
requestBody: {
|
|
32
|
+
startDate: toISODate(startDate),
|
|
33
|
+
endDate: toISODate(endDate),
|
|
34
|
+
dimensions: ['query', 'page'],
|
|
35
|
+
rowLimit: 250,
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
const rows = response.data.rows || [];
|
|
39
|
+
let imported = 0;
|
|
40
|
+
for (const row of rows) {
|
|
41
|
+
const keys = row.keys || [];
|
|
42
|
+
const query = keys[0];
|
|
43
|
+
const page = keys[1];
|
|
44
|
+
if (!query || !page)
|
|
45
|
+
continue;
|
|
46
|
+
await payload.create({
|
|
47
|
+
collection: 'seo-keyword-visibility',
|
|
48
|
+
data: {
|
|
49
|
+
capturedAt: endDate.toISOString(),
|
|
50
|
+
query,
|
|
51
|
+
page,
|
|
52
|
+
impressions: row.impressions ?? undefined,
|
|
53
|
+
clicks: row.clicks ?? undefined,
|
|
54
|
+
ctr: row.ctr ?? undefined,
|
|
55
|
+
position: row.position ?? undefined,
|
|
56
|
+
source: 'gsc',
|
|
57
|
+
},
|
|
58
|
+
overrideAccess: true,
|
|
59
|
+
});
|
|
60
|
+
imported += 1;
|
|
61
|
+
}
|
|
62
|
+
return { imported, enabled: true };
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
console.warn('GSC sync failed:', error);
|
|
66
|
+
return { imported: 0, enabled: true, skipped: 'request-failed' };
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
exports.syncGSCKeywordVisibility = syncGSCKeywordVisibility;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { SEOIssue, SEOIssueSeverity } from '../utilities/types';
|
|
2
|
+
export declare const severityWeights: Record<SEOIssueSeverity, number>;
|
|
3
|
+
export declare const safeUrl: (value: string, fallbackOrigin: string) => string | null;
|
|
4
|
+
export declare const normalizeURL: (value: string) => string;
|
|
5
|
+
export declare const createIssueFingerprint: (url: string, ruleID: string, message: string) => string;
|
|
6
|
+
export declare const countIssuesBySeverity: (issues: SEOIssue[]) => Record<SEOIssueSeverity, number>;
|
|
7
|
+
//# sourceMappingURL=helpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../src/utilities/helpers.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AAEpE,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,gBAAgB,EAAE,MAAM,CAM5D,CAAA;AAED,eAAO,MAAM,OAAO,GAAI,OAAO,MAAM,EAAE,gBAAgB,MAAM,kBAM5D,CAAA;AAED,eAAO,MAAM,YAAY,GAAI,OAAO,MAAM,WAWzC,CAAA;AAED,eAAO,MAAM,sBAAsB,GAAI,KAAK,MAAM,EAAE,QAAQ,MAAM,EAAE,SAAS,MAAM,WACU,CAAA;AAE7F,eAAO,MAAM,qBAAqB,GAAI,QAAQ,QAAQ,EAAE,qCAOrD,CAAA"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.countIssuesBySeverity = exports.createIssueFingerprint = exports.normalizeURL = exports.safeUrl = exports.severityWeights = void 0;
|
|
7
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
8
|
+
exports.severityWeights = {
|
|
9
|
+
critical: 22,
|
|
10
|
+
high: 14,
|
|
11
|
+
medium: 8,
|
|
12
|
+
low: 4,
|
|
13
|
+
info: 1,
|
|
14
|
+
};
|
|
15
|
+
const safeUrl = (value, fallbackOrigin) => {
|
|
16
|
+
try {
|
|
17
|
+
return new URL(value, fallbackOrigin).toString();
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
exports.safeUrl = safeUrl;
|
|
24
|
+
const normalizeURL = (value) => {
|
|
25
|
+
try {
|
|
26
|
+
const parsed = new URL(value);
|
|
27
|
+
parsed.hash = '';
|
|
28
|
+
if ((parsed.protocol === 'http:' && parsed.port === '80') || (parsed.protocol === 'https:' && parsed.port === '443')) {
|
|
29
|
+
parsed.port = '';
|
|
30
|
+
}
|
|
31
|
+
return parsed.toString().replace(/\/$/, '') || parsed.origin;
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return value;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
exports.normalizeURL = normalizeURL;
|
|
38
|
+
const createIssueFingerprint = (url, ruleID, message) => crypto_1.default.createHash('sha1').update(`${(0, exports.normalizeURL)(url)}|${ruleID}|${message}`).digest('hex');
|
|
39
|
+
exports.createIssueFingerprint = createIssueFingerprint;
|
|
40
|
+
const countIssuesBySeverity = (issues) => issues.reduce((acc, issue) => {
|
|
41
|
+
acc[issue.severity] += 1;
|
|
42
|
+
return acc;
|
|
43
|
+
}, { critical: 0, high: 0, medium: 0, low: 0, info: 0 });
|
|
44
|
+
exports.countIssuesBySeverity = countIssuesBySeverity;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pagespeed.d.ts","sourceRoot":"","sources":["../../src/utilities/pagespeed.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAA;AA2B7D,eAAO,MAAM,mBAAmB,GAAU,MAAM,MAAM,EAAE,KAAG,OAAO,CAAC,mBAAmB,GAAG,IAAI,CA4B5F,CAAA"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getPageSpeedSummary = void 0;
|
|
4
|
+
const fetchPageSpeedForURL = async (url, apiKey) => {
|
|
5
|
+
const endpoint = new URL('https://www.googleapis.com/pagespeedonline/v5/runPagespeed');
|
|
6
|
+
endpoint.searchParams.set('url', url);
|
|
7
|
+
endpoint.searchParams.set('strategy', 'mobile');
|
|
8
|
+
endpoint.searchParams.set('key', apiKey);
|
|
9
|
+
const response = await fetch(endpoint.toString(), { cache: 'no-store' });
|
|
10
|
+
if (!response.ok) {
|
|
11
|
+
throw new Error(`PageSpeed request failed with status ${response.status}`);
|
|
12
|
+
}
|
|
13
|
+
const json = (await response.json());
|
|
14
|
+
const lighthouseScore = Number(json?.lighthouseResult?.categories?.performance?.score ?? 0);
|
|
15
|
+
const metrics = json?.loadingExperience?.metrics || {};
|
|
16
|
+
const lcp = Number(metrics?.LARGEST_CONTENTFUL_PAINT_MS?.percentile ?? 0);
|
|
17
|
+
const clsRaw = Number(metrics?.CUMULATIVE_LAYOUT_SHIFT_SCORE?.percentile ?? 0);
|
|
18
|
+
const cls = clsRaw > 0 ? clsRaw / 100 : 0;
|
|
19
|
+
return {
|
|
20
|
+
performanceScore: lighthouseScore > 0 ? Math.round(lighthouseScore * 100) : undefined,
|
|
21
|
+
lcpMs: lcp > 0 ? lcp : undefined,
|
|
22
|
+
cls: cls > 0 ? Number(cls.toFixed(3)) : undefined,
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
const getPageSpeedSummary = async (urls) => {
|
|
26
|
+
const apiKey = process.env.SEO_PAGESPEED_API_KEY;
|
|
27
|
+
if (!apiKey || urls.length === 0)
|
|
28
|
+
return null;
|
|
29
|
+
const sampled = urls.slice(0, 5);
|
|
30
|
+
const results = await Promise.allSettled(sampled.map((url) => fetchPageSpeedForURL(url, apiKey)));
|
|
31
|
+
const successful = results
|
|
32
|
+
.filter((result) => result.status === 'fulfilled')
|
|
33
|
+
.map((result) => result.value);
|
|
34
|
+
if (!successful.length)
|
|
35
|
+
return null;
|
|
36
|
+
const performance = successful.map((entry) => entry.performanceScore).filter((value) => typeof value === 'number');
|
|
37
|
+
const lcps = successful.map((entry) => entry.lcpMs).filter((value) => typeof value === 'number');
|
|
38
|
+
const clsValues = successful.map((entry) => entry.cls).filter((value) => typeof value === 'number');
|
|
39
|
+
const average = (values) => values.length ? Math.round(values.reduce((sum, value) => sum + value, 0) / values.length) : undefined;
|
|
40
|
+
const averageCLS = clsValues.length
|
|
41
|
+
? Number((clsValues.reduce((sum, value) => sum + value, 0) / clsValues.length).toFixed(3))
|
|
42
|
+
: undefined;
|
|
43
|
+
return {
|
|
44
|
+
averagePerformanceScore: average(performance),
|
|
45
|
+
averageLCPMs: average(lcps),
|
|
46
|
+
averageCLS,
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
exports.getPageSpeedSummary = getPageSpeedSummary;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"providers.d.ts","sourceRoot":"","sources":["../../src/utilities/providers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAA;AAY5D,eAAO,MAAM,qBAAqB,QAAO,kBAMxC,CAAA"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getSEOProviderAdapter = void 0;
|
|
4
|
+
const manualProvider = {
|
|
5
|
+
name: 'manual',
|
|
6
|
+
isConfigured: () => true,
|
|
7
|
+
};
|
|
8
|
+
const externalProvider = {
|
|
9
|
+
name: process.env.SEO_PROVIDER || 'none',
|
|
10
|
+
isConfigured: () => Boolean(process.env.SEO_PROVIDER && process.env.SEO_PROVIDER_API_KEY),
|
|
11
|
+
};
|
|
12
|
+
const getSEOProviderAdapter = () => {
|
|
13
|
+
if (externalProvider.isConfigured()) {
|
|
14
|
+
return externalProvider;
|
|
15
|
+
}
|
|
16
|
+
return manualProvider;
|
|
17
|
+
};
|
|
18
|
+
exports.getSEOProviderAdapter = getSEOProviderAdapter;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Payload } from 'payload';
|
|
2
|
+
import type { SEOAuditRunType, SEOSiteRecord } from '../utilities/types';
|
|
3
|
+
export declare const enforceSEORetention: (payload: Payload, keepSnapshots?: number) => Promise<void>;
|
|
4
|
+
export declare const runSEOSnapshot: ({ payload, site, runType, }: {
|
|
5
|
+
payload: Payload;
|
|
6
|
+
site: SEOSiteRecord;
|
|
7
|
+
runType: SEOAuditRunType;
|
|
8
|
+
}) => Promise<{
|
|
9
|
+
snapshotID: number | string;
|
|
10
|
+
pagesChecked: number;
|
|
11
|
+
overallScore: number;
|
|
12
|
+
issueCounts: Record<import("../utilities/types").SEOIssueSeverity, number>;
|
|
13
|
+
}>;
|
|
14
|
+
//# sourceMappingURL=runAudit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runAudit.d.ts","sourceRoot":"","sources":["../../src/utilities/runAudit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AAQtC,OAAO,KAAK,EAAE,eAAe,EAA2B,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAqDjG,eAAO,MAAM,mBAAmB,GAAU,SAAS,OAAO,EAAE,sBAAkB,kBAqC7E,CAAA;AAED,eAAO,MAAM,cAAc,GAAU,6BAIlC;IACD,OAAO,EAAE,OAAO,CAAA;IAChB,IAAI,EAAE,aAAa,CAAA;IACnB,OAAO,EAAE,eAAe,CAAA;CACzB;gBAoIyC,MAAM,GAAG,MAAM;;;;EAmBxD,CAAA"}
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runSEOSnapshot = exports.enforceSEORetention = void 0;
|
|
4
|
+
const checks_1 = require("../utilities/checks");
|
|
5
|
+
const crawler_1 = require("../utilities/crawler");
|
|
6
|
+
const helpers_1 = require("../utilities/helpers");
|
|
7
|
+
const gsc_1 = require("../utilities/gsc");
|
|
8
|
+
const pagespeed_1 = require("../utilities/pagespeed");
|
|
9
|
+
const scoring_1 = require("../utilities/scoring");
|
|
10
|
+
const providers_1 = require("../utilities/providers");
|
|
11
|
+
const average = (values) => {
|
|
12
|
+
if (!values.length)
|
|
13
|
+
return 0;
|
|
14
|
+
return Math.round(values.reduce((sum, value) => sum + value, 0) / values.length);
|
|
15
|
+
};
|
|
16
|
+
const applyPerformanceIssues = (issues, pageSpeed, site) => {
|
|
17
|
+
if (!pageSpeed)
|
|
18
|
+
return issues;
|
|
19
|
+
const nextIssues = [...issues];
|
|
20
|
+
if (typeof pageSpeed.averagePerformanceScore === 'number' && pageSpeed.averagePerformanceScore < 70) {
|
|
21
|
+
nextIssues.push({
|
|
22
|
+
fingerprint: `perf-score-${pageSpeed.averagePerformanceScore}`,
|
|
23
|
+
ruleID: 'pagespeed-performance-low',
|
|
24
|
+
category: 'performance',
|
|
25
|
+
severity: pageSpeed.averagePerformanceScore < 50 ? 'high' : 'medium',
|
|
26
|
+
message: `Average Lighthouse performance score is ${pageSpeed.averagePerformanceScore}.`,
|
|
27
|
+
recommendation: 'Optimize render-blocking assets, image delivery, and JS execution.',
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
const targetLCP = Number(site.thresholds?.targetLCPMs || 2500);
|
|
31
|
+
if (typeof pageSpeed.averageLCPMs === 'number' && pageSpeed.averageLCPMs > targetLCP) {
|
|
32
|
+
nextIssues.push({
|
|
33
|
+
fingerprint: `perf-lcp-${pageSpeed.averageLCPMs}`,
|
|
34
|
+
ruleID: 'pagespeed-lcp-high',
|
|
35
|
+
category: 'performance',
|
|
36
|
+
severity: pageSpeed.averageLCPMs > targetLCP * 1.5 ? 'high' : 'medium',
|
|
37
|
+
message: `Average LCP is ${pageSpeed.averageLCPMs}ms.`,
|
|
38
|
+
recommendation: 'Improve server response times, preload key assets, and optimize hero content.',
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
const targetCLS = Number(site.thresholds?.targetCLS || 0.1);
|
|
42
|
+
if (typeof pageSpeed.averageCLS === 'number' && pageSpeed.averageCLS > targetCLS) {
|
|
43
|
+
nextIssues.push({
|
|
44
|
+
fingerprint: `perf-cls-${pageSpeed.averageCLS}`,
|
|
45
|
+
ruleID: 'pagespeed-cls-high',
|
|
46
|
+
category: 'performance',
|
|
47
|
+
severity: pageSpeed.averageCLS > targetCLS * 2 ? 'high' : 'medium',
|
|
48
|
+
message: `Average CLS is ${pageSpeed.averageCLS}.`,
|
|
49
|
+
recommendation: 'Set explicit media dimensions and reduce layout shifts from dynamic UI elements.',
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
return nextIssues;
|
|
53
|
+
};
|
|
54
|
+
const enforceSEORetention = async (payload, keepSnapshots = 16) => {
|
|
55
|
+
const snapshots = await payload.find({
|
|
56
|
+
collection: 'seo-snapshots',
|
|
57
|
+
where: {},
|
|
58
|
+
limit: 200,
|
|
59
|
+
sort: '-startedAt',
|
|
60
|
+
depth: 0,
|
|
61
|
+
overrideAccess: true,
|
|
62
|
+
});
|
|
63
|
+
const excess = snapshots.docs.slice(keepSnapshots);
|
|
64
|
+
if (!excess.length)
|
|
65
|
+
return;
|
|
66
|
+
for (const snapshot of excess) {
|
|
67
|
+
const snapshotID = snapshot.id;
|
|
68
|
+
const pageResults = await payload.find({
|
|
69
|
+
collection: 'seo-page-results',
|
|
70
|
+
where: { snapshot: { equals: snapshotID } },
|
|
71
|
+
limit: 5000,
|
|
72
|
+
depth: 0,
|
|
73
|
+
overrideAccess: true,
|
|
74
|
+
});
|
|
75
|
+
for (const pageResult of pageResults.docs) {
|
|
76
|
+
await payload.delete({
|
|
77
|
+
collection: 'seo-page-results',
|
|
78
|
+
id: pageResult.id,
|
|
79
|
+
overrideAccess: true,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
await payload.delete({
|
|
83
|
+
collection: 'seo-snapshots',
|
|
84
|
+
id: snapshotID,
|
|
85
|
+
overrideAccess: true,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
exports.enforceSEORetention = enforceSEORetention;
|
|
90
|
+
const runSEOSnapshot = async ({ payload, site, runType, }) => {
|
|
91
|
+
const runLabel = `${site.name || 'Site'} SEO Snapshot ${new Date().toISOString()}`;
|
|
92
|
+
const snapshot = await payload.create({
|
|
93
|
+
collection: 'seo-snapshots',
|
|
94
|
+
data: {
|
|
95
|
+
runLabel,
|
|
96
|
+
runType,
|
|
97
|
+
status: 'pending',
|
|
98
|
+
startedAt: new Date().toISOString(),
|
|
99
|
+
},
|
|
100
|
+
overrideAccess: true,
|
|
101
|
+
});
|
|
102
|
+
try {
|
|
103
|
+
const crawl = await (0, crawler_1.crawlSiteForSEO)(site);
|
|
104
|
+
const pageResults = crawl.pages.map((page) => {
|
|
105
|
+
const check = (0, checks_1.runSEOChecks)(page.url, page.html);
|
|
106
|
+
const score = (0, scoring_1.scoreSEOIssues)(check.issues);
|
|
107
|
+
return {
|
|
108
|
+
url: page.url,
|
|
109
|
+
path: page.path,
|
|
110
|
+
statusCode: page.statusCode,
|
|
111
|
+
check,
|
|
112
|
+
score,
|
|
113
|
+
};
|
|
114
|
+
});
|
|
115
|
+
const pageSpeedSummary = site.integrations?.enablePageSpeed !== false
|
|
116
|
+
? await (0, pagespeed_1.getPageSpeedSummary)(pageResults.map((page) => page.url))
|
|
117
|
+
: null;
|
|
118
|
+
const allIssues = applyPerformanceIssues(pageResults.flatMap((result) => result.check.issues), pageSpeedSummary, site);
|
|
119
|
+
const issueCounts = (0, helpers_1.countIssuesBySeverity)(allIssues);
|
|
120
|
+
const providerAdapter = (0, providers_1.getSEOProviderAdapter)();
|
|
121
|
+
const keywordVisibilitySync = await (0, gsc_1.syncGSCKeywordVisibility)({
|
|
122
|
+
payload,
|
|
123
|
+
site,
|
|
124
|
+
});
|
|
125
|
+
const summaryText = keywordVisibilitySync.enabled
|
|
126
|
+
? `Automated audit completed using ${providerAdapter.name} authority adapter. GSC rows imported: ${keywordVisibilitySync.imported}.`
|
|
127
|
+
: `Automated audit completed using ${providerAdapter.name} authority adapter.`;
|
|
128
|
+
const scores = {
|
|
129
|
+
metadata: average(pageResults.map((result) => result.score.metadata)),
|
|
130
|
+
indexability: average(pageResults.map((result) => result.score.indexability)),
|
|
131
|
+
structure: average(pageResults.map((result) => result.score.structure)),
|
|
132
|
+
links: average(pageResults.map((result) => result.score.links)),
|
|
133
|
+
media: average(pageResults.map((result) => result.score.media)),
|
|
134
|
+
structuredData: average(pageResults.map((result) => result.score.structuredData)),
|
|
135
|
+
performance: typeof pageSpeedSummary?.averagePerformanceScore === 'number'
|
|
136
|
+
? pageSpeedSummary.averagePerformanceScore
|
|
137
|
+
: average(pageResults.map((result) => result.score.performance)),
|
|
138
|
+
};
|
|
139
|
+
const overallScore = Math.round(scores.metadata * 0.2 +
|
|
140
|
+
scores.indexability * 0.2 +
|
|
141
|
+
scores.structure * 0.15 +
|
|
142
|
+
scores.links * 0.1 +
|
|
143
|
+
scores.media * 0.1 +
|
|
144
|
+
scores.structuredData * 0.1 +
|
|
145
|
+
scores.performance * 0.15);
|
|
146
|
+
for (const result of pageResults) {
|
|
147
|
+
await payload.create({
|
|
148
|
+
collection: 'seo-page-results',
|
|
149
|
+
data: {
|
|
150
|
+
snapshot: snapshot.id,
|
|
151
|
+
url: result.url,
|
|
152
|
+
path: result.path,
|
|
153
|
+
statusCode: result.statusCode,
|
|
154
|
+
title: result.check.title,
|
|
155
|
+
metaDescription: result.check.metaDescription,
|
|
156
|
+
canonical: result.check.canonical,
|
|
157
|
+
robotsMeta: result.check.robotsMeta,
|
|
158
|
+
h1Count: result.check.h1Count,
|
|
159
|
+
headingOrderIssues: result.check.headingOrderIssues,
|
|
160
|
+
internalLinks: result.check.internalLinks,
|
|
161
|
+
externalLinks: result.check.externalLinks,
|
|
162
|
+
imagesTotal: result.check.imagesTotal,
|
|
163
|
+
imagesMissingAlt: result.check.imagesMissingAlt,
|
|
164
|
+
structuredDataBlocks: result.check.structuredDataBlocks,
|
|
165
|
+
scoreBreakdown: {
|
|
166
|
+
metadata: result.score.metadata,
|
|
167
|
+
indexability: result.score.indexability,
|
|
168
|
+
structure: result.score.structure,
|
|
169
|
+
links: result.score.links,
|
|
170
|
+
media: result.score.media,
|
|
171
|
+
structuredData: result.score.structuredData,
|
|
172
|
+
},
|
|
173
|
+
overallScore: result.score.overall,
|
|
174
|
+
issues: result.check.issues,
|
|
175
|
+
checkedAt: new Date().toISOString(),
|
|
176
|
+
},
|
|
177
|
+
overrideAccess: true,
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
await payload.update({
|
|
181
|
+
collection: 'seo-snapshots',
|
|
182
|
+
id: snapshot.id,
|
|
183
|
+
data: {
|
|
184
|
+
status: 'completed',
|
|
185
|
+
completedAt: new Date().toISOString(),
|
|
186
|
+
metrics: {
|
|
187
|
+
pagesCrawled: crawl.visitedCount,
|
|
188
|
+
pagesChecked: pageResults.length,
|
|
189
|
+
avgLighthousePerformance: pageSpeedSummary?.averagePerformanceScore,
|
|
190
|
+
avgLCPMs: pageSpeedSummary?.averageLCPMs,
|
|
191
|
+
avgCLS: pageSpeedSummary?.averageCLS,
|
|
192
|
+
},
|
|
193
|
+
issueCounts,
|
|
194
|
+
scores: {
|
|
195
|
+
...scores,
|
|
196
|
+
overall: overallScore,
|
|
197
|
+
},
|
|
198
|
+
summary: summaryText,
|
|
199
|
+
},
|
|
200
|
+
overrideAccess: true,
|
|
201
|
+
});
|
|
202
|
+
await (0, exports.enforceSEORetention)(payload);
|
|
203
|
+
return {
|
|
204
|
+
snapshotID: snapshot.id,
|
|
205
|
+
pagesChecked: pageResults.length,
|
|
206
|
+
overallScore,
|
|
207
|
+
issueCounts,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
catch (error) {
|
|
211
|
+
await payload.update({
|
|
212
|
+
collection: 'seo-snapshots',
|
|
213
|
+
id: snapshot.id,
|
|
214
|
+
data: {
|
|
215
|
+
status: 'failed',
|
|
216
|
+
completedAt: new Date().toISOString(),
|
|
217
|
+
errorMessage: error instanceof Error ? error.message : String(error),
|
|
218
|
+
},
|
|
219
|
+
overrideAccess: true,
|
|
220
|
+
});
|
|
221
|
+
throw error;
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
exports.runSEOSnapshot = runSEOSnapshot;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scoring.d.ts","sourceRoot":"","sources":["../../src/utilities/scoring.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAoB,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAmBlF,eAAO,MAAM,cAAc,GAAI,QAAQ,QAAQ,EAAE,KAAG,YA8BnD,CAAA"}
|