snow-flow 2.0.2 → 2.0.3
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/mcp-config.json → .mcp.json} +13 -12
- package/.releaserc.json +47 -0
- package/README.md +6 -0
- package/package.json +1 -1
- package/.claude/claude-settings.json +0 -15
- package/.claude/commands/analysis/README.md +0 -9
- package/.claude/commands/automation/README.md +0 -9
- package/.claude/commands/github/README.md +0 -11
- package/.claude/commands/helpers/standard-checkpoint-hooks.sh +0 -179
- package/.claude/commands/hooks/README.md +0 -11
- package/.claude/commands/monitoring/README.md +0 -9
- package/.claude/commands/optimization/README.md +0 -9
- package/.claude/commands/training/README.md +0 -9
- package/.claude/commands/workflows/README.md +0 -9
- package/.claude/config.json +0 -36
- package/.claude/helpers/github-setup.sh +0 -28
- package/.claude/helpers/quick-start.sh +0 -19
- package/.claude/helpers/setup-mcp.sh +0 -18
- package/.claude/settings.json +0 -114
- package/.claude/settings.local.json +0 -33
- package/.claude-flow/metrics/agent-metrics.json +0 -1
- package/.claude-flow/metrics/performance.json +0 -9
- package/.claude-flow/metrics/system-metrics.json +0 -15986
- package/.claude-flow/metrics/task-metrics.json +0 -10
- package/.roo/README.md +0 -402
- package/.roo/mcp-list.txt +0 -257
- package/.roo/mcp.json +0 -32
- package/.roo/workflows/basic-tdd.json +0 -32
- package/.roomodes +0 -201
- package/CLAUDE.md +0 -393
- package/bin/snow-flow.js +0 -3
- package/dist/cli.js.bak +0 -2052
- package/dist/config/snow-flow-config.d.ts +0 -1492
- package/dist/config/snow-flow-config.js +0 -938
- package/scripts/create-env.js +0 -91
- package/scripts/postinstall.js +0 -30
- package/scripts/register-mcp-servers.js +0 -210
- package/scripts/reset-mcp-servers.js +0 -246
- package/scripts/setup-mcp.js +0 -106
- package/scripts/update-version.js +0 -31
- package/src/agents/README.md +0 -192
- package/src/agents/base-agent.ts +0 -251
- package/src/agents/coordinator.ts +0 -971
- package/src/agents/index.ts +0 -18
- package/src/agents/queen-403-handler.ts +0 -230
- package/src/agents/queen-agent.ts +0 -1399
- package/src/agents/script-writer-agent.ts +0 -769
- package/src/agents/security-agent.ts +0 -726
- package/src/agents/widget-creator-agent.ts +0 -568
- package/src/api/error-handling.ts +0 -1758
- package/src/api/natural-language-mapper.ts +0 -630
- package/src/api/performance-optimizer.ts +0 -891
- package/src/api/transaction-manager.ts +0 -754
- package/src/cli/deploy-artifact.ts +0 -304
- package/src/cli/snow-flow-cli-integration.ts +0 -434
- package/src/cli.ts +0 -4455
- package/src/cli.ts.bak +0 -5616
- package/src/compliance/advanced-compliance-system.ts +0 -1268
- package/src/compliance/index.ts +0 -19
- package/src/config/snow-flow-config.ts +0 -1019
- package/src/documentation/index.ts +0 -16
- package/src/documentation/self-documenting-system.ts +0 -1421
- package/src/dynamic-version.ts +0 -44
- package/src/healing/index.ts +0 -20
- package/src/healing/self-healing-system.ts +0 -1435
- package/src/health/README.md +0 -161
- package/src/health/system-health.ts +0 -995
- package/src/index.ts +0 -60
- package/src/intelligence/acl-analyzer.ts +0 -377
- package/src/intelligence/auto-resolution-engine.ts +0 -643
- package/src/intelligence/gap-analysis-engine.ts +0 -488
- package/src/intelligence/manual-instructions-generator.ts +0 -744
- package/src/intelligence/mcp-coverage-analyzer.ts +0 -708
- package/src/intelligence/multi-pass-requirements-analyzer.ts +0 -603
- package/src/intelligence/performance-recommendations-engine.ts +0 -1223
- package/src/intelligence/requirements-analyzer.ts +0 -1148
- package/src/intelligence/task-analyzer.ts +0 -628
- package/src/managers/scope-manager.ts +0 -555
- package/src/mcp/advanced/servicenow-advanced-features-mcp.ts +0 -16110
- package/src/mcp/base-mcp-server.ts +0 -660
- package/src/mcp/http-transport-wrapper.ts +0 -426
- package/src/mcp/service-discovery-client.ts +0 -312
- package/src/mcp/servicenow-automation-mcp-refactored.ts +0 -678
- package/src/mcp/servicenow-automation-mcp.ts +0 -838
- package/src/mcp/servicenow-deployment-mcp-refactored.ts +0 -1292
- package/src/mcp/servicenow-deployment-mcp.ts +0 -7244
- package/src/mcp/servicenow-graph-memory-mcp-refactored.ts +0 -721
- package/src/mcp/servicenow-graph-memory-mcp.ts +0 -832
- package/src/mcp/servicenow-integration-mcp-refactored.ts +0 -655
- package/src/mcp/servicenow-integration-mcp.ts +0 -819
- package/src/mcp/servicenow-intelligent-mcp-refactored.ts +0 -977
- package/src/mcp/servicenow-intelligent-mcp.ts +0 -4638
- package/src/mcp/servicenow-mcp-server.ts +0 -728
- package/src/mcp/servicenow-memory-mcp.ts +0 -474
- package/src/mcp/servicenow-operations-mcp-refactored.ts +0 -1995
- package/src/mcp/servicenow-operations-mcp.ts +0 -3384
- package/src/mcp/servicenow-platform-development-mcp-refactored.ts +0 -611
- package/src/mcp/servicenow-platform-development-mcp.ts +0 -970
- package/src/mcp/servicenow-progressive-indexer.ts +0 -370
- package/src/mcp/servicenow-reporting-analytics-mcp-refactored.ts +0 -695
- package/src/mcp/servicenow-reporting-analytics-mcp.ts +0 -1123
- package/src/mcp/servicenow-security-compliance-mcp-refactored.ts +0 -642
- package/src/mcp/servicenow-security-compliance-mcp.ts +0 -1157
- package/src/mcp/servicenow-update-set-mcp-refactored.ts +0 -751
- package/src/mcp/servicenow-update-set-mcp.ts +0 -811
- package/src/mcp/shared/agent-context-provider.ts +0 -365
- package/src/mcp/shared/base-mcp-server.ts +0 -2463
- package/src/mcp/shared/mcp-memory-manager.ts +0 -540
- package/src/mcp/shared/mcp-resource-manager.ts +0 -367
- package/src/mcp/snow-flow-mcp.ts +0 -911
- package/src/mcp/start-all-mcp-servers.ts +0 -102
- package/src/mcp/start-servicenow-mcp.ts +0 -67
- package/src/monitoring/enhanced-monitoring-system.ts +0 -1345
- package/src/monitoring/performance-tracker.ts +0 -777
- package/src/optimization/cost-optimization-engine.ts +0 -1125
- package/src/optimization/index.ts +0 -10
- package/src/queen/README.md +0 -403
- package/src/queen/agent-factory.ts +0 -574
- package/src/queen/index.ts +0 -94
- package/src/queen/mcp-execution-bridge.ts +0 -681
- package/src/queen/neural-learning.ts +0 -319
- package/src/queen/parallel-agent-engine.ts +0 -848
- package/src/queen/queen-memory-system.ts +0 -490
- package/src/queen/queen-memory.ts +0 -456
- package/src/queen/servicenow-queen.ts +0 -1029
- package/src/queen/types.ts +0 -87
- package/src/rollback/smart-rollback-system.ts +0 -866
- package/src/schemas/deployment.schema.json +0 -58
- package/src/schemas/flow.schema.json +0 -79
- package/src/schemas/widget.schema.json +0 -72
- package/src/snow-flow-system.ts +0 -541
- package/src/sparc/sparc-help.ts +0 -49
- package/src/sparc/team-sparc.ts +0 -88
- package/src/strategies/global-scope-strategy.ts +0 -736
- package/src/templates/base/application.template.json +0 -45
- package/src/templates/base/business_rule.template.json +0 -33
- package/src/templates/base/script_include.template.json +0 -18
- package/src/templates/base/table.template.json +0 -64
- package/src/templates/base/widget.template.json +0 -25
- package/src/templates/patterns/composite.incident-management.template.json +0 -140
- package/src/templates/patterns/widget.dashboard.template.json +0 -238
- package/src/templates/patterns/widget.datatable.template.json +0 -292
- package/src/testing/integration-test-suite.ts +0 -1276
- package/src/types/index.ts +0 -183
- package/src/types/neo4j-driver.d.ts +0 -23
- package/src/types/servicenow.types.ts +0 -307
- package/src/types/snow-flow.types.ts +0 -186
- package/src/types/todo.types.ts +0 -70
- package/src/utils/action-type-cache.ts +0 -286
- package/src/utils/agent-detector.ts +0 -573
- package/src/utils/artifact-tracker.ts +0 -379
- package/src/utils/dependency-detector.ts +0 -233
- package/src/utils/deployment-metadata-handler.ts +0 -518
- package/src/utils/error-recovery.ts +0 -680
- package/src/utils/logger.ts +0 -60
- package/src/utils/mcp-auth-middleware.ts +0 -262
- package/src/utils/mcp-config-manager.ts +0 -270
- package/src/utils/mcp-server-manager.ts +0 -446
- package/src/utils/mcp-tool-registry.ts +0 -298
- package/src/utils/migrate-snow-flow.ts +0 -98
- package/src/utils/oauth-html-templates.ts +0 -188
- package/src/utils/scope-utils.ts +0 -443
- package/src/utils/servicenow-client.ts +0 -3321
- package/src/utils/servicenow-id-generator.ts +0 -181
- package/src/utils/snow-memory-manager.ts +0 -605
- package/src/utils/snow-oauth.ts +0 -797
- package/src/utils/template-engine.ts +0 -463
- package/src/utils/theme-manager.ts +0 -295
- package/src/utils/unified-auth-store.ts +0 -222
- package/src/utils/update-set-importer.ts +0 -422
- package/src/utils/update-set-xml-packager.ts +0 -371
- package/src/utils/widget-template-generator.ts +0 -1744
- package/src/version.ts +0 -1950
|
@@ -1,754 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Transaction Management for Multi-Step Flow Creation
|
|
3
|
-
* Provides ACID-like properties for complex flow operations
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { ServiceNowClient } from '../utils/servicenow-client.js';
|
|
7
|
-
import { FlowErrorHandler, RollbackOperation, TransactionState } from './error-handling.js';
|
|
8
|
-
import { Logger } from '../utils/logger.js';
|
|
9
|
-
|
|
10
|
-
export interface TransactionDefinition {
|
|
11
|
-
id: string;
|
|
12
|
-
name: string;
|
|
13
|
-
description: string;
|
|
14
|
-
steps: TransactionStep[];
|
|
15
|
-
timeout: number;
|
|
16
|
-
isolation_level: 'read_uncommitted' | 'read_committed' | 'repeatable_read' | 'serializable';
|
|
17
|
-
rollback_on_error: boolean;
|
|
18
|
-
retry_policy: RetryPolicy;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export interface TransactionStep {
|
|
22
|
-
id: string;
|
|
23
|
-
name: string;
|
|
24
|
-
operation: 'create' | 'update' | 'delete' | 'validate' | 'custom';
|
|
25
|
-
table: string;
|
|
26
|
-
data?: any;
|
|
27
|
-
condition?: string;
|
|
28
|
-
dependencies: string[];
|
|
29
|
-
compensation?: CompensationAction;
|
|
30
|
-
timeout: number;
|
|
31
|
-
retry_count: number;
|
|
32
|
-
critical: boolean;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export interface CompensationAction {
|
|
36
|
-
type: 'delete' | 'update' | 'restore' | 'custom';
|
|
37
|
-
table: string;
|
|
38
|
-
data?: any;
|
|
39
|
-
custom_function?: () => Promise<void>;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export interface RetryPolicy {
|
|
43
|
-
max_attempts: number;
|
|
44
|
-
initial_delay: number;
|
|
45
|
-
max_delay: number;
|
|
46
|
-
backoff_multiplier: number;
|
|
47
|
-
retryable_errors: string[];
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export interface TransactionExecution {
|
|
51
|
-
id: string;
|
|
52
|
-
definition: TransactionDefinition;
|
|
53
|
-
state: TransactionExecutionState;
|
|
54
|
-
start_time: Date;
|
|
55
|
-
end_time?: Date;
|
|
56
|
-
current_step?: string;
|
|
57
|
-
completed_steps: string[];
|
|
58
|
-
failed_steps: string[];
|
|
59
|
-
results: Map<string, any>;
|
|
60
|
-
errors: Map<string, Error>;
|
|
61
|
-
compensations: CompensationAction[];
|
|
62
|
-
locks: Set<string>;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export enum TransactionExecutionState {
|
|
66
|
-
PENDING = 'pending',
|
|
67
|
-
RUNNING = 'running',
|
|
68
|
-
COMPLETED = 'completed',
|
|
69
|
-
FAILED = 'failed',
|
|
70
|
-
COMPENSATING = 'compensating',
|
|
71
|
-
COMPENSATED = 'compensated',
|
|
72
|
-
TIMEOUT = 'timeout'
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
export interface LockManager {
|
|
76
|
-
acquire(resource: string, timeout: number): Promise<string>;
|
|
77
|
-
release(lockId: string): Promise<void>;
|
|
78
|
-
isLocked(resource: string): boolean;
|
|
79
|
-
getLockOwner(resource: string): string | null;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
export class TransactionManager {
|
|
83
|
-
private client: ServiceNowClient;
|
|
84
|
-
private errorHandler: FlowErrorHandler;
|
|
85
|
-
private logger: Logger;
|
|
86
|
-
private activeTransactions: Map<string, TransactionExecution> = new Map();
|
|
87
|
-
private lockManager: LockManager;
|
|
88
|
-
private sagaLog: Map<string, any[]> = new Map();
|
|
89
|
-
|
|
90
|
-
constructor(client: ServiceNowClient, errorHandler: FlowErrorHandler) {
|
|
91
|
-
this.client = client;
|
|
92
|
-
this.errorHandler = errorHandler;
|
|
93
|
-
this.logger = new Logger('TransactionManager');
|
|
94
|
-
this.lockManager = new SimpleLockManager();
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Execute a transaction with ACID properties
|
|
99
|
-
*/
|
|
100
|
-
async executeTransaction(definition: TransactionDefinition): Promise<{
|
|
101
|
-
success: boolean;
|
|
102
|
-
transaction_id: string;
|
|
103
|
-
results?: Map<string, any>;
|
|
104
|
-
errors?: Error[];
|
|
105
|
-
compensation_applied?: boolean;
|
|
106
|
-
}> {
|
|
107
|
-
const execution: TransactionExecution = {
|
|
108
|
-
id: definition.id,
|
|
109
|
-
definition,
|
|
110
|
-
state: TransactionExecutionState.PENDING,
|
|
111
|
-
start_time: new Date(),
|
|
112
|
-
completed_steps: [],
|
|
113
|
-
failed_steps: [],
|
|
114
|
-
results: new Map(),
|
|
115
|
-
errors: new Map(),
|
|
116
|
-
compensations: [],
|
|
117
|
-
locks: new Set()
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
this.activeTransactions.set(definition.id, execution);
|
|
121
|
-
|
|
122
|
-
try {
|
|
123
|
-
// Initialize saga log
|
|
124
|
-
this.sagaLog.set(definition.id, []);
|
|
125
|
-
|
|
126
|
-
// Start transaction
|
|
127
|
-
await this.startTransaction(execution);
|
|
128
|
-
|
|
129
|
-
// Execute steps
|
|
130
|
-
const result = await this.executeSteps(execution);
|
|
131
|
-
|
|
132
|
-
if (result.success) {
|
|
133
|
-
await this.commitTransaction(execution);
|
|
134
|
-
return {
|
|
135
|
-
success: true,
|
|
136
|
-
transaction_id: execution.id,
|
|
137
|
-
results: execution.results
|
|
138
|
-
};
|
|
139
|
-
} else {
|
|
140
|
-
await this.rollbackTransaction(execution);
|
|
141
|
-
return {
|
|
142
|
-
success: false,
|
|
143
|
-
transaction_id: execution.id,
|
|
144
|
-
errors: Array.from(execution.errors.values()),
|
|
145
|
-
compensation_applied: true
|
|
146
|
-
};
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
} catch (error) {
|
|
150
|
-
this.logger.error('Transaction execution failed', { transactionId: definition.id, error });
|
|
151
|
-
await this.rollbackTransaction(execution);
|
|
152
|
-
|
|
153
|
-
return {
|
|
154
|
-
success: false,
|
|
155
|
-
transaction_id: execution.id,
|
|
156
|
-
errors: [error instanceof Error ? error : new Error(String(error))],
|
|
157
|
-
compensation_applied: true
|
|
158
|
-
};
|
|
159
|
-
} finally {
|
|
160
|
-
this.activeTransactions.delete(definition.id);
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Execute a saga pattern transaction
|
|
166
|
-
*/
|
|
167
|
-
async executeSaga(definition: TransactionDefinition): Promise<{
|
|
168
|
-
success: boolean;
|
|
169
|
-
transaction_id: string;
|
|
170
|
-
results?: Map<string, any>;
|
|
171
|
-
compensations_applied?: CompensationAction[];
|
|
172
|
-
}> {
|
|
173
|
-
const execution: TransactionExecution = {
|
|
174
|
-
id: definition.id,
|
|
175
|
-
definition,
|
|
176
|
-
state: TransactionExecutionState.PENDING,
|
|
177
|
-
start_time: new Date(),
|
|
178
|
-
completed_steps: [],
|
|
179
|
-
failed_steps: [],
|
|
180
|
-
results: new Map(),
|
|
181
|
-
errors: new Map(),
|
|
182
|
-
compensations: [],
|
|
183
|
-
locks: new Set()
|
|
184
|
-
};
|
|
185
|
-
|
|
186
|
-
this.activeTransactions.set(definition.id, execution);
|
|
187
|
-
|
|
188
|
-
try {
|
|
189
|
-
execution.state = TransactionExecutionState.RUNNING;
|
|
190
|
-
|
|
191
|
-
// Execute steps with saga pattern
|
|
192
|
-
for (const step of definition.steps) {
|
|
193
|
-
try {
|
|
194
|
-
const stepResult = await this.executeStep(execution, step);
|
|
195
|
-
execution.results.set(step.id, stepResult);
|
|
196
|
-
execution.completed_steps.push(step.id);
|
|
197
|
-
|
|
198
|
-
// Log successful step for saga
|
|
199
|
-
this.logSagaStep(execution.id, step, stepResult);
|
|
200
|
-
|
|
201
|
-
} catch (error) {
|
|
202
|
-
execution.errors.set(step.id, error instanceof Error ? error : new Error(String(error)));
|
|
203
|
-
execution.failed_steps.push(step.id);
|
|
204
|
-
|
|
205
|
-
// If step failed, compensate previous steps
|
|
206
|
-
await this.compensateSteps(execution);
|
|
207
|
-
|
|
208
|
-
return {
|
|
209
|
-
success: false,
|
|
210
|
-
transaction_id: execution.id,
|
|
211
|
-
compensations_applied: execution.compensations
|
|
212
|
-
};
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
execution.state = TransactionExecutionState.COMPLETED;
|
|
217
|
-
execution.end_time = new Date();
|
|
218
|
-
|
|
219
|
-
return {
|
|
220
|
-
success: true,
|
|
221
|
-
transaction_id: execution.id,
|
|
222
|
-
results: execution.results
|
|
223
|
-
};
|
|
224
|
-
|
|
225
|
-
} catch (error) {
|
|
226
|
-
execution.state = TransactionExecutionState.FAILED;
|
|
227
|
-
execution.end_time = new Date();
|
|
228
|
-
|
|
229
|
-
await this.compensateSteps(execution);
|
|
230
|
-
|
|
231
|
-
return {
|
|
232
|
-
success: false,
|
|
233
|
-
transaction_id: execution.id,
|
|
234
|
-
compensations_applied: execution.compensations
|
|
235
|
-
};
|
|
236
|
-
} finally {
|
|
237
|
-
this.activeTransactions.delete(definition.id);
|
|
238
|
-
this.sagaLog.delete(definition.id);
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
/**
|
|
243
|
-
* Create a distributed transaction across multiple ServiceNow instances
|
|
244
|
-
*/
|
|
245
|
-
async executeDistributedTransaction(
|
|
246
|
-
definition: TransactionDefinition,
|
|
247
|
-
participants: string[]
|
|
248
|
-
): Promise<{
|
|
249
|
-
success: boolean;
|
|
250
|
-
transaction_id: string;
|
|
251
|
-
participant_results?: Map<string, any>;
|
|
252
|
-
failed_participants?: string[];
|
|
253
|
-
}> {
|
|
254
|
-
const transactionId = definition.id;
|
|
255
|
-
const participantResults = new Map<string, any>();
|
|
256
|
-
const failedParticipants: string[] = [];
|
|
257
|
-
|
|
258
|
-
try {
|
|
259
|
-
// Phase 1: Prepare all participants
|
|
260
|
-
const prepareResults = await Promise.allSettled(
|
|
261
|
-
participants.map(participant => this.prepareParticipant(participant, definition))
|
|
262
|
-
);
|
|
263
|
-
|
|
264
|
-
// Check if all participants are prepared
|
|
265
|
-
const allPrepared = prepareResults.every(result => result.status === 'fulfilled');
|
|
266
|
-
|
|
267
|
-
if (!allPrepared) {
|
|
268
|
-
// Abort transaction - some participants failed to prepare
|
|
269
|
-
await this.abortDistributedTransaction(transactionId, participants);
|
|
270
|
-
|
|
271
|
-
prepareResults.forEach((result, index) => {
|
|
272
|
-
if (result.status === 'rejected') {
|
|
273
|
-
failedParticipants.push(participants[index]);
|
|
274
|
-
}
|
|
275
|
-
});
|
|
276
|
-
|
|
277
|
-
return {
|
|
278
|
-
success: false,
|
|
279
|
-
transaction_id: transactionId,
|
|
280
|
-
failed_participants: failedParticipants
|
|
281
|
-
};
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
// Phase 2: Commit all participants
|
|
285
|
-
const commitResults = await Promise.allSettled(
|
|
286
|
-
participants.map(participant => this.commitParticipant(participant, transactionId))
|
|
287
|
-
);
|
|
288
|
-
|
|
289
|
-
// Check if all participants committed successfully
|
|
290
|
-
const allCommitted = commitResults.every(result => result.status === 'fulfilled');
|
|
291
|
-
|
|
292
|
-
if (!allCommitted) {
|
|
293
|
-
// Some participants failed to commit - this is a critical situation
|
|
294
|
-
this.logger.error('Distributed transaction commit failed', {
|
|
295
|
-
transactionId,
|
|
296
|
-
failedParticipants: commitResults
|
|
297
|
-
.map((result, index) => result.status === 'rejected' ? participants[index] : null)
|
|
298
|
-
.filter(Boolean)
|
|
299
|
-
});
|
|
300
|
-
|
|
301
|
-
// Attempt to rollback successful commits
|
|
302
|
-
await this.rollbackDistributedTransaction(transactionId, participants);
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
commitResults.forEach((result, index) => {
|
|
306
|
-
if (result.status === 'fulfilled') {
|
|
307
|
-
participantResults.set(participants[index], result.value);
|
|
308
|
-
} else {
|
|
309
|
-
failedParticipants.push(participants[index]);
|
|
310
|
-
}
|
|
311
|
-
});
|
|
312
|
-
|
|
313
|
-
return {
|
|
314
|
-
success: allCommitted,
|
|
315
|
-
transaction_id: transactionId,
|
|
316
|
-
participant_results: participantResults,
|
|
317
|
-
failed_participants: failedParticipants.length > 0 ? failedParticipants : undefined
|
|
318
|
-
};
|
|
319
|
-
|
|
320
|
-
} catch (error) {
|
|
321
|
-
this.logger.error('Distributed transaction failed', { transactionId, error });
|
|
322
|
-
await this.abortDistributedTransaction(transactionId, participants);
|
|
323
|
-
|
|
324
|
-
return {
|
|
325
|
-
success: false,
|
|
326
|
-
transaction_id: transactionId,
|
|
327
|
-
failed_participants: participants
|
|
328
|
-
};
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
/**
|
|
333
|
-
* Start transaction with isolation
|
|
334
|
-
*/
|
|
335
|
-
private async startTransaction(execution: TransactionExecution): Promise<void> {
|
|
336
|
-
execution.state = TransactionExecutionState.RUNNING;
|
|
337
|
-
this.logger.info('Starting transaction', { transactionId: execution.id });
|
|
338
|
-
|
|
339
|
-
// Acquire locks based on isolation level
|
|
340
|
-
await this.acquireLocks(execution);
|
|
341
|
-
|
|
342
|
-
// Start ServiceNow transaction tracking
|
|
343
|
-
const transactionId = this.errorHandler.startTransaction(execution.definition.name);
|
|
344
|
-
execution.id = transactionId;
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
/**
|
|
348
|
-
* Execute all steps in the transaction
|
|
349
|
-
*/
|
|
350
|
-
private async executeSteps(execution: TransactionExecution): Promise<{ success: boolean }> {
|
|
351
|
-
const steps = this.orderStepsByDependencies(execution.definition.steps);
|
|
352
|
-
|
|
353
|
-
for (const step of steps) {
|
|
354
|
-
execution.current_step = step.id;
|
|
355
|
-
|
|
356
|
-
try {
|
|
357
|
-
// Check timeout
|
|
358
|
-
if (this.isTransactionTimeout(execution)) {
|
|
359
|
-
execution.state = TransactionExecutionState.TIMEOUT;
|
|
360
|
-
throw new Error('Transaction timeout');
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
// Execute step with retry
|
|
364
|
-
const result = await this.executeStepWithRetry(execution, step);
|
|
365
|
-
execution.results.set(step.id, result);
|
|
366
|
-
execution.completed_steps.push(step.id);
|
|
367
|
-
|
|
368
|
-
// Add to rollback stack
|
|
369
|
-
this.errorHandler.addOperation(execution.id, {
|
|
370
|
-
type: step.operation as any,
|
|
371
|
-
table: step.table,
|
|
372
|
-
record_id: result?.sys_id,
|
|
373
|
-
original_data: step.operation === 'update' ? step.data : undefined,
|
|
374
|
-
new_data: result,
|
|
375
|
-
dependencies: step.dependencies
|
|
376
|
-
});
|
|
377
|
-
|
|
378
|
-
} catch (error) {
|
|
379
|
-
execution.errors.set(step.id, error instanceof Error ? error : new Error(String(error)));
|
|
380
|
-
execution.failed_steps.push(step.id);
|
|
381
|
-
|
|
382
|
-
if (step.critical || execution.definition.rollback_on_error) {
|
|
383
|
-
return { success: false };
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
// Continue with non-critical steps
|
|
387
|
-
this.logger.warn('Non-critical step failed, continuing', { stepId: step.id, error });
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
return { success: execution.failed_steps.length === 0 };
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
/**
|
|
395
|
-
* Execute a single step with retry logic
|
|
396
|
-
*/
|
|
397
|
-
private async executeStepWithRetry(execution: TransactionExecution, step: TransactionStep): Promise<any> {
|
|
398
|
-
const retryPolicy = execution.definition.retry_policy;
|
|
399
|
-
let lastError: Error | null = null;
|
|
400
|
-
let delay = retryPolicy.initial_delay;
|
|
401
|
-
|
|
402
|
-
for (let attempt = 1; attempt <= retryPolicy.max_attempts; attempt++) {
|
|
403
|
-
try {
|
|
404
|
-
return await this.executeStep(execution, step);
|
|
405
|
-
} catch (error) {
|
|
406
|
-
lastError = error instanceof Error ? error : new Error(String(error));
|
|
407
|
-
|
|
408
|
-
// Check if error is retryable
|
|
409
|
-
const isRetryable = retryPolicy.retryable_errors.some(retryableError =>
|
|
410
|
-
lastError!.message.toLowerCase().includes(retryableError.toLowerCase())
|
|
411
|
-
);
|
|
412
|
-
|
|
413
|
-
if (!isRetryable || attempt === retryPolicy.max_attempts) {
|
|
414
|
-
throw lastError;
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
// Wait before retry
|
|
418
|
-
await new Promise(resolve => setTimeout(resolve, delay));
|
|
419
|
-
delay = Math.min(delay * retryPolicy.backoff_multiplier, retryPolicy.max_delay);
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
throw lastError;
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
/**
|
|
427
|
-
* Execute a single step
|
|
428
|
-
*/
|
|
429
|
-
private async executeStep(execution: TransactionExecution, step: TransactionStep): Promise<any> {
|
|
430
|
-
this.logger.debug('Executing step', { transactionId: execution.id, stepId: step.id });
|
|
431
|
-
|
|
432
|
-
// Check dependencies
|
|
433
|
-
for (const dependency of step.dependencies) {
|
|
434
|
-
if (!execution.completed_steps.includes(dependency)) {
|
|
435
|
-
throw new Error(`Dependency ${dependency} not completed`);
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
// Execute based on operation type
|
|
440
|
-
switch (step.operation) {
|
|
441
|
-
case 'create':
|
|
442
|
-
return await this.client.createRecord(step.table, step.data);
|
|
443
|
-
|
|
444
|
-
case 'update':
|
|
445
|
-
if (!step.data?.sys_id) {
|
|
446
|
-
throw new Error('sys_id required for update operation');
|
|
447
|
-
}
|
|
448
|
-
return await this.client.updateRecord(step.table, step.data.sys_id, step.data);
|
|
449
|
-
|
|
450
|
-
case 'delete':
|
|
451
|
-
if (!step.data?.sys_id) {
|
|
452
|
-
throw new Error('sys_id required for delete operation');
|
|
453
|
-
}
|
|
454
|
-
return await this.client.deleteRecord(step.table, step.data.sys_id);
|
|
455
|
-
|
|
456
|
-
case 'validate':
|
|
457
|
-
return await this.validateData(step.table, step.data, step.condition);
|
|
458
|
-
|
|
459
|
-
case 'custom':
|
|
460
|
-
if (step.data?.custom_function) {
|
|
461
|
-
return await step.data.custom_function();
|
|
462
|
-
}
|
|
463
|
-
throw new Error('Custom function not provided');
|
|
464
|
-
|
|
465
|
-
default:
|
|
466
|
-
throw new Error(`Unknown operation: ${step.operation}`);
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
/**
|
|
471
|
-
* Commit transaction
|
|
472
|
-
*/
|
|
473
|
-
private async commitTransaction(execution: TransactionExecution): Promise<void> {
|
|
474
|
-
execution.state = TransactionExecutionState.COMPLETED;
|
|
475
|
-
execution.end_time = new Date();
|
|
476
|
-
|
|
477
|
-
// Commit ServiceNow transaction
|
|
478
|
-
await this.errorHandler.commitTransaction(execution.id);
|
|
479
|
-
|
|
480
|
-
// Release locks
|
|
481
|
-
await this.releaseLocks(execution);
|
|
482
|
-
|
|
483
|
-
this.logger.info('Transaction committed', { transactionId: execution.id });
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
/**
|
|
487
|
-
* Rollback transaction
|
|
488
|
-
*/
|
|
489
|
-
private async rollbackTransaction(execution: TransactionExecution): Promise<void> {
|
|
490
|
-
execution.state = TransactionExecutionState.COMPENSATING;
|
|
491
|
-
|
|
492
|
-
try {
|
|
493
|
-
// Rollback ServiceNow transaction
|
|
494
|
-
await this.errorHandler.rollbackTransaction(execution.id);
|
|
495
|
-
|
|
496
|
-
// Apply compensations
|
|
497
|
-
await this.compensateSteps(execution);
|
|
498
|
-
|
|
499
|
-
execution.state = TransactionExecutionState.COMPENSATED;
|
|
500
|
-
} catch (error) {
|
|
501
|
-
execution.state = TransactionExecutionState.FAILED;
|
|
502
|
-
this.logger.error('Transaction rollback failed', { transactionId: execution.id, error });
|
|
503
|
-
} finally {
|
|
504
|
-
// Always release locks
|
|
505
|
-
await this.releaseLocks(execution);
|
|
506
|
-
execution.end_time = new Date();
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
/**
|
|
511
|
-
* Apply compensations for completed steps
|
|
512
|
-
*/
|
|
513
|
-
private async compensateSteps(execution: TransactionExecution): Promise<void> {
|
|
514
|
-
const completedSteps = execution.definition.steps.filter(step =>
|
|
515
|
-
execution.completed_steps.includes(step.id)
|
|
516
|
-
);
|
|
517
|
-
|
|
518
|
-
// Apply compensations in reverse order
|
|
519
|
-
for (const step of completedSteps.reverse()) {
|
|
520
|
-
if (step.compensation) {
|
|
521
|
-
try {
|
|
522
|
-
await this.applyCompensation(execution, step.compensation);
|
|
523
|
-
execution.compensations.push(step.compensation);
|
|
524
|
-
} catch (error) {
|
|
525
|
-
this.logger.error('Compensation failed', { stepId: step.id, error });
|
|
526
|
-
}
|
|
527
|
-
}
|
|
528
|
-
}
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
/**
|
|
532
|
-
* Apply a compensation action
|
|
533
|
-
*/
|
|
534
|
-
private async applyCompensation(execution: TransactionExecution, compensation: CompensationAction): Promise<void> {
|
|
535
|
-
switch (compensation.type) {
|
|
536
|
-
case 'delete':
|
|
537
|
-
const result = execution.results.get(compensation.table);
|
|
538
|
-
if (result?.sys_id) {
|
|
539
|
-
await this.client.deleteRecord(compensation.table, result.sys_id);
|
|
540
|
-
}
|
|
541
|
-
break;
|
|
542
|
-
|
|
543
|
-
case 'update':
|
|
544
|
-
if (compensation.data?.sys_id) {
|
|
545
|
-
await this.client.updateRecord(compensation.table, compensation.data.sys_id, compensation.data);
|
|
546
|
-
}
|
|
547
|
-
break;
|
|
548
|
-
|
|
549
|
-
case 'restore':
|
|
550
|
-
if (compensation.data) {
|
|
551
|
-
await this.client.createRecord(compensation.table, compensation.data);
|
|
552
|
-
}
|
|
553
|
-
break;
|
|
554
|
-
|
|
555
|
-
case 'custom':
|
|
556
|
-
if (compensation.custom_function) {
|
|
557
|
-
await compensation.custom_function();
|
|
558
|
-
}
|
|
559
|
-
break;
|
|
560
|
-
}
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
/**
|
|
564
|
-
* Acquire locks for transaction isolation
|
|
565
|
-
*/
|
|
566
|
-
private async acquireLocks(execution: TransactionExecution): Promise<void> {
|
|
567
|
-
const resourcesToLock = new Set<string>();
|
|
568
|
-
|
|
569
|
-
// Collect resources to lock
|
|
570
|
-
for (const step of execution.definition.steps) {
|
|
571
|
-
if (step.operation === 'update' || step.operation === 'delete') {
|
|
572
|
-
resourcesToLock.add(`${step.table}:${step.data?.sys_id}`);
|
|
573
|
-
} else if (step.operation === 'create') {
|
|
574
|
-
resourcesToLock.add(`${step.table}:create`);
|
|
575
|
-
}
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
// Acquire locks
|
|
579
|
-
for (const resource of resourcesToLock) {
|
|
580
|
-
try {
|
|
581
|
-
const lockId = await this.lockManager.acquire(resource, execution.definition.timeout);
|
|
582
|
-
execution.locks.add(lockId);
|
|
583
|
-
} catch (error) {
|
|
584
|
-
// Release already acquired locks
|
|
585
|
-
await this.releaseLocks(execution);
|
|
586
|
-
throw new Error(`Failed to acquire lock for ${resource}: ${error}`);
|
|
587
|
-
}
|
|
588
|
-
}
|
|
589
|
-
}
|
|
590
|
-
|
|
591
|
-
/**
|
|
592
|
-
* Release all locks
|
|
593
|
-
*/
|
|
594
|
-
private async releaseLocks(execution: TransactionExecution): Promise<void> {
|
|
595
|
-
for (const lockId of execution.locks) {
|
|
596
|
-
try {
|
|
597
|
-
await this.lockManager.release(lockId);
|
|
598
|
-
} catch (error) {
|
|
599
|
-
this.logger.warn('Failed to release lock', { lockId, error });
|
|
600
|
-
}
|
|
601
|
-
}
|
|
602
|
-
execution.locks.clear();
|
|
603
|
-
}
|
|
604
|
-
|
|
605
|
-
/**
|
|
606
|
-
* Order steps by dependencies
|
|
607
|
-
*/
|
|
608
|
-
private orderStepsByDependencies(steps: TransactionStep[]): TransactionStep[] {
|
|
609
|
-
const ordered: TransactionStep[] = [];
|
|
610
|
-
const remaining = [...steps];
|
|
611
|
-
|
|
612
|
-
while (remaining.length > 0) {
|
|
613
|
-
const nextSteps = remaining.filter(step =>
|
|
614
|
-
step.dependencies.every(dep => ordered.some(orderedStep => orderedStep.id === dep))
|
|
615
|
-
);
|
|
616
|
-
|
|
617
|
-
if (nextSteps.length === 0) {
|
|
618
|
-
throw new Error('Circular dependency detected in transaction steps');
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
ordered.push(...nextSteps);
|
|
622
|
-
remaining.splice(0, remaining.length, ...remaining.filter(step => !nextSteps.includes(step)));
|
|
623
|
-
}
|
|
624
|
-
|
|
625
|
-
return ordered;
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
/**
|
|
629
|
-
* Check if transaction has timed out
|
|
630
|
-
*/
|
|
631
|
-
private isTransactionTimeout(execution: TransactionExecution): boolean {
|
|
632
|
-
const elapsed = Date.now() - execution.start_time.getTime();
|
|
633
|
-
return elapsed > execution.definition.timeout;
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
/**
|
|
637
|
-
* Validate data against condition
|
|
638
|
-
*/
|
|
639
|
-
private async validateData(table: string, data: any, condition?: string): Promise<boolean> {
|
|
640
|
-
if (!condition) return true;
|
|
641
|
-
|
|
642
|
-
// Simple validation logic - could be enhanced with more complex rules
|
|
643
|
-
const record = await this.client.getRecord(table, data.sys_id);
|
|
644
|
-
return record !== null;
|
|
645
|
-
}
|
|
646
|
-
|
|
647
|
-
/**
|
|
648
|
-
* Log saga step
|
|
649
|
-
*/
|
|
650
|
-
private logSagaStep(transactionId: string, step: TransactionStep, result: any): void {
|
|
651
|
-
const sagaLog = this.sagaLog.get(transactionId) || [];
|
|
652
|
-
sagaLog.push({
|
|
653
|
-
step_id: step.id,
|
|
654
|
-
operation: step.operation,
|
|
655
|
-
table: step.table,
|
|
656
|
-
result: result,
|
|
657
|
-
timestamp: new Date()
|
|
658
|
-
});
|
|
659
|
-
this.sagaLog.set(transactionId, sagaLog);
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
/**
|
|
663
|
-
* Prepare participant for distributed transaction
|
|
664
|
-
*/
|
|
665
|
-
private async prepareParticipant(participant: string, definition: TransactionDefinition): Promise<any> {
|
|
666
|
-
// Implementation would send prepare request to participant
|
|
667
|
-
// For now, return success
|
|
668
|
-
return { prepared: true, participant };
|
|
669
|
-
}
|
|
670
|
-
|
|
671
|
-
/**
|
|
672
|
-
* Commit participant in distributed transaction
|
|
673
|
-
*/
|
|
674
|
-
private async commitParticipant(participant: string, transactionId: string): Promise<any> {
|
|
675
|
-
// Implementation would send commit request to participant
|
|
676
|
-
return { committed: true, participant };
|
|
677
|
-
}
|
|
678
|
-
|
|
679
|
-
/**
|
|
680
|
-
* Abort distributed transaction
|
|
681
|
-
*/
|
|
682
|
-
private async abortDistributedTransaction(transactionId: string, participants: string[]): Promise<void> {
|
|
683
|
-
// Implementation would send abort request to all participants
|
|
684
|
-
this.logger.info('Aborting distributed transaction', { transactionId, participants });
|
|
685
|
-
}
|
|
686
|
-
|
|
687
|
-
/**
|
|
688
|
-
* Rollback distributed transaction
|
|
689
|
-
*/
|
|
690
|
-
private async rollbackDistributedTransaction(transactionId: string, participants: string[]): Promise<void> {
|
|
691
|
-
// Implementation would send rollback request to all participants
|
|
692
|
-
this.logger.info('Rolling back distributed transaction', { transactionId, participants });
|
|
693
|
-
}
|
|
694
|
-
|
|
695
|
-
/**
|
|
696
|
-
* Get transaction status
|
|
697
|
-
*/
|
|
698
|
-
getTransactionStatus(transactionId: string): TransactionExecution | null {
|
|
699
|
-
return this.activeTransactions.get(transactionId) || null;
|
|
700
|
-
}
|
|
701
|
-
|
|
702
|
-
/**
|
|
703
|
-
* Get all active transactions
|
|
704
|
-
*/
|
|
705
|
-
getActiveTransactions(): TransactionExecution[] {
|
|
706
|
-
return Array.from(this.activeTransactions.values());
|
|
707
|
-
}
|
|
708
|
-
}
|
|
709
|
-
|
|
710
|
-
/**
|
|
711
|
-
* Simple lock manager implementation
|
|
712
|
-
*/
|
|
713
|
-
class SimpleLockManager implements LockManager {
|
|
714
|
-
private locks: Map<string, { lockId: string; owner: string; timestamp: Date }> = new Map();
|
|
715
|
-
|
|
716
|
-
async acquire(resource: string, timeout: number): Promise<string> {
|
|
717
|
-
const lockId = `lock_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
718
|
-
const startTime = Date.now();
|
|
719
|
-
|
|
720
|
-
while (Date.now() - startTime < timeout) {
|
|
721
|
-
if (!this.locks.has(resource)) {
|
|
722
|
-
this.locks.set(resource, {
|
|
723
|
-
lockId,
|
|
724
|
-
owner: 'transaction_manager',
|
|
725
|
-
timestamp: new Date()
|
|
726
|
-
});
|
|
727
|
-
return lockId;
|
|
728
|
-
}
|
|
729
|
-
|
|
730
|
-
// Wait before retry
|
|
731
|
-
await new Promise(resolve => setTimeout(resolve, 100));
|
|
732
|
-
}
|
|
733
|
-
|
|
734
|
-
throw new Error(`Failed to acquire lock for ${resource}: timeout`);
|
|
735
|
-
}
|
|
736
|
-
|
|
737
|
-
async release(lockId: string): Promise<void> {
|
|
738
|
-
for (const [resource, lock] of this.locks) {
|
|
739
|
-
if (lock.lockId === lockId) {
|
|
740
|
-
this.locks.delete(resource);
|
|
741
|
-
return;
|
|
742
|
-
}
|
|
743
|
-
}
|
|
744
|
-
}
|
|
745
|
-
|
|
746
|
-
isLocked(resource: string): boolean {
|
|
747
|
-
return this.locks.has(resource);
|
|
748
|
-
}
|
|
749
|
-
|
|
750
|
-
getLockOwner(resource: string): string | null {
|
|
751
|
-
const lock = this.locks.get(resource);
|
|
752
|
-
return lock ? lock.owner : null;
|
|
753
|
-
}
|
|
754
|
-
}
|