mcp-wordpress 2.4.2 → 2.5.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 (348) hide show
  1. package/README.md +114 -48
  2. package/dist/ajv-patch.js +34 -0
  3. package/dist/cache/CacheInvalidation.d.ts +3 -1
  4. package/dist/cache/CacheInvalidation.d.ts.map +1 -1
  5. package/dist/cache/CacheInvalidation.js +10 -4
  6. package/dist/cache/CacheInvalidation.js.map +1 -1
  7. package/dist/cache/CacheManager.d.ts +3 -2
  8. package/dist/cache/CacheManager.d.ts.map +1 -1
  9. package/dist/cache/CacheManager.js +11 -3
  10. package/dist/cache/CacheManager.js.map +1 -1
  11. package/dist/cache/HttpCacheWrapper.d.ts +7 -6
  12. package/dist/cache/HttpCacheWrapper.d.ts.map +1 -1
  13. package/dist/cache/HttpCacheWrapper.js +8 -5
  14. package/dist/cache/HttpCacheWrapper.js.map +1 -1
  15. package/dist/cache/__tests__/HttpCacheWrapper.test.js +6 -5
  16. package/dist/cache/__tests__/HttpCacheWrapper.test.js.map +1 -1
  17. package/dist/cache/index.d.ts +3 -3
  18. package/dist/cache/index.d.ts.map +1 -1
  19. package/dist/cache/index.js +1 -1
  20. package/dist/cache/index.js.map +1 -1
  21. package/dist/client/CachedWordPressClient.d.ts +23 -9
  22. package/dist/client/CachedWordPressClient.d.ts.map +1 -1
  23. package/dist/client/CachedWordPressClient.js +4 -1
  24. package/dist/client/CachedWordPressClient.js.map +1 -1
  25. package/dist/client/MockWordPressClient.d.ts +2 -1
  26. package/dist/client/MockWordPressClient.d.ts.map +1 -1
  27. package/dist/client/MockWordPressClient.js +3 -1
  28. package/dist/client/MockWordPressClient.js.map +1 -1
  29. package/dist/client/api.d.ts +17 -13
  30. package/dist/client/api.d.ts.map +1 -1
  31. package/dist/client/api.js +135 -30
  32. package/dist/client/api.js.map +1 -1
  33. package/dist/client/auth.d.ts.map +1 -1
  34. package/dist/client/auth.js +2 -3
  35. package/dist/client/auth.js.map +1 -1
  36. package/dist/client/managers/AuthenticationManager.d.ts +55 -2
  37. package/dist/client/managers/AuthenticationManager.d.ts.map +1 -1
  38. package/dist/client/managers/AuthenticationManager.js +269 -71
  39. package/dist/client/managers/AuthenticationManager.js.map +1 -1
  40. package/dist/client/managers/BaseManager.d.ts +3 -3
  41. package/dist/client/managers/BaseManager.d.ts.map +1 -1
  42. package/dist/client/managers/BaseManager.js +11 -5
  43. package/dist/client/managers/BaseManager.js.map +1 -1
  44. package/dist/client/managers/RequestManager.d.ts +2 -2
  45. package/dist/client/managers/RequestManager.d.ts.map +1 -1
  46. package/dist/client/managers/RequestManager.js +25 -12
  47. package/dist/client/managers/RequestManager.js.map +1 -1
  48. package/dist/config/Config.d.ts +155 -0
  49. package/dist/config/Config.d.ts.map +1 -0
  50. package/dist/config/Config.js +215 -0
  51. package/dist/config/Config.js.map +1 -0
  52. package/dist/config/ConfigurationSchema.d.ts +21 -21
  53. package/dist/config/ConfigurationSchema.d.ts.map +1 -1
  54. package/dist/config/ConfigurationSchema.js +19 -2
  55. package/dist/config/ConfigurationSchema.js.map +1 -1
  56. package/dist/config/ServerConfiguration.d.ts +2 -1
  57. package/dist/config/ServerConfiguration.d.ts.map +1 -1
  58. package/dist/config/ServerConfiguration.js +50 -41
  59. package/dist/config/ServerConfiguration.js.map +1 -1
  60. package/dist/docs/DocumentationGenerator.d.ts +9 -8
  61. package/dist/docs/DocumentationGenerator.d.ts.map +1 -1
  62. package/dist/docs/DocumentationGenerator.js +10 -7
  63. package/dist/docs/DocumentationGenerator.js.map +1 -1
  64. package/dist/docs/MarkdownFormatter.d.ts.map +1 -1
  65. package/dist/docs/MarkdownFormatter.js +3 -2
  66. package/dist/docs/MarkdownFormatter.js.map +1 -1
  67. package/dist/dxt-entry.cjs +81 -0
  68. package/dist/dxt-entry.js +15 -14
  69. package/dist/dxt-entry.js.map +1 -1
  70. package/dist/index.d.ts +3 -1
  71. package/dist/index.d.ts.map +1 -1
  72. package/dist/index.js +37 -21
  73. package/dist/index.js.map +1 -1
  74. package/dist/performance/MetricsCollector.d.ts +13 -7
  75. package/dist/performance/MetricsCollector.d.ts.map +1 -1
  76. package/dist/performance/MetricsCollector.js +69 -27
  77. package/dist/performance/MetricsCollector.js.map +1 -1
  78. package/dist/performance/PerformanceAnalytics.d.ts +8 -2
  79. package/dist/performance/PerformanceAnalytics.d.ts.map +1 -1
  80. package/dist/performance/PerformanceAnalytics.js +17 -47
  81. package/dist/performance/PerformanceAnalytics.js.map +1 -1
  82. package/dist/performance/PerformanceMonitor.d.ts +2 -1
  83. package/dist/performance/PerformanceMonitor.d.ts.map +1 -1
  84. package/dist/performance/PerformanceMonitor.js +12 -13
  85. package/dist/performance/PerformanceMonitor.js.map +1 -1
  86. package/dist/performance/index.d.ts +2 -2
  87. package/dist/performance/index.d.ts.map +1 -1
  88. package/dist/security/AISecurityScanner.d.ts +1 -0
  89. package/dist/security/AISecurityScanner.d.ts.map +1 -1
  90. package/dist/security/AISecurityScanner.js +22 -12
  91. package/dist/security/AISecurityScanner.js.map +1 -1
  92. package/dist/security/AutomatedRemediation.d.ts +4 -3
  93. package/dist/security/AutomatedRemediation.d.ts.map +1 -1
  94. package/dist/security/AutomatedRemediation.js +46 -15
  95. package/dist/security/AutomatedRemediation.js.map +1 -1
  96. package/dist/security/InputValidator.d.ts +13 -9
  97. package/dist/security/InputValidator.d.ts.map +1 -1
  98. package/dist/security/InputValidator.js +4 -2
  99. package/dist/security/InputValidator.js.map +1 -1
  100. package/dist/security/SecurityCIPipeline.d.ts +1 -1
  101. package/dist/security/SecurityCIPipeline.d.ts.map +1 -1
  102. package/dist/security/SecurityCIPipeline.js +38 -29
  103. package/dist/security/SecurityCIPipeline.js.map +1 -1
  104. package/dist/security/SecurityConfig.d.ts +3 -3
  105. package/dist/security/SecurityConfig.d.ts.map +1 -1
  106. package/dist/security/SecurityConfig.js +13 -9
  107. package/dist/security/SecurityConfig.js.map +1 -1
  108. package/dist/security/SecurityConfigManager.d.ts +2 -2
  109. package/dist/security/SecurityConfigManager.d.ts.map +1 -1
  110. package/dist/security/SecurityConfigManager.js +20 -15
  111. package/dist/security/SecurityConfigManager.js.map +1 -1
  112. package/dist/security/SecurityMonitoring.d.ts +2 -2
  113. package/dist/security/SecurityMonitoring.d.ts.map +1 -1
  114. package/dist/security/SecurityMonitoring.js +19 -17
  115. package/dist/security/SecurityMonitoring.js.map +1 -1
  116. package/dist/security/SecurityReviewer.d.ts.map +1 -1
  117. package/dist/security/SecurityReviewer.js +10 -7
  118. package/dist/security/SecurityReviewer.js.map +1 -1
  119. package/dist/security/index.d.ts +24 -23
  120. package/dist/security/index.d.ts.map +1 -1
  121. package/dist/security/index.js +52 -23
  122. package/dist/security/index.js.map +1 -1
  123. package/dist/server/ConnectionTester.d.ts +12 -4
  124. package/dist/server/ConnectionTester.d.ts.map +1 -1
  125. package/dist/server/ConnectionTester.js +96 -22
  126. package/dist/server/ConnectionTester.js.map +1 -1
  127. package/dist/server/ToolRegistry.d.ts +2 -2
  128. package/dist/server/ToolRegistry.d.ts.map +1 -1
  129. package/dist/server/ToolRegistry.js +10 -5
  130. package/dist/server/ToolRegistry.js.map +1 -1
  131. package/dist/tools/BaseToolManager.d.ts +47 -11
  132. package/dist/tools/BaseToolManager.d.ts.map +1 -1
  133. package/dist/tools/BaseToolManager.js +168 -29
  134. package/dist/tools/BaseToolManager.js.map +1 -1
  135. package/dist/tools/auth.d.ts +16 -10
  136. package/dist/tools/auth.d.ts.map +1 -1
  137. package/dist/tools/auth.js +3 -2
  138. package/dist/tools/auth.js.map +1 -1
  139. package/dist/tools/cache.d.ts +30 -30
  140. package/dist/tools/cache.d.ts.map +1 -1
  141. package/dist/tools/cache.js +1 -6
  142. package/dist/tools/cache.js.map +1 -1
  143. package/dist/tools/comments.d.ts +20 -20
  144. package/dist/tools/comments.d.ts.map +1 -1
  145. package/dist/tools/comments.js +16 -9
  146. package/dist/tools/comments.js.map +1 -1
  147. package/dist/tools/media.d.ts +18 -16
  148. package/dist/tools/media.d.ts.map +1 -1
  149. package/dist/tools/media.js +16 -15
  150. package/dist/tools/media.js.map +1 -1
  151. package/dist/tools/pages.d.ts +19 -17
  152. package/dist/tools/pages.d.ts.map +1 -1
  153. package/dist/tools/pages.js +16 -12
  154. package/dist/tools/pages.js.map +1 -1
  155. package/dist/tools/performance.d.ts +11 -1
  156. package/dist/tools/performance.d.ts.map +1 -1
  157. package/dist/tools/performance.js +67 -34
  158. package/dist/tools/performance.js.map +1 -1
  159. package/dist/tools/posts/PostHandlers.d.ts +46 -0
  160. package/dist/tools/posts/PostHandlers.d.ts.map +1 -0
  161. package/dist/tools/posts/PostHandlers.js +400 -0
  162. package/dist/tools/posts/PostHandlers.js.map +1 -0
  163. package/dist/tools/posts/PostToolDefinitions.d.ts +37 -0
  164. package/dist/tools/posts/PostToolDefinitions.d.ts.map +1 -0
  165. package/dist/tools/posts/PostToolDefinitions.js +236 -0
  166. package/dist/tools/posts/PostToolDefinitions.js.map +1 -0
  167. package/dist/tools/posts/index.d.ts +138 -0
  168. package/dist/tools/posts/index.d.ts.map +1 -0
  169. package/dist/tools/posts/index.js +163 -0
  170. package/dist/tools/posts/index.js.map +1 -0
  171. package/dist/tools/posts.d.ts +10 -246
  172. package/dist/tools/posts.d.ts.map +1 -1
  173. package/dist/tools/posts.js +11 -723
  174. package/dist/tools/posts.js.map +1 -1
  175. package/dist/tools/site.d.ts +19 -18
  176. package/dist/tools/site.d.ts.map +1 -1
  177. package/dist/tools/site.js +14 -10
  178. package/dist/tools/site.js.map +1 -1
  179. package/dist/tools/taxonomies.d.ts +23 -24
  180. package/dist/tools/taxonomies.d.ts.map +1 -1
  181. package/dist/tools/taxonomies.js +24 -18
  182. package/dist/tools/taxonomies.js.map +1 -1
  183. package/dist/tools/users.d.ts +20 -15
  184. package/dist/tools/users.d.ts.map +1 -1
  185. package/dist/tools/users.js +12 -8
  186. package/dist/tools/users.js.map +1 -1
  187. package/dist/types/client.d.ts +48 -41
  188. package/dist/types/client.d.ts.map +1 -1
  189. package/dist/types/client.js +30 -5
  190. package/dist/types/client.js.map +1 -1
  191. package/dist/types/enhanced.d.ts +237 -0
  192. package/dist/types/enhanced.d.ts.map +1 -0
  193. package/dist/types/enhanced.js +49 -0
  194. package/dist/types/enhanced.js.map +1 -0
  195. package/dist/types/index.d.ts +15 -12
  196. package/dist/types/index.d.ts.map +1 -1
  197. package/dist/types/index.js +2 -0
  198. package/dist/types/index.js.map +1 -1
  199. package/dist/types/mcp.d.ts +12 -12
  200. package/dist/types/mcp.d.ts.map +1 -1
  201. package/dist/types/requests.d.ts +322 -0
  202. package/dist/types/requests.d.ts.map +1 -0
  203. package/dist/types/requests.js +8 -0
  204. package/dist/types/requests.js.map +1 -0
  205. package/dist/types/tools.d.ts +506 -0
  206. package/dist/types/tools.d.ts.map +1 -0
  207. package/dist/types/tools.js +8 -0
  208. package/dist/types/tools.js.map +1 -0
  209. package/dist/types/wordpress.d.ts +43 -15
  210. package/dist/types/wordpress.d.ts.map +1 -1
  211. package/dist/types/wordpress.js +8 -1
  212. package/dist/types/wordpress.js.map +1 -1
  213. package/dist/utils/debug.d.ts +19 -11
  214. package/dist/utils/debug.d.ts.map +1 -1
  215. package/dist/utils/debug.js +46 -10
  216. package/dist/utils/debug.js.map +1 -1
  217. package/dist/utils/enhancedError.d.ts +8 -8
  218. package/dist/utils/enhancedError.d.ts.map +1 -1
  219. package/dist/utils/enhancedError.js.map +1 -1
  220. package/dist/utils/error.d.ts +2 -4
  221. package/dist/utils/error.d.ts.map +1 -1
  222. package/dist/utils/error.js +42 -5
  223. package/dist/utils/error.js.map +1 -1
  224. package/dist/utils/logger.d.ts +106 -0
  225. package/dist/utils/logger.d.ts.map +1 -0
  226. package/dist/utils/logger.js +280 -0
  227. package/dist/utils/logger.js.map +1 -0
  228. package/dist/utils/streaming.d.ts +9 -9
  229. package/dist/utils/streaming.d.ts.map +1 -1
  230. package/dist/utils/streaming.js +71 -52
  231. package/dist/utils/streaming.js.map +1 -1
  232. package/dist/utils/toolWrapper.d.ts +9 -7
  233. package/dist/utils/toolWrapper.d.ts.map +1 -1
  234. package/dist/utils/toolWrapper.js.map +1 -1
  235. package/dist/utils/validation/core.d.ts +21 -0
  236. package/dist/utils/validation/core.d.ts.map +1 -0
  237. package/dist/utils/validation/core.js +71 -0
  238. package/dist/utils/validation/core.js.map +1 -0
  239. package/dist/utils/validation/index.d.ts +25 -0
  240. package/dist/utils/validation/index.d.ts.map +1 -0
  241. package/dist/utils/validation/index.js +29 -0
  242. package/dist/utils/validation/index.js.map +1 -0
  243. package/dist/utils/validation/network.d.ts +19 -0
  244. package/dist/utils/validation/network.d.ts.map +1 -0
  245. package/dist/utils/validation/network.js +93 -0
  246. package/dist/utils/validation/network.js.map +1 -0
  247. package/dist/utils/validation/rateLimit.d.ts +21 -0
  248. package/dist/utils/validation/rateLimit.d.ts.map +1 -0
  249. package/dist/utils/validation/rateLimit.js +43 -0
  250. package/dist/utils/validation/rateLimit.js.map +1 -0
  251. package/dist/utils/validation/security.d.ts +29 -0
  252. package/dist/utils/validation/security.d.ts.map +1 -0
  253. package/dist/utils/validation/security.js +327 -0
  254. package/dist/utils/validation/security.js.map +1 -0
  255. package/dist/utils/validation/wordpress.d.ts +31 -0
  256. package/dist/utils/validation/wordpress.d.ts.map +1 -0
  257. package/dist/utils/validation/wordpress.js +146 -0
  258. package/dist/utils/validation/wordpress.js.map +1 -0
  259. package/dist/utils/validation.d.ts +13 -82
  260. package/dist/utils/validation.d.ts.map +1 -1
  261. package/dist/utils/validation.js +25 -343
  262. package/dist/utils/validation.js.map +1 -1
  263. package/docs/BADGE_UPDATES.md +132 -0
  264. package/docs/CI_CD_IMPROVEMENTS.md +191 -0
  265. package/docs/INCREMENTAL_COVERAGE.md +183 -0
  266. package/docs/api/README.md +3 -1
  267. package/docs/api/openapi.json +5 -1
  268. package/docs/api/summary.json +1 -1
  269. package/docs/api/tools/wp_create_post.md +12 -14
  270. package/docs/examples/claude-desktop-config.md +1 -1
  271. package/docs/examples/docker-production.md +100 -93
  272. package/docs/examples/multi-site-setup.md +5 -4
  273. package/docs/examples/single-site-setup.md +3 -4
  274. package/docs/examples/use-case-workflows.md +4 -5
  275. package/docs/integrations/claude-desktop.md +31 -31
  276. package/docs/integrations/cline.md +4 -4
  277. package/docs/integrations/vs-code.md +9 -8
  278. package/docs/user-guides/SMITHERY_SETUP.md +10 -10
  279. package/package.json +44 -25
  280. package/src/cache/CacheInvalidation.ts +12 -5
  281. package/src/cache/CacheManager.ts +18 -15
  282. package/src/cache/HttpCacheWrapper.ts +30 -59
  283. package/src/cache/__tests__/HttpCacheWrapper.test.ts +6 -5
  284. package/src/cache/index.ts +3 -14
  285. package/src/client/CachedWordPressClient.ts +32 -30
  286. package/src/client/MockWordPressClient.ts +4 -2
  287. package/src/client/api.ts +186 -64
  288. package/src/client/auth.ts +15 -40
  289. package/src/client/managers/AuthenticationManager.ts +337 -77
  290. package/src/client/managers/BaseManager.ts +18 -30
  291. package/src/client/managers/RequestManager.ts +39 -44
  292. package/src/config/Config.ts +308 -0
  293. package/src/config/ConfigurationSchema.ts +23 -2
  294. package/src/config/ServerConfiguration.ts +51 -47
  295. package/src/docs/DocumentationGenerator.ts +50 -39
  296. package/src/docs/MarkdownFormatter.ts +19 -29
  297. package/src/dxt-entry.cjs +26 -16
  298. package/src/dxt-entry.ts +17 -27
  299. package/src/index.ts +42 -28
  300. package/src/performance/MetricsCollector.ts +108 -86
  301. package/src/performance/PerformanceAnalytics.ts +69 -164
  302. package/src/performance/PerformanceMonitor.ts +32 -47
  303. package/src/performance/index.ts +2 -10
  304. package/src/security/AISecurityScanner.ts +22 -12
  305. package/src/security/AutomatedRemediation.ts +49 -18
  306. package/src/security/InputValidator.ts +9 -6
  307. package/src/security/SecurityCIPipeline.ts +53 -37
  308. package/src/security/SecurityConfig.ts +22 -22
  309. package/src/security/SecurityConfigManager.ts +23 -19
  310. package/src/security/SecurityMonitoring.ts +24 -21
  311. package/src/security/SecurityReviewer.ts +10 -7
  312. package/src/security/index.ts +64 -29
  313. package/src/server/ConnectionTester.ts +120 -31
  314. package/src/server/ToolRegistry.ts +31 -21
  315. package/src/tools/BaseToolManager.ts +286 -33
  316. package/src/tools/auth.ts +20 -8
  317. package/src/tools/cache.ts +5 -15
  318. package/src/tools/comments.ts +34 -48
  319. package/src/tools/media.ts +41 -53
  320. package/src/tools/pages.ts +32 -54
  321. package/src/tools/performance.ts +141 -176
  322. package/src/tools/posts/PostHandlers.ts +474 -0
  323. package/src/tools/posts/PostToolDefinitions.ts +250 -0
  324. package/src/tools/posts/index.ts +192 -0
  325. package/src/tools/posts.ts +24 -780
  326. package/src/tools/site.ts +34 -19
  327. package/src/tools/taxonomies.ts +41 -57
  328. package/src/tools/users.ts +28 -16
  329. package/src/types/client.ts +114 -138
  330. package/src/types/enhanced.ts +318 -0
  331. package/src/types/index.ts +51 -30
  332. package/src/types/mcp.ts +20 -42
  333. package/src/types/requests.ts +378 -0
  334. package/src/types/tools.ts +608 -0
  335. package/src/types/wordpress.ts +56 -34
  336. package/src/utils/debug.ts +77 -59
  337. package/src/utils/enhancedError.ts +8 -8
  338. package/src/utils/error.ts +53 -31
  339. package/src/utils/logger.ts +351 -0
  340. package/src/utils/streaming.ts +86 -68
  341. package/src/utils/toolWrapper.ts +10 -12
  342. package/src/utils/validation/core.ts +108 -0
  343. package/src/utils/validation/index.ts +36 -0
  344. package/src/utils/validation/network.ts +132 -0
  345. package/src/utils/validation/rateLimit.ts +54 -0
  346. package/src/utils/validation/security.ts +361 -0
  347. package/src/utils/validation/wordpress.ts +180 -0
  348. package/src/utils/validation.ts +47 -470
