duron 0.3.0-beta.10 → 0.3.0-beta.12
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/action-job.d.ts +33 -2
- package/dist/action-job.d.ts.map +1 -1
- package/dist/action-job.js +87 -22
- package/dist/action-manager.d.ts +44 -2
- package/dist/action-manager.d.ts.map +1 -1
- package/dist/action-manager.js +64 -3
- package/dist/action.d.ts +146 -3
- package/dist/action.d.ts.map +1 -1
- package/dist/action.js +131 -0
- package/dist/adapters/adapter.d.ts +365 -8
- package/dist/adapters/adapter.d.ts.map +1 -1
- package/dist/adapters/adapter.js +221 -15
- package/dist/adapters/postgres/base.d.ts +174 -5
- package/dist/adapters/postgres/base.d.ts.map +1 -1
- package/dist/adapters/postgres/base.js +349 -66
- package/dist/adapters/postgres/pglite.d.ts +37 -0
- package/dist/adapters/postgres/pglite.d.ts.map +1 -1
- package/dist/adapters/postgres/pglite.js +38 -0
- package/dist/adapters/postgres/postgres.d.ts +35 -0
- package/dist/adapters/postgres/postgres.d.ts.map +1 -1
- package/dist/adapters/postgres/postgres.js +42 -0
- package/dist/adapters/postgres/schema.d.ts +118 -35
- package/dist/adapters/postgres/schema.d.ts.map +1 -1
- package/dist/adapters/postgres/schema.default.d.ts +119 -36
- package/dist/adapters/postgres/schema.default.d.ts.map +1 -1
- package/dist/adapters/postgres/schema.default.js +2 -2
- package/dist/adapters/postgres/schema.js +55 -22
- package/dist/adapters/schemas.d.ts +107 -80
- package/dist/adapters/schemas.d.ts.map +1 -1
- package/dist/adapters/schemas.js +131 -26
- package/dist/client.d.ts +315 -9
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +391 -21
- package/dist/constants.js +6 -0
- package/dist/errors.d.ts +119 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +111 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/server.d.ts +91 -37
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +81 -25
- package/dist/step-manager.d.ts +111 -4
- package/dist/step-manager.d.ts.map +1 -1
- package/dist/step-manager.js +340 -69
- package/dist/telemetry/adapter.d.ts +322 -0
- package/dist/telemetry/adapter.d.ts.map +1 -1
- package/dist/telemetry/adapter.js +145 -0
- package/dist/telemetry/index.d.ts +1 -4
- package/dist/telemetry/index.d.ts.map +1 -1
- package/dist/telemetry/index.js +2 -4
- package/dist/telemetry/local-span-exporter.d.ts +56 -0
- package/dist/telemetry/local-span-exporter.d.ts.map +1 -0
- package/dist/telemetry/local-span-exporter.js +118 -0
- package/dist/telemetry/local.d.ts +48 -0
- package/dist/telemetry/local.d.ts.map +1 -1
- package/dist/telemetry/local.js +102 -0
- package/dist/telemetry/noop.d.ts +10 -0
- package/dist/telemetry/noop.d.ts.map +1 -1
- package/dist/telemetry/noop.js +43 -0
- package/dist/telemetry/opentelemetry.d.ts +23 -0
- package/dist/telemetry/opentelemetry.d.ts.map +1 -1
- package/dist/telemetry/opentelemetry.js +39 -0
- package/dist/utils/p-retry.d.ts +5 -0
- package/dist/utils/p-retry.d.ts.map +1 -1
- package/dist/utils/p-retry.js +8 -0
- package/dist/utils/wait-for-abort.d.ts +1 -0
- package/dist/utils/wait-for-abort.d.ts.map +1 -1
- package/dist/utils/wait-for-abort.js +1 -0
- package/migrations/postgres/{20260119153838_flimsy_thor_girl → 20260120154151_mean_magdalene}/migration.sql +27 -19
- package/migrations/postgres/{20260119153838_flimsy_thor_girl → 20260120154151_mean_magdalene}/snapshot.json +172 -65
- package/package.json +7 -2
- package/src/action-job.ts +32 -28
- package/src/action-manager.ts +5 -5
- package/src/action.ts +7 -7
- package/src/adapters/adapter.ts +54 -54
- package/src/adapters/postgres/base.ts +140 -77
- package/src/adapters/postgres/schema.default.ts +2 -2
- package/src/adapters/postgres/schema.ts +47 -23
- package/src/adapters/schemas.ts +83 -36
- package/src/client.ts +195 -42
- package/src/index.ts +1 -0
- package/src/server.ts +37 -37
- package/src/step-manager.ts +170 -86
- package/src/telemetry/index.ts +2 -20
- package/src/telemetry/local-span-exporter.ts +148 -0
- package/src/telemetry/adapter.ts +0 -642
- package/src/telemetry/local.ts +0 -429
- package/src/telemetry/noop.ts +0 -141
- package/src/telemetry/opentelemetry.ts +0 -453
package/dist/action-job.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import { type Tracer } from '@opentelemetry/api';
|
|
1
2
|
import type { Logger } from 'pino';
|
|
2
3
|
import type { Action } from './action.js';
|
|
3
4
|
import type { Adapter } from './adapters/adapter.js';
|
|
4
|
-
import type { TelemetryAdapter } from './telemetry/adapter.js';
|
|
5
5
|
export interface ActionJobOptions<TAction extends Action<any, any, any>> {
|
|
6
6
|
job: {
|
|
7
7
|
id: string;
|
|
@@ -12,15 +12,46 @@ export interface ActionJobOptions<TAction extends Action<any, any, any>> {
|
|
|
12
12
|
};
|
|
13
13
|
action: TAction;
|
|
14
14
|
database: Adapter;
|
|
15
|
-
|
|
15
|
+
tracer: Tracer;
|
|
16
16
|
variables: Record<string, unknown>;
|
|
17
17
|
logger: Logger;
|
|
18
18
|
}
|
|
19
|
+
/**
|
|
20
|
+
* ActionJob represents a single job execution for an action.
|
|
21
|
+
* Manages the execution lifecycle, timeout handling, and cancellation.
|
|
22
|
+
*
|
|
23
|
+
* @template TAction - The action type being executed
|
|
24
|
+
*/
|
|
19
25
|
export declare class ActionJob<TAction extends Action<any, any, any>> {
|
|
20
26
|
#private;
|
|
27
|
+
/**
|
|
28
|
+
* Create a new ActionJob instance.
|
|
29
|
+
*
|
|
30
|
+
* @param options - Configuration options for the action job
|
|
31
|
+
*/
|
|
21
32
|
constructor(options: ActionJobOptions<TAction>);
|
|
33
|
+
/**
|
|
34
|
+
* Execute the action job.
|
|
35
|
+
* Creates the action context, sets up timeout, executes the handler,
|
|
36
|
+
* validates output, and marks the job as completed or failed.
|
|
37
|
+
*
|
|
38
|
+
* @returns Promise resolving to the action result
|
|
39
|
+
* @throws ActionTimeoutError if the job times out
|
|
40
|
+
* @throws ActionCancelError if the job is cancelled
|
|
41
|
+
* @throws Error if the job fails or output validation fails
|
|
42
|
+
*/
|
|
22
43
|
execute(): Promise<any>;
|
|
44
|
+
/**
|
|
45
|
+
* Wait for the job execution to complete.
|
|
46
|
+
* Returns a promise that resolves when the job finishes (successfully or with error).
|
|
47
|
+
*
|
|
48
|
+
* @returns Promise that resolves when the job is done
|
|
49
|
+
*/
|
|
23
50
|
waitForDone(): Promise<void>;
|
|
51
|
+
/**
|
|
52
|
+
* Cancel the job execution.
|
|
53
|
+
* Clears the timeout and aborts the action handler.
|
|
54
|
+
*/
|
|
24
55
|
cancel(): void;
|
|
25
56
|
}
|
|
26
57
|
//# sourceMappingURL=action-job.d.ts.map
|
package/dist/action-job.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"action-job.d.ts","sourceRoot":"","sources":["../src/action-job.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"action-job.d.ts","sourceRoot":"","sources":["../src/action-job.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgD,KAAK,MAAM,EAAS,MAAM,oBAAoB,CAAA;AACrG,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AAElC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAA;AAKpD,MAAM,WAAW,gBAAgB,CAAC,OAAO,SAAS,MAAM,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC;IACrE,GAAG,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,GAAG,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAA;IACxF,MAAM,EAAE,OAAO,CAAA;IACf,QAAQ,EAAE,OAAO,CAAA;IACjB,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAClC,MAAM,EAAE,MAAM,CAAA;CACf;AAED;;;;;GAKG;AACH,qBAAa,SAAS,CAAC,OAAO,SAAS,MAAM,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC;;IAkB1D;;;;OAIG;gBACS,OAAO,EAAE,gBAAgB,CAAC,OAAO,CAAC;IA4B9C;;;;;;;;;OASG;IACG,OAAO;IAgIb;;;;;OAKG;IACH,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IAI5B;;;OAGG;IACH,MAAM;CAmBP"}
|
package/dist/action-job.js
CHANGED
|
@@ -1,11 +1,18 @@
|
|
|
1
|
+
import { context, SpanKind, SpanStatusCode, trace } from '@opentelemetry/api';
|
|
1
2
|
import { ActionCancelError, ActionTimeoutError, isCancelError, isTimeoutError, serializeError } from './errors.js';
|
|
2
3
|
import { StepManager } from './step-manager.js';
|
|
3
4
|
import waitForAbort from './utils/wait-for-abort.js';
|
|
5
|
+
/**
|
|
6
|
+
* ActionJob represents a single job execution for an action.
|
|
7
|
+
* Manages the execution lifecycle, timeout handling, and cancellation.
|
|
8
|
+
*
|
|
9
|
+
* @template TAction - The action type being executed
|
|
10
|
+
*/
|
|
4
11
|
export class ActionJob {
|
|
5
12
|
#job;
|
|
6
13
|
#action;
|
|
7
14
|
#database;
|
|
8
|
-
#
|
|
15
|
+
#tracer;
|
|
9
16
|
#variables;
|
|
10
17
|
#logger;
|
|
11
18
|
#stepManager;
|
|
@@ -13,20 +20,29 @@ export class ActionJob {
|
|
|
13
20
|
#timeoutId = null;
|
|
14
21
|
#done;
|
|
15
22
|
#resolve = null;
|
|
16
|
-
#jobSpan
|
|
23
|
+
#jobSpan;
|
|
24
|
+
// ============================================================================
|
|
25
|
+
// Constructor
|
|
26
|
+
// ============================================================================
|
|
27
|
+
/**
|
|
28
|
+
* Create a new ActionJob instance.
|
|
29
|
+
*
|
|
30
|
+
* @param options - Configuration options for the action job
|
|
31
|
+
*/
|
|
17
32
|
constructor(options) {
|
|
18
33
|
this.#job = options.job;
|
|
19
34
|
this.#action = options.action;
|
|
20
35
|
this.#database = options.database;
|
|
21
|
-
this.#
|
|
36
|
+
this.#tracer = options.tracer;
|
|
22
37
|
this.#variables = options.variables;
|
|
23
38
|
this.#logger = options.logger;
|
|
24
39
|
this.#abortController = new AbortController();
|
|
40
|
+
// Create StepManager for this job
|
|
25
41
|
this.#stepManager = new StepManager({
|
|
26
42
|
jobId: options.job.id,
|
|
27
43
|
actionName: options.job.actionName,
|
|
28
44
|
adapter: options.database,
|
|
29
|
-
|
|
45
|
+
tracer: options.tracer,
|
|
30
46
|
logger: options.logger,
|
|
31
47
|
concurrencyLimit: options.action.steps.concurrency,
|
|
32
48
|
});
|
|
@@ -34,31 +50,52 @@ export class ActionJob {
|
|
|
34
50
|
this.#resolve = resolve;
|
|
35
51
|
});
|
|
36
52
|
}
|
|
53
|
+
// ============================================================================
|
|
54
|
+
// Public API Methods
|
|
55
|
+
// ============================================================================
|
|
56
|
+
/**
|
|
57
|
+
* Execute the action job.
|
|
58
|
+
* Creates the action context, sets up timeout, executes the handler,
|
|
59
|
+
* validates output, and marks the job as completed or failed.
|
|
60
|
+
*
|
|
61
|
+
* @returns Promise resolving to the action result
|
|
62
|
+
* @throws ActionTimeoutError if the job times out
|
|
63
|
+
* @throws ActionCancelError if the job is cancelled
|
|
64
|
+
* @throws Error if the job fails or output validation fails
|
|
65
|
+
*/
|
|
37
66
|
async execute() {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
67
|
+
// Start job span - uses no-op tracer if no SDK is configured
|
|
68
|
+
this.#jobSpan = this.#tracer.startSpan(`job:${this.#action.name}`, {
|
|
69
|
+
kind: SpanKind.INTERNAL,
|
|
70
|
+
attributes: {
|
|
71
|
+
'duron.job.id': this.#job.id,
|
|
72
|
+
'duron.action.name': this.#action.name,
|
|
73
|
+
'duron.group.key': this.#job.groupKey,
|
|
74
|
+
},
|
|
43
75
|
});
|
|
76
|
+
// Set the job span on the step manager
|
|
44
77
|
this.#stepManager.setJobSpan(this.#jobSpan);
|
|
45
78
|
try {
|
|
79
|
+
// Create a child logger for this job
|
|
46
80
|
const jobLogger = this.#logger.child({
|
|
47
81
|
jobId: this.#job.id,
|
|
48
82
|
actionName: this.#action.name,
|
|
49
83
|
});
|
|
50
|
-
|
|
51
|
-
const ctx = this.#stepManager.createActionContext(this.#job, this.#action, this.#variables, this.#abortController.signal, jobLogger
|
|
84
|
+
// Create action context with step manager
|
|
85
|
+
const ctx = this.#stepManager.createActionContext(this.#job, this.#action, this.#variables, this.#abortController.signal, jobLogger);
|
|
52
86
|
this.#timeoutId = setTimeout(() => {
|
|
53
87
|
const timeoutError = new ActionTimeoutError(this.#action.name, this.#job.id, this.#job.timeoutMs);
|
|
54
88
|
this.#abortController.abort(timeoutError);
|
|
55
89
|
}, this.#job.timeoutMs);
|
|
56
90
|
this.#timeoutId?.unref?.();
|
|
91
|
+
// Execute handler with timeout - race between handler and abort signal
|
|
57
92
|
const abortWaiter = waitForAbort(this.#abortController.signal);
|
|
58
93
|
let result = null;
|
|
94
|
+
// Execute handler within the job span context so that child spans inherit the trace
|
|
95
|
+
const spanContext = trace.setSpan(context.active(), this.#jobSpan);
|
|
59
96
|
await Promise.race([
|
|
60
|
-
|
|
61
|
-
.handler(ctx)
|
|
97
|
+
context
|
|
98
|
+
.with(spanContext, () => this.#action.handler(ctx))
|
|
62
99
|
.then((res) => {
|
|
63
100
|
if (res !== undefined) {
|
|
64
101
|
result = res;
|
|
@@ -69,42 +106,54 @@ export class ActionJob {
|
|
|
69
106
|
}),
|
|
70
107
|
abortWaiter.promise,
|
|
71
108
|
]);
|
|
109
|
+
// Validate output if schema is provided
|
|
72
110
|
if (this.#action.output) {
|
|
73
111
|
result = this.#action.output.parse(result, {
|
|
74
112
|
error: () => 'Error parsing action output',
|
|
75
113
|
reportInput: true,
|
|
76
114
|
});
|
|
77
115
|
}
|
|
116
|
+
// Complete job
|
|
78
117
|
const completed = await this.#database.completeJob({ jobId: this.#job.id, output: result });
|
|
79
118
|
if (!completed) {
|
|
80
119
|
throw new Error('Job not completed');
|
|
81
120
|
}
|
|
121
|
+
// Log action completion
|
|
82
122
|
this.#logger.debug({ jobId: this.#job.id, actionName: this.#action.name }, '[ActionJob] Action finished executing');
|
|
83
|
-
|
|
123
|
+
// End job span successfully
|
|
124
|
+
this.#jobSpan.setStatus({ code: SpanStatusCode.OK });
|
|
125
|
+
this.#jobSpan.end();
|
|
84
126
|
return result;
|
|
85
127
|
}
|
|
86
128
|
catch (error) {
|
|
129
|
+
// Abort all running steps when an error occurs
|
|
130
|
+
// This ensures cascading failure and stops any steps still running
|
|
87
131
|
if (!this.#abortController.signal.aborted) {
|
|
88
132
|
this.#abortController.abort(error);
|
|
89
133
|
}
|
|
134
|
+
// Wait for step manager to drain (all steps to settle)
|
|
90
135
|
await this.#stepManager.drain();
|
|
91
136
|
if (isCancelError(error) ||
|
|
92
137
|
(error instanceof Error && error.name === 'AbortError' && isCancelError(error.cause))) {
|
|
93
138
|
this.#logger.warn({ jobId: this.#job.id, actionName: this.#action.name }, '[ActionJob] Job cancelled');
|
|
94
139
|
await this.#database.cancelJob({ jobId: this.#job.id });
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
140
|
+
// End job span as cancelled
|
|
141
|
+
this.#jobSpan.setStatus({ code: SpanStatusCode.ERROR, message: 'Job cancelled' });
|
|
142
|
+
this.#jobSpan.end();
|
|
98
143
|
return;
|
|
99
144
|
}
|
|
100
|
-
const message = isTimeoutError(error)
|
|
101
|
-
? '[ActionJob] Job timed out'
|
|
102
|
-
: '[ActionJob] Job failed';
|
|
145
|
+
const message = isTimeoutError(error) ? '[ActionJob] Job timed out' : '[ActionJob] Job failed';
|
|
103
146
|
this.#logger.error({ jobId: this.#job.id, actionName: this.#action.name }, message);
|
|
104
147
|
await this.#database.failJob({ jobId: this.#job.id, error: serializeError(error) });
|
|
105
|
-
|
|
106
|
-
|
|
148
|
+
// End job span with error
|
|
149
|
+
this.#jobSpan.setStatus({
|
|
150
|
+
code: SpanStatusCode.ERROR,
|
|
151
|
+
message: error instanceof Error ? error.message : String(error),
|
|
152
|
+
});
|
|
153
|
+
if (error instanceof Error) {
|
|
154
|
+
this.#jobSpan.recordException(error);
|
|
107
155
|
}
|
|
156
|
+
this.#jobSpan.end();
|
|
108
157
|
throw error;
|
|
109
158
|
}
|
|
110
159
|
finally {
|
|
@@ -112,14 +161,30 @@ export class ActionJob {
|
|
|
112
161
|
this.#resolve?.();
|
|
113
162
|
}
|
|
114
163
|
}
|
|
164
|
+
/**
|
|
165
|
+
* Wait for the job execution to complete.
|
|
166
|
+
* Returns a promise that resolves when the job finishes (successfully or with error).
|
|
167
|
+
*
|
|
168
|
+
* @returns Promise that resolves when the job is done
|
|
169
|
+
*/
|
|
115
170
|
waitForDone() {
|
|
116
171
|
return this.#done;
|
|
117
172
|
}
|
|
173
|
+
/**
|
|
174
|
+
* Cancel the job execution.
|
|
175
|
+
* Clears the timeout and aborts the action handler.
|
|
176
|
+
*/
|
|
118
177
|
cancel() {
|
|
119
178
|
this.#clear();
|
|
120
179
|
const cancelError = new ActionCancelError(this.#action.name, this.#job.id);
|
|
121
180
|
this.#abortController.abort(cancelError);
|
|
122
181
|
}
|
|
182
|
+
// ============================================================================
|
|
183
|
+
// Private Methods
|
|
184
|
+
// ============================================================================
|
|
185
|
+
/**
|
|
186
|
+
* Clear the timeout timer.
|
|
187
|
+
*/
|
|
123
188
|
#clear() {
|
|
124
189
|
if (this.#timeoutId) {
|
|
125
190
|
clearTimeout(this.#timeoutId);
|
package/dist/action-manager.d.ts
CHANGED
|
@@ -1,23 +1,65 @@
|
|
|
1
|
+
import type { Tracer } from '@opentelemetry/api';
|
|
1
2
|
import type { Logger } from 'pino';
|
|
2
3
|
import type { Action } from './action.js';
|
|
3
4
|
import type { Adapter, Job } from './adapters/adapter.js';
|
|
4
|
-
import type { TelemetryAdapter } from './telemetry/adapter.js';
|
|
5
5
|
export interface ActionManagerOptions<TAction extends Action<any, any, any>> {
|
|
6
6
|
action: TAction;
|
|
7
7
|
database: Adapter;
|
|
8
|
-
|
|
8
|
+
tracer: Tracer;
|
|
9
9
|
variables: Record<string, unknown>;
|
|
10
10
|
logger: Logger;
|
|
11
11
|
concurrencyLimit: number;
|
|
12
12
|
}
|
|
13
|
+
/**
|
|
14
|
+
* ActionManager manages the execution of jobs for a specific action.
|
|
15
|
+
* Uses a fastq queue to control concurrency and process jobs.
|
|
16
|
+
*
|
|
17
|
+
* @template TAction - The action type being managed
|
|
18
|
+
*/
|
|
13
19
|
export declare class ActionManager<TAction extends Action<any, any, any>> {
|
|
14
20
|
#private;
|
|
21
|
+
/**
|
|
22
|
+
* Create a new ActionManager instance.
|
|
23
|
+
*
|
|
24
|
+
* @param options - Configuration options for the action manager
|
|
25
|
+
*/
|
|
15
26
|
constructor(options: ActionManagerOptions<TAction>);
|
|
27
|
+
/**
|
|
28
|
+
* Queue a job for execution.
|
|
29
|
+
*
|
|
30
|
+
* @param job - The job to queue
|
|
31
|
+
* @returns Promise that resolves when the job is queued
|
|
32
|
+
*/
|
|
16
33
|
push(job: Job): Promise<void>;
|
|
34
|
+
/**
|
|
35
|
+
* Cancel a specific job by ID.
|
|
36
|
+
*
|
|
37
|
+
* @param jobId - The ID of the job to cancel
|
|
38
|
+
* @returns If the manager has the job, it will be cancelled and true will be returned. Otherwise, false will be returned.
|
|
39
|
+
*/
|
|
17
40
|
cancelJob(jobId: string): boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Cancel all active jobs.
|
|
43
|
+
*/
|
|
18
44
|
abortAll(): void;
|
|
45
|
+
/**
|
|
46
|
+
* Check if the queue is idle (no jobs being processed).
|
|
47
|
+
*
|
|
48
|
+
* @returns Promise resolving to `true` if idle, `false` otherwise
|
|
49
|
+
*/
|
|
19
50
|
idle(): Promise<boolean>;
|
|
51
|
+
/**
|
|
52
|
+
* Wait for the queue to drain (all jobs completed).
|
|
53
|
+
*
|
|
54
|
+
* @returns Promise that resolves when the queue is drained
|
|
55
|
+
*/
|
|
20
56
|
drain(): Promise<void>;
|
|
57
|
+
/**
|
|
58
|
+
* Stop the action manager.
|
|
59
|
+
* Aborts all active jobs and waits for the queue to drain.
|
|
60
|
+
*
|
|
61
|
+
* @returns Promise that resolves when the action manager is stopped
|
|
62
|
+
*/
|
|
21
63
|
stop(): Promise<void>;
|
|
22
64
|
}
|
|
23
65
|
//# sourceMappingURL=action-manager.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"action-manager.d.ts","sourceRoot":"","sources":["../src/action-manager.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"action-manager.d.ts","sourceRoot":"","sources":["../src/action-manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAA;AAEhD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AAElC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEzC,OAAO,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,uBAAuB,CAAA;AAEzD,MAAM,WAAW,oBAAoB,CAAC,OAAO,SAAS,MAAM,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC;IACzE,MAAM,EAAE,OAAO,CAAA;IACf,QAAQ,EAAE,OAAO,CAAA;IACjB,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAClC,MAAM,EAAE,MAAM,CAAA;IACd,gBAAgB,EAAE,MAAM,CAAA;CACzB;AAED;;;;;GAKG;AACH,qBAAa,aAAa,CAAC,OAAO,SAAS,MAAM,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC;;IAe9D;;;;OAIG;gBACS,OAAO,EAAE,oBAAoB,CAAC,OAAO,CAAC;IAsBlD;;;;;OAKG;IACG,IAAI,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;IAInC;;;;;OAKG;IACH,SAAS,CAAC,KAAK,EAAE,MAAM;IASvB;;OAEG;IACH,QAAQ,IAAI,IAAI;IAMhB;;;;OAIG;IACG,IAAI,IAAI,OAAO,CAAC,OAAO,CAAC;IAI9B;;;;OAIG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAI5B;;;;;OAKG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CA+C5B"}
|
package/dist/action-manager.js
CHANGED
|
@@ -1,22 +1,37 @@
|
|
|
1
1
|
import fastq from 'fastq';
|
|
2
2
|
import { ActionJob } from './action-job.js';
|
|
3
|
+
/**
|
|
4
|
+
* ActionManager manages the execution of jobs for a specific action.
|
|
5
|
+
* Uses a fastq queue to control concurrency and process jobs.
|
|
6
|
+
*
|
|
7
|
+
* @template TAction - The action type being managed
|
|
8
|
+
*/
|
|
3
9
|
export class ActionManager {
|
|
4
10
|
#action;
|
|
5
11
|
#database;
|
|
6
|
-
#
|
|
12
|
+
#tracer;
|
|
7
13
|
#variables;
|
|
8
14
|
#logger;
|
|
9
15
|
#queue;
|
|
10
16
|
#concurrencyLimit;
|
|
11
17
|
#activeJobs = new Map();
|
|
12
18
|
#stopped = false;
|
|
19
|
+
// ============================================================================
|
|
20
|
+
// Constructor
|
|
21
|
+
// ============================================================================
|
|
22
|
+
/**
|
|
23
|
+
* Create a new ActionManager instance.
|
|
24
|
+
*
|
|
25
|
+
* @param options - Configuration options for the action manager
|
|
26
|
+
*/
|
|
13
27
|
constructor(options) {
|
|
14
28
|
this.#action = options.action;
|
|
15
29
|
this.#database = options.database;
|
|
16
|
-
this.#
|
|
30
|
+
this.#tracer = options.tracer;
|
|
17
31
|
this.#variables = options.variables;
|
|
18
32
|
this.#logger = options.logger;
|
|
19
33
|
this.#concurrencyLimit = options.concurrencyLimit;
|
|
34
|
+
// Create fastq queue with action concurrency limit
|
|
20
35
|
this.#queue = fastq.promise(async (job) => {
|
|
21
36
|
if (this.#stopped) {
|
|
22
37
|
return;
|
|
@@ -24,9 +39,24 @@ export class ActionManager {
|
|
|
24
39
|
await this.#executeJob(job);
|
|
25
40
|
}, this.#concurrencyLimit);
|
|
26
41
|
}
|
|
42
|
+
// ============================================================================
|
|
43
|
+
// Public API Methods
|
|
44
|
+
// ============================================================================
|
|
45
|
+
/**
|
|
46
|
+
* Queue a job for execution.
|
|
47
|
+
*
|
|
48
|
+
* @param job - The job to queue
|
|
49
|
+
* @returns Promise that resolves when the job is queued
|
|
50
|
+
*/
|
|
27
51
|
async push(job) {
|
|
28
52
|
return this.#queue.push(job);
|
|
29
53
|
}
|
|
54
|
+
/**
|
|
55
|
+
* Cancel a specific job by ID.
|
|
56
|
+
*
|
|
57
|
+
* @param jobId - The ID of the job to cancel
|
|
58
|
+
* @returns If the manager has the job, it will be cancelled and true will be returned. Otherwise, false will be returned.
|
|
59
|
+
*/
|
|
30
60
|
cancelJob(jobId) {
|
|
31
61
|
const actionJob = this.#activeJobs.get(jobId);
|
|
32
62
|
if (actionJob) {
|
|
@@ -35,17 +65,36 @@ export class ActionManager {
|
|
|
35
65
|
}
|
|
36
66
|
return false;
|
|
37
67
|
}
|
|
68
|
+
/**
|
|
69
|
+
* Cancel all active jobs.
|
|
70
|
+
*/
|
|
38
71
|
abortAll() {
|
|
39
72
|
for (const actionJob of this.#activeJobs.values()) {
|
|
40
73
|
actionJob.cancel();
|
|
41
74
|
}
|
|
42
75
|
}
|
|
76
|
+
/**
|
|
77
|
+
* Check if the queue is idle (no jobs being processed).
|
|
78
|
+
*
|
|
79
|
+
* @returns Promise resolving to `true` if idle, `false` otherwise
|
|
80
|
+
*/
|
|
43
81
|
async idle() {
|
|
44
82
|
return this.#queue.idle();
|
|
45
83
|
}
|
|
84
|
+
/**
|
|
85
|
+
* Wait for the queue to drain (all jobs completed).
|
|
86
|
+
*
|
|
87
|
+
* @returns Promise that resolves when the queue is drained
|
|
88
|
+
*/
|
|
46
89
|
async drain() {
|
|
47
90
|
return this.#queue.drain();
|
|
48
91
|
}
|
|
92
|
+
/**
|
|
93
|
+
* Stop the action manager.
|
|
94
|
+
* Aborts all active jobs and waits for the queue to drain.
|
|
95
|
+
*
|
|
96
|
+
* @returns Promise that resolves when the action manager is stopped
|
|
97
|
+
*/
|
|
49
98
|
async stop() {
|
|
50
99
|
if (this.#stopped) {
|
|
51
100
|
return;
|
|
@@ -55,7 +104,16 @@ export class ActionManager {
|
|
|
55
104
|
await this.#queue.killAndDrain();
|
|
56
105
|
await Promise.all(Array.from(this.#activeJobs.values()).map((actionJob) => actionJob.waitForDone()));
|
|
57
106
|
}
|
|
107
|
+
// ============================================================================
|
|
108
|
+
// Private Methods
|
|
109
|
+
// ============================================================================
|
|
110
|
+
/**
|
|
111
|
+
* Execute a job by creating an ActionJob and running it.
|
|
112
|
+
*
|
|
113
|
+
* @param job - The job to execute
|
|
114
|
+
*/
|
|
58
115
|
async #executeJob(job) {
|
|
116
|
+
// Create ActionJob for this job
|
|
59
117
|
const actionJob = new ActionJob({
|
|
60
118
|
job: {
|
|
61
119
|
id: job.id,
|
|
@@ -66,15 +124,18 @@ export class ActionManager {
|
|
|
66
124
|
},
|
|
67
125
|
action: this.#action,
|
|
68
126
|
database: this.#database,
|
|
69
|
-
|
|
127
|
+
tracer: this.#tracer,
|
|
70
128
|
variables: this.#variables,
|
|
71
129
|
logger: this.#logger,
|
|
72
130
|
});
|
|
73
131
|
this.#activeJobs.set(job.id, actionJob);
|
|
74
132
|
try {
|
|
133
|
+
// Execute the job - all error handling is done inside ActionJob.execute()
|
|
75
134
|
await actionJob.execute();
|
|
76
135
|
}
|
|
77
136
|
finally {
|
|
137
|
+
// Always cleanup, even if the job failed or was cancelled
|
|
138
|
+
// Errors are already handled in ActionJob.execute() (logging, failing job, etc.)
|
|
78
139
|
this.#activeJobs.delete(job.id);
|
|
79
140
|
}
|
|
80
141
|
}
|