@pixelated-tech/components 3.3.5 → 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.
- package/README.COMPONENTS.md +126 -0
- package/README.md +15 -9
- package/dist/components/admin/componentusage/componentAnalysis.js +144 -0
- package/dist/components/admin/componentusage/componentDiscovery.js +85 -0
- package/dist/components/admin/deploy/deployment.integration.js +170 -0
- package/dist/components/admin/site-health/google-api-auth.js +69 -0
- package/dist/components/admin/site-health/seo-metrics.config.json +265 -0
- package/dist/components/admin/site-health/site-health-accessibility.js +158 -0
- package/dist/components/admin/site-health/site-health-axe-core.integration.js +119 -0
- package/dist/components/admin/site-health/site-health-axe-core.js +53 -0
- package/dist/components/admin/site-health/site-health-cache.js +23 -0
- package/dist/components/admin/site-health/site-health-core-web-vitals.integration.js +208 -0
- package/dist/components/admin/site-health/site-health-dependency-vulnerabilities.js +38 -0
- package/dist/components/admin/site-health/site-health-github.integration.js +81 -0
- package/dist/components/admin/site-health/site-health-github.js +34 -0
- package/dist/components/admin/site-health/site-health-google-analytics.integration.js +112 -0
- package/dist/components/admin/site-health/site-health-google-analytics.js +43 -0
- package/dist/components/admin/site-health/site-health-google-search-console.integration.js +118 -0
- package/dist/components/admin/site-health/site-health-google-search-console.js +43 -0
- package/dist/components/admin/site-health/site-health-indicators.js +71 -0
- package/dist/components/admin/site-health/site-health-on-site-seo.integration.js +578 -0
- package/dist/components/admin/site-health/site-health-on-site-seo.js +204 -0
- package/dist/components/admin/site-health/site-health-overview.js +65 -0
- package/dist/components/admin/site-health/site-health-performance.js +191 -0
- package/dist/components/admin/site-health/site-health-security.integration.js +109 -0
- package/dist/components/admin/site-health/site-health-security.js +169 -0
- package/dist/components/admin/site-health/site-health-seo.js +124 -0
- package/dist/components/admin/site-health/site-health-template.js +62 -0
- package/dist/components/admin/site-health/site-health-types.js +1 -0
- package/dist/components/admin/site-health/site-health-uptime.integration.js +29 -0
- package/dist/components/admin/site-health/site-health-uptime.js +30 -0
- package/dist/components/admin/site-health/site-health.css +427 -0
- package/dist/components/admin/sites/sites.integration.js +117 -0
- package/dist/components/cms/contentful.management.js +104 -0
- package/dist/components/cms/hubspot.components.js +3 -3
- package/dist/components/config/config.client.js +21 -10
- package/dist/components/general/table.js +3 -1
- package/dist/components/seo/googleanalytics.js +1 -2
- package/dist/components/shoppingcart/shipping.from.json +101 -0
- package/dist/components/shoppingcart/shipping.parcel.json +112 -0
- package/dist/components/shoppingcart/shipping.to.json +422 -0
- package/dist/components/shoppingcart/shoppingCartDiscountCodes.json +26 -0
- package/dist/components/shoppingcart/shoppingcart.components.js +1 -1
- package/dist/components/sitebuilder/config/ConfigBuilder.js +36 -140
- package/dist/components/sitebuilder/config/siteinfo-form.json +200 -0
- package/dist/components/sitebuilder/config/visualdesignform.json +244 -0
- package/dist/components/structured/buzzwordbingo.js +3 -2
- package/dist/data/404-data.json +128 -102
- package/dist/data/flickr.json +25 -0
- package/dist/data/form.json +368 -368
- package/dist/data/recipes.json +3251 -3251
- package/dist/data/references.json +138 -137
- package/dist/data/requestform.json +111 -0
- package/dist/data/requests.json +136 -135
- package/dist/data/resume.json +2573 -2575
- package/dist/data/routes.json +238 -238
- package/dist/data/routes2.json +141 -140
- package/dist/index.js +16 -3
- package/dist/index.server.js +36 -15
- package/dist/types/components/admin/componentusage/componentAnalysis.d.ts +35 -0
- package/dist/types/components/admin/componentusage/componentAnalysis.d.ts.map +1 -0
- package/dist/types/components/admin/componentusage/componentDiscovery.d.ts +10 -0
- package/dist/types/components/admin/componentusage/componentDiscovery.d.ts.map +1 -0
- package/dist/types/components/admin/deploy/deployment.integration.d.ts +26 -0
- package/dist/types/components/admin/deploy/deployment.integration.d.ts.map +1 -0
- package/dist/types/components/admin/site-health/google-api-auth.d.ts +37 -0
- package/dist/types/components/admin/site-health/google-api-auth.d.ts.map +1 -0
- package/dist/types/components/admin/site-health/site-health-accessibility.d.ts +6 -0
- package/dist/types/components/admin/site-health/site-health-accessibility.d.ts.map +1 -0
- package/dist/types/components/admin/site-health/site-health-axe-core.d.ts +6 -0
- package/dist/types/components/admin/site-health/site-health-axe-core.d.ts.map +1 -0
- package/dist/types/components/admin/site-health/site-health-axe-core.integration.d.ts +63 -0
- package/dist/types/components/admin/site-health/site-health-axe-core.integration.d.ts.map +1 -0
- package/dist/types/components/admin/site-health/site-health-cache.d.ts +12 -0
- package/dist/types/components/admin/site-health/site-health-cache.d.ts.map +1 -0
- package/dist/types/components/admin/site-health/site-health-core-web-vitals.integration.d.ts +3 -0
- package/dist/types/components/admin/site-health/site-health-core-web-vitals.integration.d.ts.map +1 -0
- package/dist/types/components/admin/site-health/site-health-dependency-vulnerabilities.d.ts +6 -0
- package/dist/types/components/admin/site-health/site-health-dependency-vulnerabilities.d.ts.map +1 -0
- package/dist/types/components/admin/site-health/site-health-github.d.ts +8 -0
- package/dist/types/components/admin/site-health/site-health-github.d.ts.map +1 -0
- package/dist/types/components/admin/site-health/site-health-github.integration.d.ts +26 -0
- package/dist/types/components/admin/site-health/site-health-github.integration.d.ts.map +1 -0
- package/dist/types/components/admin/site-health/site-health-google-analytics.d.ts +8 -0
- package/dist/types/components/admin/site-health/site-health-google-analytics.d.ts.map +1 -0
- package/dist/types/components/admin/site-health/site-health-google-analytics.integration.d.ts +26 -0
- package/dist/types/components/admin/site-health/site-health-google-analytics.integration.d.ts.map +1 -0
- package/dist/types/components/admin/site-health/site-health-google-search-console.d.ts +8 -0
- package/dist/types/components/admin/site-health/site-health-google-search-console.d.ts.map +1 -0
- package/dist/types/components/admin/site-health/site-health-google-search-console.integration.d.ts +46 -0
- package/dist/types/components/admin/site-health/site-health-google-search-console.integration.d.ts.map +1 -0
- package/dist/types/components/admin/site-health/site-health-indicators.d.ts +73 -0
- package/dist/types/components/admin/site-health/site-health-indicators.d.ts.map +1 -0
- package/dist/types/components/admin/site-health/site-health-on-site-seo.d.ts +4 -0
- package/dist/types/components/admin/site-health/site-health-on-site-seo.d.ts.map +1 -0
- package/dist/types/components/admin/site-health/site-health-on-site-seo.integration.d.ts +34 -0
- package/dist/types/components/admin/site-health/site-health-on-site-seo.integration.d.ts.map +1 -0
- package/dist/types/components/admin/site-health/site-health-overview.d.ts +6 -0
- package/dist/types/components/admin/site-health/site-health-overview.d.ts.map +1 -0
- package/dist/types/components/admin/site-health/site-health-performance.d.ts +6 -0
- package/dist/types/components/admin/site-health/site-health-performance.d.ts.map +1 -0
- package/dist/types/components/admin/site-health/site-health-security.d.ts +6 -0
- package/dist/types/components/admin/site-health/site-health-security.d.ts.map +1 -0
- package/dist/types/components/admin/site-health/site-health-security.integration.d.ts +29 -0
- package/dist/types/components/admin/site-health/site-health-security.integration.d.ts.map +1 -0
- package/dist/types/components/admin/site-health/site-health-seo.d.ts +6 -0
- package/dist/types/components/admin/site-health/site-health-seo.d.ts.map +1 -0
- package/dist/types/components/admin/site-health/site-health-template.d.ts +12 -0
- package/dist/types/components/admin/site-health/site-health-template.d.ts.map +1 -0
- package/dist/types/components/admin/site-health/site-health-types.d.ts +186 -0
- package/dist/types/components/admin/site-health/site-health-types.d.ts.map +1 -0
- package/dist/types/components/admin/site-health/site-health-uptime.d.ts +6 -0
- package/dist/types/components/admin/site-health/site-health-uptime.d.ts.map +1 -0
- package/dist/types/components/admin/site-health/site-health-uptime.integration.d.ts +10 -0
- package/dist/types/components/admin/site-health/site-health-uptime.integration.d.ts.map +1 -0
- package/dist/types/components/admin/sites/sites.integration.d.ts +40 -0
- package/dist/types/components/admin/sites/sites.integration.d.ts.map +1 -0
- package/dist/types/components/cms/contentful.management.d.ts +41 -0
- package/dist/types/components/cms/contentful.management.d.ts.map +1 -1
- package/dist/types/components/config/config.client.d.ts +1 -2
- package/dist/types/components/config/config.client.d.ts.map +1 -1
- package/dist/types/components/general/table.d.ts +1 -0
- package/dist/types/components/general/table.d.ts.map +1 -1
- package/dist/types/components/seo/googleanalytics.d.ts +1 -1
- package/dist/types/components/seo/googleanalytics.d.ts.map +1 -1
- package/dist/types/components/sitebuilder/config/ConfigBuilder.d.ts +4 -4
- package/dist/types/components/sitebuilder/config/ConfigBuilder.d.ts.map +1 -1
- package/dist/types/components/structured/buzzwordbingo.d.ts +1 -1
- package/dist/types/components/structured/buzzwordbingo.d.ts.map +1 -1
- package/dist/types/components/structured/buzzwordbingo.words.d.ts +2 -0
- package/dist/types/components/structured/buzzwordbingo.words.d.ts.map +1 -0
- package/dist/types/index.d.ts +16 -3
- package/dist/types/index.server.d.ts +36 -13
- package/dist/types/stories/admin/preview.d.ts +12 -0
- package/dist/types/stories/admin/preview.d.ts.map +1 -0
- package/dist/types/stories/admin/site-health.stories.d.ts +65 -0
- package/dist/types/stories/admin/site-health.stories.d.ts.map +1 -0
- package/dist/types/stories/structured/buzzword-bingo.stories.d.ts +1 -1
- package/dist/types/stories/structured/buzzword-bingo.stories.d.ts.map +1 -1
- package/dist/types/tests/site-health-axe-core.test.d.ts +2 -0
- package/dist/types/tests/site-health-axe-core.test.d.ts.map +1 -0
- package/dist/types/tests/site-health-cache.test.d.ts +2 -0
- package/dist/types/tests/site-health-cache.test.d.ts.map +1 -0
- package/dist/types/tests/site-health-indicators.test.d.ts +2 -0
- package/dist/types/tests/site-health-indicators.test.d.ts.map +1 -0
- package/dist/types/tests/site-health-overview.test.d.ts +2 -0
- package/dist/types/tests/site-health-overview.test.d.ts.map +1 -0
- package/dist/types/tests/site-health-template.test.d.ts +2 -0
- package/dist/types/tests/site-health-template.test.d.ts.map +1 -0
- package/dist/types/tests/sites.integration.test.d.ts +2 -0
- package/dist/types/tests/sites.integration.test.d.ts.map +1 -0
- package/package.json +15 -9
- package/dist/data/shipping.to.json +0 -422
- package/dist/data/siteinfo-form.json +0 -200
- package/dist/data/visualdesignform.json +0 -244
- package/dist/types/data/buzzwords.d.ts +0 -2
- package/dist/types/data/buzzwords.d.ts.map +0 -1
- /package/dist/{data/buzzwords.js → components/structured/buzzwordbingo.words.js} +0 -0
package/README.COMPONENTS.md
CHANGED
|
@@ -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
|
|
|
@@ -301,8 +302,7 @@ npm run storybook
|
|
|
301
302
|
- [ ] **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
|
|
302
303
|
- [ ] **Template Marketplace**: Pre-built industry-specific templates (restaurant, law firm, contractor, etc.) that users can clone and customize
|
|
303
304
|
- [ ] **Configuration Wizard**: Step-by-step setup wizard that collects business info, generates site configuration, and creates initial content structure
|
|
304
|
-
- [ ] **
|
|
305
|
-
- [ ] **Site Health Monitoring**: Automated monitoring dashboard that checks site performance, broken links, SEO scores, and security vulnerabilities across all sites
|
|
305
|
+
- [ IP ] **Site Health Monitoring**: Automated monitoring dashboard that checks site performance, broken links, SEO scores, and security vulnerabilities across all sites
|
|
306
306
|
- [ ] **Content Migration Tools**: Automated importers for WordPress, Squarespace, Wix, and other platforms to migrate content to pixelated sites
|
|
307
307
|
- [ ] **A/B Testing Framework**: Built-in experimentation system for testing different layouts, content, and CTAs with automatic winner selection
|
|
308
308
|
- [ ] **Personalization Engine**: Dynamic content delivery based on user behavior, location, and preferences
|
|
@@ -381,16 +381,16 @@ Project Link: [https://github.com/brianwhaley/pixelated-components](https://gith
|
|
|
381
381
|
|
|
382
382
|
### Overview
|
|
383
383
|
|
|
384
|
-
**Current Status**: ✅ 2,
|
|
384
|
+
**Current Status**: ✅ 2,370 tests passing across 77 test files
|
|
385
385
|
|
|
386
386
|
| Metric | Value |
|
|
387
387
|
|--------|-------|
|
|
388
|
-
| Test Files |
|
|
389
|
-
| Total Tests | 2,
|
|
390
|
-
| Coverage (Statements) | 76.
|
|
391
|
-
| Coverage (Lines) |
|
|
392
|
-
| Coverage (Functions) |
|
|
393
|
-
| Coverage (Branches) | 67.
|
|
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% |
|
|
394
394
|
| Test Framework | Vitest 4.x |
|
|
395
395
|
| Testing Library | @testing-library/react + jsdom |
|
|
396
396
|
|
|
@@ -408,6 +408,9 @@ npm run test:run # Single run (for CI)
|
|
|
408
408
|
**Component Coverage Summary**
|
|
409
409
|
|
|
410
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
|
|
411
414
|
- **tiles.tsx**: 100% statements
|
|
412
415
|
- **google.reviews.functions.ts**: 100% statements
|
|
413
416
|
- **accordion.tsx**: 100% statements
|
|
@@ -520,6 +523,9 @@ npm run test:run # Single run (for CI)
|
|
|
520
523
|
[license-shield]: https://img.shields.io/github/license/brianwhaley/pixelated-components.svg?style=for-the-badge
|
|
521
524
|
[license-url]: https://github.com/brianwhaley/pixelated-components/blob/master/LICENSE.txt
|
|
522
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
|
+
|
|
523
529
|
[linkedin-shield]: https://img.shields.io/badge/-LinkedIn-black.svg?style=for-the-badge&logo=linkedin&colorB=555
|
|
524
530
|
[linkedin-url]: https://linkedin.com/in/brianwhaley
|
|
525
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
|
+
}
|