@pixelated-tech/components 3.4.1 → 3.4.3

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.
Files changed (24) hide show
  1. package/README.COMPONENTS.md +148 -0
  2. package/README.md +30 -10
  3. package/dist/components/admin/site-health/seo-metrics.config.json +111 -0
  4. package/dist/components/admin/site-health/site-health-cloudwatch.integration.js +142 -0
  5. package/dist/components/admin/site-health/site-health-cloudwatch.js +44 -0
  6. package/dist/components/admin/site-health/site-health-on-site-seo.integration.js +297 -3
  7. package/dist/components/cms/pixelated.linkedin1.js +1 -0
  8. package/dist/index.js +1 -0
  9. package/dist/index.server.js +1 -0
  10. package/dist/types/components/admin/site-health/site-health-cloudwatch.d.ts +8 -0
  11. package/dist/types/components/admin/site-health/site-health-cloudwatch.d.ts.map +1 -0
  12. package/dist/types/components/admin/site-health/site-health-cloudwatch.integration.d.ts +25 -0
  13. package/dist/types/components/admin/site-health/site-health-cloudwatch.integration.d.ts.map +1 -0
  14. package/dist/types/components/admin/site-health/site-health-on-site-seo.integration.d.ts.map +1 -1
  15. package/dist/types/components/cms/pixelated.linkedin1.d.ts.map +1 -1
  16. package/dist/types/index.d.ts +1 -0
  17. package/dist/types/index.server.d.ts +1 -0
  18. package/dist/types/stories/admin/site-health.stories.d.ts +4 -0
  19. package/dist/types/stories/admin/site-health.stories.d.ts.map +1 -1
  20. package/dist/types/tests/site-health-cloudwatch.test.d.ts +2 -0
  21. package/dist/types/tests/site-health-cloudwatch.test.d.ts.map +1 -0
  22. package/dist/types/tests/site-health-on-site-seo.integration.test.d.ts +2 -0
  23. package/dist/types/tests/site-health-on-site-seo.integration.test.d.ts.map +1 -0
  24. package/package.json +3 -2
@@ -2001,6 +2001,154 @@ import { Timeline } from '@pixelated-tech/components';
2001
2001
 
2002
2002
  ---
2003
2003
 
2004
+ ## Admin Components
2005
+
2006
+ ### Site Health
2007
+
2008
+ Comprehensive site monitoring and health check components for performance, security, and SEO analysis.
2009
+
2010
+ #### SiteHealthOverview
2011
+
2012
+ Displays Core Web Vitals and Lighthouse performance metrics.
2013
+
2014
+ ```tsx
2015
+ import { SiteHealthOverview } from '@pixelated-tech/components';
2016
+
2017
+ <SiteHealthOverview siteName="my-site" />
2018
+ ```
2019
+
2020
+ #### SiteHealthAxeCore
2021
+
2022
+ Accessibility auditing using axe-core.
2023
+
2024
+ ```tsx
2025
+ import { SiteHealthAxeCore } from '@pixelated-tech/components';
2026
+
2027
+ <SiteHealthAxeCore siteName="my-site" />
2028
+ ```
2029
+
2030
+ #### SiteHealthPerformance
2031
+
2032
+ Performance monitoring and metrics.
2033
+
2034
+ ```tsx
2035
+ import { SiteHealthPerformance } from '@pixelated-tech/components';
2036
+
2037
+ <SiteHealthPerformance siteName="my-site" />
2038
+ ```
2039
+
2040
+ #### SiteHealthSecurity
2041
+
2042
+ Security scanning and vulnerability assessment.
2043
+
2044
+ ```tsx
2045
+ import { SiteHealthSecurity } from '@pixelated-tech/components';
2046
+
2047
+ <SiteHealthSecurity siteName="my-site" />
2048
+ ```
2049
+
2050
+ #### SiteHealthSEO
2051
+
2052
+ SEO analysis and recommendations.
2053
+
2054
+ ```tsx
2055
+ import { SiteHealthSEO } from '@pixelated-tech/components';
2056
+
2057
+ <SiteHealthSEO siteName="my-site" />
2058
+ ```
2059
+
2060
+ #### SiteHealthOnSiteSEO
2061
+
2062
+ Advanced on-page SEO analysis with Puppeteer rendering.
2063
+
2064
+ ```tsx
2065
+ import { SiteHealthOnSiteSEO } from '@pixelated-tech/components';
2066
+
2067
+ <SiteHealthOnSiteSEO siteName="my-site" />
2068
+ ```
2069
+
2070
+ #### SiteHealthCloudwatch
2071
+
2072
+ AWS CloudWatch uptime monitoring and availability tracking.
2073
+
2074
+ ```tsx
2075
+ import { SiteHealthCloudwatch } from '@pixelated-tech/components';
2076
+
2077
+ <SiteHealthCloudwatch
2078
+ siteName="my-site"
2079
+ startDate="2024-01-01"
2080
+ endDate="2024-01-31"
2081
+ />
2082
+ ```
2083
+
2084
+ #### SiteHealthGoogleAnalytics
2085
+
2086
+ Google Analytics integration and reporting.
2087
+
2088
+ ```tsx
2089
+ import { SiteHealthGoogleAnalytics } from '@pixelated-tech/components';
2090
+
2091
+ <SiteHealthGoogleAnalytics siteName="my-site" />
2092
+ ```
2093
+
2094
+ #### SiteHealthGoogleSearchConsole
2095
+
2096
+ Google Search Console data and insights.
2097
+
2098
+ ```tsx
2099
+ import { SiteHealthGoogleSearchConsole } from '@pixelated-tech/components';
2100
+
2101
+ <SiteHealthGoogleSearchConsole siteName="my-site" />
2102
+ ```
2103
+
2104
+ #### SiteHealthDependencyVulnerabilities
2105
+
2106
+ NPM package vulnerability scanning.
2107
+
2108
+ ```tsx
2109
+ import { SiteHealthDependencyVulnerabilities } from '@pixelated-tech/components';
2110
+
2111
+ <SiteHealthDependencyVulnerabilities siteName="my-site" />
2112
+ ```
2113
+
2114
+ #### SiteHealthGit
2115
+
2116
+ Git repository health and commit analysis.
2117
+
2118
+ ```tsx
2119
+ import { SiteHealthGit } from '@pixelated-tech/components';
2120
+
2121
+ <SiteHealthGit siteName="my-site" />
2122
+ ```
2123
+
2124
+ #### SiteHealthUptime
2125
+
2126
+ Website uptime monitoring.
2127
+
2128
+ ```tsx
2129
+ import { SiteHealthUptime } from '@pixelated-tech/components';
2130
+
2131
+ <SiteHealthUptime siteName="my-site" />
2132
+ ```
2133
+
2134
+ ### Sites
2135
+
2136
+ Site management and configuration components.
2137
+
2138
+ #### Sites Integration
2139
+
2140
+ ```tsx
2141
+ import { loadSitesConfig, saveSitesConfig } from '@pixelated-tech/components';
2142
+
2143
+ // Load site configuration
2144
+ const sites = loadSitesConfig();
2145
+
2146
+ // Save site configuration
2147
+ saveSitesConfig(sites);
2148
+ ```
2149
+
2150
+ ---
2151
+
2004
2152
  ## Entertainment
