mcp-wordpress 1.5.2 → 2.0.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 (190) hide show
  1. package/README.md +332 -61
  2. package/dist/cache/CacheInvalidation.d.ts.map +1 -1
  3. package/dist/cache/CacheInvalidation.js +4 -4
  4. package/dist/cache/CacheInvalidation.js.map +1 -1
  5. package/dist/client/MockWordPressClient.d.ts +55 -0
  6. package/dist/client/MockWordPressClient.d.ts.map +1 -0
  7. package/dist/client/MockWordPressClient.js +369 -0
  8. package/dist/client/MockWordPressClient.js.map +1 -0
  9. package/dist/client/api.d.ts +1 -0
  10. package/dist/client/api.d.ts.map +1 -1
  11. package/dist/client/api.js +26 -60
  12. package/dist/client/api.js.map +1 -1
  13. package/dist/client/managers/AuthenticationManager.d.ts.map +1 -1
  14. package/dist/client/managers/AuthenticationManager.js +4 -3
  15. package/dist/client/managers/AuthenticationManager.js.map +1 -1
  16. package/dist/config/ConfigurationSchema.d.ts +3 -3
  17. package/dist/config/ConfigurationSchema.d.ts.map +1 -1
  18. package/dist/config/ConfigurationSchema.js +7 -24
  19. package/dist/config/ConfigurationSchema.js.map +1 -1
  20. package/dist/config/ServerConfiguration.d.ts +8 -0
  21. package/dist/config/ServerConfiguration.d.ts.map +1 -1
  22. package/dist/config/ServerConfiguration.js +80 -31
  23. package/dist/config/ServerConfiguration.js.map +1 -1
  24. package/dist/docs/DocumentationGenerator.d.ts.map +1 -1
  25. package/dist/docs/DocumentationGenerator.js +5 -7
  26. package/dist/docs/DocumentationGenerator.js.map +1 -1
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +33 -29
  29. package/dist/index.js.map +1 -1
  30. package/dist/security/InputValidator.d.ts.map +1 -1
  31. package/dist/security/InputValidator.js +3 -11
  32. package/dist/security/InputValidator.js.map +1 -1
  33. package/dist/server/ToolRegistry.d.ts +4 -0
  34. package/dist/server/ToolRegistry.d.ts.map +1 -1
  35. package/dist/server/ToolRegistry.js +71 -8
  36. package/dist/server/ToolRegistry.js.map +1 -1
  37. package/dist/tools/auth.d.ts.map +1 -1
  38. package/dist/tools/auth.js +8 -3
  39. package/dist/tools/auth.js.map +1 -1
  40. package/dist/tools/posts.d.ts.map +1 -1
  41. package/dist/tools/posts.js +287 -20
  42. package/dist/tools/posts.js.map +1 -1
  43. package/dist/tools/site.d.ts.map +1 -1
  44. package/dist/tools/site.js +47 -9
  45. package/dist/tools/site.js.map +1 -1
  46. package/dist/tools/users.d.ts.map +1 -1
  47. package/dist/tools/users.js +113 -10
  48. package/dist/tools/users.js.map +1 -1
  49. package/dist/utils/enhancedError.d.ts +61 -0
  50. package/dist/utils/enhancedError.d.ts.map +1 -0
  51. package/dist/utils/enhancedError.js +221 -0
  52. package/dist/utils/enhancedError.js.map +1 -0
  53. package/dist/utils/streaming.d.ts +104 -0
  54. package/dist/utils/streaming.d.ts.map +1 -0
  55. package/dist/utils/streaming.js +312 -0
  56. package/dist/utils/streaming.js.map +1 -0
  57. package/dist/utils/validation.d.ts +19 -3
  58. package/dist/utils/validation.d.ts.map +1 -1
  59. package/dist/utils/validation.js +174 -24
  60. package/dist/utils/validation.js.map +1 -1
  61. package/docs/ARCHITECTURE.md +850 -0
  62. package/docs/CACHING.md +20 -17
  63. package/docs/CONFIGURATION.md +660 -0
  64. package/docs/DOCKER.md +61 -60
  65. package/docs/EVALUATION.md +397 -0
  66. package/docs/INSTALLATION.md +423 -0
  67. package/docs/PERFORMANCE_MONITORING.md +17 -15
  68. package/docs/SECURITY.md +621 -0
  69. package/docs/SECURITY_TESTING.md +22 -26
  70. package/docs/TEST_SITE_SETUP.md +136 -0
  71. package/docs/TROUBLESHOOTING.md +578 -0
  72. package/docs/api/README.md +76 -91
  73. package/docs/api/categories/auth.md +0 -2
  74. package/docs/api/categories/cache.md +0 -2
  75. package/docs/api/categories/comment.md +0 -2
  76. package/docs/api/categories/media.md +0 -2
  77. package/docs/api/categories/page.md +0 -2
  78. package/docs/api/categories/performance.md +0 -2
  79. package/docs/api/categories/post.md +0 -2
  80. package/docs/api/categories/site.md +0 -2
  81. package/docs/api/categories/taxonomy.md +0 -2
  82. package/docs/api/categories/user.md +0 -2
  83. package/docs/api/summary.json +1 -1
  84. package/docs/api/tools/wp_approve_comment.md +11 -3
  85. package/docs/api/tools/wp_cache_clear.md +14 -5
  86. package/docs/api/tools/wp_cache_info.md +14 -5
  87. package/docs/api/tools/wp_cache_stats.md +14 -5
  88. package/docs/api/tools/wp_cache_warm.md +14 -5
  89. package/docs/api/tools/wp_create_application_password.md +11 -3
  90. package/docs/api/tools/wp_create_category.md +11 -3
  91. package/docs/api/tools/wp_create_comment.md +14 -5
  92. package/docs/api/tools/wp_create_page.md +13 -5
  93. package/docs/api/tools/wp_create_post.md +14 -7
  94. package/docs/api/tools/wp_create_tag.md +11 -3
  95. package/docs/api/tools/wp_create_user.md +13 -5
  96. package/docs/api/tools/wp_delete_application_password.md +11 -3
  97. package/docs/api/tools/wp_delete_category.md +11 -3
  98. package/docs/api/tools/wp_delete_comment.md +11 -3
  99. package/docs/api/tools/wp_delete_media.md +10 -3
  100. package/docs/api/tools/wp_delete_page.md +10 -3
  101. package/docs/api/tools/wp_delete_post.md +11 -5
  102. package/docs/api/tools/wp_delete_tag.md +11 -3
  103. package/docs/api/tools/wp_delete_user.md +10 -3
  104. package/docs/api/tools/wp_get_application_passwords.md +11 -3
  105. package/docs/api/tools/wp_get_auth_status.md +11 -3
  106. package/docs/api/tools/wp_get_category.md +11 -3
  107. package/docs/api/tools/wp_get_comment.md +11 -3
  108. package/docs/api/tools/wp_get_current_user.md +11 -3
  109. package/docs/api/tools/wp_get_media.md +11 -3
  110. package/docs/api/tools/wp_get_page.md +11 -3
  111. package/docs/api/tools/wp_get_page_revisions.md +11 -3
  112. package/docs/api/tools/wp_get_post.md +12 -5
  113. package/docs/api/tools/wp_get_post_revisions.md +11 -3
  114. package/docs/api/tools/wp_get_site_settings.md +10 -3
  115. package/docs/api/tools/wp_get_tag.md +11 -3
  116. package/docs/api/tools/wp_get_user.md +11 -3
  117. package/docs/api/tools/wp_list_categories.md +11 -3
  118. package/docs/api/tools/wp_list_comments.md +11 -3
  119. package/docs/api/tools/wp_list_media.md +14 -5
  120. package/docs/api/tools/wp_list_pages.md +14 -5
  121. package/docs/api/tools/wp_list_posts.md +15 -7
  122. package/docs/api/tools/wp_list_tags.md +11 -3
  123. package/docs/api/tools/wp_list_users.md +11 -3
  124. package/docs/api/tools/wp_performance_alerts.md +17 -7
  125. package/docs/api/tools/wp_performance_benchmark.md +17 -7
  126. package/docs/api/tools/wp_performance_export.md +17 -7
  127. package/docs/api/tools/wp_performance_history.md +17 -7
  128. package/docs/api/tools/wp_performance_optimize.md +17 -7
  129. package/docs/api/tools/wp_performance_stats.md +17 -7
  130. package/docs/api/tools/wp_search_site.md +11 -3
  131. package/docs/api/tools/wp_spam_comment.md +11 -3
  132. package/docs/api/tools/wp_switch_auth_method.md +14 -5
  133. package/docs/api/tools/wp_test_auth.md +11 -3
  134. package/docs/api/tools/wp_update_category.md +11 -3
  135. package/docs/api/tools/wp_update_comment.md +14 -5
  136. package/docs/api/tools/wp_update_media.md +14 -5
  137. package/docs/api/tools/wp_update_page.md +13 -5
  138. package/docs/api/tools/wp_update_post.md +14 -7
  139. package/docs/api/tools/wp_update_site_settings.md +14 -5
  140. package/docs/api/tools/wp_update_tag.md +11 -3
  141. package/docs/api/tools/wp_update_user.md +13 -5
  142. package/docs/api/tools/wp_upload_media.md +13 -5
  143. package/docs/api/types/WordPressPost.md +2 -0
  144. package/docs/code-improvements.md +40 -0
  145. package/docs/contract-testing.md +1 -1
  146. package/docs/developer/API_REFERENCE.md +19 -59
  147. package/docs/developer/ARCHITECTURE.md +8 -11
  148. package/docs/developer/BUILD_SYSTEM.md +2 -2
  149. package/docs/developer/CONTRIBUTING.md +3 -5
  150. package/docs/developer/GITHUB_ACTIONS_SETUP.md +2 -2
  151. package/docs/developer/MIGRATION_GUIDE.md +5 -6
  152. package/docs/developer/README.md +2 -1
  153. package/docs/developer/REFACTORING.md +9 -15
  154. package/docs/developer/RELEASE_PROCESS.md +4 -3
  155. package/docs/developer/TESTING.md +2 -2
  156. package/docs/examples/claude-desktop-config.md +8 -0
  157. package/docs/integrations/claude-desktop.md +426 -0
  158. package/docs/integrations/cline.md +537 -0
  159. package/docs/integrations/vs-code.md +515 -0
  160. package/docs/releases/COMMUNITY_ANNOUNCEMENT_v1.1.2.md +30 -23
  161. package/docs/releases/RELEASE_NOTES_v1.1.2.md +7 -6
  162. package/docs/testing-configurations.md +11 -0
  163. package/docs/user-guides/DOCKER_NPM_DTX_SETUP.md +3 -2
  164. package/docs/user-guides/DOCKER_SETUP.md +3 -2
  165. package/docs/user-guides/DTX_SETUP.md +6 -5
  166. package/docs/user-guides/DXT_INSTALLATION.md +4 -4
  167. package/docs/user-guides/NPM_SETUP.md +4 -2
  168. package/docs/user-guides/NPX_SETUP.md +4 -2
  169. package/docs/user-guides/SMITHERY_SETUP.md +402 -0
  170. package/docs/wordpress-rest-api-authentication-troubleshooting.md +45 -42
  171. package/package.json +12 -2
  172. package/src/cache/CacheInvalidation.ts +7 -18
  173. package/src/client/MockWordPressClient.ts +398 -0
  174. package/src/client/api.ts +77 -237
  175. package/src/client/managers/AuthenticationManager.ts +19 -56
  176. package/src/config/ConfigurationSchema.ts +14 -45
  177. package/src/config/ServerConfiguration.ts +98 -71
  178. package/src/docs/DocumentationGenerator.ts +39 -123
  179. package/src/dxt-entry.cjs +4 -1
  180. package/src/index.ts +35 -54
  181. package/src/security/InputValidator.ts +15 -57
  182. package/src/server/ToolRegistry.ts +88 -17
  183. package/src/tools/auth.ts +15 -22
  184. package/src/tools/posts.ts +347 -64
  185. package/src/tools/site.ts +69 -46
  186. package/src/tools/users.ts +142 -44
  187. package/src/utils/enhancedError.ts +248 -0
  188. package/src/utils/streaming.ts +428 -0
  189. package/src/utils/validation.ts +253 -92
  190. package/dist/mcp-wordpress-1.5.2.tgz +0 -0
