snow-flow 3.0.9 → 3.0.13
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/claude-flow.bat +0 -0
- package/claude-flow.ps1 +0 -0
- package/dist/mcp/intelligent-reporting-mcp.d.ts +7 -0
- package/dist/mcp/intelligent-reporting-mcp.js +379 -0
- package/dist/mcp/servicenow-automation-mcp.js +0 -0
- package/dist/mcp/servicenow-deployment-mcp.js +0 -0
- package/dist/mcp/servicenow-integration-mcp.js +0 -0
- package/dist/mcp/servicenow-mcp-server.js +0 -0
- package/dist/mcp/servicenow-operations-mcp.js +0 -0
- package/dist/mcp/servicenow-platform-development-mcp.js +0 -0
- package/dist/mcp/servicenow-progressive-indexer.js +0 -0
- package/dist/mcp/servicenow-reporting-analytics-mcp.js +280 -29
- package/dist/mcp/servicenow-security-compliance-mcp.js +0 -0
- package/dist/mcp/servicenow-update-set-mcp.js +0 -0
- package/dist/mcp/start-all-mcp-servers.js +0 -0
- package/dist/mcp/start-servicenow-mcp.js +0 -0
- package/dist/services/widget-deployment-service.d.ts +2 -1
- package/dist/services/widget-deployment-service.js +17 -16
- package/dist/utils/anti-mock-data-validator.d.ts +66 -0
- package/dist/utils/anti-mock-data-validator.js +316 -0
- package/dist/utils/servicenow-eventual-consistency.d.ts +120 -0
- package/dist/utils/servicenow-eventual-consistency.js +220 -0
- package/memory/agents/README.md +1 -1
- package/memory/claude-flow-data.json +1 -1
- package/memory/sessions/README.md +1 -1
- package/package.json +4 -3
- package/.mcp.json +0 -22
- package/claude-flow.config.json +0 -20
- package/dist/agents/app-creator.agent.d.ts +0 -55
- package/dist/agents/app-creator.agent.js +0 -1292
- package/dist/agents/base/base-snow-agent.d.ts +0 -75
- package/dist/agents/base/base-snow-agent.js +0 -169
- package/dist/agents/base-snow-agent.d.ts +0 -63
- package/dist/agents/base-snow-agent.js +0 -296
- package/dist/agents/coordinator.d.ts +0 -200
- package/dist/agents/coordinator.js +0 -777
- package/dist/agents/flow-builder-agent.d.ts +0 -18
- package/dist/agents/flow-builder-agent.js +0 -197
- package/dist/agents/queen-403-handler.d.ts +0 -47
- package/dist/agents/queen-403-handler.js +0 -175
- package/dist/agents/queen-agent-example.d.ts +0 -23
- package/dist/agents/queen-agent-example.js +0 -240
- package/dist/agents/queen-agent.test.d.ts +0 -6
- package/dist/agents/script-generator.agent.d.ts +0 -32
- package/dist/agents/script-generator.agent.js +0 -772
- package/dist/agents/script-writer-agent.d.ts +0 -22
- package/dist/agents/script-writer-agent.js +0 -695
- package/dist/agents/security-agent.d.ts +0 -23
- package/dist/agents/security-agent.js +0 -619
- package/dist/agents/specialists/automation-specialist.agent.d.ts +0 -44
- package/dist/agents/specialists/automation-specialist.agent.js +0 -204
- package/dist/agents/specialists/data-specialist.agent.d.ts +0 -165
- package/dist/agents/specialists/data-specialist.agent.js +0 -484
- package/dist/agents/specialists/integration-specialist.agent.d.ts +0 -227
- package/dist/agents/specialists/integration-specialist.agent.js +0 -725
- package/dist/agents/specialists/reporting-specialist.agent.d.ts +0 -54
- package/dist/agents/specialists/reporting-specialist.agent.js +0 -193
- package/dist/agents/specialists/security-specialist.agent.d.ts +0 -85
- package/dist/agents/specialists/security-specialist.agent.js +0 -309
- package/dist/agents/teams/adaptive/adaptive-coordinator.agent.d.ts +0 -111
- package/dist/agents/teams/adaptive/adaptive-coordinator.agent.js +0 -383
- package/dist/agents/teams/base-team.d.ts +0 -72
- package/dist/agents/teams/base-team.js +0 -253
- package/dist/agents/teams/team-types.d.ts +0 -265
- package/dist/agents/teams/team-types.js +0 -3
- package/dist/agents/teams/widget/widget-architect.agent.d.ts +0 -61
- package/dist/agents/teams/widget/widget-architect.agent.js +0 -436
- package/dist/agents/teams/widget/widget-backend.agent.d.ts +0 -74
- package/dist/agents/teams/widget/widget-backend.agent.js +0 -1531
- package/dist/agents/teams/widget/widget-frontend.agent.d.ts +0 -67
- package/dist/agents/teams/widget/widget-frontend.agent.js +0 -1014
- package/dist/agents/teams/widget/widget-platform.agent.d.ts +0 -90
- package/dist/agents/teams/widget/widget-platform.agent.js +0 -1190
- package/dist/agents/teams/widget/widget-uiux.agent.d.ts +0 -107
- package/dist/agents/teams/widget/widget-uiux.agent.js +0 -1033
- package/dist/agents/test-agent.d.ts +0 -26
- package/dist/agents/test-agent.js +0 -734
- package/dist/agents/test-agents.d.ts +0 -7
- package/dist/agents/test-agents.js +0 -64
- package/dist/agents/ui-builder.agent.d.ts +0 -50
- package/dist/agents/ui-builder.agent.js +0 -1539
- package/dist/agents/widget-builder.agent.d.ts +0 -32
- package/dist/agents/widget-builder.agent.js +0 -439
- package/dist/agents/widget-creator-agent.d.ts +0 -18
- package/dist/agents/widget-creator-agent.js +0 -498
- package/dist/agents/workflow-designer.agent.d.ts +0 -31
- package/dist/agents/workflow-designer.agent.js +0 -583
- package/dist/api/error-handling.d.ts +0 -325
- package/dist/api/error-handling.js +0 -1488
- package/dist/api/flow-designer-api.d.ts +0 -167
- package/dist/api/flow-designer-api.js +0 -554
- package/dist/api/natural-language-mapper.d.ts +0 -124
- package/dist/api/natural-language-mapper.js +0 -501
- package/dist/api/performance-optimizer.d.ts +0 -240
- package/dist/api/performance-optimizer.js +0 -706
- package/dist/api/transaction-manager.d.ts +0 -189
- package/dist/api/transaction-manager.js +0 -576
- package/dist/assembly/team-assembler.d.ts +0 -140
- package/dist/assembly/team-assembler.js +0 -502
- package/dist/benchmark-performance.d.ts +0 -41
- package/dist/benchmark-performance.js +0 -230
- package/dist/cli/snow-flow-cli-integration.d.ts +0 -30
- package/dist/cli/snow-flow-cli-integration.js +0 -396
- package/dist/coordination/coordination-engine.d.ts +0 -41
- package/dist/coordination/coordination-engine.js +0 -324
- package/dist/coordination/coordination.test.d.ts +0 -6
- package/dist/coordination/example.d.ts +0 -31
- package/dist/coordination/example.js +0 -394
- package/dist/coordination/execution-patterns.d.ts +0 -43
- package/dist/coordination/execution-patterns.js +0 -507
- package/dist/coordination/factory.d.ts +0 -94
- package/dist/coordination/factory.js +0 -433
- package/dist/coordination/index.d.ts +0 -71
- package/dist/coordination/index.js +0 -135
- package/dist/coordination/progress-monitor.d.ts +0 -71
- package/dist/coordination/progress-monitor.js +0 -505
- package/dist/coordination/quality-gates.d.ts +0 -124
- package/dist/coordination/quality-gates.js +0 -577
- package/dist/coordination/shared-memory.d.ts +0 -39
- package/dist/coordination/shared-memory.js +0 -289
- package/dist/coordination/task-dependencies.d.ts +0 -50
- package/dist/coordination/task-dependencies.js +0 -407
- package/dist/coordination/team-coordinator.d.ts +0 -59
- package/dist/coordination/team-coordinator.js +0 -550
- package/dist/coordination/types.d.ts +0 -152
- package/dist/coordination/types.js +0 -3
- package/dist/mcp/mcp-wrapper.d.ts +0 -8
- package/dist/mcp/mcp-wrapper.js +0 -77
- package/dist/mcp/servicenow-flow-composer-enhanced.js +0 -396
- package/dist/mcp/servicenow-flow-composer-mcp.d.ts +0 -7
- package/dist/mcp/servicenow-flow-composer-mcp.js +0 -3103
- package/dist/mcp/servicenow-graph-memory-mcp.d.ts +0 -7
- package/dist/mcp/servicenow-intelligent-mcp.d.ts +0 -143
- package/dist/mcp/servicenow-intelligent-mcp.js +0 -4156
- package/dist/mcp/servicenow-xml-flow-mcp.d.ts +0 -53
- package/dist/mcp/servicenow-xml-flow-mcp.js +0 -1704
- package/dist/mcp/singleton-check.js +0 -31
- package/dist/mcp/test-simple-mcp.d.ts +0 -6
- package/dist/mcp/test-simple-mcp.js +0 -63
- package/dist/memory/hierarchical-memory-system.d.ts +0 -90
- package/dist/memory/hierarchical-memory-system.js +0 -400
- package/dist/memory/index.d.ts +0 -9
- package/dist/memory/index.js +0 -20
- package/dist/memory/mcp-integration-example.d.ts +0 -6
- package/dist/memory/mcp-integration-example.js +0 -281
- package/dist/memory/memory-client.d.ts +0 -199
- package/dist/memory/memory-client.js +0 -364
- package/dist/memory/memory-manager.d.ts +0 -29
- package/dist/memory/memory-manager.js +0 -280
- package/dist/memory/memory-operations.d.ts +0 -179
- package/dist/memory/memory-operations.js +0 -691
- package/dist/memory/memory-system.d.ts +0 -153
- package/dist/memory/memory-system.js +0 -651
- package/dist/memory/memory-test.d.ts +0 -6
- package/dist/memory/memory-test.js +0 -161
- package/dist/memory/snow-flow-memory-patterns.d.ts +0 -213
- package/dist/memory/snow-flow-memory-patterns.js +0 -200
- package/dist/memory/snow-memory.d.ts +0 -54
- package/dist/memory/snow-memory.js +0 -301
- package/dist/memory/swarm-memory.d.ts +0 -135
- package/dist/memory/swarm-memory.js +0 -378
- package/dist/optimization/flow-performance-optimizer.js +0 -723
- package/dist/orchestration/discovery/service-discovery.d.ts +0 -81
- package/dist/orchestration/discovery/service-discovery.js +0 -808
- package/dist/orchestration/distributed-orchestrator.d.ts +0 -131
- package/dist/orchestration/distributed-orchestrator.js +0 -712
- package/dist/orchestration/distribution/task-distribution-engine.d.ts +0 -53
- package/dist/orchestration/distribution/task-distribution-engine.js +0 -586
- package/dist/orchestration/flow-update-orchestrator.js +0 -648
- package/dist/orchestration/interfaces/distributed-orchestration.interface.d.ts +0 -606
- package/dist/orchestration/interfaces/distributed-orchestration.interface.js +0 -65
- package/dist/orchestration/protocols/mcpx-protocol.d.ts +0 -71
- package/dist/orchestration/protocols/mcpx-protocol.js +0 -668
- package/dist/orchestration/state/distributed-state-manager.d.ts +0 -89
- package/dist/orchestration/state/distributed-state-manager.js +0 -716
- package/dist/orchestrator/flow-composer.d.ts +0 -105
- package/dist/orchestrator/flow-composer.js +0 -404
- package/dist/orchestrator/flow-pattern-templates.d.ts +0 -260
- package/dist/orchestrator/flow-pattern-templates.js +0 -908
- package/dist/orchestrator/flow-subflow-decision-engine.d.ts +0 -202
- package/dist/orchestrator/flow-subflow-decision-engine.js +0 -769
- package/dist/orchestrator/flow-validation-engine.d.ts +0 -169
- package/dist/orchestrator/flow-validation-engine.js +0 -913
- package/dist/orchestrator/snow-orchestrator.d.ts +0 -49
- package/dist/orchestrator/snow-orchestrator.js +0 -235
- package/dist/orchestrator/subflow-creation-handler.d.ts +0 -230
- package/dist/orchestrator/subflow-creation-handler.js +0 -791
- package/dist/patterns/adaptive-patterns.d.ts +0 -140
- package/dist/patterns/adaptive-patterns.js +0 -549
- package/dist/queen/advanced-integration-test.d.ts +0 -48
- package/dist/queen/advanced-integration-test.js +0 -473
- package/dist/queen/integration-example.d.ts +0 -82
- package/dist/queen/integration-example.js +0 -219
- package/dist/queen/integration-test.d.ts +0 -72
- package/dist/queen/integration-test.js +0 -563
- package/dist/queen/mock-mcp-client.d.ts +0 -71
- package/dist/queen/mock-mcp-client.js +0 -317
- package/dist/queen/run-integration-tests.d.ts +0 -9
- package/dist/queen/run-integration-tests.js +0 -27
- package/dist/sparc/coordinators/adaptive-coordinator.d.ts +0 -54
- package/dist/sparc/coordinators/adaptive-coordinator.js +0 -367
- package/dist/sparc/coordinators/team-coordinator.d.ts +0 -66
- package/dist/sparc/coordinators/team-coordinator.js +0 -402
- package/dist/sparc/modes/team-modes.d.ts +0 -33
- package/dist/sparc/modes/team-modes.js +0 -399
- package/dist/sparc/teams/application-team.d.ts +0 -73
- package/dist/sparc/teams/application-team.js +0 -463
- package/dist/sparc/teams/flow-team.d.ts +0 -67
- package/dist/sparc/teams/flow-team.js +0 -373
- package/dist/sparc/teams/widget-team.d.ts +0 -61
- package/dist/sparc/teams/widget-team.js +0 -280
- package/dist/specialists/app-specialists.d.ts +0 -93
- package/dist/specialists/app-specialists.js +0 -1639
- package/dist/specialists/base-specialist.d.ts +0 -19
- package/dist/specialists/base-specialist.js +0 -54
- package/dist/specialists/flow-specialists.d.ts +0 -113
- package/dist/specialists/flow-specialists.js +0 -1787
- package/dist/specialists/individual-specialists.d.ts +0 -67
- package/dist/specialists/individual-specialists.js +0 -632
- package/dist/specialists/widget-specialists.d.ts +0 -70
- package/dist/specialists/widget-specialists.js +0 -1264
- package/dist/src/utils/agent-detector.js +0 -655
- package/dist/teams/adaptive-team.d.ts +0 -81
- package/dist/teams/adaptive-team.js +0 -1153
- package/dist/teams/application-team.d.ts +0 -30
- package/dist/teams/application-team.js +0 -547
- package/dist/teams/base-team.d.ts +0 -55
- package/dist/teams/base-team.js +0 -243
- package/dist/teams/flow-team.d.ts +0 -28
- package/dist/teams/flow-team.js +0 -525
- package/dist/teams/widget-team.d.ts +0 -26
- package/dist/teams/widget-team.js +0 -459
- package/dist/templates/flow-template-system.js +0 -625
- package/dist/test-fetch-incidents.d.ts +0 -2
- package/dist/test-fetch-incidents.js +0 -84
- package/dist/test-incident-access.d.ts +0 -2
- package/dist/test-incident-access.js +0 -72
- package/dist/test-iphone-flow.js +0 -189
- package/dist/test-limit-problem.d.ts +0 -2
- package/dist/test-limit-problem.js +0 -60
- package/dist/test-ml-batch-fix.d.ts +0 -2
- package/dist/test-ml-batch-fix.js +0 -104
- package/dist/test-ml-batch.d.ts +0 -2
- package/dist/test-ml-batch.js +0 -87
- package/dist/test-ml-query.d.ts +0 -2
- package/dist/test-ml-query.js +0 -100
- package/dist/test-ml-training-fix.d.ts +0 -12
- package/dist/test-ml-training-fix.js +0 -113
- package/dist/test-ml-training.d.ts +0 -2
- package/dist/test-ml-training.js +0 -32
- package/dist/test-operations-query.d.ts +0 -2
- package/dist/test-operations-query.js +0 -84
- package/dist/test-performance-query.d.ts +0 -2
- package/dist/test-performance-query.js +0 -52
- package/dist/test-singleton-protection.d.ts +0 -8
- package/dist/test-singleton-protection.js +0 -110
- package/dist/test-smart-limits.d.ts +0 -13
- package/dist/test-smart-limits.js +0 -100
- package/dist/test-snow-query-incidents.d.ts +0 -2
- package/dist/test-snow-query-incidents.js +0 -66
- package/dist/test-universal-query.d.ts +0 -2
- package/dist/test-universal-query.js +0 -106
- package/dist/testing/flow-testing-automation.js +0 -641
- package/dist/utils/complete-flow-xml-generator.js +0 -1285
- package/dist/utils/file-storage-fallback.d.ts +0 -62
- package/dist/utils/file-storage-fallback.js +0 -289
- package/dist/utils/flow-api-discovery.d.ts +0 -151
- package/dist/utils/flow-api-discovery.js +0 -439
- package/dist/utils/flow-examples.js +0 -720
- package/dist/utils/flow-structure-builder.js +0 -677
- package/dist/utils/flow-xml-generator.d.ts +0 -41
- package/dist/utils/flow-xml-generator.js +0 -258
- package/dist/utils/improved-flow-xml-generator.js +0 -820
- package/dist/utils/mcp-process-manager.backup.d.ts +0 -58
- package/dist/utils/mcp-process-manager.backup.js +0 -220
- package/dist/utils/mcp-singleton-enforcer.d.ts +0 -36
- package/dist/utils/mcp-singleton-enforcer.js +0 -201
- package/dist/utils/mcp-timeout-fix.d.ts +0 -53
- package/dist/utils/mcp-timeout-fix.js +0 -221
- package/dist/utils/migrate-claude-flow.d.ts +0 -22
- package/dist/utils/migrate-claude-flow.js +0 -120
- package/dist/utils/scope-utils.d.ts +0 -72
- package/dist/utils/scope-utils.js +0 -386
- package/dist/utils/snow-oauth-old.d.ts +0 -65
- package/dist/utils/snow-oauth-old.js +0 -345
- package/dist/utils/xml-first-flow-generator.js +0 -513
- package/memory/servicenow_artifacts/000d9224c895221055906c7518aa2d3d.json +0 -30
- package/memory/servicenow_artifacts/0196b66173303010e46b4a2214f6a7a2.json +0 -36
- package/memory/servicenow_artifacts/125e5d1d837e2a102a7ea130ceaad397.json +0 -30
- package/memory/servicenow_artifacts/7637c1f2b7112210a5e5911cde11a972.json +0 -30
- package/memory/servicenow_artifacts/default_documentation_tables_1754056582292.json +0 -55
- package/memory/servicenow_artifacts/default_documentation_tables_1754057477189.json +0 -55
- package/memory/update-set-sessions/01f82af583faea102a7ea130ceaad3d4.json +0 -9
- package/memory/update-set-sessions/27718fb983faea102a7ea130ceaad34b.json +0 -9
- package/memory/update-set-sessions/412e9bf6833e22502a7ea130ceaad30a.json +0 -8
- package/memory/update-set-sessions/66fc5a79837aea102a7ea130ceaad3ab.json +0 -9
- package/memory/update-set-sessions/71a30ff5837eea102a7ea130ceaad37f.json +0 -9
- package/memory/update-set-sessions/74fba27983faea102a7ea130ceaad3bd.json +0 -9
- package/memory/update-set-sessions/a0b147b5837eea102a7ea130ceaad344.json +0 -58
- package/memory/update-set-sessions/b13f5eb983baea102a7ea130ceaad33e.json +0 -9
- package/reports/swarm-auto-centralized-1752649029776.json +0 -13
- package/src/memory/README.md +0 -240
|
@@ -1,1488 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Comprehensive Error Handling and Rollback System
|
|
4
|
-
* Advanced error handling with automatic rollback capabilities
|
|
5
|
-
*/
|
|
6
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.FlowErrorHandler = exports.ErrorSeverity = exports.ErrorType = void 0;
|
|
8
|
-
const logger_js_1 = require("../utils/logger.js");
|
|
9
|
-
var ErrorType;
|
|
10
|
-
(function (ErrorType) {
|
|
11
|
-
ErrorType["VALIDATION_ERROR"] = "validation_error";
|
|
12
|
-
ErrorType["API_ERROR"] = "api_error";
|
|
13
|
-
ErrorType["AUTHENTICATION_ERROR"] = "authentication_error";
|
|
14
|
-
ErrorType["AUTHORIZATION_ERROR"] = "authorization_error";
|
|
15
|
-
ErrorType["NETWORK_ERROR"] = "network_error";
|
|
16
|
-
ErrorType["TIMEOUT_ERROR"] = "timeout_error";
|
|
17
|
-
ErrorType["RATE_LIMIT_ERROR"] = "rate_limit_error";
|
|
18
|
-
ErrorType["RESOURCE_NOT_FOUND"] = "resource_not_found";
|
|
19
|
-
ErrorType["CONFLICT_ERROR"] = "conflict_error";
|
|
20
|
-
ErrorType["SERVICE_UNAVAILABLE"] = "service_unavailable";
|
|
21
|
-
ErrorType["FLOW_LOGIC_ERROR"] = "flow_logic_error";
|
|
22
|
-
ErrorType["ROLLBACK_ERROR"] = "rollback_error";
|
|
23
|
-
ErrorType["UNKNOWN_ERROR"] = "unknown_error";
|
|
24
|
-
})(ErrorType || (exports.ErrorType = ErrorType = {}));
|
|
25
|
-
var ErrorSeverity;
|
|
26
|
-
(function (ErrorSeverity) {
|
|
27
|
-
ErrorSeverity["CRITICAL"] = "critical";
|
|
28
|
-
ErrorSeverity["HIGH"] = "high";
|
|
29
|
-
ErrorSeverity["MEDIUM"] = "medium";
|
|
30
|
-
ErrorSeverity["LOW"] = "low";
|
|
31
|
-
})(ErrorSeverity || (exports.ErrorSeverity = ErrorSeverity = {}));
|
|
32
|
-
class FlowErrorHandler {
|
|
33
|
-
constructor(client) {
|
|
34
|
-
this.transactions = new Map();
|
|
35
|
-
this.errorHistory = [];
|
|
36
|
-
this.maxErrorHistory = 1000;
|
|
37
|
-
this.retryDelays = [1000, 2000, 5000, 10000, 30000]; // Exponential backoff
|
|
38
|
-
this.client = client;
|
|
39
|
-
this.logger = new logger_js_1.Logger('FlowErrorHandler');
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* Handle an error with automatic recovery
|
|
43
|
-
*/
|
|
44
|
-
async handleError(error, context) {
|
|
45
|
-
const flowError = this.classifyError(error, context);
|
|
46
|
-
this.errorHistory.push(flowError);
|
|
47
|
-
// Limit error history size
|
|
48
|
-
if (this.errorHistory.length > this.maxErrorHistory) {
|
|
49
|
-
this.errorHistory.shift();
|
|
50
|
-
}
|
|
51
|
-
this.logger.error('Flow error occurred', {
|
|
52
|
-
type: flowError.type,
|
|
53
|
-
severity: flowError.severity,
|
|
54
|
-
message: flowError.message,
|
|
55
|
-
context: flowError.context
|
|
56
|
-
});
|
|
57
|
-
// Determine recovery strategy
|
|
58
|
-
const recoveryActions = this.determineRecoveryActions(flowError);
|
|
59
|
-
const result = {
|
|
60
|
-
success: false,
|
|
61
|
-
recovered: false,
|
|
62
|
-
actions_taken: []
|
|
63
|
-
};
|
|
64
|
-
// Execute recovery actions
|
|
65
|
-
for (const action of recoveryActions) {
|
|
66
|
-
try {
|
|
67
|
-
const actionResult = await this.executeRecoveryAction(action, flowError);
|
|
68
|
-
result.actions_taken.push(action);
|
|
69
|
-
if (actionResult.success) {
|
|
70
|
-
result.success = true;
|
|
71
|
-
result.recovered = true;
|
|
72
|
-
result.final_state = actionResult.final_state;
|
|
73
|
-
break;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
catch (recoveryError) {
|
|
77
|
-
this.logger.error('Recovery action failed', {
|
|
78
|
-
action: action.type,
|
|
79
|
-
error: recoveryError
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
// Generate user-friendly message
|
|
84
|
-
result.user_message = this.generateUserMessage(flowError, result);
|
|
85
|
-
result.technical_details = this.generateTechnicalDetails(flowError, result);
|
|
86
|
-
return result;
|
|
87
|
-
}
|
|
88
|
-
/**
|
|
89
|
-
* Start a new transaction
|
|
90
|
-
*/
|
|
91
|
-
startTransaction(flowId) {
|
|
92
|
-
const transactionId = this.generateTransactionId();
|
|
93
|
-
const transaction = {
|
|
94
|
-
id: transactionId,
|
|
95
|
-
flow_id: flowId,
|
|
96
|
-
operations: [],
|
|
97
|
-
status: 'active',
|
|
98
|
-
start_time: new Date(),
|
|
99
|
-
checkpoints: []
|
|
100
|
-
};
|
|
101
|
-
this.transactions.set(transactionId, transaction);
|
|
102
|
-
this.logger.info('Transaction started', { transactionId, flowId });
|
|
103
|
-
return transactionId;
|
|
104
|
-
}
|
|
105
|
-
/**
|
|
106
|
-
* Add operation to transaction
|
|
107
|
-
*/
|
|
108
|
-
addOperation(transactionId, operation) {
|
|
109
|
-
const transaction = this.transactions.get(transactionId);
|
|
110
|
-
if (!transaction) {
|
|
111
|
-
throw new Error(`Transaction ${transactionId} not found`);
|
|
112
|
-
}
|
|
113
|
-
const rollbackOperation = {
|
|
114
|
-
...operation,
|
|
115
|
-
id: this.generateOperationId(),
|
|
116
|
-
timestamp: new Date()
|
|
117
|
-
};
|
|
118
|
-
transaction.operations.push(rollbackOperation);
|
|
119
|
-
this.logger.debug('Operation added to transaction', { transactionId, operationId: rollbackOperation.id });
|
|
120
|
-
}
|
|
121
|
-
/**
|
|
122
|
-
* Create checkpoint in transaction
|
|
123
|
-
*/
|
|
124
|
-
createCheckpoint(transactionId, description) {
|
|
125
|
-
const transaction = this.transactions.get(transactionId);
|
|
126
|
-
if (!transaction) {
|
|
127
|
-
throw new Error(`Transaction ${transactionId} not found`);
|
|
128
|
-
}
|
|
129
|
-
const checkpoint = {
|
|
130
|
-
id: this.generateCheckpointId(),
|
|
131
|
-
timestamp: new Date(),
|
|
132
|
-
operation_count: transaction.operations.length,
|
|
133
|
-
state_snapshot: this.captureStateSnapshot(transaction),
|
|
134
|
-
description
|
|
135
|
-
};
|
|
136
|
-
transaction.checkpoints.push(checkpoint);
|
|
137
|
-
this.logger.info('Checkpoint created', { transactionId, checkpointId: checkpoint.id, description });
|
|
138
|
-
return checkpoint.id;
|
|
139
|
-
}
|
|
140
|
-
/**
|
|
141
|
-
* Commit transaction
|
|
142
|
-
*/
|
|
143
|
-
async commitTransaction(transactionId) {
|
|
144
|
-
const transaction = this.transactions.get(transactionId);
|
|
145
|
-
if (!transaction) {
|
|
146
|
-
throw new Error(`Transaction ${transactionId} not found`);
|
|
147
|
-
}
|
|
148
|
-
transaction.status = 'committed';
|
|
149
|
-
transaction.end_time = new Date();
|
|
150
|
-
this.logger.info('Transaction committed', {
|
|
151
|
-
transactionId,
|
|
152
|
-
operationCount: transaction.operations.length,
|
|
153
|
-
duration: transaction.end_time.getTime() - transaction.start_time.getTime()
|
|
154
|
-
});
|
|
155
|
-
// Clean up transaction after some time
|
|
156
|
-
setTimeout(() => {
|
|
157
|
-
this.transactions.delete(transactionId);
|
|
158
|
-
}, 300000); // 5 minutes
|
|
159
|
-
}
|
|
160
|
-
/**
|
|
161
|
-
* Rollback transaction
|
|
162
|
-
*/
|
|
163
|
-
async rollbackTransaction(transactionId, toCheckpoint) {
|
|
164
|
-
const transaction = this.transactions.get(transactionId);
|
|
165
|
-
if (!transaction) {
|
|
166
|
-
throw new Error(`Transaction ${transactionId} not found`);
|
|
167
|
-
}
|
|
168
|
-
this.logger.info('Starting transaction rollback', { transactionId, toCheckpoint });
|
|
169
|
-
let operationsToRollback = transaction.operations.slice();
|
|
170
|
-
// If rolling back to a checkpoint, only rollback operations after that checkpoint
|
|
171
|
-
if (toCheckpoint) {
|
|
172
|
-
const checkpoint = transaction.checkpoints.find(cp => cp.id === toCheckpoint);
|
|
173
|
-
if (checkpoint) {
|
|
174
|
-
operationsToRollback = transaction.operations.slice(checkpoint.operation_count);
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
// Rollback operations in reverse order
|
|
178
|
-
operationsToRollback.reverse();
|
|
179
|
-
const rollbackErrors = [];
|
|
180
|
-
for (const operation of operationsToRollback) {
|
|
181
|
-
try {
|
|
182
|
-
await this.rollbackOperation(operation);
|
|
183
|
-
this.logger.debug('Operation rolled back', { operationId: operation.id });
|
|
184
|
-
}
|
|
185
|
-
catch (error) {
|
|
186
|
-
rollbackErrors.push(error instanceof Error ? error : new Error(String(error)));
|
|
187
|
-
this.logger.error('Rollback operation failed', { operationId: operation.id, error });
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
transaction.status = rollbackErrors.length > 0 ? 'failed' : 'rolled_back';
|
|
191
|
-
transaction.end_time = new Date();
|
|
192
|
-
if (rollbackErrors.length > 0) {
|
|
193
|
-
throw new Error(`Rollback failed with ${rollbackErrors.length} errors: ${rollbackErrors.map(e => e.message).join(', ')}`);
|
|
194
|
-
}
|
|
195
|
-
this.logger.info('Transaction rollback completed', { transactionId });
|
|
196
|
-
}
|
|
197
|
-
/**
|
|
198
|
-
* Rollback a single operation
|
|
199
|
-
*/
|
|
200
|
-
async rollbackOperation(operation) {
|
|
201
|
-
switch (operation.type) {
|
|
202
|
-
case 'create':
|
|
203
|
-
// Delete the created record
|
|
204
|
-
if (operation.record_id) {
|
|
205
|
-
await this.client.deleteRecord(operation.table, operation.record_id);
|
|
206
|
-
}
|
|
207
|
-
break;
|
|
208
|
-
case 'update':
|
|
209
|
-
// Restore original data
|
|
210
|
-
if (operation.record_id && operation.original_data) {
|
|
211
|
-
await this.client.updateRecord(operation.table, operation.record_id, operation.original_data);
|
|
212
|
-
}
|
|
213
|
-
break;
|
|
214
|
-
case 'delete':
|
|
215
|
-
// Recreate the deleted record
|
|
216
|
-
if (operation.original_data) {
|
|
217
|
-
await this.client.createRecord(operation.table, operation.original_data);
|
|
218
|
-
}
|
|
219
|
-
break;
|
|
220
|
-
}
|
|
221
|
-
// Execute custom rollback function if provided
|
|
222
|
-
if (operation.rollback_function) {
|
|
223
|
-
await operation.rollback_function();
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
/**
|
|
227
|
-
* Classify error type and severity
|
|
228
|
-
*/
|
|
229
|
-
classifyError(error, context) {
|
|
230
|
-
let type = ErrorType.UNKNOWN_ERROR;
|
|
231
|
-
let severity = ErrorSeverity.MEDIUM;
|
|
232
|
-
let recoverable = true;
|
|
233
|
-
let suggestedActions = [];
|
|
234
|
-
const errorMessage = error.message.toLowerCase();
|
|
235
|
-
// Classify by error message patterns
|
|
236
|
-
if (errorMessage.includes('validation') || errorMessage.includes('invalid')) {
|
|
237
|
-
type = ErrorType.VALIDATION_ERROR;
|
|
238
|
-
severity = ErrorSeverity.HIGH;
|
|
239
|
-
suggestedActions = [
|
|
240
|
-
{ type: 'fallback', description: 'Use fallback values', priority: 'high' },
|
|
241
|
-
{ type: 'notify', description: 'Notify user of validation issues', priority: 'medium' }
|
|
242
|
-
];
|
|
243
|
-
}
|
|
244
|
-
else if (errorMessage.includes('unauthorized') || errorMessage.includes('authentication')) {
|
|
245
|
-
type = ErrorType.AUTHENTICATION_ERROR;
|
|
246
|
-
severity = ErrorSeverity.CRITICAL;
|
|
247
|
-
recoverable = false;
|
|
248
|
-
suggestedActions = [
|
|
249
|
-
{ type: 'notify', description: 'Request user to re-authenticate', priority: 'high' }
|
|
250
|
-
];
|
|
251
|
-
}
|
|
252
|
-
else if (errorMessage.includes('forbidden') || errorMessage.includes('access denied')) {
|
|
253
|
-
type = ErrorType.AUTHORIZATION_ERROR;
|
|
254
|
-
severity = ErrorSeverity.HIGH;
|
|
255
|
-
recoverable = false;
|
|
256
|
-
suggestedActions = [
|
|
257
|
-
{ type: 'notify', description: 'Inform user of insufficient permissions', priority: 'high' }
|
|
258
|
-
];
|
|
259
|
-
}
|
|
260
|
-
else if (errorMessage.includes('network') || errorMessage.includes('connection')) {
|
|
261
|
-
type = ErrorType.NETWORK_ERROR;
|
|
262
|
-
severity = ErrorSeverity.MEDIUM;
|
|
263
|
-
suggestedActions = [
|
|
264
|
-
{ type: 'retry', description: 'Retry with exponential backoff', priority: 'high' },
|
|
265
|
-
{ type: 'fallback', description: 'Use cached data if available', priority: 'medium' }
|
|
266
|
-
];
|
|
267
|
-
}
|
|
268
|
-
else if (errorMessage.includes('timeout')) {
|
|
269
|
-
type = ErrorType.TIMEOUT_ERROR;
|
|
270
|
-
severity = ErrorSeverity.MEDIUM;
|
|
271
|
-
suggestedActions = [
|
|
272
|
-
{ type: 'retry', description: 'Retry with increased timeout', priority: 'high' }
|
|
273
|
-
];
|
|
274
|
-
}
|
|
275
|
-
else if (errorMessage.includes('rate limit') || errorMessage.includes('too many requests')) {
|
|
276
|
-
type = ErrorType.RATE_LIMIT_ERROR;
|
|
277
|
-
severity = ErrorSeverity.LOW;
|
|
278
|
-
suggestedActions = [
|
|
279
|
-
{ type: 'retry', description: 'Retry after delay', priority: 'high', parameters: { delay: 60000 } }
|
|
280
|
-
];
|
|
281
|
-
}
|
|
282
|
-
else if (errorMessage.includes('not found') || errorMessage.includes('404')) {
|
|
283
|
-
type = ErrorType.RESOURCE_NOT_FOUND;
|
|
284
|
-
severity = ErrorSeverity.HIGH;
|
|
285
|
-
suggestedActions = [
|
|
286
|
-
{ type: 'fallback', description: 'Create missing resource', priority: 'high' },
|
|
287
|
-
{ type: 'notify', description: 'Inform user of missing resource', priority: 'medium' }
|
|
288
|
-
];
|
|
289
|
-
}
|
|
290
|
-
else if (errorMessage.includes('conflict') || errorMessage.includes('409')) {
|
|
291
|
-
type = ErrorType.CONFLICT_ERROR;
|
|
292
|
-
severity = ErrorSeverity.MEDIUM;
|
|
293
|
-
suggestedActions = [
|
|
294
|
-
{ type: 'retry', description: 'Retry after resolving conflict', priority: 'high' },
|
|
295
|
-
{ type: 'fallback', description: 'Use alternative approach', priority: 'medium' }
|
|
296
|
-
];
|
|
297
|
-
}
|
|
298
|
-
else if (errorMessage.includes('service unavailable') || errorMessage.includes('503')) {
|
|
299
|
-
type = ErrorType.SERVICE_UNAVAILABLE;
|
|
300
|
-
severity = ErrorSeverity.HIGH;
|
|
301
|
-
suggestedActions = [
|
|
302
|
-
{ type: 'retry', description: 'Retry after service recovery', priority: 'high' },
|
|
303
|
-
{ type: 'notify', description: 'Inform user of service unavailability', priority: 'medium' }
|
|
304
|
-
];
|
|
305
|
-
}
|
|
306
|
-
// Determine if rollback is needed
|
|
307
|
-
if (context.flow_id && ['create', 'update', 'delete'].includes(context.operation)) {
|
|
308
|
-
suggestedActions.unshift({
|
|
309
|
-
type: 'rollback',
|
|
310
|
-
description: 'Rollback partial changes',
|
|
311
|
-
priority: 'high'
|
|
312
|
-
});
|
|
313
|
-
}
|
|
314
|
-
return {
|
|
315
|
-
type,
|
|
316
|
-
severity,
|
|
317
|
-
message: error.message,
|
|
318
|
-
context,
|
|
319
|
-
original_error: error,
|
|
320
|
-
recoverable,
|
|
321
|
-
suggested_actions: suggestedActions,
|
|
322
|
-
retry_count: 0,
|
|
323
|
-
max_retries: this.getMaxRetries(type)
|
|
324
|
-
};
|
|
325
|
-
}
|
|
326
|
-
/**
|
|
327
|
-
* Determine recovery actions based on error
|
|
328
|
-
*/
|
|
329
|
-
determineRecoveryActions(error) {
|
|
330
|
-
const actions = [];
|
|
331
|
-
// Sort suggested actions by priority
|
|
332
|
-
const sortedActions = error.suggested_actions.sort((a, b) => {
|
|
333
|
-
const priorityOrder = { high: 3, medium: 2, low: 1 };
|
|
334
|
-
return priorityOrder[b.priority] - priorityOrder[a.priority];
|
|
335
|
-
});
|
|
336
|
-
// Add retry logic for recoverable errors
|
|
337
|
-
if (error.recoverable && error.retry_count < error.max_retries) {
|
|
338
|
-
actions.push({
|
|
339
|
-
type: 'retry',
|
|
340
|
-
description: `Retry operation (attempt ${error.retry_count + 1}/${error.max_retries})`,
|
|
341
|
-
priority: 'high',
|
|
342
|
-
parameters: {
|
|
343
|
-
delay: this.retryDelays[Math.min(error.retry_count, this.retryDelays.length - 1)]
|
|
344
|
-
}
|
|
345
|
-
});
|
|
346
|
-
}
|
|
347
|
-
// Add suggested actions
|
|
348
|
-
actions.push(...sortedActions);
|
|
349
|
-
return actions;
|
|
350
|
-
}
|
|
351
|
-
/**
|
|
352
|
-
* Execute a recovery action
|
|
353
|
-
*/
|
|
354
|
-
async executeRecoveryAction(action, error) {
|
|
355
|
-
switch (action.type) {
|
|
356
|
-
case 'retry':
|
|
357
|
-
return await this.executeRetry(action, error);
|
|
358
|
-
case 'rollback':
|
|
359
|
-
return await this.executeRollback(action, error);
|
|
360
|
-
case 'fallback':
|
|
361
|
-
return await this.executeFallback(action, error);
|
|
362
|
-
case 'skip':
|
|
363
|
-
return await this.executeSkip(action, error);
|
|
364
|
-
case 'notify':
|
|
365
|
-
return await this.executeNotify(action, error);
|
|
366
|
-
default:
|
|
367
|
-
return { success: false };
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
/**
|
|
371
|
-
* Execute retry action
|
|
372
|
-
*/
|
|
373
|
-
async executeRetry(action, error) {
|
|
374
|
-
const delay = action.parameters?.delay || 1000;
|
|
375
|
-
this.logger.info('Executing retry', { delay, retryCount: error.retry_count });
|
|
376
|
-
// Wait before retry
|
|
377
|
-
await new Promise(resolve => setTimeout(resolve, delay));
|
|
378
|
-
try {
|
|
379
|
-
// Retry the original operation
|
|
380
|
-
const result = await this.retryOriginalOperation(error);
|
|
381
|
-
return { success: true, final_state: result };
|
|
382
|
-
}
|
|
383
|
-
catch (retryError) {
|
|
384
|
-
error.retry_count = (error.retry_count || 0) + 1;
|
|
385
|
-
return { success: false };
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
/**
|
|
389
|
-
* Execute rollback action
|
|
390
|
-
*/
|
|
391
|
-
async executeRollback(action, error) {
|
|
392
|
-
if (!error.context.flow_id) {
|
|
393
|
-
return { success: false };
|
|
394
|
-
}
|
|
395
|
-
// Find transaction for this flow
|
|
396
|
-
const transaction = Array.from(this.transactions.values())
|
|
397
|
-
.find(t => t.flow_id === error.context.flow_id && t.status === 'active');
|
|
398
|
-
if (!transaction) {
|
|
399
|
-
return { success: false };
|
|
400
|
-
}
|
|
401
|
-
try {
|
|
402
|
-
await this.rollbackTransaction(transaction.id);
|
|
403
|
-
return { success: true, final_state: 'rolled_back' };
|
|
404
|
-
}
|
|
405
|
-
catch (rollbackError) {
|
|
406
|
-
return { success: false };
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
/**
|
|
410
|
-
* Execute fallback action
|
|
411
|
-
*/
|
|
412
|
-
async executeFallback(action, error) {
|
|
413
|
-
// Implement fallback logic based on error type
|
|
414
|
-
switch (error.type) {
|
|
415
|
-
case ErrorType.RESOURCE_NOT_FOUND:
|
|
416
|
-
return await this.createMissingResource(error);
|
|
417
|
-
case ErrorType.VALIDATION_ERROR:
|
|
418
|
-
return await this.useFallbackValues(error);
|
|
419
|
-
case ErrorType.NETWORK_ERROR:
|
|
420
|
-
return await this.useCachedData(error);
|
|
421
|
-
default:
|
|
422
|
-
return { success: false };
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
/**
|
|
426
|
-
* Execute skip action
|
|
427
|
-
*/
|
|
428
|
-
async executeSkip(action, error) {
|
|
429
|
-
this.logger.info('Skipping failed operation', { error: error.type });
|
|
430
|
-
return { success: true, final_state: 'skipped' };
|
|
431
|
-
}
|
|
432
|
-
/**
|
|
433
|
-
* Execute notify action
|
|
434
|
-
*/
|
|
435
|
-
async executeNotify(action, error) {
|
|
436
|
-
// Create notification record
|
|
437
|
-
try {
|
|
438
|
-
await this.client.createRecord('sys_email', {
|
|
439
|
-
to: 'admin@company.com',
|
|
440
|
-
subject: `Flow Error: ${error.type}`,
|
|
441
|
-
body: `An error occurred in flow operation: ${error.message}`
|
|
442
|
-
});
|
|
443
|
-
return { success: true, final_state: 'notified' };
|
|
444
|
-
}
|
|
445
|
-
catch (notifyError) {
|
|
446
|
-
return { success: false };
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
/**
|
|
450
|
-
* Retry original operation with exponential backoff
|
|
451
|
-
*/
|
|
452
|
-
async retryOriginalOperation(error) {
|
|
453
|
-
const maxRetries = error.max_retries || 3;
|
|
454
|
-
const currentRetryCount = error.retry_count || 0;
|
|
455
|
-
this.logger.info(`Attempting retry ${currentRetryCount + 1}/${maxRetries} for operation`, {
|
|
456
|
-
operation: error.context.operation,
|
|
457
|
-
error_type: error.type,
|
|
458
|
-
previous_attempts: currentRetryCount
|
|
459
|
-
});
|
|
460
|
-
// Check if we've exceeded max retries
|
|
461
|
-
if (currentRetryCount >= maxRetries) {
|
|
462
|
-
this.logger.error('Maximum retry attempts exceeded', {
|
|
463
|
-
operation: error.context.operation,
|
|
464
|
-
total_attempts: currentRetryCount,
|
|
465
|
-
final_error: error.message
|
|
466
|
-
});
|
|
467
|
-
return {
|
|
468
|
-
success: false,
|
|
469
|
-
final_state: null,
|
|
470
|
-
retry_exhausted: true,
|
|
471
|
-
total_attempts: currentRetryCount,
|
|
472
|
-
last_error: error.message,
|
|
473
|
-
suggested_actions: [
|
|
474
|
-
'Review the operation configuration',
|
|
475
|
-
'Check ServiceNow system status',
|
|
476
|
-
'Contact administrator for manual intervention',
|
|
477
|
-
'Consider alternative approach'
|
|
478
|
-
]
|
|
479
|
-
};
|
|
480
|
-
}
|
|
481
|
-
// Calculate retry delay using exponential backoff
|
|
482
|
-
const baseDelay = this.retryDelays[Math.min(currentRetryCount, this.retryDelays.length - 1)];
|
|
483
|
-
const jitterDelay = baseDelay + Math.random() * 1000; // Add jitter to prevent thundering herd
|
|
484
|
-
this.logger.info(`Waiting ${Math.round(jitterDelay)}ms before retry attempt`, {
|
|
485
|
-
retry_count: currentRetryCount + 1,
|
|
486
|
-
base_delay: baseDelay,
|
|
487
|
-
actual_delay: Math.round(jitterDelay)
|
|
488
|
-
});
|
|
489
|
-
// Wait before retry
|
|
490
|
-
await this.delay(jitterDelay);
|
|
491
|
-
try {
|
|
492
|
-
// Determine retry strategy based on error type
|
|
493
|
-
const retryResult = await this.executeRetryBasedOnErrorType(error);
|
|
494
|
-
if (retryResult.success) {
|
|
495
|
-
this.logger.info('Retry attempt successful', {
|
|
496
|
-
operation: error.context.operation,
|
|
497
|
-
retry_count: currentRetryCount + 1,
|
|
498
|
-
success_after_attempts: currentRetryCount + 1
|
|
499
|
-
});
|
|
500
|
-
return {
|
|
501
|
-
success: true,
|
|
502
|
-
final_state: retryResult.data,
|
|
503
|
-
retry_count: currentRetryCount + 1,
|
|
504
|
-
recovery_method: 'retry_with_backoff'
|
|
505
|
-
};
|
|
506
|
-
}
|
|
507
|
-
else {
|
|
508
|
-
// Update error with new retry count and continue retry cycle
|
|
509
|
-
const updatedError = {
|
|
510
|
-
...error,
|
|
511
|
-
retry_count: currentRetryCount + 1,
|
|
512
|
-
message: retryResult.error || error.message
|
|
513
|
-
};
|
|
514
|
-
// Recursively retry if not at max attempts
|
|
515
|
-
return await this.retryOriginalOperation(updatedError);
|
|
516
|
-
}
|
|
517
|
-
}
|
|
518
|
-
catch (retryError) {
|
|
519
|
-
this.logger.error('Error during retry attempt', {
|
|
520
|
-
retry_count: currentRetryCount + 1,
|
|
521
|
-
error: retryError instanceof Error ? retryError.message : String(retryError)
|
|
522
|
-
});
|
|
523
|
-
// Create updated error for next retry or final failure
|
|
524
|
-
const updatedError = {
|
|
525
|
-
...error,
|
|
526
|
-
retry_count: currentRetryCount + 1,
|
|
527
|
-
message: retryError instanceof Error ? retryError.message : String(retryError),
|
|
528
|
-
original_error: retryError instanceof Error ? retryError : new Error(String(retryError))
|
|
529
|
-
};
|
|
530
|
-
// Continue retry cycle
|
|
531
|
-
return await this.retryOriginalOperation(updatedError);
|
|
532
|
-
}
|
|
533
|
-
}
|
|
534
|
-
/**
|
|
535
|
-
* Execute retry based on specific error type
|
|
536
|
-
*/
|
|
537
|
-
async executeRetryBasedOnErrorType(error) {
|
|
538
|
-
try {
|
|
539
|
-
switch (error.type) {
|
|
540
|
-
case ErrorType.AUTHENTICATION_ERROR:
|
|
541
|
-
return await this.retryAuthenticationOperation(error);
|
|
542
|
-
case ErrorType.NETWORK_ERROR:
|
|
543
|
-
case ErrorType.TIMEOUT_ERROR:
|
|
544
|
-
return await this.retryNetworkOperation(error);
|
|
545
|
-
case ErrorType.RATE_LIMIT_ERROR:
|
|
546
|
-
return await this.retryRateLimitedOperation(error);
|
|
547
|
-
case ErrorType.SERVICE_UNAVAILABLE:
|
|
548
|
-
return await this.retryServiceUnavailableOperation(error);
|
|
549
|
-
case ErrorType.API_ERROR:
|
|
550
|
-
return await this.retryAPIOperation(error);
|
|
551
|
-
case ErrorType.CONFLICT_ERROR:
|
|
552
|
-
return await this.retryConflictOperation(error);
|
|
553
|
-
case ErrorType.RESOURCE_NOT_FOUND:
|
|
554
|
-
return await this.retryResourceOperation(error);
|
|
555
|
-
default:
|
|
556
|
-
return await this.retryGenericOperation(error);
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
|
-
catch (executeError) {
|
|
560
|
-
return {
|
|
561
|
-
success: false,
|
|
562
|
-
error: executeError instanceof Error ? executeError.message : String(executeError)
|
|
563
|
-
};
|
|
564
|
-
}
|
|
565
|
-
}
|
|
566
|
-
/**
|
|
567
|
-
* Retry authentication operations
|
|
568
|
-
*/
|
|
569
|
-
async retryAuthenticationOperation(error) {
|
|
570
|
-
try {
|
|
571
|
-
this.logger.info('Retrying authentication operation');
|
|
572
|
-
// Try to refresh authentication
|
|
573
|
-
const credentials = this.client.credentialsInstance;
|
|
574
|
-
if (credentials?.refreshToken) {
|
|
575
|
-
// Attempt token refresh
|
|
576
|
-
const refreshResult = await this.client.oauth?.refreshAccessToken();
|
|
577
|
-
if (refreshResult?.success) {
|
|
578
|
-
// Retry the original operation with new token
|
|
579
|
-
return await this.retryWithNewAuthentication(error);
|
|
580
|
-
}
|
|
581
|
-
}
|
|
582
|
-
return {
|
|
583
|
-
success: false,
|
|
584
|
-
error: 'Authentication refresh failed - manual re-authentication required'
|
|
585
|
-
};
|
|
586
|
-
}
|
|
587
|
-
catch (authError) {
|
|
588
|
-
return {
|
|
589
|
-
success: false,
|
|
590
|
-
error: authError instanceof Error ? authError.message : String(authError)
|
|
591
|
-
};
|
|
592
|
-
}
|
|
593
|
-
}
|
|
594
|
-
/**
|
|
595
|
-
* Retry network operations
|
|
596
|
-
*/
|
|
597
|
-
async retryNetworkOperation(error) {
|
|
598
|
-
try {
|
|
599
|
-
this.logger.info('Retrying network operation');
|
|
600
|
-
// For network errors, simply retry the original request
|
|
601
|
-
return await this.executeOriginalOperation(error);
|
|
602
|
-
}
|
|
603
|
-
catch (networkError) {
|
|
604
|
-
return {
|
|
605
|
-
success: false,
|
|
606
|
-
error: networkError instanceof Error ? networkError.message : String(networkError)
|
|
607
|
-
};
|
|
608
|
-
}
|
|
609
|
-
}
|
|
610
|
-
/**
|
|
611
|
-
* Retry rate limited operations with longer delay
|
|
612
|
-
*/
|
|
613
|
-
async retryRateLimitedOperation(error) {
|
|
614
|
-
try {
|
|
615
|
-
this.logger.info('Retrying rate limited operation with extended delay');
|
|
616
|
-
// Rate limit errors need longer delays
|
|
617
|
-
const rateLimitDelay = 30000 + Math.random() * 10000; // 30-40 seconds
|
|
618
|
-
await this.delay(rateLimitDelay);
|
|
619
|
-
return await this.executeOriginalOperation(error);
|
|
620
|
-
}
|
|
621
|
-
catch (rateLimitError) {
|
|
622
|
-
return {
|
|
623
|
-
success: false,
|
|
624
|
-
error: rateLimitError instanceof Error ? rateLimitError.message : String(rateLimitError)
|
|
625
|
-
};
|
|
626
|
-
}
|
|
627
|
-
}
|
|
628
|
-
/**
|
|
629
|
-
* Retry service unavailable operations
|
|
630
|
-
*/
|
|
631
|
-
async retryServiceUnavailableOperation(error) {
|
|
632
|
-
try {
|
|
633
|
-
this.logger.info('Retrying operation after service unavailability');
|
|
634
|
-
// Check service health before retry
|
|
635
|
-
const healthCheck = await this.performServiceHealthCheck();
|
|
636
|
-
if (!healthCheck.healthy) {
|
|
637
|
-
return {
|
|
638
|
-
success: false,
|
|
639
|
-
error: `Service still unavailable: ${healthCheck.reason}`
|
|
640
|
-
};
|
|
641
|
-
}
|
|
642
|
-
return await this.executeOriginalOperation(error);
|
|
643
|
-
}
|
|
644
|
-
catch (serviceError) {
|
|
645
|
-
return {
|
|
646
|
-
success: false,
|
|
647
|
-
error: serviceError instanceof Error ? serviceError.message : String(serviceError)
|
|
648
|
-
};
|
|
649
|
-
}
|
|
650
|
-
}
|
|
651
|
-
/**
|
|
652
|
-
* Retry API operations
|
|
653
|
-
*/
|
|
654
|
-
async retryAPIOperation(error) {
|
|
655
|
-
try {
|
|
656
|
-
this.logger.info('Retrying API operation');
|
|
657
|
-
// API errors might be transient, retry with same parameters
|
|
658
|
-
return await this.executeOriginalOperation(error);
|
|
659
|
-
}
|
|
660
|
-
catch (apiError) {
|
|
661
|
-
return {
|
|
662
|
-
success: false,
|
|
663
|
-
error: apiError instanceof Error ? apiError.message : String(apiError)
|
|
664
|
-
};
|
|
665
|
-
}
|
|
666
|
-
}
|
|
667
|
-
/**
|
|
668
|
-
* Retry conflict operations with modified data
|
|
669
|
-
*/
|
|
670
|
-
async retryConflictOperation(error) {
|
|
671
|
-
try {
|
|
672
|
-
this.logger.info('Retrying conflict operation with modifications');
|
|
673
|
-
// For conflicts, try to modify the operation slightly
|
|
674
|
-
const modifiedOperation = await this.modifyOperationForConflictResolution(error);
|
|
675
|
-
return await this.executeModifiedOperation(error, modifiedOperation);
|
|
676
|
-
}
|
|
677
|
-
catch (conflictError) {
|
|
678
|
-
return {
|
|
679
|
-
success: false,
|
|
680
|
-
error: conflictError instanceof Error ? conflictError.message : String(conflictError)
|
|
681
|
-
};
|
|
682
|
-
}
|
|
683
|
-
}
|
|
684
|
-
/**
|
|
685
|
-
* Retry resource operations
|
|
686
|
-
*/
|
|
687
|
-
async retryResourceOperation(error) {
|
|
688
|
-
try {
|
|
689
|
-
this.logger.info('Retrying resource operation');
|
|
690
|
-
// For resource not found, try to create the resource first
|
|
691
|
-
const resourceCreated = await this.ensureResourceExists(error);
|
|
692
|
-
if (resourceCreated.success) {
|
|
693
|
-
return await this.executeOriginalOperation(error);
|
|
694
|
-
}
|
|
695
|
-
return {
|
|
696
|
-
success: false,
|
|
697
|
-
error: `Failed to create required resource: ${resourceCreated.error}`
|
|
698
|
-
};
|
|
699
|
-
}
|
|
700
|
-
catch (resourceError) {
|
|
701
|
-
return {
|
|
702
|
-
success: false,
|
|
703
|
-
error: resourceError instanceof Error ? resourceError.message : String(resourceError)
|
|
704
|
-
};
|
|
705
|
-
}
|
|
706
|
-
}
|
|
707
|
-
/**
|
|
708
|
-
* Generic retry operation
|
|
709
|
-
*/
|
|
710
|
-
async retryGenericOperation(error) {
|
|
711
|
-
try {
|
|
712
|
-
this.logger.info('Performing generic retry operation');
|
|
713
|
-
// Generic retry - just execute the original operation again
|
|
714
|
-
return await this.executeOriginalOperation(error);
|
|
715
|
-
}
|
|
716
|
-
catch (genericError) {
|
|
717
|
-
return {
|
|
718
|
-
success: false,
|
|
719
|
-
error: genericError instanceof Error ? genericError.message : String(genericError)
|
|
720
|
-
};
|
|
721
|
-
}
|
|
722
|
-
}
|
|
723
|
-
/**
|
|
724
|
-
* Execute the original operation that failed
|
|
725
|
-
*/
|
|
726
|
-
async executeOriginalOperation(error) {
|
|
727
|
-
try {
|
|
728
|
-
// This would reconstruct and execute the original operation
|
|
729
|
-
// For now, we'll simulate the operation based on context
|
|
730
|
-
const operation = error.context.operation;
|
|
731
|
-
const operationType = this.extractOperationType(operation);
|
|
732
|
-
switch (operationType) {
|
|
733
|
-
case 'deploy':
|
|
734
|
-
return await this.retryDeployOperation(error);
|
|
735
|
-
case 'search':
|
|
736
|
-
return await this.retrySearchOperation(error);
|
|
737
|
-
case 'create':
|
|
738
|
-
return await this.retryCreateOperation(error);
|
|
739
|
-
case 'update':
|
|
740
|
-
return await this.retryUpdateOperation(error);
|
|
741
|
-
default:
|
|
742
|
-
// Generic HTTP request retry
|
|
743
|
-
return await this.retryHTTPRequest(error);
|
|
744
|
-
}
|
|
745
|
-
}
|
|
746
|
-
catch (executeError) {
|
|
747
|
-
return {
|
|
748
|
-
success: false,
|
|
749
|
-
error: executeError instanceof Error ? executeError.message : String(executeError)
|
|
750
|
-
};
|
|
751
|
-
}
|
|
752
|
-
}
|
|
753
|
-
// Helper methods for specific operation types
|
|
754
|
-
async retryDeployOperation(error) {
|
|
755
|
-
try {
|
|
756
|
-
this.logger.info('Retrying deployment operation', {
|
|
757
|
-
component_type: error.context.component_type,
|
|
758
|
-
component_id: error.context.component_id
|
|
759
|
-
});
|
|
760
|
-
// Extract deployment parameters from context
|
|
761
|
-
const deploymentData = error.context.user_input;
|
|
762
|
-
if (!deploymentData) {
|
|
763
|
-
return { success: false, error: 'No deployment data available for retry' };
|
|
764
|
-
}
|
|
765
|
-
// Retry based on component type
|
|
766
|
-
switch (error.context.component_type) {
|
|
767
|
-
case 'widget':
|
|
768
|
-
return await this.retryWidgetDeployment(deploymentData, error);
|
|
769
|
-
case 'flow':
|
|
770
|
-
return await this.retryFlowDeployment(deploymentData, error);
|
|
771
|
-
case 'application':
|
|
772
|
-
return await this.retryApplicationDeployment(deploymentData, error);
|
|
773
|
-
case 'script':
|
|
774
|
-
return await this.retryScriptDeployment(deploymentData, error);
|
|
775
|
-
default:
|
|
776
|
-
return await this.retryGenericDeployment(deploymentData, error);
|
|
777
|
-
}
|
|
778
|
-
}
|
|
779
|
-
catch (deployError) {
|
|
780
|
-
return {
|
|
781
|
-
success: false,
|
|
782
|
-
error: deployError instanceof Error ? deployError.message : String(deployError)
|
|
783
|
-
};
|
|
784
|
-
}
|
|
785
|
-
}
|
|
786
|
-
async retrySearchOperation(error) {
|
|
787
|
-
try {
|
|
788
|
-
this.logger.info('Retrying search operation', { operation: error.context.operation });
|
|
789
|
-
const searchParams = error.context.user_input;
|
|
790
|
-
if (!searchParams) {
|
|
791
|
-
return { success: false, error: 'No search parameters available for retry' };
|
|
792
|
-
}
|
|
793
|
-
// Modify search parameters if this is a retry to avoid same failure
|
|
794
|
-
const modifiedParams = {
|
|
795
|
-
...searchParams,
|
|
796
|
-
sysparm_limit: Math.min(searchParams.sysparm_limit || 100, 50), // Reduce limit
|
|
797
|
-
sysparm_offset: searchParams.sysparm_offset || 0,
|
|
798
|
-
retry_attempt: (error.retry_count || 0) + 1
|
|
799
|
-
};
|
|
800
|
-
// Perform the search request
|
|
801
|
-
const result = await this.client.makeRequest({
|
|
802
|
-
method: 'GET',
|
|
803
|
-
endpoint: searchParams.endpoint || '/api/now/table/sys_metadata',
|
|
804
|
-
params: modifiedParams
|
|
805
|
-
});
|
|
806
|
-
if (result.status === 200) {
|
|
807
|
-
return { success: true, data: result.data };
|
|
808
|
-
}
|
|
809
|
-
else {
|
|
810
|
-
return { success: false, error: `Search failed with status: ${result.status}` };
|
|
811
|
-
}
|
|
812
|
-
}
|
|
813
|
-
catch (searchError) {
|
|
814
|
-
return {
|
|
815
|
-
success: false,
|
|
816
|
-
error: searchError instanceof Error ? searchError.message : String(searchError)
|
|
817
|
-
};
|
|
818
|
-
}
|
|
819
|
-
}
|
|
820
|
-
async retryCreateOperation(error) {
|
|
821
|
-
try {
|
|
822
|
-
this.logger.info('Retrying create operation', {
|
|
823
|
-
table: error.context.component_type,
|
|
824
|
-
retry_count: error.retry_count
|
|
825
|
-
});
|
|
826
|
-
const createData = error.context.user_input;
|
|
827
|
-
if (!createData || !createData.table) {
|
|
828
|
-
return { success: false, error: 'No create data or table specified for retry' };
|
|
829
|
-
}
|
|
830
|
-
// Modify data for conflict resolution if needed
|
|
831
|
-
const retryData = { ...createData.data };
|
|
832
|
-
// If this is a conflict retry, modify unique fields
|
|
833
|
-
if (error.type === ErrorType.CONFLICT_ERROR && error.retry_count > 0) {
|
|
834
|
-
const timestamp = Date.now();
|
|
835
|
-
if (retryData.name) {
|
|
836
|
-
retryData.name = `${retryData.name}_retry_${timestamp}`;
|
|
837
|
-
}
|
|
838
|
-
if (retryData.sys_name) {
|
|
839
|
-
retryData.sys_name = `${retryData.sys_name}_retry_${timestamp}`;
|
|
840
|
-
}
|
|
841
|
-
}
|
|
842
|
-
// Perform the create request
|
|
843
|
-
const result = await this.client.createRecord(createData.table, retryData);
|
|
844
|
-
if (result && result.sys_id) {
|
|
845
|
-
this.logger.info('Create operation retry successful', { sys_id: result.sys_id });
|
|
846
|
-
return { success: true, data: result };
|
|
847
|
-
}
|
|
848
|
-
else {
|
|
849
|
-
return { success: false, error: 'Create operation failed - no sys_id returned' };
|
|
850
|
-
}
|
|
851
|
-
}
|
|
852
|
-
catch (createError) {
|
|
853
|
-
return {
|
|
854
|
-
success: false,
|
|
855
|
-
error: createError instanceof Error ? createError.message : String(createError)
|
|
856
|
-
};
|
|
857
|
-
}
|
|
858
|
-
}
|
|
859
|
-
async retryUpdateOperation(error) {
|
|
860
|
-
try {
|
|
861
|
-
this.logger.info('Retrying update operation', {
|
|
862
|
-
table: error.context.component_type,
|
|
863
|
-
record_id: error.context.component_id
|
|
864
|
-
});
|
|
865
|
-
const updateData = error.context.user_input;
|
|
866
|
-
if (!updateData || !updateData.table || !updateData.record_id) {
|
|
867
|
-
return { success: false, error: 'No update data, table, or record ID available for retry' };
|
|
868
|
-
}
|
|
869
|
-
// First, verify the record still exists
|
|
870
|
-
try {
|
|
871
|
-
const existingRecord = await this.client.getRecord(updateData.table, updateData.record_id);
|
|
872
|
-
if (!existingRecord) {
|
|
873
|
-
return { success: false, error: 'Record no longer exists for update' };
|
|
874
|
-
}
|
|
875
|
-
}
|
|
876
|
-
catch (getError) {
|
|
877
|
-
return { success: false, error: 'Cannot verify record existence for update retry' };
|
|
878
|
-
}
|
|
879
|
-
// Perform the update request
|
|
880
|
-
const result = await this.client.updateRecord(updateData.table, updateData.record_id, updateData.data);
|
|
881
|
-
if (result) {
|
|
882
|
-
this.logger.info('Update operation retry successful', { sys_id: updateData.record_id });
|
|
883
|
-
return { success: true, data: result };
|
|
884
|
-
}
|
|
885
|
-
else {
|
|
886
|
-
return { success: false, error: 'Update operation failed' };
|
|
887
|
-
}
|
|
888
|
-
}
|
|
889
|
-
catch (updateError) {
|
|
890
|
-
return {
|
|
891
|
-
success: false,
|
|
892
|
-
error: updateError instanceof Error ? updateError.message : String(updateError)
|
|
893
|
-
};
|
|
894
|
-
}
|
|
895
|
-
}
|
|
896
|
-
async retryHTTPRequest(error) {
|
|
897
|
-
try {
|
|
898
|
-
this.logger.info('Retrying HTTP request', { operation: error.context.operation });
|
|
899
|
-
const requestData = error.context.user_input;
|
|
900
|
-
if (!requestData) {
|
|
901
|
-
return { success: false, error: 'No request data available for HTTP retry' };
|
|
902
|
-
}
|
|
903
|
-
// Construct request with retry-safe parameters
|
|
904
|
-
const requestConfig = {
|
|
905
|
-
method: requestData.method || 'GET',
|
|
906
|
-
endpoint: requestData.endpoint || requestData.url,
|
|
907
|
-
params: requestData.params,
|
|
908
|
-
data: requestData.data,
|
|
909
|
-
timeout: Math.min((requestData.timeout || 30000) * 1.5, 60000), // Increase timeout
|
|
910
|
-
retry_attempt: (error.retry_count || 0) + 1
|
|
911
|
-
};
|
|
912
|
-
// Add retry headers
|
|
913
|
-
const headers = {
|
|
914
|
-
...requestData.headers,
|
|
915
|
-
'X-Retry-Attempt': String(requestConfig.retry_attempt),
|
|
916
|
-
'X-Request-ID': `retry_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
|
|
917
|
-
};
|
|
918
|
-
const result = await this.client.makeRequest({
|
|
919
|
-
...requestConfig,
|
|
920
|
-
headers
|
|
921
|
-
});
|
|
922
|
-
if (result.status >= 200 && result.status < 300) {
|
|
923
|
-
return { success: true, data: result.data };
|
|
924
|
-
}
|
|
925
|
-
else {
|
|
926
|
-
return {
|
|
927
|
-
success: false,
|
|
928
|
-
error: `HTTP request failed with status: ${result.status} - ${result.statusText}`
|
|
929
|
-
};
|
|
930
|
-
}
|
|
931
|
-
}
|
|
932
|
-
catch (httpError) {
|
|
933
|
-
return {
|
|
934
|
-
success: false,
|
|
935
|
-
error: httpError instanceof Error ? httpError.message : String(httpError)
|
|
936
|
-
};
|
|
937
|
-
}
|
|
938
|
-
}
|
|
939
|
-
// Additional helper methods
|
|
940
|
-
async retryWithNewAuthentication(error) {
|
|
941
|
-
// Retry with refreshed authentication
|
|
942
|
-
return await this.executeOriginalOperation(error);
|
|
943
|
-
}
|
|
944
|
-
async performServiceHealthCheck() {
|
|
945
|
-
try {
|
|
946
|
-
// Basic health check - try to access ServiceNow instance
|
|
947
|
-
const response = await this.client.makeRequest({
|
|
948
|
-
method: 'GET',
|
|
949
|
-
endpoint: '/api/now/v2/table/sys_user',
|
|
950
|
-
params: { sysparm_limit: 1 }
|
|
951
|
-
});
|
|
952
|
-
return { healthy: response.status === 200 };
|
|
953
|
-
}
|
|
954
|
-
catch (healthError) {
|
|
955
|
-
return {
|
|
956
|
-
healthy: false,
|
|
957
|
-
reason: healthError instanceof Error ? healthError.message : 'Unknown health check error'
|
|
958
|
-
};
|
|
959
|
-
}
|
|
960
|
-
}
|
|
961
|
-
async modifyOperationForConflictResolution(error) {
|
|
962
|
-
// Modify operation to resolve conflicts (e.g., add timestamp to name)
|
|
963
|
-
const timestamp = Date.now();
|
|
964
|
-
return {
|
|
965
|
-
...error.context.system_state,
|
|
966
|
-
name: `${error.context.system_state?.name || 'item'}_${timestamp}`,
|
|
967
|
-
modified_for_conflict: true
|
|
968
|
-
};
|
|
969
|
-
}
|
|
970
|
-
async executeModifiedOperation(error, modifiedData) {
|
|
971
|
-
try {
|
|
972
|
-
this.logger.info('Executing modified operation for conflict resolution', {
|
|
973
|
-
original_operation: error.context.operation,
|
|
974
|
-
modifications: modifiedData.modified_for_conflict ? 'conflict_resolution' : 'standard'
|
|
975
|
-
});
|
|
976
|
-
// Execute the operation with modified data based on operation type
|
|
977
|
-
const operationType = this.extractOperationType(error.context.operation);
|
|
978
|
-
switch (operationType) {
|
|
979
|
-
case 'create':
|
|
980
|
-
const table = error.context.component_type || 'sys_metadata';
|
|
981
|
-
const result = await this.client.createRecord(table, modifiedData);
|
|
982
|
-
return { success: true, data: result };
|
|
983
|
-
case 'update':
|
|
984
|
-
if (!error.context.component_id) {
|
|
985
|
-
return { success: false, error: 'No component ID for update operation' };
|
|
986
|
-
}
|
|
987
|
-
const updateResult = await this.client.updateRecord(error.context.component_type || 'sys_metadata', error.context.component_id, modifiedData);
|
|
988
|
-
return { success: true, data: updateResult };
|
|
989
|
-
case 'deploy':
|
|
990
|
-
// For deployment operations, use the deployment data structure
|
|
991
|
-
return await this.retryGenericDeployment(modifiedData, error);
|
|
992
|
-
default:
|
|
993
|
-
return { success: false, error: 'Unsupported operation type for modification' };
|
|
994
|
-
}
|
|
995
|
-
}
|
|
996
|
-
catch (executeError) {
|
|
997
|
-
return {
|
|
998
|
-
success: false,
|
|
999
|
-
error: executeError instanceof Error ? executeError.message : String(executeError)
|
|
1000
|
-
};
|
|
1001
|
-
}
|
|
1002
|
-
}
|
|
1003
|
-
async ensureResourceExists(error) {
|
|
1004
|
-
try {
|
|
1005
|
-
this.logger.info('Attempting to create missing resource', {
|
|
1006
|
-
resource_type: error.context.component_type,
|
|
1007
|
-
resource_id: error.context.component_id
|
|
1008
|
-
});
|
|
1009
|
-
// Extract resource information from context
|
|
1010
|
-
const resourceData = error.context.user_input;
|
|
1011
|
-
if (!resourceData) {
|
|
1012
|
-
return { success: false, error: 'No resource data available for creation' };
|
|
1013
|
-
}
|
|
1014
|
-
// Determine resource type and create accordingly
|
|
1015
|
-
switch (error.context.component_type) {
|
|
1016
|
-
case 'table':
|
|
1017
|
-
return await this.createMissingTable(resourceData);
|
|
1018
|
-
case 'field':
|
|
1019
|
-
return await this.createMissingField(resourceData);
|
|
1020
|
-
case 'user':
|
|
1021
|
-
return await this.createMissingUser(resourceData);
|
|
1022
|
-
case 'group':
|
|
1023
|
-
return await this.createMissingGroup(resourceData);
|
|
1024
|
-
default:
|
|
1025
|
-
// Generic resource creation
|
|
1026
|
-
return await this.createGenericResource(resourceData);
|
|
1027
|
-
}
|
|
1028
|
-
}
|
|
1029
|
-
catch (resourceError) {
|
|
1030
|
-
return {
|
|
1031
|
-
success: false,
|
|
1032
|
-
error: resourceError instanceof Error ? resourceError.message : String(resourceError)
|
|
1033
|
-
};
|
|
1034
|
-
}
|
|
1035
|
-
}
|
|
1036
|
-
async delay(ms) {
|
|
1037
|
-
return new Promise(resolve => setTimeout(resolve, ms));
|
|
1038
|
-
}
|
|
1039
|
-
/**
|
|
1040
|
-
* Create missing resource
|
|
1041
|
-
*/
|
|
1042
|
-
async createMissingResource(error) {
|
|
1043
|
-
// Implementation depends on the specific resource type
|
|
1044
|
-
return { success: false };
|
|
1045
|
-
}
|
|
1046
|
-
/**
|
|
1047
|
-
* Use fallback values
|
|
1048
|
-
*/
|
|
1049
|
-
async useFallbackValues(error) {
|
|
1050
|
-
// Implementation depends on the specific validation error
|
|
1051
|
-
return { success: false };
|
|
1052
|
-
}
|
|
1053
|
-
/**
|
|
1054
|
-
* Use cached data
|
|
1055
|
-
*/
|
|
1056
|
-
async useCachedData(error) {
|
|
1057
|
-
// Implementation depends on the specific cached data available
|
|
1058
|
-
return { success: false };
|
|
1059
|
-
}
|
|
1060
|
-
/**
|
|
1061
|
-
* Generate user-friendly error message
|
|
1062
|
-
*/
|
|
1063
|
-
generateUserMessage(error, result) {
|
|
1064
|
-
if (result.recovered) {
|
|
1065
|
-
return `The operation encountered an issue but was automatically recovered. ${error.message}`;
|
|
1066
|
-
}
|
|
1067
|
-
switch (error.type) {
|
|
1068
|
-
case ErrorType.AUTHENTICATION_ERROR:
|
|
1069
|
-
return 'Authentication failed. Please check your credentials and try again.';
|
|
1070
|
-
case ErrorType.AUTHORIZATION_ERROR:
|
|
1071
|
-
return 'You do not have permission to perform this operation.';
|
|
1072
|
-
case ErrorType.VALIDATION_ERROR:
|
|
1073
|
-
return 'The provided data is invalid. Please check your inputs and try again.';
|
|
1074
|
-
case ErrorType.NETWORK_ERROR:
|
|
1075
|
-
return 'Network connection failed. Please check your connection and try again.';
|
|
1076
|
-
case ErrorType.RESOURCE_NOT_FOUND:
|
|
1077
|
-
return 'The requested resource was not found. It may have been deleted or moved.';
|
|
1078
|
-
default:
|
|
1079
|
-
return `An error occurred: ${error.message}`;
|
|
1080
|
-
}
|
|
1081
|
-
}
|
|
1082
|
-
/**
|
|
1083
|
-
* Generate technical details
|
|
1084
|
-
*/
|
|
1085
|
-
generateTechnicalDetails(error, result) {
|
|
1086
|
-
const details = {
|
|
1087
|
-
error_type: error.type,
|
|
1088
|
-
severity: error.severity,
|
|
1089
|
-
message: error.message,
|
|
1090
|
-
context: error.context,
|
|
1091
|
-
actions_taken: result.actions_taken.map(a => a.type),
|
|
1092
|
-
recovered: result.recovered,
|
|
1093
|
-
timestamp: new Date().toISOString()
|
|
1094
|
-
};
|
|
1095
|
-
return JSON.stringify(details, null, 2);
|
|
1096
|
-
}
|
|
1097
|
-
/**
|
|
1098
|
-
* Get max retries for error type
|
|
1099
|
-
*/
|
|
1100
|
-
getMaxRetries(errorType) {
|
|
1101
|
-
switch (errorType) {
|
|
1102
|
-
case ErrorType.NETWORK_ERROR:
|
|
1103
|
-
case ErrorType.TIMEOUT_ERROR:
|
|
1104
|
-
return 3;
|
|
1105
|
-
case ErrorType.RATE_LIMIT_ERROR:
|
|
1106
|
-
return 2;
|
|
1107
|
-
case ErrorType.SERVICE_UNAVAILABLE:
|
|
1108
|
-
return 5;
|
|
1109
|
-
default:
|
|
1110
|
-
return 1;
|
|
1111
|
-
}
|
|
1112
|
-
}
|
|
1113
|
-
/**
|
|
1114
|
-
* Capture state snapshot
|
|
1115
|
-
*/
|
|
1116
|
-
captureStateSnapshot(transaction) {
|
|
1117
|
-
return {
|
|
1118
|
-
transaction_id: transaction.id,
|
|
1119
|
-
flow_id: transaction.flow_id,
|
|
1120
|
-
operation_count: transaction.operations.length,
|
|
1121
|
-
status: transaction.status,
|
|
1122
|
-
timestamp: new Date().toISOString()
|
|
1123
|
-
};
|
|
1124
|
-
}
|
|
1125
|
-
/**
|
|
1126
|
-
* Generate transaction ID
|
|
1127
|
-
*/
|
|
1128
|
-
generateTransactionId() {
|
|
1129
|
-
return `tx_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
1130
|
-
}
|
|
1131
|
-
/**
|
|
1132
|
-
* Generate operation ID
|
|
1133
|
-
*/
|
|
1134
|
-
generateOperationId() {
|
|
1135
|
-
return `op_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
1136
|
-
}
|
|
1137
|
-
/**
|
|
1138
|
-
* Generate checkpoint ID
|
|
1139
|
-
*/
|
|
1140
|
-
generateCheckpointId() {
|
|
1141
|
-
return `cp_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
1142
|
-
}
|
|
1143
|
-
/**
|
|
1144
|
-
* Get error statistics
|
|
1145
|
-
*/
|
|
1146
|
-
getErrorStatistics() {
|
|
1147
|
-
const stats = {
|
|
1148
|
-
total_errors: this.errorHistory.length,
|
|
1149
|
-
errors_by_type: {},
|
|
1150
|
-
errors_by_severity: {},
|
|
1151
|
-
recovery_rate: 0
|
|
1152
|
-
};
|
|
1153
|
-
let recoveredCount = 0;
|
|
1154
|
-
for (const error of this.errorHistory) {
|
|
1155
|
-
// Count by type
|
|
1156
|
-
stats.errors_by_type[error.type] = (stats.errors_by_type[error.type] || 0) + 1;
|
|
1157
|
-
// Count by severity
|
|
1158
|
-
stats.errors_by_severity[error.severity] = (stats.errors_by_severity[error.severity] || 0) + 1;
|
|
1159
|
-
// Count recovered errors
|
|
1160
|
-
if (error.recoverable) {
|
|
1161
|
-
recoveredCount++;
|
|
1162
|
-
}
|
|
1163
|
-
}
|
|
1164
|
-
stats.recovery_rate = stats.total_errors > 0 ? recoveredCount / stats.total_errors : 0;
|
|
1165
|
-
return stats;
|
|
1166
|
-
}
|
|
1167
|
-
/**
|
|
1168
|
-
* Get active transactions
|
|
1169
|
-
*/
|
|
1170
|
-
getActiveTransactions() {
|
|
1171
|
-
return Array.from(this.transactions.values())
|
|
1172
|
-
.filter(t => t.status === 'active');
|
|
1173
|
-
}
|
|
1174
|
-
// =====================================
|
|
1175
|
-
// DEPLOYMENT RETRY METHODS
|
|
1176
|
-
// =====================================
|
|
1177
|
-
/**
|
|
1178
|
-
* Retry widget deployment
|
|
1179
|
-
*/
|
|
1180
|
-
async retryWidgetDeployment(deploymentData, error) {
|
|
1181
|
-
try {
|
|
1182
|
-
this.logger.info('Retrying widget deployment', { widget_name: deploymentData.name });
|
|
1183
|
-
// Construct widget data with retry modifications
|
|
1184
|
-
const widgetData = {
|
|
1185
|
-
name: deploymentData.name,
|
|
1186
|
-
title: deploymentData.title,
|
|
1187
|
-
template: deploymentData.template,
|
|
1188
|
-
server_script: deploymentData.server_script,
|
|
1189
|
-
client_script: deploymentData.client_script,
|
|
1190
|
-
css: deploymentData.css,
|
|
1191
|
-
category: deploymentData.category || 'custom',
|
|
1192
|
-
roles: deploymentData.roles || '',
|
|
1193
|
-
public: deploymentData.public !== false
|
|
1194
|
-
};
|
|
1195
|
-
// If this is a conflict retry, modify the name
|
|
1196
|
-
if (error.type === ErrorType.CONFLICT_ERROR && (error.retry_count || 0) > 0) {
|
|
1197
|
-
widgetData.name = `${widgetData.name}_retry_${Date.now()}`;
|
|
1198
|
-
}
|
|
1199
|
-
const result = await this.client.createRecord('sp_widget', widgetData);
|
|
1200
|
-
return { success: true, data: result };
|
|
1201
|
-
}
|
|
1202
|
-
catch (widgetError) {
|
|
1203
|
-
return {
|
|
1204
|
-
success: false,
|
|
1205
|
-
error: widgetError instanceof Error ? widgetError.message : String(widgetError)
|
|
1206
|
-
};
|
|
1207
|
-
}
|
|
1208
|
-
}
|
|
1209
|
-
/**
|
|
1210
|
-
* Retry flow deployment
|
|
1211
|
-
*/
|
|
1212
|
-
async retryFlowDeployment(deploymentData, error) {
|
|
1213
|
-
try {
|
|
1214
|
-
this.logger.info('Retrying flow deployment', { flow_name: deploymentData.name });
|
|
1215
|
-
const flowData = {
|
|
1216
|
-
name: deploymentData.name,
|
|
1217
|
-
description: deploymentData.description,
|
|
1218
|
-
active: deploymentData.active !== false,
|
|
1219
|
-
flow_designer: deploymentData.flow_definition || '{}',
|
|
1220
|
-
trigger_conditions: deploymentData.trigger_conditions,
|
|
1221
|
-
table: deploymentData.table
|
|
1222
|
-
};
|
|
1223
|
-
// Handle conflict retry
|
|
1224
|
-
if (error.type === ErrorType.CONFLICT_ERROR && (error.retry_count || 0) > 0) {
|
|
1225
|
-
flowData.name = `${flowData.name}_retry_${Date.now()}`;
|
|
1226
|
-
}
|
|
1227
|
-
const result = await this.client.createRecord('sys_hub_flow', flowData);
|
|
1228
|
-
return { success: true, data: result };
|
|
1229
|
-
}
|
|
1230
|
-
catch (flowError) {
|
|
1231
|
-
return {
|
|
1232
|
-
success: false,
|
|
1233
|
-
error: flowError instanceof Error ? flowError.message : String(flowError)
|
|
1234
|
-
};
|
|
1235
|
-
}
|
|
1236
|
-
}
|
|
1237
|
-
/**
|
|
1238
|
-
* Retry application deployment
|
|
1239
|
-
*/
|
|
1240
|
-
async retryApplicationDeployment(deploymentData, error) {
|
|
1241
|
-
try {
|
|
1242
|
-
this.logger.info('Retrying application deployment', { app_name: deploymentData.name });
|
|
1243
|
-
const appData = {
|
|
1244
|
-
name: deploymentData.name,
|
|
1245
|
-
short_description: deploymentData.short_description,
|
|
1246
|
-
description: deploymentData.description,
|
|
1247
|
-
version: deploymentData.version || '1.0.0',
|
|
1248
|
-
vendor: deploymentData.vendor || 'Custom',
|
|
1249
|
-
vendor_prefix: deploymentData.vendor_prefix,
|
|
1250
|
-
active: deploymentData.active !== false
|
|
1251
|
-
};
|
|
1252
|
-
// Handle conflict retry
|
|
1253
|
-
if (error.type === ErrorType.CONFLICT_ERROR && (error.retry_count || 0) > 0) {
|
|
1254
|
-
appData.name = `${appData.name}_retry_${Date.now()}`;
|
|
1255
|
-
}
|
|
1256
|
-
const result = await this.client.createRecord('sys_app', appData);
|
|
1257
|
-
return { success: true, data: result };
|
|
1258
|
-
}
|
|
1259
|
-
catch (appError) {
|
|
1260
|
-
return {
|
|
1261
|
-
success: false,
|
|
1262
|
-
error: appError instanceof Error ? appError.message : String(appError)
|
|
1263
|
-
};
|
|
1264
|
-
}
|
|
1265
|
-
}
|
|
1266
|
-
/**
|
|
1267
|
-
* Retry script deployment
|
|
1268
|
-
*/
|
|
1269
|
-
async retryScriptDeployment(deploymentData, error) {
|
|
1270
|
-
try {
|
|
1271
|
-
this.logger.info('Retrying script deployment', { script_name: deploymentData.name });
|
|
1272
|
-
const scriptData = {
|
|
1273
|
-
name: deploymentData.name,
|
|
1274
|
-
script: deploymentData.script,
|
|
1275
|
-
description: deploymentData.description,
|
|
1276
|
-
api_name: deploymentData.api_name,
|
|
1277
|
-
client_callable: deploymentData.client_callable || false,
|
|
1278
|
-
active: deploymentData.active !== false
|
|
1279
|
-
};
|
|
1280
|
-
// Handle conflict retry
|
|
1281
|
-
if (error.type === ErrorType.CONFLICT_ERROR && (error.retry_count || 0) > 0) {
|
|
1282
|
-
scriptData.name = `${scriptData.name}_retry_${Date.now()}`;
|
|
1283
|
-
}
|
|
1284
|
-
const result = await this.client.createRecord('sys_script_include', scriptData);
|
|
1285
|
-
return { success: true, data: result };
|
|
1286
|
-
}
|
|
1287
|
-
catch (scriptError) {
|
|
1288
|
-
return {
|
|
1289
|
-
success: false,
|
|
1290
|
-
error: scriptError instanceof Error ? scriptError.message : String(scriptError)
|
|
1291
|
-
};
|
|
1292
|
-
}
|
|
1293
|
-
}
|
|
1294
|
-
/**
|
|
1295
|
-
* Generic deployment retry
|
|
1296
|
-
*/
|
|
1297
|
-
async retryGenericDeployment(deploymentData, error) {
|
|
1298
|
-
try {
|
|
1299
|
-
this.logger.info('Retrying generic deployment', {
|
|
1300
|
-
component_type: error.context.component_type,
|
|
1301
|
-
data_keys: Object.keys(deploymentData)
|
|
1302
|
-
});
|
|
1303
|
-
// Determine table from component type or data
|
|
1304
|
-
const table = deploymentData.table ||
|
|
1305
|
-
this.getTableFromComponentType(error.context.component_type) ||
|
|
1306
|
-
'sys_metadata';
|
|
1307
|
-
const result = await this.client.createRecord(table, deploymentData);
|
|
1308
|
-
return { success: true, data: result };
|
|
1309
|
-
}
|
|
1310
|
-
catch (genericError) {
|
|
1311
|
-
return {
|
|
1312
|
-
success: false,
|
|
1313
|
-
error: genericError instanceof Error ? genericError.message : String(genericError)
|
|
1314
|
-
};
|
|
1315
|
-
}
|
|
1316
|
-
}
|
|
1317
|
-
// =====================================
|
|
1318
|
-
// RESOURCE CREATION METHODS
|
|
1319
|
-
// =====================================
|
|
1320
|
-
/**
|
|
1321
|
-
* Create missing table
|
|
1322
|
-
*/
|
|
1323
|
-
async createMissingTable(resourceData) {
|
|
1324
|
-
try {
|
|
1325
|
-
const tableData = {
|
|
1326
|
-
name: resourceData.table_name || resourceData.name,
|
|
1327
|
-
label: resourceData.label || resourceData.table_name,
|
|
1328
|
-
super_class: resourceData.super_class || 'sys_metadata',
|
|
1329
|
-
is_extendable: resourceData.is_extendable !== false
|
|
1330
|
-
};
|
|
1331
|
-
await this.client.createRecord('sys_db_object', tableData);
|
|
1332
|
-
return { success: true };
|
|
1333
|
-
}
|
|
1334
|
-
catch (error) {
|
|
1335
|
-
return { success: false, error: error instanceof Error ? error.message : String(error) };
|
|
1336
|
-
}
|
|
1337
|
-
}
|
|
1338
|
-
/**
|
|
1339
|
-
* Create missing field
|
|
1340
|
-
*/
|
|
1341
|
-
async createMissingField(resourceData) {
|
|
1342
|
-
try {
|
|
1343
|
-
const fieldData = {
|
|
1344
|
-
table: resourceData.table,
|
|
1345
|
-
column_name: resourceData.field_name,
|
|
1346
|
-
column_label: resourceData.label || resourceData.field_name,
|
|
1347
|
-
internal_type: resourceData.type || 'string',
|
|
1348
|
-
max_length: resourceData.max_length || 40
|
|
1349
|
-
};
|
|
1350
|
-
await this.client.createRecord('sys_dictionary', fieldData);
|
|
1351
|
-
return { success: true };
|
|
1352
|
-
}
|
|
1353
|
-
catch (error) {
|
|
1354
|
-
return { success: false, error: error instanceof Error ? error.message : String(error) };
|
|
1355
|
-
}
|
|
1356
|
-
}
|
|
1357
|
-
/**
|
|
1358
|
-
* Create missing user
|
|
1359
|
-
*/
|
|
1360
|
-
async createMissingUser(resourceData) {
|
|
1361
|
-
try {
|
|
1362
|
-
const userData = {
|
|
1363
|
-
user_name: resourceData.user_name,
|
|
1364
|
-
first_name: resourceData.first_name || 'Unknown',
|
|
1365
|
-
last_name: resourceData.last_name || 'User',
|
|
1366
|
-
email: resourceData.email,
|
|
1367
|
-
active: resourceData.active !== false
|
|
1368
|
-
};
|
|
1369
|
-
await this.client.createRecord('sys_user', userData);
|
|
1370
|
-
return { success: true };
|
|
1371
|
-
}
|
|
1372
|
-
catch (error) {
|
|
1373
|
-
return { success: false, error: error instanceof Error ? error.message : String(error) };
|
|
1374
|
-
}
|
|
1375
|
-
}
|
|
1376
|
-
/**
|
|
1377
|
-
* Create missing group
|
|
1378
|
-
*/
|
|
1379
|
-
async createMissingGroup(resourceData) {
|
|
1380
|
-
try {
|
|
1381
|
-
const groupData = {
|
|
1382
|
-
name: resourceData.group_name || resourceData.name,
|
|
1383
|
-
description: resourceData.description || `Group ${resourceData.name}`,
|
|
1384
|
-
active: resourceData.active !== false
|
|
1385
|
-
};
|
|
1386
|
-
await this.client.createRecord('sys_user_group', groupData);
|
|
1387
|
-
return { success: true };
|
|
1388
|
-
}
|
|
1389
|
-
catch (error) {
|
|
1390
|
-
return { success: false, error: error instanceof Error ? error.message : String(error) };
|
|
1391
|
-
}
|
|
1392
|
-
}
|
|
1393
|
-
/**
|
|
1394
|
-
* Create generic resource
|
|
1395
|
-
*/
|
|
1396
|
-
async createGenericResource(resourceData) {
|
|
1397
|
-
try {
|
|
1398
|
-
const table = resourceData.table || 'sys_metadata';
|
|
1399
|
-
await this.client.createRecord(table, resourceData);
|
|
1400
|
-
return { success: true };
|
|
1401
|
-
}
|
|
1402
|
-
catch (error) {
|
|
1403
|
-
return { success: false, error: error instanceof Error ? error.message : String(error) };
|
|
1404
|
-
}
|
|
1405
|
-
}
|
|
1406
|
-
// =====================================
|
|
1407
|
-
// UTILITY METHODS
|
|
1408
|
-
// =====================================
|
|
1409
|
-
/**
|
|
1410
|
-
* Get ServiceNow table name from component type
|
|
1411
|
-
*/
|
|
1412
|
-
getTableFromComponentType(componentType) {
|
|
1413
|
-
if (!componentType)
|
|
1414
|
-
return null;
|
|
1415
|
-
const tableMap = {
|
|
1416
|
-
'widget': 'sp_widget',
|
|
1417
|
-
'flow': 'sys_hub_flow',
|
|
1418
|
-
'application': 'sys_app',
|
|
1419
|
-
'script': 'sys_script_include',
|
|
1420
|
-
'business_rule': 'sys_script',
|
|
1421
|
-
'client_script': 'sys_script_client',
|
|
1422
|
-
'ui_policy': 'sys_ui_policy',
|
|
1423
|
-
'ui_action': 'sys_ui_action',
|
|
1424
|
-
'table': 'sys_db_object',
|
|
1425
|
-
'field': 'sys_dictionary',
|
|
1426
|
-
'user': 'sys_user',
|
|
1427
|
-
'group': 'sys_user_group'
|
|
1428
|
-
};
|
|
1429
|
-
return tableMap[componentType.toLowerCase()] || null;
|
|
1430
|
-
}
|
|
1431
|
-
/**
|
|
1432
|
-
* Enhanced operation type extraction
|
|
1433
|
-
*/
|
|
1434
|
-
extractOperationType(operation) {
|
|
1435
|
-
const op = operation.toLowerCase();
|
|
1436
|
-
// More specific operation detection
|
|
1437
|
-
if (op.includes('deploy') || op.includes('create_') || op.includes('snow_deploy'))
|
|
1438
|
-
return 'deploy';
|
|
1439
|
-
if (op.includes('search') || op.includes('find') || op.includes('query') || op.includes('discover'))
|
|
1440
|
-
return 'search';
|
|
1441
|
-
if (op.includes('create') || op.includes('insert') || op.includes('add'))
|
|
1442
|
-
return 'create';
|
|
1443
|
-
if (op.includes('update') || op.includes('modify') || op.includes('edit') || op.includes('patch'))
|
|
1444
|
-
return 'update';
|
|
1445
|
-
if (op.includes('delete') || op.includes('remove'))
|
|
1446
|
-
return 'delete';
|
|
1447
|
-
if (op.includes('get') || op.includes('fetch') || op.includes('retrieve'))
|
|
1448
|
-
return 'read';
|
|
1449
|
-
if (op.includes('authenticate') || op.includes('login') || op.includes('auth'))
|
|
1450
|
-
return 'auth';
|
|
1451
|
-
if (op.includes('http') || op.includes('request') || op.includes('api'))
|
|
1452
|
-
return 'http';
|
|
1453
|
-
return 'generic';
|
|
1454
|
-
}
|
|
1455
|
-
/**
|
|
1456
|
-
* Check if error type is retryable
|
|
1457
|
-
*/
|
|
1458
|
-
isRetryableError(errorType) {
|
|
1459
|
-
const retryableErrors = [
|
|
1460
|
-
ErrorType.NETWORK_ERROR,
|
|
1461
|
-
ErrorType.TIMEOUT_ERROR,
|
|
1462
|
-
ErrorType.RATE_LIMIT_ERROR,
|
|
1463
|
-
ErrorType.SERVICE_UNAVAILABLE,
|
|
1464
|
-
ErrorType.API_ERROR,
|
|
1465
|
-
ErrorType.CONFLICT_ERROR
|
|
1466
|
-
];
|
|
1467
|
-
return retryableErrors.includes(errorType);
|
|
1468
|
-
}
|
|
1469
|
-
/**
|
|
1470
|
-
* Get circuit breaker status for operation
|
|
1471
|
-
*/
|
|
1472
|
-
getCircuitBreakerStatus(operation) {
|
|
1473
|
-
// Simple circuit breaker implementation
|
|
1474
|
-
const recentErrors = this.errorHistory
|
|
1475
|
-
.filter(error => error.context.operation === operation &&
|
|
1476
|
-
error.context.timestamp.getTime() > Date.now() - 300000 // Last 5 minutes
|
|
1477
|
-
);
|
|
1478
|
-
const failureCount = recentErrors.length;
|
|
1479
|
-
const isOpen = failureCount >= 5; // Open circuit after 5 failures
|
|
1480
|
-
return {
|
|
1481
|
-
isOpen,
|
|
1482
|
-
failureCount,
|
|
1483
|
-
lastFailureTime: recentErrors.length > 0 ? recentErrors[recentErrors.length - 1].context.timestamp : undefined
|
|
1484
|
-
};
|
|
1485
|
-
}
|
|
1486
|
-
}
|
|
1487
|
-
exports.FlowErrorHandler = FlowErrorHandler;
|
|
1488
|
-
//# sourceMappingURL=error-handling.js.map
|