2005
2153
 
2006
2154
  ### NerdJoke
package/README.md CHANGED
@@ -165,6 +165,8 @@ User interface and interaction components:
165
165
  - **Menu** - Navigation components (Simple, Accordion, Expando)
166
166
  - **Tab** - Tabbed interface component for organizing content
167
167
  - **Tiles** - Image grid and tile layouts
168
+ - **FontSelector** - Font selection and Google Fonts integration
169
+ - **CompoundFontSelector** - Advanced font selection with multiple font families
168
170
 
169
171
  ### Development Tools
170
172
  Components for development, configuration, and site building:
@@ -172,6 +174,7 @@ Components for development, configuration, and site building:
172
174
  - **ComponentSelector** - Component selection interface
173
175
  - **ComponentTree** - Visual component hierarchy display
174
176
  - **ConfigBuilder** - Interactive configuration builder for site settings, metadata, routes, and visual design tokens
177
+ - **ConfigEngine** - Configuration processing and validation engine
175
178
  - **PageBuilderUI** - User interface for page building
176
179
  - **SaveLoadSection** - Save and load functionality for configurations
177
180
 
@@ -213,6 +216,23 @@ External service integrations:
213
216
  - **Yelp** - Business reviews and ratings
214
217
 
215
218
 
219
+ ### Site Health & Monitoring
220
+ Comprehensive site health monitoring and analytics:
221
+ - **SiteHealthOverview** - Dashboard overview of site health metrics
222
+ - **SiteHealthPerformance** - Performance monitoring and optimization insights
223
+ - **SiteHealthAccessibility** - Accessibility compliance testing with axe-core
224
+ - **SiteHealthSecurity** - Security vulnerability scanning and recommendations
225
+ - **SiteHealthSEO** - On-page SEO analysis and scoring
226
+ - **SiteHealthOnSiteSEO** - Advanced on-page SEO metrics (browser caching, gzip compression, mobile-first indexing, etc.)
227
+ - **SiteHealthGoogleAnalytics** - Google Analytics data integration
228
+ - **SiteHealthGoogleSearchConsole** - Google Search Console integration
229
+ - **SiteHealthCloudwatch** - AWS CloudWatch uptime monitoring
230
+ - **SiteHealthGit** - Git repository health and status
231
+ - **SiteHealthUptime** - Uptime monitoring and alerts
232
+ - **SiteHealthAxeCore** - Automated accessibility testing
233
+ - **SiteHealthDependencyVulnerabilities** - Dependency security scanning
234
+
235
+
216
236
  ## 🎨 Visual Design Configuration
217
237
 
218
238
  The ConfigBuilder now includes a **Visual Design** tab that allows users to configure visual design tokens such as colors, fonts, spacing, and other design system variables. These tokens are stored in the `routes.json` file under the `visualdesign` object and can be used throughout your application for consistent theming.
@@ -302,7 +322,6 @@ npm run storybook
302
322
  - [ ] **Project Scaffolding CLI**: Interactive CLI tool that generates complete Next.js projects with pixelated-components pre-configured, including routes.json, layout.tsx, package.json, and basic page structure
303
323
  - [ ] **Template Marketplace**: Pre-built industry-specific templates (restaurant, law firm, contractor, etc.) that users can clone and customize
304
324
  - [ ] **Configuration Wizard**: Step-by-step setup wizard that collects business info, generates site configuration, and creates initial content structure
305
- - [ IP ] **Site Health Monitoring**: Automated monitoring dashboard that checks site performance, broken links, SEO scores, and security vulnerabilities across all sites
306
325
  - [ ] **Content Migration Tools**: Automated importers for WordPress, Squarespace, Wix, and other platforms to migrate content to pixelated sites
307
326
  - [ ] **A/B Testing Framework**: Built-in experimentation system for testing different layouts, content, and CTAs with automatic winner selection
308
327
  - [ ] **Personalization Engine**: Dynamic content delivery based on user behavior, location, and preferences
