@pixelated-tech/components 3.9.17 → 3.10.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/components/admin/site-health/google.api.integration.js +13 -3
- package/dist/components/admin/site-health/seo-metrics.config.json +0 -10
- package/dist/components/admin/site-health/site-health-cloudwatch.integration.js +10 -3
- package/dist/components/admin/site-health/site-health-uptime.integration.js +1 -1
- package/dist/components/general/accordion.css +4 -4
- package/dist/components/general/faq-accordion.js +4 -2
- package/dist/components/general/resume.css +4 -0
- package/dist/components/general/utilities.js +6 -0
- package/dist/config/pixelated.config.json.enc +1 -1
- package/dist/index.adminclient.js +0 -1
- package/dist/index.adminserver.js +2 -1
- package/dist/scripts/pixelated-eslint-plugin.js +64 -15
- package/dist/types/components/admin/site-health/google.api.integration.d.ts.map +1 -1
- package/dist/types/components/admin/site-health/site-health-cloudwatch.integration.d.ts.map +1 -1
- package/dist/types/components/general/faq-accordion.d.ts.map +1 -1
- package/dist/types/components/general/utilities.d.ts +2 -0
- package/dist/types/components/general/utilities.d.ts.map +1 -1
- package/dist/types/index.adminclient.d.ts +0 -1
- package/dist/types/index.adminserver.d.ts +1 -1
- package/dist/types/test/test-data.d.ts +1282 -0
- package/dist/types/test/test-data.d.ts.map +1 -0
- package/dist/types/tests/required-faq.rule.test.d.ts +2 -0
- package/dist/types/tests/required-faq.rule.test.d.ts.map +1 -0
- package/dist/types/tests/routecache.analytics.consumer.integration.test.d.ts +2 -0
- package/dist/types/tests/routecache.analytics.consumer.integration.test.d.ts.map +1 -0
- package/dist/types/tests/routecache.consumer.integration.test.d.ts +2 -0
- package/dist/types/tests/routecache.consumer.integration.test.d.ts.map +1 -0
- package/dist/types/tests/routes-json-smoke.test.d.ts +2 -0
- package/dist/types/tests/routes-json-smoke.test.d.ts.map +1 -0
- package/dist/types/tests/site-health-cache.unit.test.2.d.ts +2 -0
- package/dist/types/tests/site-health-cache.unit.test.2.d.ts.map +1 -0
- package/dist/types/tests/site-health-cache.unit.test.d.ts +2 -0
- package/dist/types/tests/site-health-cache.unit.test.d.ts.map +1 -0
- package/package.json +14 -14
- package/dist/components/admin/site-health/site-health-cache.js +0 -23
- package/dist/components/config/config.example.js +0 -77
- package/dist/types/components/admin/site-health/site-health-cache.d.ts +0 -12
- package/dist/types/components/admin/site-health/site-health-cache.d.ts.map +0 -1
- package/dist/types/components/config/config.example.d.ts +0 -4
- package/dist/types/components/config/config.example.d.ts.map +0 -1
|
@@ -5,8 +5,10 @@
|
|
|
5
5
|
*/
|
|
6
6
|
"use server";
|
|
7
7
|
import { google } from 'googleapis';
|
|
8
|
-
import {
|
|
8
|
+
import { CacheManager } from '../../general/cache-manager';
|
|
9
9
|
import { calculateDateRanges, formatChartDate, getCachedData, setCachedData } from './google.api.utils';
|
|
10
|
+
// Migration-time debug flag (owner requested): verbose cache traces during migration
|
|
11
|
+
const debug = false; // keep as literal during migration for traceability
|
|
10
12
|
/**
|
|
11
13
|
* Create authenticated Google API client for a specific service
|
|
12
14
|
*/
|
|
@@ -71,7 +73,7 @@ export async function createSearchConsoleClient(config) {
|
|
|
71
73
|
};
|
|
72
74
|
}
|
|
73
75
|
// Cache for analytics data (1 hour)
|
|
74
|
-
const analyticsCache = new
|
|
76
|
+
const analyticsCache = new CacheManager({ prefix: 'sitehealth-analytics-', ttl: 60 * 60 * 1000 });
|
|
75
77
|
/**
|
|
76
78
|
* Get Google Analytics data for a site with current/previous period comparison
|
|
77
79
|
*/
|
|
@@ -81,8 +83,12 @@ export async function getGoogleAnalyticsData(config, siteName, startDate, endDat
|
|
|
81
83
|
const cacheKey = `analytics-${siteName}-${startDate || 'default'}-${endDate || 'default'}`;
|
|
82
84
|
const cached = getCachedData(analyticsCache, cacheKey);
|
|
83
85
|
if (cached) {
|
|
86
|
+
if (debug)
|
|
87
|
+
console.debug('[site-health][analytics] cache HIT', cacheKey);
|
|
84
88
|
return { success: true, data: cached };
|
|
85
89
|
}
|
|
90
|
+
if (debug)
|
|
91
|
+
console.debug('[site-health][analytics] cache MISS', cacheKey);
|
|
86
92
|
if (!config.ga4PropertyId || config.ga4PropertyId === 'GA4_PROPERTY_ID_HERE') {
|
|
87
93
|
return {
|
|
88
94
|
success: false,
|
|
@@ -149,6 +155,8 @@ export async function getGoogleAnalyticsData(config, siteName, startDate, endDat
|
|
|
149
155
|
});
|
|
150
156
|
}
|
|
151
157
|
// Cache the result
|
|
158
|
+
if (debug)
|
|
159
|
+
console.debug('[site-health][analytics] caching', cacheKey, 'rows=', chartData.length);
|
|
152
160
|
setCachedData(analyticsCache, cacheKey, chartData);
|
|
153
161
|
return { success: true, data: chartData };
|
|
154
162
|
}
|
|
@@ -161,7 +169,7 @@ export async function getGoogleAnalyticsData(config, siteName, startDate, endDat
|
|
|
161
169
|
}
|
|
162
170
|
}
|
|
163
171
|
// Cache for search console data (1 hour)
|
|
164
|
-
const searchConsoleCache = new
|
|
172
|
+
const searchConsoleCache = new CacheManager({ prefix: 'sitehealth-searchconsole-', ttl: 60 * 60 * 1000 });
|
|
165
173
|
/**
|
|
166
174
|
* Get Google Search Console data for a site with current/previous period comparison
|
|
167
175
|
*/
|
|
@@ -245,6 +253,8 @@ export async function getSearchConsoleData(config, siteName, startDate, endDate)
|
|
|
245
253
|
});
|
|
246
254
|
}
|
|
247
255
|
// Cache the result
|
|
256
|
+
if (debug)
|
|
257
|
+
console.debug('[site-health][searchconsole] caching', cacheKey, 'rows=', chartData.length);
|
|
248
258
|
setCachedData(searchConsoleCache, cacheKey, chartData);
|
|
249
259
|
return { success: true, data: chartData };
|
|
250
260
|
}
|
|
@@ -188,16 +188,6 @@
|
|
|
188
188
|
"scoreLogic": "present",
|
|
189
189
|
"displayTemplate": "{{count}} robots meta tag(s) found"
|
|
190
190
|
},
|
|
191
|
-
"hreflang-tags": {
|
|
192
|
-
"id": "hreflang-tags",
|
|
193
|
-
"title": "Hreflang Tags",
|
|
194
|
-
"description": "Checks for hreflang link tags for international SEO",
|
|
195
|
-
"scoreDisplayMode": "binary",
|
|
196
|
-
"pattern": "<link[^>]*rel=[\"']alternate[\"'][^>]*hreflang=[\"'][^\"']*[\"'][^>]*>",
|
|
197
|
-
"countLogic": "count",
|
|
198
|
-
"scoreLogic": "present",
|
|
199
|
-
"displayTemplate": "{{count}} hreflang link(s) found"
|
|
200
|
-
},
|
|
201
191
|
"indexability-status": {
|
|
202
192
|
"id": "indexability-status",
|
|
203
193
|
"title": "Indexability Status",
|
|
@@ -4,10 +4,11 @@
|
|
|
4
4
|
*/
|
|
5
5
|
"use server";
|
|
6
6
|
import { CloudWatchClient, GetMetricDataCommand } from '@aws-sdk/client-cloudwatch';
|
|
7
|
-
import {
|
|
7
|
+
import { CacheManager } from '../../general/cache-manager';
|
|
8
8
|
import { getFullPixelatedConfig } from '../../config/config';
|
|
9
|
+
const debug = false; // migration-time verbose logging
|
|
9
10
|
// Cache for health check data (15 minutes)
|
|
10
|
-
const healthCheckCache = new
|
|
11
|
+
const healthCheckCache = new CacheManager({ prefix: 'sitehealth-cloudwatch-', ttl: 15 * 60 * 1000 });
|
|
11
12
|
/**
|
|
12
13
|
* Get health check data for a site using CloudWatch metrics
|
|
13
14
|
*/
|
|
@@ -17,7 +18,11 @@ export async function getCloudwatchHealthCheckData(config, siteName, startDate,
|
|
|
17
18
|
const cacheKey = `cloudwatch-${siteName}-${config.healthCheckId}-${startDate || 'default'}-${endDate || 'default'}`;
|
|
18
19
|
const cached = healthCheckCache.get(cacheKey);
|
|
19
20
|
if (cached) {
|
|
20
|
-
|
|
21
|
+
if (Array.isArray(cached)) {
|
|
22
|
+
return { success: true, data: cached };
|
|
23
|
+
}
|
|
24
|
+
// defensive: cached value present but not an array — coerce to empty array
|
|
25
|
+
return { success: true, data: [] };
|
|
21
26
|
}
|
|
22
27
|
// Use CloudWatch to get historical health check data
|
|
23
28
|
// Prefer credentials from unified config (pixelated.config.json) when present (avoids env vars)
|
|
@@ -133,6 +138,8 @@ export async function getCloudwatchHealthCheckData(config, siteName, startDate,
|
|
|
133
138
|
filledData = data;
|
|
134
139
|
}
|
|
135
140
|
// Cache the result
|
|
141
|
+
if (debug)
|
|
142
|
+
console.debug('[site-health][cloudwatch] caching', cacheKey, 'rows=', filledData.length);
|
|
136
143
|
healthCheckCache.set(cacheKey, filledData);
|
|
137
144
|
return { success: true, data: filledData };
|
|
138
145
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Route53Client, GetHealthCheckStatusCommand } from '@aws-sdk/client-route-53';
|
|
2
2
|
import { getFullPixelatedConfig } from '../../config/config';
|
|
3
|
-
const debug =
|
|
3
|
+
const debug = false;
|
|
4
4
|
export async function checkUptimeHealth(healthCheckId) {
|
|
5
5
|
try {
|
|
6
6
|
// Simple Route 53 call (global service). Prefer credentials from pixelated.config.json when present
|
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
.accordion-title {
|
|
18
18
|
cursor: pointer;
|
|
19
19
|
padding: 1rem;
|
|
20
|
-
font-size: 1rem;
|
|
21
|
-
font-weight: 600;
|
|
20
|
+
/* font-size: 1rem;
|
|
21
|
+
font-weight: 600; */
|
|
22
22
|
color: #1a202c;
|
|
23
23
|
background: none;
|
|
24
24
|
border: none;
|
|
@@ -62,8 +62,8 @@ details[open] .accordion-title::before {
|
|
|
62
62
|
|
|
63
63
|
.accordion-title h3 {
|
|
64
64
|
margin: 0;
|
|
65
|
-
font-size: inherit;
|
|
66
|
-
font-weight: inherit;
|
|
65
|
+
/* font-size: inherit;
|
|
66
|
+
font-weight: inherit; */
|
|
67
67
|
color: inherit;
|
|
68
68
|
flex: 1;
|
|
69
69
|
}
|
|
@@ -10,7 +10,8 @@ const categoryIcons = {
|
|
|
10
10
|
'Content & Management': '📝',
|
|
11
11
|
'Support & Maintenance': '🛠️',
|
|
12
12
|
'Ownership & Legal': '📋',
|
|
13
|
-
'Services': '💼'
|
|
13
|
+
'Services': '💼',
|
|
14
|
+
'': ''
|
|
14
15
|
};
|
|
15
16
|
FAQAccordion.propTypes = {
|
|
16
17
|
faqsData: PropTypes.shape({
|
|
@@ -55,7 +56,8 @@ export function FAQAccordion({ faqsData }) {
|
|
|
55
56
|
const accordionItems = filteredFaqs.map((faq, index) => {
|
|
56
57
|
const content = Array.isArray(faq.acceptedAnswer.text) ? (_jsx("div", { children: faq.acceptedAnswer.text.map((paragraph, pIndex) => (_jsx("p", { dangerouslySetInnerHTML: { __html: paragraph } }, pIndex))) })) : (_jsx("div", { dangerouslySetInnerHTML: { __html: faq.acceptedAnswer.text } }));
|
|
57
58
|
return {
|
|
58
|
-
title: `${categoryIcons[faq.category] || '❓'} ${faq.name}`,
|
|
59
|
+
/* title: `${categoryIcons[faq.category as CategoryKey] || '❓'} ${faq.name}`, */
|
|
60
|
+
title: `${categoryIcons[faq.category] || ''} ${faq.name}`,
|
|
59
61
|
content,
|
|
60
62
|
open: expandedStates[index] || undefined
|
|
61
63
|
};
|
|
@@ -53,6 +53,12 @@ export function generateUUID() {
|
|
|
53
53
|
export function capitalize(str) {
|
|
54
54
|
return str[0].toUpperCase() + str.toLowerCase().slice(1);
|
|
55
55
|
}
|
|
56
|
+
/** Capitalize the first letter of each word in `input`. */
|
|
57
|
+
export function capitalizeWords(input) {
|
|
58
|
+
if (!input)
|
|
59
|
+
return input;
|
|
60
|
+
return input.replace(/\p{L}[\p{L}'’-]*/gu, (word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase());
|
|
61
|
+
}
|
|
56
62
|
/*
|
|
57
63
|
Array.prototype.contains = function(obj) {
|
|
58
64
|
return this.indexOf(obj) > -1;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
pxl:v1:
|
|
1
|
+
pxl:v1:3e3b3b2f37db000bc900016a:eccf9d997d641ae455d0d6966b1d3eee:8b2d37b2b7c74f499c58dcd7edbf0ef2c8df5161f9bec74000f6f28ff33ae0f0d2324f24f06600185bfafe3610e27d32182ba2b8c6bb47202f9cfdb3ee29a96154e7613de886588b8dd76b998c73755358174e0f2a095eadd91003e139a90b5fb6cd009da65d10a4b235f247a396dcbe1c01af5210f0f6e9ecee80b52d9441378d387453fbbd70580cc2701e794e213bf6f52ced2f886c431ff41d76b3e4858abc0da2da144f86367c5d07f8a94a88273b9ed807a2d887a607a419cd6082e0dd7978450300d72095772305e1419d4ddfd3df46c7e3d82a7753ce22411c56d7ef761e7eb88bfd6b8edb5b88838ea75a991e242ee63e98de176cc8f1da9a5bdaa5bb9f2935d07f08e36d0e41702df58b49306f70630365f54ec569d033ae7e4bf13de4d50c3762f18f8ec5f8dbb7066cc56271f714a104a9ce19486e44fc329256893fdafa38a18f3aeb7fcd83ef31c247cb2072f66cfca1656396bbebad2826fb612ff8f99511a03672e282a60e6e300b3f4b7cb869534d4d8b7330531d2c1ed4ded7922e05ed39f2f166bc083f930ba5e2fb8f51d77bd433c95810ac6768ac2cdc2043812477a56432febca7b3d04ae73151b2d3d06bc66c21687c0c88cef4f886806c14f85b4b30d87a4e295cd6cee27ac7b8ad80fa70513accb8b5cd786d32713ceb12209515fa4078afd6c8c0a98739ad30428e662199cb4f618263cf94389be769570e562c631537ffe2f71a694026e7d45ed0dd4cb6762ad1b35209db6bfa06bbef0ab783181b12d3cd48f965edd907bf26747feea7b8445466382082e6dcf745fd5dfd5e574875e9b6fd5a52d17003445edcfb27c81cfba29a8e54c0cbc0c036d09a13b444002d49502251cd27865a14dc25a2b6c3bd766f9da8eb8a50f448ddf10cc926c9d42161c55811fa0c349d75df05122f3753b2c2be707f0d1c7515fdf586af24ee8ed9ce7dce9909e96e59f7576e504f50bd7ef971ac9f8d134130273a21ac74219010dbb5511050cffd9416020a9f9b2bb53144f8f328535d93b2fe5b3c9be1864daf80ae288d70a46b909e3fb8f1762b77c50e79de7f3b936647d5a392203ae87a6f13e57160cc526aa186e01800b282e57e6f5e06fc96560e16928aa640a1233b848caaadd7bfc266e4136773d348e2b74ba835d6a429abade5421a7be9278a8b9e1a41e6a2146d67bc60a04fb40bc90d61edefd3ca68ee91fd7179eb0513c30e81596f4bdaaf70aa179c90cf12e6aa72a90257395deeae0345eca67c1fa44b9db56580272000cd63079079547527bedf5cd91a02f1ec15e33b93b9988b92cbae1163c989731e82822653ed84c457cab76e85c87e77699a2be93bbfe46b74d66c8447a3d26a64b9b6e140320d7b131171d76de75ae8acbf2744d5d00ad7a017751984cc2bcfe6bd1872285819f35cb0e26c61538f50b2db653be9867f5d0f3f036231980f66f5936fdf3abb63b1d9a5cc51a9fa444554906bdb100e82594bd9f6599e4ed7253562df3fc58e8b44fc32fd81e0434bfff5d019b5145ea8b72276338b96d30ed5136df64137b885b304fccac2a6041c0ee02d1325d04e69882bb47405c20011a186a1f9e9018ae3bd0bd801e5ca33fe38032c325bb848f8fa67690cd4a894cf6b417ce3e5785f219cc98d1fd779392d250feea67882b90254e034541df5a49ed399aacb392f7df42e896f3cba7b901c6771340683168564072ae2731b1b7b28aa19a14faae93e7f20a917fab47e21b75a7c66f4ed439ca20231e21cd4139bc5332a7c359d7c685ef1bdca810168461f3d7e57c80349fb2ed74964a24352fa65d7f1e70cb05e688e8ae389a96f77c86e9c1968cd0c0cc8b7cb3253ad11ad55a91b79f01d60af5183fe402ebdb0e384d9c40e26afeef85c91fcba27c3c29cc6aeeaeebfc616514d95da6125885f38e9f4acf3f8c35ca00085d84b7d0f28df876edc3a9b19e553af2d23312d02fd56e19f0740832d52a344348a27744d9fb0b55a1d8a0a14c4a9e68d25ec09eb5202b7f5ca78c8ce5c42bcfbc05436b2d94ca564c76ba647aa8cbd03ffc6ca5b04e89b3226d2840988016265d84d71b41dae769644d711798935186cbee96b9e696b6480e5e7f011bfb24427d796dfbb4a256cb109e32481126a4330787cb4f94ab95b14b122eb7718e6fe732d9c1806e4a078dbcb84aa56a365233c05b59a2b947120e0654d4dd976b397d283c89f9c70270bf86c112da8e21dd4c42cb98e3355751b623304a3e07e7a5945bfdf9dafcff1d87534bf92f6dfe4672716dc285ba29245785b3944edc0d1a0a01887cdb74a5f8d924e1784cf0e9a641dc8dc3aa48b594f581c6718f486442fe46d2bf7682ead64ea8973912fcecb7c1cfcf02943c292b9ca3eec366b1f982494e51900e846ed3ed7c1b05acc01eb14a93e549c984830160a9de6d1f5e5cb44360235f1f5d2376798a7865f8826ac3ee899a9f30083d4f0cf1317bbeac55cf55e81e82808050ed99bec9c4c56c0b4c8ccaeddd93b9aa20ba5c613baf874fcef785ec97e2377bdb1022ee0b86ff3ddd6651fa34ae97ff8e69a62c4a40bff9cd34d8c344ae59d565f00f097eb417e3c020d8761db0f0a046f1acdb552db446c06272ac7882ac3abed2bb5a9b3dcebdcaf1e085f5d05b7bbfb25a2085b5e6e1418f1b95ab82ca744b0e31b917d4e7af73f4b55f6597944044bd55431e6475143483f7a0922e20137070c928d507678ccbc92ac94ce250ff585ebc45aab92d2a4cc36a97bcad85fb37c0f2bf8f1f9c95116bd2c12b473f23a234da3f5f8f130449a1ad55a2b5c4b293b913558ffb2e09a583853d23c557751b2fdbe6ac173d063663ee5d40096501d6653c53399dbb9df96dd829b8fb848ea24cc67b29f65063f3fc1e5bdba05e8b23e8e0b9b0dd8909535da7532dbbdb5428140098c8bb0a0a71a46545f85aa8dc511e6dd4abd23e283ddb7ec9fd9202ccad200a34d05c395a84cdee7f0273bdc8dbd6f5b546e6255c9e2216b0596c91068ec584700e1f1201f53888a525ea8e51df5e953a2cbd38bb83c8a21474ee05f1f8823688304c3a3c1bcd882fc8d74c357181b906c1ca4571236a3a8536fa3eddab9db0239a9aa6031c427ea3e0c50880fe8dae67e0e9440d695a00a831e8d1319de801cefa712b1fa6452bedde5c71206dee7fc2bdea2db0ea9820bed6a72dd0134976812058d712333994bfc1a5adad8a1e36e9a3eca4543d691f08846f0ca751611270b9f02c6590771962d8c3b939ce2caa07fe4d0cedb44ffa7e0345fefec7d384854b8bb061ca1970464c1e9ef4fc69216fe9084c45c672a15497bbb165964dc2692a7717cbc45cb0a8746db20ff6523ed2289b3470d98290a504d09e230d16683cdfd12c9a31dfa5e4417c13e2523e36371409a3438080b84887cd9a9d3cfc10f7a2359b6b84044ef34ae50144c2692be1d91b1b0d874b3a3c8e9ac0ade9ffde0488a7bffeec69fcd14fb5f32cd7bfcb24c0491b1f679383318dbf01bc0c1bec7945b116c2413c1cbdd44db41aa8eabcf794e25844aa6d942e59e9b7ff76e19d5a29b77760de72268b5c195583927dc54dedcc5097119d3a9c804455fab7eeaa5f7dea70cca406eabd11afca4a4a95acce2ea2d1f3d0bf9d57d0569c13010b2cda1fa1c9ed6d32547802301aedd803d4424ff9621515c7a0355a02953398b06119343ada45216a266ef1f7ff1e064b0e93117f750f885b1bc9055ed4456913f9f6f7ea9050aff8939fa86ca18366aa1324ac44427091f0f13e7e1e5198eb465872f9c0f14bc08253a6064631c3cfa1d8f2a11b0c13c1f46ec71db24c9b1e98c9ea50ed72971bc16005aafd5a05b372623b26aa2123bb585ad6a69bbde3d297d4458ecf000c2e96e89d1e09e0c47c7f1a92546720ad9c6da9d2f017ce1b28fe90f4919a30c2f92a1199bb0be0e8bb7c08c4cb0ee1e9f8671fb5b08c5421eb99ab870f631bd530436d17109d24299a0672c0c7c6b723a40e6fa115dde1645cd4edaecdc231a3cc40d7bb4108958e299ea3b9312c7cf9338479d9db93548cf2159068b137054dd2be73ca87dfb5868c10e444ed89a45588a43dae7292b82c2921b43afd50640d7bf70329b800ff30ceabb1de72eed1efd74648e09f49230c4913ea8e48c5e97317fb62cccf5252304c12f9ffacff79c49d879ba18ca2e614eb59b745a7a74fdeb1e2dd1181ec5679e7acce8e724ab60b89e496f3b925f2bbdde8248a2db00f71cbd9ba3dfd652d1aae6f361b5a07ec94ac3527d24fc225752c32aa5bfcdce9cc372f1dcb6e6372f8ee8872f74ca112a7a51b1df1142c1eec501a69e34217975cb026d4580d1ae54e153260ab12b8d207b3264d30f74435ed8f2133509058919fd4fe5bf29f8afe68250ecd7552a8dd03e56977579a117ec309da8a31a81fd083f557182ab9a777ec0594ed6d748d48179fd75564501b262e47900454fdfb6c664dd006bcfd250d64a405eb41786e2e9e220cbdb14dc83e48d27a590fa1515245d4b212ec03ada6f3acc6d119a7d4b702eaf54ede1f36ad066914e66173d3ab47fce9993d39df270428983aec047e727c1496c5619b85eb00a30ec3a24ad619ccbb23367f60e0c572ef0c2b6aa1b48a53e1500386c7c214d0458a40ba7ad467aef49939cae711f153eac93d9d97344b089c2eea45d6c5cb175c442356f12c0231938bff50e7552c7d40ec27a98f0c559426e689d6c5b20c549e2b5167117823eb1e7175bc981ae0a7f865f6cb6d1ee769db6541fc7b098b44ddc51c2a987c210b93755bacf4422d6a3f3cda5b37f0fcb955b063a257e512d0df1af3f41a6703fe6037d71148d0fe4555726e19fb1a5f76405c1c528c604c1cdadbadc7e3289813b6ab3313d1a345365dcea0a34962990ba5223af15e733067bfc75038d13dfeb7abe9ff9015c85decd248bd60a516927dcb15ea989d07c72e1c3ba363f90bae5f17e7f4b7b1014e7eea49cb200f49be4f88df7b733bec6709bf7d7ceacee13a4f55adc3f6ad15a9ed03f4d785980265443155b624c8f931a59f26c4351858f0af02b3472ef6cb3c269df8e6c627c5cbdb2164a0546493465d92313b7a748a2b32fc52aad73729ecfbcdced568c415cdf1a746c58be501e47796de60b556e701db3c3be83f8b77d3d2f26
|
|
@@ -21,7 +21,6 @@ export * from './components/admin/site-health/site-health-mock-context';
|
|
|
21
21
|
// Client-safe admin components (can run on both client and server)
|
|
22
22
|
export * from './components/admin/site-health/google.api.utils';
|
|
23
23
|
export * from './components/admin/site-health/seo-constants';
|
|
24
|
-
export * from './components/admin/site-health/site-health-cache';
|
|
25
24
|
export * from './components/admin/site-health/site-health-indicators';
|
|
26
25
|
export * from './components/admin/site-health/site-health-on-site-seo.integration';
|
|
27
26
|
export * from './components/admin/site-health/site-health-performance';
|
|
@@ -5,11 +5,12 @@ export * from './components/admin/site-health/google.api.integration';
|
|
|
5
5
|
export * from './components/admin/site-health/google.api.utils';
|
|
6
6
|
export * from './components/admin/site-health/seo-constants';
|
|
7
7
|
export * from './components/admin/site-health/site-health-axe-core.integration';
|
|
8
|
-
export * from './components/admin/site-health/site-health-cache';
|
|
9
8
|
export * from './components/admin/site-health/site-health-cloudwatch.integration';
|
|
10
9
|
export * from './components/admin/site-health/site-health-core-web-vitals.integration';
|
|
11
10
|
export * from './components/admin/site-health/site-health-github.integration';
|
|
12
11
|
export * from './components/admin/site-health/site-health-google-analytics.integration';
|
|
12
|
+
// Re-export CacheManager for server/admin consumers (migration: RouteCache → CacheManager)
|
|
13
|
+
export * from './components/general/cache-manager';
|
|
13
14
|
export * from './components/admin/site-health/site-health-google-search-console.integration';
|
|
14
15
|
export * from './components/admin/site-health/site-health-indicators';
|
|
15
16
|
export * from './components/admin/site-health/site-health-on-site-seo.integration';
|
|
@@ -428,8 +428,8 @@ const requiredFaqRule = {
|
|
|
428
428
|
recommended: true,
|
|
429
429
|
},
|
|
430
430
|
messages: {
|
|
431
|
-
missingFaqPage: 'FAQ page is missing. FAQ pages are strongly recommended
|
|
432
|
-
missingFaqSchema: 'FAQSchema is missing from the FAQ page.',
|
|
431
|
+
missingFaqPage: 'FAQ page is missing. FAQ pages are strongly recommended (examples: src/app/faqs/page.tsx, src/app/(pages)/faqs/page.tsx, src/pages/faqs/index.tsx).',
|
|
432
|
+
missingFaqSchema: 'FAQSchema (SchemaFAQ / JSON-LD @type:FAQPage) is missing from the FAQ page.',
|
|
433
433
|
},
|
|
434
434
|
},
|
|
435
435
|
create(context) {
|
|
@@ -437,27 +437,76 @@ const requiredFaqRule = {
|
|
|
437
437
|
if (!context.getFilename().endsWith('layout.tsx')) return {};
|
|
438
438
|
|
|
439
439
|
const projectRoot = context.cwd;
|
|
440
|
-
|
|
440
|
+
|
|
441
|
+
function findFaqPath(root) {
|
|
442
|
+
// Walk `src/` and match any path segment or filename named `faq` or `faqs`.
|
|
443
|
+
// Return the first matching `page.*` / `index.*` or a direct faq(s).(ts|tsx|js|jsx) file.
|
|
444
|
+
// Returns `null` when no candidate is found.
|
|
445
|
+
const srcRoot = path.join(root, 'src');
|
|
446
|
+
if (!fs.existsSync(srcRoot)) return null;
|
|
447
|
+
|
|
448
|
+
const stack = [srcRoot];
|
|
449
|
+
const filePattern = /(^|\/)faqs?\.(t|j)sx?$/i; // matches .../faq.tsx, .../faqs.js, etc.
|
|
450
|
+
while (stack.length) {
|
|
451
|
+
const cur = stack.pop();
|
|
452
|
+
try {
|
|
453
|
+
const entries = fs.readdirSync(cur, { withFileTypes: true });
|
|
454
|
+
for (const e of entries) {
|
|
455
|
+
const full = path.join(cur, e.name);
|
|
456
|
+
const rel = path.relative(root, full).replace(/\\/g, '/');
|
|
457
|
+
|
|
458
|
+
if (e.isDirectory()) {
|
|
459
|
+
// directory named faq/faqs -> prefer page/index inside it
|
|
460
|
+
if (/^faqs?$/i.test(e.name)) {
|
|
461
|
+
const candidates = [
|
|
462
|
+
path.join(full, 'page.tsx'),
|
|
463
|
+
path.join(full, 'page.ts'),
|
|
464
|
+
path.join(full, 'index.tsx'),
|
|
465
|
+
path.join(full, 'index.ts'),
|
|
466
|
+
];
|
|
467
|
+
for (const c of candidates) if (fs.existsSync(c)) return c;
|
|
468
|
+
}
|
|
469
|
+
// continue walking
|
|
470
|
+
stack.push(full);
|
|
471
|
+
continue;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
// direct file matches like src/pages/faqs.tsx
|
|
475
|
+
if (filePattern.test(rel)) return full;
|
|
476
|
+
}
|
|
477
|
+
} catch (err) {
|
|
478
|
+
/* ignore unreadable dirs */
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
return null;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
const faqPath = findFaqPath(projectRoot);
|
|
441
486
|
|
|
442
487
|
return {
|
|
443
488
|
'Program:exit'() {
|
|
444
|
-
|
|
489
|
+
// If finder returned nothing or the candidate does not exist -> missing page
|
|
490
|
+
if (!faqPath || !fs.existsSync(faqPath)) {
|
|
445
491
|
context.report({
|
|
446
492
|
loc: { line: 1, column: 0 },
|
|
447
493
|
messageId: 'missingFaqPage',
|
|
448
494
|
});
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
// Accept component-based SchemaFAQ, `FAQSchema` identifier, or JSON-LD @type:FAQPage
|
|
499
|
+
try {
|
|
500
|
+
const content = fs.readFileSync(faqPath, 'utf8');
|
|
501
|
+
const hasSchema = /FAQSchema|SchemaFAQ|"@type"\s*:\s*"FAQPage"/i.test(content);
|
|
502
|
+
if (!hasSchema) {
|
|
503
|
+
context.report({
|
|
504
|
+
loc: { line: 1, column: 0 },
|
|
505
|
+
messageId: 'missingFaqSchema',
|
|
506
|
+
});
|
|
460
507
|
}
|
|
508
|
+
} catch (e) {
|
|
509
|
+
// Ignore read errors
|
|
461
510
|
}
|
|
462
511
|
},
|
|
463
512
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"google.api.integration.d.ts","sourceRoot":"","sources":["../../../../../src/components/admin/site-health/google.api.integration.ts"],"names":[],"mappings":"AAAA;;;;GAIG;
|
|
1
|
+
{"version":3,"file":"google.api.integration.d.ts","sourceRoot":"","sources":["../../../../../src/components/admin/site-health/google.api.integration.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"site-health-cloudwatch.integration.d.ts","sourceRoot":"","sources":["../../../../../src/components/admin/site-health/site-health-cloudwatch.integration.ts"],"names":[],"mappings":"AAAA;;;GAGG;
|
|
1
|
+
{"version":3,"file":"site-health-cloudwatch.integration.d.ts","sourceRoot":"","sources":["../../../../../src/components/admin/site-health/site-health-cloudwatch.integration.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAUH,MAAM,WAAW,2BAA2B;IAC1C,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,oBAAoB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,6BAA6B;IAC7C,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,oBAAoB,EAAE,CAAC;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAKD;;GAEG;AACH,wBAAsB,4BAA4B,CACjD,MAAM,EAAE,2BAA2B,EACnC,QAAQ,EAAE,MAAM,EAChB,SAAS,CAAC,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,6BAA6B,CAAC,CAsJxC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"faq-accordion.d.ts","sourceRoot":"","sources":["../../../../src/components/general/faq-accordion.tsx"],"names":[],"mappings":"AACA,OAAO,SAAS,EAAE,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAEnD,OAAO,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"faq-accordion.d.ts","sourceRoot":"","sources":["../../../../src/components/general/faq-accordion.tsx"],"names":[],"mappings":"AACA,OAAO,SAAS,EAAE,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAEnD,OAAO,qBAAqB,CAAC;AA+B7B,MAAM,MAAM,gBAAgB,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,SAAS,CAAC,CAAC;AACzE,wBAAgB,YAAY,CAAC,EAAE,QAAQ,EAAE,EAAE,gBAAgB,2CAuF1D;yBAvFe,YAAY"}
|
|
@@ -6,6 +6,8 @@ export declare function randomBetween(min: number, max: number): number;
|
|
|
6
6
|
export declare function generateKey(): string;
|
|
7
7
|
export declare function generateUUID(): string;
|
|
8
8
|
export declare function capitalize(str: string): string;
|
|
9
|
+
/** Capitalize the first letter of each word in `input`. */
|
|
10
|
+
export declare function capitalizeWords(input: string): string;
|
|
9
11
|
export declare function attributeMap(oldAttribute: string): string;
|
|
10
12
|
/**
|
|
11
13
|
* Adds a single 'change' event listener to the document and uses event delegation
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utilities.d.ts","sourceRoot":"","sources":["../../../../src/components/general/utilities.ts"],"names":[],"mappings":"AAGA,wBAAgB,QAAQ,CAAE,GAAG,EAAE,MAAM,oBAUpC;AAGD,wBAAgB,SAAS,CAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG;;EAmBxC;AAED,wBAAgB,aAAa,CAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,UAOtD;AAED,wBAAgB,WAAW,WAc1B;AAED,wBAAgB,YAAY,WAK3B;AAED,wBAAgB,UAAU,CAAE,GAAG,EAAE,MAAM,UAEtC;AAQD,wBAAgB,YAAY,CAAE,YAAY,EAAE,MAAM,UAgCjD;AAID;;;;OAII;AACJ,wBAAgB,YAAY,SAoB3B;AAOD;;;GAGG;AACH,eAAO,MAAM,oBAAoB,UA0BhC,CAAC;AAGF;;GAEG;AACH,eAAO,MAAM,uBAAuB,UAQnC,CAAC;AAEF,eAAO,MAAM,wBAAwB,UAOpC,CAAC;AAGF;;GAEG;AACH,eAAO,MAAM,oBAAoB,UAoBhC,CAAC"}
|
|
1
|
+
{"version":3,"file":"utilities.d.ts","sourceRoot":"","sources":["../../../../src/components/general/utilities.ts"],"names":[],"mappings":"AAGA,wBAAgB,QAAQ,CAAE,GAAG,EAAE,MAAM,oBAUpC;AAGD,wBAAgB,SAAS,CAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG;;EAmBxC;AAED,wBAAgB,aAAa,CAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,UAOtD;AAED,wBAAgB,WAAW,WAc1B;AAED,wBAAgB,YAAY,WAK3B;AAED,wBAAgB,UAAU,CAAE,GAAG,EAAE,MAAM,UAEtC;AAGD,2DAA2D;AAC3D,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAKrD;AAQD,wBAAgB,YAAY,CAAE,YAAY,EAAE,MAAM,UAgCjD;AAID;;;;OAII;AACJ,wBAAgB,YAAY,SAoB3B;AAOD;;;GAGG;AACH,eAAO,MAAM,oBAAoB,UA0BhC,CAAC;AAGF;;GAEG;AACH,eAAO,MAAM,uBAAuB,UAQnC,CAAC;AAEF,eAAO,MAAM,wBAAwB,UAOpC,CAAC;AAGF;;GAEG;AACH,eAAO,MAAM,oBAAoB,UAoBhC,CAAC"}
|
|
@@ -17,7 +17,6 @@ export * from "./components/admin/site-health/site-health-uptime";
|
|
|
17
17
|
export * from "./components/admin/site-health/site-health-mock-context";
|
|
18
18
|
export * from "./components/admin/site-health/google.api.utils";
|
|
19
19
|
export * from "./components/admin/site-health/seo-constants";
|
|
20
|
-
export * from "./components/admin/site-health/site-health-cache";
|
|
21
20
|
export * from "./components/admin/site-health/site-health-indicators";
|
|
22
21
|
export * from "./components/admin/site-health/site-health-on-site-seo.integration";
|
|
23
22
|
export * from "./components/admin/site-health/site-health-performance";
|
|
@@ -5,11 +5,11 @@ export * from "./components/admin/site-health/google.api.integration";
|
|
|
5
5
|
export * from "./components/admin/site-health/google.api.utils";
|
|
6
6
|
export * from "./components/admin/site-health/seo-constants";
|
|
7
7
|
export * from "./components/admin/site-health/site-health-axe-core.integration";
|
|
8
|
-
export * from "./components/admin/site-health/site-health-cache";
|
|
9
8
|
export * from "./components/admin/site-health/site-health-cloudwatch.integration";
|
|
10
9
|
export * from "./components/admin/site-health/site-health-core-web-vitals.integration";
|
|
11
10
|
export * from "./components/admin/site-health/site-health-github.integration";
|
|
12
11
|
export * from "./components/admin/site-health/site-health-google-analytics.integration";
|
|
12
|
+
export * from "./components/general/cache-manager";
|
|
13
13
|
export * from "./components/admin/site-health/site-health-google-search-console.integration";
|
|
14
14
|
export * from "./components/admin/site-health/site-health-indicators";
|
|
15
15
|
export * from "./components/admin/site-health/site-health-on-site-seo.integration";
|