@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
@@ -53,6 +53,12 @@ For a complete working example of Pixelated Components in action, check out the
53
53
  - [PageEngine](#pageengine)
54
54
  - [SaveLoadSection](#saveloadsection)
55
55
 
56
+ ### Admin Components
57
+ - [Component Usage](#component-usage)
58
+ - [Deploy](#deploy)
59
+ - [Site Health](#site-health)
60
+ - [Sites](#sites)
61
+
56
62
  ### SEO & Schema
57
63
  - [404 Page](#404-page)
58
64
  - [GoogleAnalytics](#googleanalytics)
@@ -1382,6 +1388,126 @@ import { GoogleFontsImports } from '@pixelated-tech/components';
1382
1388
 
1383
1389
  ---
1384
1390
 
1391
+ ## Admin Components
1392
+
1393
+ Admin components provide tools for managing and monitoring websites, including site health monitoring, deployment, and component usage analysis.
1394
+
1395
+ ### Component Usage
1396
+
1397
+ Tracks and analyzes component usage across your application.
1398
+
1399
+ ```typescript
1400
+ import { ComponentDiscovery, ComponentAnalysis } from '@pixelated-tech/components/server';
1401
+
1402
+ // Discover all components in your project
1403
+ const components = await ComponentDiscovery.discoverComponents('./src');
1404
+
1405
+ // Analyze component usage
1406
+ const analysis = await ComponentAnalysis.analyzeUsage('./src', components);
1407
+ ```
1408
+
1409
+ #### Features
1410
+
1411
+ - **Component Discovery**: Automatically finds all React components in your codebase
1412
+ - **Usage Analysis**: Tracks where and how components are used
1413
+ - **Server-safe**: Safe to use in API routes and server components
1414
+
1415
+ ### Deploy
1416
+
1417
+ Deployment utilities for managing site deployments.
1418
+
1419
+ ```typescript
1420
+ import { DeploymentIntegration } from '@pixelated-tech/components/server';
1421
+
1422
+ // Deploy to production
1423
+ const result = await DeploymentIntegration.deploy({
1424
+ source: './dist',
1425
+ destination: 'production-site',
1426
+ config: deploymentConfig
1427
+ });
1428
+ ```
1429
+
1430
+ #### Features
1431
+
1432
+ - **Automated Deployment**: Streamlined deployment process
1433
+ - **Configuration Management**: Flexible deployment configurations
1434
+ - **Error Handling**: Comprehensive error reporting and recovery
1435
+
1436
+ ### Site Health
1437
+
1438
+ Comprehensive site health monitoring components.
1439
+
1440
+ ```typescript
1441
+ import {
1442
+ SiteHealthOverview,
1443
+ SiteHealthAxeCore,
1444
+ SiteHealthPerformance
1445
+ } from '@pixelated-tech/components';
1446
+
1447
+ // Core Web Vitals overview
1448
+ <SiteHealthOverview siteName="example.com" />
1449
+
1450
+ // Accessibility testing with axe-core
1451
+ <SiteHealthAxeCore siteName="example.com" />
1452
+
1453
+ // Performance metrics
1454
+ <SiteHealthPerformance siteName="example.com" />
1455
+ ```
1456
+
1457
+ #### Available Health Checks
1458
+
1459
+ - **Axe Core Accessibility**: Automated accessibility testing
1460
+ - **Core Web Vitals**: Performance metrics (LCP, FID, CLS)
1461
+ - **Google Analytics**: Traffic and engagement data
1462
+ - **Google Search Console**: Search performance and indexing
1463
+ - **On-site SEO**: Meta tags, structured data, and SEO elements
1464
+ - **Security Scan**: Security headers and vulnerabilities
1465
+ - **Dependency Vulnerabilities**: Outdated or vulnerable dependencies
1466
+ - **GitHub Integration**: Repository health and activity
1467
+ - **Uptime Monitoring**: Site availability and response times
1468
+
1469
+ #### Features
1470
+
1471
+ - **Real-time Monitoring**: Live data from various APIs and services
1472
+ - **Caching**: Built-in caching with configurable TTL
1473
+ - **Error Handling**: Graceful error handling and fallbacks
1474
+ - **Server-safe**: Components work in server and client environments
1475
+
1476
+ ### Sites
1477
+
1478
+ Site configuration management utilities.
1479
+
1480
+ ```typescript
1481
+ import {
1482
+ loadSitesConfig,
1483
+ saveSitesConfig,
1484
+ getSiteConfig,
1485
+ validateSiteConfig
1486
+ } from '@pixelated-tech/components/server';
1487
+
1488
+ // Load site configurations
1489
+ const sites = await loadSitesConfig();
1490
+
1491
+ // Get specific site
1492
+ const site = await getSiteConfig('my-site');
1493
+
1494
+ // Validate site configuration
1495
+ const validation = validateSiteConfig(site);
1496
+ if (!validation.valid) {
1497
+ console.error('Invalid site config:', validation.errors);
1498
+ }
1499
+ ```
1500
+
1501
+ #### Features
1502
+
1503
+ - **Configuration Management**: Load and save site configurations
1504
+ - **Validation**: Comprehensive site configuration validation
1505
+ - **GA4 Integration**: Google Analytics 4 property validation
1506
+ - **Search Console**: Google Search Console URL validation
1507
+ - **File System Operations**: Safe file operations with error handling
1508
+
1509
+ ---
1510
+
1385
1511
  ## SEO & Schema
1386
1512
 
1387
1513
  ### JSON-LD Schemas
package/README.md CHANGED
@@ -4,6 +4,7 @@
4
4
  [![Stargazers][stars-shield]][stars-url]
5
5
  [![Issues][issues-shield]][issues-url]
6
6
  [![MIT License][license-shield]][license-url]
7
+ [![Coverage][coverage-shield]][coverage-url]
7
8
  [![LinkedIn][linkedin-shield]][linkedin-url]
8
9
 
9
10
 
@@ -380,16 +381,16 @@ Project Link: [https://github.com/brianwhaley/pixelated-components](https://gith
380
381
 
381
382
  ### Overview
382
383
 
383
- **Current Status**: ✅ 2,246 tests passing across 67 test files
384
+ **Current Status**: ✅ 2,370 tests passing across 77 test files
384
385
 
385
386
  | Metric | Value |
386
387
  |--------|-------|
387
- | Test Files | 71 |
388
- | Total Tests | 2,291 |
389
- | Coverage (Statements) | 76.09% |
390
- | Coverage (Lines) | 78.76% |
391
- | Coverage (Functions) | 77.11% |
392
- | Coverage (Branches) | 67.37% |
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% |
393
394
  | Test Framework | Vitest 4.x |
394
395
  | Testing Library | @testing-library/react + jsdom |
395
396
 
@@ -407,6 +408,9 @@ npm run test:run # Single run (for CI)
407
408
  **Component Coverage Summary**
408
409
 
409
410
  #### Component Coverage (Sorted by Statement Coverage)
411
+ - **site-health-cache.ts**: 100% statements *(new shared caching utility)*
412
+ - **sites.integration.ts**: 100% statements *(new site management functions)*
413
+ - **site-health-indicators.ts**: 100% statements
410
414
  - **tiles.tsx**: 100% statements
411
415
  - **google.reviews.functions.ts**: 100% statements
412
416
  - **accordion.tsx**: 100% statements
@@ -519,6 +523,9 @@ npm run test:run # Single run (for CI)
519
523
  [license-shield]: https://img.shields.io/github/license/brianwhaley/pixelated-components.svg?style=for-the-badge
520
524
  [license-url]: https://github.com/brianwhaley/pixelated-components/blob/master/LICENSE.txt
521
525
 
526
+ [coverage-shield]: https://img.shields.io/badge/coverage-77%25-brightgreen?style=for-the-badge
527
+ [coverage-url]: https://github.com/brianwhaley/pixelated-components
528
+
522
529
  [linkedin-shield]: https://img.shields.io/badge/-LinkedIn-black.svg?style=for-the-badge&logo=linkedin&colorB=555
523
530
  [linkedin-url]: https://linkedin.com/in/brianwhaley
524
531
 
@@ -0,0 +1,144 @@
1
+ /**
2
+ * Component Usage Analysis Services
3
+ * Server-side utilities for analyzing component usage across sites
4
+ */
5
+ import { promises as fs } from 'fs';
6
+ import path from 'path';
7
+ /**
8
+ * Convert folder/filename format to actual export name
9
+ * e.g., "cms/calendly" -> "Calendly", "general/modal" -> "Modal"
10
+ */
11
+ export function folderFilenameToExportName(folderFilename) {
12
+ const parts = folderFilename.split('/');
13
+ let filename = parts[parts.length - 1];
14
+ // Strip common suffixes
15
+ filename = filename.replace(/\.components$/, '');
16
+ // Strip common prefixes
17
+ filename = filename.replace(/^schema-/, '');
18
+ // Default: capitalize each part and join
19
+ return filename
20
+ .split(/[-.]/)
21
+ .map(word => word.charAt(0).toUpperCase() + word.slice(1))
22
+ .join('');
23
+ }
24
+ /**
25
+ * Get all files recursively from a directory
26
+ */
27
+ export async function getAllFiles(dirPath, extensions = []) {
28
+ const files = [];
29
+ async function scan(dir) {
30
+ const entries = await fs.readdir(dir, { withFileTypes: true });
31
+ for (const entry of entries) {
32
+ const fullPath = path.join(dir, entry.name);
33
+ if (entry.isDirectory()) {
34
+ // Skip common non-source directories
35
+ if (!['node_modules', '.next', '.git', 'out', 'dist', 'build', '.DS_Store'].includes(entry.name)) {
36
+ await scan(fullPath);
37
+ }
38
+ }
39
+ else if (entry.isFile()) {
40
+ // Check if file has desired extension or if no extensions specified
41
+ const ext = path.extname(entry.name);
42
+ if (extensions.length === 0 || extensions.includes(ext)) {
43
+ files.push(fullPath);
44
+ }
45
+ }
46
+ }
47
+ }
48
+ try {
49
+ await scan(dirPath);
50
+ }
51
+ catch {
52
+ // Dir doesn't exist, continue
53
+ }
54
+ // If no source dirs found, scan the whole dir but exclude common non-source
55
+ if (files.length === 0) {
56
+ await scan(dirPath);
57
+ }
58
+ return files;
59
+ }
60
+ /**
61
+ * Check if a component is used in a site
62
+ */
63
+ export async function checkComponentUsage(sitePath, componentName) {
64
+ try {
65
+ const files = await getAllFiles(sitePath, ['.tsx', '.ts', '.jsx', '.js']);
66
+ // Special case for semantic components that export multiple functions
67
+ if (componentName === 'general/semantic') {
68
+ const semanticExports = [
69
+ 'PageTitleHeader', 'PageSection', 'PageSectionHeader', 'PageSectionBackgroundImage',
70
+ 'PageGridItem', 'PageFlexItem', 'PageHeader', 'PageHero', 'PageMain', 'PageNav', 'PageFooter'
71
+ ];
72
+ for (const file of files) {
73
+ const content = await fs.readFile(file, 'utf-8');
74
+ if (content.includes('@pixelated-tech/components')) {
75
+ // Check if any semantic export is imported (case insensitive)
76
+ for (const exportName of semanticExports) {
77
+ const contentLower = content.toLowerCase();
78
+ const exportNameLower = exportName.toLowerCase();
79
+ if (contentLower.includes(exportNameLower) ||
80
+ new RegExp(`import.*${exportNameLower}.*from.*@pixelated-tech/components`, 'i').test(content)) {
81
+ return true;
82
+ }
83
+ }
84
+ }
85
+ }
86
+ return false;
87
+ }
88
+ // Convert folder/filename to actual export name for checking
89
+ const exportName = folderFilenameToExportName(componentName);
90
+ for (const file of files) {
91
+ const content = await fs.readFile(file, 'utf-8');
92
+ // Check for import statements - look for the actual export name (case insensitive)
93
+ const contentLower = content.toLowerCase();
94
+ const exportNameLower = exportName.toLowerCase();
95
+ if (content.includes('@pixelated-tech/components') &&
96
+ (contentLower.includes(exportNameLower) ||
97
+ new RegExp(`import.*${exportNameLower}.*from.*@pixelated-tech/components`, 'i').test(content))) {
98
+ return true;
99
+ }
100
+ }
101
+ return false;
102
+ }
103
+ catch (error) {
104
+ console.error(`Error checking usage for ${componentName} in ${sitePath}:`, error);
105
+ return false;
106
+ }
107
+ }
108
+ /**
109
+ * Analyze component usage across all sites
110
+ */
111
+ export async function analyzeComponentUsage(components, sites) {
112
+ // Build usage matrix in parallel
113
+ const usageMatrix = {};
114
+ // Initialize matrix
115
+ for (const component of components) {
116
+ usageMatrix[component] = {};
117
+ for (const site of sites) {
118
+ usageMatrix[component][site.name] = false; // default
119
+ }
120
+ }
121
+ // Collect all check promises
122
+ const checkPromises = components.flatMap(component => sites.map(site => checkComponentUsage(site.localPath, component).then(isUsed => ({
123
+ component,
124
+ siteName: site.name,
125
+ isUsed
126
+ }))));
127
+ // Run all checks in parallel
128
+ const results = await Promise.allSettled(checkPromises);
129
+ // Populate matrix with results
130
+ results.forEach(result => {
131
+ if (result.status === 'fulfilled') {
132
+ const { component, siteName, isUsed } = result.value;
133
+ usageMatrix[component][siteName] = isUsed;
134
+ }
135
+ else {
136
+ console.error('Check failed:', result.reason);
137
+ }
138
+ });
139
+ return {
140
+ components,
141
+ siteList: sites,
142
+ usageMatrix
143
+ };
144
+ }
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Dynamic Component Discovery
3
+ * Discovers components by parsing pixelated-components exports at runtime
4
+ */
5
+ import fs from 'fs';
6
+ import path from 'path';
7
+ /**
8
+ * Get the pixelated-components package path (now that we're inside the library)
9
+ */
10
+ function getPixelatedComponentsPath() {
11
+ // Since this is now running in pixelated-admin, resolve from the current working directory
12
+ try {
13
+ // Get the current working directory (should be pixelated-admin root)
14
+ const cwd = process.cwd();
15
+ // Navigate to node_modules/@pixelated-tech/components
16
+ const packagePath = path.join(cwd, 'node_modules', '@pixelated-tech', 'components');
17
+ // Verify the path exists
18
+ if (fs.existsSync(packagePath)) {
19
+ return packagePath;
20
+ }
21
+ else {
22
+ // Fallback to require.resolve but strip the /ROOT/ prefix if present
23
+ const resolvedPath = require.resolve('@pixelated-tech/components/package.json');
24
+ if (resolvedPath.startsWith('/ROOT/')) {
25
+ // Remove the /ROOT/ prefix and use the actual filesystem path
26
+ const actualPath = resolvedPath.replace('/ROOT/', '/');
27
+ const packageDir = path.dirname(actualPath);
28
+ if (fs.existsSync(packageDir)) {
29
+ return packageDir;
30
+ }
31
+ }
32
+ // Use the resolved path as-is
33
+ const packageDir = path.dirname(resolvedPath);
34
+ return packageDir;
35
+ }
36
+ }
37
+ catch (error) {
38
+ console.error('Error resolving package path:', error);
39
+ // Fallback to relative path resolution
40
+ const currentDir = __dirname;
41
+ // Navigate up: componentusage -> admin -> components -> pixelated-components root
42
+ const fallbackPath = path.resolve(currentDir, '../../../');
43
+ return fallbackPath;
44
+ }
45
+ }
46
+ /**
47
+ * Discover components dynamically by parsing the pixelated-components index
48
+ * This runs on the server side during API calls
49
+ */
50
+ export async function discoverComponentsFromLibrary() {
51
+ try {
52
+ // Get the pixelated-components package path
53
+ const pixelatedPath = getPixelatedComponentsPath();
54
+ // Look for the built dist/index.js file
55
+ const indexPath = path.join(pixelatedPath, 'dist', 'index.js');
56
+ // Read and parse the index.js file
57
+ const indexContent = fs.readFileSync(indexPath, 'utf-8');
58
+ const componentNames = parseComponentExports(indexContent);
59
+ return componentNames;
60
+ }
61
+ catch (error) {
62
+ console.error('Error in dynamic component discovery:', error);
63
+ // Return empty array if discovery fails
64
+ return [];
65
+ }
66
+ }
67
+ /**
68
+ * Parse ALL export statements from index.js and format as folder/filename
69
+ * No filtering - includes everything for maximum inclusivity
70
+ */
71
+ function parseComponentExports(content) {
72
+ const componentNames = [];
73
+ const lines = content.split('\n');
74
+ for (const line of lines) {
75
+ const trimmed = line.trim();
76
+ // Match: export * from './components/folder/filename';
77
+ const match = trimmed.match(/export \* from '\.\/components\/([^']+)';/);
78
+ if (match) {
79
+ const componentPath = match[1];
80
+ // Format as folder/filename (e.g., "cms/calendly", "general/modal")
81
+ componentNames.push(componentPath);
82
+ }
83
+ }
84
+ return [...new Set(componentNames)].sort(); // Remove duplicates and sort alphabetically
85
+ }
@@ -0,0 +1,170 @@
1
+ /**
2
+ * Deployment Integration Services
3
+ * Server-side utilities for site deployment operations
4
+ */
5
+ import { exec } from 'child_process';
6
+ import { promisify } from 'util';
7
+ const execAsync = promisify(exec);
8
+ /**
9
+ * Execute deployment script for a site
10
+ */
11
+ export async function executeDeployment(request, siteConfig, isLocalExecution = false) {
12
+ // Only allow local execution for security
13
+ if (!isLocalExecution) {
14
+ throw new Error('Deployment execution is only allowed when running locally');
15
+ }
16
+ const { versionType, commitMessage, environments } = request;
17
+ const { localPath, remote } = siteConfig;
18
+ return executeScript(siteConfig.name, versionType, commitMessage, environments, localPath, remote);
19
+ }
20
+ /**
21
+ * Execute the deployment script
22
+ */
23
+ async function executeScript(siteName, versionType, commitMessage, environments, localPath, remote) {
24
+ const sourceBranch = 'dev'; // Always deploy from dev branch
25
+ try {
26
+ // Change to the site directory
27
+ process.chdir(localPath);
28
+ // Get current branch and ensure we're on dev
29
+ const { stdout: currentBranch } = await execAsync('git branch --show-current');
30
+ if (currentBranch.trim() !== sourceBranch) {
31
+ throw new Error(`Must be on ${sourceBranch} branch to deploy. Current branch: ${currentBranch.trim()}`);
32
+ }
33
+ // Check for uncommitted changes
34
+ const { stdout: status } = await execAsync('git status --porcelain');
35
+ if (status.trim()) {
36
+ throw new Error('There are uncommitted changes. Please commit or stash them before deploying.');
37
+ }
38
+ // Pull latest changes
39
+ await execAsync('git pull origin dev');
40
+ // Run prep commands
41
+ const prepResult = await runPrepCommands(siteName, versionType, commitMessage);
42
+ // Deploy to each environment
43
+ const environmentResults = {};
44
+ for (const env of environments) {
45
+ try {
46
+ const result = await deployToEnvironment(siteName, env, versionType, commitMessage, remote);
47
+ environmentResults[env] = result;
48
+ }
49
+ catch (error) {
50
+ environmentResults[env] = `Failed: ${error.message}`;
51
+ }
52
+ }
53
+ return {
54
+ prep: prepResult,
55
+ environments: environmentResults
56
+ };
57
+ }
58
+ catch (error) {
59
+ throw new Error(`Deployment failed: ${error.message}`);
60
+ }
61
+ }
62
+ /**
63
+ * Run preparation commands before deployment
64
+ */
65
+ async function runPrepCommands(siteName, versionType, commitMessage) {
66
+ const results = [];
67
+ try {
68
+ // Update packages first
69
+ results.push('Updating packages...');
70
+ try {
71
+ const { stdout: outdatedOutput } = await execAsync('npm outdated --json', { timeout: 60000 });
72
+ const outdated = JSON.parse(outdatedOutput);
73
+ const packagesToUpdate = Object.keys(outdated).map(pkg => `${pkg}@${outdated[pkg].latest}`);
74
+ if (packagesToUpdate.length > 0) {
75
+ for (const pkg of packagesToUpdate.slice(0, 10)) { // Limit to 10 packages to avoid timeouts
76
+ try {
77
+ await execAsync(`npm install --save ${pkg}`, { timeout: 120000 });
78
+ results.push(`Updated ${pkg}`);
79
+ }
80
+ catch (error) {
81
+ results.push(`Failed to update ${pkg}: ${error.message}`);
82
+ }
83
+ }
84
+ }
85
+ else {
86
+ results.push('All packages are up to date');
87
+ }
88
+ }
89
+ catch (error) {
90
+ results.push(`Package update check failed: ${error.message}`);
91
+ }
92
+ // Run linting
93
+ try {
94
+ await execAsync('npm run lint', { timeout: 120000 });
95
+ results.push('Linting passed');
96
+ }
97
+ catch (error) {
98
+ results.push(`Linting failed: ${error.message}`);
99
+ }
100
+ // Run audit fix
101
+ try {
102
+ await execAsync('npm audit fix --force', { timeout: 120000 });
103
+ results.push('Security audit fixes applied');
104
+ }
105
+ catch (error) {
106
+ results.push(`Audit fix failed: ${error.message}`);
107
+ }
108
+ // Update version based on type
109
+ if (versionType === 'patch') {
110
+ await execAsync('npm version patch --no-git-tag-version');
111
+ results.push('Updated patch version');
112
+ }
113
+ else if (versionType === 'minor') {
114
+ await execAsync('npm version minor --no-git-tag-version');
115
+ results.push('Updated minor version');
116
+ }
117
+ else if (versionType === 'major') {
118
+ await execAsync('npm version major --no-git-tag-version');
119
+ results.push('Updated major version');
120
+ }
121
+ // Build the project
122
+ await execAsync('npm run build', { timeout: 300000 });
123
+ results.push('Built project successfully');
124
+ // Commit changes
125
+ await execAsync(`git add . -v`, { timeout: 60000 });
126
+ await execAsync(`git commit -m "${commitMessage.replace(/"/g, '\\"')}"`, { timeout: 60000 });
127
+ results.push(`Committed changes: ${commitMessage}`);
128
+ // Get the new version
129
+ const { stdout: version } = await execAsync('node -p "require(\'./package.json\').version"');
130
+ results.push(`New version: ${version.trim()}`);
131
+ return results.join('\n');
132
+ }
133
+ catch (error) {
134
+ throw new Error(`Prep failed: ${error.message}`);
135
+ }
136
+ }
137
+ /**
138
+ * Deploy to a specific environment
139
+ */
140
+ async function deployToEnvironment(siteName, environment, versionType, commitMessage, remote) {
141
+ const results = [];
142
+ const sourceBranch = 'dev';
143
+ try {
144
+ // Determine target branch
145
+ const branch = environment === 'prod' ? 'main' : 'dev';
146
+ const pushCmd = `git push ${environment === 'dev' ? '-u ' : ''}${remote} ${sourceBranch}:${branch} --tags`;
147
+ results.push(`Pushing to ${environment} (${branch})...`);
148
+ const { stdout, stderr } = await execAsync(pushCmd, { timeout: 300000 }); // 5 minute timeout
149
+ if (stdout)
150
+ results.push(stdout);
151
+ if (stderr)
152
+ results.push(stderr);
153
+ results.push(`✓ Successfully deployed ${siteName} to ${environment}`);
154
+ return results.join('\n');
155
+ }
156
+ catch (error) {
157
+ const errorMsg = `Push to ${environment} failed: ${error.message}`;
158
+ // Handle expected git errors gracefully
159
+ if (error.message.includes('non-fast-forward')) {
160
+ return `${errorMsg}\nNon-fast-forward error, you may need to force push or resolve conflicts manually.`;
161
+ }
162
+ if (error.message.includes('timeout') || error.message.includes('Command failed')) {
163
+ return `${errorMsg}\nGit push failed, you may need to retry manually.`;
164
+ }
165
+ if (error.message.includes('Repository not found') || error.message.includes('does not exist')) {
166
+ return `${errorMsg}\nRepository not found or access denied. Check your git remote configuration.`;
167
+ }
168
+ throw new Error(errorMsg);
169
+ }
170
+ }
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Google API Authentication Utilities
3
+ * Shared authentication logic for Google services (Analytics, Search Console, etc.)
4
+ */
5
+ "use server";
6
+ import { google } from 'googleapis';
7
+ /**
8
+ * Create authenticated Google API client for a specific service
9
+ */
10
+ export async function createGoogleAuthClient(config, scopes) {
11
+ try {
12
+ let auth;
13
+ if (config.serviceAccountKey) {
14
+ // Use service account authentication (recommended)
15
+ const credentials = JSON.parse(config.serviceAccountKey);
16
+ auth = new google.auth.GoogleAuth({
17
+ credentials,
18
+ scopes,
19
+ });
20
+ }
21
+ else if (config.clientId && config.clientSecret && config.refreshToken) {
22
+ // Fallback to OAuth2 (deprecated for server-side apps)
23
+ const oauth2Client = new google.auth.OAuth2(config.clientId, config.clientSecret);
24
+ oauth2Client.setCredentials({
25
+ refresh_token: config.refreshToken,
26
+ });
27
+ auth = oauth2Client;
28
+ }
29
+ else {
30
+ return {
31
+ success: false,
32
+ error: 'Google credentials not configured. Set GOOGLE_SERVICE_ACCOUNT_KEY or OAuth credentials.'
33
+ };
34
+ }
35
+ return { success: true, auth };
36
+ }
37
+ catch (error) {
38
+ return {
39
+ success: false,
40
+ error: `Authentication failed: ${error.message}`
41
+ };
42
+ }
43
+ }
44
+ /**
45
+ * Create Analytics Data API client
46
+ */
47
+ export async function createAnalyticsClient(config) {
48
+ const result = await createGoogleAuthClient(config, ['https://www.googleapis.com/auth/analytics.readonly']);
49
+ if (!result.success)
50
+ return result;
51
+ return {
52
+ success: true,
53
+ client: google.analyticsdata({ version: 'v1beta', auth: result.auth }),
54
+ auth: result.auth
55
+ };
56
+ }
57
+ /**
58
+ * Create Search Console API client
59
+ */
60
+ export async function createSearchConsoleClient(config) {
61
+ const result = await createGoogleAuthClient(config, ['https://www.googleapis.com/auth/webmasters.readonly']);
62
+ if (!result.success)
63
+ return result;
64
+ return {
65
+ success: true,
66
+ client: google.searchconsole({ version: 'v1', auth: result.auth }),
67
+ auth: result.auth
68
+ };
69
+ }