@@ -381,16 +400,16 @@ Project Link: [https://github.com/brianwhaley/pixelated-components](https://gith
381
400
 
382
401
  ### Overview
383
402
 
384
- **Current Status**: ✅ 2,370 tests passing across 77 test files
403
+ **Current Status**: ✅ 2,387 tests passing across 79 test files
385
404
 
386
405
  | Metric | Value |
387
406
  |--------|-------|
388
- | Test Files | 77 |
389
- | Total Tests | 2,370 |
390
- | Coverage (Statements) | 76.98% |
391
- | Coverage (Lines) | 79.55% |
392
- | Coverage (Functions) | 78.09% |
393
- | Coverage (Branches) | 67.57% |
407
+ | Test Files | 79 |
408
+ | Total Tests | 2,387 |
409
+ | Coverage (Statements) | 77.13% |
410
+ | Coverage (Lines) | 79.73% |
411
+ | Coverage (Functions) | 77.98% |
412
+ | Coverage (Branches) | 67.55% |
394
413
  | Test Framework | Vitest 4.x |
395
414
  | Testing Library | @testing-library/react + jsdom |
396
415
 
@@ -429,8 +448,8 @@ npm run test:run # Single run (for CI)
429
448
  - **buzzwordbingo.tsx**: 100% statements
430
449
  - **markdown.tsx**: 100% statements
431
450
  - **timeline.tsx**: 100% statements
451
+ - **config.client.tsx**: 100% statements
432
452
  - **sidepanel.tsx**: 97.5% statements
433
- - **config.server.tsx**: 50% statements
434
453
  - **config.ts**: 96.55% statements
435
454
  - **google.reviews.components.tsx**: 95.83% statements
436
455
  - **schema-blogposting.tsx**: 95.24% statements
@@ -440,7 +459,7 @@ npm run test:run # Single run (for CI)
440
459
  - **css.tsx**: 91.43% statements
441
460
  - **functions.ts**: 90.91% statements
442
461
  - **menu-expando.tsx**: 90.12% statements
443
- - **config.client.tsx**: 100% statements
462
+ - **site-health-cloudwatch.tsx**: 88% statements
444
463
  - **loading.tsx**: 85.71% statements
445
464
  - **SaveLoadSection.tsx**: 84.85% statements
446
465
  - **table.tsx**: 84.48% statements
@@ -461,6 +480,7 @@ npm run test:run # Single run (for CI)
461
480
  - **componentMap.tsx**: 60% statements
462
481
  - **propTypeIntrospection.tsx**: 60% statements
463
482
  - **wordpress.functions.ts**: 51.43% statements
483
+ - **config.server.tsx**: 50% statements
464
484
  - **PageEngine.tsx**: 48% statements
465
485
  - **componentGeneration.tsx**: 38.89% statements
466
486
  - **socialcard.tsx**: 29.51% statements
@@ -187,6 +187,101 @@
187
187
  "countLogic": "count",
188
188
  "scoreLogic": "present",
189
189
  "displayTemplate": "{{count}} ItemProp tag(s) found"
190
+ },
191
+ "robots-meta-directives": {
192
+ "id": "robots-meta-directives",
193
+ "title": "Robots Meta Directives",
194
+ "description": "Checks for robots meta tags (noindex, nofollow, noarchive)",
195
+ "scoreDisplayMode": "binary",
196
+ "pattern": "<meta[^>]*name=[\"']robots[\"'][^>]*content=[\"'][^\"']*[\"'][^>]*>",
197
+ "countLogic": "count",
198
+ "scoreLogic": "present",
199
+ "displayTemplate": "{{count}} robots meta tag(s) found"
200
+ },
201
+ "hreflang-tags": {
202
+ "id": "hreflang-tags",
203
+ "title": "Hreflang Tags",
204
+ "description": "Checks for hreflang link tags for international SEO",
205
+ "scoreDisplayMode": "binary",
206
+ "pattern": "<link[^>]*rel=[\"']alternate[\"'][^>]*hreflang=[\"'][^\"']*[\"'][^>]*>",
207
+ "countLogic": "count",
208
+ "scoreLogic": "present",
209
+ "displayTemplate": "{{count}} hreflang link(s) found"
210
+ },
211
+ "indexability-status": {
212
+ "id": "indexability-status",
213
+ "title": "Indexability Status",
214
+ "description": "Checks for unintended noindex directives",
215
+ "scoreDisplayMode": "binary",
216
+ "pattern": "<meta[^>]*name=[\"']?robots[\"']?[^>]*content=[\"']?[^\"']*noindex[^\"']*[\"']?[^>]*>",
217
+ "countLogic": "count",
218
+ "scoreLogic": "exact",
219
+ "expectedCount": 0,
220
+ "displayTemplate": "{{count}} noindex directive(s) found"
221
+ },
222
+ "image-optimization": {
223
+ "id": "image-optimization",
224
+ "title": "Image Optimization",
225
+ "description": "Checks for lazy loading and modern image formats",
226
+ "scoreDisplayMode": "binary",
227
+ "pattern": "<img[^>]*loading=[\"']lazy[\"'][^>]*>|<img[^>]*\\.(webp|avif)[^>]*>",
228
+ "countLogic": "count",
229
+ "scoreLogic": "present",
230
+ "displayTemplate": "{{count}} optimized image(s) found"
231
+ },
232
+ "pagination-structure": {
233
+ "id": "pagination-structure",
234
+ "title": "Pagination Structure",
235
+ "description": "Checks for proper rel='next/prev' pagination links",
236
+ "scoreDisplayMode": "binary",
237
+ "pattern": "<link[^>]*rel=[\"']?(next|prev)[\"']?[^>]*href=[\"']?[^\"']*[\"']?[^>]*>",
238
+ "countLogic": "count",
239
+ "scoreLogic": "present",
240
+ "displayTemplate": "{{count}} pagination link(s) found"
241
+ },
242
+ "local-seo-elements": {
243
+ "id": "local-seo-elements",
244
+ "title": "Local SEO Elements",
245
+ "description": "Checks for local business schema markup",
246
+ "scoreDisplayMode": "binary",
247
+ "pattern": "(<script[^>]*type=[\"']application/ld\\+json[\"'][^>]*>[^<]*\"@type\"[^<]*\"LocalBusiness\"[^<]*</script>)|(<[^>]*itemtype=[\"'][^\"']*LocalBusiness[^\"']*[\"'][^>]*>)",
248
+ "countLogic": "count",
249
+ "scoreLogic": "present",
250
+ "displayTemplate": "{{count}} local business schema element(s) found"
251
+ },
252
+ "amp-validation": {
253
+ "id": "amp-validation",
254
+ "title": "AMP Validation",
255
+ "description": "Checks for Accelerated Mobile Pages implementation",
256
+ "scoreDisplayMode": "binary",
257
+ "pattern": "<html[^>]*amp[^>]*>|<script[^>]*src=[\"']?[^\"']*cdn\\.ampproject\\.org[^\"']*[\"']?[^>]*>",
258
+ "countLogic": "count",
259
+ "scoreLogic": "present",
260
+ "displayTemplate": "{{count}} AMP element(s) found"
261
+ },
262
+ "mobile-first-indexing": {
263
+ "id": "mobile-first-indexing",
264
+ "title": "Mobile-First Indexing",
265
+ "description": "Checks for mobile-friendly viewport and responsive design",
266
+ "scoreDisplayMode": "binary",
267
+ "dataCollector": "collectMobileFirstIndexingData",
268
+ "scorer": "calculateMobileFirstIndexingScore"
269
+ },
270
+ "international-seo": {
271
+ "id": "international-seo",
272
+ "title": "International SEO",
273
+ "description": "Checks for currency and locale detection elements",
274
+ "scoreDisplayMode": "binary",
275
+ "dataCollector": "collectInternationalSEOData",
276
+ "scorer": "calculateInternationalSEOData"
277
+ },
278
+ "faceted-navigation": {
279
+ "id": "faceted-navigation",
280
+ "title": "Faceted Navigation",
281
+ "description": "Checks for SEO-friendly filter and navigation URLs",
282
+ "scoreDisplayMode": "numeric",
283
+ "dataCollector": "collectFacetedNavigationData",
284
+ "scorer": "calculateFacetedNavigationScore"
190
285
  }
191
286
  }
192
287
  },
@@ -258,6 +353,22 @@
258
353
  "scoreDisplayMode": "binary",
259
354
  "dataCollector": null,
260
355
  "scorer": null
356
+ },
357
+ "gzip-compression": {
358
+ "id": "gzip-compression",
359
+ "title": "Gzip Compression",
360
+ "description": "Checks for server compression verification (requires HTTP headers analysis)",
361
+ "scoreDisplayMode": "binary",
362
+ "dataCollector": "collectGzipCompressionData",
363
+ "scorer": "calculateGzipCompressionScore"
364
+ },
365
+ "browser-caching": {
366
+ "id": "browser-caching",
367
+ "title": "Browser Caching",
368
+ "description": "Checks cache headers for static assets (requires HTTP headers analysis)",
369
+ "scoreDisplayMode": "binary",
370
+ "dataCollector": "collectBrowserCachingData",
371
+ "scorer": "calculateBrowserCachingScore"
261
372
  }
262
373
  }
263
374
  }
