snow-flow 3.0.10 → 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/dist/mcp/intelligent-reporting-mcp.d.ts +7 -0
- package/dist/mcp/intelligent-reporting-mcp.js +379 -0
- package/dist/mcp/servicenow-reporting-analytics-mcp.js +147 -10
- 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/package.json +4 -3
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* ServiceNow Eventual Consistency Handler
|
|
4
|
+
*
|
|
5
|
+
* ServiceNow uses a distributed database architecture with eventual consistency:
|
|
6
|
+
* - Write operations go to primary database (immediate)
|
|
7
|
+
* - Read operations may use read replicas (1-3 second lag)
|
|
8
|
+
* - This causes race conditions in deployment verification
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.appConsistency = exports.flowConsistency = exports.widgetConsistency = exports.CONSISTENCY_CONFIGS = exports.ServiceNowEventualConsistency = void 0;
|
|
12
|
+
const logger_js_1 = require("./logger.js");
|
|
13
|
+
class ServiceNowEventualConsistency {
|
|
14
|
+
constructor(loggerName = 'EventualConsistency') {
|
|
15
|
+
this.logger = new logger_js_1.Logger(loggerName);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Retry a ServiceNow operation with exponential backoff
|
|
19
|
+
* Designed specifically for post-deployment verification
|
|
20
|
+
*/
|
|
21
|
+
async retryWithBackoff(operation, config = {}) {
|
|
22
|
+
const { maxRetries = 5, baseDelay = 1000, maxDelay = 8000, backoffMultiplier = 1.5 } = config;
|
|
23
|
+
let lastError;
|
|
24
|
+
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
25
|
+
try {
|
|
26
|
+
if (attempt > 0) {
|
|
27
|
+
const delay = Math.min(baseDelay * Math.pow(backoffMultiplier, attempt), maxDelay);
|
|
28
|
+
this.logger.info(`⏳ Retry attempt ${attempt + 1}/${maxRetries} (waiting ${delay}ms for consistency)`);
|
|
29
|
+
await this.sleep(delay);
|
|
30
|
+
}
|
|
31
|
+
const result = await operation();
|
|
32
|
+
if (result) {
|
|
33
|
+
this.logger.info(`✅ Operation succeeded on attempt ${attempt + 1}`);
|
|
34
|
+
return { result, success: true, attempts: attempt + 1 };
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
lastError = error;
|
|
39
|
+
if (this.isPermanentError(error)) {
|
|
40
|
+
this.logger.error(`❌ Permanent error detected:`, error);
|
|
41
|
+
return { result: null, success: false, attempts: attempt + 1 };
|
|
42
|
+
}
|
|
43
|
+
if (attempt === 0) {
|
|
44
|
+
this.logger.info(`⏳ ${this.getErrorDescription(error)} - typical ServiceNow consistency issue`);
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
this.logger.info(`⏳ Still getting ${error.status || 'error'}, retrying...`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
this.logger.warn(`⚠️ Operation failed after ${maxRetries} attempts`);
|
|
52
|
+
this.logger.warn(`⚠️ Last error: ${lastError?.message || 'Unknown'}`);
|
|
53
|
+
return { result: null, success: false, attempts: maxRetries };
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Verify a ServiceNow record exists with retry logic
|
|
57
|
+
*/
|
|
58
|
+
async verifyRecordExists(client, table, sys_id, config = {}) {
|
|
59
|
+
this.logger.info(`🔍 Verifying ${table} record ${sys_id} (handling eventual consistency)`);
|
|
60
|
+
const operation = async () => {
|
|
61
|
+
const response = await client.getRecord(table, sys_id);
|
|
62
|
+
return response && response.sys_id === sys_id ? response : null;
|
|
63
|
+
};
|
|
64
|
+
const { result, success, attempts } = await this.retryWithBackoff(operation, config);
|
|
65
|
+
if (!success) {
|
|
66
|
+
const isLikelyTimingIssue = this.assessIfTimingIssue(attempts, config.maxRetries);
|
|
67
|
+
if (isLikelyTimingIssue) {
|
|
68
|
+
this.logger.warn(`⚠️ This appears to be a ServiceNow consistency issue, not a deployment failure`);
|
|
69
|
+
this.logger.warn(`⚠️ Check directly: /${table}.do?sys_id=${sys_id}`);
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
success: false,
|
|
73
|
+
attempts,
|
|
74
|
+
isLikelyTimingIssue
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
return {
|
|
78
|
+
success: true,
|
|
79
|
+
attempts,
|
|
80
|
+
isLikelyTimingIssue: false
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Verify multiple records exist (batch verification)
|
|
85
|
+
*/
|
|
86
|
+
async verifyMultipleRecords(client, verifications, config = {}) {
|
|
87
|
+
const results = [];
|
|
88
|
+
for (const verification of verifications) {
|
|
89
|
+
const result = await this.verifyRecordExists(client, verification.table, verification.sys_id, config);
|
|
90
|
+
results.push({
|
|
91
|
+
...result,
|
|
92
|
+
table: verification.table,
|
|
93
|
+
sys_id: verification.sys_id,
|
|
94
|
+
name: verification.name
|
|
95
|
+
});
|
|
96
|
+
// Small delay between verifications to avoid rate limiting
|
|
97
|
+
if (verifications.length > 1) {
|
|
98
|
+
await this.sleep(200);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return results;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Execute operation with eventual consistency retry
|
|
105
|
+
* Generic wrapper for any ServiceNow operation that might fail due to timing
|
|
106
|
+
*/
|
|
107
|
+
async executeWithRetry(operationName, operation, validator, config = {}) {
|
|
108
|
+
this.logger.info(`🚀 Executing ${operationName} with consistency handling`);
|
|
109
|
+
const { result, success, attempts } = await this.retryWithBackoff(async () => {
|
|
110
|
+
const opResult = await operation();
|
|
111
|
+
return validator(opResult) ? opResult : null;
|
|
112
|
+
}, config);
|
|
113
|
+
return {
|
|
114
|
+
result,
|
|
115
|
+
success,
|
|
116
|
+
metadata: {
|
|
117
|
+
operationName,
|
|
118
|
+
attempts,
|
|
119
|
+
isLikelyTimingIssue: !success && this.assessIfTimingIssue(attempts, config.maxRetries)
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Sleep utility
|
|
125
|
+
*/
|
|
126
|
+
sleep(ms) {
|
|
127
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Determine if an error indicates a permanent failure vs timing issue
|
|
131
|
+
*/
|
|
132
|
+
isPermanentError(error) {
|
|
133
|
+
// Permanent errors that shouldn't be retried
|
|
134
|
+
if (error.status === 401)
|
|
135
|
+
return true; // Authentication
|
|
136
|
+
if (error.status === 400 && !error.message?.includes('null'))
|
|
137
|
+
return true; // Bad request
|
|
138
|
+
if (error.message?.includes('table does not exist'))
|
|
139
|
+
return true; // Invalid table
|
|
140
|
+
if (error.message?.includes('permission denied') && error.status !== 403)
|
|
141
|
+
return true; // Real permission issues
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Get user-friendly error description
|
|
146
|
+
*/
|
|
147
|
+
getErrorDescription(error) {
|
|
148
|
+
if (error.status === 403)
|
|
149
|
+
return 'Access temporarily denied (403)';
|
|
150
|
+
if (error.status === 404)
|
|
151
|
+
return 'Record not found (404)';
|
|
152
|
+
if (error.message?.includes('null'))
|
|
153
|
+
return 'Null response received';
|
|
154
|
+
return `Error ${error.status || 'unknown'}`;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Assess if failure is likely due to timing issues
|
|
158
|
+
*/
|
|
159
|
+
assessIfTimingIssue(attempts, maxRetries = 5) {
|
|
160
|
+
// If we exhausted all retries and got timing-related errors, it's likely a timing issue
|
|
161
|
+
return attempts >= maxRetries;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Create a pre-configured instance for widget deployments
|
|
165
|
+
*/
|
|
166
|
+
static createForWidgets() {
|
|
167
|
+
return new ServiceNowEventualConsistency('WidgetConsistency');
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Create a pre-configured instance for flow deployments
|
|
171
|
+
*/
|
|
172
|
+
static createForFlows() {
|
|
173
|
+
return new ServiceNowEventualConsistency('FlowConsistency');
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Create a pre-configured instance for application deployments
|
|
177
|
+
*/
|
|
178
|
+
static createForApplications() {
|
|
179
|
+
return new ServiceNowEventualConsistency('AppConsistency');
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
exports.ServiceNowEventualConsistency = ServiceNowEventualConsistency;
|
|
183
|
+
/**
|
|
184
|
+
* Default configuration for different deployment types
|
|
185
|
+
*/
|
|
186
|
+
exports.CONSISTENCY_CONFIGS = {
|
|
187
|
+
// Fast retry for simple records
|
|
188
|
+
WIDGET: {
|
|
189
|
+
maxRetries: 5,
|
|
190
|
+
baseDelay: 800,
|
|
191
|
+
maxDelay: 6000,
|
|
192
|
+
backoffMultiplier: 1.4
|
|
193
|
+
},
|
|
194
|
+
// Medium retry for complex records
|
|
195
|
+
FLOW: {
|
|
196
|
+
maxRetries: 6,
|
|
197
|
+
baseDelay: 1200,
|
|
198
|
+
maxDelay: 8000,
|
|
199
|
+
backoffMultiplier: 1.5
|
|
200
|
+
},
|
|
201
|
+
// Longer retry for applications with dependencies
|
|
202
|
+
APPLICATION: {
|
|
203
|
+
maxRetries: 8,
|
|
204
|
+
baseDelay: 1500,
|
|
205
|
+
maxDelay: 12000,
|
|
206
|
+
backoffMultiplier: 1.6
|
|
207
|
+
},
|
|
208
|
+
// Quick check for batch operations
|
|
209
|
+
BATCH: {
|
|
210
|
+
maxRetries: 3,
|
|
211
|
+
baseDelay: 500,
|
|
212
|
+
maxDelay: 3000,
|
|
213
|
+
backoffMultiplier: 1.3
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
// Export singleton instances
|
|
217
|
+
exports.widgetConsistency = ServiceNowEventualConsistency.createForWidgets();
|
|
218
|
+
exports.flowConsistency = ServiceNowEventualConsistency.createForFlows();
|
|
219
|
+
exports.appConsistency = ServiceNowEventualConsistency.createForApplications();
|
|
220
|
+
//# sourceMappingURL=servicenow-eventual-consistency.js.map
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "snow-flow",
|
|
3
|
-
"version": "3.0.
|
|
4
|
-
"description": "Snow-Flow v3.0.
|
|
3
|
+
"version": "3.0.13",
|
|
4
|
+
"description": "Snow-Flow v3.0.13: ZERO MOCK DATA GUARANTEE! 🔥 All reporting, dashboards, and analytics tools now use 100% REAL ServiceNow data with anti-mock validation. Advanced AI table discovery, race condition fixes, and comprehensive real-data enforcement. NO demo/sample/test data ever used - only live data from your actual ServiceNow instance. Plus intelligent reporting, deployment fixes, and 100+ MCP tools.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "commonjs",
|
|
7
7
|
"bin": {
|
|
@@ -358,7 +358,8 @@
|
|
|
358
358
|
"mcpCapabilities": {
|
|
359
359
|
"realApiIntegration": true,
|
|
360
360
|
"noMockData": true,
|
|
361
|
-
"mockDataNote": "
|
|
361
|
+
"mockDataNote": "🔥 ZERO MOCK DATA GUARANTEE - All reports, dashboards, and analytics use 100% REAL ServiceNow data from your actual instance. NO demo/sample/test data ever used.",
|
|
362
|
+
"reportingDataPolicy": "ALL LIVE DATA - Every report, dashboard, chart, and KPI populated exclusively with real records from your ServiceNow tables",
|
|
362
363
|
"productionReady": true,
|
|
363
364
|
"batchOptimization": true,
|
|
364
365
|
"intelligentAnalysis": true,
|