mcp-wordpress 2.6.4 → 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.
Files changed (332) hide show
  1. package/README.md +1 -1
  2. package/dist/cache/CacheInvalidation.d.ts +25 -6
  3. package/dist/cache/CacheInvalidation.d.ts.map +1 -1
  4. package/dist/cache/CacheInvalidation.js +168 -16
  5. package/dist/cache/CacheInvalidation.js.map +1 -1
  6. package/dist/cache/HttpCacheWrapper.d.ts.map +1 -1
  7. package/dist/cache/HttpCacheWrapper.js +3 -4
  8. package/dist/cache/HttpCacheWrapper.js.map +1 -1
  9. package/dist/cache/SEOCacheManager.d.ts +150 -0
  10. package/dist/cache/SEOCacheManager.d.ts.map +1 -0
  11. package/dist/cache/SEOCacheManager.js +275 -0
  12. package/dist/cache/SEOCacheManager.js.map +1 -0
  13. package/dist/client/SEOWordPressClient.d.ts +164 -0
  14. package/dist/client/SEOWordPressClient.d.ts.map +1 -0
  15. package/dist/client/SEOWordPressClient.js +674 -0
  16. package/dist/client/SEOWordPressClient.js.map +1 -0
  17. package/dist/client/api.d.ts.map +1 -1
  18. package/dist/client/api.js +50 -20
  19. package/dist/client/api.js.map +1 -1
  20. package/dist/client/auth.js +19 -19
  21. package/dist/client/auth.js.map +1 -1
  22. package/dist/client/index.d.ts +11 -0
  23. package/dist/client/index.d.ts.map +1 -0
  24. package/dist/client/index.js +14 -0
  25. package/dist/client/index.js.map +1 -0
  26. package/dist/client/managers/AuthManager.d.ts +39 -0
  27. package/dist/client/managers/AuthManager.d.ts.map +1 -0
  28. package/dist/client/managers/AuthManager.js +142 -0
  29. package/dist/client/managers/AuthManager.js.map +1 -0
  30. package/dist/client/managers/AuthenticationManager.d.ts.map +1 -1
  31. package/dist/client/managers/AuthenticationManager.js +10 -9
  32. package/dist/client/managers/AuthenticationManager.js.map +1 -1
  33. package/dist/client/managers/BaseManager.d.ts.map +1 -1
  34. package/dist/client/managers/BaseManager.js +12 -0
  35. package/dist/client/managers/BaseManager.js.map +1 -1
  36. package/dist/client/managers/ComposedAuthenticationManager.d.ts +94 -0
  37. package/dist/client/managers/ComposedAuthenticationManager.d.ts.map +1 -0
  38. package/dist/client/managers/ComposedAuthenticationManager.js +340 -0
  39. package/dist/client/managers/ComposedAuthenticationManager.js.map +1 -0
  40. package/dist/client/managers/ComposedManagerFactory.d.ts +104 -0
  41. package/dist/client/managers/ComposedManagerFactory.d.ts.map +1 -0
  42. package/dist/client/managers/ComposedManagerFactory.js +180 -0
  43. package/dist/client/managers/ComposedManagerFactory.js.map +1 -0
  44. package/dist/client/managers/ComposedRequestManager.d.ts +82 -0
  45. package/dist/client/managers/ComposedRequestManager.d.ts.map +1 -0
  46. package/dist/client/managers/ComposedRequestManager.js +260 -0
  47. package/dist/client/managers/ComposedRequestManager.js.map +1 -0
  48. package/dist/client/managers/JWTAuthImplementation.d.ts +86 -0
  49. package/dist/client/managers/JWTAuthImplementation.d.ts.map +1 -0
  50. package/dist/client/managers/JWTAuthImplementation.js +240 -0
  51. package/dist/client/managers/JWTAuthImplementation.js.map +1 -0
  52. package/dist/client/managers/ManagersIndex.d.ts +6 -0
  53. package/dist/client/managers/ManagersIndex.d.ts.map +1 -0
  54. package/dist/client/managers/ManagersIndex.js +6 -0
  55. package/dist/client/managers/ManagersIndex.js.map +1 -0
  56. package/dist/client/managers/RequestManager.d.ts.map +1 -1
  57. package/dist/client/managers/RequestManager.js +5 -3
  58. package/dist/client/managers/RequestManager.js.map +1 -1
  59. package/dist/client/managers/composed/MigrationAdapter.d.ts +80 -0
  60. package/dist/client/managers/composed/MigrationAdapter.d.ts.map +1 -0
  61. package/dist/client/managers/composed/MigrationAdapter.js +214 -0
  62. package/dist/client/managers/composed/MigrationAdapter.js.map +1 -0
  63. package/dist/client/managers/composed/index.d.ts +23 -0
  64. package/dist/client/managers/composed/index.d.ts.map +1 -0
  65. package/dist/client/managers/composed/index.js +26 -0
  66. package/dist/client/managers/composed/index.js.map +1 -0
  67. package/dist/client/managers/implementations/ConfigurationProviderImpl.d.ts +27 -0
  68. package/dist/client/managers/implementations/ConfigurationProviderImpl.d.ts.map +1 -0
  69. package/dist/client/managers/implementations/ConfigurationProviderImpl.js +41 -0
  70. package/dist/client/managers/implementations/ConfigurationProviderImpl.js.map +1 -0
  71. package/dist/client/managers/implementations/ErrorHandlerImpl.d.ts +31 -0
  72. package/dist/client/managers/implementations/ErrorHandlerImpl.d.ts.map +1 -0
  73. package/dist/client/managers/implementations/ErrorHandlerImpl.js +73 -0
  74. package/dist/client/managers/implementations/ErrorHandlerImpl.js.map +1 -0
  75. package/dist/client/managers/implementations/ParameterValidatorImpl.d.ts +47 -0
  76. package/dist/client/managers/implementations/ParameterValidatorImpl.d.ts.map +1 -0
  77. package/dist/client/managers/implementations/ParameterValidatorImpl.js +141 -0
  78. package/dist/client/managers/implementations/ParameterValidatorImpl.js.map +1 -0
  79. package/dist/client/managers/interfaces/ManagerInterfaces.d.ts +147 -0
  80. package/dist/client/managers/interfaces/ManagerInterfaces.d.ts.map +1 -0
  81. package/dist/client/managers/interfaces/ManagerInterfaces.js +6 -0
  82. package/dist/client/managers/interfaces/ManagerInterfaces.js.map +1 -0
  83. package/dist/config/Config.d.ts +30 -0
  84. package/dist/config/Config.d.ts.map +1 -1
  85. package/dist/config/Config.js +30 -0
  86. package/dist/config/Config.js.map +1 -1
  87. package/dist/config/ConfigurationSchema.d.ts +75 -198
  88. package/dist/config/ConfigurationSchema.d.ts.map +1 -1
  89. package/dist/config/ConfigurationSchema.js +17 -17
  90. package/dist/config/ConfigurationSchema.js.map +1 -1
  91. package/dist/config/ServerConfiguration.d.ts +2 -2
  92. package/dist/config/ServerConfiguration.d.ts.map +1 -1
  93. package/dist/config/ServerConfiguration.js +15 -13
  94. package/dist/config/ServerConfiguration.js.map +1 -1
  95. package/dist/config/index.d.ts +8 -0
  96. package/dist/config/index.d.ts.map +1 -0
  97. package/dist/config/index.js +11 -0
  98. package/dist/config/index.js.map +1 -0
  99. package/dist/docs/DocumentationGenerator.js +2 -2
  100. package/dist/docs/DocumentationGenerator.js.map +1 -1
  101. package/dist/dxt-entry.js +3 -3
  102. package/dist/dxt-entry.js.map +1 -1
  103. package/dist/index.d.ts +1 -0
  104. package/dist/index.d.ts.map +1 -1
  105. package/dist/index.js +38 -37
  106. package/dist/index.js.map +1 -1
  107. package/dist/performance/MetricsCollector.d.ts.map +1 -1
  108. package/dist/performance/MetricsCollector.js +5 -4
  109. package/dist/performance/MetricsCollector.js.map +1 -1
  110. package/dist/security/AISecurityScanner.js +7 -7
  111. package/dist/security/AISecurityScanner.js.map +1 -1
  112. package/dist/security/AutomatedRemediation.d.ts.map +1 -1
  113. package/dist/security/AutomatedRemediation.js +11 -11
  114. package/dist/security/AutomatedRemediation.js.map +1 -1
  115. package/dist/security/InputValidator.d.ts +50 -126
  116. package/dist/security/InputValidator.d.ts.map +1 -1
  117. package/dist/security/InputValidator.js +9 -9
  118. package/dist/security/InputValidator.js.map +1 -1
  119. package/dist/security/SecurityCIPipeline.d.ts +47 -5
  120. package/dist/security/SecurityCIPipeline.d.ts.map +1 -1
  121. package/dist/security/SecurityCIPipeline.js +390 -49
  122. package/dist/security/SecurityCIPipeline.js.map +1 -1
  123. package/dist/security/SecurityConfigManager.js +10 -10
  124. package/dist/security/SecurityConfigManager.js.map +1 -1
  125. package/dist/security/SecurityMonitoring.js +4 -4
  126. package/dist/security/SecurityMonitoring.js.map +1 -1
  127. package/dist/security/SecurityReviewer.d.ts.map +1 -1
  128. package/dist/security/SecurityReviewer.js +13 -6
  129. package/dist/security/SecurityReviewer.js.map +1 -1
  130. package/dist/security/index.js +3 -3
  131. package/dist/security/index.js.map +1 -1
  132. package/dist/server/ConnectionTester.js +5 -5
  133. package/dist/server/ConnectionTester.js.map +1 -1
  134. package/dist/server/ToolRegistry.d.ts +2 -2
  135. package/dist/server/ToolRegistry.d.ts.map +1 -1
  136. package/dist/server/ToolRegistry.js +7 -6
  137. package/dist/server/ToolRegistry.js.map +1 -1
  138. package/dist/server/index.d.ts +7 -0
  139. package/dist/server/index.d.ts.map +1 -0
  140. package/dist/server/index.js +9 -0
  141. package/dist/server/index.js.map +1 -0
  142. package/dist/tools/BaseToolManager.d.ts.map +1 -1
  143. package/dist/tools/BaseToolManager.js +11 -11
  144. package/dist/tools/BaseToolManager.js.map +1 -1
  145. package/dist/tools/auth.d.ts.map +1 -1
  146. package/dist/tools/auth.js +7 -7
  147. package/dist/tools/auth.js.map +1 -1
  148. package/dist/tools/comments.d.ts.map +1 -1
  149. package/dist/tools/comments.js +14 -14
  150. package/dist/tools/comments.js.map +1 -1
  151. package/dist/tools/index.d.ts +1 -0
  152. package/dist/tools/index.d.ts.map +1 -1
  153. package/dist/tools/index.js +1 -0
  154. package/dist/tools/index.js.map +1 -1
  155. package/dist/tools/media.d.ts.map +1 -1
  156. package/dist/tools/media.js +14 -11
  157. package/dist/tools/media.js.map +1 -1
  158. package/dist/tools/pages.d.ts.map +1 -1
  159. package/dist/tools/pages.js +12 -12
  160. package/dist/tools/pages.js.map +1 -1
  161. package/dist/tools/performance.d.ts.map +1 -1
  162. package/dist/tools/performance.js +13 -11
  163. package/dist/tools/performance.js.map +1 -1
  164. package/dist/tools/posts/PostHandlers.d.ts.map +1 -1
  165. package/dist/tools/posts/PostHandlers.js +16 -16
  166. package/dist/tools/posts/PostHandlers.js.map +1 -1
  167. package/dist/tools/posts/PostToolDefinitions.d.ts.map +1 -1
  168. package/dist/tools/posts/index.d.ts.map +1 -1
  169. package/dist/tools/seo/BulkOperations.d.ts +113 -0
  170. package/dist/tools/seo/BulkOperations.d.ts.map +1 -0
  171. package/dist/tools/seo/BulkOperations.js +398 -0
  172. package/dist/tools/seo/BulkOperations.js.map +1 -0
  173. package/dist/tools/seo/SEOHandlers.d.ts +55 -0
  174. package/dist/tools/seo/SEOHandlers.d.ts.map +1 -0
  175. package/dist/tools/seo/SEOHandlers.js +255 -0
  176. package/dist/tools/seo/SEOHandlers.js.map +1 -0
  177. package/dist/tools/seo/SEOToolDefinitions.d.ts +59 -0
  178. package/dist/tools/seo/SEOToolDefinitions.d.ts.map +1 -0
  179. package/dist/tools/seo/SEOToolDefinitions.js +385 -0
  180. package/dist/tools/seo/SEOToolDefinitions.js.map +1 -0
  181. package/dist/tools/seo/SEOTools.d.ts +203 -0
  182. package/dist/tools/seo/SEOTools.d.ts.map +1 -0
  183. package/dist/tools/seo/SEOTools.js +708 -0
  184. package/dist/tools/seo/SEOTools.js.map +1 -0
  185. package/dist/tools/seo/analyzers/ContentAnalyzer.d.ts +94 -0
  186. package/dist/tools/seo/analyzers/ContentAnalyzer.d.ts.map +1 -0
  187. package/dist/tools/seo/analyzers/ContentAnalyzer.js +402 -0
  188. package/dist/tools/seo/analyzers/ContentAnalyzer.js.map +1 -0
  189. package/dist/tools/seo/auditors/SiteAuditor.d.ts +121 -0
  190. package/dist/tools/seo/auditors/SiteAuditor.d.ts.map +1 -0
  191. package/dist/tools/seo/auditors/SiteAuditor.js +600 -0
  192. package/dist/tools/seo/auditors/SiteAuditor.js.map +1 -0
  193. package/dist/tools/seo/generators/MetaGenerator.d.ts +128 -0
  194. package/dist/tools/seo/generators/MetaGenerator.d.ts.map +1 -0
  195. package/dist/tools/seo/generators/MetaGenerator.js +547 -0
  196. package/dist/tools/seo/generators/MetaGenerator.js.map +1 -0
  197. package/dist/tools/seo/generators/SchemaGenerator.d.ts +204 -0
  198. package/dist/tools/seo/generators/SchemaGenerator.d.ts.map +1 -0
  199. package/dist/tools/seo/generators/SchemaGenerator.js +670 -0
  200. package/dist/tools/seo/generators/SchemaGenerator.js.map +1 -0
  201. package/dist/tools/seo/index.d.ts +17 -0
  202. package/dist/tools/seo/index.d.ts.map +1 -0
  203. package/dist/tools/seo/index.js +18 -0
  204. package/dist/tools/seo/index.js.map +1 -0
  205. package/dist/tools/seo/optimizers/InternalLinkingSuggester.d.ts +186 -0
  206. package/dist/tools/seo/optimizers/InternalLinkingSuggester.d.ts.map +1 -0
  207. package/dist/tools/seo/optimizers/InternalLinkingSuggester.js +683 -0
  208. package/dist/tools/seo/optimizers/InternalLinkingSuggester.js.map +1 -0
  209. package/dist/tools/site.d.ts.map +1 -1
  210. package/dist/tools/site.js +12 -12
  211. package/dist/tools/site.js.map +1 -1
  212. package/dist/tools/taxonomies.d.ts.map +1 -1
  213. package/dist/tools/taxonomies.js +20 -20
  214. package/dist/tools/taxonomies.js.map +1 -1
  215. package/dist/tools/users.d.ts.map +1 -1
  216. package/dist/tools/users.js +12 -12
  217. package/dist/tools/users.js.map +1 -1
  218. package/dist/types/client.d.ts +8 -6
  219. package/dist/types/client.d.ts.map +1 -1
  220. package/dist/types/client.js.map +1 -1
  221. package/dist/types/seo.d.ts +473 -0
  222. package/dist/types/seo.d.ts.map +1 -0
  223. package/dist/types/seo.js +94 -0
  224. package/dist/types/seo.js.map +1 -0
  225. package/dist/utils/enhancedError.js +1 -1
  226. package/dist/utils/enhancedError.js.map +1 -1
  227. package/dist/utils/error.d.ts.map +1 -1
  228. package/dist/utils/error.js +0 -1
  229. package/dist/utils/error.js.map +1 -1
  230. package/dist/utils/index.d.ts +12 -0
  231. package/dist/utils/index.d.ts.map +1 -0
  232. package/dist/utils/index.js +18 -0
  233. package/dist/utils/index.js.map +1 -0
  234. package/dist/utils/logger.js +3 -3
  235. package/dist/utils/logger.js.map +1 -1
  236. package/dist/utils/toolWrapper.d.ts +2 -2
  237. package/dist/utils/toolWrapper.js +8 -8
  238. package/dist/utils/toolWrapper.js.map +1 -1
  239. package/dist/utils/validation/core.d.ts.map +1 -1
  240. package/dist/utils/validation/core.js.map +1 -1
  241. package/dist/utils/validation/index.d.ts.map +1 -1
  242. package/dist/utils/validation/index.js.map +1 -1
  243. package/dist/utils/validation/network.js +3 -3
  244. package/dist/utils/validation/network.js.map +1 -1
  245. package/dist/utils/validation/rateLimit.js.map +1 -1
  246. package/dist/utils/validation/security.js.map +1 -1
  247. package/dist/utils/validation/wordpress.js.map +1 -1
  248. package/dist/utils/version.d.ts +144 -0
  249. package/dist/utils/version.d.ts.map +1 -0
  250. package/dist/utils/version.js +318 -0
  251. package/dist/utils/version.js.map +1 -0
  252. package/package.json +21 -55
  253. package/src/cache/CacheInvalidation.ts +183 -20
  254. package/src/cache/HttpCacheWrapper.ts +8 -5
  255. package/src/cache/SEOCacheManager.ts +330 -0
  256. package/src/cache/__tests__/CacheInvalidation.test.ts +6 -11
  257. package/src/cache/__tests__/CachedWordPressClient.test.ts +37 -62
  258. package/src/client/SEOWordPressClient.ts +876 -0
  259. package/src/client/api.ts +50 -21
  260. package/src/client/auth.ts +19 -19
  261. package/src/client/index.ts +16 -0
  262. package/src/client/managers/AuthManager.ts +175 -0
  263. package/src/client/managers/AuthenticationManager.ts +16 -14
  264. package/src/client/managers/BaseManager.ts +24 -5
  265. package/src/client/managers/ComposedAuthenticationManager.ts +409 -0
  266. package/src/client/managers/ComposedManagerFactory.ts +231 -0
  267. package/src/client/managers/ComposedRequestManager.ts +336 -0
  268. package/src/client/managers/JWTAuthImplementation.ts +326 -0
  269. package/src/client/managers/ManagersIndex.ts +6 -0
  270. package/src/client/managers/RequestManager.ts +9 -7
  271. package/src/client/managers/composed/MigrationAdapter.ts +263 -0
  272. package/src/client/managers/composed/index.ts +47 -0
  273. package/src/client/managers/implementations/ConfigurationProviderImpl.ts +52 -0
  274. package/src/client/managers/implementations/ErrorHandlerImpl.ts +102 -0
  275. package/src/client/managers/implementations/ParameterValidatorImpl.ts +221 -0
  276. package/src/client/managers/interfaces/ManagerInterfaces.ts +171 -0
  277. package/src/config/Config.ts +63 -0
  278. package/src/config/ConfigurationSchema.ts +17 -17
  279. package/src/config/ServerConfiguration.ts +18 -16
  280. package/src/config/index.ts +13 -0
  281. package/src/docs/DocumentationGenerator.ts +2 -2
  282. package/src/dxt-entry.ts +3 -3
  283. package/src/index.ts +43 -43
  284. package/src/performance/MetricsCollector.ts +15 -11
  285. package/src/security/AISecurityScanner.ts +7 -7
  286. package/src/security/AutomatedRemediation.ts +13 -11
  287. package/src/security/InputValidator.ts +10 -9
  288. package/src/security/SecurityCIPipeline.ts +494 -56
  289. package/src/security/SecurityConfigManager.ts +10 -10
  290. package/src/security/SecurityMonitoring.ts +5 -5
  291. package/src/security/SecurityReviewer.ts +13 -6
  292. package/src/security/index.ts +3 -3
  293. package/src/server/ConnectionTester.ts +5 -5
  294. package/src/server/ToolRegistry.ts +9 -8
  295. package/src/server/index.ts +10 -0
  296. package/src/tools/BaseToolManager.ts +55 -83
  297. package/src/tools/auth.ts +21 -12
  298. package/src/tools/comments.ts +23 -19
  299. package/src/tools/index.ts +1 -0
  300. package/src/tools/media.ts +23 -20
  301. package/src/tools/pages.ts +20 -13
  302. package/src/tools/performance.ts +101 -32
  303. package/src/tools/posts/PostHandlers.ts +23 -23
  304. package/src/tools/posts/PostToolDefinitions.ts +1 -1
  305. package/src/tools/posts/index.ts +2 -2
  306. package/src/tools/seo/BulkOperations.ts +557 -0
  307. package/src/tools/seo/SEOHandlers.ts +296 -0
  308. package/src/tools/seo/SEOToolDefinitions.ts +402 -0
  309. package/src/tools/seo/SEOTools.ts +871 -0
  310. package/src/tools/seo/analyzers/ContentAnalyzer.ts +493 -0
  311. package/src/tools/seo/auditors/SiteAuditor.ts +787 -0
  312. package/src/tools/seo/generators/MetaGenerator.ts +694 -0
  313. package/src/tools/seo/generators/SchemaGenerator.ts +955 -0
  314. package/src/tools/seo/index.ts +47 -0
  315. package/src/tools/seo/optimizers/InternalLinkingSuggester.ts +934 -0
  316. package/src/tools/site.ts +27 -26
  317. package/src/tools/taxonomies.ts +29 -25
  318. package/src/tools/users.ts +20 -13
  319. package/src/types/client.ts +8 -6
  320. package/src/types/seo.ts +546 -0
  321. package/src/utils/enhancedError.ts +1 -1
  322. package/src/utils/error.ts +1 -2
  323. package/src/utils/index.ts +23 -0
  324. package/src/utils/logger.ts +3 -3
  325. package/src/utils/toolWrapper.ts +10 -10
  326. package/src/utils/validation/core.ts +2 -2
  327. package/src/utils/validation/index.ts +2 -2
  328. package/src/utils/validation/network.ts +5 -5
  329. package/src/utils/validation/rateLimit.ts +1 -1
  330. package/src/utils/validation/security.ts +1 -1
  331. package/src/utils/validation/wordpress.ts +1 -1
  332. package/src/utils/version.ts +402 -0