@@ -0,0 +1,142 @@
1
+ /**
2
+ * CloudWatch Health Check Integration Services
3
+ * Server-side utilities for Route53 health check data retrieval via CloudWatch
4
+ */
5
+ "use server";
6
+ import { CloudWatchClient, GetMetricDataCommand } from '@aws-sdk/client-cloudwatch';
7
+ import { RouteCache } from './site-health-cache';
8
+ // Cache for health check data (15 minutes)
9
+ const healthCheckCache = new RouteCache(15 * 60 * 1000);
10
+ /**
11
+ * Get health check data for a site using CloudWatch metrics
12
+ */
13
+ export async function getCloudwatchHealthCheckData(config, siteName, startDate, endDate) {
14
+ try {
15
+ // Check cache first
16
+ const cacheKey = `cloudwatch-${siteName}-${config.healthCheckId}-${startDate || 'default'}-${endDate || 'default'}`;
17
+ const cached = healthCheckCache.get(cacheKey);
18
+ if (cached) {
19
+ return { success: true, data: cached };
20
+ }
21
+ // Use CloudWatch to get historical health check data
22
+ const cloudWatchClient = new CloudWatchClient({
23
+ region: config.region || 'us-east-1'
24
+ });
25
+ // Set up date range
26
+ const endTime = endDate ? new Date(endDate) : new Date();
27
+ const startTime = startDate ? new Date(startDate) : new Date(endTime.getTime() - (30 * 24 * 60 * 60 * 1000)); // 30 days ago
28
+ // Add 1 day to end time to include the full end date
29
+ const endTimePlusOne = new Date(endTime);
30
+ endTimePlusOne.setDate(endTimePlusOne.getDate() + 1);
31
+ console.log(`CloudWatch: Fetching data for health check ${config.healthCheckId} from ${startTime.toISOString()} to ${endTimePlusOne.toISOString()}`);
32
+ const metricDataQuery = {
33
+ MetricDataQueries: [
34
+ {
35
+ Id: 'healthCheckStatus',
36
+ MetricStat: {
37
+ Metric: {
38
+ Namespace: 'AWS/Route53',
39
+ MetricName: 'HealthCheckStatus',
40
+ Dimensions: [
41
+ {
42
+ Name: 'HealthCheckId',
43
+ Value: config.healthCheckId
44
+ }
45
+ ]
46
+ },
47
+ Period: 3600, // 1 hour intervals
48
+ Stat: 'Average'
49
+ },
50
+ ReturnData: true
51
+ }
52
+ ],
53
+ StartTime: startTime,
54
+ EndTime: endTimePlusOne
55
+ };
56
+ const command = new GetMetricDataCommand(metricDataQuery);
57
+ const response = await cloudWatchClient.send(command);
58
+ console.log(`CloudWatch: Received ${response.MetricDataResults?.[0]?.Timestamps?.length || 0} data points`);
59
+ if (!response.MetricDataResults || response.MetricDataResults.length === 0) {
60
+ console.log(`CloudWatch: No metric data found for health check ${config.healthCheckId}`);
61
+ return {
62
+ success: false,
63
+ error: 'No health check metric data found'
64
+ };
65
+ }
66
+ const metricResult = response.MetricDataResults[0];
67
+ if (!metricResult.Timestamps || !metricResult.Values) {
68
+ console.log(`CloudWatch: No timestamps or values in metric data`);
69
+ return {
70
+ success: false,
71
+ error: 'No health check metric data available'
72
+ };
73
+ }
74
+ // Group data by date
75
+ const dateGroups = {};
76
+ metricResult.Timestamps.forEach((timestamp, index) => {
77
+ const date = timestamp.toISOString().split('T')[0]; // YYYY-MM-DD
78
+ const value = metricResult.Values[index];
79
+ if (!dateGroups[date]) {
80
+ dateGroups[date] = { success: 0, failure: 0 };
81
+ }
82
+ // CloudWatch returns 1 for healthy, 0 for unhealthy
83
+ if (value >= 0.5) { // Consider >= 0.5 as success
84
+ dateGroups[date].success++;
85
+ }
86
+ else {
87
+ dateGroups[date].failure++;
88
+ }
89
+ });
90
+ // Convert to data points
91
+ const data = Object.entries(dateGroups)
92
+ .map(([date, counts]) => {
93
+ const total = counts.success + counts.failure;
94
+ const successRate = total > 0 ? (counts.success / total) * 100 : 0;
95
+ return {
96
+ date,
97
+ successCount: counts.success,
98
+ failureCount: counts.failure,
99
+ totalChecks: total,
100
+ successRate: Math.round(successRate * 100) / 100 // Round to 2 decimal places
101
+ };
102
+ })
103
+ .sort((a, b) => a.date.localeCompare(b.date));
104
+ console.log(`CloudWatch: Processed ${data.length} date groups with data`);
105
+ // Fill in the date range with data points for each day
106
+ let filledData = [];
107
+ if (startDate && endDate) {
108
+ const start = new Date(startDate);
109
+ const end = new Date(endDate);
110
+ const dataMap = new Map(data.map(d => [d.date, d]));
111
+ for (let date = new Date(start); date <= end; date.setDate(date.getDate() + 1)) {
112
+ const dateStr = date.toISOString().split('T')[0];
113
+ const existingData = dataMap.get(dateStr);
114
+ if (existingData) {
115
+ filledData.push(existingData);
116
+ }
117
+ else {
118
+ filledData.push({
119
+ date: dateStr,
120
+ successCount: 0,
121
+ failureCount: 0,
122
+ totalChecks: 0,
123
+ successRate: 0
124
+ });
125
+ }
126
+ }
127
+ }
128
+ else {
129
+ filledData = data;
130
+ }
131
+ // Cache the result
132
+ healthCheckCache.set(cacheKey, filledData);
133
+ return { success: true, data: filledData };
134
+ }
135
+ catch (error) {
136
+ console.error('CloudWatch error:', error);
137
+ return {
138
+ success: false,
139
+ error: error instanceof Error ? error.message : 'Failed to fetch health check data from CloudWatch'
140
+ };
141
+ }
142
+ }
@@ -0,0 +1,44 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { ComposedChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from 'recharts';
4
+ import { SiteHealthTemplate } from './site-health-template';
5
+ export function SiteHealthCloudwatch({ siteName, startDate, endDate }) {
6
+ const fetchCloudwatchData = async (site) => {
7
+ const params = new URLSearchParams({ siteName: site });
8
+ if (startDate)
9
+ params.append('startDate', startDate);
10
+ if (endDate)
11
+ params.append('endDate', endDate);
12
+ const response = await fetch(`/api/site-health/cloudwatch?${params.toString()}`);
13
+ if (!response.ok) {
14
+ throw new Error(`Failed to fetch CloudWatch data: ${response.status}`);
15
+ }
16
+ const result = await response.json();
17
+ if (!result.success) {
18
+ if (result.error?.includes('Health Check ID not configured')) {
19
+ throw new Error('Route53 Health Check ID not configured for this site');
20
+ }
21
+ else {
22
+ throw new Error(result.error || 'Failed to load CloudWatch health check data');
23
+ }
24
+ }
25
+ return result.data;
26
+ };
27
+ return (_jsx(SiteHealthTemplate, { siteName: siteName, title: "CloudWatch Uptime", columnSpan: 2, fetchData: fetchCloudwatchData, children: (data) => {
28
+ if (!data || data.length === 0) {
29
+ return (_jsx("div", { className: "flex items-center justify-center h-64", children: _jsx("div", { className: "text-gray-500", children: "No uptime data available. Route53 health checks may not be configured to send metrics to CloudWatch." }) }));
30
+ }
31
+ // Check if all data points have zero checks (no actual data)
32
+ const hasActualData = data.some(point => point.totalChecks > 0);
33
+ if (!hasActualData) {
34
+ return (_jsx("div", { className: "flex items-center justify-center h-64", children: _jsxs("div", { className: "text-gray-500", children: ["Health check exists but has no metric data in CloudWatch for the selected period.", _jsx("br", {}), "Route53 health checks must be configured to send metrics to CloudWatch for historical data."] }) }));
35
+ }
36
+ return (_jsx("div", { children: _jsx("div", { style: { width: '100%', height: '400px', border: '1px solid #ddd' }, children: _jsx(ResponsiveContainer, { width: "100%", height: "100%", children: _jsxs(ComposedChart, { data: data, margin: { top: 40, right: 30, left: 20, bottom: 5 }, children: [_jsx("text", { x: "50%", y: 20, textAnchor: "middle", fontSize: "16", fontWeight: "bold", fill: "#374151", children: "CloudWatch Health Check Availability Over Time" }), _jsx(CartesianGrid, { strokeDasharray: "3 3" }), _jsx(XAxis, { dataKey: "date", tick: { fontSize: 12 }, angle: -45, textAnchor: "end", height: 60 }), _jsx(YAxis, { tick: { fontSize: 12 }, label: { value: 'Check Count', angle: -90, position: 'insideLeft' } }), _jsx(Tooltip, { formatter: (value, name) => [
37
+ value?.toLocaleString() || '0',
38
+ name || 'Unknown'
39
+ ], labelFormatter: (label) => `Date: ${label}` }), _jsx(Legend, { wrapperStyle: {
40
+ fontSize: '12px',
41
+ paddingTop: '10px'
42
+ } }), _jsx(Bar, { dataKey: "successCount", stackId: "checks", fill: "#10b981", name: "Successful Checks", radius: [2, 2, 0, 0] }), _jsx(Bar, { dataKey: "failureCount", stackId: "checks", fill: "#ef4444", name: "Failed Checks", radius: [2, 2, 0, 0] })] }, `cloudwatch-chart-${data.length}`) }) }) }));
43
+ } }));
44
+ }
@@ -19,7 +19,12 @@ const dataCollectors = {
19
19
  collectSemanticTagsData,
20
20
  collectTitleTagsData,
21
21
  collectMetaKeywordsData,
22
- collectMetaDescriptionsData
22
+ collectMetaDescriptionsData,
23
+ collectMobileFirstIndexingData,
24
+ collectInternationalSEOData,
25
+ collectFacetedNavigationData,
26
+ collectBrowserCachingData,
27
+ collectGzipCompressionData
23
28
  };
24
29
  /**
25
30
  * Registry of scoring functions
@@ -28,7 +33,12 @@ const scorers = {
28
33
  calculateSemanticTagsScore,
29
34
  calculateTitleTagsScore,
30
35
  calculateMetaKeywordsScore,
31
- calculateMetaDescriptionsScore
36
+ calculateMetaDescriptionsScore,
37
+ calculateMobileFirstIndexingScore,
38
+ calculateInternationalSEOData,
39
+ calculateFacetedNavigationScore,
40
+ calculateBrowserCachingScore,
41
+ calculateGzipCompressionScore
32
42
  };
33
43
  /**
34
44
  * Data collection functions
@@ -229,6 +239,262 @@ function calculateMetaDescriptionsScore(descriptionData) {
229
239
  }
230
240
  };
231
241
  }
242
+ /**
243
+ * Mobile-First Indexing Data Collector
244
+ */
245
+ function collectMobileFirstIndexingData(html) {
246
+ const hasViewport = /<meta[^>]*name=["']viewport["'][^>]*content=["'][^"']*["'][^>]*>/i.test(html);
247
+ const hasResponsiveMeta = /<meta[^>]*name=["']viewport["'][^>]*content=["'][^"']*width=device-width[^"']*["'][^>]*>/i.test(html);
248
+ const hasMobileStyles = /@media[^}]*max-width[^}]*mobile|phone|tablet/i.test(html) || /<link[^>]*media=["'][^"']*handheld[^"']*["'][^>]*>/i.test(html);
249
+ return {
250
+ hasViewport,
251
+ hasResponsiveMeta,
252
+ hasMobileStyles,
253
+ score: (hasViewport && hasResponsiveMeta) ? 1 : 0
254
+ };
255
+ }
256
+ /**
257
+ * Mobile-First Indexing Scorer
258
+ */
259
+ function calculateMobileFirstIndexingScore(data) {
260
+ const score = data.score;
261
+ const displayValue = score ? 'Mobile-friendly viewport detected' : 'Missing or inadequate viewport configuration';
262
+ return {
263
+ score,
264
+ displayValue,
265
+ details: {
266
+ items: [
267
+ { type: 'viewport', present: data.hasViewport, optimal: data.hasResponsiveMeta },
268
+ { type: 'responsive-styles', present: data.hasMobileStyles }
269
+ ]
270
+ }
271
+ };
272
+ }
273
+ /**
274
+ * International SEO Data Collector
275
+ */
276
+ function collectInternationalSEOData(html) {
277
+ const currencySymbols = html.match(/[£€¥$₹₽₩₦₨₪₫₡₵₺₴₸₼₲₱₭₯₰₳₶₷₹₻₽₾₿]/g) || [];
278
+ const langAttributes = html.match(/lang=["'][^"']*["']/gi) || [];
279
+ const localeIndicators = html.match(/(en-US|en-GB|fr-FR|de-DE|es-ES|it-IT|pt-BR|ja-JP|ko-KR|zh-CN|zh-TW|ru-RU|ar-SA|hi-IN)/gi) || [];
280
+ return {
281
+ currencyCount: currencySymbols.length,
282
+ langAttributes: langAttributes.length,
283
+ localeIndicators: localeIndicators.length,
284
+ hasInternationalElements: currencySymbols.length > 0 || langAttributes.length > 0 || localeIndicators.length > 0
285
+ };
286
+ }
287
+ /**
288
+ * International SEO Scorer
289
+ */
290
+ function calculateInternationalSEOData(data) {
291
+ const score = data.hasInternationalElements ? 1 : 0;
292
+ const displayValue = score ? `${data.currencyCount} currencies, ${data.langAttributes} lang attributes, ${data.localeIndicators} locale indicators found` : 'No international SEO elements detected';
293
+ return {
294
+ score,
295
+ displayValue,
296
+ details: {
297
+ items: [
298
+ { type: 'currencies', count: data.currencyCount },
299
+ { type: 'lang-attributes', count: data.langAttributes },
300
+ { type: 'locale-indicators', count: data.localeIndicators }
301
+ ]
302
+ }
303
+ };
304
+ }
305
+ /**
306
+ * Faceted Navigation Data Collector
307
+ */
308
+ function collectFacetedNavigationData(html) {
309
+ // Look for URL patterns that suggest faceted navigation (query parameters, filters)
310
+ const filterUrls = html.match(/href=["'][^"']*[?&][^"']*filter[^"']*["']/gi) || [];
311
+ const sortUrls = html.match(/href=["'][^"']*[?&][^"']*sort[^"']*["']/gi) || [];
312
+ const categoryUrls = html.match(/href=["'][^"']*[?&][^"']*category[^"']*["']/gi) || [];
313
+ const cleanUrls = html.match(/href=["'][^"']*\/[^?]*\/[^?]*["']/gi) || [];
314
+ return {
315
+ filterUrls: filterUrls.length,
316
+ sortUrls: sortUrls.length,
317
+ categoryUrls: categoryUrls.length,
318
+ cleanUrls: cleanUrls.length,
319
+ hasFacetedNavigation: (filterUrls.length + sortUrls.length + categoryUrls.length) > 0
320
+ };
321
+ }
322
+ /**
323
+ * Faceted Navigation Scorer
324
+ */
325
+ function calculateFacetedNavigationScore(data) {
326
+ const totalFaceted = data.filterUrls + data.sortUrls + data.categoryUrls;
327
+ const totalUrls = data.cleanUrls + totalFaceted;
328
+ // Calculate score as percentage of clean URLs vs total URLs
329
+ // Higher score = more clean URLs (better for SEO)
330
+ let score = 1; // Default to 100% if no URLs found
331
+ let displayValue = 'No URLs detected';
332
+ if (totalUrls > 0) {
333
+ score = data.cleanUrls / totalUrls; // Ratio of clean URLs to total URLs
334
+ const percentage = Math.round(score * 100);
335
+ if (totalFaceted === 0) {
336
+ displayValue = `100% clean URLs - excellent for SEO`;
337
+ }
338
+ else if (score >= 0.8) {
339
+ displayValue = `${percentage}% clean URLs (${data.cleanUrls} clean, ${totalFaceted} faceted)`;
340
+ }
341
+ else if (score >= 0.5) {
342
+ displayValue = `${percentage}% clean URLs - consider reducing faceted navigation (${data.cleanUrls} clean, ${totalFaceted} faceted)`;
343
+ }
344
+ else {
345
+ displayValue = `${percentage}% clean URLs - high faceted navigation may hurt SEO (${data.cleanUrls} clean, ${totalFaceted} faceted)`;
346
+ }
347
+ }
348
+ return {
349
+ score,
350
+ displayValue,
351
+ details: {
352
+ items: [
353
+ { type: 'clean-urls', count: data.cleanUrls },
354
+ { type: 'filter-urls', count: data.filterUrls },
355
+ { type: 'sort-urls', count: data.sortUrls },
356
+ { type: 'category-urls', count: data.categoryUrls }
357
+ ]
358
+ }
359
+ };
360
+ }
361
+ /**
362
+ * Browser Caching Data Collector
363
+ */
364
+ function collectBrowserCachingData(response) {
365
+ // Get response headers from Puppeteer response object
366
+ const headers = {};
367
+ try {
368
+ const rawHeaders = response.headers();
369
+ for (const [key, value] of Object.entries(rawHeaders)) {
370
+ headers[key.toLowerCase()] = value;
371
+ }
372
+ }
373
+ catch (error) {
374
+ console.warn('Failed to get response headers:', error);
375
+ }
376
+ const cacheControl = headers['cache-control'] || '';
377
+ const expires = headers['expires'] || '';
378
+ const lastModified = headers['last-modified'] || '';
379
+ const etag = headers['etag'] || '';
380
+ const age = headers['age'] || '';
381
+ return {
382
+ cacheControl,
383
+ expires,
384
+ lastModified,
385
+ etag,
386
+ age,
387
+ hasCachingHeaders: !!(cacheControl || expires || lastModified || etag)
388
+ };
389
+ }
390
+ /**
391
+ * Browser Caching Scorer
392
+ */
393
+ function calculateBrowserCachingScore(data) {
394
+ let score = 0;
395
+ let displayValue = 'No caching headers detected';
396
+ const issues = [];
397
+ const goodHeaders = [];
398
+ // Check Cache-Control header
399
+ if (data.cacheControl) {
400
+ goodHeaders.push('Cache-Control');
401
+ // Check for good caching directives
402
+ if (data.cacheControl.includes('max-age') && !data.cacheControl.includes('no-cache') && !data.cacheControl.includes('no-store')) {
403
+ score = 1;
404
+ displayValue = 'Good caching headers found';
405
+ }
406
+ else if (data.cacheControl.includes('no-cache') || data.cacheControl.includes('no-store')) {
407
+ issues.push('Cache-Control prevents caching');
408
+ }
409
+ }
410
+ // Check Expires header
411
+ if (data.expires) {
412
+ goodHeaders.push('Expires');
413
+ const expiresDate = new Date(data.expires);
414
+ const now = new Date();
415
+ if (expiresDate > now) {
416
+ if (score === 0)
417
+ score = 0.5; // Partial credit for expires header
418
+ }
419
+ }
420
+ // Check other headers
421
+ if (data.lastModified)
422
+ goodHeaders.push('Last-Modified');
423
+ if (data.etag)
424
+ goodHeaders.push('ETag');
425
+ if (data.age)
426
+ goodHeaders.push('Age');
427
+ if (goodHeaders.length > 0 && score === 0) {
428
+ score = 0.5; // Partial credit for having some caching headers
429
+ displayValue = `Basic caching headers found: ${goodHeaders.join(', ')}`;
430
+ }
431
+ if (issues.length > 0) {
432
+ displayValue += ` (${issues.join(', ')})`;
433
+ }
434
+ return {
435
+ score,
436
+ displayValue,
437
+ details: {
438
+ headers: {
439
+ 'cache-control': data.cacheControl,
440
+ 'expires': data.expires,
441
+ 'last-modified': data.lastModified,
442
+ 'etag': data.etag,
443
+ 'age': data.age
444
+ }
445
+ }
446
+ };
447
+ }
448
+ /**
449
+ * Gzip Compression Data Collector
450
+ */
451
+ function collectGzipCompressionData(response) {
452
+ // Get response headers from Puppeteer response object
453
+ const headers = {};
454
+ try {
455
+ const rawHeaders = response.headers();
456
+ for (const [key, value] of Object.entries(rawHeaders)) {
457
+ headers[key.toLowerCase()] = value;
458
+ }
459
+ }
460
+ catch (error) {
461
+ console.warn('Failed to get response headers:', error);
462
+ }
463
+ const contentEncoding = headers['content-encoding'] || '';
464
+ const contentLength = headers['content-length'] || '';
465
+ const transferEncoding = headers['transfer-encoding'] || '';
466
+ return {
467
+ contentEncoding,
468
+ contentLength,
469
+ transferEncoding,
470
+ isCompressed: contentEncoding.includes('gzip') || contentEncoding.includes('deflate') || transferEncoding.includes('chunked')
471
+ };
472
+ }
473
+ /**
474
+ * Gzip Compression Scorer
475
+ */
476
+ function calculateGzipCompressionScore(data) {
477
+ let score = 0;
478
+ let displayValue = 'No compression detected';
479
+ if (data.isCompressed) {
480
+ score = 1;
481
+ displayValue = `Compression enabled: ${data.contentEncoding || data.transferEncoding}`;
482
+ }
483
+ else {
484
+ displayValue = 'Response not compressed - consider enabling gzip compression';
485
+ }
486
+ return {
487
+ score,
488
+ displayValue,
489
+ details: {
490
+ headers: {
491
+ 'content-encoding': data.contentEncoding,
492
+ 'content-length': data.contentLength,
493
+ 'transfer-encoding': data.transferEncoding
494
+ }
495
+ }
496
+ };
497
+ }
232
498
  /**
233
499
  * Crawl the site to discover internal pages
234
500
  */
@@ -387,12 +653,19 @@ async function analyzeSinglePage(url) {
387
653
  const collector = dataCollectors[metric.dataCollector];
388
654
  const scorer = scorers[metric.scorer];
389
655
  if (collector && scorer) {
390
- const rawData = collector(html, pageData.title);
656
+ // Pass response object for collectors that need headers (like browser caching and gzip compression)
657
+ const rawData = (metric.dataCollector === 'collectBrowserCachingData' || metric.dataCollector === 'collectGzipCompressionData')
658
+ ? collector(html, pageData.title, response)
659
+ : collector(html, pageData.title);
391
660
  const result = scorer(rawData);
392
661
  score = result.score;
393
662
  displayValue = result.displayValue;
394
663
  details = result.details;
395
664
  }
665
+ else {
666
+ score = 0;
667
+ displayValue = `Data collector or scorer not found: ${metric.dataCollector}/${metric.scorer}`;
668
+ }
396
669
  }
397
670
  else if (metric.pattern) {
398
671
  // Use pattern-based analysis
@@ -401,6 +674,11 @@ async function analyzeSinglePage(url) {
401
674
  displayValue = result.displayValue;
402
675
  details = result.details;
403
676
  }
677
+ else {
678
+ // Neither data collector/scorer nor pattern available
679
+ score = 0;
680
+ displayValue = 'Configuration incomplete - no pattern or data collector defined';
681
+ }
404
682
  // Override H1 and H2 results with direct DOM counts for accuracy and speed
405
683
  if (metric.id === 'h1-tags') {
406
684
  score = pageData.h1Count === (metric.expectedCount || 1) ? 1 : 0;
@@ -520,6 +798,22 @@ async function performSiteWideAudits(baseUrl) {
520
798
  displayValue = 'Manifest.webmanifest not accessible';
521
799
  }
522
800
  break;
801
+ case 'gzip-compression':
802
+ score = 0; // Placeholder - would need HTTP header analysis
803
+ displayValue = 'Requires server header analysis';
804
+ break;
805
+ case 'browser-caching':
806
+ score = 0; // Placeholder - would need HTTP header analysis
807
+ displayValue = 'Requires server header analysis';
808
+ break;
809
+ case 'duplicate-content-detection':
810
+ score = 0; // Placeholder - would need multi-page content analysis
811
+ displayValue = 'Requires comprehensive site crawl';
812
+ break;
813
+ case 'safe-browsing-status':
814
+ score = 0; // Placeholder - would need external API
815
+ displayValue = 'Requires Google Safe Browsing API';
816
+ break;
523
817
  default:
524
818
  score = 0;
525
819
  displayValue = 'Not implemented';
@@ -1,4 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /* eslint-disable @typescript-eslint/no-unused-vars */
2
3
  import React, { useState, useEffect } from 'react';
3
4
  export function LinkedIn() {
4
5
  const [data, setData] = useState(null);
package/dist/index.js CHANGED
@@ -88,6 +88,7 @@ export * from './components/admin/site-health/site-health-github';
88
88
  export * from './components/admin/site-health/site-health-google-analytics';
89
89
  export * from './components/admin/site-health/site-health-google-search-console';
90
90
  export * from './components/admin/site-health/site-health-on-site-seo';
91
+ export * from './components/admin/site-health/site-health-cloudwatch';
91
92
  export * from './components/admin/site-health/seo-constants';
92
93
  export * from './components/admin/site-health/site-health-overview';
93
94
  export * from './components/admin/site-health/site-health-performance';
@@ -14,6 +14,7 @@ export * from './components/admin/site-health/site-health-google-analytics.integ
14
14
  export * from './components/admin/site-health/site-health-google-search-console.integration';
15
15
  export * from './components/admin/site-health/site-health-indicators';
16
16
  export * from './components/admin/site-health/site-health-on-site-seo.integration';
17
+ export * from './components/admin/site-health/site-health-cloudwatch.integration';
17
18
  export * from './components/admin/site-health/seo-constants';
18
19
  export * from './components/admin/site-health/site-health-security.integration';
19
20
  export * from './components/admin/site-health/site-health-performance';
@@ -0,0 +1,8 @@
1
+ interface SiteHealthCloudwatchProps {
2
+ siteName: string;
3
+ startDate?: string;
4
+ endDate?: string;
5
+ }
6
+ export declare function SiteHealthCloudwatch({ siteName, startDate, endDate }: SiteHealthCloudwatchProps): import("react/jsx-runtime").JSX.Element;
7
+ export {};
8
+ //# sourceMappingURL=site-health-cloudwatch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"site-health-cloudwatch.d.ts","sourceRoot":"","sources":["../../../../../src/components/admin/site-health/site-health-cloudwatch.tsx"],"names":[],"mappings":"AAcA,UAAU,yBAAyB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,wBAAgB,oBAAoB,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,EAAE,yBAAyB,2CAiH/F"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * CloudWatch Health Check Integration Services
3
+ * Server-side utilities for Route53 health check data retrieval via CloudWatch
4
+ */
5
+ export interface CloudwatchHealthCheckConfig {
6
+ healthCheckId: string;
7
+ region?: string;
8
+ }
9
+ export interface HealthCheckDataPoint {
10
+ date: string;
11
+ successCount: number;
12
+ failureCount: number;
13
+ totalChecks: number;
14
+ successRate: number;
15
+ }
16
+ export interface CloudwatchHealthCheckResponse {
17
+ success: boolean;
18
+ data?: HealthCheckDataPoint[];
19
+ error?: string;
20
+ }
21
+ /**
22
+ * Get health check data for a site using CloudWatch metrics
23
+ */
24
+ export declare function getCloudwatchHealthCheckData(config: CloudwatchHealthCheckConfig, siteName: string, startDate?: string, endDate?: string): Promise<CloudwatchHealthCheckResponse>;
25
+ //# sourceMappingURL=site-health-cloudwatch.integration.d.ts.map
@@ -0,0 +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;AAOH,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,CAiJxC"}
@@ -1 +1 @@
1
- {"version":3,"file":"site-health-on-site-seo.integration.d.ts","sourceRoot":"","sources":["../../../../../src/components/admin/site-health/site-health-on-site-seo.integration.ts"],"names":[],"mappings":"AA+RA,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,gBAAgB,EAAE,QAAQ,GAAG,eAAe,CAAC;IAC7C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,SAAS,GAAG,SAAS,CAAC;IAChC,OAAO,CAAC,EAAE;QACR,KAAK,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;KACxC,CAAC;CACH;AAED,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,cAAc,EAAE,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,aAAa,EAAE,YAAY,EAAE,CAAC;IAC9B,YAAY,EAAE,cAAc,EAAE,CAAC;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAuYD;;GAEG;AACH,wBAAsB,wBAAwB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAwFtF"}
1
+ {"version":3,"file":"site-health-on-site-seo.integration.d.ts","sourceRoot":"","sources":["../../../../../src/components/admin/site-health/site-health-on-site-seo.integration.ts"],"names":[],"mappings":"AA6jBA,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,gBAAgB,EAAE,QAAQ,GAAG,eAAe,CAAC;IAC7C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,SAAS,GAAG,SAAS,CAAC;IAChC,OAAO,CAAC,EAAE;QACR,KAAK,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;KACxC,CAAC;CACH;AAED,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,cAAc,EAAE,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,aAAa,EAAE,YAAY,EAAE,CAAC;IAC9B,YAAY,EAAE,cAAc,EAAE,CAAC;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAqaD;;GAEG;AACH,wBAAsB,wBAAwB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAwFtF"}
@@ -1 +1 @@
1
- {"version":3,"file":"pixelated.linkedin1.d.ts","sourceRoot":"","sources":["../../../../src/components/cms/pixelated.linkedin1.js"],"names":[],"mappings":"AAEA,oEAiGC"}
1
+ {"version":3,"file":"pixelated.linkedin1.d.ts","sourceRoot":"","sources":["../../../../src/components/cms/pixelated.linkedin1.js"],"names":[],"mappings":"AAGA,oEAiGC"}
@@ -87,6 +87,7 @@ export * from "./components/admin/site-health/site-health-github";
87
87
  export * from "./components/admin/site-health/site-health-google-analytics";
88
88
  export * from "./components/admin/site-health/site-health-google-search-console";
89
89
  export * from "./components/admin/site-health/site-health-on-site-seo";
90
+ export * from "./components/admin/site-health/site-health-cloudwatch";
90
91
  export * from "./components/admin/site-health/seo-constants";
91
92
  export * from "./components/admin/site-health/site-health-overview";
92
93
  export * from "./components/admin/site-health/site-health-performance";
@@ -10,6 +10,7 @@ export * from "./components/admin/site-health/site-health-google-analytics.integ
10
10
  export * from "./components/admin/site-health/site-health-google-search-console.integration";
11
11
  export * from "./components/admin/site-health/site-health-indicators";
12
12
  export * from "./components/admin/site-health/site-health-on-site-seo.integration";
13
+ export * from "./components/admin/site-health/site-health-cloudwatch.integration";
13
14
  export * from "./components/admin/site-health/seo-constants";
14
15
  export * from "./components/admin/site-health/site-health-security.integration";
15
16
  export * from "./components/admin/site-health/site-health-performance";
@@ -62,4 +62,8 @@ export declare const UptimeHealthCard: {
62
62
  (): import("react/jsx-runtime").JSX.Element;
63
63
  storyName: string;
64
64
  };
65
+ export declare const CloudwatchHealthCard: {
66
+ (): import("react/jsx-runtime").JSX.Element;
67
+ storyName: string;
68
+ };
65
69
  //# sourceMappingURL=site-health.stories.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"site-health.stories.d.ts","sourceRoot":"","sources":["../../../../src/stories/admin/site-health.stories.tsx"],"names":[],"mappings":";;;;;;;;;;;AAeA,wBAUE;AAwEF,eAAO,MAAM,iBAAiB;;;CA6H7B,CAAC;AAIF,eAAO,MAAM,kBAAkB;;;CAkH9B,CAAC;AAIF,eAAO,MAAM,oBAAoB;;;CAkChC,CAAC;AAIF,eAAO,MAAM,qBAAqB;;;CAgCjC,CAAC;AAIF,eAAO,MAAM,kBAAkB;;;CA0B9B,CAAC;AAIF,eAAO,MAAM,aAAa;;;CA0BzB,CAAC;AAIF,eAAO,MAAM,uBAAuB;;;CA0BnC,CAAC;AAIF,eAAO,MAAM,yBAAyB;;;CAyBrC,CAAC;AAIF,eAAO,MAAM,6BAA6B;;;CAyBzC,CAAC;AAIF,eAAO,MAAM,mBAAmB;;;CA0B/B,CAAC;AAIF,eAAO,MAAM,mCAAmC;;;CA0B/C,CAAC;AAIF,eAAO,MAAM,gBAAgB;;;CAyB5B,CAAC;AAIF,eAAO,MAAM,gBAAgB;;;CA0B5B,CAAC"}
1
+ {"version":3,"file":"site-health.stories.d.ts","sourceRoot":"","sources":["../../../../src/stories/admin/site-health.stories.tsx"],"names":[],"mappings":";;;;;;;;;;;AAgBA,wBAUE;AAwEF,eAAO,MAAM,iBAAiB;;;CA6H7B,CAAC;AAIF,eAAO,MAAM,kBAAkB;;;CAkH9B,CAAC;AAIF,eAAO,MAAM,oBAAoB;;;CAkChC,CAAC;AAIF,eAAO,MAAM,qBAAqB;;;CAgCjC,CAAC;AAIF,eAAO,MAAM,kBAAkB;;;CA0B9B,CAAC;AAIF,eAAO,MAAM,aAAa;;;CA0BzB,CAAC;AAIF,eAAO,MAAM,uBAAuB;;;CA0BnC,CAAC;AAIF,eAAO,MAAM,yBAAyB;;;CAyBrC,CAAC;AAIF,eAAO,MAAM,6BAA6B;;;CAyBzC,CAAC;AAIF,eAAO,MAAM,mBAAmB;;;CA0B/B,CAAC;AAIF,eAAO,MAAM,mCAAmC;;;CA0B/C,CAAC;AAIF,eAAO,MAAM,gBAAgB;;;CAyB5B,CAAC;AAIF,eAAO,MAAM,gBAAgB;;;CA0B5B,CAAC;AAIF,eAAO,MAAM,oBAAoB;;;CA+BhC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=site-health-cloudwatch.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"site-health-cloudwatch.test.d.ts","sourceRoot":"","sources":["../../../src/tests/site-health-cloudwatch.test.tsx"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=site-health-on-site-seo.integration.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"site-health-on-site-seo.integration.test.d.ts","sourceRoot":"","sources":["../../../src/tests/site-health-on-site-seo.integration.test.ts"],"names":[],"mappings":""}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pixelated-tech/components",
3
- "version": "3.4.1",
3
+ "version": "3.4.3",
4
4
  "private": false,
5
5
  "author": {
6
6
  "name": "Pixelated Technologies",
@@ -66,7 +66,7 @@
66
66
  "copy2pixelatedadmin": "rm -rf ../pixelated-admin/node_modules/@pixelated-tech/components/dist && cp -r dist ../pixelated-admin/node_modules/@pixelated-tech/components/",
67
67
  "copy2all": "npm run copy2brianwhaley && npm run copy2informationfocus && npm run copy2oaktreelandscaping && npm run copy2palmettoepoxy && npm run copy2pixelated && npm run copy2pixelvivid && npm run copy2pixelatedtest && npm run copy2template && npm run copy2pixelatedadmin",
68
68
  "storybook": "rm -rf node_modules/.cache && storybook dev -p 6006",
69
- "buildStorybook": "rm -rf node_modules/.cache && storybook build",
69
+ "buildStorybook": "rm -rf node_modules/.cache && NODE_OPTIONS=\"--max-old-space-size=4096\" storybook build",
70
70
  "test": "vitest",
71
71
  "test:ui": "vitest --ui",
72
72
  "test:coverage": "vitest run --coverage",
@@ -79,6 +79,7 @@
79
79
  "build-webpack-rsync": "rsync -a --include='*' --include='*/' src/css dist/css"
80
80
  },
81
81
  "dependencies": {
82
+ "@aws-sdk/client-cloudwatch": "^3.958.0",
82
83
  "@aws-sdk/client-route-53": "^3.958.0",
83
84
  "date-fns": "^4.1.0",
84
85
  "globals": "^17.0.0",