@cogitator-ai/workflows 0.1.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/LICENSE +21 -0
- package/README.md +97 -0
- package/dist/builder.d.ts +39 -0
- package/dist/builder.d.ts.map +1 -0
- package/dist/builder.js +224 -0
- package/dist/builder.js.map +1 -0
- package/dist/checkpoint.d.ts +33 -0
- package/dist/checkpoint.d.ts.map +1 -0
- package/dist/checkpoint.js +108 -0
- package/dist/checkpoint.js.map +1 -0
- package/dist/executor.d.ts +24 -0
- package/dist/executor.d.ts.map +1 -0
- package/dist/executor.js +207 -0
- package/dist/executor.js.map +1 -0
- package/dist/human/approval-store.d.ts +95 -0
- package/dist/human/approval-store.d.ts.map +1 -0
- package/dist/human/approval-store.js +377 -0
- package/dist/human/approval-store.js.map +1 -0
- package/dist/human/human-node.d.ts +104 -0
- package/dist/human/human-node.d.ts.map +1 -0
- package/dist/human/human-node.js +342 -0
- package/dist/human/human-node.js.map +1 -0
- package/dist/human/index.d.ts +17 -0
- package/dist/human/index.d.ts.map +1 -0
- package/dist/human/index.js +17 -0
- package/dist/human/index.js.map +1 -0
- package/dist/human/notifiers.d.ts +85 -0
- package/dist/human/notifiers.d.ts.map +1 -0
- package/dist/human/notifiers.js +289 -0
- package/dist/human/notifiers.js.map +1 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/manager/index.d.ts +19 -0
- package/dist/manager/index.d.ts.map +1 -0
- package/dist/manager/index.js +17 -0
- package/dist/manager/index.js.map +1 -0
- package/dist/manager/run-store.d.ts +78 -0
- package/dist/manager/run-store.d.ts.map +1 -0
- package/dist/manager/run-store.js +390 -0
- package/dist/manager/run-store.js.map +1 -0
- package/dist/manager/scheduler.d.ts +159 -0
- package/dist/manager/scheduler.d.ts.map +1 -0
- package/dist/manager/scheduler.js +355 -0
- package/dist/manager/scheduler.js.map +1 -0
- package/dist/manager/workflow-manager.d.ts +114 -0
- package/dist/manager/workflow-manager.d.ts.map +1 -0
- package/dist/manager/workflow-manager.js +460 -0
- package/dist/manager/workflow-manager.js.map +1 -0
- package/dist/nodes/agent.d.ts +24 -0
- package/dist/nodes/agent.d.ts.map +1 -0
- package/dist/nodes/agent.js +37 -0
- package/dist/nodes/agent.js.map +1 -0
- package/dist/nodes/base.d.ts +12 -0
- package/dist/nodes/base.d.ts.map +1 -0
- package/dist/nodes/base.js +5 -0
- package/dist/nodes/base.js.map +1 -0
- package/dist/nodes/function.d.ts +27 -0
- package/dist/nodes/function.d.ts.map +1 -0
- package/dist/nodes/function.js +29 -0
- package/dist/nodes/function.js.map +1 -0
- package/dist/nodes/index.d.ts +8 -0
- package/dist/nodes/index.d.ts.map +1 -0
- package/dist/nodes/index.js +8 -0
- package/dist/nodes/index.js.map +1 -0
- package/dist/nodes/tool.d.ts +19 -0
- package/dist/nodes/tool.d.ts.map +1 -0
- package/dist/nodes/tool.js +26 -0
- package/dist/nodes/tool.js.map +1 -0
- package/dist/observability/exporters.d.ts +93 -0
- package/dist/observability/exporters.d.ts.map +1 -0
- package/dist/observability/exporters.js +330 -0
- package/dist/observability/exporters.js.map +1 -0
- package/dist/observability/index.d.ts +17 -0
- package/dist/observability/index.d.ts.map +1 -0
- package/dist/observability/index.js +17 -0
- package/dist/observability/index.js.map +1 -0
- package/dist/observability/metrics.d.ts +114 -0
- package/dist/observability/metrics.d.ts.map +1 -0
- package/dist/observability/metrics.js +435 -0
- package/dist/observability/metrics.js.map +1 -0
- package/dist/observability/span-attributes.d.ts +95 -0
- package/dist/observability/span-attributes.d.ts.map +1 -0
- package/dist/observability/span-attributes.js +142 -0
- package/dist/observability/span-attributes.js.map +1 -0
- package/dist/observability/tracer.d.ts +110 -0
- package/dist/observability/tracer.d.ts.map +1 -0
- package/dist/observability/tracer.js +409 -0
- package/dist/observability/tracer.js.map +1 -0
- package/dist/patterns/index.d.ts +15 -0
- package/dist/patterns/index.d.ts.map +1 -0
- package/dist/patterns/index.js +15 -0
- package/dist/patterns/index.js.map +1 -0
- package/dist/patterns/map-reduce.d.ts +223 -0
- package/dist/patterns/map-reduce.d.ts.map +1 -0
- package/dist/patterns/map-reduce.js +378 -0
- package/dist/patterns/map-reduce.js.map +1 -0
- package/dist/saga/circuit-breaker.d.ts +153 -0
- package/dist/saga/circuit-breaker.d.ts.map +1 -0
- package/dist/saga/circuit-breaker.js +306 -0
- package/dist/saga/circuit-breaker.js.map +1 -0
- package/dist/saga/compensation.d.ts +134 -0
- package/dist/saga/compensation.d.ts.map +1 -0
- package/dist/saga/compensation.js +240 -0
- package/dist/saga/compensation.js.map +1 -0
- package/dist/saga/dead-letter.d.ts +113 -0
- package/dist/saga/dead-letter.d.ts.map +1 -0
- package/dist/saga/dead-letter.js +307 -0
- package/dist/saga/dead-letter.js.map +1 -0
- package/dist/saga/idempotency.d.ts +95 -0
- package/dist/saga/idempotency.d.ts.map +1 -0
- package/dist/saga/idempotency.js +266 -0
- package/dist/saga/idempotency.js.map +1 -0
- package/dist/saga/index.d.ts +16 -0
- package/dist/saga/index.d.ts.map +1 -0
- package/dist/saga/index.js +16 -0
- package/dist/saga/index.js.map +1 -0
- package/dist/saga/retry.d.ts +59 -0
- package/dist/saga/retry.d.ts.map +1 -0
- package/dist/saga/retry.js +222 -0
- package/dist/saga/retry.js.map +1 -0
- package/dist/scheduler.d.ts +37 -0
- package/dist/scheduler.d.ts.map +1 -0
- package/dist/scheduler.js +151 -0
- package/dist/scheduler.js.map +1 -0
- package/dist/subworkflows/index.d.ts +16 -0
- package/dist/subworkflows/index.d.ts.map +1 -0
- package/dist/subworkflows/index.js +16 -0
- package/dist/subworkflows/index.js.map +1 -0
- package/dist/subworkflows/parallel-subworkflows.d.ts +139 -0
- package/dist/subworkflows/parallel-subworkflows.d.ts.map +1 -0
- package/dist/subworkflows/parallel-subworkflows.js +270 -0
- package/dist/subworkflows/parallel-subworkflows.js.map +1 -0
- package/dist/subworkflows/subworkflow-node.d.ts +136 -0
- package/dist/subworkflows/subworkflow-node.d.ts.map +1 -0
- package/dist/subworkflows/subworkflow-node.js +164 -0
- package/dist/subworkflows/subworkflow-node.js.map +1 -0
- package/dist/timers/cron-parser.d.ts +110 -0
- package/dist/timers/cron-parser.d.ts.map +1 -0
- package/dist/timers/cron-parser.js +412 -0
- package/dist/timers/cron-parser.js.map +1 -0
- package/dist/timers/index.d.ts +18 -0
- package/dist/timers/index.d.ts.map +1 -0
- package/dist/timers/index.js +18 -0
- package/dist/timers/index.js.map +1 -0
- package/dist/timers/timer-manager.d.ts +219 -0
- package/dist/timers/timer-manager.d.ts.map +1 -0
- package/dist/timers/timer-manager.js +351 -0
- package/dist/timers/timer-manager.js.map +1 -0
- package/dist/timers/timer-node.d.ts +144 -0
- package/dist/timers/timer-node.d.ts.map +1 -0
- package/dist/timers/timer-node.js +246 -0
- package/dist/timers/timer-node.js.map +1 -0
- package/dist/timers/timer-store.d.ts +90 -0
- package/dist/timers/timer-store.d.ts.map +1 -0
- package/dist/timers/timer-store.js +357 -0
- package/dist/timers/timer-store.js.map +1 -0
- package/dist/triggers/cron-trigger.d.ts +102 -0
- package/dist/triggers/cron-trigger.d.ts.map +1 -0
- package/dist/triggers/cron-trigger.js +309 -0
- package/dist/triggers/cron-trigger.js.map +1 -0
- package/dist/triggers/index.d.ts +14 -0
- package/dist/triggers/index.d.ts.map +1 -0
- package/dist/triggers/index.js +10 -0
- package/dist/triggers/index.js.map +1 -0
- package/dist/triggers/rate-limiter.d.ts +130 -0
- package/dist/triggers/rate-limiter.d.ts.map +1 -0
- package/dist/triggers/rate-limiter.js +294 -0
- package/dist/triggers/rate-limiter.js.map +1 -0
- package/dist/triggers/trigger-manager.d.ts +166 -0
- package/dist/triggers/trigger-manager.d.ts.map +1 -0
- package/dist/triggers/trigger-manager.js +436 -0
- package/dist/triggers/trigger-manager.js.map +1 -0
- package/dist/triggers/webhook-trigger.d.ts +150 -0
- package/dist/triggers/webhook-trigger.d.ts.map +1 -0
- package/dist/triggers/webhook-trigger.js +452 -0
- package/dist/triggers/webhook-trigger.js.map +1 -0
- package/package.json +44 -0
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compensation Manager for Saga pattern
|
|
3
|
+
*
|
|
4
|
+
* Features:
|
|
5
|
+
* - Automatic compensation on failure
|
|
6
|
+
* - Reverse-order execution
|
|
7
|
+
* - Partial compensation (only completed steps)
|
|
8
|
+
* - Compensation condition checking
|
|
9
|
+
* - Compensation result tracking
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Compensation Manager class
|
|
13
|
+
*/
|
|
14
|
+
export class CompensationManager {
|
|
15
|
+
steps = new Map();
|
|
16
|
+
completedNodes = new Map();
|
|
17
|
+
executionOrder = [];
|
|
18
|
+
/**
|
|
19
|
+
* Register a compensation step for a node
|
|
20
|
+
*/
|
|
21
|
+
registerCompensation(nodeId, compensationFn, options = {}) {
|
|
22
|
+
this.steps.set(nodeId, {
|
|
23
|
+
nodeId,
|
|
24
|
+
compensationFn,
|
|
25
|
+
condition: options.condition,
|
|
26
|
+
order: options.order ?? 'reverse',
|
|
27
|
+
timeout: options.timeout,
|
|
28
|
+
retries: options.retries ?? 0,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Register compensation from config
|
|
33
|
+
*/
|
|
34
|
+
registerFromConfig(nodeId, config) {
|
|
35
|
+
if (!config.compensate)
|
|
36
|
+
return;
|
|
37
|
+
this.registerCompensation(nodeId, config.compensate, {
|
|
38
|
+
condition: config.compensateCondition,
|
|
39
|
+
order: config.compensateOrder,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Mark a node as completed with its result
|
|
44
|
+
*/
|
|
45
|
+
markCompleted(nodeId, result) {
|
|
46
|
+
this.completedNodes.set(nodeId, result);
|
|
47
|
+
this.executionOrder.push(nodeId);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Clear a node's completion status (for retries)
|
|
51
|
+
*/
|
|
52
|
+
clearCompleted(nodeId) {
|
|
53
|
+
this.completedNodes.delete(nodeId);
|
|
54
|
+
const idx = this.executionOrder.indexOf(nodeId);
|
|
55
|
+
if (idx !== -1) {
|
|
56
|
+
this.executionOrder.splice(idx, 1);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Check if a node has a compensation step
|
|
61
|
+
*/
|
|
62
|
+
hasCompensation(nodeId) {
|
|
63
|
+
return this.steps.has(nodeId);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Get nodes that need compensation (completed nodes with compensation steps)
|
|
67
|
+
*/
|
|
68
|
+
getCompensableNodes() {
|
|
69
|
+
return this.executionOrder.filter((nodeId) => this.steps.has(nodeId) && this.completedNodes.has(nodeId));
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Execute compensation for all completed nodes
|
|
73
|
+
*/
|
|
74
|
+
async compensate(state, failedNodeId, error) {
|
|
75
|
+
const startTime = Date.now();
|
|
76
|
+
const compensated = [];
|
|
77
|
+
const partialFailures = [];
|
|
78
|
+
const nodesToCompensate = this.getCompensableNodes();
|
|
79
|
+
const sortedNodes = this.sortByCompensationOrder(nodesToCompensate);
|
|
80
|
+
for (const nodeId of sortedNodes) {
|
|
81
|
+
const step = this.steps.get(nodeId);
|
|
82
|
+
const originalResult = this.completedNodes.get(nodeId);
|
|
83
|
+
const stepStart = Date.now();
|
|
84
|
+
if (step.condition && !step.condition(state, error)) {
|
|
85
|
+
compensated.push({
|
|
86
|
+
nodeId,
|
|
87
|
+
success: true,
|
|
88
|
+
duration: 0,
|
|
89
|
+
skipped: true,
|
|
90
|
+
skipReason: 'Condition not met',
|
|
91
|
+
});
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
let lastError;
|
|
95
|
+
let success = false;
|
|
96
|
+
for (let attempt = 0; attempt <= (step.retries ?? 0); attempt++) {
|
|
97
|
+
try {
|
|
98
|
+
if (step.timeout) {
|
|
99
|
+
await this.withTimeout(step.compensationFn(state, originalResult), step.timeout);
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
await step.compensationFn(state, originalResult);
|
|
103
|
+
}
|
|
104
|
+
success = true;
|
|
105
|
+
break;
|
|
106
|
+
}
|
|
107
|
+
catch (err) {
|
|
108
|
+
lastError = err instanceof Error ? err : new Error(String(err));
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
compensated.push({
|
|
112
|
+
nodeId,
|
|
113
|
+
success,
|
|
114
|
+
error: lastError,
|
|
115
|
+
duration: Date.now() - stepStart,
|
|
116
|
+
skipped: false,
|
|
117
|
+
});
|
|
118
|
+
if (!success) {
|
|
119
|
+
partialFailures.push(nodeId);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return {
|
|
123
|
+
triggeredBy: {
|
|
124
|
+
nodeId: failedNodeId,
|
|
125
|
+
error,
|
|
126
|
+
},
|
|
127
|
+
compensated,
|
|
128
|
+
totalDuration: Date.now() - startTime,
|
|
129
|
+
allSuccessful: partialFailures.length === 0,
|
|
130
|
+
partialFailures,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Sort nodes by their compensation order
|
|
135
|
+
*/
|
|
136
|
+
sortByCompensationOrder(nodes) {
|
|
137
|
+
const result = [];
|
|
138
|
+
const parallel = [];
|
|
139
|
+
const reverse = [];
|
|
140
|
+
const forward = [];
|
|
141
|
+
for (const nodeId of nodes) {
|
|
142
|
+
const step = this.steps.get(nodeId);
|
|
143
|
+
const order = step?.order ?? 'reverse';
|
|
144
|
+
switch (order) {
|
|
145
|
+
case 'parallel':
|
|
146
|
+
parallel.push(nodeId);
|
|
147
|
+
break;
|
|
148
|
+
case 'forward':
|
|
149
|
+
forward.push(nodeId);
|
|
150
|
+
break;
|
|
151
|
+
case 'reverse':
|
|
152
|
+
default:
|
|
153
|
+
reverse.push(nodeId);
|
|
154
|
+
break;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
result.push(...parallel);
|
|
158
|
+
const reverseOrder = [...this.executionOrder]
|
|
159
|
+
.reverse()
|
|
160
|
+
.filter((n) => reverse.includes(n));
|
|
161
|
+
result.push(...reverseOrder);
|
|
162
|
+
const forwardOrder = this.executionOrder.filter((n) => forward.includes(n));
|
|
163
|
+
result.push(...forwardOrder);
|
|
164
|
+
return result;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Execute with timeout
|
|
168
|
+
*/
|
|
169
|
+
async withTimeout(promise, timeoutMs) {
|
|
170
|
+
return Promise.race([
|
|
171
|
+
promise,
|
|
172
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error('Compensation timeout')), timeoutMs)),
|
|
173
|
+
]);
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Reset manager state
|
|
177
|
+
*/
|
|
178
|
+
reset() {
|
|
179
|
+
this.completedNodes.clear();
|
|
180
|
+
this.executionOrder = [];
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Get current state summary
|
|
184
|
+
*/
|
|
185
|
+
getSummary() {
|
|
186
|
+
return {
|
|
187
|
+
registeredSteps: this.steps.size,
|
|
188
|
+
completedNodes: this.completedNodes.size,
|
|
189
|
+
compensableNodes: this.getCompensableNodes().length,
|
|
190
|
+
executionOrder: [...this.executionOrder],
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Create a compensation manager
|
|
196
|
+
*/
|
|
197
|
+
export function createCompensationManager() {
|
|
198
|
+
return new CompensationManager();
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Compensation builder for fluent API
|
|
202
|
+
*/
|
|
203
|
+
export class CompensationBuilder {
|
|
204
|
+
steps = [];
|
|
205
|
+
/**
|
|
206
|
+
* Add a compensation step
|
|
207
|
+
*/
|
|
208
|
+
addStep(nodeId, fn, options = {}) {
|
|
209
|
+
this.steps.push({ nodeId, fn, options });
|
|
210
|
+
return this;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Add conditional compensation
|
|
214
|
+
*/
|
|
215
|
+
addConditionalStep(nodeId, fn, condition, options = {}) {
|
|
216
|
+
this.steps.push({
|
|
217
|
+
nodeId,
|
|
218
|
+
fn,
|
|
219
|
+
options: { ...options, condition },
|
|
220
|
+
});
|
|
221
|
+
return this;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Build the compensation manager
|
|
225
|
+
*/
|
|
226
|
+
build() {
|
|
227
|
+
const manager = new CompensationManager();
|
|
228
|
+
for (const step of this.steps) {
|
|
229
|
+
manager.registerCompensation(step.nodeId, step.fn, step.options);
|
|
230
|
+
}
|
|
231
|
+
return manager;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Create a compensation builder
|
|
236
|
+
*/
|
|
237
|
+
export function compensationBuilder() {
|
|
238
|
+
return new CompensationBuilder();
|
|
239
|
+
}
|
|
240
|
+
//# sourceMappingURL=compensation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compensation.js","sourceRoot":"","sources":["../../src/saga/compensation.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dead Letter Queue (DLQ) for failed workflow nodes
|
|
3
|
+
*
|
|
4
|
+
* Features:
|
|
5
|
+
* - Multiple storage backends (memory, file)
|
|
6
|
+
* - Failed node tracking with full context
|
|
7
|
+
* - Retry capability
|
|
8
|
+
* - Expiration/cleanup
|
|
9
|
+
* - Query and filtering
|
|
10
|
+
*/
|
|
11
|
+
import type { DeadLetterEntry, WorkflowState } from '@cogitator-ai/types';
|
|
12
|
+
/**
|
|
13
|
+
* Extended dead letter entry (alias for DeadLetterEntry, for backwards compat)
|
|
14
|
+
*/
|
|
15
|
+
export type ExtendedDeadLetterEntry = DeadLetterEntry;
|
|
16
|
+
/**
|
|
17
|
+
* DLQ query filters
|
|
18
|
+
*/
|
|
19
|
+
export interface DLQFilters {
|
|
20
|
+
workflowId?: string;
|
|
21
|
+
workflowName?: string;
|
|
22
|
+
nodeId?: string;
|
|
23
|
+
minAttempts?: number;
|
|
24
|
+
maxAttempts?: number;
|
|
25
|
+
createdAfter?: number;
|
|
26
|
+
createdBefore?: number;
|
|
27
|
+
tags?: string[];
|
|
28
|
+
limit?: number;
|
|
29
|
+
offset?: number;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Abstract DLQ implementation
|
|
33
|
+
*/
|
|
34
|
+
export declare abstract class BaseDLQ {
|
|
35
|
+
abstract add(entry: DeadLetterEntry): Promise<string>;
|
|
36
|
+
abstract get(id: string): Promise<DeadLetterEntry | null>;
|
|
37
|
+
abstract list(filters?: DLQFilters): Promise<DeadLetterEntry[]>;
|
|
38
|
+
abstract retry(id: string): Promise<boolean>;
|
|
39
|
+
abstract remove(id: string): Promise<boolean>;
|
|
40
|
+
abstract count(filters?: DLQFilters): Promise<number>;
|
|
41
|
+
abstract clear(): Promise<void>;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* In-memory Dead Letter Queue
|
|
45
|
+
*/
|
|
46
|
+
export declare class InMemoryDLQ extends BaseDLQ {
|
|
47
|
+
private entries;
|
|
48
|
+
private defaultTTL;
|
|
49
|
+
private cleanupInterval?;
|
|
50
|
+
constructor(options?: {
|
|
51
|
+
defaultTTL?: number;
|
|
52
|
+
cleanupIntervalMs?: number;
|
|
53
|
+
});
|
|
54
|
+
add(entry: DeadLetterEntry): Promise<string>;
|
|
55
|
+
get(id: string): Promise<DeadLetterEntry | null>;
|
|
56
|
+
list(filters?: DLQFilters): Promise<DeadLetterEntry[]>;
|
|
57
|
+
retry(id: string): Promise<boolean>;
|
|
58
|
+
remove(id: string): Promise<boolean>;
|
|
59
|
+
count(filters?: DLQFilters): Promise<number>;
|
|
60
|
+
clear(): Promise<void>;
|
|
61
|
+
/**
|
|
62
|
+
* Cleanup expired entries
|
|
63
|
+
*/
|
|
64
|
+
private cleanup;
|
|
65
|
+
/**
|
|
66
|
+
* Stop cleanup interval
|
|
67
|
+
*/
|
|
68
|
+
destroy(): void;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* File-based Dead Letter Queue
|
|
72
|
+
*/
|
|
73
|
+
export declare class FileDLQ extends BaseDLQ {
|
|
74
|
+
private directory;
|
|
75
|
+
private defaultTTL;
|
|
76
|
+
private initialized;
|
|
77
|
+
constructor(directory: string, options?: {
|
|
78
|
+
defaultTTL?: number;
|
|
79
|
+
});
|
|
80
|
+
private ensureInitialized;
|
|
81
|
+
private getFilePath;
|
|
82
|
+
add(entry: DeadLetterEntry): Promise<string>;
|
|
83
|
+
get(id: string): Promise<DeadLetterEntry | null>;
|
|
84
|
+
list(filters?: DLQFilters): Promise<DeadLetterEntry[]>;
|
|
85
|
+
retry(id: string): Promise<boolean>;
|
|
86
|
+
remove(id: string): Promise<boolean>;
|
|
87
|
+
count(filters?: DLQFilters): Promise<number>;
|
|
88
|
+
clear(): Promise<void>;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Create DLQ entry from error
|
|
92
|
+
*/
|
|
93
|
+
export declare function createDLQEntry(nodeId: string, workflowId: string, workflowName: string, state: WorkflowState, error: Error, options?: {
|
|
94
|
+
input?: unknown;
|
|
95
|
+
attempts?: number;
|
|
96
|
+
maxAttempts?: number;
|
|
97
|
+
tags?: string[];
|
|
98
|
+
metadata?: Record<string, unknown>;
|
|
99
|
+
}): DeadLetterEntry;
|
|
100
|
+
/**
|
|
101
|
+
* Create an in-memory DLQ
|
|
102
|
+
*/
|
|
103
|
+
export declare function createInMemoryDLQ(options?: {
|
|
104
|
+
defaultTTL?: number;
|
|
105
|
+
cleanupIntervalMs?: number;
|
|
106
|
+
}): InMemoryDLQ;
|
|
107
|
+
/**
|
|
108
|
+
* Create a file-based DLQ
|
|
109
|
+
*/
|
|
110
|
+
export declare function createFileDLQ(directory: string, options?: {
|
|
111
|
+
defaultTTL?: number;
|
|
112
|
+
}): FileDLQ;
|
|
113
|
+
//# sourceMappingURL=dead-letter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dead-letter.d.ts","sourceRoot":"","sources":["../../src/saga/dead-letter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dead Letter Queue (DLQ) for failed workflow nodes
|
|
3
|
+
*
|
|
4
|
+
* Features:
|
|
5
|
+
* - Multiple storage backends (memory, file)
|
|
6
|
+
* - Failed node tracking with full context
|
|
7
|
+
* - Retry capability
|
|
8
|
+
* - Expiration/cleanup
|
|
9
|
+
* - Query and filtering
|
|
10
|
+
*/
|
|
11
|
+
import { nanoid } from 'nanoid';
|
|
12
|
+
import { promises as fs } from 'fs';
|
|
13
|
+
import { join } from 'path';
|
|
14
|
+
/**
|
|
15
|
+
* Abstract DLQ implementation
|
|
16
|
+
*/
|
|
17
|
+
export class BaseDLQ {
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* In-memory Dead Letter Queue
|
|
21
|
+
*/
|
|
22
|
+
export class InMemoryDLQ extends BaseDLQ {
|
|
23
|
+
entries = new Map();
|
|
24
|
+
defaultTTL;
|
|
25
|
+
cleanupInterval;
|
|
26
|
+
constructor(options = {}) {
|
|
27
|
+
super();
|
|
28
|
+
this.defaultTTL = options.defaultTTL ?? 7 * 24 * 60 * 60 * 1000;
|
|
29
|
+
if (options.cleanupIntervalMs) {
|
|
30
|
+
this.cleanupInterval = setInterval(() => void this.cleanup(), options.cleanupIntervalMs);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
async add(entry) {
|
|
34
|
+
const id = `dlq_${nanoid(12)}`;
|
|
35
|
+
const now = Date.now();
|
|
36
|
+
const extended = {
|
|
37
|
+
...entry,
|
|
38
|
+
id,
|
|
39
|
+
createdAt: now,
|
|
40
|
+
expiresAt: now + this.defaultTTL,
|
|
41
|
+
};
|
|
42
|
+
this.entries.set(id, extended);
|
|
43
|
+
return id;
|
|
44
|
+
}
|
|
45
|
+
async get(id) {
|
|
46
|
+
const entry = this.entries.get(id);
|
|
47
|
+
if (!entry)
|
|
48
|
+
return null;
|
|
49
|
+
if (entry.expiresAt && entry.expiresAt < Date.now()) {
|
|
50
|
+
this.entries.delete(id);
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
return entry;
|
|
54
|
+
}
|
|
55
|
+
async list(filters = {}) {
|
|
56
|
+
const now = Date.now();
|
|
57
|
+
let results = [];
|
|
58
|
+
for (const entry of this.entries.values()) {
|
|
59
|
+
if (entry.expiresAt && entry.expiresAt < now)
|
|
60
|
+
continue;
|
|
61
|
+
if (filters.workflowId && entry.workflowId !== filters.workflowId)
|
|
62
|
+
continue;
|
|
63
|
+
if (filters.workflowName && entry.workflowName !== filters.workflowName)
|
|
64
|
+
continue;
|
|
65
|
+
if (filters.nodeId && entry.nodeId !== filters.nodeId)
|
|
66
|
+
continue;
|
|
67
|
+
if (filters.minAttempts !== undefined && entry.attempts < filters.minAttempts)
|
|
68
|
+
continue;
|
|
69
|
+
if (filters.maxAttempts !== undefined && entry.attempts > filters.maxAttempts)
|
|
70
|
+
continue;
|
|
71
|
+
if (filters.createdAfter !== undefined && entry.createdAt < filters.createdAfter)
|
|
72
|
+
continue;
|
|
73
|
+
if (filters.createdBefore !== undefined && entry.createdAt > filters.createdBefore)
|
|
74
|
+
continue;
|
|
75
|
+
if (filters.tags && !filters.tags.every((t) => entry.tags?.includes(t)))
|
|
76
|
+
continue;
|
|
77
|
+
results.push(entry);
|
|
78
|
+
}
|
|
79
|
+
results.sort((a, b) => b.createdAt - a.createdAt);
|
|
80
|
+
if (filters.offset) {
|
|
81
|
+
results = results.slice(filters.offset);
|
|
82
|
+
}
|
|
83
|
+
if (filters.limit) {
|
|
84
|
+
results = results.slice(0, filters.limit);
|
|
85
|
+
}
|
|
86
|
+
return results;
|
|
87
|
+
}
|
|
88
|
+
async retry(id) {
|
|
89
|
+
const entry = await this.get(id);
|
|
90
|
+
if (!entry)
|
|
91
|
+
return false;
|
|
92
|
+
entry.attempts++;
|
|
93
|
+
entry.lastAttempt = Date.now();
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
async remove(id) {
|
|
97
|
+
return this.entries.delete(id);
|
|
98
|
+
}
|
|
99
|
+
async count(filters = {}) {
|
|
100
|
+
const results = await this.list(filters);
|
|
101
|
+
return results.length;
|
|
102
|
+
}
|
|
103
|
+
async clear() {
|
|
104
|
+
this.entries.clear();
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Cleanup expired entries
|
|
108
|
+
*/
|
|
109
|
+
async cleanup() {
|
|
110
|
+
const now = Date.now();
|
|
111
|
+
const toDelete = [];
|
|
112
|
+
for (const [id, entry] of this.entries) {
|
|
113
|
+
if (entry.expiresAt && entry.expiresAt < now) {
|
|
114
|
+
toDelete.push(id);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
for (const id of toDelete) {
|
|
118
|
+
this.entries.delete(id);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Stop cleanup interval
|
|
123
|
+
*/
|
|
124
|
+
destroy() {
|
|
125
|
+
if (this.cleanupInterval) {
|
|
126
|
+
clearInterval(this.cleanupInterval);
|
|
127
|
+
this.cleanupInterval = undefined;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* File-based Dead Letter Queue
|
|
133
|
+
*/
|
|
134
|
+
export class FileDLQ extends BaseDLQ {
|
|
135
|
+
directory;
|
|
136
|
+
defaultTTL;
|
|
137
|
+
initialized = false;
|
|
138
|
+
constructor(directory, options = {}) {
|
|
139
|
+
super();
|
|
140
|
+
this.directory = directory;
|
|
141
|
+
this.defaultTTL = options.defaultTTL ?? 7 * 24 * 60 * 60 * 1000;
|
|
142
|
+
}
|
|
143
|
+
async ensureInitialized() {
|
|
144
|
+
if (this.initialized)
|
|
145
|
+
return;
|
|
146
|
+
try {
|
|
147
|
+
await fs.mkdir(this.directory, { recursive: true });
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
}
|
|
151
|
+
this.initialized = true;
|
|
152
|
+
}
|
|
153
|
+
getFilePath(id) {
|
|
154
|
+
return join(this.directory, `${id}.json`);
|
|
155
|
+
}
|
|
156
|
+
async add(entry) {
|
|
157
|
+
await this.ensureInitialized();
|
|
158
|
+
const id = `dlq_${nanoid(12)}`;
|
|
159
|
+
const now = Date.now();
|
|
160
|
+
const extended = {
|
|
161
|
+
...entry,
|
|
162
|
+
id,
|
|
163
|
+
createdAt: now,
|
|
164
|
+
expiresAt: now + this.defaultTTL,
|
|
165
|
+
};
|
|
166
|
+
const filePath = this.getFilePath(id);
|
|
167
|
+
await fs.writeFile(filePath, JSON.stringify(extended, null, 2));
|
|
168
|
+
return id;
|
|
169
|
+
}
|
|
170
|
+
async get(id) {
|
|
171
|
+
await this.ensureInitialized();
|
|
172
|
+
try {
|
|
173
|
+
const filePath = this.getFilePath(id);
|
|
174
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
175
|
+
const entry = JSON.parse(content);
|
|
176
|
+
if (entry.expiresAt && entry.expiresAt < Date.now()) {
|
|
177
|
+
await this.remove(id);
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
180
|
+
return entry;
|
|
181
|
+
}
|
|
182
|
+
catch {
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
async list(filters = {}) {
|
|
187
|
+
await this.ensureInitialized();
|
|
188
|
+
const now = Date.now();
|
|
189
|
+
let results = [];
|
|
190
|
+
try {
|
|
191
|
+
const files = await fs.readdir(this.directory);
|
|
192
|
+
for (const file of files) {
|
|
193
|
+
if (!file.endsWith('.json'))
|
|
194
|
+
continue;
|
|
195
|
+
const filePath = join(this.directory, file);
|
|
196
|
+
try {
|
|
197
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
198
|
+
const entry = JSON.parse(content);
|
|
199
|
+
if (entry.expiresAt && entry.expiresAt < now) {
|
|
200
|
+
await fs.unlink(filePath).catch(() => { });
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
if (filters.workflowId && entry.workflowId !== filters.workflowId)
|
|
204
|
+
continue;
|
|
205
|
+
if (filters.workflowName && entry.workflowName !== filters.workflowName)
|
|
206
|
+
continue;
|
|
207
|
+
if (filters.nodeId && entry.nodeId !== filters.nodeId)
|
|
208
|
+
continue;
|
|
209
|
+
if (filters.minAttempts !== undefined && entry.attempts < filters.minAttempts)
|
|
210
|
+
continue;
|
|
211
|
+
if (filters.maxAttempts !== undefined && entry.attempts > filters.maxAttempts)
|
|
212
|
+
continue;
|
|
213
|
+
if (filters.createdAfter !== undefined && entry.createdAt < filters.createdAfter)
|
|
214
|
+
continue;
|
|
215
|
+
if (filters.createdBefore !== undefined && entry.createdAt > filters.createdBefore)
|
|
216
|
+
continue;
|
|
217
|
+
if (filters.tags && !filters.tags.every((t) => entry.tags?.includes(t)))
|
|
218
|
+
continue;
|
|
219
|
+
results.push(entry);
|
|
220
|
+
}
|
|
221
|
+
catch {
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
catch {
|
|
226
|
+
}
|
|
227
|
+
results.sort((a, b) => b.createdAt - a.createdAt);
|
|
228
|
+
if (filters.offset) {
|
|
229
|
+
results = results.slice(filters.offset);
|
|
230
|
+
}
|
|
231
|
+
if (filters.limit) {
|
|
232
|
+
results = results.slice(0, filters.limit);
|
|
233
|
+
}
|
|
234
|
+
return results;
|
|
235
|
+
}
|
|
236
|
+
async retry(id) {
|
|
237
|
+
const entry = await this.get(id);
|
|
238
|
+
if (!entry)
|
|
239
|
+
return false;
|
|
240
|
+
entry.attempts++;
|
|
241
|
+
entry.lastAttempt = Date.now();
|
|
242
|
+
const filePath = this.getFilePath(id);
|
|
243
|
+
await fs.writeFile(filePath, JSON.stringify(entry, null, 2));
|
|
244
|
+
return true;
|
|
245
|
+
}
|
|
246
|
+
async remove(id) {
|
|
247
|
+
try {
|
|
248
|
+
await fs.unlink(this.getFilePath(id));
|
|
249
|
+
return true;
|
|
250
|
+
}
|
|
251
|
+
catch {
|
|
252
|
+
return false;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
async count(filters = {}) {
|
|
256
|
+
const results = await this.list(filters);
|
|
257
|
+
return results.length;
|
|
258
|
+
}
|
|
259
|
+
async clear() {
|
|
260
|
+
await this.ensureInitialized();
|
|
261
|
+
try {
|
|
262
|
+
const files = await fs.readdir(this.directory);
|
|
263
|
+
await Promise.all(files
|
|
264
|
+
.filter((f) => f.endsWith('.json'))
|
|
265
|
+
.map((f) => fs.unlink(join(this.directory, f)).catch(() => { })));
|
|
266
|
+
}
|
|
267
|
+
catch {
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Create DLQ entry from error
|
|
273
|
+
*/
|
|
274
|
+
export function createDLQEntry(nodeId, workflowId, workflowName, state, error, options = {}) {
|
|
275
|
+
return {
|
|
276
|
+
id: '',
|
|
277
|
+
nodeId,
|
|
278
|
+
workflowId,
|
|
279
|
+
workflowName,
|
|
280
|
+
state,
|
|
281
|
+
input: options.input,
|
|
282
|
+
error: {
|
|
283
|
+
name: error.name,
|
|
284
|
+
message: error.message,
|
|
285
|
+
stack: error.stack,
|
|
286
|
+
},
|
|
287
|
+
attempts: options.attempts ?? 1,
|
|
288
|
+
maxAttempts: options.maxAttempts ?? 1,
|
|
289
|
+
lastAttempt: Date.now(),
|
|
290
|
+
createdAt: Date.now(),
|
|
291
|
+
tags: options.tags,
|
|
292
|
+
metadata: options.metadata,
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Create an in-memory DLQ
|
|
297
|
+
*/
|
|
298
|
+
export function createInMemoryDLQ(options) {
|
|
299
|
+
return new InMemoryDLQ(options);
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Create a file-based DLQ
|
|
303
|
+
*/
|
|
304
|
+
export function createFileDLQ(directory, options) {
|
|
305
|
+
return new FileDLQ(directory, options);
|
|
306
|
+
}
|
|
307
|
+
//# sourceMappingURL=dead-letter.js.map
|