@@ -0,0 +1,708 @@
1
+ /**
2
+ * WordPress SEO Tools - Main Module
3
+ *
4
+ * This module provides comprehensive SEO management functionality for WordPress
5
+ * through the MCP protocol. It combines content analysis, metadata generation,
6
+ * schema markup, and optimization capabilities in a modular architecture.
7
+ *
8
+ * Features:
9
+ * - Content analysis for readability and keyword optimization
10
+ * - Metadata generation with AI assistance and safety filters
11
+ * - Schema markup generation and validation
12
+ * - Internal linking optimization
13
+ * - Site audits and Core Web Vitals monitoring
14
+ * - Bulk operations with progress tracking
15
+ *
16
+ * @since 2.7.0
17
+ */
18
+ import { WordPressClient } from "../../client/api.js";
19
+ import { SEOWordPressClient } from "../../client/SEOWordPressClient.js";
20
+ import { LoggerFactory } from "../../utils/logger.js";
21
+ import { validateRequired, handleToolError } from "../../utils/error.js";
22
+ import { ContentAnalyzer } from "./analyzers/ContentAnalyzer.js";
23
+ import { MetaGenerator } from "./generators/MetaGenerator.js";
24
+ import { SchemaGenerator } from "./generators/SchemaGenerator.js";
25
+ import { InternalLinkingSuggester } from "./optimizers/InternalLinkingSuggester.js";
26
+ import { SiteAuditor } from "./auditors/SiteAuditor.js";
27
+ import { BulkOperations } from "./BulkOperations.js";
28
+ import { SEOCacheManager } from "../../cache/SEOCacheManager.js";
29
+ import { seoToolDefinitions } from "./SEOToolDefinitions.js";
30
+ import { handleAnalyzeContent, handleGenerateMetadata, handleBulkUpdateMetadata, handleGenerateSchema, handleValidateSchema, handleSuggestInternalLinks, handlePerformSiteAudit, handleTrackSERPPositions, handleKeywordResearch, handleTestSEOIntegration, handleGetLiveSEOData, } from "./SEOHandlers.js";
31
+ /**
32
+ * Main SEOTools class that provides WordPress SEO management functionality.
33
+ *
34
+ * This class serves as the interface between the MCP framework and WordPress
35
+ * SEO operations. It follows the established pattern of other tool classes
36
+ * while providing SEO-specific capabilities.
37
+ *
38
+ * The class is designed with a modular architecture:
39
+ * - Analyzers for content evaluation
40
+ * - Generators for metadata and schema creation
41
+ * - Validators for ensuring compliance
42
+ * - Optimizers for performance improvements
43
+ *
44
+ * @since 2.7.0
45
+ */
46
+ export class SEOTools {
47
+ logger = LoggerFactory.tool("seo");
48
+ clients = new Map();
49
+ seoClients = new Map();
50
+ contentAnalyzer;
51
+ metaGenerator;
52
+ schemaGenerator;
53
+ cacheManager;
54
+ // Client-dependent components created per request
55
+ // private internalLinkingSuggester: InternalLinkingSuggester;
56
+ // private siteAuditor: SiteAuditor;
57
+ // private bulkOperations: BulkOperations;
58
+ constructor() {
59
+ this.contentAnalyzer = new ContentAnalyzer();
60
+ this.metaGenerator = new MetaGenerator();
61
+ this.schemaGenerator = new SchemaGenerator();
62
+ this.cacheManager = new SEOCacheManager();
63
+ // Note: Client-dependent components will be initialized when needed in handlers
64
+ // this.internalLinkingSuggester - initialized per request
65
+ // this.siteAuditor - initialized per request
66
+ // this.bulkOperations - initialized per request
67
+ }
68
+ /**
69
+ * Analyzes content for SEO optimization opportunities.
70
+ *
71
+ * Performs comprehensive analysis including:
72
+ * - Readability scoring (Flesch-Kincaid)
73
+ * - Keyword density and distribution
74
+ * - Content structure evaluation
75
+ * - Meta tag optimization suggestions
76
+ *
77
+ * @param params - Analysis parameters including post ID and analysis type
78
+ * @returns Detailed SEO analysis with scores and recommendations
79
+ */
80
+ async analyzeContent(params) {
81
+ const siteLogger = LoggerFactory.tool("wp_seo_analyze_content", params.site);
82
+ return await siteLogger.time("SEO content analysis", async () => {
83
+ try {
84
+ validateRequired(params, ["postId", "analysisType"]);
85
+ const client = this.getSiteClient(params.site);
86
+ // Check cache first
87
+ const cacheKey = `seo:analyze:${params.site}:${params.postId}:${params.analysisType}`;
88
+ const cached = await this.getCachedResult(cacheKey);
89
+ if (cached) {
90
+ siteLogger.debug("Cache hit for content analysis", { cacheKey });
91
+ return cached;
92
+ }
93
+ // Perform analysis (implementation will be added in analyzers)
94
+ const result = await this.performAnalysis(client, params);
95
+ // Cache the result
96
+ await this.cacheResult(cacheKey, result, 21600); // 6 hours
97
+ return result;
98
+ }
99
+ catch (_error) {
100
+ handleToolError(_error, "analyze content", {
101
+ site: params.site,
102
+ postId: params.postId,
103
+ });
104
+ throw _error; // handleToolError will format it properly
105
+ }
106
+ });
107
+ }
108
+ /**
109
+ * Generates optimized metadata for posts.
110
+ *
111
+ * Creates SEO-friendly metadata including:
112
+ * - Title tags (60 character limit)
113
+ * - Meta descriptions (155-160 characters)
114
+ * - OpenGraph tags
115
+ * - Twitter Card metadata
116
+ *
117
+ * @param params - Generation parameters including content and constraints
118
+ * @returns Generated metadata with safety filters applied
119
+ */
120
+ async generateMetadata(params) {
121
+ const siteLogger = LoggerFactory.tool("wp_seo_generate_meta", params.site);
122
+ return await siteLogger.time("Generate SEO metadata", async () => {
123
+ try {
124
+ validateRequired(params, ["postId"]);
125
+ const client = this.getSiteClient(params.site);
126
+ // Implementation will be added in generators
127
+ const metadata = await this.createMetadata(client, params);
128
+ return metadata;
129
+ }
130
+ catch (_error) {
131
+ handleToolError(_error, "generate metadata", {
132
+ site: params.site,
133
+ postId: params.postId,
134
+ });
135
+ throw _error;
136
+ }
137
+ });
138
+ }
139
+ /**
140
+ * Performs bulk metadata updates with progress tracking.
141
+ *
142
+ * Handles large-scale metadata updates with:
143
+ * - Batch processing (10 posts per batch)
144
+ * - Progress event streaming
145
+ * - Retry logic with exponential backoff
146
+ * - Dry-run mode for validation
147
+ *
148
+ * @param params - Bulk operation parameters
149
+ * @returns Operation results with success/failure counts
150
+ */
151
+ async bulkUpdateMetadata(params) {
152
+ const siteLogger = LoggerFactory.tool("wp_seo_bulk_update", params.site);
153
+ return await siteLogger.time("Bulk metadata update", async () => {
154
+ try {
155
+ validateRequired(params, ["postIds", "updates"]);
156
+ const client = this.getSiteClient(params.site);
157
+ // Implementation will be added
158
+ const result = await this.processBulkUpdate(client, params);
159
+ return result;
160
+ }
161
+ catch (_error) {
162
+ handleToolError(_error, "bulk update metadata", {
163
+ site: params.site,
164
+ count: params.postIds?.length,
165
+ });
166
+ throw _error;
167
+ }
168
+ });
169
+ }
170
+ /**
171
+ * Generates structured data schema markup.
172
+ *
173
+ * Creates JSON-LD schema for:
174
+ * - Article, Product, FAQ, HowTo
175
+ * - Organization, Website, BreadcrumbList
176
+ * - Event, Recipe, Course, and more
177
+ *
178
+ * @param params - Schema generation parameters
179
+ * @returns Valid JSON-LD schema markup
180
+ */
181
+ async generateSchema(params) {
182
+ const siteLogger = LoggerFactory.tool("wp_seo_generate_schema", params.site);
183
+ return await siteLogger.time("Generate schema markup", async () => {
184
+ try {
185
+ validateRequired(params, ["postId", "schemaType"]);
186
+ const client = this.getSiteClient(params.site);
187
+ // Check cache
188
+ const cacheKey = `seo:schema:${params.site}:${params.postId}:${params.schemaType}`;
189
+ const cached = await this.getCachedResult(cacheKey);
190
+ if (cached) {
191
+ return cached;
192
+ }
193
+ // Generate schema (implementation in generators)
194
+ const schema = await this.createSchema(client, params);
195
+ // Cache for 24 hours
196
+ await this.cacheResult(cacheKey, schema, 86400);
197
+ return schema;
198
+ }
199
+ catch (_error) {
200
+ handleToolError(_error, "generate schema", {
201
+ site: params.site,
202
+ postId: params.postId,
203
+ schemaType: params.schemaType,
204
+ });
205
+ throw _error;
206
+ }
207
+ });
208
+ }
209
+ /**
210
+ * Validates schema markup for correctness.
211
+ *
212
+ * Performs validation using:
213
+ * - Local validation rules
214
+ * - Google Rich Results Test API (optional)
215
+ * - Schema.org specifications
216
+ *
217
+ * @param params - Validation parameters including schema JSON
218
+ * @returns Validation results with errors and warnings
219
+ */
220
+ async validateSchema(params) {
221
+ const siteLogger = LoggerFactory.tool("wp_seo_validate_schema", params.site);
222
+ return await siteLogger.time("Validate schema markup", async () => {
223
+ try {
224
+ validateRequired(params, ["schema"]);
225
+ // Implementation will be added in validators
226
+ const validation = await this.performSchemaValidation(params);
227
+ return validation;
228
+ }
229
+ catch (_error) {
230
+ handleToolError(_error, "validate schema", {
231
+ site: params.site,
232
+ });
233
+ throw _error;
234
+ }
235
+ });
236
+ }
237
+ /**
238
+ * Suggests internal linking opportunities.
239
+ *
240
+ * Analyzes content to suggest:
241
+ * - Related posts for contextual linking
242
+ * - Anchor text recommendations
243
+ * - Hub-and-spoke content architectures
244
+ * - Topical cluster connections
245
+ *
246
+ * @param params - Linking parameters including post ID
247
+ * @returns Internal linking suggestions with confidence scores
248
+ */
249
+ async suggestInternalLinks(params) {
250
+ const siteLogger = LoggerFactory.tool("wp_seo_internal_linking", params.site);
251
+ return await siteLogger.time("Suggest internal links", async () => {
252
+ try {
253
+ validateRequired(params, ["postId"]);
254
+ const client = this.getSiteClient(params.site);
255
+ // Implementation will be added in optimizers
256
+ const suggestions = await this.findLinkingOpportunities(client, params);
257
+ return suggestions;
258
+ }
259
+ catch (_error) {
260
+ handleToolError(_error, "suggest internal links", {
261
+ site: params.site,
262
+ postId: params.postId,
263
+ });
264
+ throw _error;
265
+ }
266
+ });
267
+ }
268
+ /**
269
+ * Performs comprehensive site SEO audit.
270
+ *
271
+ * Audits include:
272
+ * - Technical SEO issues
273
+ * - Content quality analysis
274
+ * - Core Web Vitals assessment
275
+ * - Mobile usability checks
276
+ * - Structured data validation
277
+ *
278
+ * @param params - Audit parameters including scope and depth
279
+ * @returns Detailed audit results with prioritized recommendations
280
+ */
281
+ async performSiteAudit(params) {
282
+ const siteLogger = LoggerFactory.tool("wp_seo_site_audit", params.site);
283
+ return await siteLogger.time("Perform site audit", async () => {
284
+ try {
285
+ const client = this.getSiteClient(params.site);
286
+ // Cache key for audit results (1 hour TTL)
287
+ const cacheKey = `seo:audit:${params.site}:${params.auditType || "full"}`;
288
+ const cached = await this.getCachedResult(cacheKey);
289
+ if (cached && !params.force) {
290
+ return cached;
291
+ }
292
+ // Perform audit (implementation will be added)
293
+ const audit = await this.executeSiteAudit(client, params);
294
+ // Cache for 1 hour
295
+ await this.cacheResult(cacheKey, audit, 3600);
296
+ return audit;
297
+ }
298
+ catch (_error) {
299
+ handleToolError(_error, "perform site audit", {
300
+ site: params.site,
301
+ auditType: params.auditType,
302
+ });
303
+ throw _error;
304
+ }
305
+ });
306
+ }
307
+ /**
308
+ * Retrieves all SEO tool definitions for MCP registration.
309
+ *
310
+ * @returns Array of SEO tool definitions with handlers
311
+ */
312
+ getTools() {
313
+ return seoToolDefinitions.map((toolDef) => ({
314
+ ...toolDef,
315
+ handler: this.getHandlerForTool(toolDef.name),
316
+ }));
317
+ }
318
+ /**
319
+ * Maps tool names to their corresponding handler methods.
320
+ *
321
+ * @param toolName - Name of the tool
322
+ * @returns Handler function for the tool
323
+ * @private
324
+ */
325
+ getHandlerForTool(toolName) {
326
+ const handlers = {
327
+ wp_seo_analyze_content: handleAnalyzeContent,
328
+ wp_seo_generate_metadata: handleGenerateMetadata,
329
+ wp_seo_bulk_update_metadata: handleBulkUpdateMetadata,
330
+ wp_seo_generate_schema: handleGenerateSchema,
331
+ wp_seo_validate_schema: handleValidateSchema,
332
+ wp_seo_suggest_internal_links: handleSuggestInternalLinks,
333
+ wp_seo_site_audit: handlePerformSiteAudit,
334
+ wp_seo_track_serp: handleTrackSERPPositions,
335
+ wp_seo_keyword_research: handleKeywordResearch,
336
+ wp_seo_test_integration: handleTestSEOIntegration,
337
+ wp_seo_get_live_data: handleGetLiveSEOData,
338
+ };
339
+ return (handlers[toolName] ||
340
+ (() => {
341
+ throw new Error(`Unknown SEO tool: ${toolName}`);
342
+ }));
343
+ }
344
+ /**
345
+ * Gets or creates a WordPress client for the specified site.
346
+ *
347
+ * @param site - Site identifier
348
+ * @returns WordPress client instance
349
+ * @private
350
+ */
351
+ getSiteClient(site) {
352
+ const siteId = site || "default";
353
+ if (!this.clients.has(siteId)) {
354
+ // Create new client for site (implementation depends on multi-site config)
355
+ const client = new WordPressClient();
356
+ this.clients.set(siteId, client);
357
+ }
358
+ return this.clients.get(siteId);
359
+ }
360
+ /**
361
+ * Get SEO-enhanced WordPress client for a specific site
362
+ */
363
+ async getSEOClient(site) {
364
+ const siteId = site || "default";
365
+ if (!this.seoClients.has(siteId)) {
366
+ // Get base client config and create SEO client
367
+ const baseClient = this.getSiteClient(site);
368
+ const seoClient = new SEOWordPressClient(baseClient.config);
369
+ // Initialize SEO capabilities
370
+ await seoClient.initializeSEO();
371
+ this.seoClients.set(siteId, seoClient);
372
+ }
373
+ return this.seoClients.get(siteId);
374
+ }
375
+ /**
376
+ * Retrieves cached result if available.
377
+ *
378
+ * @param key - Cache key
379
+ * @returns Cached result or null
380
+ * @private
381
+ */
382
+ async getCachedResult(key) {
383
+ return this.cacheManager.get(key);
384
+ }
385
+ /**
386
+ * Caches a result with specified TTL.
387
+ *
388
+ * @param key - Cache key
389
+ * @param result - Result to cache
390
+ * @param ttl - Time to live in seconds
391
+ * @private
392
+ */
393
+ async cacheResult(key, result, ttl) {
394
+ await this.cacheManager.set(key, result, ttl);
395
+ }
396
+ // Implementation methods
397
+ async performAnalysis(client, params) {
398
+ // Fetch the post data
399
+ const post = await client.getPost(params.postId);
400
+ // Perform content analysis
401
+ return await this.contentAnalyzer.analyzePost(post, params);
402
+ }
403
+ async createMetadata(client, params) {
404
+ // Fetch the post data
405
+ const post = await client.getPost(params.postId);
406
+ // Check cache first
407
+ const cacheKey = `seo:metadata:${params.site}:${params.postId}`;
408
+ const cached = await this.getCachedResult(cacheKey);
409
+ if (cached) {
410
+ this.logger.debug("Cache hit for metadata generation", { cacheKey });
411
+ return cached;
412
+ }
413
+ // Generate metadata
414
+ const metadata = await this.metaGenerator.generateMetadata(post, params, {
415
+ includeKeywords: Boolean(params.focusKeywords?.length),
416
+ brandVoice: "professional", // Could be configurable
417
+ includeCallToAction: true,
418
+ preserveExisting: false,
419
+ });
420
+ // Cache for 2 hours
421
+ await this.cacheResult(cacheKey, metadata, 7200);
422
+ return metadata;
423
+ }
424
+ async processBulkUpdate(client, params) {
425
+ // Initialize BulkOperations for the specific client if needed
426
+ const bulkOps = new BulkOperations(client, this.cacheManager, {
427
+ batchSize: 10,
428
+ maxRetries: 3,
429
+ enableProgress: true,
430
+ });
431
+ // Set up progress callback for logging
432
+ const progressCallback = (progress) => {
433
+ this.logger.info("Bulk operation progress", {
434
+ progress: `${progress.processed}/${progress.total}`,
435
+ success: progress.completed,
436
+ failed: progress.failed,
437
+ currentBatch: `${progress.currentBatch}/${progress.totalBatches}`,
438
+ eta: progress.eta,
439
+ });
440
+ };
441
+ // Execute bulk metadata update
442
+ return await bulkOps.bulkUpdateMetadata(params, progressCallback);
443
+ }
444
+ async createSchema(client, params) {
445
+ // Fetch the post data
446
+ const post = await client.getPost(params.postId);
447
+ if (!post) {
448
+ throw new Error(`Post ${params.postId} not found`);
449
+ }
450
+ // Generate schema markup
451
+ const options = {
452
+ includeAuthor: true,
453
+ includeOrganization: true,
454
+ includeBreadcrumbs: params.schemaType === "BreadcrumbList",
455
+ includeImages: true,
456
+ siteConfig: {
457
+ name: "WordPress Site", // This could be configurable
458
+ url: "https://example.com", // This could come from site settings
459
+ description: "WordPress site with SEO optimization",
460
+ },
461
+ customProperties: {
462
+ ...(params.focusKeywords && { keywords: params.focusKeywords }),
463
+ },
464
+ };
465
+ return await this.schemaGenerator.generateSchema(post, params, options);
466
+ }
467
+ async performSchemaValidation(params) {
468
+ if (!params.schema) {
469
+ throw new Error("Schema markup is required for validation");
470
+ }
471
+ // Basic validation using the SchemaGenerator's validation method
472
+ const validation = this.schemaGenerator.validateSchema(params.schema);
473
+ return {
474
+ valid: validation.valid,
475
+ errors: validation.errors,
476
+ warnings: [], // Could add warnings for best practices
477
+ schemaType: params.schema["@type"],
478
+ validatedAt: new Date().toISOString(),
479
+ };
480
+ }
481
+ async findLinkingOpportunities(client, params) {
482
+ // Fetch the source post
483
+ const post = await client.getPost(params.postId);
484
+ if (!post) {
485
+ throw new Error(`Post ${params.postId} not found`);
486
+ }
487
+ // Initialize internal linking suggester for the specific client
488
+ const linkingSuggester = new InternalLinkingSuggester(client, {
489
+ maxSuggestions: params.maxSuggestions || 10,
490
+ minRelevanceScore: params.minimumRelevance || 30,
491
+ maxLinksPerPost: 5,
492
+ useSemanticAnalysis: true,
493
+ enableContextualPlacement: true,
494
+ });
495
+ // Generate internal linking suggestions
496
+ const suggestions = await linkingSuggester.generateSuggestions(post, params);
497
+ return {
498
+ postId: params.postId,
499
+ postTitle: post.title?.rendered || "Untitled",
500
+ suggestions,
501
+ totalSuggestions: suggestions.length,
502
+ averageRelevance: suggestions.length > 0
503
+ ? (suggestions.reduce((sum, s) => sum + s.relevance, 0) / suggestions.length).toFixed(1)
504
+ : 0,
505
+ generatedAt: new Date().toISOString(),
506
+ };
507
+ }
508
+ /**
509
+ * Test SEO plugin integration and WordPress API connectivity
510
+ */
511
+ async testSEOIntegration(params) {
512
+ const siteLogger = LoggerFactory.tool("wp_seo_test_integration", params.site);
513
+ return await siteLogger.time("Test SEO integration", async () => {
514
+ try {
515
+ const seoClient = await this.getSEOClient(params.site);
516
+ // Test the integration
517
+ const integrationTest = await seoClient.testSEOIntegration();
518
+ // Get some sample SEO data
519
+ let sampleSEOData = null;
520
+ if (integrationTest.canReadMetadata && integrationTest.samplePostsWithSEO > 0) {
521
+ try {
522
+ const posts = await seoClient.getPosts({ per_page: 1, status: ["publish"] });
523
+ if (posts && posts.length > 0) {
524
+ sampleSEOData = await seoClient.getSEOMetadata(posts[0].id);
525
+ }
526
+ }
527
+ catch (_error) {
528
+ // Sample data fetch failed, but that's okay
529
+ this.logger.debug("Failed to fetch sample SEO data", { _error: _error });
530
+ }
531
+ }
532
+ const result = {
533
+ integrationTest,
534
+ sampleSEOData,
535
+ recommendations: this.generateIntegrationRecommendations(integrationTest),
536
+ testedAt: new Date().toISOString(),
537
+ };
538
+ siteLogger.info("SEO integration test completed", {
539
+ plugin: integrationTest.pluginDetected,
540
+ canRead: integrationTest.canReadMetadata,
541
+ canWrite: integrationTest.canWriteMetadata,
542
+ postsWithSEO: integrationTest.samplePostsWithSEO,
543
+ });
544
+ return result;
545
+ }
546
+ catch (_error) {
547
+ handleToolError(_error, "test SEO integration", {
548
+ site: params.site,
549
+ });
550
+ throw _error;
551
+ }
552
+ });
553
+ }
554
+ /**
555
+ * Get live SEO data for multiple posts
556
+ */
557
+ async getLiveSEOData(params) {
558
+ const siteLogger = LoggerFactory.tool("wp_seo_get_live_data", params.site);
559
+ return await siteLogger.time("Get live SEO data", async () => {
560
+ try {
561
+ const seoClient = await this.getSEOClient(params.site);
562
+ // Get all posts with SEO data
563
+ const postsWithSEO = await seoClient.getAllPostsWithSEO({
564
+ maxPosts: params.maxPosts || 20,
565
+ includePages: true,
566
+ });
567
+ // Analyze the SEO data
568
+ const analysis = this.analyzeLiveSEOData(postsWithSEO);
569
+ const result = {
570
+ totalContent: postsWithSEO.length,
571
+ contentWithSEO: postsWithSEO.filter((p) => p.seoData).length,
572
+ analysis,
573
+ posts: postsWithSEO.map((post) => ({
574
+ id: post.id,
575
+ title: post.title?.rendered,
576
+ type: post.type,
577
+ url: post.link,
578
+ seoData: post.seoData
579
+ ? {
580
+ hasTitle: !!post.seoData.title,
581
+ hasDescription: !!post.seoData.description,
582
+ hasFocusKeyword: !!post.seoData.focusKeyword,
583
+ plugin: post.seoData.plugin,
584
+ lastModified: post.seoData.lastModified,
585
+ }
586
+ : null,
587
+ })),
588
+ retrievedAt: new Date().toISOString(),
589
+ };
590
+ siteLogger.info("Live SEO data retrieved", {
591
+ totalContent: result.totalContent,
592
+ withSEO: result.contentWithSEO,
593
+ plugin: analysis.detectedPlugin,
594
+ });
595
+ return result;
596
+ }
597
+ catch (_error) {
598
+ handleToolError(_error, "get live SEO data", {
599
+ site: params.site,
600
+ maxPosts: params.maxPosts,
601
+ });
602
+ throw _error;
603
+ }
604
+ });
605
+ }
606
+ async executeSiteAudit(client, params) {
607
+ // Initialize site auditor for the specific client
608
+ const siteAuditor = new SiteAuditor(client, {
609
+ includeTechnical: params.includeTechnical !== false,
610
+ includeContent: params.includeContent !== false,
611
+ includeArchitecture: params.includeArchitecture !== false,
612
+ includePerformance: params.includePerformance !== false,
613
+ includeAccessibility: params.includeAccessibility || false,
614
+ maxPagesForContentAudit: params.maxPages || 50,
615
+ minSeverityLevel: params.minSeverity || "medium",
616
+ includeRecommendations: params.includeRecommendations !== false,
617
+ });
618
+ // Perform comprehensive site audit
619
+ return await siteAuditor.performSiteAudit(params);
620
+ }
621
+ /**
622
+ * Generate integration recommendations based on test results
623
+ */
624
+ generateIntegrationRecommendations(testResult) {
625
+ const recommendations = [];
626
+ if (testResult.pluginDetected === "none") {
627
+ recommendations.push("Consider installing a WordPress SEO plugin like Yoast SEO or RankMath for better SEO metadata management");
628
+ }
629
+ if (!testResult.canReadMetadata) {
630
+ recommendations.push("SEO metadata reading failed. Check WordPress REST API permissions and ensure the detected SEO plugin exposes metadata via REST API");
631
+ }
632
+ if (!testResult.canWriteMetadata) {
633
+ recommendations.push("SEO metadata writing failed. Verify user permissions for editing posts and pages via WordPress REST API");
634
+ }
635
+ if (testResult.samplePostsWithSEO === 0) {
636
+ recommendations.push("No SEO metadata found on sample posts. Consider adding SEO titles and descriptions to your content");
637
+ }
638
+ else if (testResult.samplePostsWithSEO < 3) {
639
+ recommendations.push("Limited SEO metadata detected. Consider optimizing more posts with SEO titles, descriptions, and focus keywords");
640
+ }
641
+ if (testResult.errors && testResult.errors.length > 0) {
642
+ recommendations.push("Integration errors detected. Review error details and check WordPress configuration, plugin settings, and REST API accessibility");
643
+ }
644
+ if (recommendations.length === 0) {
645
+ recommendations.push("SEO integration is working well! All tests passed successfully.");
646
+ }
647
+ return recommendations;
648
+ }
649
+ /**
650
+ * Analyze live SEO data for insights
651
+ */
652
+ analyzeLiveSEOData(posts) {
653
+ const analysis = {
654
+ detectedPlugin: "none",
655
+ totalPosts: posts.length,
656
+ postsWithSEO: 0,
657
+ postsWithTitles: 0,
658
+ postsWithDescriptions: 0,
659
+ postsWithFocusKeywords: 0,
660
+ averageMetadataCompleteness: 0,
661
+ recommendations: [],
662
+ };
663
+ const pluginCounts = { yoast: 0, rankmath: 0, seopress: 0, none: 0 };
664
+ for (const post of posts) {
665
+ if (post.seoData) {
666
+ analysis.postsWithSEO++;
667
+ // Count plugin usage
668
+ pluginCounts[post.seoData.plugin]++;
669
+ // Count metadata presence
670
+ if (post.seoData.title)
671
+ analysis.postsWithTitles++;
672
+ if (post.seoData.description)
673
+ analysis.postsWithDescriptions++;
674
+ if (post.seoData.focusKeyword)
675
+ analysis.postsWithFocusKeywords++;
676
+ }
677
+ }
678
+ // Determine most common plugin
679
+ const mostUsedPlugin = Object.entries(pluginCounts).reduce((a, b) => pluginCounts[a[0]] > pluginCounts[b[0]] ? a : b);
680
+ analysis.detectedPlugin = mostUsedPlugin[0];
681
+ // Calculate metadata completeness
682
+ if (analysis.postsWithSEO > 0) {
683
+ const titleCompleteness = (analysis.postsWithTitles / analysis.postsWithSEO) * 100;
684
+ const descriptionCompleteness = (analysis.postsWithDescriptions / analysis.postsWithSEO) * 100;
685
+ const keywordCompleteness = (analysis.postsWithFocusKeywords / analysis.postsWithSEO) * 100;
686
+ analysis.averageMetadataCompleteness = Math.round((titleCompleteness + descriptionCompleteness + keywordCompleteness) / 3);
687
+ }
688
+ // Generate recommendations
689
+ const seoPercentage = (analysis.postsWithSEO / analysis.totalPosts) * 100;
690
+ if (seoPercentage < 50) {
691
+ analysis.recommendations.push(`Only ${Math.round(seoPercentage)}% of content has SEO metadata. Consider optimizing more posts.`);
692
+ }
693
+ if (analysis.averageMetadataCompleteness < 70) {
694
+ analysis.recommendations.push(`Metadata completeness is ${analysis.averageMetadataCompleteness}%. Focus on adding missing titles, descriptions, and focus keywords.`);
695
+ }
696
+ const titlePercentage = analysis.postsWithSEO > 0 ? (analysis.postsWithTitles / analysis.postsWithSEO) * 100 : 0;
697
+ if (titlePercentage < 80) {
698
+ analysis.recommendations.push(`${Math.round(100 - titlePercentage)}% of SEO-enabled posts lack custom titles. Add SEO titles for better search visibility.`);
699
+ }
700
+ const descPercentage = analysis.postsWithSEO > 0 ? (analysis.postsWithDescriptions / analysis.postsWithSEO) * 100 : 0;
701
+ if (descPercentage < 80) {
702
+ analysis.recommendations.push(`${Math.round(100 - descPercentage)}% of SEO-enabled posts lack meta descriptions. Add descriptions to improve click-through rates.`);
703
+ }
704
+ return analysis;
705
+ }
706
+ }
707
+ export default SEOTools;
708
+ //# sourceMappingURL=SEOTools.js.map