@@ -0,0 +1,248 @@
1
+ import { getErrorMessage } from "./error.js";
2
+
3
+ export interface ErrorContext {
4
+ operation: string;
5
+ parameters?: Record<string, any>;
6
+ suggestions?: string[];
7
+ troubleshooting?: string[];
8
+ }
9
+
10
+ /**
11
+ * Enhanced error handling with helpful messages and suggestions
12
+ */
13
+ export class EnhancedError extends Error {
14
+ public suggestions: string[];
15
+ public troubleshooting: string[];
16
+ public context: ErrorContext;
17
+
18
+ constructor(message: string, context: ErrorContext) {
19
+ super(message);
20
+ this.name = "EnhancedError";
21
+ this.context = context;
22
+ this.suggestions = context.suggestions || [];
23
+ this.troubleshooting = context.troubleshooting || [];
24
+ }
25
+
26
+ /**
27
+ * Format error message with suggestions
28
+ */
29
+ toString(): string {
30
+ let message = `❌ ${this.message}`;
31
+
32
+ if (this.suggestions.length > 0) {
33
+ message += `\n\n💡 **Suggestions:**\n`;
34
+ this.suggestions.forEach((suggestion) => {
35
+ message += `• ${suggestion}\n`;
36
+ });
37
+ }
38
+
39
+ if (this.troubleshooting.length > 0) {
40
+ message += `\n🔧 **Troubleshooting:**\n`;
41
+ this.troubleshooting.forEach((step) => {
42
+ message += `• ${step}\n`;
43
+ });
44
+ }
45
+
46
+ return message;
47
+ }
48
+ }
49
+
50
+ /**
51
+ * Common error handlers with context-aware suggestions
52
+ */
53
+ export class ErrorHandlers {
54
+ /**
55
+ * Handle post not found errors
56
+ */
57
+ static postNotFound(postId: number, originalError: any): EnhancedError {
58
+ const errorMessage = getErrorMessage(originalError);
59
+
60
+ return new EnhancedError(`Post with ID ${postId} not found`, {
61
+ operation: "get_post",
62
+ parameters: { postId },
63
+ suggestions: [
64
+ "Check if the post ID is correct",
65
+ "Verify the post exists and is published",
66
+ "Try listing posts first with wp_list_posts to see available posts",
67
+ "Check if you have permission to view this post",
68
+ ],
69
+ troubleshooting: [
70
+ "Use wp_list_posts to see all available posts",
71
+ "Check the post status - it might be draft, private, or trashed",
72
+ "Verify your user permissions for viewing posts",
73
+ `Original error: ${errorMessage}`,
74
+ ],
75
+ });
76
+ }
77
+
78
+ /**
79
+ * Handle authentication errors
80
+ */
81
+ static authenticationFailed(originalError: any): EnhancedError {
82
+ const errorMessage = getErrorMessage(originalError);
83
+
84
+ return new EnhancedError("Authentication failed", {
85
+ operation: "authenticate",
86
+ suggestions: [
87
+ "Check your WordPress credentials (username and app password)",
88
+ "Verify the WordPress site URL is correct",
89
+ "Ensure application passwords are enabled on your WordPress site",
90
+ "Try regenerating your application password",
91
+ ],
92
+ troubleshooting: [
93
+ "Go to your WordPress admin → Users → Profile → Application Passwords",
94
+ "Create a new application password with a descriptive name",
95
+ "Make sure the WordPress site URL ends with wp-json/wp/v2",
96
+ "Check if your site has any security plugins blocking REST API",
97
+ `Original error: ${errorMessage}`,
98
+ ],
99
+ });
100
+ }
101
+
102
+ /**
103
+ * Handle site parameter missing errors
104
+ */
105
+ static siteParameterMissing(availableSites: string[]): EnhancedError {
106
+ return new EnhancedError("Multiple sites configured - site parameter required", {
107
+ operation: "site_selection",
108
+ suggestions: [
109
+ "Add --site parameter to specify which site to use",
110
+ `Available sites: ${availableSites.join(", ")}`,
111
+ 'Example: wp_list_posts --site="your-site-id"',
112
+ ],
113
+ troubleshooting: [
114
+ "Check your mcp-wordpress.config.json for configured sites",
115
+ "Use the site ID from your configuration file",
116
+ "Site IDs are defined in the 'id' field of each site config",
117
+ ],
118
+ });
119
+ }
120
+
121
+ /**
122
+ * Handle site not found errors
123
+ */
124
+ static siteNotFound(siteId: string, availableSites: string[]): EnhancedError {
125
+ return new EnhancedError(`Site with ID '${siteId}' not found`, {
126
+ operation: "site_selection",
127
+ parameters: { siteId },
128
+ suggestions: [
129
+ `Available sites: ${availableSites.join(", ")}`,
130
+ "Check the spelling of the site ID",
131
+ "Verify the site is configured in mcp-wordpress.config.json",
132
+ ],
133
+ troubleshooting: [
134
+ "Open mcp-wordpress.config.json to see configured sites",
135
+ "Check the 'id' field for each site configuration",
136
+ "Ensure the site ID matches exactly (case-sensitive)",
137
+ ],
138
+ });
139
+ }
140
+
141
+ /**
142
+ * Handle permission errors
143
+ */
144
+ static permissionDenied(operation: string, originalError: any): EnhancedError {
145
+ const errorMessage = getErrorMessage(originalError);
146
+
147
+ return new EnhancedError(`Permission denied for ${operation}`, {
148
+ operation,
149
+ suggestions: [
150
+ "Check if your user has the required permissions",
151
+ "Verify you're logged in with the correct user account",
152
+ "Contact your WordPress administrator for permission changes",
153
+ ],
154
+ troubleshooting: [
155
+ "Check your user role in WordPress admin → Users",
156
+ "Verify the required capabilities for this operation",
157
+ "Try using an administrator account",
158
+ `Original error: ${errorMessage}`,
159
+ ],
160
+ });
161
+ }
162
+
163
+ /**
164
+ * Handle network/connection errors
165
+ */
166
+ static connectionError(originalError: any): EnhancedError {
167
+ const errorMessage = getErrorMessage(originalError);
168
+
169
+ return new EnhancedError("Failed to connect to WordPress site", {
170
+ operation: "connection",
171
+ suggestions: [
172
+ "Check your internet connection",
173
+ "Verify the WordPress site URL is correct and accessible",
174
+ "Check if the site is temporarily down",
175
+ ],
176
+ troubleshooting: [
177
+ "Try accessing the site in your browser",
178
+ "Check if the REST API is enabled: visit yoursite.com/wp-json/",
179
+ "Verify there are no firewall or security plugins blocking access",
180
+ `Original error: ${errorMessage}`,
181
+ ],
182
+ });
183
+ }
184
+
185
+ /**
186
+ * Handle validation errors
187
+ */
188
+ static validationError(field: string, value: any, expectedType: string): EnhancedError {
189
+ return new EnhancedError(`Invalid ${field}: expected ${expectedType}, got ${typeof value}`, {
190
+ operation: "validation",
191
+ parameters: { field, value, expectedType },
192
+ suggestions: [
193
+ `Provide a valid ${expectedType} for ${field}`,
194
+ "Check the parameter format and type",
195
+ "Refer to the tool documentation for correct parameter format",
196
+ ],
197
+ troubleshooting: [
198
+ "Review the tool's parameter requirements",
199
+ "Check for typos in parameter names",
200
+ "Ensure parameter values match the expected type",
201
+ ],
202
+ });
203
+ }
204
+
205
+ /**
206
+ * Handle rate limiting errors
207
+ */
208
+ static rateLimitExceeded(originalError: any): EnhancedError {
209
+ const errorMessage = getErrorMessage(originalError);
210
+
211
+ return new EnhancedError("Rate limit exceeded", {
212
+ operation: "rate_limit",
213
+ suggestions: [
214
+ "Wait a few minutes before trying again",
215
+ "Reduce the frequency of requests",
216
+ "Use pagination for large operations",
217
+ ],
218
+ troubleshooting: [
219
+ "Check your WordPress hosting plan limits",
220
+ "Consider upgrading your hosting plan for higher limits",
221
+ "Implement delays between requests",
222
+ `Original error: ${errorMessage}`,
223
+ ],
224
+ });
225
+ }
226
+
227
+ /**
228
+ * Generic error handler with basic suggestions
229
+ */
230
+ static generic(operation: string, originalError: any): EnhancedError {
231
+ const errorMessage = getErrorMessage(originalError);
232
+
233
+ return new EnhancedError(`Failed to ${operation}`, {
234
+ operation,
235
+ suggestions: [
236
+ "Check your WordPress credentials and permissions",
237
+ "Verify the site is accessible and functioning",
238
+ "Try the operation again after a brief wait",
239
+ ],
240
+ troubleshooting: [
241
+ "Test your connection with a simpler operation first",
242
+ "Check WordPress admin for any issues",
243
+ "Review the WordPress error logs",
244
+ `Original error: ${errorMessage}`,
245
+ ],
246
+ });
247
+ }
248
+ }
@@ -0,0 +1,428 @@
1
+ /**
2
+ * Streaming utilities for handling large result sets
3
+ * Implements streaming responses for better performance and memory usage
4
+ */
5
+
6
+ // Node.js streaming imports removed - not currently used but available for future enhancement
7
+
8
+ export interface StreamingOptions {
9
+ batchSize?: number;
10
+ delay?: number;
11
+ transformItem?: (item: any) => any;
12
+ filterItem?: (item: any) => boolean;
13
+ }
14
+
15
+ export interface StreamingResult<T> {
16
+ data: T[];
17
+ hasMore: boolean;
18
+ cursor?: string | undefined;
19
+ total?: number | undefined;
20
+ processed: number;
21
+ }
22
+
23
+ /**
24
+ * Creates a streaming response for large datasets
25
+ */
26
+ export class DataStreamer<T> {
27
+ private batchSize: number;
28
+ private delay: number;
29
+ private transformItem: ((item: T) => any) | undefined;
30
+ private filterItem: ((item: T) => boolean) | undefined;
31
+
32
+ constructor(options: StreamingOptions = {}) {
33
+ this.batchSize = options.batchSize || 50;
34
+ this.delay = options.delay || 0;
35
+ this.transformItem = options.transformItem || undefined;
36
+ this.filterItem = options.filterItem || undefined;
37
+ }
38
+
39
+ /**
40
+ * Processes data in batches with streaming
41
+ */
42
+ async *streamBatches<U>(
43
+ data: T[],
44
+ processor: (batch: T[]) => Promise<U[]>,
45
+ ): AsyncGenerator<StreamingResult<U>, void, unknown> {
46
+ const total = data.length;
47
+ let processed = 0;
48
+
49
+ for (let i = 0; i < data.length; i += this.batchSize) {
50
+ const batch = data.slice(i, i + this.batchSize);
51
+
52
+ // Apply filtering if provided
53
+ const filteredBatch = this.filterItem ? batch.filter(this.filterItem) : batch;
54
+
55
+ // Process the batch
56
+ const processedBatch = await processor(filteredBatch);
57
+
58
+ // Apply transformation if provided
59
+ const transformedBatch = this.transformItem
60
+ ? processedBatch.map((item: any) => this.transformItem!(item))
61
+ : processedBatch;
62
+
63
+ processed += batch.length;
64
+ const hasMore = processed < total;
65
+
66
+ yield {
67
+ data: transformedBatch,
68
+ hasMore,
69
+ cursor: hasMore ? String(i + this.batchSize) : undefined,
70
+ total,
71
+ processed,
72
+ };
73
+
74
+ // Add delay between batches if specified
75
+ if (this.delay > 0 && hasMore) {
76
+ await new Promise((resolve) => setTimeout(resolve, this.delay));
77
+ }
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Processes large datasets with pagination
83
+ */
84
+ async *streamPages<U>(
85
+ fetcher: (page: number, perPage: number) => Promise<{ data: T[]; hasMore: boolean }>,
86
+ processor: (items: T[]) => Promise<U[]>,
87
+ ): AsyncGenerator<StreamingResult<U>, void, unknown> {
88
+ let page = 1;
89
+ let totalProcessed = 0;
90
+
91
+ while (true) {
92
+ const result = await fetcher(page, this.batchSize);
93
+
94
+ if (result.data.length === 0) {
95
+ break;
96
+ }
97
+
98
+ // Apply filtering if provided
99
+ const filteredData = this.filterItem ? result.data.filter(this.filterItem) : result.data;
100
+
101
+ // Process the data
102
+ const processedData = await processor(filteredData);
103
+
104
+ // Apply transformation if provided
105
+ const transformedData = this.transformItem
106
+ ? processedData.map((item: any) => this.transformItem!(item))
107
+ : processedData;
108
+
109
+ totalProcessed += result.data.length;
110
+
111
+ yield {
112
+ data: transformedData,
113
+ hasMore: result.hasMore,
114
+ cursor: result.hasMore ? String(page + 1) : undefined,
115
+ total: undefined, // Unknown for paginated results
116
+ processed: totalProcessed,
117
+ };
118
+
119
+ if (!result.hasMore) {
120
+ break;
121
+ }
122
+
123
+ page++;
124
+
125
+ // Add delay between pages if specified
126
+ if (this.delay > 0) {
127
+ await new Promise((resolve) => setTimeout(resolve, this.delay));
128
+ }
129
+ }
130
+ }
131
+ }
132
+
133
+ /**
134
+ * Streaming formatter for WordPress data
135
+ */
136
+ export class WordPressDataStreamer {
137
+ /**
138
+ * Streams WordPress posts with author and taxonomy information
139
+ */
140
+ static async *streamPosts(
141
+ posts: any[],
142
+ options: {
143
+ includeAuthor?: boolean;
144
+ includeCategories?: boolean;
145
+ includeTags?: boolean;
146
+ batchSize?: number;
147
+ } = {},
148
+ ): AsyncGenerator<StreamingResult<any>, void, unknown> {
149
+ const streamer = new DataStreamer<any>({
150
+ batchSize: options.batchSize || 20,
151
+ transformItem: (post) => ({
152
+ id: post.id,
153
+ title: post.title?.rendered || "Untitled",
154
+ excerpt: post.excerpt?.rendered
155
+ ? post.excerpt.rendered.replace(/<[^>]*>/g, "").substring(0, 150) + "..."
156
+ : "No excerpt",
157
+ status: post.status,
158
+ date: new Date(post.date).toLocaleDateString(),
159
+ link: post.link,
160
+ author: options.includeAuthor ? post.author : undefined,
161
+ categories: options.includeCategories ? post.categories : undefined,
162
+ tags: options.includeTags ? post.tags : undefined,
163
+ }),
164
+ filterItem: (post) => post.status !== "trash", // Filter out trashed posts
165
+ });
166
+
167
+ const processor = async (batch: any[]) => {
168
+ // Simulate processing time for large datasets
169
+ await new Promise((resolve) => setTimeout(resolve, 10));
170
+ return batch;
171
+ };
172
+
173
+ for await (const result of streamer.streamBatches(posts, processor)) {
174
+ yield result;
175
+ }
176
+ }
177
+
178
+ /**
179
+ * Streams WordPress users with role information
180
+ */
181
+ static async *streamUsers(
182
+ users: any[],
183
+ options: {
184
+ includeRoles?: boolean;
185
+ includeCapabilities?: boolean;
186
+ batchSize?: number;
187
+ } = {},
188
+ ): AsyncGenerator<StreamingResult<any>, void, unknown> {
189
+ const streamer = new DataStreamer<any>({
190
+ batchSize: options.batchSize || 30,
191
+ transformItem: (user) => ({
192
+ id: user.id,
193
+ name: user.name || "No name",
194
+ username: user.slug || "unknown",
195
+ email: user.email || "No email",
196
+ roles: options.includeRoles ? user.roles : undefined,
197
+ capabilities: options.includeCapabilities ? Object.keys(user.capabilities || {}) : undefined,
198
+ registeredDate: user.registered_date ? new Date(user.registered_date).toLocaleDateString() : "Unknown",
199
+ }),
200
+ });
201
+
202
+ const processor = async (batch: any[]) => {
203
+ // Add user processing logic here if needed
204
+ return batch;
205
+ };
206
+
207
+ for await (const result of streamer.streamBatches(users, processor)) {
208
+ yield result;
209
+ }
210
+ }
211
+
212
+ /**
213
+ * Streams WordPress comments with moderation status
214
+ */
215
+ static async *streamComments(
216
+ comments: any[],
217
+ options: {
218
+ includeAuthor?: boolean;
219
+ includePost?: boolean;
220
+ batchSize?: number;
221
+ } = {},
222
+ ): AsyncGenerator<StreamingResult<any>, void, unknown> {
223
+ const streamer = new DataStreamer<any>({
224
+ batchSize: options.batchSize || 40,
225
+ transformItem: (comment) => ({
226
+ id: comment.id,
227
+ content: comment.content?.rendered
228
+ ? comment.content.rendered.replace(/<[^>]*>/g, "").substring(0, 200) + "..."
229
+ : "No content",
230
+ status: comment.status,
231
+ date: new Date(comment.date).toLocaleDateString(),
232
+ author: options.includeAuthor
233
+ ? {
234
+ name: comment.author_name,
235
+ email: comment.author_email,
236
+ url: comment.author_url,
237
+ }
238
+ : undefined,
239
+ post: options.includePost ? comment.post : undefined,
240
+ }),
241
+ filterItem: (comment) => comment.status !== "spam", // Filter out spam comments
242
+ });
243
+
244
+ const processor = async (batch: any[]) => {
245
+ // Add comment processing logic here if needed
246
+ return batch;
247
+ };
248
+
249
+ for await (const result of streamer.streamBatches(comments, processor)) {
250
+ yield result;
251
+ }
252
+ }
253
+ }
254
+
255
+ /**
256
+ * Utility functions for streaming responses
257
+ */
258
+ export class StreamingUtils {
259
+ /**
260
+ * Formats streaming results for display
261
+ */
262
+ static formatStreamingResponse(
263
+ results: StreamingResult<any>[],
264
+ type: "posts" | "users" | "comments" | "media" = "posts",
265
+ ): string {
266
+ const allData = results.flatMap((result) => result.data);
267
+ const totalProcessed = results[results.length - 1]?.processed || 0;
268
+ const hasMore = results[results.length - 1]?.hasMore || false;
269
+
270
+ const typeEmojis = {
271
+ posts: "📄",
272
+ users: "👥",
273
+ comments: "💬",
274
+ media: "📎",
275
+ };
276
+
277
+ const emoji = typeEmojis[type];
278
+ const typeName = type.charAt(0).toUpperCase() + type.slice(1);
279
+
280
+ let response = `${emoji} **${typeName} Results** (Streamed)\n\n`;
281
+ response += `📊 **Summary**: ${allData.length} items displayed, ${totalProcessed} processed total\n`;
282
+
283
+ if (hasMore) {
284
+ response += `⏳ **Status**: More data available (streaming in progress)\n`;
285
+ } else {
286
+ response += `✅ **Status**: Complete\n`;
287
+ }
288
+
289
+ response += `🕐 **Retrieved**: ${new Date().toLocaleString()}\n\n`;
290
+
291
+ // Format individual items
292
+ allData.forEach((item, index) => {
293
+ response += `${index + 1}. **${item.title || item.name || item.content?.substring(0, 50) || "Item"}**\n`;
294
+
295
+ if (item.excerpt) response += ` 📝 ${item.excerpt}\n`;
296
+ if (item.email) response += ` 📧 ${item.email}\n`;
297
+ if (item.status) response += ` 🏷️ Status: ${item.status}\n`;
298
+ if (item.date) response += ` 📅 Date: ${item.date}\n`;
299
+
300
+ response += "\n";
301
+ });
302
+
303
+ return response;
304
+ }
305
+
306
+ /**
307
+ * Implements progressive loading for large datasets
308
+ */
309
+ static async loadProgressively<T>(
310
+ fetcher: (offset: number, limit: number) => Promise<T[]>,
311
+ options: {
312
+ initialLoad?: number;
313
+ batchSize?: number;
314
+ maxItems?: number;
315
+ onProgress?: (loaded: number, total?: number) => void;
316
+ } = {},
317
+ ): Promise<T[]> {
318
+ const initialLoad = options.initialLoad || 50;
319
+ const batchSize = options.batchSize || 25;
320
+ const maxItems = options.maxItems || 1000;
321
+
322
+ const results: T[] = [];
323
+ let offset = 0;
324
+ let hasMore = true;
325
+
326
+ // Initial load
327
+ const initialBatch = await fetcher(offset, initialLoad);
328
+ results.push(...initialBatch);
329
+ offset += initialLoad;
330
+
331
+ if (options.onProgress) {
332
+ options.onProgress(results.length);
333
+ }
334
+
335
+ // Progressive loading
336
+ while (hasMore && results.length < maxItems) {
337
+ const batch = await fetcher(offset, batchSize);
338
+
339
+ if (batch.length === 0) {
340
+ hasMore = false;
341
+ break;
342
+ }
343
+
344
+ results.push(...batch);
345
+ offset += batchSize;
346
+
347
+ if (options.onProgress) {
348
+ options.onProgress(results.length, maxItems);
349
+ }
350
+
351
+ // Small delay to prevent overwhelming the server
352
+ await new Promise((resolve) => setTimeout(resolve, 100));
353
+ }
354
+
355
+ return results.slice(0, maxItems);
356
+ }
357
+ }
358
+
359
+ /**
360
+ * Memory-efficient data processor
361
+ */
362
+ export class MemoryEfficientProcessor {
363
+ /**
364
+ * Processes large datasets with memory monitoring
365
+ */
366
+ static async processLargeDataset<T, U>(
367
+ dataProvider: () => AsyncGenerator<T[], void, unknown>,
368
+ processor: (items: T[]) => Promise<U[]>,
369
+ options: {
370
+ maxMemoryUsage?: number; // in MB
371
+ batchSize?: number;
372
+ onProgress?: (processed: number) => void;
373
+ } = {},
374
+ ): Promise<U[]> {
375
+ const maxMemory = options.maxMemoryUsage || 100; // 100MB default
376
+ const batchSize = options.batchSize || 50;
377
+ const results: U[] = [];
378
+ let processed = 0;
379
+
380
+ for await (const batch of dataProvider()) {
381
+ // Check memory usage
382
+ const memoryUsage = process.memoryUsage();
383
+ const memoryUsageMB = memoryUsage.heapUsed / 1024 / 1024;
384
+
385
+ if (memoryUsageMB > maxMemory) {
386
+ // If memory usage is too high, process in smaller batches
387
+ const smallerBatches = this.chunkArray(batch, Math.floor(batchSize / 2));
388
+
389
+ for (const smallBatch of smallerBatches) {
390
+ const processed_batch = await processor(smallBatch);
391
+ results.push(...processed_batch);
392
+ processed += smallBatch.length;
393
+
394
+ if (options.onProgress) {
395
+ options.onProgress(processed);
396
+ }
397
+
398
+ // Force garbage collection if available
399
+ if (global.gc) {
400
+ global.gc();
401
+ }
402
+ }
403
+ } else {
404
+ // Normal processing
405
+ const processed_batch = await processor(batch);
406
+ results.push(...processed_batch);
407
+ processed += batch.length;
408
+
409
+ if (options.onProgress) {
410
+ options.onProgress(processed);
411
+ }
412
+ }
413
+ }
414
+
415
+ return results;
416
+ }
417
+
418
+ /**
419
+ * Helper method to split arrays into chunks
420
+ */
421
+ private static chunkArray<T>(array: T[], chunkSize: number): T[][] {
422
+ const chunks: T[][] = [];
423
+ for (let i = 0; i < array.length; i += chunkSize) {
424
+ chunks.push(array.slice(i, i + chunkSize));
425
+ }
426
+ return chunks;
427
+ }
428
+ }