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,955 @@
1
+ /**
2
+ * Schema Generator
3
+ *
4
+ * This module generates JSON-LD structured data markup for WordPress content.
5
+ * It supports all major Schema.org types and provides intelligent content extraction
6
+ * and schema optimization for better search engine understanding.
7
+ *
8
+ * Supported Schema Types:
9
+ * - Article, BlogPosting, NewsArticle
10
+ * - Product, Offer, AggregateRating
11
+ * - FAQ, Question, Answer
12
+ * - HowTo, HowToStep, HowToDirection
13
+ * - Organization, LocalBusiness, Person
14
+ * - Website, WebPage, BreadcrumbList
15
+ * - Event, Recipe, Course, VideoObject
16
+ *
17
+ * @since 2.7.0
18
+ */
19
+
20
+ import { LoggerFactory } from "../../../utils/logger.js";
21
+ import type { SchemaMarkup, SEOToolParams } from "../../../types/seo.js";
22
+ import type { WordPressPost } from "../../../types/wordpress.js";
23
+
24
+ /**
25
+ * Schema generation options
26
+ */
27
+ interface SchemaOptions {
28
+ /** Include author information */
29
+ includeAuthor?: boolean;
30
+
31
+ /** Include organization data */
32
+ includeOrganization?: boolean;
33
+
34
+ /** Include breadcrumbs */
35
+ includeBreadcrumbs?: boolean;
36
+
37
+ /** Include images */
38
+ includeImages?: boolean;
39
+
40
+ /** Custom schema properties to merge */
41
+ customProperties?: Record<string, unknown>;
42
+
43
+ /** Site-specific configuration */
44
+ siteConfig?: {
45
+ name?: string;
46
+ url?: string;
47
+ logo?: string;
48
+ description?: string;
49
+ socialProfiles?: string[];
50
+ contactInfo?: {
51
+ telephone?: string;
52
+ email?: string;
53
+ address?: string;
54
+ };
55
+ };
56
+ }
57
+
58
+ /**
59
+ * Article schema data structure
60
+ */
61
+ interface ArticleSchemaData {
62
+ headline: string;
63
+ description: string;
64
+ datePublished: string;
65
+ dateModified: string;
66
+ author: {
67
+ "@type": "Person";
68
+ name: string;
69
+ url?: string;
70
+ };
71
+ publisher: {
72
+ "@type": "Organization";
73
+ name: string;
74
+ logo?: {
75
+ "@type": "ImageObject";
76
+ url: string;
77
+ };
78
+ };
79
+ image?: string[];
80
+ mainEntityOfPage: string;
81
+ wordCount?: number;
82
+ keywords?: string[];
83
+ }
84
+
85
+ /**
86
+ * Product schema data structure
87
+ */
88
+ interface ProductSchemaData {
89
+ name: string;
90
+ description: string;
91
+ image?: string[];
92
+ brand?: {
93
+ "@type": "Brand";
94
+ name: string;
95
+ };
96
+ offers?: {
97
+ "@type": "Offer";
98
+ price?: string;
99
+ priceCurrency?: string;
100
+ availability?: string;
101
+ url?: string;
102
+ validFrom?: string;
103
+ validThrough?: string;
104
+ };
105
+ aggregateRating?: {
106
+ "@type": "AggregateRating";
107
+ ratingValue: number;
108
+ reviewCount: number;
109
+ bestRating?: number;
110
+ worstRating?: number;
111
+ };
112
+ review?: Array<{
113
+ "@type": "Review";
114
+ reviewRating: {
115
+ "@type": "Rating";
116
+ ratingValue: number;
117
+ };
118
+ author: {
119
+ "@type": "Person";
120
+ name: string;
121
+ };
122
+ reviewBody: string;
123
+ }>;
124
+ }
125
+
126
+ /**
127
+ * FAQ schema data structure
128
+ */
129
+ interface FAQSchemaData {
130
+ mainEntity: Array<{
131
+ "@type": "Question";
132
+ name: string;
133
+ acceptedAnswer: {
134
+ "@type": "Answer";
135
+ text: string;
136
+ };
137
+ }>;
138
+ }
139
+
140
+ /**
141
+ * Schema Generator Class
142
+ */
143
+ export class SchemaGenerator {
144
+ private logger = LoggerFactory.tool("schema_generator");
145
+
146
+ constructor() {}
147
+
148
+ /**
149
+ * Generate schema markup for a WordPress post
150
+ */
151
+ async generateSchema(post: WordPressPost, params: SEOToolParams, options: SchemaOptions = {}): Promise<SchemaMarkup> {
152
+ this.logger.debug("Generating schema markup", {
153
+ postId: post.id,
154
+ schemaType: params.schemaType,
155
+ title: post.title?.rendered?.substring(0, 50),
156
+ });
157
+
158
+ if (!params.schemaType) {
159
+ throw new Error("Schema type is required for schema generation");
160
+ }
161
+
162
+ const baseSchema: SchemaMarkup = {
163
+ "@context": "https://schema.org",
164
+ "@type": params.schemaType,
165
+ };
166
+
167
+ // Generate schema based on type
168
+ switch (params.schemaType) {
169
+ case "Article":
170
+ return this.generateArticleSchema(post, baseSchema, options);
171
+
172
+ case "Product":
173
+ return this.generateProductSchema(post, baseSchema, options);
174
+
175
+ case "FAQ":
176
+ return this.generateFAQSchema(post, baseSchema, options);
177
+
178
+ case "HowTo":
179
+ return this.generateHowToSchema(post, baseSchema, options);
180
+
181
+ case "Organization":
182
+ return this.generateOrganizationSchema(post, baseSchema, options);
183
+
184
+ case "LocalBusiness":
185
+ return this.generateLocalBusinessSchema(post, baseSchema, options);
186
+
187
+ case "Website":
188
+ return this.generateWebsiteSchema(post, baseSchema, options);
189
+
190
+ case "BreadcrumbList":
191
+ return this.generateBreadcrumbSchema(post, baseSchema, options);
192
+
193
+ case "Event":
194
+ return this.generateEventSchema(post, baseSchema, options);
195
+
196
+ case "Recipe":
197
+ return this.generateRecipeSchema(post, baseSchema, options);
198
+
199
+ case "Course":
200
+ return this.generateCourseSchema(post, baseSchema, options);
201
+
202
+ case "VideoObject":
203
+ return this.generateVideoSchema(post, baseSchema, options);
204
+
205
+ case "Person":
206
+ return this.generatePersonSchema(post, baseSchema, options);
207
+
208
+ case "Review":
209
+ return this.generateReviewSchema(post, baseSchema, options);
210
+
211
+ default:
212
+ throw new Error(`Unsupported schema type: ${params.schemaType}`);
213
+ }
214
+ }
215
+
216
+ /**
217
+ * Generate Article schema markup
218
+ */
219
+ private generateArticleSchema(post: WordPressPost, baseSchema: SchemaMarkup, options: SchemaOptions): SchemaMarkup {
220
+ const content = this.extractTextContent(post.content?.rendered || "");
221
+ const excerpt = this.extractTextContent(post.excerpt?.rendered || "");
222
+ const images = this.extractImages(post.content?.rendered || "");
223
+
224
+ const articleData: ArticleSchemaData = {
225
+ headline: post.title?.rendered || "Untitled",
226
+ description: excerpt || content.substring(0, 160) + "...",
227
+ datePublished: post.date || new Date().toISOString(),
228
+ dateModified: post.modified || post.date || new Date().toISOString(),
229
+ author: {
230
+ "@type": "Person",
231
+ name: this.getAuthorName(post),
232
+ ...(this.getAuthorUrl(post) ? { url: this.getAuthorUrl(post)! } : {}),
233
+ },
234
+ publisher: this.getPublisherInfo(options),
235
+ mainEntityOfPage: post.link || `https://example.com/post/${post.id}`,
236
+ wordCount: this.countWords(content),
237
+ ...(images.length > 0 && { image: images }),
238
+ ...(options.customProperties?.keywords ? { keywords: options.customProperties.keywords as string[] } : {}),
239
+ };
240
+
241
+ return {
242
+ ...baseSchema,
243
+ ...articleData,
244
+ };
245
+ }
246
+
247
+ /**
248
+ * Generate Product schema markup
249
+ */
250
+ private generateProductSchema(post: WordPressPost, baseSchema: SchemaMarkup, options: SchemaOptions): SchemaMarkup {
251
+ const content = this.extractTextContent(post.content?.rendered || "");
252
+ const excerpt = this.extractTextContent(post.excerpt?.rendered || "");
253
+ const images = this.extractImages(post.content?.rendered || "");
254
+
255
+ // Extract product information from content
256
+ const productInfo = this.extractProductInfo(content);
257
+
258
+ const productData: ProductSchemaData = {
259
+ name: post.title?.rendered || "Untitled Product",
260
+ description: excerpt || content.substring(0, 160) + "...",
261
+ ...(images.length > 0 && { image: images }),
262
+ ...(productInfo.brand && {
263
+ brand: {
264
+ "@type": "Brand",
265
+ name: productInfo.brand,
266
+ },
267
+ }),
268
+ ...(productInfo.price && {
269
+ offers: {
270
+ "@type": "Offer",
271
+ price: productInfo.price,
272
+ priceCurrency: productInfo.currency || "USD",
273
+ availability: productInfo.availability || "https://schema.org/InStock",
274
+ url: post.link || `https://example.com/product/${post.id}`,
275
+ ...(productInfo.validFrom && { validFrom: productInfo.validFrom }),
276
+ ...(productInfo.validThrough && { validThrough: productInfo.validThrough }),
277
+ },
278
+ }),
279
+ ...(productInfo.rating && {
280
+ aggregateRating: {
281
+ "@type": "AggregateRating",
282
+ ratingValue: productInfo.rating.average,
283
+ reviewCount: productInfo.rating.count,
284
+ bestRating: productInfo.rating.best || 5,
285
+ worstRating: productInfo.rating.worst || 1,
286
+ },
287
+ }),
288
+ };
289
+
290
+ return {
291
+ ...baseSchema,
292
+ ...productData,
293
+ };
294
+ }
295
+
296
+ /**
297
+ * Generate FAQ schema markup
298
+ */
299
+ private generateFAQSchema(post: WordPressPost, baseSchema: SchemaMarkup, options: SchemaOptions): SchemaMarkup {
300
+ const content = post.content?.rendered || "";
301
+ const faqItems = this.extractFAQItems(content);
302
+
303
+ if (faqItems.length === 0) {
304
+ this.logger.warn("No FAQ items found in content", { postId: post.id });
305
+ }
306
+
307
+ const faqData: FAQSchemaData = {
308
+ mainEntity: faqItems.map((item) => ({
309
+ "@type": "Question",
310
+ name: item.question,
311
+ acceptedAnswer: {
312
+ "@type": "Answer",
313
+ text: item.answer,
314
+ },
315
+ })),
316
+ };
317
+
318
+ return {
319
+ ...baseSchema,
320
+ ...faqData,
321
+ };
322
+ }
323
+
324
+ /**
325
+ * Generate HowTo schema markup
326
+ */
327
+ private generateHowToSchema(post: WordPressPost, baseSchema: SchemaMarkup, options: SchemaOptions): SchemaMarkup {
328
+ const content = post.content?.rendered || "";
329
+ const steps = this.extractHowToSteps(content);
330
+ const images = this.extractImages(content);
331
+ const totalTime = this.extractDuration(content);
332
+
333
+ return {
334
+ ...baseSchema,
335
+ name: post.title?.rendered || "Untitled Guide",
336
+ description: this.extractTextContent(post.excerpt?.rendered || "").substring(0, 160),
337
+ ...(images.length > 0 && { image: images }),
338
+ ...(totalTime && { totalTime }),
339
+ supply: this.extractSupplies(content),
340
+ tool: this.extractTools(content),
341
+ step: steps.map((step, index) => ({
342
+ "@type": "HowToStep",
343
+ position: index + 1,
344
+ name: step.name,
345
+ text: step.text,
346
+ ...(step.image && { image: step.image }),
347
+ })),
348
+ };
349
+ }
350
+
351
+ /**
352
+ * Generate Organization schema markup
353
+ */
354
+ private generateOrganizationSchema(
355
+ post: WordPressPost,
356
+ baseSchema: SchemaMarkup,
357
+ options: SchemaOptions,
358
+ ): SchemaMarkup {
359
+ const siteConfig = options.siteConfig || {};
360
+
361
+ return {
362
+ ...baseSchema,
363
+ name: siteConfig.name || post.title?.rendered || "Organization",
364
+ description: siteConfig.description || this.extractTextContent(post.excerpt?.rendered || ""),
365
+ url: siteConfig.url || post.link || "https://example.com",
366
+ ...(siteConfig.logo && {
367
+ logo: {
368
+ "@type": "ImageObject",
369
+ url: siteConfig.logo,
370
+ },
371
+ }),
372
+ ...(siteConfig.socialProfiles && { sameAs: siteConfig.socialProfiles }),
373
+ ...(siteConfig.contactInfo && {
374
+ contactPoint: {
375
+ "@type": "ContactPoint",
376
+ ...(siteConfig.contactInfo.telephone && { telephone: siteConfig.contactInfo.telephone }),
377
+ ...(siteConfig.contactInfo.email && { email: siteConfig.contactInfo.email }),
378
+ },
379
+ }),
380
+ };
381
+ }
382
+
383
+ /**
384
+ * Generate LocalBusiness schema markup
385
+ */
386
+ private generateLocalBusinessSchema(
387
+ post: WordPressPost,
388
+ baseSchema: SchemaMarkup,
389
+ options: SchemaOptions,
390
+ ): SchemaMarkup {
391
+ const organizationSchema = this.generateOrganizationSchema(post, baseSchema, options);
392
+ const businessInfo = this.extractBusinessInfo(post.content?.rendered || "");
393
+
394
+ return {
395
+ ...organizationSchema,
396
+ "@type": "LocalBusiness",
397
+ ...(businessInfo.address && {
398
+ address: {
399
+ "@type": "PostalAddress",
400
+ streetAddress: businessInfo.address.street,
401
+ addressLocality: businessInfo.address.city,
402
+ addressRegion: businessInfo.address.state,
403
+ postalCode: businessInfo.address.zip,
404
+ addressCountry: businessInfo.address.country,
405
+ },
406
+ }),
407
+ ...(businessInfo.phone && { telephone: businessInfo.phone }),
408
+ ...(businessInfo.hours && { openingHours: businessInfo.hours }),
409
+ ...(businessInfo.priceRange && { priceRange: businessInfo.priceRange }),
410
+ };
411
+ }
412
+
413
+ /**
414
+ * Generate Website schema markup
415
+ */
416
+ private generateWebsiteSchema(post: WordPressPost, baseSchema: SchemaMarkup, options: SchemaOptions): SchemaMarkup {
417
+ const siteConfig = options.siteConfig || {};
418
+
419
+ return {
420
+ ...baseSchema,
421
+ name: siteConfig.name || "Website",
422
+ description: siteConfig.description || this.extractTextContent(post.excerpt?.rendered || ""),
423
+ url: siteConfig.url || "https://example.com",
424
+ ...(options.includeAuthor && {
425
+ author: {
426
+ "@type": "Organization",
427
+ name: siteConfig.name || "Website Owner",
428
+ },
429
+ }),
430
+ potentialAction: {
431
+ "@type": "SearchAction",
432
+ target: {
433
+ "@type": "EntryPoint",
434
+ urlTemplate: `${siteConfig.url || "https://example.com"}?s={search_term_string}`,
435
+ },
436
+ "query-input": "required name=search_term_string",
437
+ },
438
+ };
439
+ }
440
+
441
+ /**
442
+ * Generate BreadcrumbList schema markup
443
+ */
444
+ private generateBreadcrumbSchema(
445
+ post: WordPressPost,
446
+ baseSchema: SchemaMarkup,
447
+ options: SchemaOptions,
448
+ ): SchemaMarkup {
449
+ // Extract breadcrumb path from post URL or categories
450
+ const breadcrumbs = this.extractBreadcrumbs(post);
451
+
452
+ return {
453
+ ...baseSchema,
454
+ itemListElement: breadcrumbs.map((breadcrumb, index) => ({
455
+ "@type": "ListItem",
456
+ position: index + 1,
457
+ name: breadcrumb.name,
458
+ item: breadcrumb.url,
459
+ })),
460
+ };
461
+ }
462
+
463
+ /**
464
+ * Generate Event schema markup
465
+ */
466
+ private generateEventSchema(post: WordPressPost, baseSchema: SchemaMarkup, options: SchemaOptions): SchemaMarkup {
467
+ const eventInfo = this.extractEventInfo(post.content?.rendered || "");
468
+
469
+ return {
470
+ ...baseSchema,
471
+ name: post.title?.rendered || "Event",
472
+ description: this.extractTextContent(post.excerpt?.rendered || ""),
473
+ startDate: eventInfo.startDate || new Date().toISOString(),
474
+ ...(eventInfo.endDate && { endDate: eventInfo.endDate }),
475
+ ...(eventInfo.location && {
476
+ location: {
477
+ "@type": "Place",
478
+ name: eventInfo.location.name,
479
+ ...(eventInfo.location.address && {
480
+ address: {
481
+ "@type": "PostalAddress",
482
+ streetAddress: eventInfo.location.address,
483
+ },
484
+ }),
485
+ },
486
+ }),
487
+ ...(eventInfo.organizer && {
488
+ organizer: {
489
+ "@type": "Organization",
490
+ name: eventInfo.organizer,
491
+ },
492
+ }),
493
+ };
494
+ }
495
+
496
+ /**
497
+ * Generate Recipe schema markup
498
+ */
499
+ private generateRecipeSchema(post: WordPressPost, baseSchema: SchemaMarkup, options: SchemaOptions): SchemaMarkup {
500
+ const recipeInfo = this.extractRecipeInfo(post.content?.rendered || "");
501
+ const images = this.extractImages(post.content?.rendered || "");
502
+
503
+ return {
504
+ ...baseSchema,
505
+ name: post.title?.rendered || "Recipe",
506
+ description: this.extractTextContent(post.excerpt?.rendered || ""),
507
+ ...(images.length > 0 && { image: images }),
508
+ author: {
509
+ "@type": "Person",
510
+ name: this.getAuthorName(post),
511
+ },
512
+ ...(recipeInfo.prepTime && { prepTime: recipeInfo.prepTime }),
513
+ ...(recipeInfo.cookTime && { cookTime: recipeInfo.cookTime }),
514
+ ...(recipeInfo.totalTime && { totalTime: recipeInfo.totalTime }),
515
+ ...(recipeInfo.servings && { recipeYield: recipeInfo.servings }),
516
+ ...(recipeInfo.ingredients.length > 0 && { recipeIngredient: recipeInfo.ingredients }),
517
+ ...(recipeInfo.instructions.length > 0 && {
518
+ recipeInstructions: recipeInfo.instructions.map((instruction, index) => ({
519
+ "@type": "HowToStep",
520
+ position: index + 1,
521
+ text: instruction,
522
+ })),
523
+ }),
524
+ ...(recipeInfo.nutrition && {
525
+ nutrition: {
526
+ "@type": "NutritionInformation",
527
+ calories: recipeInfo.nutrition.calories,
528
+ },
529
+ }),
530
+ };
531
+ }
532
+
533
+ /**
534
+ * Generate Course schema markup
535
+ */
536
+ private generateCourseSchema(post: WordPressPost, baseSchema: SchemaMarkup, options: SchemaOptions): SchemaMarkup {
537
+ const courseInfo = this.extractCourseInfo(post.content?.rendered || "");
538
+
539
+ return {
540
+ ...baseSchema,
541
+ name: post.title?.rendered || "Course",
542
+ description: this.extractTextContent(post.excerpt?.rendered || ""),
543
+ provider: {
544
+ "@type": "Organization",
545
+ name: options.siteConfig?.name || "Course Provider",
546
+ },
547
+ ...(courseInfo.instructor && {
548
+ instructor: {
549
+ "@type": "Person",
550
+ name: courseInfo.instructor,
551
+ },
552
+ }),
553
+ ...(courseInfo.duration && { timeRequired: courseInfo.duration }),
554
+ ...(courseInfo.level && { courseLevel: courseInfo.level }),
555
+ ...(courseInfo.prerequisites.length > 0 && { coursePrerequisites: courseInfo.prerequisites }),
556
+ };
557
+ }
558
+
559
+ /**
560
+ * Generate VideoObject schema markup
561
+ */
562
+ private generateVideoSchema(post: WordPressPost, baseSchema: SchemaMarkup, options: SchemaOptions): SchemaMarkup {
563
+ const videoInfo = this.extractVideoInfo(post.content?.rendered || "");
564
+
565
+ return {
566
+ ...baseSchema,
567
+ name: post.title?.rendered || "Video",
568
+ description: this.extractTextContent(post.excerpt?.rendered || ""),
569
+ ...(videoInfo.url && { contentUrl: videoInfo.url }),
570
+ ...(videoInfo.thumbnail && { thumbnailUrl: videoInfo.thumbnail }),
571
+ ...(videoInfo.duration && { duration: videoInfo.duration }),
572
+ uploadDate: post.date || new Date().toISOString(),
573
+ author: {
574
+ "@type": "Person",
575
+ name: this.getAuthorName(post),
576
+ },
577
+ };
578
+ }
579
+
580
+ /**
581
+ * Generate Person schema markup
582
+ */
583
+ private generatePersonSchema(post: WordPressPost, baseSchema: SchemaMarkup, options: SchemaOptions): SchemaMarkup {
584
+ const personInfo = this.extractPersonInfo(post.content?.rendered || "");
585
+
586
+ return {
587
+ ...baseSchema,
588
+ name: post.title?.rendered || "Person",
589
+ description: this.extractTextContent(post.excerpt?.rendered || ""),
590
+ ...(personInfo.jobTitle && { jobTitle: personInfo.jobTitle }),
591
+ ...(personInfo.affiliation && {
592
+ affiliation: {
593
+ "@type": "Organization",
594
+ name: personInfo.affiliation,
595
+ },
596
+ }),
597
+ ...(personInfo.email && { email: personInfo.email }),
598
+ ...(personInfo.socialProfiles && { sameAs: personInfo.socialProfiles }),
599
+ };
600
+ }
601
+
602
+ /**
603
+ * Generate Review schema markup
604
+ */
605
+ private generateReviewSchema(post: WordPressPost, baseSchema: SchemaMarkup, options: SchemaOptions): SchemaMarkup {
606
+ const reviewInfo = this.extractReviewInfo(post.content?.rendered || "");
607
+
608
+ return {
609
+ ...baseSchema,
610
+ itemReviewed: {
611
+ "@type": reviewInfo.itemType || "Thing",
612
+ name: reviewInfo.itemName || "Reviewed Item",
613
+ },
614
+ reviewRating: {
615
+ "@type": "Rating",
616
+ ratingValue: reviewInfo.rating || 5,
617
+ bestRating: reviewInfo.bestRating || 5,
618
+ worstRating: reviewInfo.worstRating || 1,
619
+ },
620
+ author: {
621
+ "@type": "Person",
622
+ name: this.getAuthorName(post),
623
+ },
624
+ reviewBody: this.extractTextContent(post.content?.rendered || ""),
625
+ datePublished: post.date || new Date().toISOString(),
626
+ };
627
+ }
628
+
629
+ // Helper methods for content extraction
630
+
631
+ /**
632
+ * Extract plain text from HTML content
633
+ */
634
+ private extractTextContent(html: string): string {
635
+ return html
636
+ .replace(/<[^>]*>/g, " ")
637
+ .replace(/\s+/g, " ")
638
+ .trim();
639
+ }
640
+
641
+ /**
642
+ * Extract images from HTML content
643
+ */
644
+ private extractImages(html: string): string[] {
645
+ const images: string[] = [];
646
+ const imgRegex = /<img[^>]+src="([^"]+)"/gi;
647
+ let match;
648
+
649
+ while ((match = imgRegex.exec(html)) !== null) {
650
+ images.push(match[1]);
651
+ }
652
+
653
+ return images;
654
+ }
655
+
656
+ /**
657
+ * Count words in text
658
+ */
659
+ private countWords(text: string): number {
660
+ return text.split(/\s+/).filter((word) => word.length > 0).length;
661
+ }
662
+
663
+ /**
664
+ * Get author name from post
665
+ */
666
+ private getAuthorName(post: WordPressPost): string {
667
+ // This would typically come from WordPress API author data
668
+ return "Author"; // Placeholder
669
+ }
670
+
671
+ /**
672
+ * Get author URL from post
673
+ */
674
+ private getAuthorUrl(post: WordPressPost): string | undefined {
675
+ // This would typically come from WordPress API author data
676
+ return undefined; // Placeholder
677
+ }
678
+
679
+ /**
680
+ * Get publisher information
681
+ */
682
+ private getPublisherInfo(options: SchemaOptions): ArticleSchemaData["publisher"] {
683
+ const siteConfig = options.siteConfig || {};
684
+
685
+ return {
686
+ "@type": "Organization",
687
+ name: siteConfig.name || "Publisher",
688
+ ...(siteConfig.logo && {
689
+ logo: {
690
+ "@type": "ImageObject",
691
+ url: siteConfig.logo,
692
+ },
693
+ }),
694
+ };
695
+ }
696
+
697
+ /**
698
+ * Extract product information from content
699
+ */
700
+ private extractProductInfo(content: string): {
701
+ brand?: string;
702
+ price?: string;
703
+ currency?: string;
704
+ availability?: string;
705
+ validFrom?: string;
706
+ validThrough?: string;
707
+ rating?: {
708
+ average: number;
709
+ count: number;
710
+ best?: number;
711
+ worst?: number;
712
+ };
713
+ } {
714
+ // Implement product information extraction logic
715
+ return {}; // Placeholder
716
+ }
717
+
718
+ /**
719
+ * Extract FAQ items from content
720
+ */
721
+ private extractFAQItems(html: string): Array<{ question: string; answer: string }> {
722
+ const faqItems: Array<{ question: string; answer: string }> = [];
723
+
724
+ // Look for FAQ patterns in HTML
725
+ const faqRegex = /<h[23][^>]*>(.*?)<\/h[23]>\s*<p[^>]*>(.*?)<\/p>/gi;
726
+ let match;
727
+
728
+ while ((match = faqRegex.exec(html)) !== null) {
729
+ const question = this.extractTextContent(match[1]);
730
+ const answer = this.extractTextContent(match[2]);
731
+
732
+ if (question && answer) {
733
+ faqItems.push({ question, answer });
734
+ }
735
+ }
736
+
737
+ return faqItems;
738
+ }
739
+
740
+ /**
741
+ * Extract HowTo steps from content
742
+ */
743
+ private extractHowToSteps(html: string): Array<{ name: string; text: string; image?: string }> {
744
+ const steps: Array<{ name: string; text: string; image?: string }> = [];
745
+
746
+ // Look for step patterns in HTML
747
+ const stepRegex = /<h[23][^>]*>(.*?)<\/h[23]>\s*<p[^>]*>(.*?)<\/p>/gi;
748
+ let match;
749
+
750
+ while ((match = stepRegex.exec(html)) !== null) {
751
+ const name = this.extractTextContent(match[1]);
752
+ const text = this.extractTextContent(match[2]);
753
+
754
+ if (name && text) {
755
+ steps.push({ name, text });
756
+ }
757
+ }
758
+
759
+ return steps;
760
+ }
761
+
762
+ /**
763
+ * Extract duration from content
764
+ */
765
+ private extractDuration(content: string): string | undefined {
766
+ const durationMatch = content.match(/(\d+)\s*(minutes?|hours?|mins?)/i);
767
+ if (durationMatch) {
768
+ const value = parseInt(durationMatch[1]);
769
+ const unit = durationMatch[2].toLowerCase();
770
+
771
+ if (unit.startsWith("min")) {
772
+ return `PT${value}M`;
773
+ } else if (unit.startsWith("hour")) {
774
+ return `PT${value}H`;
775
+ }
776
+ }
777
+ return undefined;
778
+ }
779
+
780
+ /**
781
+ * Extract supplies from content
782
+ */
783
+ private extractSupplies(content: string): string[] {
784
+ // Implement supplies extraction logic
785
+ return []; // Placeholder
786
+ }
787
+
788
+ /**
789
+ * Extract tools from content
790
+ */
791
+ private extractTools(content: string): string[] {
792
+ // Implement tools extraction logic
793
+ return []; // Placeholder
794
+ }
795
+
796
+ /**
797
+ * Extract business information from content
798
+ */
799
+ private extractBusinessInfo(content: string): {
800
+ address?: {
801
+ street: string;
802
+ city: string;
803
+ state: string;
804
+ zip: string;
805
+ country: string;
806
+ };
807
+ phone?: string;
808
+ hours?: string[];
809
+ priceRange?: string;
810
+ } {
811
+ // Implement business info extraction logic
812
+ return {}; // Placeholder
813
+ }
814
+
815
+ /**
816
+ * Extract breadcrumbs from post
817
+ */
818
+ private extractBreadcrumbs(post: WordPressPost): Array<{ name: string; url: string }> {
819
+ // Implement breadcrumb extraction logic
820
+ return [
821
+ { name: "Home", url: "https://example.com" },
822
+ { name: post.title?.rendered || "Post", url: post.link || "#" },
823
+ ]; // Placeholder
824
+ }
825
+
826
+ /**
827
+ * Extract event information from content
828
+ */
829
+ private extractEventInfo(content: string): {
830
+ startDate?: string;
831
+ endDate?: string;
832
+ location?: {
833
+ name: string;
834
+ address?: string;
835
+ };
836
+ organizer?: string;
837
+ } {
838
+ // Implement event info extraction logic
839
+ return {}; // Placeholder
840
+ }
841
+
842
+ /**
843
+ * Extract recipe information from content
844
+ */
845
+ private extractRecipeInfo(content: string): {
846
+ prepTime?: string;
847
+ cookTime?: string;
848
+ totalTime?: string;
849
+ servings?: string;
850
+ ingredients: string[];
851
+ instructions: string[];
852
+ nutrition?: {
853
+ calories: string;
854
+ };
855
+ } {
856
+ // Implement recipe info extraction logic
857
+ return {
858
+ ingredients: [],
859
+ instructions: [],
860
+ }; // Placeholder
861
+ }
862
+
863
+ /**
864
+ * Extract course information from content
865
+ */
866
+ private extractCourseInfo(content: string): {
867
+ instructor?: string;
868
+ duration?: string;
869
+ level?: string;
870
+ prerequisites: string[];
871
+ } {
872
+ // Implement course info extraction logic
873
+ return {
874
+ prerequisites: [],
875
+ }; // Placeholder
876
+ }
877
+
878
+ /**
879
+ * Extract video information from content
880
+ */
881
+ private extractVideoInfo(content: string): {
882
+ url?: string;
883
+ thumbnail?: string;
884
+ duration?: string;
885
+ } {
886
+ // Implement video info extraction logic
887
+ return {}; // Placeholder
888
+ }
889
+
890
+ /**
891
+ * Extract person information from content
892
+ */
893
+ private extractPersonInfo(content: string): {
894
+ jobTitle?: string;
895
+ affiliation?: string;
896
+ email?: string;
897
+ socialProfiles?: string[];
898
+ } {
899
+ // Implement person info extraction logic
900
+ return {}; // Placeholder
901
+ }
902
+
903
+ /**
904
+ * Extract review information from content
905
+ */
906
+ private extractReviewInfo(content: string): {
907
+ itemName?: string;
908
+ itemType?: string;
909
+ rating?: number;
910
+ bestRating?: number;
911
+ worstRating?: number;
912
+ } {
913
+ // Look for rating patterns in content
914
+ const ratingMatch = content.match(/(\d+(?:\.\d+)?)\s*(?:out of|\/)\s*(\d+)/i);
915
+
916
+ if (ratingMatch) {
917
+ return {
918
+ rating: parseFloat(ratingMatch[1]),
919
+ bestRating: parseInt(ratingMatch[2]),
920
+ };
921
+ }
922
+
923
+ return {}; // Placeholder
924
+ }
925
+
926
+ /**
927
+ * Validate generated schema markup
928
+ */
929
+ validateSchema(schema: SchemaMarkup): { valid: boolean; errors: string[] } {
930
+ const errors: string[] = [];
931
+
932
+ // Basic validation
933
+ if (!schema["@context"]) {
934
+ errors.push("Missing @context");
935
+ }
936
+
937
+ if (!schema["@type"]) {
938
+ errors.push("Missing @type");
939
+ }
940
+
941
+ // Type-specific validation
942
+ if (schema["@type"] === "Article" && !schema.headline) {
943
+ errors.push("Article schema missing required headline property");
944
+ }
945
+
946
+ if (schema["@type"] === "Product" && !schema.name) {
947
+ errors.push("Product schema missing required name property");
948
+ }
949
+
950
+ return {
951
+ valid: errors.length === 0,
952
+ errors,
953
+ };
954
+ }
955
+ }