@diia-inhouse/workflow 1.17.11 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/activities/index.d.ts +1 -0
- package/dist/activities/index.js +2 -18
- package/dist/activities/proxy.d.ts +34 -0
- package/dist/activities/proxy.js +16 -24
- package/dist/activity.d.ts +2 -0
- package/dist/activity.js +2 -15
- package/dist/cli/checkWorkflowDeterminism.js +249 -275
- package/dist/cli/determinism/errorClassifier.js +56 -60
- package/dist/cli/determinism/historyFiles.js +68 -97
- package/dist/cli/determinism/index.js +7 -19
- package/dist/cli/determinism/replayExecutor.js +114 -133
- package/dist/cli/determinism/replayOptions.js +13 -22
- package/dist/cli/determinism/report.js +55 -45
- package/dist/cli/determinism/reportPrinter.js +101 -138
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +79 -119
- package/dist/cli/syncTemporalSchedules.js +74 -91
- package/dist/cli/updateTemporalSchedule.js +43 -53
- package/dist/client.d.ts +3 -0
- package/dist/client.js +3 -19
- package/dist/common.d.ts +2 -0
- package/dist/common.js +2 -13
- package/dist/encryption/crypto.d.ts +7 -0
- package/dist/encryption/crypto.js +20 -22
- package/dist/encryption/dataConverter.d.ts +7 -0
- package/dist/encryption/dataConverter.js +15 -22
- package/dist/encryption/encryptionCodec.d.ts +31 -0
- package/dist/encryption/encryptionCodec.js +108 -124
- package/dist/encryption/index.d.ts +3 -0
- package/dist/encryption/index.js +4 -20
- package/dist/index.d.ts +7 -0
- package/dist/index.js +6 -42
- package/dist/instrumentation.js +6 -10
- package/dist/interceptors/asyncLocalStorageBridge.js +29 -66
- package/dist/interceptors/traceLogAttributes.d.ts +6 -0
- package/dist/interceptors/traceLogAttributes.js +16 -54
- package/dist/interceptors.d.ts +6 -0
- package/dist/interceptors.js +6 -8
- package/dist/interfaces/config.d.ts +58 -0
- package/dist/interfaces/index.d.ts +1 -0
- package/dist/interfaces/services/schedulesExporter.d.ts +96 -0
- package/dist/interfaces/services/worker.d.ts +60 -0
- package/dist/operations.d.ts +9 -0
- package/dist/operations.js +11 -75
- package/dist/services/client.d.ts +24 -0
- package/dist/services/client.js +89 -96
- package/dist/services/schedulesExporter.d.ts +101 -0
- package/dist/services/schedulesExporter.js +456 -0
- package/dist/services/worker/identity.d.ts +4 -0
- package/dist/services/worker/identity.js +6 -9
- package/dist/services/worker.d.ts +124 -0
- package/dist/services/worker.js +324 -304
- package/dist/services/workerHealth.d.ts +15 -0
- package/dist/services/workerHealth.js +26 -35
- package/dist/testing.d.ts +42 -0
- package/dist/testing.js +43 -54
- package/dist/worker.d.ts +9 -0
- package/dist/worker.js +7 -25
- package/package.json +40 -37
- package/dist/activities/index.js.map +0 -1
- package/dist/activities/proxy.js.map +0 -1
- package/dist/activity.js.map +0 -1
- package/dist/cli/checkWorkflowDeterminism.js.map +0 -1
- package/dist/cli/determinism/errorClassifier.js.map +0 -1
- package/dist/cli/determinism/historyFiles.js.map +0 -1
- package/dist/cli/determinism/index.js.map +0 -1
- package/dist/cli/determinism/replayExecutor.js.map +0 -1
- package/dist/cli/determinism/replayOptions.js.map +0 -1
- package/dist/cli/determinism/report.js.map +0 -1
- package/dist/cli/determinism/reportPrinter.js.map +0 -1
- package/dist/cli/determinism/types.js +0 -3
- package/dist/cli/determinism/types.js.map +0 -1
- package/dist/cli/index.js.map +0 -1
- package/dist/cli/syncTemporalSchedules.js.map +0 -1
- package/dist/cli/updateTemporalSchedule.js.map +0 -1
- package/dist/client.js.map +0 -1
- package/dist/common.js.map +0 -1
- package/dist/encryption/crypto.js.map +0 -1
- package/dist/encryption/dataConverter.js.map +0 -1
- package/dist/encryption/encryptionCodec.js.map +0 -1
- package/dist/encryption/index.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/instrumentation.js.map +0 -1
- package/dist/interceptors/asyncLocalStorageBridge.js.map +0 -1
- package/dist/interceptors/index.js +0 -8
- package/dist/interceptors/index.js.map +0 -1
- package/dist/interceptors/traceLogAttributes.js.map +0 -1
- package/dist/interceptors.js.map +0 -1
- package/dist/interfaces/config.js +0 -3
- package/dist/interfaces/config.js.map +0 -1
- package/dist/interfaces/index.js +0 -18
- package/dist/interfaces/index.js.map +0 -1
- package/dist/interfaces/services/worker.js +0 -3
- package/dist/interfaces/services/worker.js.map +0 -1
- package/dist/operations.js.map +0 -1
- package/dist/services/client.js.map +0 -1
- package/dist/services/index.js +0 -19
- package/dist/services/index.js.map +0 -1
- package/dist/services/worker/identity.js.map +0 -1
- package/dist/services/worker/index.js +0 -18
- package/dist/services/worker/index.js.map +0 -1
- package/dist/services/worker.js.map +0 -1
- package/dist/services/workerHealth.js.map +0 -1
- package/dist/testing.js.map +0 -1
- package/dist/types/activities/index.d.ts +0 -1
- package/dist/types/activities/proxy.d.ts +0 -35
- package/dist/types/activity.d.ts +0 -1
- package/dist/types/cli/checkWorkflowDeterminism.d.ts +0 -19
- package/dist/types/cli/determinism/errorClassifier.d.ts +0 -15
- package/dist/types/cli/determinism/historyFiles.d.ts +0 -18
- package/dist/types/cli/determinism/index.d.ts +0 -10
- package/dist/types/cli/determinism/replayExecutor.d.ts +0 -9
- package/dist/types/cli/determinism/replayOptions.d.ts +0 -7
- package/dist/types/cli/determinism/report.d.ts +0 -16
- package/dist/types/cli/determinism/reportPrinter.d.ts +0 -5
- package/dist/types/cli/determinism/types.d.ts +0 -44
- package/dist/types/cli/index.d.ts +0 -2
- package/dist/types/cli/syncTemporalSchedules.d.ts +0 -12
- package/dist/types/cli/updateTemporalSchedule.d.ts +0 -9
- package/dist/types/client.d.ts +0 -2
- package/dist/types/common.d.ts +0 -1
- package/dist/types/encryption/crypto.d.ts +0 -3
- package/dist/types/encryption/dataConverter.d.ts +0 -3
- package/dist/types/encryption/encryptionCodec.d.ts +0 -27
- package/dist/types/encryption/index.d.ts +0 -3
- package/dist/types/index.d.ts +0 -3
- package/dist/types/instrumentation.d.ts +0 -2
- package/dist/types/interceptors/asyncLocalStorageBridge.d.ts +0 -21
- package/dist/types/interceptors/index.d.ts +0 -2
- package/dist/types/interceptors/traceLogAttributes.d.ts +0 -2
- package/dist/types/interceptors.d.ts +0 -2
- package/dist/types/interfaces/config.d.ts +0 -38
- package/dist/types/interfaces/index.d.ts +0 -1
- package/dist/types/interfaces/services/worker.d.ts +0 -37
- package/dist/types/operations.d.ts +0 -5
- package/dist/types/services/client.d.ts +0 -20
- package/dist/types/services/index.d.ts +0 -2
- package/dist/types/services/worker/identity.d.ts +0 -1
- package/dist/types/services/worker/index.d.ts +0 -1
- package/dist/types/services/worker.d.ts +0 -113
- package/dist/types/services/workerHealth.d.ts +0 -11
- package/dist/types/testing.d.ts +0 -42
- package/dist/types/worker.d.ts +0 -3
- package/dist/worker.js.map +0 -1
|
@@ -1,65 +1,61 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
exports.isNewStepsAdded = isNewStepsAdded;
|
|
4
|
-
exports.classifyReplayError = classifyReplayError;
|
|
5
|
-
const workflow_1 = require("@temporalio/workflow");
|
|
1
|
+
import { DeterminismViolationError } from "@temporalio/workflow";
|
|
2
|
+
//#region src/cli/determinism/errorClassifier.ts
|
|
6
3
|
const activityMismatchRegex = /Activity type of scheduled event '(.+?)' does not match activity type of activity command '(.+?)'/;
|
|
7
4
|
function isNewStepsAdded(error) {
|
|
8
|
-
|
|
5
|
+
return error.errorType === "DeterminismViolation" && error.errorMessage.includes("WorkflowExecutionCompleted");
|
|
9
6
|
}
|
|
10
7
|
function classifyReplayError(workflowId, error) {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
},
|
|
63
|
-
};
|
|
8
|
+
if (error instanceof DeterminismViolationError) {
|
|
9
|
+
const message = error.message || "";
|
|
10
|
+
const match = message.match(activityMismatchRegex);
|
|
11
|
+
if (match) {
|
|
12
|
+
const [, scheduledEvent, activityCommand] = match;
|
|
13
|
+
return {
|
|
14
|
+
type: "determinism-violation",
|
|
15
|
+
subtype: "activity-mismatch",
|
|
16
|
+
entry: {
|
|
17
|
+
workflowId,
|
|
18
|
+
errorType: "DeterminismViolation",
|
|
19
|
+
errorMessage: message,
|
|
20
|
+
details: {
|
|
21
|
+
issue: "Activity Type Mismatch",
|
|
22
|
+
explanation: `The workflow history expected activity '${scheduledEvent}' but the code attempted to execute '${activityCommand}'`
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
if (message.includes("WorkflowExecutionCompleted")) return {
|
|
28
|
+
type: "determinism-violation",
|
|
29
|
+
subtype: "new-steps-added",
|
|
30
|
+
entry: {
|
|
31
|
+
workflowId,
|
|
32
|
+
errorType: "DeterminismViolation",
|
|
33
|
+
errorMessage: message,
|
|
34
|
+
details: {
|
|
35
|
+
issue: "New Steps Added",
|
|
36
|
+
explanation: "This workflow has been modified to add new steps after the point where it previously completed. This is safe to ignore as it does not affect existing history."
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
return {
|
|
41
|
+
type: "determinism-violation",
|
|
42
|
+
subtype: "other",
|
|
43
|
+
entry: {
|
|
44
|
+
workflowId,
|
|
45
|
+
errorType: "DeterminismViolation",
|
|
46
|
+
errorMessage: message
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
type: "replay-failure",
|
|
52
|
+
subtype: void 0,
|
|
53
|
+
entry: {
|
|
54
|
+
workflowId,
|
|
55
|
+
errorType: "ReplayFailure",
|
|
56
|
+
errorMessage: error.message
|
|
57
|
+
}
|
|
58
|
+
};
|
|
64
59
|
}
|
|
65
|
-
//#
|
|
60
|
+
//#endregion
|
|
61
|
+
export { classifyReplayError, isNewStepsAdded };
|
|
@@ -1,107 +1,78 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.collectHistoryFiles = collectHistoryFiles;
|
|
7
|
-
exports.parseHistoryFile = parseHistoryFile;
|
|
8
|
-
exports.loadHistoryEntries = loadHistoryEntries;
|
|
9
|
-
const node_fs_1 = require("node:fs");
|
|
10
|
-
const node_path_1 = __importDefault(require("node:path"));
|
|
11
|
-
const ENCRYPTED_ENCODING = 'YmluYXJ5L2VuY3J5cHRlZA=='; // base64 of 'binary/encrypted'
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
3
|
+
//#region src/cli/determinism/historyFiles.ts
|
|
4
|
+
const ENCRYPTED_ENCODING = "YmluYXJ5L2VuY3J5cHRlZA==";
|
|
12
5
|
function collectHistoryFiles(dir) {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const fullPath = node_path_1.default.join(dir, entry.name);
|
|
23
|
-
if (entry.isDirectory()) {
|
|
24
|
-
files.push(...collectHistoryFiles(fullPath));
|
|
25
|
-
}
|
|
26
|
-
else if (entry.name.endsWith('.json')) {
|
|
27
|
-
files.push(fullPath);
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
return files;
|
|
6
|
+
if (!existsSync(dir)) throw new Error(`History directory does not exist: ${dir}`);
|
|
7
|
+
const files = [];
|
|
8
|
+
const entries = readdirSync(dir, { withFileTypes: true });
|
|
9
|
+
for (const entry of entries) {
|
|
10
|
+
const fullPath = path.join(dir, entry.name);
|
|
11
|
+
if (entry.isDirectory()) files.push(...collectHistoryFiles(fullPath));
|
|
12
|
+
else if (entry.name.endsWith(".json")) files.push(fullPath);
|
|
13
|
+
}
|
|
14
|
+
return files;
|
|
31
15
|
}
|
|
32
16
|
const TERMINAL_EVENT_TYPES = new Set([
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
17
|
+
"EVENT_TYPE_WORKFLOW_EXECUTION_COMPLETED",
|
|
18
|
+
"EVENT_TYPE_WORKFLOW_EXECUTION_FAILED",
|
|
19
|
+
"EVENT_TYPE_WORKFLOW_EXECUTION_TIMED_OUT",
|
|
20
|
+
"EVENT_TYPE_WORKFLOW_EXECUTION_CANCELED",
|
|
21
|
+
"EVENT_TYPE_WORKFLOW_EXECUTION_TERMINATED"
|
|
38
22
|
]);
|
|
39
23
|
function parseHistoryFile(filePath) {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
24
|
+
const workflowId = path.basename(filePath, ".json");
|
|
25
|
+
const content = JSON.parse(readFileSync(filePath, "utf8"));
|
|
26
|
+
const history = content.history ?? content;
|
|
27
|
+
const events = history.events ?? [];
|
|
28
|
+
return {
|
|
29
|
+
workflowId,
|
|
30
|
+
workflowType: events.find((e) => e.eventType === "EVENT_TYPE_WORKFLOW_EXECUTION_STARTED")?.workflowExecutionStartedEventAttributes?.workflowType?.name ?? "unknown",
|
|
31
|
+
history,
|
|
32
|
+
encrypted: hasEncryptedPayloads(events),
|
|
33
|
+
running: !events.some((e) => TERMINAL_EVENT_TYPES.has(e.eventType ?? ""))
|
|
34
|
+
};
|
|
50
35
|
}
|
|
51
36
|
function hasEncryptedPayloads(events) {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
if (!Array.isArray(payloads)) {
|
|
63
|
-
continue;
|
|
64
|
-
}
|
|
65
|
-
for (const payload of payloads) {
|
|
66
|
-
if (payload.metadata?.encoding === ENCRYPTED_ENCODING) {
|
|
67
|
-
return true;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
return false;
|
|
37
|
+
for (const event of events) for (const [key, val] of Object.entries(event)) {
|
|
38
|
+
if (!key.includes("Attributes") || typeof val !== "object" || val === null) continue;
|
|
39
|
+
for (const nested of Object.values(val)) {
|
|
40
|
+
if (typeof nested !== "object" || nested === null) continue;
|
|
41
|
+
const payloads = nested.payloads;
|
|
42
|
+
if (!Array.isArray(payloads)) continue;
|
|
43
|
+
for (const payload of payloads) if (payload.metadata?.encoding === ENCRYPTED_ENCODING) return true;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return false;
|
|
74
47
|
}
|
|
75
48
|
function loadHistoryEntries(historyDir, workflows, options = {}) {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
return { entries, encryptedCount, runningCount };
|
|
49
|
+
const { limit, encryptionEnabled = false, logger } = options;
|
|
50
|
+
let historyFiles = collectHistoryFiles(historyDir);
|
|
51
|
+
if (limit && limit > 0 && historyFiles.length > limit) historyFiles = historyFiles.slice(0, limit);
|
|
52
|
+
const entries = [];
|
|
53
|
+
let encryptedCount = 0;
|
|
54
|
+
let runningCount = 0;
|
|
55
|
+
for (const filePath of historyFiles) try {
|
|
56
|
+
const entry = parseHistoryFile(filePath);
|
|
57
|
+
if (entry.workflowType !== "unknown" && !workflows[entry.workflowType]) continue;
|
|
58
|
+
if (entry.running) {
|
|
59
|
+
runningCount++;
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
if (entry.encrypted && !encryptionEnabled) {
|
|
63
|
+
encryptedCount++;
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
entries.push(entry);
|
|
67
|
+
} catch (err) {
|
|
68
|
+
const workflowId = path.basename(filePath, ".json");
|
|
69
|
+
logger?.warn(`Skipping ${workflowId} — failed to parse: ${err.message}`);
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
entries,
|
|
73
|
+
encryptedCount,
|
|
74
|
+
runningCount
|
|
75
|
+
};
|
|
106
76
|
}
|
|
107
|
-
//#
|
|
77
|
+
//#endregion
|
|
78
|
+
export { loadHistoryEntries };
|
|
@@ -1,19 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
Object.defineProperty(exports, "loadHistoryEntries", { enumerable: true, get: function () { return historyFiles_1.loadHistoryEntries; } });
|
|
9
|
-
var report_1 = require("./report");
|
|
10
|
-
Object.defineProperty(exports, "DeterminismReportBuilder", { enumerable: true, get: function () { return report_1.DeterminismReportBuilder; } });
|
|
11
|
-
var replayOptions_1 = require("./replayOptions");
|
|
12
|
-
Object.defineProperty(exports, "buildReplayOptions", { enumerable: true, get: function () { return replayOptions_1.buildReplayOptions; } });
|
|
13
|
-
Object.defineProperty(exports, "resolveWorkflowsPath", { enumerable: true, get: function () { return replayOptions_1.resolveWorkflowsPath; } });
|
|
14
|
-
var replayExecutor_1 = require("./replayExecutor");
|
|
15
|
-
Object.defineProperty(exports, "replayBatch", { enumerable: true, get: function () { return replayExecutor_1.replayBatch; } });
|
|
16
|
-
Object.defineProperty(exports, "replaySingle", { enumerable: true, get: function () { return replayExecutor_1.replaySingle; } });
|
|
17
|
-
var reportPrinter_1 = require("./reportPrinter");
|
|
18
|
-
Object.defineProperty(exports, "printReport", { enumerable: true, get: function () { return reportPrinter_1.printReport; } });
|
|
19
|
-
//# sourceMappingURL=index.js.map
|
|
1
|
+
import "./errorClassifier.js";
|
|
2
|
+
import "./historyFiles.js";
|
|
3
|
+
import "./report.js";
|
|
4
|
+
import "./replayOptions.js";
|
|
5
|
+
import "./replayExecutor.js";
|
|
6
|
+
import "./reportPrinter.js";
|
|
7
|
+
export {};
|
|
@@ -1,139 +1,120 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}
|
|
15
|
-
}
|
|
1
|
+
import { classifyReplayError } from "./errorClassifier.js";
|
|
2
|
+
import { DeterminismViolationError } from "@temporalio/workflow";
|
|
3
|
+
import { Worker } from "@temporalio/worker";
|
|
4
|
+
import { setTimeout as setTimeout$1 } from "node:timers/promises";
|
|
5
|
+
//#region src/cli/determinism/replayExecutor.ts
|
|
6
|
+
var TimeoutError = class extends Error {
|
|
7
|
+
timeoutMs;
|
|
8
|
+
name = "TimeoutError";
|
|
9
|
+
constructor(timeoutMs) {
|
|
10
|
+
super(`Replay timed out after ${timeoutMs / 1e3}s`);
|
|
11
|
+
this.timeoutMs = timeoutMs;
|
|
12
|
+
}
|
|
13
|
+
};
|
|
16
14
|
function runWithTimeout(options, history, workflowId, timeoutMs) {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
.finally(() => clearTimeout(timer));
|
|
22
|
-
});
|
|
15
|
+
return new Promise((resolve, reject) => {
|
|
16
|
+
const timer = setTimeout(() => reject(new TimeoutError(timeoutMs)), timeoutMs);
|
|
17
|
+
Worker.runReplayHistory(options, history, workflowId).then(resolve, reject).finally(() => clearTimeout(timer));
|
|
18
|
+
});
|
|
23
19
|
}
|
|
24
20
|
async function replaySingle(options, history, workflowId, workflowType, config) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
};
|
|
21
|
+
let failedAttempts = 0;
|
|
22
|
+
const originalErrors = [];
|
|
23
|
+
for (let attempt = 1; attempt <= config.maxRetries; attempt++) try {
|
|
24
|
+
if (attempt > 1) await setTimeout$1(Math.min(config.retryDelayMs * Math.pow(2, attempt - 2), 4e3));
|
|
25
|
+
await runWithTimeout(options, history, workflowId, config.timeoutMs);
|
|
26
|
+
return {
|
|
27
|
+
status: "success",
|
|
28
|
+
workflowId,
|
|
29
|
+
workflowType,
|
|
30
|
+
recoveredOnRetry: attempt > 1,
|
|
31
|
+
failedAttempts,
|
|
32
|
+
originalErrors
|
|
33
|
+
};
|
|
34
|
+
} catch (err) {
|
|
35
|
+
if (err instanceof DeterminismViolationError) return {
|
|
36
|
+
status: "failure",
|
|
37
|
+
workflowId,
|
|
38
|
+
workflowType,
|
|
39
|
+
error: classifyReplayError(workflowId, err).entry
|
|
40
|
+
};
|
|
41
|
+
if (err instanceof TimeoutError) return {
|
|
42
|
+
status: "timeout",
|
|
43
|
+
workflowId,
|
|
44
|
+
workflowType,
|
|
45
|
+
timeoutMs: err.timeoutMs
|
|
46
|
+
};
|
|
47
|
+
failedAttempts++;
|
|
48
|
+
originalErrors.push(`Attempt ${attempt}: ${err instanceof Error ? err.message : String(err)}`);
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
status: "failure",
|
|
52
|
+
workflowId,
|
|
53
|
+
workflowType,
|
|
54
|
+
error: {
|
|
55
|
+
workflowId,
|
|
56
|
+
errorType: "ReplayFailure",
|
|
57
|
+
errorMessage: `Replay failed after ${config.maxRetries} attempts`,
|
|
58
|
+
details: { originalErrors }
|
|
59
|
+
}
|
|
60
|
+
};
|
|
66
61
|
}
|
|
67
|
-
const DEFAULT_BATCH_TIMEOUT_MS =
|
|
62
|
+
const DEFAULT_BATCH_TIMEOUT_MS = 3e4;
|
|
68
63
|
async function* replayBatch(options, entries, timeoutMs = DEFAULT_BATCH_TIMEOUT_MS) {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
},
|
|
124
|
-
};
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
else {
|
|
128
|
-
yield {
|
|
129
|
-
status: 'success',
|
|
130
|
-
workflowId: result.workflowId,
|
|
131
|
-
workflowType,
|
|
132
|
-
recoveredOnRetry: false,
|
|
133
|
-
failedAttempts: 0,
|
|
134
|
-
originalErrors: [],
|
|
135
|
-
};
|
|
136
|
-
}
|
|
137
|
-
}
|
|
64
|
+
const entryMap = new Map(entries.map((e) => [e.workflowId, e]));
|
|
65
|
+
const entryOrder = entries.map((e) => e.workflowId);
|
|
66
|
+
let nextExpectedIndex = 0;
|
|
67
|
+
async function* historyIterator() {
|
|
68
|
+
for (const entry of entries) yield {
|
|
69
|
+
history: entry.history,
|
|
70
|
+
workflowId: entry.workflowId
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
const iterator = Worker.runReplayHistories(options, historyIterator())[Symbol.asyncIterator]();
|
|
74
|
+
while (true) {
|
|
75
|
+
const nextResult = await Promise.race([iterator.next(), setTimeout$1(timeoutMs).then(() => "timeout")]);
|
|
76
|
+
if (nextResult === "timeout") {
|
|
77
|
+
const stuckWorkflowId = entryOrder[nextExpectedIndex];
|
|
78
|
+
if (stuckWorkflowId) yield {
|
|
79
|
+
status: "timeout",
|
|
80
|
+
workflowId: stuckWorkflowId,
|
|
81
|
+
workflowType: entryMap.get(stuckWorkflowId)?.workflowType ?? "unknown",
|
|
82
|
+
timeoutMs
|
|
83
|
+
};
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
if (nextResult.done) break;
|
|
87
|
+
const result = nextResult.value;
|
|
88
|
+
const workflowType = entryMap.get(result.workflowId)?.workflowType ?? "unknown";
|
|
89
|
+
const resultIndex = entryOrder.indexOf(result.workflowId);
|
|
90
|
+
if (resultIndex >= nextExpectedIndex) nextExpectedIndex = resultIndex + 1;
|
|
91
|
+
if (result.error) if (result.error instanceof DeterminismViolationError) {
|
|
92
|
+
const classification = classifyReplayError(result.workflowId, result.error);
|
|
93
|
+
yield {
|
|
94
|
+
status: "failure",
|
|
95
|
+
workflowId: result.workflowId,
|
|
96
|
+
workflowType,
|
|
97
|
+
error: classification.entry
|
|
98
|
+
};
|
|
99
|
+
} else yield {
|
|
100
|
+
status: "failure",
|
|
101
|
+
workflowId: result.workflowId,
|
|
102
|
+
workflowType,
|
|
103
|
+
error: {
|
|
104
|
+
workflowId: result.workflowId,
|
|
105
|
+
errorType: "ReplayFailure",
|
|
106
|
+
errorMessage: result.error.message
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
else yield {
|
|
110
|
+
status: "success",
|
|
111
|
+
workflowId: result.workflowId,
|
|
112
|
+
workflowType,
|
|
113
|
+
recoveredOnRetry: false,
|
|
114
|
+
failedAttempts: 0,
|
|
115
|
+
originalErrors: []
|
|
116
|
+
};
|
|
117
|
+
}
|
|
138
118
|
}
|
|
139
|
-
//#
|
|
119
|
+
//#endregion
|
|
120
|
+
export { replayBatch, replaySingle };
|
|
@@ -1,26 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.resolveWorkflowsPath = resolveWorkflowsPath;
|
|
7
|
-
exports.buildReplayOptions = buildReplayOptions;
|
|
8
|
-
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
-
const encryption_1 = require("../../encryption");
|
|
1
|
+
import { getDataConverter } from "../../encryption/dataConverter.js";
|
|
2
|
+
import "../../encryption/index.js";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
//#region src/cli/determinism/replayOptions.ts
|
|
10
5
|
function resolveWorkflowsPath(workflowsPath) {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}
|
|
16
|
-
return fullPath;
|
|
6
|
+
const baseDir = path.resolve("./dist");
|
|
7
|
+
const fullPath = path.resolve(baseDir, workflowsPath, "index.js");
|
|
8
|
+
if (!fullPath.startsWith(baseDir + path.sep)) throw new Error(`Invalid workflows path: path traversal detected in '${workflowsPath}'`);
|
|
9
|
+
return fullPath;
|
|
17
10
|
}
|
|
18
11
|
async function buildReplayOptions(workflowsPath, encryption, envService) {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
options.dataConverter = await (0, encryption_1.getDataConverter)(encryption.keyId, envService);
|
|
23
|
-
}
|
|
24
|
-
return options;
|
|
12
|
+
const options = { workflowsPath: resolveWorkflowsPath(workflowsPath) };
|
|
13
|
+
if (encryption.enabled && envService) options.dataConverter = await getDataConverter(encryption.keyId, envService);
|
|
14
|
+
return options;
|
|
25
15
|
}
|
|
26
|
-
//#
|
|
16
|
+
//#endregion
|
|
17
|
+
export { buildReplayOptions, resolveWorkflowsPath };
|