mcp-wordpress 2.6.3 → 2.7.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.md +1 -1
- package/dist/cache/CacheInvalidation.d.ts +25 -6
- package/dist/cache/CacheInvalidation.d.ts.map +1 -1
- package/dist/cache/CacheInvalidation.js +168 -16
- package/dist/cache/CacheInvalidation.js.map +1 -1
- package/dist/cache/HttpCacheWrapper.d.ts.map +1 -1
- package/dist/cache/HttpCacheWrapper.js +3 -4
- package/dist/cache/HttpCacheWrapper.js.map +1 -1
- package/dist/cache/SEOCacheManager.d.ts +150 -0
- package/dist/cache/SEOCacheManager.d.ts.map +1 -0
- package/dist/cache/SEOCacheManager.js +275 -0
- package/dist/cache/SEOCacheManager.js.map +1 -0
- package/dist/client/SEOWordPressClient.d.ts +164 -0
- package/dist/client/SEOWordPressClient.d.ts.map +1 -0
- package/dist/client/SEOWordPressClient.js +674 -0
- package/dist/client/SEOWordPressClient.js.map +1 -0
- package/dist/client/api.d.ts.map +1 -1
- package/dist/client/api.js +50 -20
- package/dist/client/api.js.map +1 -1
- package/dist/client/auth.js +19 -19
- package/dist/client/auth.js.map +1 -1
- package/dist/client/index.d.ts +11 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +14 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/managers/AuthManager.d.ts +39 -0
- package/dist/client/managers/AuthManager.d.ts.map +1 -0
- package/dist/client/managers/AuthManager.js +142 -0
- package/dist/client/managers/AuthManager.js.map +1 -0
- package/dist/client/managers/AuthenticationManager.d.ts.map +1 -1
- package/dist/client/managers/AuthenticationManager.js +10 -9
- package/dist/client/managers/AuthenticationManager.js.map +1 -1
- package/dist/client/managers/BaseManager.d.ts.map +1 -1
- package/dist/client/managers/BaseManager.js +12 -0
- package/dist/client/managers/BaseManager.js.map +1 -1
- package/dist/client/managers/ComposedAuthenticationManager.d.ts +94 -0
- package/dist/client/managers/ComposedAuthenticationManager.d.ts.map +1 -0
- package/dist/client/managers/ComposedAuthenticationManager.js +340 -0
- package/dist/client/managers/ComposedAuthenticationManager.js.map +1 -0
- package/dist/client/managers/ComposedManagerFactory.d.ts +104 -0
- package/dist/client/managers/ComposedManagerFactory.d.ts.map +1 -0
- package/dist/client/managers/ComposedManagerFactory.js +180 -0
- package/dist/client/managers/ComposedManagerFactory.js.map +1 -0
- package/dist/client/managers/ComposedRequestManager.d.ts +82 -0
- package/dist/client/managers/ComposedRequestManager.d.ts.map +1 -0
- package/dist/client/managers/ComposedRequestManager.js +260 -0
- package/dist/client/managers/ComposedRequestManager.js.map +1 -0
- package/dist/client/managers/JWTAuthImplementation.d.ts +86 -0
- package/dist/client/managers/JWTAuthImplementation.d.ts.map +1 -0
- package/dist/client/managers/JWTAuthImplementation.js +240 -0
- package/dist/client/managers/JWTAuthImplementation.js.map +1 -0
- package/dist/client/managers/ManagersIndex.d.ts +6 -0
- package/dist/client/managers/ManagersIndex.d.ts.map +1 -0
- package/dist/client/managers/ManagersIndex.js +6 -0
- package/dist/client/managers/ManagersIndex.js.map +1 -0
- package/dist/client/managers/RequestManager.d.ts.map +1 -1
- package/dist/client/managers/RequestManager.js +5 -3
- package/dist/client/managers/RequestManager.js.map +1 -1
- package/dist/client/managers/composed/MigrationAdapter.d.ts +80 -0
- package/dist/client/managers/composed/MigrationAdapter.d.ts.map +1 -0
- package/dist/client/managers/composed/MigrationAdapter.js +214 -0
- package/dist/client/managers/composed/MigrationAdapter.js.map +1 -0
- package/dist/client/managers/composed/index.d.ts +23 -0
- package/dist/client/managers/composed/index.d.ts.map +1 -0
- package/dist/client/managers/composed/index.js +26 -0
- package/dist/client/managers/composed/index.js.map +1 -0
- package/dist/client/managers/implementations/ConfigurationProviderImpl.d.ts +27 -0
- package/dist/client/managers/implementations/ConfigurationProviderImpl.d.ts.map +1 -0
- package/dist/client/managers/implementations/ConfigurationProviderImpl.js +41 -0
- package/dist/client/managers/implementations/ConfigurationProviderImpl.js.map +1 -0
- package/dist/client/managers/implementations/ErrorHandlerImpl.d.ts +31 -0
- package/dist/client/managers/implementations/ErrorHandlerImpl.d.ts.map +1 -0
- package/dist/client/managers/implementations/ErrorHandlerImpl.js +73 -0
- package/dist/client/managers/implementations/ErrorHandlerImpl.js.map +1 -0
- package/dist/client/managers/implementations/ParameterValidatorImpl.d.ts +47 -0
- package/dist/client/managers/implementations/ParameterValidatorImpl.d.ts.map +1 -0
- package/dist/client/managers/implementations/ParameterValidatorImpl.js +141 -0
- package/dist/client/managers/implementations/ParameterValidatorImpl.js.map +1 -0
- package/dist/client/managers/interfaces/ManagerInterfaces.d.ts +147 -0
- package/dist/client/managers/interfaces/ManagerInterfaces.d.ts.map +1 -0
- package/dist/client/managers/interfaces/ManagerInterfaces.js +6 -0
- package/dist/client/managers/interfaces/ManagerInterfaces.js.map +1 -0
- package/dist/config/Config.d.ts +30 -0
- package/dist/config/Config.d.ts.map +1 -1
- package/dist/config/Config.js +30 -0
- package/dist/config/Config.js.map +1 -1
- package/dist/config/ConfigurationSchema.d.ts +75 -198
- package/dist/config/ConfigurationSchema.d.ts.map +1 -1
- package/dist/config/ConfigurationSchema.js +17 -17
- package/dist/config/ConfigurationSchema.js.map +1 -1
- package/dist/config/ServerConfiguration.d.ts +2 -2
- package/dist/config/ServerConfiguration.d.ts.map +1 -1
- package/dist/config/ServerConfiguration.js +15 -13
- package/dist/config/ServerConfiguration.js.map +1 -1
- package/dist/config/index.d.ts +8 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +11 -0
- package/dist/config/index.js.map +1 -0
- package/dist/docs/DocumentationGenerator.js +2 -2
- package/dist/docs/DocumentationGenerator.js.map +1 -1
- package/dist/dxt-entry.js +3 -3
- package/dist/dxt-entry.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +38 -37
- package/dist/index.js.map +1 -1
- package/dist/performance/MetricsCollector.d.ts.map +1 -1
- package/dist/performance/MetricsCollector.js +5 -4
- package/dist/performance/MetricsCollector.js.map +1 -1
- package/dist/security/AISecurityScanner.js +7 -7
- package/dist/security/AISecurityScanner.js.map +1 -1
- package/dist/security/AutomatedRemediation.d.ts.map +1 -1
- package/dist/security/AutomatedRemediation.js +11 -11
- package/dist/security/AutomatedRemediation.js.map +1 -1
- package/dist/security/InputValidator.d.ts +50 -126
- package/dist/security/InputValidator.d.ts.map +1 -1
- package/dist/security/InputValidator.js +9 -9
- package/dist/security/InputValidator.js.map +1 -1
- package/dist/security/SecurityCIPipeline.d.ts +47 -5
- package/dist/security/SecurityCIPipeline.d.ts.map +1 -1
- package/dist/security/SecurityCIPipeline.js +390 -49
- package/dist/security/SecurityCIPipeline.js.map +1 -1
- package/dist/security/SecurityConfigManager.js +10 -10
- package/dist/security/SecurityConfigManager.js.map +1 -1
- package/dist/security/SecurityMonitoring.js +4 -4
- package/dist/security/SecurityMonitoring.js.map +1 -1
- package/dist/security/SecurityReviewer.d.ts.map +1 -1
- package/dist/security/SecurityReviewer.js +13 -6
- package/dist/security/SecurityReviewer.js.map +1 -1
- package/dist/security/index.js +3 -3
- package/dist/security/index.js.map +1 -1
- package/dist/server/ConnectionTester.js +5 -5
- package/dist/server/ConnectionTester.js.map +1 -1
- package/dist/server/ToolRegistry.d.ts +2 -2
- package/dist/server/ToolRegistry.d.ts.map +1 -1
- package/dist/server/ToolRegistry.js +7 -6
- package/dist/server/ToolRegistry.js.map +1 -1
- package/dist/server/index.d.ts +7 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +9 -0
- package/dist/server/index.js.map +1 -0
- package/dist/tools/BaseToolManager.d.ts.map +1 -1
- package/dist/tools/BaseToolManager.js +11 -11
- package/dist/tools/BaseToolManager.js.map +1 -1
- package/dist/tools/auth.d.ts.map +1 -1
- package/dist/tools/auth.js +7 -7
- package/dist/tools/auth.js.map +1 -1
- package/dist/tools/comments.d.ts.map +1 -1
- package/dist/tools/comments.js +14 -14
- package/dist/tools/comments.js.map +1 -1
- package/dist/tools/index.d.ts +1 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +1 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/media.d.ts.map +1 -1
- package/dist/tools/media.js +14 -11
- package/dist/tools/media.js.map +1 -1
- package/dist/tools/pages.d.ts.map +1 -1
- package/dist/tools/pages.js +12 -12
- package/dist/tools/pages.js.map +1 -1
- package/dist/tools/performance.d.ts.map +1 -1
- package/dist/tools/performance.js +13 -11
- package/dist/tools/performance.js.map +1 -1
- package/dist/tools/posts/PostHandlers.d.ts.map +1 -1
- package/dist/tools/posts/PostHandlers.js +16 -16
- package/dist/tools/posts/PostHandlers.js.map +1 -1
- package/dist/tools/posts/PostToolDefinitions.d.ts.map +1 -1
- package/dist/tools/posts/index.d.ts.map +1 -1
- package/dist/tools/seo/BulkOperations.d.ts +113 -0
- package/dist/tools/seo/BulkOperations.d.ts.map +1 -0
- package/dist/tools/seo/BulkOperations.js +398 -0
- package/dist/tools/seo/BulkOperations.js.map +1 -0
- package/dist/tools/seo/SEOHandlers.d.ts +55 -0
- package/dist/tools/seo/SEOHandlers.d.ts.map +1 -0
- package/dist/tools/seo/SEOHandlers.js +255 -0
- package/dist/tools/seo/SEOHandlers.js.map +1 -0
- package/dist/tools/seo/SEOToolDefinitions.d.ts +59 -0
- package/dist/tools/seo/SEOToolDefinitions.d.ts.map +1 -0
- package/dist/tools/seo/SEOToolDefinitions.js +385 -0
- package/dist/tools/seo/SEOToolDefinitions.js.map +1 -0
- package/dist/tools/seo/SEOTools.d.ts +203 -0
- package/dist/tools/seo/SEOTools.d.ts.map +1 -0
- package/dist/tools/seo/SEOTools.js +708 -0
- package/dist/tools/seo/SEOTools.js.map +1 -0
- package/dist/tools/seo/analyzers/ContentAnalyzer.d.ts +94 -0
- package/dist/tools/seo/analyzers/ContentAnalyzer.d.ts.map +1 -0
- package/dist/tools/seo/analyzers/ContentAnalyzer.js +402 -0
- package/dist/tools/seo/analyzers/ContentAnalyzer.js.map +1 -0
- package/dist/tools/seo/auditors/SiteAuditor.d.ts +121 -0
- package/dist/tools/seo/auditors/SiteAuditor.d.ts.map +1 -0
- package/dist/tools/seo/auditors/SiteAuditor.js +600 -0
- package/dist/tools/seo/auditors/SiteAuditor.js.map +1 -0
- package/dist/tools/seo/generators/MetaGenerator.d.ts +128 -0
- package/dist/tools/seo/generators/MetaGenerator.d.ts.map +1 -0
- package/dist/tools/seo/generators/MetaGenerator.js +547 -0
- package/dist/tools/seo/generators/MetaGenerator.js.map +1 -0
- package/dist/tools/seo/generators/SchemaGenerator.d.ts +204 -0
- package/dist/tools/seo/generators/SchemaGenerator.d.ts.map +1 -0
- package/dist/tools/seo/generators/SchemaGenerator.js +670 -0
- package/dist/tools/seo/generators/SchemaGenerator.js.map +1 -0
- package/dist/tools/seo/index.d.ts +17 -0
- package/dist/tools/seo/index.d.ts.map +1 -0
- package/dist/tools/seo/index.js +18 -0
- package/dist/tools/seo/index.js.map +1 -0
- package/dist/tools/seo/optimizers/InternalLinkingSuggester.d.ts +186 -0
- package/dist/tools/seo/optimizers/InternalLinkingSuggester.d.ts.map +1 -0
- package/dist/tools/seo/optimizers/InternalLinkingSuggester.js +683 -0
- package/dist/tools/seo/optimizers/InternalLinkingSuggester.js.map +1 -0
- package/dist/tools/site.d.ts.map +1 -1
- package/dist/tools/site.js +12 -12
- package/dist/tools/site.js.map +1 -1
- package/dist/tools/taxonomies.d.ts.map +1 -1
- package/dist/tools/taxonomies.js +20 -20
- package/dist/tools/taxonomies.js.map +1 -1
- package/dist/tools/users.d.ts.map +1 -1
- package/dist/tools/users.js +12 -12
- package/dist/tools/users.js.map +1 -1
- package/dist/types/client.d.ts +8 -6
- package/dist/types/client.d.ts.map +1 -1
- package/dist/types/client.js.map +1 -1
- package/dist/types/seo.d.ts +473 -0
- package/dist/types/seo.d.ts.map +1 -0
- package/dist/types/seo.js +94 -0
- package/dist/types/seo.js.map +1 -0
- package/dist/utils/enhancedError.js +1 -1
- package/dist/utils/enhancedError.js.map +1 -1
- package/dist/utils/error.d.ts.map +1 -1
- package/dist/utils/error.js +0 -1
- package/dist/utils/error.js.map +1 -1
- package/dist/utils/index.d.ts +12 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +18 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/logger.js +3 -3
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils/toolWrapper.d.ts +2 -2
- package/dist/utils/toolWrapper.js +8 -8
- package/dist/utils/toolWrapper.js.map +1 -1
- package/dist/utils/validation/core.d.ts.map +1 -1
- package/dist/utils/validation/core.js.map +1 -1
- package/dist/utils/validation/index.d.ts.map +1 -1
- package/dist/utils/validation/index.js.map +1 -1
- package/dist/utils/validation/network.js +3 -3
- package/dist/utils/validation/network.js.map +1 -1
- package/dist/utils/validation/rateLimit.js.map +1 -1
- package/dist/utils/validation/security.js.map +1 -1
- package/dist/utils/validation/wordpress.js.map +1 -1
- package/dist/utils/version.d.ts +144 -0
- package/dist/utils/version.d.ts.map +1 -0
- package/dist/utils/version.js +318 -0
- package/dist/utils/version.js.map +1 -0
- package/package.json +24 -58
- package/src/cache/CacheInvalidation.ts +183 -20
- package/src/cache/HttpCacheWrapper.ts +8 -5
- package/src/cache/SEOCacheManager.ts +330 -0
- package/src/cache/__tests__/CacheInvalidation.test.ts +6 -11
- package/src/cache/__tests__/CachedWordPressClient.test.ts +37 -62
- package/src/client/SEOWordPressClient.ts +876 -0
- package/src/client/api.ts +50 -21
- package/src/client/auth.ts +19 -19
- package/src/client/index.ts +16 -0
- package/src/client/managers/AuthManager.ts +175 -0
- package/src/client/managers/AuthenticationManager.ts +16 -14
- package/src/client/managers/BaseManager.ts +24 -5
- package/src/client/managers/ComposedAuthenticationManager.ts +409 -0
- package/src/client/managers/ComposedManagerFactory.ts +231 -0
- package/src/client/managers/ComposedRequestManager.ts +336 -0
- package/src/client/managers/JWTAuthImplementation.ts +326 -0
- package/src/client/managers/ManagersIndex.ts +6 -0
- package/src/client/managers/RequestManager.ts +9 -7
- package/src/client/managers/composed/MigrationAdapter.ts +263 -0
- package/src/client/managers/composed/index.ts +47 -0
- package/src/client/managers/implementations/ConfigurationProviderImpl.ts +52 -0
- package/src/client/managers/implementations/ErrorHandlerImpl.ts +102 -0
- package/src/client/managers/implementations/ParameterValidatorImpl.ts +221 -0
- package/src/client/managers/interfaces/ManagerInterfaces.ts +171 -0
- package/src/config/Config.ts +63 -0
- package/src/config/ConfigurationSchema.ts +17 -17
- package/src/config/ServerConfiguration.ts +18 -16
- package/src/config/index.ts +13 -0
- package/src/docs/DocumentationGenerator.ts +2 -2
- package/src/dxt-entry.ts +3 -3
- package/src/index.ts +43 -43
- package/src/performance/MetricsCollector.ts +15 -11
- package/src/security/AISecurityScanner.ts +7 -7
- package/src/security/AutomatedRemediation.ts +13 -11
- package/src/security/InputValidator.ts +10 -9
- package/src/security/SecurityCIPipeline.ts +494 -56
- package/src/security/SecurityConfigManager.ts +10 -10
- package/src/security/SecurityMonitoring.ts +5 -5
- package/src/security/SecurityReviewer.ts +13 -6
- package/src/security/index.ts +3 -3
- package/src/server/ConnectionTester.ts +5 -5
- package/src/server/ToolRegistry.ts +9 -8
- package/src/server/index.ts +10 -0
- package/src/tools/BaseToolManager.ts +55 -83
- package/src/tools/auth.ts +21 -12
- package/src/tools/comments.ts +23 -19
- package/src/tools/index.ts +1 -0
- package/src/tools/media.ts +23 -20
- package/src/tools/pages.ts +20 -13
- package/src/tools/performance.ts +101 -32
- package/src/tools/posts/PostHandlers.ts +23 -23
- package/src/tools/posts/PostToolDefinitions.ts +1 -1
- package/src/tools/posts/index.ts +2 -2
- package/src/tools/seo/BulkOperations.ts +557 -0
- package/src/tools/seo/SEOHandlers.ts +296 -0
- package/src/tools/seo/SEOToolDefinitions.ts +402 -0
- package/src/tools/seo/SEOTools.ts +871 -0
- package/src/tools/seo/analyzers/ContentAnalyzer.ts +493 -0
- package/src/tools/seo/auditors/SiteAuditor.ts +787 -0
- package/src/tools/seo/generators/MetaGenerator.ts +694 -0
- package/src/tools/seo/generators/SchemaGenerator.ts +955 -0
- package/src/tools/seo/index.ts +47 -0
- package/src/tools/seo/optimizers/InternalLinkingSuggester.ts +934 -0
- package/src/tools/site.ts +27 -26
- package/src/tools/taxonomies.ts +29 -25
- package/src/tools/users.ts +20 -13
- package/src/types/client.ts +8 -6
- package/src/types/seo.ts +546 -0
- package/src/utils/enhancedError.ts +1 -1
- package/src/utils/error.ts +1 -2
- package/src/utils/index.ts +23 -0
- package/src/utils/logger.ts +3 -3
- package/src/utils/toolWrapper.ts +10 -10
- package/src/utils/validation/core.ts +2 -2
- package/src/utils/validation/index.ts +2 -2
- package/src/utils/validation/network.ts +5 -5
- package/src/utils/validation/rateLimit.ts +1 -1
- package/src/utils/validation/security.ts +1 -1
- package/src/utils/validation/wordpress.ts +1 -1
- package/src/utils/version.ts +402 -0
- package/src/dxt-entry.cjs +0 -68
|
@@ -0,0 +1,674 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SEO WordPress Client
|
|
3
|
+
*
|
|
4
|
+
* Extended WordPress REST API client with enhanced SEO capabilities.
|
|
5
|
+
* Provides specialized methods for SEO metadata management, schema markup,
|
|
6
|
+
* and integration with popular WordPress SEO plugins like Yoast and RankMath.
|
|
7
|
+
*
|
|
8
|
+
* Features:
|
|
9
|
+
* - SEO metadata retrieval and updates via REST API
|
|
10
|
+
* - Integration with Yoast SEO and RankMath plugin APIs
|
|
11
|
+
* - Schema markup management through WordPress custom fields
|
|
12
|
+
* - Bulk SEO operations with progress tracking
|
|
13
|
+
* - SEO-specific data normalization and validation
|
|
14
|
+
* - Plugin-agnostic metadata handling
|
|
15
|
+
*
|
|
16
|
+
* @since 2.7.0
|
|
17
|
+
*/
|
|
18
|
+
import { WordPressClient } from "./api.js";
|
|
19
|
+
import { LoggerFactory } from "../utils/logger.js";
|
|
20
|
+
import { handleToolError } from "../utils/error.js";
|
|
21
|
+
/**
|
|
22
|
+
* Extended WordPress client with SEO capabilities
|
|
23
|
+
*/
|
|
24
|
+
export class SEOWordPressClient extends WordPressClient {
|
|
25
|
+
logger = LoggerFactory.tool("seo_client");
|
|
26
|
+
detectedPlugin = "none";
|
|
27
|
+
pluginFields;
|
|
28
|
+
constructor(config) {
|
|
29
|
+
super(config);
|
|
30
|
+
// Define field mappings for different SEO plugins
|
|
31
|
+
this.pluginFields = {
|
|
32
|
+
yoast: {
|
|
33
|
+
title: "_yoast_wpseo_title",
|
|
34
|
+
description: "_yoast_wpseo_metadesc",
|
|
35
|
+
focusKeyword: "_yoast_wpseo_focuskw",
|
|
36
|
+
canonical: "_yoast_wpseo_canonical",
|
|
37
|
+
noindex: "_yoast_wpseo_meta-robots-noindex",
|
|
38
|
+
nofollow: "_yoast_wpseo_meta-robots-nofollow",
|
|
39
|
+
schema: "_yoast_wpseo_schema_page_type",
|
|
40
|
+
},
|
|
41
|
+
rankmath: {
|
|
42
|
+
title: "rank_math_title",
|
|
43
|
+
description: "rank_math_description",
|
|
44
|
+
focusKeyword: "rank_math_focus_keyword",
|
|
45
|
+
canonical: "rank_math_canonical_url",
|
|
46
|
+
robots: "rank_math_robots",
|
|
47
|
+
schema: "rank_math_rich_snippet",
|
|
48
|
+
},
|
|
49
|
+
seopress: {
|
|
50
|
+
title: "_seopress_titles_title",
|
|
51
|
+
description: "_seopress_titles_desc",
|
|
52
|
+
targetKeyword: "_seopress_analysis_target_kw",
|
|
53
|
+
canonical: "_seopress_robots_canonical",
|
|
54
|
+
robots: "_seopress_robots_index",
|
|
55
|
+
},
|
|
56
|
+
none: {
|
|
57
|
+
title: "meta_title",
|
|
58
|
+
description: "meta_description",
|
|
59
|
+
focusKeyword: "focus_keyword",
|
|
60
|
+
canonical: "canonical_url",
|
|
61
|
+
noindex: "robots_noindex",
|
|
62
|
+
nofollow: "robots_nofollow",
|
|
63
|
+
schema: "schema_markup",
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Initialize and detect SEO plugins
|
|
69
|
+
*/
|
|
70
|
+
async initializeSEO() {
|
|
71
|
+
this.logger.debug("Initializing SEO client and detecting plugins");
|
|
72
|
+
try {
|
|
73
|
+
// Detect active SEO plugins
|
|
74
|
+
await this.detectSEOPlugins();
|
|
75
|
+
this.logger.info("SEO client initialized", {
|
|
76
|
+
detectedPlugin: this.detectedPlugin,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
catch (_error) {
|
|
80
|
+
this.logger.warn("SEO plugin detection failed, using generic approach", {
|
|
81
|
+
_error: _error instanceof Error ? _error.message : String(_error),
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Detect active SEO plugins on the WordPress site
|
|
87
|
+
*/
|
|
88
|
+
async detectSEOPlugins() {
|
|
89
|
+
try {
|
|
90
|
+
// Get list of installed plugins
|
|
91
|
+
const plugins = await this.get("/wp/v2/plugins");
|
|
92
|
+
if (!Array.isArray(plugins)) {
|
|
93
|
+
this.logger.debug("Could not retrieve plugins list");
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
// Check for active SEO plugins in order of preference
|
|
97
|
+
const activePlugins = plugins.filter((plugin) => plugin.status === "active");
|
|
98
|
+
// Check for Yoast SEO
|
|
99
|
+
if (activePlugins.some((plugin) => plugin.slug === "wordpress-seo")) {
|
|
100
|
+
this.detectedPlugin = "yoast";
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
// Check for RankMath
|
|
104
|
+
if (activePlugins.some((plugin) => plugin.slug === "seo-by-rank-math")) {
|
|
105
|
+
this.detectedPlugin = "rankmath";
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
// Check for SEOPress
|
|
109
|
+
if (activePlugins.some((plugin) => plugin.slug === "wp-seopress")) {
|
|
110
|
+
this.detectedPlugin = "seopress";
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
this.logger.debug("No SEO plugins detected, using generic metadata approach");
|
|
114
|
+
}
|
|
115
|
+
catch (_error) {
|
|
116
|
+
this.logger.error("Plugin detection failed", {
|
|
117
|
+
_error: _error instanceof Error ? _error.message : String(_error),
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Get SEO metadata for a post or page
|
|
123
|
+
*/
|
|
124
|
+
async getSEOMetadata(postId, type = "post") {
|
|
125
|
+
this.logger.debug("Fetching SEO metadata", { postId, type, plugin: this.detectedPlugin });
|
|
126
|
+
try {
|
|
127
|
+
// Get the post/page with meta fields
|
|
128
|
+
const content = type === "post"
|
|
129
|
+
? await this.getPost(postId, "edit") // edit context includes meta
|
|
130
|
+
: await this.getPage(postId, "edit");
|
|
131
|
+
if (!content) {
|
|
132
|
+
throw new Error(`${type} with ID ${postId} not found`);
|
|
133
|
+
}
|
|
134
|
+
// Extract SEO metadata based on detected plugin
|
|
135
|
+
const metadata = this.extractSEOMetadata(content);
|
|
136
|
+
// Extract schema markup if available
|
|
137
|
+
const schema = this.extractSchemaMarkup(content);
|
|
138
|
+
// Get raw plugin data for debugging
|
|
139
|
+
const raw = this.getRawPluginData(content);
|
|
140
|
+
// For malformed plugin data (detected plugin but no actual data), return null for title/description
|
|
141
|
+
const pluginDetected = this.detectedPlugin !== "none";
|
|
142
|
+
const pluginDataMalformed = pluginDetected && !this.hasPluginData(content);
|
|
143
|
+
return {
|
|
144
|
+
postId: postId,
|
|
145
|
+
plugin: this.detectedPlugin,
|
|
146
|
+
title: pluginDataMalformed ? null : metadata.title,
|
|
147
|
+
description: pluginDataMalformed ? null : metadata.description,
|
|
148
|
+
canonical: metadata.canonical || null,
|
|
149
|
+
focusKeyword: metadata.focusKeyword || null,
|
|
150
|
+
openGraph: metadata.openGraph || { title: null, description: null },
|
|
151
|
+
twitter: metadata.twitterCard ? {
|
|
152
|
+
title: metadata.twitterCard.title || null,
|
|
153
|
+
description: metadata.twitterCard.description || null
|
|
154
|
+
} : { title: null, description: null },
|
|
155
|
+
schema,
|
|
156
|
+
raw,
|
|
157
|
+
lastModified: content.modified || content.date || new Date().toISOString(),
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
catch (_error) {
|
|
161
|
+
handleToolError(_error, "get SEO metadata", { postId, type });
|
|
162
|
+
throw _error;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Update SEO metadata for a post or page
|
|
167
|
+
*/
|
|
168
|
+
async updateSEOMetadata(postId, metadata, type = "post") {
|
|
169
|
+
this.logger.debug("Updating SEO metadata", { postId, type, plugin: this.detectedPlugin });
|
|
170
|
+
try {
|
|
171
|
+
// Check if SEO plugin is available
|
|
172
|
+
if (this.detectedPlugin === "none") {
|
|
173
|
+
return {
|
|
174
|
+
success: false,
|
|
175
|
+
message: "No SEO plugin detected. Cannot update SEO metadata.",
|
|
176
|
+
postId,
|
|
177
|
+
plugin: this.detectedPlugin,
|
|
178
|
+
title: null,
|
|
179
|
+
description: null,
|
|
180
|
+
focusKeyword: null,
|
|
181
|
+
canonical: null,
|
|
182
|
+
openGraph: { title: null, description: null },
|
|
183
|
+
twitter: { title: null, description: null },
|
|
184
|
+
schema: undefined,
|
|
185
|
+
raw: {},
|
|
186
|
+
lastModified: new Date().toISOString(),
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
// Prepare meta fields based on detected plugin
|
|
190
|
+
const metaFields = this.prepareSEOMetaFields(metadata);
|
|
191
|
+
// Update the post/page with new meta fields
|
|
192
|
+
const updateData = {
|
|
193
|
+
id: postId,
|
|
194
|
+
meta: metaFields,
|
|
195
|
+
};
|
|
196
|
+
const _updatedContent = type === "post" ? await this.updatePost(updateData) : await this.updatePage(updateData);
|
|
197
|
+
// Return updated SEO data
|
|
198
|
+
return await this.getSEOMetadata(postId, type);
|
|
199
|
+
}
|
|
200
|
+
catch (_error) {
|
|
201
|
+
handleToolError(_error, "update SEO metadata", { postId, type });
|
|
202
|
+
throw _error;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Bulk get SEO metadata for multiple posts
|
|
207
|
+
*/
|
|
208
|
+
async bulkGetSEOMetadata(params) {
|
|
209
|
+
this.logger.info("Starting bulk SEO metadata retrieval", {
|
|
210
|
+
postCount: params.postIds.length,
|
|
211
|
+
batchSize: params.batchSize || 10,
|
|
212
|
+
});
|
|
213
|
+
const results = [];
|
|
214
|
+
const batchSize = params.batchSize || 10;
|
|
215
|
+
const total = params.postIds.length;
|
|
216
|
+
// Process in batches
|
|
217
|
+
for (let i = 0; i < params.postIds.length; i += batchSize) {
|
|
218
|
+
const batch = params.postIds.slice(i, i + batchSize);
|
|
219
|
+
const batchPromises = batch.map(async (postId) => {
|
|
220
|
+
try {
|
|
221
|
+
return await this.getSEOMetadata(postId);
|
|
222
|
+
}
|
|
223
|
+
catch (_error) {
|
|
224
|
+
this.logger.warn("Failed to get SEO metadata for post", {
|
|
225
|
+
postId,
|
|
226
|
+
_error: _error instanceof Error ? _error.message : String(_error),
|
|
227
|
+
});
|
|
228
|
+
return null;
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
const batchResults = await Promise.all(batchPromises);
|
|
232
|
+
results.push(...batchResults.filter((r) => r !== null));
|
|
233
|
+
// Report progress
|
|
234
|
+
const processed = Math.min(i + batchSize, total);
|
|
235
|
+
if (params.progressCallback) {
|
|
236
|
+
params.progressCallback(processed, total);
|
|
237
|
+
}
|
|
238
|
+
// Small delay between batches to avoid overwhelming the server
|
|
239
|
+
if (i + batchSize < params.postIds.length) {
|
|
240
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
this.logger.info("Bulk SEO metadata retrieval completed", {
|
|
244
|
+
requested: total,
|
|
245
|
+
successful: results.length,
|
|
246
|
+
failed: total - results.length,
|
|
247
|
+
});
|
|
248
|
+
return results;
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Bulk update SEO metadata for multiple posts
|
|
252
|
+
*/
|
|
253
|
+
async bulkUpdateSEOMetadata(params) {
|
|
254
|
+
this.logger.info("Starting bulk SEO metadata update", {
|
|
255
|
+
postCount: params.postIds.length,
|
|
256
|
+
batchSize: params.batchSize || 5, // Smaller batches for updates
|
|
257
|
+
});
|
|
258
|
+
const results = [];
|
|
259
|
+
const batchSize = params.batchSize || 5;
|
|
260
|
+
const total = params.postIds.length;
|
|
261
|
+
// Process in smaller batches for updates
|
|
262
|
+
for (let i = 0; i < params.postIds.length; i += batchSize) {
|
|
263
|
+
const batch = params.postIds.slice(i, i + batchSize);
|
|
264
|
+
const batchPromises = batch.map(async (postId) => {
|
|
265
|
+
try {
|
|
266
|
+
return await this.updateSEOMetadata(postId, params.metadata);
|
|
267
|
+
}
|
|
268
|
+
catch (_error) {
|
|
269
|
+
this.logger.warn("Failed to update SEO metadata for post", {
|
|
270
|
+
postId,
|
|
271
|
+
_error: _error instanceof Error ? _error.message : String(_error),
|
|
272
|
+
});
|
|
273
|
+
return null;
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
const batchResults = await Promise.all(batchPromises);
|
|
277
|
+
results.push(...batchResults.filter((r) => r !== null));
|
|
278
|
+
// Report progress
|
|
279
|
+
const processed = Math.min(i + batchSize, total);
|
|
280
|
+
if (params.progressCallback) {
|
|
281
|
+
params.progressCallback(processed, total);
|
|
282
|
+
}
|
|
283
|
+
// Longer delay between update batches
|
|
284
|
+
if (i + batchSize < params.postIds.length) {
|
|
285
|
+
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
this.logger.info("Bulk SEO metadata update completed", {
|
|
289
|
+
requested: total,
|
|
290
|
+
successful: results.length,
|
|
291
|
+
failed: total - results.length,
|
|
292
|
+
});
|
|
293
|
+
return results;
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Get all posts with SEO metadata for site audit
|
|
297
|
+
*/
|
|
298
|
+
async getAllPostsWithSEO(params = {}) {
|
|
299
|
+
this.logger.debug("Fetching all posts with SEO data for audit", params);
|
|
300
|
+
const maxPosts = params.maxPosts || 100;
|
|
301
|
+
const results = [];
|
|
302
|
+
try {
|
|
303
|
+
// Get posts
|
|
304
|
+
const posts = await this.getPosts({
|
|
305
|
+
per_page: maxPosts,
|
|
306
|
+
status: ["publish"],
|
|
307
|
+
context: "edit", // Include meta fields
|
|
308
|
+
});
|
|
309
|
+
// Get pages if requested
|
|
310
|
+
let pages = [];
|
|
311
|
+
if (params.includePages) {
|
|
312
|
+
pages = await this.getPages({
|
|
313
|
+
per_page: Math.max(20, Math.floor(maxPosts / 5)), // 20% allocation for pages
|
|
314
|
+
status: ["publish"],
|
|
315
|
+
context: "edit",
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
// Process posts
|
|
319
|
+
for (const post of posts || []) {
|
|
320
|
+
try {
|
|
321
|
+
const seoData = await this.getSEOMetadata(post.id, "post");
|
|
322
|
+
results.push({ ...post, seoData });
|
|
323
|
+
}
|
|
324
|
+
catch (_error) {
|
|
325
|
+
// Include post without SEO data if metadata fetch fails
|
|
326
|
+
results.push(post);
|
|
327
|
+
this.logger.debug("Failed to get SEO data for post", { postId: post.id });
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
// Process pages
|
|
331
|
+
for (const page of pages) {
|
|
332
|
+
try {
|
|
333
|
+
const seoData = await this.getSEOMetadata(page.id, "page");
|
|
334
|
+
results.push({ ...page, seoData });
|
|
335
|
+
}
|
|
336
|
+
catch (_error) {
|
|
337
|
+
// Include page without SEO data if metadata fetch fails
|
|
338
|
+
results.push(page);
|
|
339
|
+
this.logger.debug("Failed to get SEO data for page", { pageId: page.id });
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
this.logger.info("Retrieved posts with SEO data", {
|
|
343
|
+
totalPosts: posts?.length || 0,
|
|
344
|
+
totalPages: pages.length,
|
|
345
|
+
withSEOData: results.filter((r) => r.seoData).length,
|
|
346
|
+
});
|
|
347
|
+
return results;
|
|
348
|
+
}
|
|
349
|
+
catch (_error) {
|
|
350
|
+
handleToolError(_error, "get all posts with SEO data", params);
|
|
351
|
+
throw _error;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Extract SEO metadata from WordPress post/page object
|
|
356
|
+
*/
|
|
357
|
+
extractSEOMetadata(content) {
|
|
358
|
+
const meta = (Array.isArray(content.meta) ? {} : content.meta || {});
|
|
359
|
+
const fields = this.pluginFields[this.detectedPlugin];
|
|
360
|
+
// Extract basic metadata with plugin-specific field handling
|
|
361
|
+
const focusKeyword = this.getPluginFocusKeyword(meta, fields);
|
|
362
|
+
const canonical = this.extractMetaValue(meta, fields?.canonical);
|
|
363
|
+
const metadata = {
|
|
364
|
+
title: this.extractMetaValue(meta, fields?.title) || content.title?.rendered || "",
|
|
365
|
+
description: this.extractMetaValue(meta, fields?.description) || content.excerpt?.rendered || "",
|
|
366
|
+
...(focusKeyword && { focusKeyword }),
|
|
367
|
+
...(canonical && { canonical }),
|
|
368
|
+
};
|
|
369
|
+
// Extract robots directives based on plugin
|
|
370
|
+
if (this.detectedPlugin === "yoast" && "noindex" in fields && "nofollow" in fields) {
|
|
371
|
+
metadata.robots = {
|
|
372
|
+
index: this.extractMetaValue(meta, fields.noindex) !== "1",
|
|
373
|
+
follow: this.extractMetaValue(meta, fields.nofollow) !== "1",
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
else if (this.detectedPlugin === "rankmath" && "robots" in fields) {
|
|
377
|
+
const robotsValue = this.extractMetaValue(meta, fields.robots) || "";
|
|
378
|
+
metadata.robots = {
|
|
379
|
+
index: !robotsValue.includes("noindex"),
|
|
380
|
+
follow: !robotsValue.includes("nofollow"),
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
// Extract OpenGraph and Twitter data based on plugin
|
|
384
|
+
if (this.detectedPlugin === "yoast" && meta.yoast_head_json) {
|
|
385
|
+
const yoastData = meta.yoast_head_json;
|
|
386
|
+
metadata.openGraph = {
|
|
387
|
+
title: yoastData.og_title || metadata.title,
|
|
388
|
+
description: yoastData.og_description || metadata.description,
|
|
389
|
+
type: content.type === "page" ? "website" : "article",
|
|
390
|
+
url: content.link,
|
|
391
|
+
};
|
|
392
|
+
const twitterTitle = yoastData.twitter_title;
|
|
393
|
+
const twitterDescription = yoastData.twitter_description;
|
|
394
|
+
metadata.twitterCard = {
|
|
395
|
+
card: "summary",
|
|
396
|
+
...(twitterTitle && { title: twitterTitle }),
|
|
397
|
+
...(twitterDescription && { description: twitterDescription }),
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
else {
|
|
401
|
+
// Default OpenGraph data
|
|
402
|
+
metadata.openGraph = {
|
|
403
|
+
title: metadata.title,
|
|
404
|
+
description: metadata.description,
|
|
405
|
+
type: content.type === "page" ? "website" : "article",
|
|
406
|
+
url: content.link,
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
return metadata;
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* Extract schema markup from post meta
|
|
413
|
+
*/
|
|
414
|
+
extractSchemaMarkup(content) {
|
|
415
|
+
const meta = (Array.isArray(content.meta) ? {} : content.meta || {});
|
|
416
|
+
const fields = this.pluginFields[this.detectedPlugin];
|
|
417
|
+
const schemaData = this.getPluginSchemaData(meta, fields);
|
|
418
|
+
if (!schemaData) {
|
|
419
|
+
return undefined;
|
|
420
|
+
}
|
|
421
|
+
try {
|
|
422
|
+
// Try to parse JSON-LD schema
|
|
423
|
+
if (typeof schemaData === "string" && schemaData.startsWith("{")) {
|
|
424
|
+
return JSON.parse(schemaData);
|
|
425
|
+
}
|
|
426
|
+
// Handle Yoast schema page types
|
|
427
|
+
if (this.detectedPlugin === "yoast" && schemaData) {
|
|
428
|
+
return {
|
|
429
|
+
"@context": "https://schema.org",
|
|
430
|
+
"@type": schemaData,
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
return undefined;
|
|
434
|
+
}
|
|
435
|
+
catch {
|
|
436
|
+
return undefined;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
/**
|
|
440
|
+
* Prepare meta fields for SEO metadata update
|
|
441
|
+
*/
|
|
442
|
+
prepareSEOMetaFields(metadata) {
|
|
443
|
+
const fields = this.pluginFields[this.detectedPlugin];
|
|
444
|
+
const metaFields = {};
|
|
445
|
+
// Map metadata to plugin-specific fields
|
|
446
|
+
if (metadata.title && fields?.title) {
|
|
447
|
+
metaFields[fields.title] = metadata.title;
|
|
448
|
+
}
|
|
449
|
+
if (metadata.description && fields?.description) {
|
|
450
|
+
metaFields[fields.description] = metadata.description;
|
|
451
|
+
}
|
|
452
|
+
if (metadata.focusKeyword) {
|
|
453
|
+
const focusKeywordField = this.getPluginFocusKeywordField(fields);
|
|
454
|
+
if (focusKeywordField) {
|
|
455
|
+
metaFields[focusKeywordField] = metadata.focusKeyword;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
if (metadata.canonical && fields?.canonical) {
|
|
459
|
+
metaFields[fields.canonical] = metadata.canonical;
|
|
460
|
+
}
|
|
461
|
+
// Handle robots directives
|
|
462
|
+
if (metadata.robots && this.detectedPlugin === "yoast" && "noindex" in fields && "nofollow" in fields) {
|
|
463
|
+
if (fields.noindex) {
|
|
464
|
+
metaFields[fields.noindex] = metadata.robots.index ? "0" : "1";
|
|
465
|
+
}
|
|
466
|
+
if (fields.nofollow) {
|
|
467
|
+
metaFields[fields.nofollow] = metadata.robots.follow ? "0" : "1";
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
else if (metadata.robots && this.detectedPlugin === "rankmath" && "robots" in fields) {
|
|
471
|
+
const robotsArray = [];
|
|
472
|
+
if (!metadata.robots.index)
|
|
473
|
+
robotsArray.push("noindex");
|
|
474
|
+
if (!metadata.robots.follow)
|
|
475
|
+
robotsArray.push("nofollow");
|
|
476
|
+
metaFields[fields.robots] = robotsArray.join(",");
|
|
477
|
+
}
|
|
478
|
+
return metaFields;
|
|
479
|
+
}
|
|
480
|
+
/**
|
|
481
|
+
* Extract meta value with array handling
|
|
482
|
+
*/
|
|
483
|
+
extractMetaValue(meta, fieldName) {
|
|
484
|
+
if (!fieldName) {
|
|
485
|
+
return null;
|
|
486
|
+
}
|
|
487
|
+
// For Yoast, check yoast_head_json first
|
|
488
|
+
if (this.detectedPlugin === "yoast" && meta.yoast_head_json) {
|
|
489
|
+
const yoastData = meta.yoast_head_json;
|
|
490
|
+
// Map field names to yoast_head_json properties
|
|
491
|
+
const yoastFieldMap = {
|
|
492
|
+
'_yoast_wpseo_title': 'title',
|
|
493
|
+
'_yoast_wpseo_metadesc': 'description',
|
|
494
|
+
'_yoast_wpseo_canonical': 'canonical',
|
|
495
|
+
'_yoast_wpseo_focuskw': 'focuskw'
|
|
496
|
+
};
|
|
497
|
+
const yoastField = yoastFieldMap[fieldName];
|
|
498
|
+
if (yoastField && yoastData[yoastField]) {
|
|
499
|
+
return yoastData[yoastField];
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
// Fallback to direct meta field lookup
|
|
503
|
+
if (!meta[fieldName]) {
|
|
504
|
+
return null;
|
|
505
|
+
}
|
|
506
|
+
const value = meta[fieldName];
|
|
507
|
+
// WordPress meta values can be arrays
|
|
508
|
+
if (Array.isArray(value)) {
|
|
509
|
+
return value[0] || null;
|
|
510
|
+
}
|
|
511
|
+
return value || null;
|
|
512
|
+
}
|
|
513
|
+
/**
|
|
514
|
+
* Get plugin-specific focus keyword field
|
|
515
|
+
*/
|
|
516
|
+
getPluginFocusKeyword(meta, fields) {
|
|
517
|
+
if (this.detectedPlugin === "seopress" && "targetKeyword" in fields) {
|
|
518
|
+
return this.extractMetaValue(meta, fields.targetKeyword) || undefined;
|
|
519
|
+
}
|
|
520
|
+
else if ("focusKeyword" in fields) {
|
|
521
|
+
return this.extractMetaValue(meta, fields.focusKeyword) || undefined;
|
|
522
|
+
}
|
|
523
|
+
return undefined;
|
|
524
|
+
}
|
|
525
|
+
/**
|
|
526
|
+
* Get plugin-specific schema data field
|
|
527
|
+
*/
|
|
528
|
+
getPluginSchemaData(meta, fields) {
|
|
529
|
+
if ("schema" in fields) {
|
|
530
|
+
return this.extractMetaValue(meta, fields.schema) || undefined;
|
|
531
|
+
}
|
|
532
|
+
return undefined;
|
|
533
|
+
}
|
|
534
|
+
/**
|
|
535
|
+
* Get plugin-specific focus keyword field name
|
|
536
|
+
*/
|
|
537
|
+
getPluginFocusKeywordField(fields) {
|
|
538
|
+
if (this.detectedPlugin === "seopress" && "targetKeyword" in fields) {
|
|
539
|
+
return fields.targetKeyword;
|
|
540
|
+
}
|
|
541
|
+
else if ("focusKeyword" in fields) {
|
|
542
|
+
return fields.focusKeyword;
|
|
543
|
+
}
|
|
544
|
+
return undefined;
|
|
545
|
+
}
|
|
546
|
+
/**
|
|
547
|
+
* Test SEO plugin integration
|
|
548
|
+
*/
|
|
549
|
+
async testSEOIntegration() {
|
|
550
|
+
this.logger.info("Testing SEO integration");
|
|
551
|
+
const result = {
|
|
552
|
+
pluginDetected: this.detectedPlugin,
|
|
553
|
+
canReadMetadata: false,
|
|
554
|
+
canWriteMetadata: false,
|
|
555
|
+
samplePostsWithSEO: 0,
|
|
556
|
+
errors: [],
|
|
557
|
+
};
|
|
558
|
+
try {
|
|
559
|
+
// Test reading metadata from recent posts
|
|
560
|
+
const testPosts = await this.getPosts({ per_page: 5, status: ["publish"] });
|
|
561
|
+
if (testPosts && testPosts.length > 0) {
|
|
562
|
+
let postsWithSEO = 0;
|
|
563
|
+
for (const post of testPosts) {
|
|
564
|
+
try {
|
|
565
|
+
const seoData = await this.getSEOMetadata(post.id);
|
|
566
|
+
if (seoData.title || seoData.description) {
|
|
567
|
+
postsWithSEO++;
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
catch (_error) {
|
|
571
|
+
result.errors?.push(`Failed to read SEO data for post ${post.id}: ${_error.message}`);
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
result.samplePostsWithSEO = postsWithSEO;
|
|
575
|
+
result.canReadMetadata = true;
|
|
576
|
+
}
|
|
577
|
+
// Test writing metadata (if we have posts to test with)
|
|
578
|
+
if (testPosts && testPosts.length > 0 && result.canReadMetadata) {
|
|
579
|
+
try {
|
|
580
|
+
const testPost = testPosts[0];
|
|
581
|
+
const originalSEO = await this.getSEOMetadata(testPost.id);
|
|
582
|
+
// Make a small test update
|
|
583
|
+
const testMetadata = {
|
|
584
|
+
description: (originalSEO.description || "") + " [TEST]",
|
|
585
|
+
};
|
|
586
|
+
await this.updateSEOMetadata(testPost.id, testMetadata);
|
|
587
|
+
// Restore original data
|
|
588
|
+
await this.updateSEOMetadata(testPost.id, {
|
|
589
|
+
description: originalSEO.description || "",
|
|
590
|
+
});
|
|
591
|
+
result.canWriteMetadata = true;
|
|
592
|
+
}
|
|
593
|
+
catch (_error) {
|
|
594
|
+
result.errors?.push(`Failed to write SEO data: ${_error.message}`);
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
this.logger.info("SEO integration test completed", result);
|
|
598
|
+
return result;
|
|
599
|
+
}
|
|
600
|
+
catch (_error) {
|
|
601
|
+
result.errors?.push(`SEO integration test failed: ${_error.message}`);
|
|
602
|
+
return result;
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
/**
|
|
606
|
+
* Get integration status for SEO features
|
|
607
|
+
*/
|
|
608
|
+
getIntegrationStatus() {
|
|
609
|
+
const hasPlugin = this.detectedPlugin !== "none";
|
|
610
|
+
return {
|
|
611
|
+
hasPlugin,
|
|
612
|
+
plugin: this.detectedPlugin,
|
|
613
|
+
canReadMetadata: hasPlugin,
|
|
614
|
+
canWriteMetadata: hasPlugin,
|
|
615
|
+
features: {
|
|
616
|
+
metaTags: hasPlugin,
|
|
617
|
+
schema: hasPlugin,
|
|
618
|
+
socialMedia: hasPlugin,
|
|
619
|
+
xmlSitemap: false, // Would need additional API calls
|
|
620
|
+
breadcrumbs: false,
|
|
621
|
+
},
|
|
622
|
+
};
|
|
623
|
+
}
|
|
624
|
+
/**
|
|
625
|
+
* Check if content has plugin-specific SEO data
|
|
626
|
+
*/
|
|
627
|
+
hasPluginData(content) {
|
|
628
|
+
const meta = (Array.isArray(content.meta) ? {} : content.meta || {});
|
|
629
|
+
switch (this.detectedPlugin) {
|
|
630
|
+
case "yoast":
|
|
631
|
+
// Check for yoast_head_json or individual meta fields
|
|
632
|
+
return (meta.yoast_head_json !== null && meta.yoast_head_json !== undefined) ||
|
|
633
|
+
(meta._yoast_wpseo_title !== null && meta._yoast_wpseo_title !== undefined);
|
|
634
|
+
case "rankmath":
|
|
635
|
+
return meta.rank_math_title !== null && meta.rank_math_title !== undefined;
|
|
636
|
+
case "seopress":
|
|
637
|
+
return meta._seopress_titles_title !== null && meta._seopress_titles_title !== undefined;
|
|
638
|
+
default:
|
|
639
|
+
return false;
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
/**
|
|
643
|
+
* Get raw plugin data for debugging purposes
|
|
644
|
+
*/
|
|
645
|
+
getRawPluginData(content) {
|
|
646
|
+
const meta = (Array.isArray(content.meta) ? {} : content.meta || {});
|
|
647
|
+
// Return the raw plugin-specific data based on detected plugin
|
|
648
|
+
switch (this.detectedPlugin) {
|
|
649
|
+
case "yoast":
|
|
650
|
+
return meta.yoast_head_json || {};
|
|
651
|
+
case "rankmath":
|
|
652
|
+
// Extract RankMath specific fields
|
|
653
|
+
const rankMathData = {};
|
|
654
|
+
Object.keys(meta).forEach(key => {
|
|
655
|
+
if (key.startsWith('rank_math_')) {
|
|
656
|
+
rankMathData[key] = meta[key];
|
|
657
|
+
}
|
|
658
|
+
});
|
|
659
|
+
return rankMathData;
|
|
660
|
+
case "seopress":
|
|
661
|
+
// Extract SEOPress specific fields
|
|
662
|
+
const seopressData = {};
|
|
663
|
+
Object.keys(meta).forEach(key => {
|
|
664
|
+
if (key.startsWith('_seopress_')) {
|
|
665
|
+
seopressData[key] = meta[key];
|
|
666
|
+
}
|
|
667
|
+
});
|
|
668
|
+
return seopressData;
|
|
669
|
+
default:
|
|
670
|
+
return {};
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
//# sourceMappingURL=SEOWordPressClient.js.map
|