bunqueue 2.8.48 → 2.8.50
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/README.md +19 -9
- package/dist/application/contextFactory.d.ts +4 -2
- package/dist/application/contextFactory.js +3 -0
- package/dist/application/flowFailureRecovery.d.ts +17 -0
- package/dist/application/flowFailureRecovery.js +92 -0
- package/dist/application/operations/ack.d.ts +3 -2
- package/dist/application/operations/ack.js +34 -8
- package/dist/application/operations/flowPush.d.ts +9 -0
- package/dist/application/operations/flowPush.js +112 -0
- package/dist/application/operations/flowTopologyValidation.d.ts +3 -0
- package/dist/application/operations/flowTopologyValidation.js +101 -0
- package/dist/application/operations/flowValidation.d.ts +3 -0
- package/dist/application/operations/flowValidation.js +166 -0
- package/dist/application/operations/push.d.ts +3 -0
- package/dist/application/operations/push.js +2 -2
- package/dist/application/operations/pushInsert.d.ts +7 -1
- package/dist/application/operations/pushInsert.js +18 -8
- package/dist/application/operations/pushLocks.d.ts +1 -0
- package/dist/application/operations/pushLocks.js +5 -1
- package/dist/application/operations/queryOperations.js +7 -6
- package/dist/application/queueManager.d.ts +5 -0
- package/dist/application/queueManager.js +195 -60
- package/dist/cli/commandRouter.d.ts +1 -1
- package/dist/client/flow.d.ts +3 -6
- package/dist/client/flow.js +71 -210
- package/dist/client/flowAtomic.d.ts +9 -0
- package/dist/client/flowAtomic.js +24 -0
- package/dist/client/flowJobCoreMethods.d.ts +28 -0
- package/dist/client/flowJobCoreMethods.js +151 -0
- package/dist/client/flowJobDependencies.d.ts +7 -0
- package/dist/client/flowJobDependencies.js +64 -0
- package/dist/client/flowJobFactory.d.ts +11 -24
- package/dist/client/flowJobFactory.js +91 -360
- package/dist/client/flowJobMoveMethods.d.ts +16 -0
- package/dist/client/flowJobMoveMethods.js +112 -0
- package/dist/client/flowJobTypes.d.ts +29 -0
- package/dist/client/flowJobTypes.js +6 -0
- package/dist/client/flowLegacyPlan.d.ts +16 -0
- package/dist/client/flowLegacyPlan.js +118 -0
- package/dist/client/flowOptions.d.ts +10 -0
- package/dist/client/flowOptions.js +44 -0
- package/dist/client/flowPlan.d.ts +16 -0
- package/dist/client/flowPlan.js +93 -0
- package/dist/client/flowPush.js +4 -1
- package/dist/client/flowReader.d.ts +10 -0
- package/dist/client/flowReader.js +147 -0
- package/dist/client/jobHelpers.js +2 -0
- package/dist/client/workflow/clock.d.ts +3 -1
- package/dist/client/workflow/clock.js +18 -1
- package/dist/client/workflow/compensationChild.d.ts +30 -0
- package/dist/client/workflow/compensationChild.js +65 -0
- package/dist/client/workflow/compensationPass.d.ts +16 -0
- package/dist/client/workflow/compensationPass.js +96 -0
- package/dist/client/workflow/compensationSupport.d.ts +24 -0
- package/dist/client/workflow/compensationSupport.js +57 -0
- package/dist/client/workflow/compensator.d.ts +11 -50
- package/dist/client/workflow/compensator.js +22 -374
- package/dist/client/workflow/definitionGuard.d.ts +11 -0
- package/dist/client/workflow/definitionGuard.js +22 -0
- package/dist/client/workflow/engine.d.ts +2 -2
- package/dist/client/workflow/engine.js +8 -8
- package/dist/client/workflow/eventTypes.d.ts +44 -0
- package/dist/client/workflow/eventTypes.js +2 -0
- package/dist/client/workflow/executionTypes.d.ts +133 -0
- package/dist/client/workflow/executionTypes.js +2 -0
- package/dist/client/workflow/executor.d.ts +4 -17
- package/dist/client/workflow/executor.js +50 -208
- package/dist/client/workflow/executorLifecycle.d.ts +18 -0
- package/dist/client/workflow/executorLifecycle.js +69 -0
- package/dist/client/workflow/executorNodes.d.ts +17 -0
- package/dist/client/workflow/executorNodes.js +138 -0
- package/dist/client/workflow/identity.d.ts +2 -0
- package/dist/client/workflow/identity.js +8 -0
- package/dist/client/workflow/index.d.ts +1 -1
- package/dist/client/workflow/loops.d.ts +3 -5
- package/dist/client/workflow/loops.js +25 -23
- package/dist/client/workflow/mapRunner.d.ts +4 -0
- package/dist/client/workflow/mapRunner.js +45 -0
- package/dist/client/workflow/recovery.js +10 -3
- package/dist/client/workflow/runner.d.ts +2 -21
- package/dist/client/workflow/runner.js +39 -104
- package/dist/client/workflow/runnerTiming.d.ts +14 -0
- package/dist/client/workflow/runnerTiming.js +66 -0
- package/dist/client/workflow/stepTypes.d.ts +171 -0
- package/dist/client/workflow/stepTypes.js +3 -0
- package/dist/client/workflow/store.d.ts +17 -17
- package/dist/client/workflow/store.js +65 -100
- package/dist/client/workflow/storeExecutionCodec.d.ts +11 -0
- package/dist/client/workflow/storeExecutionCodec.js +34 -0
- package/dist/client/workflow/storeListing.d.ts +11 -0
- package/dist/client/workflow/storeListing.js +45 -0
- package/dist/client/workflow/storeMaintenance.d.ts +4 -0
- package/dist/client/workflow/storeMaintenance.js +40 -0
- package/dist/client/workflow/storeSignals.d.ts +9 -0
- package/dist/client/workflow/storeSignals.js +38 -2
- package/dist/client/workflow/subWorkflowRunner.d.ts +13 -0
- package/dist/client/workflow/subWorkflowRunner.js +40 -0
- package/dist/client/workflow/types.d.ts +4 -356
- package/dist/client/workflow/types.js +1 -3
- package/dist/client/workflow/waitFor.js +39 -26
- package/dist/client/workflow/workflow.d.ts +16 -59
- package/dist/client/workflow/workflow.js +53 -179
- package/dist/client/workflow/workflowDecisions.d.ts +11 -0
- package/dist/client/workflow/workflowDecisions.js +27 -0
- package/dist/client/workflow/workflowDefinition.d.ts +16 -0
- package/dist/client/workflow/workflowDefinition.js +123 -0
- package/dist/client/workflow/workflowIntrospection.d.ts +5 -0
- package/dist/client/workflow/workflowIntrospection.js +46 -0
- package/dist/client/workflow/workflowValidation.d.ts +44 -0
- package/dist/client/workflow/workflowValidation.js +143 -0
- package/dist/domain/types/command.d.ts +7 -1
- package/dist/domain/types/flow.d.ts +25 -0
- package/dist/domain/types/flow.js +1 -0
- package/dist/infrastructure/persistence/schema.d.ts +2 -2
- package/dist/infrastructure/persistence/schema.js +52 -3
- package/dist/infrastructure/persistence/sqlite.d.ts +16 -0
- package/dist/infrastructure/persistence/sqlite.js +129 -7
- package/dist/infrastructure/persistence/sqliteBatch.js +10 -4
- package/dist/infrastructure/persistence/sqliteSerializer.d.ts +2 -0
- package/dist/infrastructure/persistence/sqliteSerializer.js +18 -5
- package/dist/infrastructure/persistence/statements.d.ts +4 -0
- package/dist/infrastructure/persistence/statements.js +8 -2
- package/dist/infrastructure/server/handlerRoutes.js +3 -0
- package/dist/infrastructure/server/handlers/advanced.js +2 -2
- package/dist/infrastructure/server/handlers/flow.d.ts +7 -0
- package/dist/infrastructure/server/handlers/flow.js +11 -0
- package/dist/infrastructure/server/handlers/index.d.ts +1 -0
- package/dist/infrastructure/server/handlers/index.js +1 -0
- package/package.json +4 -2
|
@@ -1,29 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Flow Job Factory
|
|
3
|
-
* Creates
|
|
3
|
+
* Creates BullMQ-compatible Job objects backed by FlowProducer transports.
|
|
4
4
|
*/
|
|
5
|
+
import type { Job as DomainJob } from '../domain/types/job';
|
|
6
|
+
import type { FlowJobCallbacks } from './flowJobTypes';
|
|
5
7
|
import type { Job } from './types';
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
export interface FlowJobCallbacks {
|
|
9
|
-
/** Execution mode — enables full method wiring when provided */
|
|
10
|
-
embedded?: boolean;
|
|
11
|
-
tcp?: TcpConnectionPool | null;
|
|
12
|
-
updateData?: (id: string, data: unknown) => Promise<void>;
|
|
13
|
-
updateProgress?: (id: string, progress: number | object) => Promise<void>;
|
|
14
|
-
log?: (id: string, message: string) => Promise<void>;
|
|
15
|
-
promote?: (id: string) => Promise<void>;
|
|
16
|
-
remove?: (id: string) => Promise<void>;
|
|
17
|
-
changePriority?: (id: string, opts: {
|
|
18
|
-
priority: number;
|
|
19
|
-
lifo?: boolean;
|
|
20
|
-
}) => Promise<void>;
|
|
21
|
-
changeDelay?: (id: string, delay: number) => Promise<void>;
|
|
22
|
-
clearLogs?: (id: string) => Promise<void>;
|
|
23
|
-
retry?: (id: string) => Promise<void>;
|
|
24
|
-
getState?: (id: string) => Promise<string>;
|
|
25
|
-
}
|
|
26
|
-
/** Extract user data (remove internal fields like __parentId, __childrenIds, name) */
|
|
8
|
+
export type { FlowJobCallbacks } from './flowJobTypes';
|
|
9
|
+
/** Extract user data while removing internal flow metadata and the stored job name. */
|
|
27
10
|
export declare function extractUserDataFromInternal(data: Record<string, unknown>): unknown;
|
|
28
|
-
|
|
29
|
-
|
|
11
|
+
interface FlowJobObjectOptions {
|
|
12
|
+
callbacks?: FlowJobCallbacks;
|
|
13
|
+
snapshot?: DomainJob;
|
|
14
|
+
}
|
|
15
|
+
/** Create a Job object for a newly committed or fetched flow node. */
|
|
16
|
+
export declare function createFlowJobObject<T>(id: string, name: string, data: T, queueName: string, factoryOptions?: FlowJobObjectOptions): Job<T>;
|
|
@@ -1,227 +1,45 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Flow Job Factory
|
|
3
|
-
* Creates
|
|
3
|
+
* Creates BullMQ-compatible Job objects backed by FlowProducer transports.
|
|
4
4
|
*/
|
|
5
|
-
import { getSharedManager } from './manager';
|
|
6
|
-
import { buildFailCommand, failEmbeddedArgs } from './queue/failWire';
|
|
7
5
|
import { jobId } from '../domain/types/job';
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
6
|
+
import { buildJobProperties, buildSerializationMethods } from './jobConversionHelpers';
|
|
7
|
+
import { buildJobOpts } from './jobHelpers';
|
|
8
|
+
import { buildFlowJobCoreMethods } from './flowJobCoreMethods';
|
|
9
|
+
import { buildFlowJobMoveMethods } from './flowJobMoveMethods';
|
|
10
|
+
import { getSharedManager } from './manager';
|
|
11
|
+
const JOB_STATES = new Set([
|
|
12
|
+
'waiting',
|
|
13
|
+
'active',
|
|
14
|
+
'completed',
|
|
15
|
+
'failed',
|
|
16
|
+
'delayed',
|
|
17
|
+
'prioritized',
|
|
18
|
+
'waiting-children',
|
|
19
|
+
'unknown',
|
|
20
|
+
]);
|
|
21
|
+
function stateAsType(state) {
|
|
22
|
+
return JOB_STATES.has(state) ? state : 'unknown';
|
|
23
|
+
}
|
|
24
|
+
function buildStateResolver(id, callbacks) {
|
|
25
|
+
if (callbacks?.getState) {
|
|
26
|
+
return () => callbacks.getState(id).then(stateAsType);
|
|
15
27
|
}
|
|
16
|
-
|
|
28
|
+
if (callbacks?.embedded) {
|
|
29
|
+
return () => getSharedManager().getJobState(jobId(id)).then(stateAsType);
|
|
30
|
+
}
|
|
31
|
+
if (callbacks?.tcp) {
|
|
32
|
+
return () => callbacks.tcp.send({ cmd: 'GetState', id }).then((response) => {
|
|
33
|
+
if (response.ok !== true) {
|
|
34
|
+
throw new Error(typeof response.error === 'string' ? response.error : 'GetState failed');
|
|
35
|
+
}
|
|
36
|
+
return stateAsType(typeof response.state === 'string' ? response.state : 'unknown');
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
return () => Promise.resolve('unknown');
|
|
17
40
|
}
|
|
18
|
-
|
|
19
|
-
export function createFlowJobObject(id, name, data, queueName, callbacks) {
|
|
20
|
-
const ts = Date.now();
|
|
21
|
-
const embedded = !!callbacks?.embedded;
|
|
22
|
-
const tcp = callbacks?.tcp ?? null;
|
|
23
|
-
const stateAsType = (s) => {
|
|
24
|
-
const valid = [
|
|
25
|
-
'waiting',
|
|
26
|
-
'active',
|
|
27
|
-
'completed',
|
|
28
|
-
'failed',
|
|
29
|
-
'delayed',
|
|
30
|
-
'prioritized',
|
|
31
|
-
'waiting-children',
|
|
32
|
-
'unknown',
|
|
33
|
-
];
|
|
34
|
-
return (valid.includes(s) ? s : 'unknown');
|
|
35
|
-
};
|
|
36
|
-
const getStateInternal = () => {
|
|
37
|
-
if (callbacks?.getState)
|
|
38
|
-
return callbacks.getState(id).then(stateAsType);
|
|
39
|
-
if (embedded) {
|
|
40
|
-
return getSharedManager()
|
|
41
|
-
.getJobState(jobId(id))
|
|
42
|
-
.then((s) => stateAsType(s));
|
|
43
|
-
}
|
|
44
|
-
if (tcp) {
|
|
45
|
-
return tcp.send({ cmd: 'GetState', id }).then((r) => {
|
|
46
|
-
const s = r.state ?? 'unknown';
|
|
47
|
-
return stateAsType(s);
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
return Promise.resolve('unknown');
|
|
51
|
-
};
|
|
41
|
+
function defaultSerialization(id, name, data, queueName, timestamp) {
|
|
52
42
|
return {
|
|
53
|
-
id,
|
|
54
|
-
name,
|
|
55
|
-
data,
|
|
56
|
-
queueName,
|
|
57
|
-
attemptsMade: 0,
|
|
58
|
-
timestamp: ts,
|
|
59
|
-
progress: 0,
|
|
60
|
-
delay: 0,
|
|
61
|
-
processedOn: undefined,
|
|
62
|
-
finishedOn: undefined,
|
|
63
|
-
stacktrace: null,
|
|
64
|
-
stalledCounter: 0,
|
|
65
|
-
priority: 0,
|
|
66
|
-
parentKey: undefined,
|
|
67
|
-
opts: {},
|
|
68
|
-
token: undefined,
|
|
69
|
-
processedBy: undefined,
|
|
70
|
-
deduplicationId: undefined,
|
|
71
|
-
repeatJobKey: undefined,
|
|
72
|
-
attemptsStarted: 0,
|
|
73
|
-
updateProgress: async (progress) => {
|
|
74
|
-
if (callbacks?.updateProgress) {
|
|
75
|
-
return callbacks.updateProgress(id, progress);
|
|
76
|
-
}
|
|
77
|
-
if (embedded) {
|
|
78
|
-
await getSharedManager().updateProgress(jobId(id), progress);
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
if (tcp)
|
|
82
|
-
await tcp.send({ cmd: 'Progress', id, progress: progress });
|
|
83
|
-
},
|
|
84
|
-
log: async (message) => {
|
|
85
|
-
if (callbacks?.log)
|
|
86
|
-
return callbacks.log(id, message);
|
|
87
|
-
if (embedded) {
|
|
88
|
-
getSharedManager().addLog(jobId(id), message);
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
if (tcp)
|
|
92
|
-
await tcp.send({ cmd: 'AddLog', id, message });
|
|
93
|
-
},
|
|
94
|
-
getState: getStateInternal,
|
|
95
|
-
remove: async () => {
|
|
96
|
-
if (callbacks?.remove)
|
|
97
|
-
return callbacks.remove(id);
|
|
98
|
-
if (embedded) {
|
|
99
|
-
await getSharedManager().cancel(jobId(id));
|
|
100
|
-
return;
|
|
101
|
-
}
|
|
102
|
-
if (tcp)
|
|
103
|
-
await tcp.send({ cmd: 'Cancel', id });
|
|
104
|
-
},
|
|
105
|
-
retry: async () => {
|
|
106
|
-
if (callbacks?.retry)
|
|
107
|
-
return callbacks.retry(id);
|
|
108
|
-
if (embedded) {
|
|
109
|
-
const mgr = getSharedManager();
|
|
110
|
-
const state = await mgr.getJobState(jobId(id));
|
|
111
|
-
if (state === 'failed') {
|
|
112
|
-
const count = mgr.retryDlq(queueName, jobId(id));
|
|
113
|
-
if (count === 0)
|
|
114
|
-
throw new Error(`Job ${id} is failed but not present in DLQ`);
|
|
115
|
-
return;
|
|
116
|
-
}
|
|
117
|
-
if (state === 'active') {
|
|
118
|
-
const ok = await mgr.moveActiveToWait(jobId(id));
|
|
119
|
-
if (!ok)
|
|
120
|
-
throw new Error(`Failed to retry active job ${id}`);
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
123
|
-
if (state === 'waiting' || state === 'prioritized' || state === 'delayed')
|
|
124
|
-
return;
|
|
125
|
-
throw new Error(`Cannot retry job ${id} from state '${state}'`);
|
|
126
|
-
}
|
|
127
|
-
if (!tcp)
|
|
128
|
-
return;
|
|
129
|
-
const res = await tcp.send({ cmd: 'MoveToWait', id });
|
|
130
|
-
if (res.ok !== true) {
|
|
131
|
-
const err = typeof res.error === 'string' ? res.error : 'retry failed';
|
|
132
|
-
throw new Error(err);
|
|
133
|
-
}
|
|
134
|
-
},
|
|
135
|
-
getChildrenValues: async () => {
|
|
136
|
-
if (embedded) {
|
|
137
|
-
return (await getSharedManager().getChildrenValues(jobId(id)));
|
|
138
|
-
}
|
|
139
|
-
if (!tcp)
|
|
140
|
-
return {};
|
|
141
|
-
const res = await tcp.send({ cmd: 'GetChildrenValues', id });
|
|
142
|
-
const vals = res.data?.values ?? {};
|
|
143
|
-
return vals;
|
|
144
|
-
},
|
|
145
|
-
isWaiting: async () => (await getStateInternal()) === 'waiting',
|
|
146
|
-
isActive: async () => (await getStateInternal()) === 'active',
|
|
147
|
-
isDelayed: async () => (await getStateInternal()) === 'delayed',
|
|
148
|
-
isCompleted: async () => (await getStateInternal()) === 'completed',
|
|
149
|
-
isFailed: async () => (await getStateInternal()) === 'failed',
|
|
150
|
-
isWaitingChildren: async () => (await getStateInternal()) === 'waiting-children',
|
|
151
|
-
updateData: async (newData) => {
|
|
152
|
-
if (callbacks?.updateData)
|
|
153
|
-
return callbacks.updateData(id, newData);
|
|
154
|
-
if (embedded) {
|
|
155
|
-
await getSharedManager().updateJobData(jobId(id), newData);
|
|
156
|
-
return;
|
|
157
|
-
}
|
|
158
|
-
if (tcp)
|
|
159
|
-
await tcp.send({ cmd: 'Update', id, data: newData });
|
|
160
|
-
},
|
|
161
|
-
promote: async () => {
|
|
162
|
-
if (callbacks?.promote)
|
|
163
|
-
return callbacks.promote(id);
|
|
164
|
-
if (embedded) {
|
|
165
|
-
await getSharedManager().promote(jobId(id));
|
|
166
|
-
return;
|
|
167
|
-
}
|
|
168
|
-
if (tcp)
|
|
169
|
-
await tcp.send({ cmd: 'Promote', id });
|
|
170
|
-
},
|
|
171
|
-
changeDelay: async (delay) => {
|
|
172
|
-
if (callbacks?.changeDelay)
|
|
173
|
-
return callbacks.changeDelay(id, delay);
|
|
174
|
-
if (embedded) {
|
|
175
|
-
await getSharedManager().changeDelay(jobId(id), delay);
|
|
176
|
-
return;
|
|
177
|
-
}
|
|
178
|
-
if (tcp)
|
|
179
|
-
await tcp.send({ cmd: 'ChangeDelay', id, delay });
|
|
180
|
-
},
|
|
181
|
-
changePriority: async (opts) => {
|
|
182
|
-
if (callbacks?.changePriority) {
|
|
183
|
-
return callbacks.changePriority(id, { priority: opts.priority, lifo: opts.lifo });
|
|
184
|
-
}
|
|
185
|
-
if (embedded) {
|
|
186
|
-
await getSharedManager().changePriority(jobId(id), opts.priority, opts.lifo);
|
|
187
|
-
return;
|
|
188
|
-
}
|
|
189
|
-
if (tcp) {
|
|
190
|
-
await tcp.send({ cmd: 'ChangePriority', id, priority: opts.priority, lifo: opts.lifo });
|
|
191
|
-
}
|
|
192
|
-
},
|
|
193
|
-
extendLock: async (token, duration) => {
|
|
194
|
-
if (embedded) {
|
|
195
|
-
const ok = await getSharedManager().extendLock(jobId(id), token, duration);
|
|
196
|
-
return ok ? duration : 0;
|
|
197
|
-
}
|
|
198
|
-
if (!tcp)
|
|
199
|
-
return 0;
|
|
200
|
-
const res = await tcp.send({ cmd: 'ExtendLock', id, token, duration });
|
|
201
|
-
return res.ok === true ? duration : 0;
|
|
202
|
-
},
|
|
203
|
-
clearLogs: async (keepLogs) => {
|
|
204
|
-
if (callbacks?.clearLogs)
|
|
205
|
-
return callbacks.clearLogs(id);
|
|
206
|
-
if (embedded) {
|
|
207
|
-
getSharedManager().clearLogs(jobId(id), keepLogs);
|
|
208
|
-
return;
|
|
209
|
-
}
|
|
210
|
-
if (tcp)
|
|
211
|
-
await tcp.send({ cmd: 'ClearLogs', id, keepLogs });
|
|
212
|
-
},
|
|
213
|
-
getDependencies: async () => {
|
|
214
|
-
const childIds = await fetchChildIds(id, embedded, tcp);
|
|
215
|
-
return computeDepsFlow(queueName, childIds, embedded, tcp);
|
|
216
|
-
},
|
|
217
|
-
getDependenciesCount: async () => {
|
|
218
|
-
const childIds = await fetchChildIds(id, embedded, tcp);
|
|
219
|
-
const deps = await computeDepsFlow(queueName, childIds, embedded, tcp);
|
|
220
|
-
return {
|
|
221
|
-
processed: Object.keys(deps.processed).length,
|
|
222
|
-
unprocessed: deps.unprocessed.length,
|
|
223
|
-
};
|
|
224
|
-
},
|
|
225
43
|
toJSON: () => ({
|
|
226
44
|
id,
|
|
227
45
|
name,
|
|
@@ -229,7 +47,7 @@ export function createFlowJobObject(id, name, data, queueName, callbacks) {
|
|
|
229
47
|
opts: {},
|
|
230
48
|
progress: 0,
|
|
231
49
|
delay: 0,
|
|
232
|
-
timestamp
|
|
50
|
+
timestamp,
|
|
233
51
|
attemptsMade: 0,
|
|
234
52
|
stacktrace: null,
|
|
235
53
|
queueQualifiedName: `bull:${queueName}`,
|
|
@@ -241,153 +59,66 @@ export function createFlowJobObject(id, name, data, queueName, callbacks) {
|
|
|
241
59
|
opts: '{}',
|
|
242
60
|
progress: '0',
|
|
243
61
|
delay: '0',
|
|
244
|
-
timestamp: String(
|
|
62
|
+
timestamp: String(timestamp),
|
|
245
63
|
attemptsMade: '0',
|
|
246
64
|
stacktrace: null,
|
|
247
65
|
}),
|
|
248
|
-
moveToCompleted: async (returnValue) => {
|
|
249
|
-
if (embedded) {
|
|
250
|
-
await getSharedManager().ack(jobId(id), returnValue);
|
|
251
|
-
return null;
|
|
252
|
-
}
|
|
253
|
-
if (tcp)
|
|
254
|
-
await tcp.send({ cmd: 'ACK', id, result: returnValue });
|
|
255
|
-
return null;
|
|
256
|
-
},
|
|
257
|
-
moveToFailed: async (error) => {
|
|
258
|
-
// Forward stacktrace + UnrecoverableError (was dropped; #74/#111-class).
|
|
259
|
-
if (embedded) {
|
|
260
|
-
await getSharedManager().fail(jobId(id), ...failEmbeddedArgs(error));
|
|
261
|
-
return;
|
|
262
|
-
}
|
|
263
|
-
if (tcp)
|
|
264
|
-
await tcp.send(buildFailCommand(id, error));
|
|
265
|
-
},
|
|
266
|
-
moveToWait: async () => {
|
|
267
|
-
if (embedded)
|
|
268
|
-
return await getSharedManager().moveActiveToWait(jobId(id));
|
|
269
|
-
if (!tcp)
|
|
270
|
-
return false;
|
|
271
|
-
const res = await tcp.send({ cmd: 'MoveToWait', id });
|
|
272
|
-
return res.ok === true;
|
|
273
|
-
},
|
|
274
|
-
moveToDelayed: async (timestamp) => {
|
|
275
|
-
const delay = Math.max(0, timestamp - Date.now());
|
|
276
|
-
if (embedded) {
|
|
277
|
-
await getSharedManager().moveToDelayed(jobId(id), delay);
|
|
278
|
-
return;
|
|
279
|
-
}
|
|
280
|
-
if (tcp)
|
|
281
|
-
await tcp.send({ cmd: 'MoveToDelayed', id, delay });
|
|
282
|
-
},
|
|
283
|
-
moveToWaitingChildren: async () => {
|
|
284
|
-
if (embedded)
|
|
285
|
-
return await getSharedManager().moveToWaitingChildren(jobId(id));
|
|
286
|
-
throw new Error('moveToWaitingChildren is not supported in TCP mode — no server command available');
|
|
287
|
-
},
|
|
288
|
-
waitUntilFinished: async (_qe, ttl) => {
|
|
289
|
-
const timeout = ttl ?? 30000;
|
|
290
|
-
if (embedded) {
|
|
291
|
-
const mgr = getSharedManager();
|
|
292
|
-
const job = await mgr.getJob(jobId(id));
|
|
293
|
-
if (!job)
|
|
294
|
-
throw new Error(`Job ${id} not found`);
|
|
295
|
-
if (job.completedAt)
|
|
296
|
-
return mgr.getResult(jobId(id));
|
|
297
|
-
const ok = await mgr.waitForJobCompletion(jobId(id), timeout);
|
|
298
|
-
if (!ok)
|
|
299
|
-
throw new Error(`waitUntilFinished timed out after ${timeout}ms`);
|
|
300
|
-
return mgr.getResult(jobId(id));
|
|
301
|
-
}
|
|
302
|
-
if (!tcp)
|
|
303
|
-
throw new Error('waitUntilFinished: no connection');
|
|
304
|
-
const res = await tcp.send({ cmd: 'WaitJob', id, timeout });
|
|
305
|
-
const typed = res;
|
|
306
|
-
if (!typed.completed)
|
|
307
|
-
throw new Error(`waitUntilFinished timed out after ${timeout}ms`);
|
|
308
|
-
return typed.result;
|
|
309
|
-
},
|
|
310
|
-
discard: () => {
|
|
311
|
-
if (embedded) {
|
|
312
|
-
void getSharedManager().discard(jobId(id));
|
|
313
|
-
return;
|
|
314
|
-
}
|
|
315
|
-
if (tcp)
|
|
316
|
-
void tcp.send({ cmd: 'Discard', id });
|
|
317
|
-
},
|
|
318
|
-
getFailedChildrenValues: async () => {
|
|
319
|
-
if (embedded)
|
|
320
|
-
return await getSharedManager().getFailedChildrenValues(jobId(id));
|
|
321
|
-
if (!tcp)
|
|
322
|
-
return {};
|
|
323
|
-
const res = await tcp.send({ cmd: 'GetFailedChildrenValues', id });
|
|
324
|
-
return res.values ?? {};
|
|
325
|
-
},
|
|
326
|
-
getIgnoredChildrenFailures: async () => {
|
|
327
|
-
if (embedded)
|
|
328
|
-
return await getSharedManager().getIgnoredChildrenFailures(jobId(id));
|
|
329
|
-
if (!tcp)
|
|
330
|
-
return {};
|
|
331
|
-
const res = await tcp.send({ cmd: 'GetIgnoredChildrenFailures', id });
|
|
332
|
-
return res.values ?? {};
|
|
333
|
-
},
|
|
334
|
-
removeChildDependency: async () => {
|
|
335
|
-
if (embedded)
|
|
336
|
-
return await getSharedManager().removeChildDependency(jobId(id));
|
|
337
|
-
if (!tcp)
|
|
338
|
-
return false;
|
|
339
|
-
const res = await tcp.send({ cmd: 'RemoveChildDependency', id });
|
|
340
|
-
return res.removed ?? false;
|
|
341
|
-
},
|
|
342
|
-
removeDeduplicationKey: () => Promise.reject(new Error('removeDeduplicationKey is not implemented — no server primitive available')),
|
|
343
|
-
removeUnprocessedChildren: async () => {
|
|
344
|
-
if (embedded) {
|
|
345
|
-
await getSharedManager().removeUnprocessedChildren(jobId(id));
|
|
346
|
-
return;
|
|
347
|
-
}
|
|
348
|
-
if (tcp)
|
|
349
|
-
await tcp.send({ cmd: 'RemoveUnprocessedChildren', id });
|
|
350
|
-
},
|
|
351
66
|
};
|
|
352
67
|
}
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
68
|
+
/** Extract user data while removing internal flow metadata and the stored job name. */
|
|
69
|
+
export function extractUserDataFromInternal(data) {
|
|
70
|
+
const result = {};
|
|
71
|
+
for (const [key, value] of Object.entries(data)) {
|
|
72
|
+
if (!key.startsWith('__') && key !== 'name')
|
|
73
|
+
result[key] = value;
|
|
357
74
|
}
|
|
358
|
-
|
|
359
|
-
return [];
|
|
360
|
-
const jobRes = await tcp.send({ cmd: 'GetJob', id });
|
|
361
|
-
const parent = jobRes.job;
|
|
362
|
-
return (parent?.childrenIds ?? []).map(String);
|
|
75
|
+
return result;
|
|
363
76
|
}
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
const
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
77
|
+
/** Create a Job object for a newly committed or fetched flow node. */
|
|
78
|
+
export function createFlowJobObject(id, name, data, queueName, factoryOptions = {}) {
|
|
79
|
+
const { callbacks, snapshot } = factoryOptions;
|
|
80
|
+
const timestamp = Date.now();
|
|
81
|
+
const runtime = {
|
|
82
|
+
id,
|
|
83
|
+
queueName,
|
|
84
|
+
callbacks,
|
|
85
|
+
embedded: callbacks?.embedded === true,
|
|
86
|
+
tcp: callbacks?.tcp ?? null,
|
|
87
|
+
getState: buildStateResolver(id, callbacks),
|
|
88
|
+
};
|
|
89
|
+
const result = {
|
|
90
|
+
id,
|
|
91
|
+
name,
|
|
92
|
+
data,
|
|
93
|
+
queueName,
|
|
94
|
+
attemptsMade: 0,
|
|
95
|
+
timestamp,
|
|
96
|
+
progress: 0,
|
|
97
|
+
delay: 0,
|
|
98
|
+
processedOn: undefined,
|
|
99
|
+
finishedOn: undefined,
|
|
100
|
+
stacktrace: null,
|
|
101
|
+
stalledCounter: 0,
|
|
102
|
+
priority: 0,
|
|
103
|
+
parentKey: undefined,
|
|
104
|
+
opts: {},
|
|
105
|
+
token: undefined,
|
|
106
|
+
processedBy: undefined,
|
|
107
|
+
deduplicationId: undefined,
|
|
108
|
+
repeatJobKey: undefined,
|
|
109
|
+
attemptsStarted: 0,
|
|
110
|
+
...buildFlowJobCoreMethods(runtime),
|
|
111
|
+
...defaultSerialization(id, name, data, queueName, timestamp),
|
|
112
|
+
...buildFlowJobMoveMethods(runtime),
|
|
113
|
+
};
|
|
114
|
+
if (!snapshot)
|
|
115
|
+
return result;
|
|
116
|
+
const options = buildJobOpts(snapshot);
|
|
117
|
+
Object.assign(result, buildJobProperties(snapshot, name), { data, opts: options });
|
|
118
|
+
const serialization = buildSerializationMethods(snapshot, id, name, options);
|
|
119
|
+
Object.assign(result, {
|
|
120
|
+
toJSON: () => ({ ...serialization.toJSON(), data }),
|
|
121
|
+
asJSON: () => ({ ...serialization.asJSON(), data: JSON.stringify(data) }),
|
|
122
|
+
});
|
|
123
|
+
return result;
|
|
393
124
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type FlowJobRuntime } from './flowJobTypes';
|
|
2
|
+
/** Lifecycle and failure-inspection methods exposed by a FlowProducer Job. */
|
|
3
|
+
export declare function buildFlowJobMoveMethods(runtime: FlowJobRuntime): {
|
|
4
|
+
moveToCompleted: (returnValue: unknown) => Promise<null>;
|
|
5
|
+
moveToFailed: (error: Error) => Promise<void>;
|
|
6
|
+
moveToWait: () => Promise<boolean>;
|
|
7
|
+
moveToDelayed: (timestamp: number) => Promise<undefined>;
|
|
8
|
+
moveToWaitingChildren: () => Promise<boolean>;
|
|
9
|
+
waitUntilFinished: (_queueEvents: unknown, ttl?: number) => Promise<unknown>;
|
|
10
|
+
discard: () => void;
|
|
11
|
+
getFailedChildrenValues: () => Promise<Record<string, string>>;
|
|
12
|
+
getIgnoredChildrenFailures: () => Promise<Record<string, string>>;
|
|
13
|
+
removeChildDependency: () => Promise<boolean>;
|
|
14
|
+
removeDeduplicationKey: () => Promise<boolean>;
|
|
15
|
+
removeUnprocessedChildren: () => Promise<undefined>;
|
|
16
|
+
};
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { jobId } from '../domain/types/job';
|
|
2
|
+
import { getSharedManager } from './manager';
|
|
3
|
+
import { buildFailCommand, failEmbeddedArgs } from './queue/failWire';
|
|
4
|
+
import { assertFlowTcpOk } from './flowJobTypes';
|
|
5
|
+
/** Lifecycle and failure-inspection methods exposed by a FlowProducer Job. */
|
|
6
|
+
export function buildFlowJobMoveMethods(runtime) {
|
|
7
|
+
const { id, embedded, tcp } = runtime;
|
|
8
|
+
return {
|
|
9
|
+
moveToCompleted: async (returnValue) => {
|
|
10
|
+
if (embedded) {
|
|
11
|
+
await getSharedManager().ack(jobId(id), returnValue);
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
if (tcp)
|
|
15
|
+
assertFlowTcpOk(await tcp.send({ cmd: 'ACK', id, result: returnValue }), 'ACK');
|
|
16
|
+
return null;
|
|
17
|
+
},
|
|
18
|
+
moveToFailed: async (error) => {
|
|
19
|
+
if (embedded) {
|
|
20
|
+
await getSharedManager().fail(jobId(id), ...failEmbeddedArgs(error));
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
if (tcp)
|
|
24
|
+
assertFlowTcpOk(await tcp.send(buildFailCommand(id, error)), 'FAIL');
|
|
25
|
+
},
|
|
26
|
+
moveToWait: async () => {
|
|
27
|
+
if (embedded)
|
|
28
|
+
return getSharedManager().moveActiveToWait(jobId(id));
|
|
29
|
+
if (!tcp)
|
|
30
|
+
return false;
|
|
31
|
+
assertFlowTcpOk(await tcp.send({ cmd: 'MoveToWait', id }), 'MoveToWait');
|
|
32
|
+
return true;
|
|
33
|
+
},
|
|
34
|
+
moveToDelayed: async (timestamp) => {
|
|
35
|
+
const delay = Math.max(0, timestamp - Date.now());
|
|
36
|
+
if (embedded)
|
|
37
|
+
return void (await getSharedManager().moveToDelayed(jobId(id), delay));
|
|
38
|
+
if (!tcp)
|
|
39
|
+
return;
|
|
40
|
+
assertFlowTcpOk(await tcp.send({ cmd: 'MoveToDelayed', id, delay }), 'MoveToDelayed');
|
|
41
|
+
},
|
|
42
|
+
moveToWaitingChildren: async () => {
|
|
43
|
+
if (embedded)
|
|
44
|
+
return getSharedManager().moveToWaitingChildren(jobId(id));
|
|
45
|
+
throw new Error('moveToWaitingChildren is not supported in TCP mode — no server command available');
|
|
46
|
+
},
|
|
47
|
+
waitUntilFinished: async (_queueEvents, ttl) => {
|
|
48
|
+
const timeout = ttl ?? 30_000;
|
|
49
|
+
if (embedded) {
|
|
50
|
+
const manager = getSharedManager();
|
|
51
|
+
const job = await manager.getJob(jobId(id));
|
|
52
|
+
if (!job)
|
|
53
|
+
throw new Error(`Job ${id} not found`);
|
|
54
|
+
if (job.completedAt)
|
|
55
|
+
return manager.getResult(jobId(id));
|
|
56
|
+
if (!(await manager.waitForJobCompletion(jobId(id), timeout))) {
|
|
57
|
+
throw new Error(`waitUntilFinished timed out after ${timeout}ms`);
|
|
58
|
+
}
|
|
59
|
+
return manager.getResult(jobId(id));
|
|
60
|
+
}
|
|
61
|
+
if (!tcp)
|
|
62
|
+
throw new Error('waitUntilFinished: no connection');
|
|
63
|
+
const response = await tcp.send({ cmd: 'WaitJob', id, timeout });
|
|
64
|
+
assertFlowTcpOk(response, 'WaitJob');
|
|
65
|
+
const typed = response;
|
|
66
|
+
if (!typed.completed)
|
|
67
|
+
throw new Error(`waitUntilFinished timed out after ${timeout}ms`);
|
|
68
|
+
return typed.result;
|
|
69
|
+
},
|
|
70
|
+
discard: () => {
|
|
71
|
+
if (embedded)
|
|
72
|
+
void getSharedManager().discard(jobId(id));
|
|
73
|
+
else if (tcp)
|
|
74
|
+
void tcp.send({ cmd: 'Discard', id });
|
|
75
|
+
},
|
|
76
|
+
getFailedChildrenValues: async () => {
|
|
77
|
+
if (embedded)
|
|
78
|
+
return getSharedManager().getFailedChildrenValues(jobId(id));
|
|
79
|
+
if (!tcp)
|
|
80
|
+
return {};
|
|
81
|
+
const response = await tcp.send({ cmd: 'GetFailedChildrenValues', id });
|
|
82
|
+
assertFlowTcpOk(response, 'GetFailedChildrenValues');
|
|
83
|
+
return response.values ?? {};
|
|
84
|
+
},
|
|
85
|
+
getIgnoredChildrenFailures: async () => {
|
|
86
|
+
if (embedded)
|
|
87
|
+
return getSharedManager().getIgnoredChildrenFailures(jobId(id));
|
|
88
|
+
if (!tcp)
|
|
89
|
+
return {};
|
|
90
|
+
const response = await tcp.send({ cmd: 'GetIgnoredChildrenFailures', id });
|
|
91
|
+
assertFlowTcpOk(response, 'GetIgnoredChildrenFailures');
|
|
92
|
+
return response.values ?? {};
|
|
93
|
+
},
|
|
94
|
+
removeChildDependency: async () => {
|
|
95
|
+
if (embedded)
|
|
96
|
+
return getSharedManager().removeChildDependency(jobId(id));
|
|
97
|
+
if (!tcp)
|
|
98
|
+
return false;
|
|
99
|
+
const response = await tcp.send({ cmd: 'RemoveChildDependency', id });
|
|
100
|
+
assertFlowTcpOk(response, 'RemoveChildDependency');
|
|
101
|
+
return response.removed ?? false;
|
|
102
|
+
},
|
|
103
|
+
removeDeduplicationKey: () => Promise.reject(new Error('removeDeduplicationKey is not implemented — no server primitive available')),
|
|
104
|
+
removeUnprocessedChildren: async () => {
|
|
105
|
+
if (embedded)
|
|
106
|
+
return void (await getSharedManager().removeUnprocessedChildren(jobId(id)));
|
|
107
|
+
if (!tcp)
|
|
108
|
+
return;
|
|
109
|
+
assertFlowTcpOk(await tcp.send({ cmd: 'RemoveUnprocessedChildren', id }), 'RemoveUnprocessedChildren');
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { JobStateType } from './types';
|
|
2
|
+
import type { TcpConnectionPool } from './tcpPool';
|
|
3
|
+
/** Callbacks used to wire FlowProducer Job mutations to the selected transport. */
|
|
4
|
+
export interface FlowJobCallbacks {
|
|
5
|
+
embedded?: boolean;
|
|
6
|
+
tcp?: TcpConnectionPool | null;
|
|
7
|
+
updateData?: (id: string, data: unknown) => Promise<void>;
|
|
8
|
+
updateProgress?: (id: string, progress: number | object) => Promise<void>;
|
|
9
|
+
log?: (id: string, message: string) => Promise<void>;
|
|
10
|
+
promote?: (id: string) => Promise<void>;
|
|
11
|
+
remove?: (id: string) => Promise<void>;
|
|
12
|
+
changePriority?: (id: string, opts: {
|
|
13
|
+
priority: number;
|
|
14
|
+
lifo?: boolean;
|
|
15
|
+
}) => Promise<void>;
|
|
16
|
+
changeDelay?: (id: string, delay: number) => Promise<void>;
|
|
17
|
+
clearLogs?: (id: string, keepLogs?: number) => Promise<void>;
|
|
18
|
+
retry?: (id: string) => Promise<void>;
|
|
19
|
+
getState?: (id: string) => Promise<string>;
|
|
20
|
+
}
|
|
21
|
+
export interface FlowJobRuntime {
|
|
22
|
+
id: string;
|
|
23
|
+
queueName: string;
|
|
24
|
+
callbacks?: FlowJobCallbacks;
|
|
25
|
+
embedded: boolean;
|
|
26
|
+
tcp: TcpConnectionPool | null;
|
|
27
|
+
getState: () => Promise<JobStateType>;
|
|
28
|
+
}
|
|
29
|
+
export declare function assertFlowTcpOk(response: Record<string, unknown>, operation: string): void;
|