@pixelated-tech/components 3.3.6 → 3.4.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.
Files changed (148) hide show
  1. package/README.COMPONENTS.md +126 -0
  2. package/README.md +14 -7
  3. package/dist/components/admin/componentusage/componentAnalysis.js +144 -0
  4. package/dist/components/admin/componentusage/componentDiscovery.js +85 -0
  5. package/dist/components/admin/deploy/deployment.integration.js +170 -0
  6. package/dist/components/admin/site-health/google-api-auth.js +69 -0
  7. package/dist/components/admin/site-health/seo-metrics.config.json +265 -0
  8. package/dist/components/admin/site-health/site-health-accessibility.js +158 -0
  9. package/dist/components/admin/site-health/site-health-axe-core.integration.js +119 -0
  10. package/dist/components/admin/site-health/site-health-axe-core.js +53 -0
  11. package/dist/components/admin/site-health/site-health-cache.js +23 -0
  12. package/dist/components/admin/site-health/site-health-core-web-vitals.integration.js +208 -0
  13. package/dist/components/admin/site-health/site-health-dependency-vulnerabilities.js +38 -0
  14. package/dist/components/admin/site-health/site-health-github.integration.js +81 -0
  15. package/dist/components/admin/site-health/site-health-github.js +34 -0
  16. package/dist/components/admin/site-health/site-health-google-analytics.integration.js +112 -0
  17. package/dist/components/admin/site-health/site-health-google-analytics.js +43 -0
  18. package/dist/components/admin/site-health/site-health-google-search-console.integration.js +118 -0
  19. package/dist/components/admin/site-health/site-health-google-search-console.js +43 -0
  20. package/dist/components/admin/site-health/site-health-indicators.js +71 -0
  21. package/dist/components/admin/site-health/site-health-on-site-seo.integration.js +578 -0
  22. package/dist/components/admin/site-health/site-health-on-site-seo.js +204 -0
  23. package/dist/components/admin/site-health/site-health-overview.js +65 -0
  24. package/dist/components/admin/site-health/site-health-performance.js +191 -0
  25. package/dist/components/admin/site-health/site-health-security.integration.js +109 -0
  26. package/dist/components/admin/site-health/site-health-security.js +169 -0
  27. package/dist/components/admin/site-health/site-health-seo.js +124 -0
  28. package/dist/components/admin/site-health/site-health-template.js +62 -0
  29. package/dist/components/admin/site-health/site-health-types.js +1 -0
  30. package/dist/components/admin/site-health/site-health-uptime.integration.js +29 -0
  31. package/dist/components/admin/site-health/site-health-uptime.js +30 -0
  32. package/dist/components/admin/site-health/site-health.css +427 -0
  33. package/dist/components/admin/sites/sites.integration.js +117 -0
  34. package/dist/components/cms/contentful.management.js +104 -0
  35. package/dist/components/shoppingcart/shipping.from.json +101 -0
  36. package/dist/components/shoppingcart/shipping.parcel.json +112 -0
  37. package/dist/components/shoppingcart/shipping.to.json +422 -0
  38. package/dist/components/shoppingcart/shoppingCartDiscountCodes.json +26 -0
  39. package/dist/components/shoppingcart/shoppingcart.components.js +1 -1
  40. package/dist/components/sitebuilder/config/ConfigBuilder.js +36 -140
  41. package/dist/components/sitebuilder/config/siteinfo-form.json +200 -0
  42. package/dist/components/sitebuilder/config/visualdesignform.json +244 -0
  43. package/dist/components/structured/buzzwordbingo.js +3 -2
  44. package/dist/data/404-data.json +128 -102
  45. package/dist/data/flickr.json +25 -0
  46. package/dist/data/form.json +368 -368
  47. package/dist/data/recipes.json +3251 -3251
  48. package/dist/data/references.json +138 -137
  49. package/dist/data/requestform.json +111 -0
  50. package/dist/data/requests.json +136 -135
  51. package/dist/data/resume.json +2573 -2575
  52. package/dist/data/routes.json +238 -238
  53. package/dist/data/routes2.json +141 -140
  54. package/dist/index.js +16 -3
  55. package/dist/index.server.js +36 -15
  56. package/dist/types/components/admin/componentusage/componentAnalysis.d.ts +35 -0
  57. package/dist/types/components/admin/componentusage/componentAnalysis.d.ts.map +1 -0
  58. package/dist/types/components/admin/componentusage/componentDiscovery.d.ts +10 -0
  59. package/dist/types/components/admin/componentusage/componentDiscovery.d.ts.map +1 -0
  60. package/dist/types/components/admin/deploy/deployment.integration.d.ts +26 -0
  61. package/dist/types/components/admin/deploy/deployment.integration.d.ts.map +1 -0
  62. package/dist/types/components/admin/site-health/google-api-auth.d.ts +37 -0
  63. package/dist/types/components/admin/site-health/google-api-auth.d.ts.map +1 -0
  64. package/dist/types/components/admin/site-health/site-health-accessibility.d.ts +6 -0
  65. package/dist/types/components/admin/site-health/site-health-accessibility.d.ts.map +1 -0
  66. package/dist/types/components/admin/site-health/site-health-axe-core.d.ts +6 -0
  67. package/dist/types/components/admin/site-health/site-health-axe-core.d.ts.map +1 -0
  68. package/dist/types/components/admin/site-health/site-health-axe-core.integration.d.ts +63 -0
  69. package/dist/types/components/admin/site-health/site-health-axe-core.integration.d.ts.map +1 -0
  70. package/dist/types/components/admin/site-health/site-health-cache.d.ts +12 -0
  71. package/dist/types/components/admin/site-health/site-health-cache.d.ts.map +1 -0
  72. package/dist/types/components/admin/site-health/site-health-core-web-vitals.integration.d.ts +3 -0
  73. package/dist/types/components/admin/site-health/site-health-core-web-vitals.integration.d.ts.map +1 -0
  74. package/dist/types/components/admin/site-health/site-health-dependency-vulnerabilities.d.ts +6 -0
  75. package/dist/types/components/admin/site-health/site-health-dependency-vulnerabilities.d.ts.map +1 -0
  76. package/dist/types/components/admin/site-health/site-health-github.d.ts +8 -0
  77. package/dist/types/components/admin/site-health/site-health-github.d.ts.map +1 -0
  78. package/dist/types/components/admin/site-health/site-health-github.integration.d.ts +26 -0
  79. package/dist/types/components/admin/site-health/site-health-github.integration.d.ts.map +1 -0
  80. package/dist/types/components/admin/site-health/site-health-google-analytics.d.ts +8 -0
  81. package/dist/types/components/admin/site-health/site-health-google-analytics.d.ts.map +1 -0
  82. package/dist/types/components/admin/site-health/site-health-google-analytics.integration.d.ts +26 -0
  83. package/dist/types/components/admin/site-health/site-health-google-analytics.integration.d.ts.map +1 -0
  84. package/dist/types/components/admin/site-health/site-health-google-search-console.d.ts +8 -0
  85. package/dist/types/components/admin/site-health/site-health-google-search-console.d.ts.map +1 -0
  86. package/dist/types/components/admin/site-health/site-health-google-search-console.integration.d.ts +46 -0
  87. package/dist/types/components/admin/site-health/site-health-google-search-console.integration.d.ts.map +1 -0
  88. package/dist/types/components/admin/site-health/site-health-indicators.d.ts +73 -0
  89. package/dist/types/components/admin/site-health/site-health-indicators.d.ts.map +1 -0
  90. package/dist/types/components/admin/site-health/site-health-on-site-seo.d.ts +4 -0
  91. package/dist/types/components/admin/site-health/site-health-on-site-seo.d.ts.map +1 -0
  92. package/dist/types/components/admin/site-health/site-health-on-site-seo.integration.d.ts +34 -0
  93. package/dist/types/components/admin/site-health/site-health-on-site-seo.integration.d.ts.map +1 -0
  94. package/dist/types/components/admin/site-health/site-health-overview.d.ts +6 -0
  95. package/dist/types/components/admin/site-health/site-health-overview.d.ts.map +1 -0
  96. package/dist/types/components/admin/site-health/site-health-performance.d.ts +6 -0
  97. package/dist/types/components/admin/site-health/site-health-performance.d.ts.map +1 -0
  98. package/dist/types/components/admin/site-health/site-health-security.d.ts +6 -0
  99. package/dist/types/components/admin/site-health/site-health-security.d.ts.map +1 -0
  100. package/dist/types/components/admin/site-health/site-health-security.integration.d.ts +29 -0
  101. package/dist/types/components/admin/site-health/site-health-security.integration.d.ts.map +1 -0
  102. package/dist/types/components/admin/site-health/site-health-seo.d.ts +6 -0
  103. package/dist/types/components/admin/site-health/site-health-seo.d.ts.map +1 -0
  104. package/dist/types/components/admin/site-health/site-health-template.d.ts +12 -0
  105. package/dist/types/components/admin/site-health/site-health-template.d.ts.map +1 -0
  106. package/dist/types/components/admin/site-health/site-health-types.d.ts +186 -0
  107. package/dist/types/components/admin/site-health/site-health-types.d.ts.map +1 -0
  108. package/dist/types/components/admin/site-health/site-health-uptime.d.ts +6 -0
  109. package/dist/types/components/admin/site-health/site-health-uptime.d.ts.map +1 -0
  110. package/dist/types/components/admin/site-health/site-health-uptime.integration.d.ts +10 -0
  111. package/dist/types/components/admin/site-health/site-health-uptime.integration.d.ts.map +1 -0
  112. package/dist/types/components/admin/sites/sites.integration.d.ts +40 -0
  113. package/dist/types/components/admin/sites/sites.integration.d.ts.map +1 -0
  114. package/dist/types/components/cms/contentful.management.d.ts +41 -0
  115. package/dist/types/components/cms/contentful.management.d.ts.map +1 -1
  116. package/dist/types/components/sitebuilder/config/ConfigBuilder.d.ts +4 -4
  117. package/dist/types/components/sitebuilder/config/ConfigBuilder.d.ts.map +1 -1
  118. package/dist/types/components/structured/buzzwordbingo.d.ts +1 -1
  119. package/dist/types/components/structured/buzzwordbingo.d.ts.map +1 -1
  120. package/dist/types/components/structured/buzzwordbingo.words.d.ts +2 -0
  121. package/dist/types/components/structured/buzzwordbingo.words.d.ts.map +1 -0
  122. package/dist/types/index.d.ts +16 -3
  123. package/dist/types/index.server.d.ts +36 -13
  124. package/dist/types/stories/admin/preview.d.ts +12 -0
  125. package/dist/types/stories/admin/preview.d.ts.map +1 -0
  126. package/dist/types/stories/admin/site-health.stories.d.ts +65 -0
  127. package/dist/types/stories/admin/site-health.stories.d.ts.map +1 -0
  128. package/dist/types/stories/structured/buzzword-bingo.stories.d.ts +1 -1
  129. package/dist/types/stories/structured/buzzword-bingo.stories.d.ts.map +1 -1
  130. package/dist/types/tests/site-health-axe-core.test.d.ts +2 -0
  131. package/dist/types/tests/site-health-axe-core.test.d.ts.map +1 -0
  132. package/dist/types/tests/site-health-cache.test.d.ts +2 -0
  133. package/dist/types/tests/site-health-cache.test.d.ts.map +1 -0
  134. package/dist/types/tests/site-health-indicators.test.d.ts +2 -0
  135. package/dist/types/tests/site-health-indicators.test.d.ts.map +1 -0
  136. package/dist/types/tests/site-health-overview.test.d.ts +2 -0
  137. package/dist/types/tests/site-health-overview.test.d.ts.map +1 -0
  138. package/dist/types/tests/site-health-template.test.d.ts +2 -0
  139. package/dist/types/tests/site-health-template.test.d.ts.map +1 -0
  140. package/dist/types/tests/sites.integration.test.d.ts +2 -0
  141. package/dist/types/tests/sites.integration.test.d.ts.map +1 -0
  142. package/package.json +14 -8
  143. package/dist/data/shipping.to.json +0 -422
  144. package/dist/data/siteinfo-form.json +0 -200
  145. package/dist/data/visualdesignform.json +0 -244
  146. package/dist/types/data/buzzwords.d.ts +0 -2
  147. package/dist/types/data/buzzwords.d.ts.map +0 -1
  148. /package/dist/{data/buzzwords.js → components/structured/buzzwordbingo.words.js} +0 -0
