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.
Files changed (333) 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 +24 -58
  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
  333. package/src/dxt-entry.cjs +0 -68
@@ -0,0 +1,493 @@
1
+ /**
2
+ * Content Analyzer for SEO
3
+ *
4
+ * Analyzes WordPress content for SEO optimization opportunities including
5
+ * readability scoring, keyword analysis, content structure evaluation,
6
+ * and technical SEO factors.
7
+ *
8
+ * @since 2.7.0
9
+ */
10
+
11
+ import { LoggerFactory } from "../../../utils/logger.js";
12
+ import { Config } from "../../../config/Config.js";
13
+ import type { WordPressPost } from "../../../types/wordpress.js";
14
+ import type { SEOAnalysisResult, SEOMetrics, SEORecommendation, SEOToolParams } from "../../../types/seo.js";
15
+
16
+ /**
17
+ * Content analyzer for SEO evaluation
18
+ */
19
+ export class ContentAnalyzer {
20
+ private logger = LoggerFactory.tool("content_analyzer");
21
+ private config = Config.getInstance().get().seo;
22
+
23
+ /**
24
+ * Analyze post content for SEO factors
25
+ *
26
+ * @param post - WordPress post data
27
+ * @param params - Analysis parameters
28
+ * @returns Comprehensive SEO analysis
29
+ */
30
+ async analyzePost(post: WordPressPost, params: SEOToolParams): Promise<SEOAnalysisResult> {
31
+ const siteLogger = LoggerFactory.tool("seo_analyze_content", params.site);
32
+
33
+ return await siteLogger.time("Content SEO analysis", async () => {
34
+ // Extract content for analysis
35
+ const content = this.extractContent(post);
36
+ const plainText = this.stripHtml(content);
37
+
38
+ // Perform various analyses
39
+ const metrics = await this.calculateMetrics(plainText, content, params);
40
+ const recommendations = await this.generateRecommendations(post, metrics, params);
41
+ const keywordAnalysis = params.focusKeywords?.length
42
+ ? await this.analyzeKeywords(plainText, params.focusKeywords[0])
43
+ : undefined;
44
+ const structure = await this.analyzeStructure(content);
45
+
46
+ // Calculate overall SEO score
47
+ const score = this.calculateOverallScore(metrics, recommendations);
48
+ const status = this.getScoreStatus(score);
49
+
50
+ const result: SEOAnalysisResult = {
51
+ score,
52
+ status,
53
+ metrics,
54
+ recommendations,
55
+ structure,
56
+ analyzedAt: new Date().toISOString(),
57
+ };
58
+
59
+ if (keywordAnalysis) {
60
+ result.keywordAnalysis = keywordAnalysis;
61
+ }
62
+
63
+ return result;
64
+ });
65
+ }
66
+
67
+ /**
68
+ * Extract text content from WordPress post
69
+ *
70
+ * @param post - WordPress post
71
+ * @returns Combined content string
72
+ * @private
73
+ */
74
+ private extractContent(post: WordPressPost): string {
75
+ const titleContent = typeof post.title === "object" ? post.title.rendered : post.title || "";
76
+ const bodyContent = typeof post.content === "object" ? post.content.rendered : post.content || "";
77
+ const excerptContent = typeof post.excerpt === "object" ? post.excerpt.rendered : post.excerpt || "";
78
+
79
+ return `${titleContent} ${bodyContent} ${excerptContent}`.trim();
80
+ }
81
+
82
+ /**
83
+ * Strip HTML tags and return plain text
84
+ *
85
+ * @param html - HTML content
86
+ * @returns Plain text
87
+ * @private
88
+ */
89
+ private stripHtml(html: string): string {
90
+ // Remove HTML tags, scripts, and styles
91
+ return html
92
+ .replace(/<script[^>]*>[\s\S]*?<\/script>/gi, "")
93
+ .replace(/<style[^>]*>[\s\S]*?<\/style>/gi, "")
94
+ .replace(/<[^>]*>/g, "")
95
+ .replace(/&nbsp;/g, " ")
96
+ .replace(/&[#\w]+;/g, "")
97
+ .replace(/\s+/g, " ")
98
+ .trim();
99
+ }
100
+
101
+ /**
102
+ * Calculate comprehensive content metrics
103
+ *
104
+ * @param plainText - Plain text content
105
+ * @param htmlContent - Original HTML content
106
+ * @param params - Analysis parameters
107
+ * @returns SEO metrics
108
+ * @private
109
+ */
110
+ private async calculateMetrics(plainText: string, htmlContent: string, params: SEOToolParams): Promise<SEOMetrics> {
111
+ const words = this.getWords(plainText);
112
+ const sentences = this.getSentences(plainText);
113
+ const syllables = this.countSyllables(plainText);
114
+
115
+ // Calculate readability scores
116
+ const avgWordsPerSentence = words.length / Math.max(sentences.length, 1);
117
+ const avgSyllablesPerWord = syllables / Math.max(words.length, 1);
118
+
119
+ const fleschReadingEase = this.calculateFleschReadingEase(avgWordsPerSentence, avgSyllablesPerWord);
120
+
121
+ const fleschKincaidGrade = this.calculateFleschKincaidGrade(avgWordsPerSentence, avgSyllablesPerWord);
122
+
123
+ // Keyword density for focus keyword
124
+ const keywordDensity = params.focusKeywords?.length
125
+ ? this.calculateKeywordDensity(plainText, params.focusKeywords[0])
126
+ : 0;
127
+
128
+ // HTML structure analysis
129
+ const headings = this.countHeadings(htmlContent);
130
+ const links = this.countLinks(htmlContent);
131
+ const images = this.countImages(htmlContent);
132
+
133
+ return {
134
+ wordCount: words.length,
135
+ avgWordsPerSentence,
136
+ avgSyllablesPerWord,
137
+ fleschReadingEase,
138
+ fleschKincaidGrade,
139
+ keywordDensity,
140
+ headingCount: headings.total,
141
+ internalLinkCount: links.internal,
142
+ externalLinkCount: links.external,
143
+ imageCount: images.total,
144
+ imagesWithAltText: images.withAlt,
145
+ readingTime: Math.ceil(words.length / 200), // Assume 200 WPM reading speed
146
+ };
147
+ }
148
+
149
+ /**
150
+ * Generate SEO recommendations based on analysis
151
+ *
152
+ * @param post - WordPress post
153
+ * @param metrics - Calculated metrics
154
+ * @param params - Analysis parameters
155
+ * @returns Array of recommendations
156
+ * @private
157
+ */
158
+ private async generateRecommendations(
159
+ post: WordPressPost,
160
+ metrics: SEOMetrics,
161
+ params: SEOToolParams,
162
+ ): Promise<SEORecommendation[]> {
163
+ const recommendations: SEORecommendation[] = [];
164
+
165
+ // Word count recommendations
166
+ if (metrics.wordCount < this.config.analysis.minWordCount) {
167
+ recommendations.push({
168
+ type: "content",
169
+ priority: "high",
170
+ message: `Content is too short (${metrics.wordCount} words). Aim for at least ${this.config.analysis.minWordCount} words for better SEO.`,
171
+ impact: 80,
172
+ autoFixAvailable: false,
173
+ helpUrl: "https://developers.google.com/search/docs/fundamentals/creating-helpful-content",
174
+ });
175
+ }
176
+
177
+ // Readability recommendations
178
+ if (metrics.fleschReadingEase < this.config.analysis.minReadabilityScore) {
179
+ recommendations.push({
180
+ type: "content",
181
+ priority: "medium",
182
+ message: `Content readability is low (${metrics.fleschReadingEase.toFixed(1)}). Consider using shorter sentences and simpler words.`,
183
+ impact: 60,
184
+ autoFixAvailable: false,
185
+ suggestedFix: "Break up long sentences and use more common vocabulary.",
186
+ });
187
+ }
188
+
189
+ // Keyword density recommendations
190
+ if (params.focusKeywords?.length) {
191
+ const targetDensity = this.config.analysis.targetKeywordDensity;
192
+ const maxDensity = this.config.analysis.maxKeywordDensity;
193
+
194
+ if (metrics.keywordDensity < targetDensity) {
195
+ recommendations.push({
196
+ type: "keyword",
197
+ priority: "medium",
198
+ message: `Focus keyword density is low (${metrics.keywordDensity.toFixed(1)}%). Consider adding the keyword "${params.focusKeywords[0]}" more naturally throughout the content.`,
199
+ impact: 70,
200
+ autoFixAvailable: false,
201
+ });
202
+ } else if (metrics.keywordDensity > maxDensity) {
203
+ recommendations.push({
204
+ type: "keyword",
205
+ priority: "medium",
206
+ message: `Focus keyword density is too high (${metrics.keywordDensity.toFixed(1)}%). This might be considered keyword stuffing.`,
207
+ impact: 75,
208
+ autoFixAvailable: false,
209
+ suggestedFix: `Reduce keyword usage to around ${targetDensity}%`,
210
+ });
211
+ }
212
+ }
213
+
214
+ // Image recommendations
215
+ if (metrics.imageCount > 0 && metrics.imagesWithAltText < metrics.imageCount) {
216
+ const missingAlt = metrics.imageCount - metrics.imagesWithAltText;
217
+ recommendations.push({
218
+ type: "technical",
219
+ priority: "high",
220
+ message: `${missingAlt} image(s) missing alt text. Alt text improves accessibility and SEO.`,
221
+ impact: 85,
222
+ autoFixAvailable: false,
223
+ suggestedFix: "Add descriptive alt text to all images",
224
+ });
225
+ }
226
+
227
+ // Heading structure recommendations
228
+ if (metrics.headingCount === 0) {
229
+ recommendations.push({
230
+ type: "structure",
231
+ priority: "high",
232
+ message: "No headings found. Use H1-H6 tags to structure your content.",
233
+ impact: 90,
234
+ autoFixAvailable: false,
235
+ suggestedFix: "Add at least one H1 heading and use H2-H6 for subheadings",
236
+ });
237
+ }
238
+
239
+ // Internal linking recommendations
240
+ if (metrics.internalLinkCount === 0) {
241
+ recommendations.push({
242
+ type: "structure",
243
+ priority: "medium",
244
+ message: "No internal links found. Internal linking helps with site navigation and SEO.",
245
+ impact: 65,
246
+ autoFixAvailable: false,
247
+ suggestedFix: "Add 2-3 relevant internal links to other pages on your site",
248
+ });
249
+ }
250
+
251
+ return recommendations;
252
+ }
253
+
254
+ /**
255
+ * Analyze focus keyword usage
256
+ *
257
+ * @param plainText - Plain text content
258
+ * @param keyword - Focus keyword
259
+ * @returns Keyword analysis
260
+ * @private
261
+ */
262
+ private async analyzeKeywords(
263
+ plainText: string,
264
+ keyword: string,
265
+ ): Promise<{
266
+ primaryKeyword: string;
267
+ keywordFound: boolean;
268
+ occurrences: number;
269
+ density: number;
270
+ semanticKeywords: string[];
271
+ competitorGap?: string[];
272
+ }> {
273
+ const lowerText = plainText.toLowerCase();
274
+ const lowerKeyword = keyword.toLowerCase();
275
+ const words = this.getWords(plainText);
276
+
277
+ // Count exact keyword occurrences
278
+ const keywordRegex = new RegExp(`\\b${lowerKeyword.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`, "gi");
279
+ const matches = lowerText.match(keywordRegex) || [];
280
+ const occurrences = matches.length;
281
+
282
+ // Calculate keyword density
283
+ const density = (occurrences / words.length) * 100;
284
+
285
+ // Find semantic keywords (simple implementation)
286
+ const semanticKeywords = this.findSemanticKeywords(plainText, keyword);
287
+
288
+ return {
289
+ primaryKeyword: keyword,
290
+ keywordFound: occurrences > 0,
291
+ occurrences,
292
+ density,
293
+ semanticKeywords,
294
+ };
295
+ }
296
+
297
+ /**
298
+ * Analyze content structure
299
+ *
300
+ * @param htmlContent - HTML content
301
+ * @returns Structure analysis
302
+ * @private
303
+ */
304
+ private async analyzeStructure(htmlContent: string): Promise<{
305
+ hasH1: boolean;
306
+ h1Text: string;
307
+ headingHierarchy: boolean;
308
+ paragraphCount: number;
309
+ avgParagraphLength: number;
310
+ }> {
311
+ const h1Match = htmlContent.match(/<h1[^>]*>(.*?)<\/h1>/i);
312
+ const h1Text = h1Match ? this.stripHtml(h1Match[1]) : "";
313
+ const hasH1 = Boolean(h1Match);
314
+
315
+ // Check heading hierarchy (simplified)
316
+ const headingHierarchy = this.checkHeadingHierarchy(htmlContent);
317
+
318
+ // Analyze paragraphs
319
+ const paragraphs = htmlContent.match(/<p[^>]*>[\s\S]*?<\/p>/gi) || [];
320
+ const paragraphTexts = paragraphs.map((p) => this.stripHtml(p)).filter((text) => text.trim().length > 0);
321
+ const avgParagraphLength =
322
+ paragraphTexts.length > 0
323
+ ? paragraphTexts.reduce((sum, text) => sum + this.getWords(text).length, 0) / paragraphTexts.length
324
+ : 0;
325
+
326
+ return {
327
+ hasH1,
328
+ h1Text,
329
+ headingHierarchy,
330
+ paragraphCount: paragraphTexts.length,
331
+ avgParagraphLength,
332
+ };
333
+ }
334
+
335
+ // Helper methods
336
+
337
+ private getWords(text: string): string[] {
338
+ return text.toLowerCase().match(/\b\w+\b/g) || [];
339
+ }
340
+
341
+ private getSentences(text: string): string[] {
342
+ return text.split(/[.!?]+/).filter((s) => s.trim().length > 0);
343
+ }
344
+
345
+ private countSyllables(text: string): number {
346
+ const words = this.getWords(text);
347
+ return words.reduce((total, word) => {
348
+ // Simple syllable counting heuristic
349
+ const vowels = word.match(/[aeiouy]+/g);
350
+ let count = vowels ? vowels.length : 0;
351
+ if (word.endsWith("e")) count--;
352
+ return total + Math.max(1, count);
353
+ }, 0);
354
+ }
355
+
356
+ private calculateFleschReadingEase(avgWordsPerSentence: number, avgSyllablesPerWord: number): number {
357
+ return 206.835 - 1.015 * avgWordsPerSentence - 84.6 * avgSyllablesPerWord;
358
+ }
359
+
360
+ private calculateFleschKincaidGrade(avgWordsPerSentence: number, avgSyllablesPerWord: number): number {
361
+ return 0.39 * avgWordsPerSentence + 11.8 * avgSyllablesPerWord - 15.59;
362
+ }
363
+
364
+ private calculateKeywordDensity(text: string, keyword: string): number {
365
+ const words = this.getWords(text);
366
+ const keywordWords = this.getWords(keyword.toLowerCase());
367
+
368
+ if (words.length === 0 || keywordWords.length === 0) return 0;
369
+
370
+ // Count occurrences of the exact keyword phrase
371
+ const textString = words.join(" ");
372
+ const keywordString = keywordWords.join(" ");
373
+ const regex = new RegExp(keywordString.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "gi");
374
+ const matches = textString.match(regex) || [];
375
+
376
+ return (matches.length / words.length) * 100;
377
+ }
378
+
379
+ private countHeadings(htmlContent: string): { total: number; byLevel: Record<string, number> } {
380
+ const byLevel: Record<string, number> = {};
381
+ let total = 0;
382
+
383
+ for (let i = 1; i <= 6; i++) {
384
+ const regex = new RegExp(`<h${i}[^>]*>.*?</h${i}>`, "gi");
385
+ const matches = htmlContent.match(regex) || [];
386
+ byLevel[`h${i}`] = matches.length;
387
+ total += matches.length;
388
+ }
389
+
390
+ return { total, byLevel };
391
+ }
392
+
393
+ private countLinks(htmlContent: string): { internal: number; external: number; total: number } {
394
+ const linkRegex = /<a[^>]+href=["']([^"']+)["'][^>]*>/gi;
395
+ const links = [];
396
+ let match;
397
+
398
+ while ((match = linkRegex.exec(htmlContent)) !== null) {
399
+ links.push(match[1]);
400
+ }
401
+
402
+ const internal = links.filter(
403
+ (href) => href.startsWith("/") || href.startsWith("#") || href.includes(this.getCurrentDomain()),
404
+ ).length;
405
+
406
+ const external = links.length - internal;
407
+
408
+ return { internal, external, total: links.length };
409
+ }
410
+
411
+ private countImages(htmlContent: string): { total: number; withAlt: number } {
412
+ const imageRegex = /<img[^>]*>/gi;
413
+ const images = htmlContent.match(imageRegex) || [];
414
+
415
+ const withAlt = images.filter((img) => /alt\s*=\s*["'][^"']+["']/i.test(img)).length;
416
+
417
+ return { total: images.length, withAlt };
418
+ }
419
+
420
+ private findSemanticKeywords(text: string, mainKeyword: string): string[] {
421
+ // Simple semantic keyword detection
422
+ // In a real implementation, this would use NLP libraries or APIs
423
+ const _words = this.getWords(text);
424
+ const _mainKeywordWords = this.getWords(mainKeyword.toLowerCase());
425
+
426
+ // Find words that commonly appear with the main keyword
427
+ const semanticKeywords: string[] = [];
428
+
429
+ // This is a simplified implementation
430
+ // Real semantic analysis would use word embeddings, LSI, or similar techniques
431
+
432
+ return semanticKeywords.slice(0, 10); // Return top 10 semantic keywords
433
+ }
434
+
435
+ private checkHeadingHierarchy(htmlContent: string): boolean {
436
+ const headingMatches = htmlContent.match(/<h([1-6])[^>]*>/gi);
437
+ if (!headingMatches) return true;
438
+
439
+ const levels = headingMatches.map((match) => {
440
+ const levelMatch = match.match(/<h([1-6])/i);
441
+ return levelMatch ? parseInt(levelMatch[1]) : 0;
442
+ });
443
+
444
+ // Check if headings follow proper hierarchy (no skipping levels)
445
+ let currentLevel = 0;
446
+ for (const level of levels) {
447
+ if (level > currentLevel + 1) {
448
+ return false; // Skipped a level
449
+ }
450
+ currentLevel = Math.max(currentLevel, level);
451
+ }
452
+
453
+ return true;
454
+ }
455
+
456
+ private getCurrentDomain(): string {
457
+ // This would need to be determined from the WordPress site URL
458
+ // For testing purposes, return a default domain
459
+ return "localhost";
460
+ }
461
+
462
+ private calculateOverallScore(metrics: SEOMetrics, recommendations: SEORecommendation[]): number {
463
+ let score = 100;
464
+
465
+ // Deduct points for each recommendation based on impact
466
+ for (const rec of recommendations) {
467
+ const deduction = rec.impact * 0.3; // Scale impact to reasonable deduction
468
+ score -= deduction;
469
+ }
470
+
471
+ // Bonus points for good metrics
472
+ if (metrics.wordCount >= this.config.analysis.minWordCount) {
473
+ score += 5;
474
+ }
475
+
476
+ if (metrics.fleschReadingEase >= this.config.analysis.minReadabilityScore) {
477
+ score += 5;
478
+ }
479
+
480
+ if (metrics.imagesWithAltText === metrics.imageCount && metrics.imageCount > 0) {
481
+ score += 5;
482
+ }
483
+
484
+ return Math.max(0, Math.min(100, Math.round(score)));
485
+ }
486
+
487
+ private getScoreStatus(score: number): "poor" | "needs-improvement" | "good" | "excellent" {
488
+ if (score >= 90) return "excellent";
489
+ if (score >= 70) return "good";
490
+ if (score >= 50) return "needs-improvement";
491
+ return "poor";
492
+ }
493
+ }