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.
- package/README.md +1 -1
- package/dist/cache/CacheInvalidation.d.ts +25 -6
- package/dist/cache/CacheInvalidation.d.ts.map +1 -1
- package/dist/cache/CacheInvalidation.js +168 -16
- package/dist/cache/CacheInvalidation.js.map +1 -1
- package/dist/cache/HttpCacheWrapper.d.ts.map +1 -1
- package/dist/cache/HttpCacheWrapper.js +3 -4
- package/dist/cache/HttpCacheWrapper.js.map +1 -1
- package/dist/cache/SEOCacheManager.d.ts +150 -0
- package/dist/cache/SEOCacheManager.d.ts.map +1 -0
- package/dist/cache/SEOCacheManager.js +275 -0
- package/dist/cache/SEOCacheManager.js.map +1 -0
- package/dist/client/SEOWordPressClient.d.ts +164 -0
- package/dist/client/SEOWordPressClient.d.ts.map +1 -0
- package/dist/client/SEOWordPressClient.js +674 -0
- package/dist/client/SEOWordPressClient.js.map +1 -0
- package/dist/client/api.d.ts.map +1 -1
- package/dist/client/api.js +50 -20
- package/dist/client/api.js.map +1 -1
- package/dist/client/auth.js +19 -19
- package/dist/client/auth.js.map +1 -1
- package/dist/client/index.d.ts +11 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +14 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/managers/AuthManager.d.ts +39 -0
- package/dist/client/managers/AuthManager.d.ts.map +1 -0
- package/dist/client/managers/AuthManager.js +142 -0
- package/dist/client/managers/AuthManager.js.map +1 -0
- package/dist/client/managers/AuthenticationManager.d.ts.map +1 -1
- package/dist/client/managers/AuthenticationManager.js +10 -9
- package/dist/client/managers/AuthenticationManager.js.map +1 -1
- package/dist/client/managers/BaseManager.d.ts.map +1 -1
- package/dist/client/managers/BaseManager.js +12 -0
- package/dist/client/managers/BaseManager.js.map +1 -1
- package/dist/client/managers/ComposedAuthenticationManager.d.ts +94 -0
- package/dist/client/managers/ComposedAuthenticationManager.d.ts.map +1 -0
- package/dist/client/managers/ComposedAuthenticationManager.js +340 -0
- package/dist/client/managers/ComposedAuthenticationManager.js.map +1 -0
- package/dist/client/managers/ComposedManagerFactory.d.ts +104 -0
- package/dist/client/managers/ComposedManagerFactory.d.ts.map +1 -0
- package/dist/client/managers/ComposedManagerFactory.js +180 -0
- package/dist/client/managers/ComposedManagerFactory.js.map +1 -0
- package/dist/client/managers/ComposedRequestManager.d.ts +82 -0
- package/dist/client/managers/ComposedRequestManager.d.ts.map +1 -0
- package/dist/client/managers/ComposedRequestManager.js +260 -0
- package/dist/client/managers/ComposedRequestManager.js.map +1 -0
- package/dist/client/managers/JWTAuthImplementation.d.ts +86 -0
- package/dist/client/managers/JWTAuthImplementation.d.ts.map +1 -0
- package/dist/client/managers/JWTAuthImplementation.js +240 -0
- package/dist/client/managers/JWTAuthImplementation.js.map +1 -0
- package/dist/client/managers/ManagersIndex.d.ts +6 -0
- package/dist/client/managers/ManagersIndex.d.ts.map +1 -0
- package/dist/client/managers/ManagersIndex.js +6 -0
- package/dist/client/managers/ManagersIndex.js.map +1 -0
- package/dist/client/managers/RequestManager.d.ts.map +1 -1
- package/dist/client/managers/RequestManager.js +5 -3
- package/dist/client/managers/RequestManager.js.map +1 -1
- package/dist/client/managers/composed/MigrationAdapter.d.ts +80 -0
- package/dist/client/managers/composed/MigrationAdapter.d.ts.map +1 -0
- package/dist/client/managers/composed/MigrationAdapter.js +214 -0
- package/dist/client/managers/composed/MigrationAdapter.js.map +1 -0
- package/dist/client/managers/composed/index.d.ts +23 -0
- package/dist/client/managers/composed/index.d.ts.map +1 -0
- package/dist/client/managers/composed/index.js +26 -0
- package/dist/client/managers/composed/index.js.map +1 -0
- package/dist/client/managers/implementations/ConfigurationProviderImpl.d.ts +27 -0
- package/dist/client/managers/implementations/ConfigurationProviderImpl.d.ts.map +1 -0
- package/dist/client/managers/implementations/ConfigurationProviderImpl.js +41 -0
- package/dist/client/managers/implementations/ConfigurationProviderImpl.js.map +1 -0
- package/dist/client/managers/implementations/ErrorHandlerImpl.d.ts +31 -0
- package/dist/client/managers/implementations/ErrorHandlerImpl.d.ts.map +1 -0
- package/dist/client/managers/implementations/ErrorHandlerImpl.js +73 -0
- package/dist/client/managers/implementations/ErrorHandlerImpl.js.map +1 -0
- package/dist/client/managers/implementations/ParameterValidatorImpl.d.ts +47 -0
- package/dist/client/managers/implementations/ParameterValidatorImpl.d.ts.map +1 -0
- package/dist/client/managers/implementations/ParameterValidatorImpl.js +141 -0
- package/dist/client/managers/implementations/ParameterValidatorImpl.js.map +1 -0
- package/dist/client/managers/interfaces/ManagerInterfaces.d.ts +147 -0
- package/dist/client/managers/interfaces/ManagerInterfaces.d.ts.map +1 -0
- package/dist/client/managers/interfaces/ManagerInterfaces.js +6 -0
- package/dist/client/managers/interfaces/ManagerInterfaces.js.map +1 -0
- package/dist/config/Config.d.ts +30 -0
- package/dist/config/Config.d.ts.map +1 -1
- package/dist/config/Config.js +30 -0
- package/dist/config/Config.js.map +1 -1
- package/dist/config/ConfigurationSchema.d.ts +75 -198
- package/dist/config/ConfigurationSchema.d.ts.map +1 -1
- package/dist/config/ConfigurationSchema.js +17 -17
- package/dist/config/ConfigurationSchema.js.map +1 -1
- package/dist/config/ServerConfiguration.d.ts +2 -2
- package/dist/config/ServerConfiguration.d.ts.map +1 -1
- package/dist/config/ServerConfiguration.js +15 -13
- package/dist/config/ServerConfiguration.js.map +1 -1
- package/dist/config/index.d.ts +8 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +11 -0
- package/dist/config/index.js.map +1 -0
- package/dist/docs/DocumentationGenerator.js +2 -2
- package/dist/docs/DocumentationGenerator.js.map +1 -1
- package/dist/dxt-entry.js +3 -3
- package/dist/dxt-entry.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +38 -37
- package/dist/index.js.map +1 -1
- package/dist/performance/MetricsCollector.d.ts.map +1 -1
- package/dist/performance/MetricsCollector.js +5 -4
- package/dist/performance/MetricsCollector.js.map +1 -1
- package/dist/security/AISecurityScanner.js +7 -7
- package/dist/security/AISecurityScanner.js.map +1 -1
- package/dist/security/AutomatedRemediation.d.ts.map +1 -1
- package/dist/security/AutomatedRemediation.js +11 -11
- package/dist/security/AutomatedRemediation.js.map +1 -1
- package/dist/security/InputValidator.d.ts +50 -126
- package/dist/security/InputValidator.d.ts.map +1 -1
- package/dist/security/InputValidator.js +9 -9
- package/dist/security/InputValidator.js.map +1 -1
- package/dist/security/SecurityCIPipeline.d.ts +47 -5
- package/dist/security/SecurityCIPipeline.d.ts.map +1 -1
- package/dist/security/SecurityCIPipeline.js +390 -49
- package/dist/security/SecurityCIPipeline.js.map +1 -1
- package/dist/security/SecurityConfigManager.js +10 -10
- package/dist/security/SecurityConfigManager.js.map +1 -1
- package/dist/security/SecurityMonitoring.js +4 -4
- package/dist/security/SecurityMonitoring.js.map +1 -1
- package/dist/security/SecurityReviewer.d.ts.map +1 -1
- package/dist/security/SecurityReviewer.js +13 -6
- package/dist/security/SecurityReviewer.js.map +1 -1
- package/dist/security/index.js +3 -3
- package/dist/security/index.js.map +1 -1
- package/dist/server/ConnectionTester.js +5 -5
- package/dist/server/ConnectionTester.js.map +1 -1
- package/dist/server/ToolRegistry.d.ts +2 -2
- package/dist/server/ToolRegistry.d.ts.map +1 -1
- package/dist/server/ToolRegistry.js +7 -6
- package/dist/server/ToolRegistry.js.map +1 -1
- package/dist/server/index.d.ts +7 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +9 -0
- package/dist/server/index.js.map +1 -0
- package/dist/tools/BaseToolManager.d.ts.map +1 -1
- package/dist/tools/BaseToolManager.js +11 -11
- package/dist/tools/BaseToolManager.js.map +1 -1
- package/dist/tools/auth.d.ts.map +1 -1
- package/dist/tools/auth.js +7 -7
- package/dist/tools/auth.js.map +1 -1
- package/dist/tools/comments.d.ts.map +1 -1
- package/dist/tools/comments.js +14 -14
- package/dist/tools/comments.js.map +1 -1
- package/dist/tools/index.d.ts +1 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +1 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/media.d.ts.map +1 -1
- package/dist/tools/media.js +14 -11
- package/dist/tools/media.js.map +1 -1
- package/dist/tools/pages.d.ts.map +1 -1
- package/dist/tools/pages.js +12 -12
- package/dist/tools/pages.js.map +1 -1
- package/dist/tools/performance.d.ts.map +1 -1
- package/dist/tools/performance.js +13 -11
- package/dist/tools/performance.js.map +1 -1
- package/dist/tools/posts/PostHandlers.d.ts.map +1 -1
- package/dist/tools/posts/PostHandlers.js +16 -16
- package/dist/tools/posts/PostHandlers.js.map +1 -1
- package/dist/tools/posts/PostToolDefinitions.d.ts.map +1 -1
- package/dist/tools/posts/index.d.ts.map +1 -1
- package/dist/tools/seo/BulkOperations.d.ts +113 -0
- package/dist/tools/seo/BulkOperations.d.ts.map +1 -0
- package/dist/tools/seo/BulkOperations.js +398 -0
- package/dist/tools/seo/BulkOperations.js.map +1 -0
- package/dist/tools/seo/SEOHandlers.d.ts +55 -0
- package/dist/tools/seo/SEOHandlers.d.ts.map +1 -0
- package/dist/tools/seo/SEOHandlers.js +255 -0
- package/dist/tools/seo/SEOHandlers.js.map +1 -0
- package/dist/tools/seo/SEOToolDefinitions.d.ts +59 -0
- package/dist/tools/seo/SEOToolDefinitions.d.ts.map +1 -0
- package/dist/tools/seo/SEOToolDefinitions.js +385 -0
- package/dist/tools/seo/SEOToolDefinitions.js.map +1 -0
- package/dist/tools/seo/SEOTools.d.ts +203 -0
- package/dist/tools/seo/SEOTools.d.ts.map +1 -0
- package/dist/tools/seo/SEOTools.js +708 -0
- package/dist/tools/seo/SEOTools.js.map +1 -0
- package/dist/tools/seo/analyzers/ContentAnalyzer.d.ts +94 -0
- package/dist/tools/seo/analyzers/ContentAnalyzer.d.ts.map +1 -0
- package/dist/tools/seo/analyzers/ContentAnalyzer.js +402 -0
- package/dist/tools/seo/analyzers/ContentAnalyzer.js.map +1 -0
- package/dist/tools/seo/auditors/SiteAuditor.d.ts +121 -0
- package/dist/tools/seo/auditors/SiteAuditor.d.ts.map +1 -0
- package/dist/tools/seo/auditors/SiteAuditor.js +600 -0
- package/dist/tools/seo/auditors/SiteAuditor.js.map +1 -0
- package/dist/tools/seo/generators/MetaGenerator.d.ts +128 -0
- package/dist/tools/seo/generators/MetaGenerator.d.ts.map +1 -0
- package/dist/tools/seo/generators/MetaGenerator.js +547 -0
- package/dist/tools/seo/generators/MetaGenerator.js.map +1 -0
- package/dist/tools/seo/generators/SchemaGenerator.d.ts +204 -0
- package/dist/tools/seo/generators/SchemaGenerator.d.ts.map +1 -0
- package/dist/tools/seo/generators/SchemaGenerator.js +670 -0
- package/dist/tools/seo/generators/SchemaGenerator.js.map +1 -0
- package/dist/tools/seo/index.d.ts +17 -0
- package/dist/tools/seo/index.d.ts.map +1 -0
- package/dist/tools/seo/index.js +18 -0
- package/dist/tools/seo/index.js.map +1 -0
- package/dist/tools/seo/optimizers/InternalLinkingSuggester.d.ts +186 -0
- package/dist/tools/seo/optimizers/InternalLinkingSuggester.d.ts.map +1 -0
- package/dist/tools/seo/optimizers/InternalLinkingSuggester.js +683 -0
- package/dist/tools/seo/optimizers/InternalLinkingSuggester.js.map +1 -0
- package/dist/tools/site.d.ts.map +1 -1
- package/dist/tools/site.js +12 -12
- package/dist/tools/site.js.map +1 -1
- package/dist/tools/taxonomies.d.ts.map +1 -1
- package/dist/tools/taxonomies.js +20 -20
- package/dist/tools/taxonomies.js.map +1 -1
- package/dist/tools/users.d.ts.map +1 -1
- package/dist/tools/users.js +12 -12
- package/dist/tools/users.js.map +1 -1
- package/dist/types/client.d.ts +8 -6
- package/dist/types/client.d.ts.map +1 -1
- package/dist/types/client.js.map +1 -1
- package/dist/types/seo.d.ts +473 -0
- package/dist/types/seo.d.ts.map +1 -0
- package/dist/types/seo.js +94 -0
- package/dist/types/seo.js.map +1 -0
- package/dist/utils/enhancedError.js +1 -1
- package/dist/utils/enhancedError.js.map +1 -1
- package/dist/utils/error.d.ts.map +1 -1
- package/dist/utils/error.js +0 -1
- package/dist/utils/error.js.map +1 -1
- package/dist/utils/index.d.ts +12 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +18 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/logger.js +3 -3
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils/toolWrapper.d.ts +2 -2
- package/dist/utils/toolWrapper.js +8 -8
- package/dist/utils/toolWrapper.js.map +1 -1
- package/dist/utils/validation/core.d.ts.map +1 -1
- package/dist/utils/validation/core.js.map +1 -1
- package/dist/utils/validation/index.d.ts.map +1 -1
- package/dist/utils/validation/index.js.map +1 -1
- package/dist/utils/validation/network.js +3 -3
- package/dist/utils/validation/network.js.map +1 -1
- package/dist/utils/validation/rateLimit.js.map +1 -1
- package/dist/utils/validation/security.js.map +1 -1
- package/dist/utils/validation/wordpress.js.map +1 -1
- package/dist/utils/version.d.ts +144 -0
- package/dist/utils/version.d.ts.map +1 -0
- package/dist/utils/version.js +318 -0
- package/dist/utils/version.js.map +1 -0
- package/package.json +21 -55
- package/src/cache/CacheInvalidation.ts +183 -20
- package/src/cache/HttpCacheWrapper.ts +8 -5
- package/src/cache/SEOCacheManager.ts +330 -0
- package/src/cache/__tests__/CacheInvalidation.test.ts +6 -11
- package/src/cache/__tests__/CachedWordPressClient.test.ts +37 -62
- package/src/client/SEOWordPressClient.ts +876 -0
- package/src/client/api.ts +50 -21
- package/src/client/auth.ts +19 -19
- package/src/client/index.ts +16 -0
- package/src/client/managers/AuthManager.ts +175 -0
- package/src/client/managers/AuthenticationManager.ts +16 -14
- package/src/client/managers/BaseManager.ts +24 -5
- package/src/client/managers/ComposedAuthenticationManager.ts +409 -0
- package/src/client/managers/ComposedManagerFactory.ts +231 -0
- package/src/client/managers/ComposedRequestManager.ts +336 -0
- package/src/client/managers/JWTAuthImplementation.ts +326 -0
- package/src/client/managers/ManagersIndex.ts +6 -0
- package/src/client/managers/RequestManager.ts +9 -7
- package/src/client/managers/composed/MigrationAdapter.ts +263 -0
- package/src/client/managers/composed/index.ts +47 -0
- package/src/client/managers/implementations/ConfigurationProviderImpl.ts +52 -0
- package/src/client/managers/implementations/ErrorHandlerImpl.ts +102 -0
- package/src/client/managers/implementations/ParameterValidatorImpl.ts +221 -0
- package/src/client/managers/interfaces/ManagerInterfaces.ts +171 -0
- package/src/config/Config.ts +63 -0
- package/src/config/ConfigurationSchema.ts +17 -17
- package/src/config/ServerConfiguration.ts +18 -16
- package/src/config/index.ts +13 -0
- package/src/docs/DocumentationGenerator.ts +2 -2
- package/src/dxt-entry.ts +3 -3
- package/src/index.ts +43 -43
- package/src/performance/MetricsCollector.ts +15 -11
- package/src/security/AISecurityScanner.ts +7 -7
- package/src/security/AutomatedRemediation.ts +13 -11
- package/src/security/InputValidator.ts +10 -9
- package/src/security/SecurityCIPipeline.ts +494 -56
- package/src/security/SecurityConfigManager.ts +10 -10
- package/src/security/SecurityMonitoring.ts +5 -5
- package/src/security/SecurityReviewer.ts +13 -6
- package/src/security/index.ts +3 -3
- package/src/server/ConnectionTester.ts +5 -5
- package/src/server/ToolRegistry.ts +9 -8
- package/src/server/index.ts +10 -0
- package/src/tools/BaseToolManager.ts +55 -83
- package/src/tools/auth.ts +21 -12
- package/src/tools/comments.ts +23 -19
- package/src/tools/index.ts +1 -0
- package/src/tools/media.ts +23 -20
- package/src/tools/pages.ts +20 -13
- package/src/tools/performance.ts +101 -32
- package/src/tools/posts/PostHandlers.ts +23 -23
- package/src/tools/posts/PostToolDefinitions.ts +1 -1
- package/src/tools/posts/index.ts +2 -2
- package/src/tools/seo/BulkOperations.ts +557 -0
- package/src/tools/seo/SEOHandlers.ts +296 -0
- package/src/tools/seo/SEOToolDefinitions.ts +402 -0
- package/src/tools/seo/SEOTools.ts +871 -0
- package/src/tools/seo/analyzers/ContentAnalyzer.ts +493 -0
- package/src/tools/seo/auditors/SiteAuditor.ts +787 -0
- package/src/tools/seo/generators/MetaGenerator.ts +694 -0
- package/src/tools/seo/generators/SchemaGenerator.ts +955 -0
- package/src/tools/seo/index.ts +47 -0
- package/src/tools/seo/optimizers/InternalLinkingSuggester.ts +934 -0
- package/src/tools/site.ts +27 -26
- package/src/tools/taxonomies.ts +29 -25
- package/src/tools/users.ts +20 -13
- package/src/types/client.ts +8 -6
- package/src/types/seo.ts +546 -0
- package/src/utils/enhancedError.ts +1 -1
- package/src/utils/error.ts +1 -2
- package/src/utils/index.ts +23 -0
- package/src/utils/logger.ts +3 -3
- package/src/utils/toolWrapper.ts +10 -10
- package/src/utils/validation/core.ts +2 -2
- package/src/utils/validation/index.ts +2 -2
- package/src/utils/validation/network.ts +5 -5
- package/src/utils/validation/rateLimit.ts +1 -1
- package/src/utils/validation/security.ts +1 -1
- package/src/utils/validation/wordpress.ts +1 -1
- package/src/utils/version.ts +402 -0
package/src/tools/posts/index.ts
CHANGED
|
@@ -27,8 +27,8 @@
|
|
|
27
27
|
* ```
|
|
28
28
|
*/
|
|
29
29
|
|
|
30
|
-
import { WordPressClient } from "
|
|
31
|
-
import { CreatePostRequest, PostQueryParams, UpdatePostRequest, WordPressPost } from "
|
|
30
|
+
import { WordPressClient } from "@/client/api.js";
|
|
31
|
+
import { CreatePostRequest, PostQueryParams, UpdatePostRequest, WordPressPost } from "@/types/wordpress.js";
|
|
32
32
|
import { postToolDefinitions } from "./PostToolDefinitions.js";
|
|
33
33
|
import {
|
|
34
34
|
handleListPosts,
|
|
@@ -0,0 +1,557 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bulk SEO Operations
|
|
3
|
+
*
|
|
4
|
+
* This module provides batch processing capabilities for SEO operations,
|
|
5
|
+
* including metadata updates, content analysis, and schema generation.
|
|
6
|
+
* It supports chunked processing, progress tracking, retry logic, and dry-run mode.
|
|
7
|
+
*
|
|
8
|
+
* @since 2.7.0
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { WordPressClient } from "@/client/api.js";
|
|
12
|
+
import { MetaGenerator } from "./generators/MetaGenerator.js";
|
|
13
|
+
import { ContentAnalyzer } from "./analyzers/ContentAnalyzer.js";
|
|
14
|
+
import { SEOCacheManager } from "@/cache/SEOCacheManager.js";
|
|
15
|
+
import { LoggerFactory } from "@/utils/logger.js";
|
|
16
|
+
import { SEOToolParams, BulkOperationResult, SEOAnalysisResult } from "@/types/seo.js";
|
|
17
|
+
import type { WordPressPost } from "@/types/wordpress.js";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Configuration for bulk operations
|
|
21
|
+
*/
|
|
22
|
+
interface BulkOperationConfig {
|
|
23
|
+
/** Number of items per batch */
|
|
24
|
+
batchSize: number;
|
|
25
|
+
|
|
26
|
+
/** Maximum number of retry attempts */
|
|
27
|
+
maxRetries: number;
|
|
28
|
+
|
|
29
|
+
/** Initial delay for exponential backoff (ms) */
|
|
30
|
+
retryDelayMs: number;
|
|
31
|
+
|
|
32
|
+
/** Maximum delay for exponential backoff (ms) */
|
|
33
|
+
maxRetryDelayMs: number;
|
|
34
|
+
|
|
35
|
+
/** Timeout per operation (ms) */
|
|
36
|
+
operationTimeoutMs: number;
|
|
37
|
+
|
|
38
|
+
/** Enable progress callbacks */
|
|
39
|
+
enableProgress: boolean;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Progress information for bulk operations
|
|
44
|
+
*/
|
|
45
|
+
interface BulkProgress {
|
|
46
|
+
/** Total items to process */
|
|
47
|
+
total: number;
|
|
48
|
+
|
|
49
|
+
/** Items processed so far */
|
|
50
|
+
processed: number;
|
|
51
|
+
|
|
52
|
+
/** Items successfully completed */
|
|
53
|
+
completed: number;
|
|
54
|
+
|
|
55
|
+
/** Items that failed */
|
|
56
|
+
failed: number;
|
|
57
|
+
|
|
58
|
+
/** Items that were skipped */
|
|
59
|
+
skipped: number;
|
|
60
|
+
|
|
61
|
+
/** Current batch being processed */
|
|
62
|
+
currentBatch: number;
|
|
63
|
+
|
|
64
|
+
/** Total number of batches */
|
|
65
|
+
totalBatches: number;
|
|
66
|
+
|
|
67
|
+
/** Estimated completion time */
|
|
68
|
+
eta?: Date;
|
|
69
|
+
|
|
70
|
+
/** Average processing time per item (ms) */
|
|
71
|
+
avgProcessingTime: number;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Error information for failed operations
|
|
76
|
+
*/
|
|
77
|
+
interface BulkOperationError {
|
|
78
|
+
/** Post ID that failed */
|
|
79
|
+
postId: number;
|
|
80
|
+
|
|
81
|
+
/** Error message */
|
|
82
|
+
error: string;
|
|
83
|
+
|
|
84
|
+
/** Retry attempt count */
|
|
85
|
+
attempts: number;
|
|
86
|
+
|
|
87
|
+
/** Whether this error is retryable */
|
|
88
|
+
retryable: boolean;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Type for progress callback function
|
|
93
|
+
*/
|
|
94
|
+
type ProgressCallback = (progress: BulkProgress) => void;
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Bulk SEO Operations Manager
|
|
98
|
+
*/
|
|
99
|
+
export class BulkOperations {
|
|
100
|
+
private logger = LoggerFactory.tool("bulk_operations");
|
|
101
|
+
private config: BulkOperationConfig;
|
|
102
|
+
private metaGenerator: MetaGenerator;
|
|
103
|
+
private contentAnalyzer: ContentAnalyzer;
|
|
104
|
+
private cacheManager: SEOCacheManager | undefined;
|
|
105
|
+
|
|
106
|
+
constructor(
|
|
107
|
+
private client: WordPressClient,
|
|
108
|
+
cacheManager?: SEOCacheManager,
|
|
109
|
+
config?: Partial<BulkOperationConfig>,
|
|
110
|
+
) {
|
|
111
|
+
this.cacheManager = cacheManager;
|
|
112
|
+
this.metaGenerator = new MetaGenerator();
|
|
113
|
+
this.contentAnalyzer = new ContentAnalyzer();
|
|
114
|
+
|
|
115
|
+
// Default configuration
|
|
116
|
+
this.config = {
|
|
117
|
+
batchSize: 10,
|
|
118
|
+
maxRetries: 3,
|
|
119
|
+
retryDelayMs: 1000,
|
|
120
|
+
maxRetryDelayMs: 30000,
|
|
121
|
+
operationTimeoutMs: 60000,
|
|
122
|
+
enableProgress: true,
|
|
123
|
+
...config,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Bulk update metadata for multiple posts
|
|
129
|
+
*/
|
|
130
|
+
async bulkUpdateMetadata(params: SEOToolParams, progressCallback?: ProgressCallback): Promise<BulkOperationResult> {
|
|
131
|
+
const startTime = Date.now();
|
|
132
|
+
this.logger.info("Starting bulk metadata update", {
|
|
133
|
+
postIds: params.postIds?.length,
|
|
134
|
+
dryRun: params.dryRun,
|
|
135
|
+
batchSize: this.config.batchSize,
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
if (!params.postIds?.length) {
|
|
139
|
+
throw new Error("No post IDs provided for bulk operation");
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const progress: BulkProgress = {
|
|
143
|
+
total: params.postIds.length,
|
|
144
|
+
processed: 0,
|
|
145
|
+
completed: 0,
|
|
146
|
+
failed: 0,
|
|
147
|
+
skipped: 0,
|
|
148
|
+
currentBatch: 0,
|
|
149
|
+
totalBatches: Math.ceil(params.postIds.length / this.config.batchSize),
|
|
150
|
+
avgProcessingTime: 0,
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
const errors: BulkOperationError[] = [];
|
|
154
|
+
const batches = this.createBatches(params.postIds, this.config.batchSize);
|
|
155
|
+
|
|
156
|
+
// Process each batch
|
|
157
|
+
for (let batchIndex = 0; batchIndex < batches.length; batchIndex++) {
|
|
158
|
+
const batch = batches[batchIndex];
|
|
159
|
+
progress.currentBatch = batchIndex + 1;
|
|
160
|
+
|
|
161
|
+
this.logger.debug(`Processing batch ${progress.currentBatch}/${progress.totalBatches}`, {
|
|
162
|
+
batchSize: batch.length,
|
|
163
|
+
postIds: batch,
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
// Process batch items
|
|
167
|
+
await Promise.all(
|
|
168
|
+
batch.map(async (postId) => {
|
|
169
|
+
const itemStartTime = Date.now();
|
|
170
|
+
|
|
171
|
+
try {
|
|
172
|
+
await this.processMetadataUpdate(postId, params);
|
|
173
|
+
progress.completed++;
|
|
174
|
+
|
|
175
|
+
// Update average processing time
|
|
176
|
+
const processingTime = Date.now() - itemStartTime;
|
|
177
|
+
progress.avgProcessingTime =
|
|
178
|
+
(progress.avgProcessingTime * progress.processed + processingTime) / (progress.processed + 1);
|
|
179
|
+
} catch (_error) {
|
|
180
|
+
const bulkError: BulkOperationError = {
|
|
181
|
+
postId,
|
|
182
|
+
error: _error instanceof Error ? _error.message : String(_error),
|
|
183
|
+
attempts: 1,
|
|
184
|
+
retryable: this.isRetryableError(_error),
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
// Attempt retries
|
|
188
|
+
if (bulkError.retryable) {
|
|
189
|
+
const retryResult = await this.retryOperation(
|
|
190
|
+
() => this.processMetadataUpdate(postId, params),
|
|
191
|
+
bulkError,
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
if (retryResult.success) {
|
|
195
|
+
progress.completed++;
|
|
196
|
+
} else {
|
|
197
|
+
progress.failed++;
|
|
198
|
+
errors.push(retryResult.error);
|
|
199
|
+
}
|
|
200
|
+
} else {
|
|
201
|
+
progress.failed++;
|
|
202
|
+
errors.push(bulkError);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
progress.processed++;
|
|
207
|
+
}),
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
// Calculate ETA
|
|
211
|
+
if (progress.avgProcessingTime > 0 && progress.processed < progress.total) {
|
|
212
|
+
const remainingItems = progress.total - progress.processed;
|
|
213
|
+
const etaMs = remainingItems * progress.avgProcessingTime;
|
|
214
|
+
progress.eta = new Date(Date.now() + etaMs);
|
|
215
|
+
} else if (progress.processed > 0 && progress.processed < progress.total) {
|
|
216
|
+
// Fallback ETA calculation even with minimal processing time
|
|
217
|
+
const remainingItems = progress.total - progress.processed;
|
|
218
|
+
const averageTime = progress.avgProcessingTime || 100; // Fallback to 100ms
|
|
219
|
+
const etaMs = remainingItems * averageTime;
|
|
220
|
+
progress.eta = new Date(Date.now() + etaMs);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Call progress callback
|
|
224
|
+
if (progressCallback && this.config.enableProgress) {
|
|
225
|
+
progressCallback(progress);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Small delay between batches to avoid overwhelming the server
|
|
229
|
+
if (batchIndex < batches.length - 1) {
|
|
230
|
+
await this.delay(100);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const result: BulkOperationResult = {
|
|
235
|
+
total: params.postIds.length,
|
|
236
|
+
success: progress.completed,
|
|
237
|
+
failed: progress.failed,
|
|
238
|
+
skipped: progress.skipped,
|
|
239
|
+
errors: errors.map((e) => ({ postId: e.postId, error: e.error })),
|
|
240
|
+
processingTime: Date.now() - startTime,
|
|
241
|
+
dryRun: params.dryRun || false,
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
this.logger.info("Bulk metadata update completed", {
|
|
245
|
+
...result,
|
|
246
|
+
successRate: ((result.success / result.total) * 100).toFixed(1) + "%",
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
return result;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Bulk analyze content for multiple posts
|
|
254
|
+
*/
|
|
255
|
+
async bulkAnalyzeContent(
|
|
256
|
+
params: SEOToolParams,
|
|
257
|
+
progressCallback?: ProgressCallback,
|
|
258
|
+
): Promise<{ results: SEOAnalysisResult[]; summary: BulkOperationResult }> {
|
|
259
|
+
const startTime = Date.now();
|
|
260
|
+
this.logger.info("Starting bulk content analysis", {
|
|
261
|
+
postIds: params.postIds?.length,
|
|
262
|
+
analysisType: params.analysisType,
|
|
263
|
+
batchSize: this.config.batchSize,
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
if (!params.postIds?.length) {
|
|
267
|
+
throw new Error("No post IDs provided for bulk analysis");
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const results: SEOAnalysisResult[] = [];
|
|
271
|
+
const progress: BulkProgress = {
|
|
272
|
+
total: params.postIds.length,
|
|
273
|
+
processed: 0,
|
|
274
|
+
completed: 0,
|
|
275
|
+
failed: 0,
|
|
276
|
+
skipped: 0,
|
|
277
|
+
currentBatch: 0,
|
|
278
|
+
totalBatches: Math.ceil(params.postIds.length / this.config.batchSize),
|
|
279
|
+
avgProcessingTime: 0,
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
const errors: BulkOperationError[] = [];
|
|
283
|
+
const batches = this.createBatches(params.postIds, this.config.batchSize);
|
|
284
|
+
|
|
285
|
+
// Process each batch
|
|
286
|
+
for (let batchIndex = 0; batchIndex < batches.length; batchIndex++) {
|
|
287
|
+
const batch = batches[batchIndex];
|
|
288
|
+
progress.currentBatch = batchIndex + 1;
|
|
289
|
+
|
|
290
|
+
// Process batch items
|
|
291
|
+
await Promise.all(
|
|
292
|
+
batch.map(async (postId) => {
|
|
293
|
+
const itemStartTime = Date.now();
|
|
294
|
+
|
|
295
|
+
try {
|
|
296
|
+
const analysisResult = await this.processContentAnalysis(postId, params);
|
|
297
|
+
results.push(analysisResult);
|
|
298
|
+
progress.completed++;
|
|
299
|
+
|
|
300
|
+
// Update average processing time
|
|
301
|
+
const processingTime = Date.now() - itemStartTime;
|
|
302
|
+
progress.avgProcessingTime =
|
|
303
|
+
(progress.avgProcessingTime * progress.processed + processingTime) / (progress.processed + 1);
|
|
304
|
+
} catch (_error) {
|
|
305
|
+
const bulkError: BulkOperationError = {
|
|
306
|
+
postId,
|
|
307
|
+
error: _error instanceof Error ? _error.message : String(_error),
|
|
308
|
+
attempts: 1,
|
|
309
|
+
retryable: this.isRetryableError(_error),
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
// Attempt retries for analysis
|
|
313
|
+
if (bulkError.retryable) {
|
|
314
|
+
const retryResult = await this.retryOperation(
|
|
315
|
+
() => this.processContentAnalysis(postId, params),
|
|
316
|
+
bulkError,
|
|
317
|
+
);
|
|
318
|
+
|
|
319
|
+
if (retryResult.success) {
|
|
320
|
+
results.push(retryResult.result!);
|
|
321
|
+
progress.completed++;
|
|
322
|
+
} else {
|
|
323
|
+
progress.failed++;
|
|
324
|
+
errors.push(retryResult.error);
|
|
325
|
+
}
|
|
326
|
+
} else {
|
|
327
|
+
progress.failed++;
|
|
328
|
+
errors.push(bulkError);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
progress.processed++;
|
|
333
|
+
}),
|
|
334
|
+
);
|
|
335
|
+
|
|
336
|
+
// Call progress callback
|
|
337
|
+
if (progressCallback && this.config.enableProgress) {
|
|
338
|
+
progressCallback(progress);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const summary: BulkOperationResult = {
|
|
343
|
+
total: params.postIds.length,
|
|
344
|
+
success: progress.completed,
|
|
345
|
+
failed: progress.failed,
|
|
346
|
+
skipped: progress.skipped,
|
|
347
|
+
errors: errors.map((e) => ({ postId: e.postId, error: e.error })),
|
|
348
|
+
processingTime: Date.now() - startTime,
|
|
349
|
+
dryRun: false,
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
this.logger.info("Bulk content analysis completed", {
|
|
353
|
+
...summary,
|
|
354
|
+
resultsCount: results.length,
|
|
355
|
+
avgScore: results.length > 0 ? (results.reduce((sum, r) => sum + r.score, 0) / results.length).toFixed(1) : 0,
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
return { results, summary };
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Process metadata update for a single post
|
|
363
|
+
*/
|
|
364
|
+
private async processMetadataUpdate(postId: number, params: SEOToolParams): Promise<void> {
|
|
365
|
+
// Check cache first
|
|
366
|
+
const cacheKey = `bulk-meta-${postId}`;
|
|
367
|
+
if (this.cacheManager && !params.force) {
|
|
368
|
+
const cached = this.cacheManager.get(cacheKey);
|
|
369
|
+
if (cached) {
|
|
370
|
+
this.logger.debug("Using cached metadata", { postId });
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// Fetch post data
|
|
376
|
+
const post = await this.client.getPost(postId);
|
|
377
|
+
if (!post) {
|
|
378
|
+
throw new Error(`Post ${postId} not found`);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// Generate metadata
|
|
382
|
+
const metadataParams = {
|
|
383
|
+
postId,
|
|
384
|
+
...(params.focusKeywords && { focusKeywords: params.focusKeywords }),
|
|
385
|
+
...(params.site && { site: params.site }),
|
|
386
|
+
};
|
|
387
|
+
const metadata = await this.metaGenerator.generateMetadata(post, metadataParams);
|
|
388
|
+
|
|
389
|
+
if (!params.dryRun) {
|
|
390
|
+
// Apply updates to WordPress (this would need actual WordPress API calls)
|
|
391
|
+
// For now, we just simulate the update
|
|
392
|
+
this.logger.debug("Applying metadata updates", {
|
|
393
|
+
postId,
|
|
394
|
+
titleLength: metadata.title.length,
|
|
395
|
+
descriptionLength: metadata.description.length,
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
// In a real implementation, you would call:
|
|
399
|
+
// await this.client.updatePost(postId, { meta: metadata });
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// Cache the result
|
|
403
|
+
if (this.cacheManager) {
|
|
404
|
+
this.cacheManager.set(cacheKey, metadata, 3600); // 1 hour
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* Process content analysis for a single post
|
|
410
|
+
*/
|
|
411
|
+
private async processContentAnalysis(postId: number, params: SEOToolParams): Promise<SEOAnalysisResult> {
|
|
412
|
+
// Check cache first
|
|
413
|
+
const cacheKey = `bulk-analysis-${postId}-${params.analysisType || "full"}`;
|
|
414
|
+
if (this.cacheManager && !params.force) {
|
|
415
|
+
const cached = this.cacheManager.get(cacheKey);
|
|
416
|
+
if (cached) {
|
|
417
|
+
this.logger.debug("Using cached analysis", { postId });
|
|
418
|
+
return cached as SEOAnalysisResult;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// Fetch post data
|
|
423
|
+
const post = await this.client.getPost(postId);
|
|
424
|
+
if (!post) {
|
|
425
|
+
throw new Error(`Post ${postId} not found`);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// Analyze content
|
|
429
|
+
const analysisParams = {
|
|
430
|
+
postId,
|
|
431
|
+
analysisType: params.analysisType || ("full" as const),
|
|
432
|
+
...(params.site && { site: params.site }),
|
|
433
|
+
...(params.focusKeywords && { focusKeywords: params.focusKeywords }),
|
|
434
|
+
};
|
|
435
|
+
const analysisResult = await this.contentAnalyzer.analyzePost(post as WordPressPost, analysisParams);
|
|
436
|
+
|
|
437
|
+
// Cache the result
|
|
438
|
+
if (this.cacheManager) {
|
|
439
|
+
this.cacheManager.set(cacheKey, analysisResult, 21600); // 6 hours
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
return analysisResult;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* Retry operation with exponential backoff
|
|
447
|
+
*/
|
|
448
|
+
private async retryOperation<T>(
|
|
449
|
+
operation: () => Promise<T>,
|
|
450
|
+
error: BulkOperationError,
|
|
451
|
+
): Promise<{ success: boolean; result?: T; error: BulkOperationError }> {
|
|
452
|
+
let delay = this.config.retryDelayMs;
|
|
453
|
+
|
|
454
|
+
for (let attempt = 2; attempt <= this.config.maxRetries + 1; attempt++) {
|
|
455
|
+
await this.delay(delay);
|
|
456
|
+
|
|
457
|
+
try {
|
|
458
|
+
const result = await operation();
|
|
459
|
+
this.logger.debug("Retry successful", {
|
|
460
|
+
postId: error.postId,
|
|
461
|
+
attempt,
|
|
462
|
+
delay,
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
return { success: true, result, error };
|
|
466
|
+
} catch (retryError) {
|
|
467
|
+
error.attempts = attempt;
|
|
468
|
+
error.error = retryError instanceof Error ? retryError.message : String(retryError);
|
|
469
|
+
|
|
470
|
+
// Exponential backoff
|
|
471
|
+
delay = Math.min(delay * 2, this.config.maxRetryDelayMs);
|
|
472
|
+
|
|
473
|
+
this.logger.debug("Retry failed", {
|
|
474
|
+
postId: error.postId,
|
|
475
|
+
attempt,
|
|
476
|
+
error: error.error,
|
|
477
|
+
nextDelay: delay,
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
return { success: false, error };
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
/**
|
|
486
|
+
* Check if an error is retryable
|
|
487
|
+
*/
|
|
488
|
+
private isRetryableError(error: unknown): boolean {
|
|
489
|
+
if (!error) return false;
|
|
490
|
+
|
|
491
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
492
|
+
|
|
493
|
+
// Network errors are retryable
|
|
494
|
+
if (
|
|
495
|
+
errorMessage.includes("ECONNREFUSED") ||
|
|
496
|
+
errorMessage.includes("timeout") ||
|
|
497
|
+
errorMessage.includes("503") ||
|
|
498
|
+
errorMessage.includes("502") ||
|
|
499
|
+
errorMessage.includes("504")
|
|
500
|
+
) {
|
|
501
|
+
return true;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
// Authentication errors are not retryable
|
|
505
|
+
if (errorMessage.includes("401") || errorMessage.includes("403")) {
|
|
506
|
+
return false;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
// Not found errors are not retryable
|
|
510
|
+
if (errorMessage.includes("404")) {
|
|
511
|
+
return false;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
// Rate limiting is retryable
|
|
515
|
+
if (errorMessage.includes("429")) {
|
|
516
|
+
return true;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
// Default to non-retryable for safety
|
|
520
|
+
return false;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
/**
|
|
524
|
+
* Create batches from an array of items
|
|
525
|
+
*/
|
|
526
|
+
private createBatches<T>(items: T[], batchSize: number): T[][] {
|
|
527
|
+
const batches: T[][] = [];
|
|
528
|
+
|
|
529
|
+
for (let i = 0; i < items.length; i += batchSize) {
|
|
530
|
+
batches.push(items.slice(i, i + batchSize));
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
return batches;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
/**
|
|
537
|
+
* Promise-based delay utility
|
|
538
|
+
*/
|
|
539
|
+
private delay(ms: number): Promise<void> {
|
|
540
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
/**
|
|
544
|
+
* Get current configuration
|
|
545
|
+
*/
|
|
546
|
+
getConfig(): BulkOperationConfig {
|
|
547
|
+
return { ...this.config };
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
/**
|
|
551
|
+
* Update configuration
|
|
552
|
+
*/
|
|
553
|
+
updateConfig(config: Partial<BulkOperationConfig>): void {
|
|
554
|
+
this.config = { ...this.config, ...config };
|
|
555
|
+
this.logger.debug("Configuration updated", { config: this.config });
|
|
556
|
+
}
|
|
557
|
+
}
|