@@ -1,6 +1,27 @@
1
1
  /**
2
2
  * Error handling utilities
3
3
  */
4
+ import { LoggerFactory } from "./logger.js";
5
+ import { config } from "../config/Config.js";
6
+
7
+ const logger = LoggerFactory.server().child({ component: "ErrorUtils" });
8
+
9
+ // Environment flag to control legacy console logging noise. Default enabled to preserve
10
+ // backward compatibility and existing test expectations. Set LEGACY_ERROR_LOGS=0 to disable
11
+ // the direct console.error side-channel (structured logger still emits).
12
+ const LEGACY_ERROR_LOGS_ENABLED = config().error.legacyLogsEnabled;
13
+
14
+ // Internal helper to avoid sprinkling conditionals
15
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
16
+ function legacyConsoleError(...args: any[]) {
17
+ if (LEGACY_ERROR_LOGS_ENABLED) {
18
+ console.error(...args);
19
+ }
20
+ }
21
+
22
+ // Test hook: exported only for instrumentation in unit tests (tree-shakeable)
23
+ // @__PURE__ This constant has no side effects and can be dropped in production builds
24
+ export const __errorUtilsLogger = logger;
4
25
 
5
26
  export function getErrorMessage(error: unknown): string {
6
27
  if (error instanceof Error) {
@@ -23,25 +44,34 @@ export function isError(error: unknown): error is Error {
23
44
  }
24
45
 
25
46
  export function logAndReturn<T>(error: unknown, defaultValue: T): T {
26
- console.error("Error occurred:", getErrorMessage(error));
47
+ const message = getErrorMessage(error);
48
+ // Legacy console logging (can be disabled via LEGACY_ERROR_LOGS=0)
49
+ legacyConsoleError("Error occurred:", message);
50
+ logger.warn("Error occurred - returning default value", {
51
+ error: message,
52
+ });
27
53
  return defaultValue;
28
54
  }
29
55
 
30
56
  /**
31
57
  * Enhanced error handler for consistent tool error handling
32
58
  */
33
- export function handleToolError(
34
- error: unknown,
35
- operation: string,
36
- context?: Record<string, unknown>,
37
- ): never {
38
- console.error(`Error in ${operation}:`, error);
39
-
59
+ export function handleToolError(error: unknown, operation: string, context?: Record<string, unknown>): never {
60
+ const message = getErrorMessage(error);
61
+ const errObj = error instanceof Error ? error : new Error(message);
62
+ // Legacy console logging (can be disabled via LEGACY_ERROR_LOGS=0)
63
+ legacyConsoleError(`Error in ${operation}:`, errObj);
40
64
  if (context) {
41
- console.error("Context:", context);
65
+ legacyConsoleError("Context:", context);
42
66
  }
67
+ logger.error(`Error in ${operation}`, {
68
+ error: message,
69
+ ...(context && { context }),
70
+ });
43
71
 
44
- const message = getErrorMessage(error);
72
+ if (error instanceof Error && error.stack) {
73
+ logger.debug("Error stack trace", { stack: error.stack });
74
+ }
45
75
 
46
76
  // Provide more specific error messages based on error content
47
77
  if (message.includes("ECONNREFUSED") || message.includes("ENOTFOUND")) {
@@ -51,21 +81,15 @@ export function handleToolError(
51
81
  }
52
82
 
53
83
  if (message.includes("401") || message.includes("Unauthorized")) {
54
- throw new Error(
55
- `Authentication failed during ${operation}. Please check your WordPress credentials.`,
56
- );
84
+ throw new Error(`Authentication failed during ${operation}. Please check your WordPress credentials.`);
57
85
  }
58
86
 
59
87
  if (message.includes("403") || message.includes("Forbidden")) {
60
- throw new Error(
61
- `Permission denied during ${operation}. Please check your user permissions.`,
62
- );
88
+ throw new Error(`Permission denied during ${operation}. Please check your user permissions.`);
63
89
  }
64
90
 
65
91
  if (message.includes("429") || message.includes("Too Many Requests")) {
66
- throw new Error(
67
- `Rate limit exceeded during ${operation}. Please try again later.`,
68
- );
92
+ throw new Error(`Rate limit exceeded during ${operation}. Please try again later.`);
69
93
  }
70
94
 
71
95
  throw new Error(`Failed to ${operation}: ${message}`);
@@ -74,11 +98,14 @@ export function handleToolError(
74
98
  /**
75
99
  * Validates required parameters
76
100
  */
77
- export function validateRequired(
78
- params: Record<string, unknown>,
79
- required: string[],
80
- ): void {
81
- const missing = required.filter((key) => !params[key]);
101
+ export function validateRequired(params: Record<string, unknown> | unknown, required: string[]): void {
102
+ // Runtime guard: ensure params is a non-null object (tests expect throw on invalid input)
103
+ if (params === null || typeof params !== "object") {
104
+ throw new Error("Parameters must be an object");
105
+ }
106
+ // Only treat undefined or null as missing; accept legitimate falsy values: 0, false, ""
107
+ const obj = params as Record<string, unknown>;
108
+ const missing = required.filter((key) => obj[key] === undefined || obj[key] === null);
82
109
  if (missing.length > 0) {
83
110
  throw new Error(`Missing required parameters: ${missing.join(", ")}`);
84
111
  }
@@ -87,10 +114,7 @@ export function validateRequired(
87
114
  /**
88
115
  * Validates site parameter for multi-site configurations
89
116
  */
90
- export function validateSite(
91
- site: string | undefined,
92
- availableSites: string[],
93
- ): string {
117
+ export function validateSite(site: string | undefined, availableSites: string[]): string {
94
118
  if (!site) {
95
119
  if (availableSites.length === 1) {
96
120
  return availableSites[0];
@@ -101,9 +125,7 @@ export function validateSite(
101
125
  }
102
126
 
103
127
  if (!availableSites.includes(site)) {
104
- throw new Error(
105
- `Site '${site}' not found. Available sites: ${availableSites.join(", ")}`,
106
- );
128
+ throw new Error(`Site '${site}' not found. Available sites: ${availableSites.join(", ")}`);
107
129
  }
108
130
 
109
131
  return site;
@@ -0,0 +1,351 @@
1
+ /**
2
+ * Centralized Logging System
3
+ *
4
+ * Replaces scattered console.log usage with structured, configurable logging.
5
+ * Integrates with the centralized Config system for environment-aware behavior.
6
+ */
7
+
8
+ import { ConfigHelpers } from "../config/Config.js";
9
+
10
+ export type LogLevel = "trace" | "debug" | "info" | "warn" | "error" | "fatal";
11
+ export type LogContext = Record<string, unknown>;
12
+
13
+ export interface LogEntry {
14
+ readonly timestamp: string;
15
+ readonly level: LogLevel;
16
+ readonly message: string;
17
+ readonly context?: LogContext | undefined;
18
+ readonly component?: string | undefined;
19
+ readonly requestId?: string | undefined;
20
+ readonly siteId?: string | undefined;
21
+ readonly userId?: string | undefined;
22
+ }
23
+
24
+ export interface LoggerOptions {
25
+ readonly component?: string | undefined;
26
+ readonly context?: LogContext | undefined;
27
+ readonly siteId?: string | undefined;
28
+ readonly requestId?: string | undefined;
29
+ }
30
+
31
+ /**
32
+ * Log level priorities (higher number = more important)
33
+ */
34
+ const LOG_LEVELS: Record<LogLevel, number> = {
35
+ trace: 0,
36
+ debug: 1,
37
+ info: 2,
38
+ warn: 3,
39
+ error: 4,
40
+ fatal: 5,
41
+ };
42
+
43
+ /**
44
+ * Get minimum log level from configuration
45
+ */
46
+ function getMinLogLevel(): LogLevel {
47
+ const configInstance = ConfigHelpers.get();
48
+ const appConfig = configInstance.get();
49
+ const configLevel = appConfig.debug.logLevel.toLowerCase();
50
+ return LOG_LEVELS[configLevel as LogLevel] !== undefined ? (configLevel as LogLevel) : "info";
51
+ }
52
+
53
+ /**
54
+ * Check if log level should be output
55
+ */
56
+ function shouldLog(level: LogLevel): boolean {
57
+ const minLevel = getMinLogLevel();
58
+ return LOG_LEVELS[level] >= LOG_LEVELS[minLevel];
59
+ }
60
+
61
+ /**
62
+ * Format log entry for output
63
+ */
64
+ function formatLogEntry(entry: LogEntry): string {
65
+ const timestamp = entry.timestamp;
66
+ const level = entry.level.toUpperCase().padEnd(5);
67
+ const component = entry.component ? `[${entry.component}]` : "";
68
+ const siteId = entry.siteId ? `{site:${entry.siteId}}` : "";
69
+ const requestId = entry.requestId ? `{req:${entry.requestId.slice(0, 8)}}` : "";
70
+
71
+ let message = `${timestamp} ${level} ${component}${siteId}${requestId} ${entry.message}`;
72
+
73
+ if (entry.context && Object.keys(entry.context).length > 0) {
74
+ const contextStr = JSON.stringify(entry.context);
75
+ message += ` ${contextStr}`;
76
+ }
77
+
78
+ return message;
79
+ }
80
+
81
+ /**
82
+ * Sanitize sensitive data from log context
83
+ */
84
+ function sanitizeContext(context: LogContext): LogContext {
85
+ const sanitized: LogContext = {};
86
+
87
+ for (const [key, value] of Object.entries(context)) {
88
+ const keyLower = key.toLowerCase();
89
+ const isSensitive =
90
+ keyLower.includes("password") ||
91
+ keyLower.includes("secret") ||
92
+ keyLower.includes("token") ||
93
+ keyLower.includes("key") ||
94
+ keyLower.includes("credential");
95
+
96
+ if (isSensitive) {
97
+ if (typeof value === "string") {
98
+ sanitized[key] = value.length > 0 ? `[REDACTED:${value.length}chars]` : "[EMPTY]";
99
+ } else if (Array.isArray(value)) {
100
+ sanitized[key] = "[EMPTY]"; // Redact entire array for sensitive fields
101
+ } else {
102
+ sanitized[key] = value; // Keep non-string, non-array values as-is
103
+ }
104
+ } else {
105
+ sanitized[key] = value;
106
+ }
107
+ }
108
+
109
+ return sanitized;
110
+ }
111
+
112
+ /**
113
+ * Output log entry to appropriate destination
114
+ */
115
+ function outputLog(entry: LogEntry): void {
116
+ // In test environment, only log errors and fatals
117
+ if (ConfigHelpers.isTest() && LOG_LEVELS[entry.level] < LOG_LEVELS.error) {
118
+ return;
119
+ }
120
+
121
+ // In DXT mode, suppress most logging
122
+ if (ConfigHelpers.isDXT() && LOG_LEVELS[entry.level] < LOG_LEVELS.warn) {
123
+ return;
124
+ }
125
+
126
+ const formatted = formatLogEntry(entry);
127
+
128
+ // Use stderr for all log output to avoid STDIO interference
129
+ console.error(formatted);
130
+ }
131
+
132
+ /**
133
+ * Main Logger Class
134
+ */
135
+ export class Logger {
136
+ private readonly options: LoggerOptions;
137
+
138
+ constructor(options: LoggerOptions = {}) {
139
+ this.options = options;
140
+ }
141
+
142
+ /**
143
+ * Create child logger with additional context
144
+ */
145
+ child(additionalOptions: LoggerOptions): Logger {
146
+ return new Logger({
147
+ component: additionalOptions.component ?? this.options.component,
148
+ siteId: additionalOptions.siteId ?? this.options.siteId,
149
+ requestId: additionalOptions.requestId ?? this.options.requestId,
150
+ context: {
151
+ ...this.options.context,
152
+ ...additionalOptions.context,
153
+ },
154
+ });
155
+ }
156
+
157
+ /**
158
+ * Log at specific level
159
+ */
160
+ log(level: LogLevel, message: string, context?: LogContext): void {
161
+ if (!shouldLog(level)) {
162
+ return;
163
+ }
164
+
165
+ const mergedContext = context ? sanitizeContext({ ...this.options.context, ...context }) : this.options.context;
166
+
167
+ const entry: LogEntry = {
168
+ timestamp: new Date().toISOString(),
169
+ level,
170
+ message,
171
+ ...(mergedContext && Object.keys(mergedContext).length > 0 && { context: mergedContext }),
172
+ ...(this.options.component && { component: this.options.component }),
173
+ ...(this.options.requestId && { requestId: this.options.requestId }),
174
+ ...(this.options.siteId && { siteId: this.options.siteId }),
175
+ };
176
+
177
+ outputLog(entry);
178
+ }
179
+
180
+ // Convenience methods
181
+ trace(message: string, context?: LogContext): void {
182
+ this.log("trace", message, context);
183
+ }
184
+
185
+ debug(message: string, context?: LogContext): void {
186
+ this.log("debug", message, context);
187
+ }
188
+
189
+ info(message: string, context?: LogContext): void {
190
+ this.log("info", message, context);
191
+ }
192
+
193
+ warn(message: string, context?: LogContext): void {
194
+ this.log("warn", message, context);
195
+ }
196
+
197
+ error(message: string, context?: LogContext): void;
198
+ error(error: Error, context?: LogContext): void;
199
+ error(messageOrError: string | Error, context?: LogContext): void {
200
+ if (messageOrError instanceof Error) {
201
+ this.log("error", messageOrError.message, {
202
+ ...context,
203
+ errorName: messageOrError.name,
204
+ errorStack: messageOrError.stack,
205
+ });
206
+ } else {
207
+ this.log("error", messageOrError, context);
208
+ }
209
+ }
210
+
211
+ fatal(message: string, context?: LogContext): void;
212
+ fatal(error: Error, context?: LogContext): void;
213
+ fatal(messageOrError: string | Error, context?: LogContext): void {
214
+ if (messageOrError instanceof Error) {
215
+ this.log("fatal", messageOrError.message, {
216
+ ...context,
217
+ errorName: messageOrError.name,
218
+ errorStack: messageOrError.stack,
219
+ });
220
+ } else {
221
+ this.log("fatal", messageOrError, context);
222
+ }
223
+ }
224
+
225
+ /**
226
+ * Time a function execution
227
+ */
228
+ time<T>(message: string, fn: () => T): T;
229
+ time<T>(message: string, fn: () => Promise<T>): Promise<T>;
230
+ time<T>(message: string, fn: () => T | Promise<T>): T | Promise<T> {
231
+ const start = Date.now();
232
+ this.debug(`Starting: ${message}`);
233
+
234
+ try {
235
+ const result = fn();
236
+
237
+ // Check if result is a Promise by checking for then method
238
+ if (result && typeof result === "object" && "then" in result) {
239
+ // Cast to Promise<T> since we know it has a then method
240
+ const promiseResult = result as Promise<T>;
241
+ return promiseResult
242
+ .then((value: T) => {
243
+ const duration = Date.now() - start;
244
+ this.debug(`Completed: ${message}`, { duration: `${duration}ms` });
245
+ return value;
246
+ })
247
+ .catch((error: unknown) => {
248
+ const duration = Date.now() - start;
249
+ this.error(`Failed: ${message}`, {
250
+ duration: `${duration}ms`,
251
+ error: error instanceof Error ? error.message : String(error),
252
+ });
253
+ throw error;
254
+ });
255
+ } else {
256
+ const duration = Date.now() - start;
257
+ this.debug(`Completed: ${message}`, { duration: `${duration}ms` });
258
+ return result;
259
+ }
260
+ } catch (error) {
261
+ const duration = Date.now() - start;
262
+ this.error(`Failed: ${message}`, {
263
+ duration: `${duration}ms`,
264
+ error: error instanceof Error ? error.message : String(error),
265
+ });
266
+ throw error;
267
+ }
268
+ }
269
+ }
270
+
271
+ /**
272
+ * Default logger instance
273
+ */
274
+ export const logger = new Logger();
275
+
276
+ /**
277
+ * Create component-specific logger
278
+ */
279
+ export function createLogger(component: string, options: Omit<LoggerOptions, "component"> = {}): Logger {
280
+ return new Logger({ ...options, component });
281
+ }
282
+
283
+ /**
284
+ * Create site-specific logger
285
+ */
286
+ export function createSiteLogger(siteId: string, component?: string | undefined): Logger {
287
+ return new Logger({
288
+ siteId,
289
+ ...(component && { component }),
290
+ });
291
+ }
292
+
293
+ /**
294
+ * Create request-specific logger
295
+ */
296
+ export function createRequestLogger(
297
+ requestId: string,
298
+ component?: string | undefined,
299
+ siteId?: string | undefined,
300
+ ): Logger {
301
+ return new Logger({
302
+ requestId,
303
+ ...(component && { component }),
304
+ ...(siteId && { siteId }),
305
+ });
306
+ }
307
+
308
+ /**
309
+ * Logger factory for common scenarios
310
+ */
311
+ export const LoggerFactory = {
312
+ /**
313
+ * Create logger for WordPress API operations
314
+ */
315
+ api: (siteId?: string | undefined) => createLogger("API", siteId ? { siteId } : {}),
316
+
317
+ /**
318
+ * Create logger for cache operations
319
+ */
320
+ cache: (siteId?: string | undefined) => createLogger("CACHE", siteId ? { siteId } : {}),
321
+
322
+ /**
323
+ * Create logger for tool operations
324
+ */
325
+ tool: (toolName: string, siteId?: string | undefined) => createLogger(`TOOL:${toolName}`, siteId ? { siteId } : {}),
326
+
327
+ /**
328
+ * Create logger for authentication
329
+ */
330
+ auth: (siteId?: string | undefined) => createLogger("AUTH", siteId ? { siteId } : {}),
331
+
332
+ /**
333
+ * Create logger for configuration
334
+ */
335
+ config: () => createLogger("CONFIG"),
336
+
337
+ /**
338
+ * Create logger for security operations
339
+ */
340
+ security: () => createLogger("SECURITY"),
341
+
342
+ /**
343
+ * Create logger for performance monitoring
344
+ */
345
+ performance: () => createLogger("PERF"),
346
+
347
+ /**
348
+ * Create logger for server operations
349
+ */
350
+ server: () => createLogger("SERVER"),
351
+ };