@@ -0,0 +1,265 @@
1
+ {
2
+ "categories": {
3
+ "on-page": {
4
+ "name": "On-Page SEO",
5
+ "description": "Elements within individual page content",
6
+ "priority": 1,
7
+ "metrics": {
8
+ "semantic-tags": {
9
+ "id": "semantic-tags",
10
+ "title": "Semantic HTML Tags",
11
+ "description": "Checks for proper semantic HTML structure",
12
+ "scoreDisplayMode": "binary",
13
+ "dataCollector": "collectSemanticTagsData",
14
+ "scorer": "calculateSemanticTagsScore"
15
+ },
16
+ "title-tags": {
17
+ "id": "title-tags",
18
+ "title": "Title Tags",
19
+ "description": "Validates title tag presence and optimal length",
20
+ "scoreDisplayMode": "binary",
21
+ "dataCollector": "collectTitleTagsData",
22
+ "scorer": "calculateTitleTagsScore"
23
+ },
24
+ "meta-keywords": {
25
+ "id": "meta-keywords",
26
+ "title": "Meta Keywords",
27
+ "description": "Checks for meta keywords tag presence",
28
+ "scoreDisplayMode": "binary",
29
+ "dataCollector": "collectMetaKeywordsData",
30
+ "scorer": "calculateMetaKeywordsScore"
31
+ },
32
+ "meta-descriptions": {
33
+ "id": "meta-descriptions",
34
+ "title": "Meta Descriptions",
35
+ "description": "Validates meta description presence and length",
36
+ "scoreDisplayMode": "binary",
37
+ "dataCollector": "collectMetaDescriptionsData",
38
+ "scorer": "calculateMetaDescriptionsScore"
39
+ },
40
+ "content-type-charset": {
41
+ "id": "content-type-charset",
42
+ "title": "Content-Type and Charset",
43
+ "description": "Checks for proper content-type/charset declaration",
44
+ "scoreDisplayMode": "binary",
45
+ "pattern": "(<meta[^>]*http-equiv=[\"']content-type[\"'][^>]*>)|(<meta[^>]*charset=[\"'][^\"']*[\"'][^>]*>)",
46
+ "countLogic": "or",
47
+ "scoreLogic": "present",
48
+ "displayTemplate": "{{count}} content-type/charset declaration(s) found"
49
+ },
50
+ "viewport-tags": {
51
+ "id": "viewport-tags",
52
+ "title": "Viewport Tags",
53
+ "description": "Checks for viewport meta tag on mobile pages",
54
+ "scoreDisplayMode": "binary",
55
+ "pattern": "<meta[^>]*name=[\"']viewport[\"'][^>]*content=[\"'][^\"']*[\"'][^>]*>",
56
+ "countLogic": "count",
57
+ "scoreLogic": "present",
58
+ "displayTemplate": "{{count}} viewport tag(s) found"
59
+ },
60
+ "opengraph-tags": {
61
+ "id": "opengraph-tags",
62
+ "title": "OpenGraph Tags",
63
+ "description": "Checks for OpenGraph meta tags for social sharing",
64
+ "scoreDisplayMode": "binary",
65
+ "pattern": "<meta[^>]*property=[\"']og:[^\"']*[\"'][^>]*content=[\"'][^\"']*[\"'][^>]*>",
66
+ "countLogic": "count",
67
+ "scoreLogic": "present",
68
+ "displayTemplate": "{{count}} OpenGraph tag(s) found"
69
+ },
70
+ "preconnect-tags": {
71
+ "id": "preconnect-tags",
72
+ "title": "Preconnect Tags",
73
+ "description": "Checks for preconnect link tags for performance",
74
+ "scoreDisplayMode": "binary",
75
+ "pattern": "<link[^>]*rel=[\"']preconnect[\"'][^>]*>",
76
+ "countLogic": "count",
77
+ "scoreLogic": "present",
78
+ "displayTemplate": "{{count}} preconnect link(s) found"
79
+ },
80
+ "h1-tags": {
81
+ "id": "h1-tags",
82
+ "title": "H1 Tags",
83
+ "description": "Checks for proper H1 tag usage",
84
+ "scoreDisplayMode": "binary",
85
+ "pattern": "<h1[^>]*>[^<]*</h1>",
86
+ "countLogic": "exact",
87
+ "expectedCount": 1,
88
+ "scoreLogic": "exact",
89
+ "displayTemplate": "{{count}} H1 tag(s) found"
90
+ },
91
+ "h2-tags": {
92
+ "id": "h2-tags",
93
+ "title": "H2 Tags",
94
+ "description": "Checks for H2 tag usage",
95
+ "scoreDisplayMode": "binary",
96
+ "pattern": "<h2[^>]*>[^<]*</h2>",
97
+ "countLogic": "count",
98
+ "scoreLogic": "present",
99
+ "displayTemplate": "{{count}} H2 tag(s) found"
100
+ },
101
+ "canonical-urls": {
102
+ "id": "canonical-urls",
103
+ "title": "Canonical URLs",
104
+ "description": "Checks for canonical URL tags",
105
+ "scoreDisplayMode": "binary",
106
+ "pattern": "<link[^>]*rel=[\"']canonical[\"'][^>]*href=[\"'][^\"']*[\"'][^>]*>",
107
+ "countLogic": "count",
108
+ "scoreLogic": "present",
109
+ "displayTemplate": "{{count}} canonical URL(s) found"
110
+ },
111
+ "language-tags": {
112
+ "id": "language-tags",
113
+ "title": "Language Tags",
114
+ "description": "Checks for lang attribute on html element",
115
+ "scoreDisplayMode": "binary",
116
+ "pattern": "<html[^>]*lang=[\"'][^\"']*[\"'][^>]*>",
117
+ "countLogic": "count",
118
+ "scoreLogic": "present",
119
+ "displayTemplate": "{{count}} language tag(s) found"
120
+ },
121
+ "icon-tags": {
122
+ "id": "icon-tags",
123
+ "title": "Icon Tags",
124
+ "description": "Checks for icon link tags for browser tabs",
125
+ "scoreDisplayMode": "binary",
126
+ "pattern": "<link[^>]*rel=[\"']icon[\"'][^>]*>",
127
+ "countLogic": "count",
128
+ "scoreLogic": "present",
129
+ "displayTemplate": "{{count}} icon link(s) found"
130
+ },
131
+ "shortcut-icon-tags": {
132
+ "id": "shortcut-icon-tags",
133
+ "title": "Shortcut Icon Tags",
134
+ "description": "Checks for shortcut icon link tags for browser tabs",
135
+ "scoreDisplayMode": "binary",
136
+ "pattern": "<link[^>]*rel=[\"']shortcut icon[\"'][^>]*>",
137
+ "countLogic": "count",
138
+ "scoreLogic": "present",
139
+ "displayTemplate": "{{count}} shortcut icon link(s) found"
140
+ },
141
+ "manifest-tag": {
142
+ "id": "manifest-tag",
143
+ "title": "Manifest Tag",
144
+ "description": "Checks for manifest link tag to direct crawlers to WebManifest file",
145
+ "scoreDisplayMode": "binary",
146
+ "pattern": "<link[^>]*rel=[\"']manifest[\"'][^>]*href=[\"'][^\"']*[\"'][^>]*>",
147
+ "countLogic": "count",
148
+ "scoreLogic": "present",
149
+ "displayTemplate": "{{count}} manifest link(s) found"
150
+ },
151
+ "schema-markup": {
152
+ "id": "schema-markup",
153
+ "title": "Schema Markup",
154
+ "description": "Checks for structured data markup",
155
+ "scoreDisplayMode": "binary",
156
+ "pattern": "(<script[^>]*type=[\"']application/ld\\+json[\"'][^>]*>[^<]*</script>)|(<[^>]*itemtype=[\"'][^\"']*[\"'][^>]*>)",
157
+ "countLogic": "count",
158
+ "scoreLogic": "present",
159
+ "displayTemplate": "{{count}} schema markup element(s) found"
160
+ },
161
+ "twitter-cards": {
162
+ "id": "twitter-cards",
163
+ "title": "Twitter Cards",
164
+ "description": "Checks for Twitter Card meta tags",
165
+ "scoreDisplayMode": "binary",
166
+ "pattern": "<meta[^>]*name=[\"']twitter:[^\"']*[\"'][^>]*content=[\"'][^\"']*[\"'][^>]*>",
167
+ "countLogic": "count",
168
+ "scoreLogic": "present",
169
+ "displayTemplate": "{{count}} Twitter Card tag(s) found"
170
+ },
171
+ "image-alt-text": {
172
+ "id": "image-alt-text",
173
+ "title": "Image Alt Text",
174
+ "description": "Checks for alt text on images",
175
+ "scoreDisplayMode": "binary",
176
+ "pattern": "<img[^>]*alt=[\"'][^\"']*[\"'][^>]*>",
177
+ "countLogic": "count",
178
+ "scoreLogic": "present",
179
+ "displayTemplate": "{{count}} image(s) with alt text found"
180
+ },
181
+ "itemprop-tags": {
182
+ "id": "itemprop-tags",
183
+ "title": "ItemProp Tags",
184
+ "description": "Checks for ItemProp microdata tags for social media sharing",
185
+ "scoreDisplayMode": "binary",
186
+ "pattern": "<[^>]*itemprop=[\"'][^\"']*[\"'][^>]*>",
187
+ "countLogic": "count",
188
+ "scoreLogic": "present",
189
+ "displayTemplate": "{{count}} ItemProp tag(s) found"
190
+ }
191
+ }
192
+ },
193
+ "on-site": {
194
+ "name": "On-Site SEO",
195
+ "description": "Site-wide technical and structural elements",
196
+ "priority": 2,
197
+ "metrics": {
198
+ "https": {
199
+ "id": "https",
200
+ "title": "HTTPS",
201
+ "description": "Checks if site uses secure HTTPS protocol",
202
+ "scoreDisplayMode": "binary",
203
+ "dataCollector": null,
204
+ "scorer": null
205
+ },
206
+ "url-structure": {
207
+ "id": "url-structure",
208
+ "title": "URL Structure",
209
+ "description": "Checks for clean URL structure without excessive parameters",
210
+ "scoreDisplayMode": "binary",
211
+ "dataCollector": null,
212
+ "scorer": null
213
+ },
214
+ "robots-txt": {
215
+ "id": "robots-txt",
216
+ "title": "Robots.txt",
217
+ "description": "Checks for robots.txt file accessibility",
218
+ "scoreDisplayMode": "binary",
219
+ "dataCollector": null,
220
+ "scorer": null
221
+ },
222
+ "sitemap-xml": {
223
+ "id": "sitemap-xml",
224
+ "title": "Sitemap.xml",
225
+ "description": "Checks for sitemap.xml file accessibility",
226
+ "scoreDisplayMode": "binary",
227
+ "dataCollector": null,
228
+ "scorer": null
229
+ },
230
+ "internal-linking": {
231
+ "id": "internal-linking",
232
+ "title": "Internal Linking",
233
+ "description": "Checks for proper internal linking structure",
234
+ "scoreDisplayMode": "binary",
235
+ "dataCollector": null,
236
+ "scorer": null
237
+ },
238
+ "navigation": {
239
+ "id": "navigation",
240
+ "title": "Navigation",
241
+ "description": "Checks for proper navigation structure",
242
+ "scoreDisplayMode": "binary",
243
+ "dataCollector": null,
244
+ "scorer": null
245
+ },
246
+ "broken-links": {
247
+ "id": "broken-links",
248
+ "title": "Broken Links / 404s",
249
+ "description": "Checks for broken internal links",
250
+ "scoreDisplayMode": "binary",
251
+ "dataCollector": null,
252
+ "scorer": null
253
+ },
254
+ "manifest-file": {
255
+ "id": "manifest-file",
256
+ "title": "Manifest File",
257
+ "description": "Checks for manifest.webmanifest file accessibility",
258
+ "scoreDisplayMode": "binary",
259
+ "dataCollector": null,
260
+ "scorer": null
261
+ }
262
+ }
263
+ }
264
+ }
265
+ }
@@ -0,0 +1,158 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { useCallback } from 'react';
4
+ import { SiteHealthTemplate } from './site-health-template';
5
+ import { getScoreIndicator } from './site-health-indicators';
6
+ export function SiteHealthAccessibility({ siteName }) {
7
+ const fetchAccessibilityData = useCallback(async (site) => {
8
+ const response = await fetch(`/api/site-health/core-web-vitals?siteName=${encodeURIComponent(site)}`);
9
+ const result = await response.json();
10
+ if (!result.success) {
11
+ throw new Error(result.error || 'Failed to fetch accessibility data');
12
+ }
13
+ return result;
14
+ }, []);
15
+ return (_jsx(SiteHealthTemplate, { siteName: siteName, title: "PageSpeed - Accessibility", fetchData: fetchAccessibilityData, children: (data) => {
16
+ const getScoreColor = (score) => {
17
+ return getScoreIndicator(score).color;
18
+ };
19
+ const getAuditScoreIcon = (score) => {
20
+ return getScoreIndicator(score).icon;
21
+ };
22
+ // Helper function to display audit item details
23
+ const formatAuditItem = (item, auditTitle) => {
24
+ // Handle URLs
25
+ if (item.url && typeof item.url === 'string') {
26
+ return item.url;
27
+ }
28
+ // Handle sources (like JavaScript files)
29
+ if (item.source && typeof item.source === 'string') {
30
+ return item.source;
31
+ }
32
+ // Handle text descriptions
33
+ if (item.text && typeof item.text === 'string') {
34
+ return item.text;
35
+ }
36
+ // Handle entities (like "Google Tag Manager")
37
+ if (item.entity && typeof item.entity === 'string') {
38
+ return item.entity;
39
+ }
40
+ // Handle nodes with selectors
41
+ if (item.node && typeof item.node === 'object' && 'selector' in item.node) {
42
+ return `Element: ${item.node.selector}`;
43
+ }
44
+ // Handle nodes with snippets
45
+ if (item.node && typeof item.node === 'object' && 'snippet' in item.node) {
46
+ const snippet = item.node.snippet;
47
+ return `Element: ${snippet.length > 50 ? snippet.substring(0, 50) + '...' : snippet}`;
48
+ }
49
+ // Handle origins (like domains)
50
+ if (item.origin && typeof item.origin === 'string') {
51
+ return item.origin;
52
+ }
53
+ // Handle labels
54
+ if (item.label && typeof item.label === 'string') {
55
+ return item.label;
56
+ }
57
+ // Handle numeric values with units
58
+ if (item.value && typeof item.value === 'object' && 'type' in item.value && item.value.type === 'numeric') {
59
+ const value = item.value;
60
+ return `${value.value}${item.unit || ''}`;
61
+ }
62
+ // Handle statistics
63
+ if (item.statistic && typeof item.statistic === 'string' && item.value) {
64
+ if (typeof item.value === 'object' && 'type' in item.value && item.value.type === 'numeric') {
65
+ const value = item.value;
66
+ return `${item.statistic}: ${value.value}`;
67
+ }
68
+ return item.statistic;
69
+ }
70
+ // Handle timing data with audit context
71
+ if (typeof item === 'number') {
72
+ let context = '';
73
+ if (auditTitle) {
74
+ if (auditTitle.toLowerCase().includes('server') || auditTitle.toLowerCase().includes('backend')) {
75
+ context = ' server response';
76
+ }
77
+ else if (auditTitle.toLowerCase().includes('network') || auditTitle.toLowerCase().includes('request')) {
78
+ context = ' network request';
79
+ }
80
+ else if (auditTitle.toLowerCase().includes('render') || auditTitle.toLowerCase().includes('blocking')) {
81
+ context = ' render blocking';
82
+ }
83
+ else if (auditTitle.toLowerCase().includes('javascript') || auditTitle.toLowerCase().includes('js')) {
84
+ context = ' JavaScript';
85
+ }
86
+ else if (auditTitle.toLowerCase().includes('image') || auditTitle.toLowerCase().includes('media')) {
87
+ context = ' media resource';
88
+ }
89
+ }
90
+ return `${item.toFixed(2)}ms${context}`;
91
+ }
92
+ if (item.value && typeof item.value === 'number') {
93
+ const unit = item.unit || 'ms';
94
+ let context = '';
95
+ if (auditTitle && unit === 'ms') {
96
+ if (auditTitle.toLowerCase().includes('server')) {
97
+ context = ' server time';
98
+ }
99
+ else if (auditTitle.toLowerCase().includes('network')) {
100
+ context = ' network time';
101
+ }
102
+ }
103
+ return `${item.value.toFixed(2)}${unit}${context}`;
104
+ }
105
+ // If we can't find anything meaningful, show a generic message
106
+ return 'Details available';
107
+ };
108
+ if (!data?.data || data.data.length === 0) {
109
+ return (_jsx("p", { style: { color: '#6b7280' }, children: "No accessibility data available for this site." }));
110
+ }
111
+ const siteData = data.data[0];
112
+ if (siteData.status === 'error') {
113
+ return (_jsxs("p", { style: { color: '#ef4444', fontSize: '0.875rem' }, children: ["Error: ", siteData.error] }));
114
+ }
115
+ return (_jsxs(_Fragment, { children: [_jsx("h4", { className: "health-site-name", children: siteData.site.replace('-', ' ') }), _jsxs("p", { className: "health-site-url", children: ["URL: ", siteData.url] }), siteData.scores.accessibility !== null && (_jsx("div", { className: "health-score-container", children: _jsxs("div", { className: "health-score-item", children: [_jsx("div", { className: "health-score-label", children: "Accessibility Score" }), _jsxs("div", { className: "health-score-value", style: { color: getScoreColor(siteData.scores.accessibility) }, children: [Math.round((siteData.scores.accessibility || 0) * 100), "%"] }), _jsx("div", { className: "health-score-bar", children: _jsx("div", { className: "health-score-fill", style: {
116
+ width: `${(siteData.scores.accessibility || 0) * 100}%`,
117
+ backgroundColor: getScoreColor(siteData.scores.accessibility)
118
+ } }) })] }) })), siteData.categories.accessibility && siteData.categories.accessibility.audits.length > 0 && (_jsxs("div", { children: [_jsx("h5", { style: { fontSize: '1rem', fontWeight: '600', marginBottom: '1rem' }, children: "Accessibility Issues & Recommendations" }), _jsx("div", { className: "space-y-2", children: siteData.categories.accessibility.audits
119
+ .filter(audit => audit.scoreDisplayMode !== 'notApplicable')
120
+ .sort((a, b) => {
121
+ // Prioritize specific important accessibility audits
122
+ const priorityAudits = [
123
+ 'color-contrast',
124
+ 'image-alt',
125
+ 'label',
126
+ 'button-name',
127
+ 'link-name',
128
+ 'heading-order',
129
+ 'focusable-controls',
130
+ 'interactive-element-affordance',
131
+ 'logical-tab-order',
132
+ 'focus-traps',
133
+ 'bypass',
134
+ 'landmark-one-main',
135
+ 'meta-viewport',
136
+ 'html-has-lang',
137
+ 'html-lang-valid',
138
+ 'video-caption',
139
+ 'audio-caption'
140
+ ];
141
+ const aPriority = priorityAudits.indexOf(a.id);
142
+ const bPriority = priorityAudits.indexOf(b.id);
143
+ // If both are priority audits, sort by priority order
144
+ if (aPriority !== -1 && bPriority !== -1) {
145
+ return aPriority - bPriority;
146
+ }
147
+ // If only one is priority, put it first
148
+ if (aPriority !== -1)
149
+ return -1;
150
+ if (bPriority !== -1)
151
+ return 1;
152
+ // Otherwise sort by score
153
+ return (b.score || 0) - (a.score || 0);
154
+ })
155
+ .slice(0, 20)
156
+ .map((audit) => (_jsxs("div", { className: "health-audit-item", children: [_jsx("span", { className: "health-audit-icon", children: getAuditScoreIcon(audit.score) }), _jsxs("div", { className: "health-audit-content", children: [_jsxs("span", { className: "health-audit-title", children: ["(", Math.round((audit.score || 0) * 100), "%) ", audit.title] }), audit.displayValue && (_jsx("p", { className: "health-audit-description", children: audit.displayValue })), audit.details?.items && Array.isArray(audit.details.items) && audit.details.items.length > 0 && (audit.score || 0) < 0.9 && (_jsx("div", { className: "health-audit-details", children: _jsx("div", { style: { fontSize: '0.75rem', color: '#6b7280', marginTop: '0.25rem' }, children: audit.details.items.map((item, idx) => (_jsx("div", { style: { marginBottom: '0.125rem' }, children: formatAuditItem(item, audit.title) }, idx))) }) }))] })] }, audit.id))) })] })), _jsxs("p", { className: "health-timestamp", children: ["Last checked: ", new Date(siteData.timestamp).toLocaleString()] })] }));
157
+ } }));
158
+ }
@@ -0,0 +1,119 @@
1
+ "use server";
2
+ import puppeteer from 'puppeteer';
3
+ export async function performAxeCoreAnalysis(url) {
4
+ try {
5
+ // Run axe-core analysis
6
+ const axeResult = await runAxeCoreAnalysis(url);
7
+ // Calculate summary
8
+ const summary = {
9
+ violations: axeResult.violations.length,
10
+ passes: axeResult.passes.length,
11
+ incomplete: axeResult.incomplete.length,
12
+ inapplicable: axeResult.inapplicable.length,
13
+ critical: axeResult.violations.filter(v => v.impact === 'critical').length,
14
+ serious: axeResult.violations.filter(v => v.impact === 'serious').length,
15
+ moderate: axeResult.violations.filter(v => v.impact === 'moderate').length,
16
+ minor: axeResult.violations.filter(v => v.impact === 'minor').length,
17
+ };
18
+ return {
19
+ site: '', // Will be set by the caller
20
+ url: url,
21
+ result: axeResult,
22
+ summary,
23
+ timestamp: new Date().toISOString(),
24
+ status: 'success',
25
+ };
26
+ }
27
+ catch (error) {
28
+ console.error('Axe-core analysis failed:', error);
29
+ return {
30
+ site: '', // Will be set by the caller
31
+ url: url,
32
+ result: {
33
+ violations: [],
34
+ passes: [],
35
+ incomplete: [],
36
+ inapplicable: [],
37
+ testEngine: { name: 'axe-core', version: 'unknown' },
38
+ testRunner: { name: 'unknown' },
39
+ testEnvironment: {
40
+ userAgent: 'unknown',
41
+ windowWidth: 0,
42
+ windowHeight: 0,
43
+ },
44
+ timestamp: new Date().toISOString(),
45
+ url: url,
46
+ },
47
+ summary: {
48
+ violations: 0,
49
+ passes: 0,
50
+ incomplete: 0,
51
+ inapplicable: 0,
52
+ critical: 0,
53
+ serious: 0,
54
+ moderate: 0,
55
+ minor: 0,
56
+ },
57
+ timestamp: new Date().toISOString(),
58
+ status: 'error',
59
+ error: error instanceof Error ? error.message : 'Unknown error occurred during axe-core analysis',
60
+ };
61
+ }
62
+ }
63
+ async function runAxeCoreAnalysis(url) {
64
+ let browser;
65
+ try {
66
+ // Launch browser with options for better compatibility
67
+ browser = await puppeteer.launch({
68
+ headless: true,
69
+ args: [
70
+ '--no-sandbox',
71
+ '--disable-setuid-sandbox',
72
+ '--disable-dev-shm-usage',
73
+ '--disable-accelerated-2d-canvas',
74
+ '--no-first-run',
75
+ '--no-zygote',
76
+ '--single-process', // <- this one doesn't work in Windows
77
+ '--disable-gpu'
78
+ ]
79
+ });
80
+ const page = await browser.newPage();
81
+ // Set viewport for consistent results
82
+ await page.setViewport({ width: 1280, height: 720 });
83
+ // Set user agent to avoid bot detection
84
+ await page.setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36');
85
+ // Navigate to the page with timeout
86
+ await page.goto(url, {
87
+ waitUntil: 'networkidle2',
88
+ timeout: 30000
89
+ });
90
+ // Wait a bit for dynamic content to load
91
+ await new Promise(resolve => setTimeout(resolve, 2000));
92
+ // Inject axe-core by adding the script tag
93
+ await page.addScriptTag({
94
+ url: 'https://cdn.jsdelivr.net/npm/axe-core@4.8.2/axe.min.js'
95
+ });
96
+ // Wait a bit for axe to load
97
+ await new Promise(resolve => setTimeout(resolve, 1000));
98
+ // Run axe-core analysis
99
+ const result = await page.evaluate(async () => {
100
+ // Check if axe is available
101
+ if (typeof window.axe === 'undefined') {
102
+ throw new Error('axe-core not loaded');
103
+ }
104
+ // Run axe with all rules enabled
105
+ const axeResults = await window.axe.run(document, {
106
+ rules: {}, // Run all rules
107
+ runOnly: undefined, // Don't restrict to specific rule sets
108
+ reporter: 'v2'
109
+ });
110
+ return axeResults;
111
+ });
112
+ return result;
113
+ }
114
+ finally {
115
+ if (browser) {
116
+ await browser.close();
117
+ }
118
+ }
119
+ }
@@ -0,0 +1,53 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { useCallback } from 'react';
4
+ import { SiteHealthTemplate } from './site-health-template';
5
+ import { getImpactIndicator, getIncompleteIndicator, getPassingIndicator } from './site-health-indicators';
6
+ export function SiteHealthAxeCore({ siteName }) {
7
+ const fetchAxeCoreData = useCallback(async (site) => {
8
+ const response = await fetch(`/api/site-health/axe-core?siteName=${encodeURIComponent(site)}`);
9
+ const result = await response.json();
10
+ if (!result.success) {
11
+ throw new Error(result.error || 'Failed to fetch axe-core data');
12
+ }
13
+ return result;
14
+ }, []);
15
+ const getImpactColor = (impact) => {
16
+ return getImpactIndicator(impact).color;
17
+ };
18
+ const getImpactIcon = (impact) => {
19
+ return getImpactIndicator(impact).icon;
20
+ };
21
+ const formatNodeInfo = (node) => {
22
+ if (node.target && Array.isArray(node.target) && node.target.length > 0) {
23
+ // Return the CSS selector
24
+ return node.target.join(', ');
25
+ }
26
+ if (node.html) {
27
+ // Return a truncated version of the HTML
28
+ const html = node.html;
29
+ return html.length > 100 ? html.substring(0, 100) + '...' : html;
30
+ }
31
+ return 'Unknown element';
32
+ };
33
+ return (_jsx(SiteHealthTemplate, { siteName: siteName, title: "Axe-Core Accessibility", fetchData: fetchAxeCoreData, children: (data) => {
34
+ if (!data?.data || data.data.length === 0) {
35
+ return (_jsx("p", { style: { color: '#6b7280' }, children: "No axe-core data available for this site." }));
36
+ }
37
+ const siteData = data.data[0];
38
+ if (siteData.status === 'error') {
39
+ return (_jsxs("p", { style: { color: '#ef4444', fontSize: '0.875rem' }, children: ["Error: ", siteData.error] }));
40
+ }
41
+ const { result, summary } = siteData;
42
+ return (_jsxs(_Fragment, { children: [_jsx("h4", { className: "health-site-name", children: siteData.site.replace('-', ' ') }), _jsxs("p", { className: "health-site-url", children: ["URL: ", siteData.url] }), _jsx("div", { className: "health-score-container", children: _jsxs("div", { className: "health-score-item", children: [_jsx("div", { className: "health-score-label", children: "Accessibility Summary" }), _jsxs("div", { className: "health-score-grid", children: [_jsxs("div", { className: "health-stat-item", children: [_jsx("span", { className: "health-stat-label", children: "Violations : " }), _jsx("span", { className: "health-stat-value", style: { color: summary.violations > 0 ? '#ef4444' : '#10b981' }, children: summary.violations })] }), _jsxs("div", { className: "health-stat-item", children: [_jsx("span", { className: "health-stat-label", children: "Passes : " }), _jsx("span", { className: "health-stat-value", style: { color: '#10b981' }, children: summary.passes })] }), _jsxs("div", { className: "health-stat-item", children: [_jsx("span", { className: "health-stat-label", children: "Incomplete : " }), _jsx("span", { className: "health-stat-value", style: { color: '#f59e0b' }, children: summary.incomplete })] })] })] }) }), summary.violations > 0 && (_jsx("div", { className: "health-score-container", children: _jsxs("div", { className: "health-score-item", children: [_jsx("div", { className: "health-score-label", children: "Violation Impact Levels" }), _jsxs("div", { className: "health-score-grid", children: [_jsxs("div", { className: "health-stat-item", children: [_jsx("span", { className: "health-stat-label", children: "Critical : " }), _jsx("span", { className: "health-stat-value", style: { color: getImpactColor('critical') }, children: summary.critical })] }), _jsxs("div", { className: "health-stat-item", children: [_jsx("span", { className: "health-stat-label", children: "Serious : " }), _jsx("span", { className: "health-stat-value", style: { color: getImpactColor('serious') }, children: summary.serious })] }), _jsxs("div", { className: "health-stat-item", children: [_jsx("span", { className: "health-stat-label", children: "Moderate : " }), _jsx("span", { className: "health-stat-value", style: { color: getImpactColor('moderate') }, children: summary.moderate })] }), _jsxs("div", { className: "health-stat-item", children: [_jsx("span", { className: "health-stat-label", children: "Minor : " }), _jsx("span", { className: "health-stat-value", style: { color: getImpactColor('minor') }, children: summary.minor })] })] })] }) })), result.violations.length > 0 && (_jsxs("div", { children: [_jsx("h5", { style: { fontSize: '1rem', fontWeight: '600', marginBottom: '1rem' }, children: "Accessibility Violations" }), _jsx("div", { className: "space-y-2", children: result.violations
43
+ .sort((a, b) => {
44
+ const impactOrder = { critical: 4, serious: 3, moderate: 2, minor: 1 };
45
+ return impactOrder[b.impact] - impactOrder[a.impact];
46
+ })
47
+ .slice(0, 20)
48
+ .map((violation) => (_jsxs("div", { className: "health-audit-item", children: [_jsx("span", { className: "health-audit-icon", children: getImpactIcon(violation.impact) }), _jsxs("div", { className: "health-audit-content", children: [_jsxs("span", { className: "health-audit-title", children: [violation.help, " (", violation.impact, ")"] }), _jsx("p", { className: "health-audit-description", children: violation.description }), violation.nodes && violation.nodes.length > 0 && (_jsx("div", { className: "health-audit-details", children: _jsxs("div", { style: { fontSize: '0.75rem', color: '#6b7280', marginTop: '0.25rem' }, children: ["Affected elements (", violation.nodes.length, "):", violation.nodes.map((node, idx) => (_jsx("div", { style: { marginBottom: '0.125rem' }, children: formatNodeInfo(node) }, idx)))] }) })), _jsx("div", { style: { fontSize: '0.75rem', color: '#6b7280', marginTop: '0.25rem' }, children: _jsx("a", { href: violation.helpUrl, target: "_blank", rel: "noopener noreferrer", style: { color: '#3b82f6', textDecoration: 'underline' }, children: "Learn more about this rule" }) })] })] }, violation.id))) })] })), result.passes.length > 0 && result.violations.length === 0 && (_jsxs("div", { children: [_jsxs("h5", { style: { fontSize: '1rem', fontWeight: '600', marginBottom: '1rem', color: '#10b981' }, children: [getPassingIndicator().icon, " All Accessibility Checks Passed"] }), _jsxs("p", { style: { color: '#6b7280', fontSize: '0.875rem' }, children: [result.passes.length, " accessibility rules were successfully validated."] })] })), result.incomplete.length > 0 && (_jsxs("div", { style: { marginTop: '15px' }, children: [_jsxs("h5", { style: { fontSize: '1rem', fontWeight: '600', marginBottom: '1rem', color: '#f59e0b' }, children: ["Incomplete Tests (", result.incomplete.length, ")"] }), _jsx("div", { className: "space-y-2", children: result.incomplete
49
+ .sort((a, b) => a.id.localeCompare(b.id))
50
+ .slice(0, 10)
51
+ .map((incomplete) => (_jsxs("div", { className: "health-audit-item", children: [_jsx("span", { className: "health-audit-icon", children: getIncompleteIndicator().icon }), _jsxs("div", { className: "health-audit-content", children: [_jsx("span", { className: "health-audit-title", children: incomplete.help }), _jsx("p", { className: "health-audit-description", children: incomplete.description }), incomplete.nodes && incomplete.nodes.length > 0 && (_jsx("div", { className: "health-audit-details", children: _jsxs("div", { style: { fontSize: '0.75rem', color: '#6b7280', marginTop: '0.25rem' }, children: ["Elements tested (", incomplete.nodes.length, "):", incomplete.nodes.slice(0, 3).map((node, idx) => (_jsx("div", { style: { marginBottom: '0.125rem' }, children: formatNodeInfo(node) }, idx))), incomplete.nodes.length > 3 && (_jsxs("div", { style: { marginTop: '0.25rem', fontStyle: 'italic' }, children: ["... and ", incomplete.nodes.length - 3, " more elements"] }))] }) })), _jsx("div", { style: { fontSize: '0.75rem', color: '#6b7280', marginTop: '0.25rem' }, children: _jsx("a", { href: incomplete.helpUrl, target: "_blank", rel: "noopener noreferrer", style: { color: '#3b82f6', textDecoration: 'underline' }, children: "Learn more about this rule" }) })] })] }, incomplete.id))) })] })), _jsxs("div", { className: "health-timestamp", children: [_jsxs("div", { children: ["Tested with axe-core ", result.testEngine?.version || 'unknown'] }), _jsxs("div", { children: ["Last checked: ", new Date(siteData.timestamp).toLocaleString()] })] })] }));
52
+ } }));
53
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Shared caching utilities for Site Health components
3
+ */
4
+ export class RouteCache {
5
+ cache = new Map();
6
+ duration;
7
+ constructor(durationMs = 60 * 60 * 1000) {
8
+ this.duration = durationMs;
9
+ }
10
+ get(key) {
11
+ const cached = this.cache.get(key);
12
+ if (cached && Date.now() - cached.timestamp < this.duration) {
13
+ return cached.data;
14
+ }
15
+ return null;
16
+ }
17
+ set(key, data) {
18
+ this.cache.set(key, { data, timestamp: Date.now() });
19
+ }
20
+ clear() {
21
+ this.cache.clear();
22
+ }
23
+ }