duron 0.3.0-beta.8 → 0.3.0-beta.9
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.map +1 -1
- package/dist/action-job.js +7 -5
- package/dist/errors.d.ts +26 -6
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +37 -10
- package/dist/step-manager.d.ts.map +1 -1
- package/dist/step-manager.js +9 -1
- package/package.json +1 -1
- package/src/action-job.ts +12 -5
- package/src/errors.ts +58 -19
- package/src/step-manager.ts +12 -0
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,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AAElC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAA;AAGpD,OAAO,KAAK,EAAQ,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AAGpE,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,SAAS,EAAE,gBAAgB,CAAA;IAC3B,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAClC,MAAM,EAAE,MAAM,CAAA;CACf;AAQD,qBAAa,SAAS,CAAC,OAAO,SAAS,MAAM,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC;;gBAuB9C,OAAO,EAAE,gBAAgB,CAAC,OAAO,CAAC;IAsCxC,OAAO;
|
|
1
|
+
{"version":3,"file":"action-job.d.ts","sourceRoot":"","sources":["../src/action-job.ts"],"names":[],"mappings":"AAAA,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;AAGpD,OAAO,KAAK,EAAQ,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AAGpE,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,SAAS,EAAE,gBAAgB,CAAA;IAC3B,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAClC,MAAM,EAAE,MAAM,CAAA;CACf;AAQD,qBAAa,SAAS,CAAC,OAAO,SAAS,MAAM,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC;;gBAuB9C,OAAO,EAAE,gBAAgB,CAAC,OAAO,CAAC;IAsCxC,OAAO;IAkIb,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IAQ5B,MAAM;CAmBP"}
|
package/dist/action-job.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ActionCancelError, ActionTimeoutError, isCancelError,
|
|
1
|
+
import { ActionCancelError, ActionTimeoutError, isCancelError, isTimeoutError, serializeError } from './errors.js';
|
|
2
2
|
import { StepManager } from './step-manager.js';
|
|
3
3
|
import waitForAbort from './utils/wait-for-abort.js';
|
|
4
4
|
export class ActionJob {
|
|
@@ -84,6 +84,10 @@ export class ActionJob {
|
|
|
84
84
|
return result;
|
|
85
85
|
}
|
|
86
86
|
catch (error) {
|
|
87
|
+
if (!this.#abortController.signal.aborted) {
|
|
88
|
+
this.#abortController.abort(error);
|
|
89
|
+
}
|
|
90
|
+
await this.#stepManager.drain();
|
|
87
91
|
if (isCancelError(error) ||
|
|
88
92
|
(error instanceof Error && error.name === 'AbortError' && isCancelError(error.cause))) {
|
|
89
93
|
this.#logger.warn({ jobId: this.#job.id, actionName: this.#action.name }, '[ActionJob] Job cancelled');
|
|
@@ -93,11 +97,9 @@ export class ActionJob {
|
|
|
93
97
|
}
|
|
94
98
|
return;
|
|
95
99
|
}
|
|
96
|
-
const message = error
|
|
100
|
+
const message = isTimeoutError(error)
|
|
97
101
|
? '[ActionJob] Job timed out'
|
|
98
|
-
:
|
|
99
|
-
? '[ActionJob] Step timed out'
|
|
100
|
-
: '[ActionJob] Job failed';
|
|
102
|
+
: '[ActionJob] Job failed';
|
|
101
103
|
this.#logger.error({ jobId: this.#job.id, actionName: this.#action.name }, message);
|
|
102
104
|
await this.#database.failJob({ jobId: this.#job.id, error: serializeError(error) });
|
|
103
105
|
if (this.#jobSpan) {
|
package/dist/errors.d.ts
CHANGED
|
@@ -1,30 +1,53 @@
|
|
|
1
|
+
export declare const ERROR_CODES: {
|
|
2
|
+
readonly DURON_ERROR: "DURON_ERROR";
|
|
3
|
+
readonly STEP_ALREADY_EXECUTED: "STEP_ALREADY_EXECUTED";
|
|
4
|
+
readonly NON_RETRIABLE: "NON_RETRIABLE";
|
|
5
|
+
readonly ACTION_TIMEOUT: "ACTION_TIMEOUT";
|
|
6
|
+
readonly STEP_TIMEOUT: "STEP_TIMEOUT";
|
|
7
|
+
readonly ACTION_CANCEL: "ACTION_CANCEL";
|
|
8
|
+
readonly UNHANDLED_CHILD_STEPS: "UNHANDLED_CHILD_STEPS";
|
|
9
|
+
};
|
|
10
|
+
export type ErrorCode = (typeof ERROR_CODES)[keyof typeof ERROR_CODES];
|
|
1
11
|
export declare abstract class DuronError extends Error {
|
|
12
|
+
readonly code: ErrorCode;
|
|
13
|
+
readonly nonRetriable: boolean;
|
|
2
14
|
readonly cause?: unknown;
|
|
3
15
|
constructor(message: string, options?: {
|
|
4
16
|
cause?: unknown;
|
|
5
17
|
});
|
|
6
18
|
}
|
|
7
19
|
export declare class StepAlreadyExecutedError extends DuronError {
|
|
20
|
+
readonly code: "STEP_ALREADY_EXECUTED";
|
|
21
|
+
readonly nonRetriable = true;
|
|
8
22
|
constructor(stepName: string, jobId: string, actionName: string);
|
|
9
23
|
}
|
|
10
24
|
export declare class NonRetriableError extends DuronError {
|
|
25
|
+
readonly code: ErrorCode;
|
|
26
|
+
readonly nonRetriable = true;
|
|
11
27
|
}
|
|
12
28
|
export declare class ActionTimeoutError extends DuronError {
|
|
29
|
+
readonly code: "ACTION_TIMEOUT";
|
|
30
|
+
readonly nonRetriable = true;
|
|
13
31
|
constructor(actionName: string, timeoutMs: number, options?: {
|
|
14
32
|
cause?: unknown;
|
|
15
33
|
});
|
|
16
34
|
}
|
|
17
35
|
export declare class StepTimeoutError extends DuronError {
|
|
36
|
+
readonly code: "STEP_TIMEOUT";
|
|
37
|
+
readonly nonRetriable = false;
|
|
18
38
|
constructor(stepName: string, jobId: string, timeoutMs: number, options?: {
|
|
19
39
|
cause?: unknown;
|
|
20
40
|
});
|
|
21
41
|
}
|
|
22
42
|
export declare class ActionCancelError extends DuronError {
|
|
43
|
+
readonly code: "ACTION_CANCEL";
|
|
44
|
+
readonly nonRetriable = true;
|
|
23
45
|
constructor(actionName: string, jobId: string, options?: {
|
|
24
46
|
cause?: unknown;
|
|
25
47
|
});
|
|
26
48
|
}
|
|
27
49
|
export declare class UnhandledChildStepsError extends NonRetriableError {
|
|
50
|
+
readonly code: "UNHANDLED_CHILD_STEPS";
|
|
28
51
|
readonly stepName: string;
|
|
29
52
|
readonly pendingCount: number;
|
|
30
53
|
constructor(stepName: string, pendingCount: number);
|
|
@@ -37,13 +60,10 @@ export declare function isCancelError(error: unknown): error is ActionCancelErro
|
|
|
37
60
|
export type SerializableError = {
|
|
38
61
|
name: string;
|
|
39
62
|
message: string;
|
|
63
|
+
code?: ErrorCode;
|
|
64
|
+
nonRetriable?: boolean;
|
|
40
65
|
cause?: unknown;
|
|
41
66
|
stack?: string;
|
|
42
67
|
};
|
|
43
|
-
export declare function serializeError(error: unknown):
|
|
44
|
-
name: string;
|
|
45
|
-
message: string;
|
|
46
|
-
cause?: unknown;
|
|
47
|
-
stack?: string;
|
|
48
|
-
};
|
|
68
|
+
export declare function serializeError(error: unknown): SerializableError;
|
|
49
69
|
//# sourceMappingURL=errors.d.ts.map
|
package/dist/errors.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,WAAW;;;;;;;;CAQd,CAAA;AAEV,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,OAAO,WAAW,CAAC,CAAA;AAMtE,8BAAsB,UAAW,SAAQ,KAAK;IAI5C,SAAgB,IAAI,EAAE,SAAS,CAA0B;IAKzD,SAAgB,YAAY,EAAE,OAAO,CAAQ;IAO7C,SAAyB,KAAK,CAAC,EAAE,OAAO,CAAA;gBAGtC,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE;QAMR,KAAK,CAAC,EAAE,OAAO,CAAA;KAChB;CAWJ;AAKD,qBAAa,wBAAyB,SAAQ,UAAU;IACtD,SAAyB,IAAI,0BAAoC;IACjE,SAAyB,YAAY,QAAO;gBAShC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM;CAGhE;AAQD,qBAAa,iBAAkB,SAAQ,UAAU;IAC/C,SAAyB,IAAI,EAAE,SAAS,CAA4B;IACpE,SAAyB,YAAY,QAAO;CAC7C;AAKD,qBAAa,kBAAmB,SAAQ,UAAU;IAChD,SAAyB,IAAI,mBAA6B;IAC1D,SAAyB,YAAY,QAAO;gBAU1C,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE;QACR,KAAK,CAAC,EAAE,OAAO,CAAA;KAChB;CAIJ;AAKD,qBAAa,gBAAiB,SAAQ,UAAU;IAC9C,SAAyB,IAAI,iBAA2B;IACxD,SAAyB,YAAY,SAAQ;gBAW3C,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE;QACR,KAAK,CAAC,EAAE,OAAO,CAAA;KAChB;CAIJ;AAKD,qBAAa,iBAAkB,SAAQ,UAAU;IAC/C,SAAyB,IAAI,kBAA4B;IACzD,SAAyB,YAAY,QAAO;gBAU1C,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE;QACR,KAAK,CAAC,EAAE,OAAO,CAAA;KAChB;CAIJ;AAQD,qBAAa,wBAAyB,SAAQ,iBAAiB;IAC7D,SAAyB,IAAI,0BAAoC;IAKjE,SAAgB,QAAQ,EAAE,MAAM,CAAA;IAKhC,SAAgB,YAAY,EAAE,MAAM,CAAA;gBAQxB,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM;CAOnD;AAKD,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,UAAU,CAGhE;AAKD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,iBAAiB,CAE9E;AAKD,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,wBAAwB,CAE5F;AAKD,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,kBAAkB,GAAG,gBAAgB,CAG7F;AAKD,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,iBAAiB,CAExE;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,KAAK,CAAC,EAAE,MAAM,CAAA;CACf,CAAA;AAMD,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,iBAAiB,CAuChE"}
|
package/dist/errors.js
CHANGED
|
@@ -1,4 +1,15 @@
|
|
|
1
|
+
export const ERROR_CODES = {
|
|
2
|
+
DURON_ERROR: 'DURON_ERROR',
|
|
3
|
+
STEP_ALREADY_EXECUTED: 'STEP_ALREADY_EXECUTED',
|
|
4
|
+
NON_RETRIABLE: 'NON_RETRIABLE',
|
|
5
|
+
ACTION_TIMEOUT: 'ACTION_TIMEOUT',
|
|
6
|
+
STEP_TIMEOUT: 'STEP_TIMEOUT',
|
|
7
|
+
ACTION_CANCEL: 'ACTION_CANCEL',
|
|
8
|
+
UNHANDLED_CHILD_STEPS: 'UNHANDLED_CHILD_STEPS',
|
|
9
|
+
};
|
|
1
10
|
export class DuronError extends Error {
|
|
11
|
+
code = ERROR_CODES.DURON_ERROR;
|
|
12
|
+
nonRetriable = false;
|
|
2
13
|
cause;
|
|
3
14
|
constructor(message, options) {
|
|
4
15
|
super(message);
|
|
@@ -10,28 +21,39 @@ export class DuronError extends Error {
|
|
|
10
21
|
}
|
|
11
22
|
}
|
|
12
23
|
export class StepAlreadyExecutedError extends DuronError {
|
|
24
|
+
code = ERROR_CODES.STEP_ALREADY_EXECUTED;
|
|
25
|
+
nonRetriable = true;
|
|
13
26
|
constructor(stepName, jobId, actionName) {
|
|
14
27
|
super(`Step "${stepName}" has already been executed for job "${jobId}" and action "${actionName}"`);
|
|
15
28
|
}
|
|
16
29
|
}
|
|
17
30
|
export class NonRetriableError extends DuronError {
|
|
31
|
+
code = ERROR_CODES.NON_RETRIABLE;
|
|
32
|
+
nonRetriable = true;
|
|
18
33
|
}
|
|
19
34
|
export class ActionTimeoutError extends DuronError {
|
|
35
|
+
code = ERROR_CODES.ACTION_TIMEOUT;
|
|
36
|
+
nonRetriable = true;
|
|
20
37
|
constructor(actionName, timeoutMs, options) {
|
|
21
38
|
super(`Action "${actionName}" timed out after ${timeoutMs}ms`, options);
|
|
22
39
|
}
|
|
23
40
|
}
|
|
24
41
|
export class StepTimeoutError extends DuronError {
|
|
42
|
+
code = ERROR_CODES.STEP_TIMEOUT;
|
|
43
|
+
nonRetriable = false;
|
|
25
44
|
constructor(stepName, jobId, timeoutMs, options) {
|
|
26
45
|
super(`Step "${stepName}" in job "${jobId}" timed out after ${timeoutMs}ms`, options);
|
|
27
46
|
}
|
|
28
47
|
}
|
|
29
48
|
export class ActionCancelError extends DuronError {
|
|
49
|
+
code = ERROR_CODES.ACTION_CANCEL;
|
|
50
|
+
nonRetriable = true;
|
|
30
51
|
constructor(actionName, jobId, options) {
|
|
31
52
|
super(`Action "${actionName}" in job "${jobId}" was cancelled`, options);
|
|
32
53
|
}
|
|
33
54
|
}
|
|
34
55
|
export class UnhandledChildStepsError extends NonRetriableError {
|
|
56
|
+
code = ERROR_CODES.UNHANDLED_CHILD_STEPS;
|
|
35
57
|
stepName;
|
|
36
58
|
pendingCount;
|
|
37
59
|
constructor(stepName, pendingCount) {
|
|
@@ -41,36 +63,41 @@ export class UnhandledChildStepsError extends NonRetriableError {
|
|
|
41
63
|
}
|
|
42
64
|
}
|
|
43
65
|
export function isDuronError(error) {
|
|
44
|
-
|
|
66
|
+
const code = error?.code;
|
|
67
|
+
return code !== undefined && Object.values(ERROR_CODES).includes(code);
|
|
45
68
|
}
|
|
46
69
|
export function isNonRetriableError(error) {
|
|
47
|
-
return
|
|
48
|
-
error instanceof ActionCancelError ||
|
|
49
|
-
error instanceof ActionTimeoutError ||
|
|
50
|
-
error instanceof UnhandledChildStepsError);
|
|
70
|
+
return error?.nonRetriable === true;
|
|
51
71
|
}
|
|
52
72
|
export function isUnhandledChildStepsError(error) {
|
|
53
|
-
return error
|
|
73
|
+
return error?.code === ERROR_CODES.UNHANDLED_CHILD_STEPS;
|
|
54
74
|
}
|
|
55
75
|
export function isTimeoutError(error) {
|
|
56
|
-
|
|
76
|
+
const code = error?.code;
|
|
77
|
+
return code === ERROR_CODES.ACTION_TIMEOUT || code === ERROR_CODES.STEP_TIMEOUT;
|
|
57
78
|
}
|
|
58
79
|
export function isCancelError(error) {
|
|
59
|
-
return error
|
|
80
|
+
return error?.code === ERROR_CODES.ACTION_CANCEL;
|
|
60
81
|
}
|
|
61
82
|
export function serializeError(error) {
|
|
62
|
-
|
|
83
|
+
const code = error?.code;
|
|
84
|
+
const nonRetriable = error?.nonRetriable;
|
|
85
|
+
if (isTimeoutError(error)) {
|
|
63
86
|
return {
|
|
64
87
|
name: error.name,
|
|
65
88
|
message: error.message,
|
|
89
|
+
code,
|
|
90
|
+
nonRetriable,
|
|
66
91
|
cause: error.cause,
|
|
67
92
|
stack: undefined,
|
|
68
93
|
};
|
|
69
94
|
}
|
|
70
|
-
if (error
|
|
95
|
+
if (isDuronError(error)) {
|
|
71
96
|
return {
|
|
72
97
|
name: error.name,
|
|
73
98
|
message: error.message,
|
|
99
|
+
code,
|
|
100
|
+
nonRetriable,
|
|
74
101
|
cause: error.cause,
|
|
75
102
|
stack: error.stack,
|
|
76
103
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"step-manager.d.ts","sourceRoot":"","sources":["../src/step-manager.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AAClC,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAE5B,OAAO,EACL,KAAK,MAAM,EACX,KAAK,oBAAoB,EAGzB,KAAK,kBAAkB,EACvB,KAAK,WAAW,EAEjB,MAAM,aAAa,CAAA;AACpB,OAAO,KAAK,EAAE,OAAO,EAAgC,MAAM,uBAAuB,CAAA;AAClF,OAAO,EAAoE,KAAK,UAAU,EAAE,MAAM,gBAAgB,CAAA;
|
|
1
|
+
{"version":3,"file":"step-manager.d.ts","sourceRoot":"","sources":["../src/step-manager.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AAClC,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAE5B,OAAO,EACL,KAAK,MAAM,EACX,KAAK,oBAAoB,EAGzB,KAAK,kBAAkB,EACvB,KAAK,WAAW,EAEjB,MAAM,aAAa,CAAA;AACpB,OAAO,KAAK,EAAE,OAAO,EAAgC,MAAM,uBAAuB,CAAA;AAClF,OAAO,EAAoE,KAAK,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAYlH,OAAO,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,gBAAgB,EAAsB,MAAM,wBAAwB,CAAA;AA2CxG,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAA;IACZ,EAAE,EAAE,CAAC,GAAG,EAAE,kBAAkB,KAAK,OAAO,CAAC,GAAG,CAAC,CAAA;IAC7C,OAAO,EAAE,WAAW,CAAA;IACpB,WAAW,EAAE,WAAW,CAAA;IACxB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,QAAQ,EAAE,OAAO,CAAA;CAClB;AAMD,qBAAa,SAAS;;gBAYR,OAAO,EAAE,OAAO;IAoBtB,WAAW,CACf,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,EACpB,YAAY,GAAE,MAAM,GAAG,IAAW,EAClC,QAAQ,GAAE,OAAe;;;;;;;;;;IAyBrB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,CAAC,EAAE,GAAG,EAAE,KAAK,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC;IAoB7F,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC;CAG3E;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAA;IACb,UAAU,EAAE,MAAM,CAAA;IAClB,OAAO,EAAE,OAAO,CAAA;IAChB,SAAS,EAAE,gBAAgB,CAAA;IAC3B,MAAM,EAAE,MAAM,CAAA;IACd,gBAAgB,EAAE,MAAM,CAAA;CACzB;AAMD,qBAAa,WAAW;;gBAyBV,OAAO,EAAE,kBAAkB;IAqBvC,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI;IAU5B,eAAe,CAAC,OAAO,EAAE,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,EAAE,WAAW,EAAE,WAAW,KAAK,kBAAkB,CAAC,KAAK,CAAC,GAAG,IAAI;IAoBpH,mBAAmB,CAAC,MAAM,SAAS,CAAC,CAAC,SAAS,EAAE,OAAO,SAAS,CAAC,CAAC,SAAS,EAAE,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/G,GAAG,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,EAC9D,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,UAAU,CAAC,EAC3C,SAAS,EAAE,UAAU,EACrB,WAAW,EAAE,WAAW,EACxB,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,cAAc,GAC7B,oBAAoB,CAAC,MAAM,EAAE,UAAU,CAAC;IAO3C,wBAAwB,CAAC,MAAM,EAAE,MAAM,GAAG,cAAc;IAgClD,IAAI,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC;IAuBlC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CA4T7B"}
|
package/dist/step-manager.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fastq from 'fastq';
|
|
2
2
|
import { StepOptionsSchema, } from './action.js';
|
|
3
3
|
import { STEP_STATUS_CANCELLED, STEP_STATUS_COMPLETED, STEP_STATUS_FAILED } from './constants.js';
|
|
4
|
-
import { ActionCancelError, isCancelError, isNonRetriableError, NonRetriableError, StepAlreadyExecutedError, StepTimeoutError, serializeError, UnhandledChildStepsError, } from './errors.js';
|
|
4
|
+
import { ActionCancelError, isCancelError, isNonRetriableError, isTimeoutError, NonRetriableError, StepAlreadyExecutedError, StepTimeoutError, serializeError, UnhandledChildStepsError, } from './errors.js';
|
|
5
5
|
const noopTracerSpan = {
|
|
6
6
|
setAttribute() {
|
|
7
7
|
},
|
|
@@ -298,6 +298,14 @@ export class StepManager {
|
|
|
298
298
|
throw new Error(`Failed to delay step "${name}" for job "${this.#jobId}" action "${this.#actionName}"`);
|
|
299
299
|
}
|
|
300
300
|
}
|
|
301
|
+
else {
|
|
302
|
+
if (isTimeoutError(error)) {
|
|
303
|
+
;
|
|
304
|
+
error.nonRetriable = true;
|
|
305
|
+
throw error;
|
|
306
|
+
}
|
|
307
|
+
throw new NonRetriableError(`Failed to execute step="${name}", jobId="${this.#jobId}", action="${this.#actionName}", parentStepId="${parentStepId}"`, { cause: error });
|
|
308
|
+
}
|
|
301
309
|
},
|
|
302
310
|
}).catch(async (error) => {
|
|
303
311
|
if (step) {
|
package/package.json
CHANGED
package/src/action-job.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { Logger } from 'pino'
|
|
|
2
2
|
|
|
3
3
|
import type { Action } from './action.js'
|
|
4
4
|
import type { Adapter } from './adapters/adapter.js'
|
|
5
|
-
import { ActionCancelError, ActionTimeoutError, isCancelError,
|
|
5
|
+
import { ActionCancelError, ActionTimeoutError, isCancelError, isTimeoutError, serializeError } from './errors.js'
|
|
6
6
|
import { StepManager } from './step-manager.js'
|
|
7
7
|
import type { Span, TelemetryAdapter } from './telemetry/adapter.js'
|
|
8
8
|
import waitForAbort from './utils/wait-for-abort.js'
|
|
@@ -164,6 +164,15 @@ export class ActionJob<TAction extends Action<any, any, any>> {
|
|
|
164
164
|
|
|
165
165
|
return result
|
|
166
166
|
} catch (error) {
|
|
167
|
+
// Abort all running steps when an error occurs
|
|
168
|
+
// This ensures cascading failure and stops any steps still running
|
|
169
|
+
if (!this.#abortController.signal.aborted) {
|
|
170
|
+
this.#abortController.abort(error)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Wait for step manager to drain (all steps to settle)
|
|
174
|
+
await this.#stepManager.drain()
|
|
175
|
+
|
|
167
176
|
if (
|
|
168
177
|
isCancelError(error) ||
|
|
169
178
|
(error instanceof Error && error.name === 'AbortError' && isCancelError(error.cause))
|
|
@@ -179,11 +188,9 @@ export class ActionJob<TAction extends Action<any, any, any>> {
|
|
|
179
188
|
}
|
|
180
189
|
|
|
181
190
|
const message =
|
|
182
|
-
error
|
|
191
|
+
isTimeoutError(error)
|
|
183
192
|
? '[ActionJob] Job timed out'
|
|
184
|
-
:
|
|
185
|
-
? '[ActionJob] Step timed out'
|
|
186
|
-
: '[ActionJob] Job failed'
|
|
193
|
+
: '[ActionJob] Job failed'
|
|
187
194
|
|
|
188
195
|
this.#logger.error({ jobId: this.#job.id, actionName: this.#action.name }, message)
|
|
189
196
|
await this.#database.failJob({ jobId: this.#job.id, error: serializeError(error) })
|
package/src/errors.ts
CHANGED
|
@@ -1,8 +1,31 @@
|
|
|
1
|
+
// Error codes for type checking without instanceof
|
|
2
|
+
export const ERROR_CODES = {
|
|
3
|
+
DURON_ERROR: 'DURON_ERROR',
|
|
4
|
+
STEP_ALREADY_EXECUTED: 'STEP_ALREADY_EXECUTED',
|
|
5
|
+
NON_RETRIABLE: 'NON_RETRIABLE',
|
|
6
|
+
ACTION_TIMEOUT: 'ACTION_TIMEOUT',
|
|
7
|
+
STEP_TIMEOUT: 'STEP_TIMEOUT',
|
|
8
|
+
ACTION_CANCEL: 'ACTION_CANCEL',
|
|
9
|
+
UNHANDLED_CHILD_STEPS: 'UNHANDLED_CHILD_STEPS',
|
|
10
|
+
} as const
|
|
11
|
+
|
|
12
|
+
export type ErrorCode = (typeof ERROR_CODES)[keyof typeof ERROR_CODES]
|
|
13
|
+
|
|
1
14
|
/**
|
|
2
15
|
* Base class for all built-in errors in Duron.
|
|
3
16
|
* All errors include a cause property that can be serialized.
|
|
4
17
|
*/
|
|
5
18
|
export abstract class DuronError extends Error {
|
|
19
|
+
/**
|
|
20
|
+
* Error code for type checking without instanceof.
|
|
21
|
+
*/
|
|
22
|
+
public readonly code: ErrorCode = ERROR_CODES.DURON_ERROR
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Whether this error should prevent retries.
|
|
26
|
+
*/
|
|
27
|
+
public readonly nonRetriable: boolean = false
|
|
28
|
+
|
|
6
29
|
/**
|
|
7
30
|
* The underlying cause of the error, if any.
|
|
8
31
|
*
|
|
@@ -36,6 +59,9 @@ export abstract class DuronError extends Error {
|
|
|
36
59
|
* Error thrown when attempting to execute a step that has already been executed.
|
|
37
60
|
*/
|
|
38
61
|
export class StepAlreadyExecutedError extends DuronError {
|
|
62
|
+
public override readonly code = ERROR_CODES.STEP_ALREADY_EXECUTED
|
|
63
|
+
public override readonly nonRetriable = true
|
|
64
|
+
|
|
39
65
|
/**
|
|
40
66
|
* Create a new StepAlreadyExecutedError.
|
|
41
67
|
*
|
|
@@ -55,13 +81,17 @@ export class StepAlreadyExecutedError extends DuronError {
|
|
|
55
81
|
* without retrying, even if retry options are configured.
|
|
56
82
|
*/
|
|
57
83
|
export class NonRetriableError extends DuronError {
|
|
58
|
-
|
|
84
|
+
public override readonly code: ErrorCode = ERROR_CODES.NON_RETRIABLE
|
|
85
|
+
public override readonly nonRetriable = true
|
|
59
86
|
}
|
|
60
87
|
|
|
61
88
|
/**
|
|
62
89
|
* Error thrown when an action exceeds its timeout.
|
|
63
90
|
*/
|
|
64
91
|
export class ActionTimeoutError extends DuronError {
|
|
92
|
+
public override readonly code = ERROR_CODES.ACTION_TIMEOUT
|
|
93
|
+
public override readonly nonRetriable = true
|
|
94
|
+
|
|
65
95
|
/**
|
|
66
96
|
* Create a new ActionTimeoutError.
|
|
67
97
|
*
|
|
@@ -84,6 +114,9 @@ export class ActionTimeoutError extends DuronError {
|
|
|
84
114
|
* Error thrown when a step exceeds its timeout.
|
|
85
115
|
*/
|
|
86
116
|
export class StepTimeoutError extends DuronError {
|
|
117
|
+
public override readonly code = ERROR_CODES.STEP_TIMEOUT
|
|
118
|
+
public override readonly nonRetriable = false
|
|
119
|
+
|
|
87
120
|
/**
|
|
88
121
|
* Create a new StepTimeoutError.
|
|
89
122
|
*
|
|
@@ -108,6 +141,9 @@ export class StepTimeoutError extends DuronError {
|
|
|
108
141
|
* Error thrown when an action is cancelled.
|
|
109
142
|
*/
|
|
110
143
|
export class ActionCancelError extends DuronError {
|
|
144
|
+
public override readonly code = ERROR_CODES.ACTION_CANCEL
|
|
145
|
+
public override readonly nonRetriable = true
|
|
146
|
+
|
|
111
147
|
/**
|
|
112
148
|
* Create a new ActionCancelError.
|
|
113
149
|
*
|
|
@@ -133,6 +169,8 @@ export class ActionCancelError extends DuronError {
|
|
|
133
169
|
* but not properly awaited. All child steps must be awaited before the parent returns.
|
|
134
170
|
*/
|
|
135
171
|
export class UnhandledChildStepsError extends NonRetriableError {
|
|
172
|
+
public override readonly code = ERROR_CODES.UNHANDLED_CHILD_STEPS
|
|
173
|
+
|
|
136
174
|
/**
|
|
137
175
|
* The name of the parent step that completed with unhandled children.
|
|
138
176
|
*/
|
|
@@ -162,45 +200,44 @@ export class UnhandledChildStepsError extends NonRetriableError {
|
|
|
162
200
|
* Checks if an error is a DuronError instance.
|
|
163
201
|
*/
|
|
164
202
|
export function isDuronError(error: unknown): error is DuronError {
|
|
165
|
-
|
|
203
|
+
const code = (error as any)?.code
|
|
204
|
+
return code !== undefined && Object.values(ERROR_CODES).includes(code)
|
|
166
205
|
}
|
|
167
206
|
|
|
168
207
|
/**
|
|
169
208
|
* Checks if an error is a NonRetriableError instance.
|
|
170
209
|
*/
|
|
171
210
|
export function isNonRetriableError(error: unknown): error is NonRetriableError {
|
|
172
|
-
return (
|
|
173
|
-
error instanceof NonRetriableError ||
|
|
174
|
-
error instanceof ActionCancelError ||
|
|
175
|
-
error instanceof ActionTimeoutError ||
|
|
176
|
-
error instanceof UnhandledChildStepsError
|
|
177
|
-
)
|
|
211
|
+
return (error as any)?.nonRetriable === true
|
|
178
212
|
}
|
|
179
213
|
|
|
180
214
|
/**
|
|
181
215
|
* Checks if an error is an UnhandledChildStepsError instance.
|
|
182
216
|
*/
|
|
183
217
|
export function isUnhandledChildStepsError(error: unknown): error is UnhandledChildStepsError {
|
|
184
|
-
return error
|
|
218
|
+
return (error as any)?.code === ERROR_CODES.UNHANDLED_CHILD_STEPS
|
|
185
219
|
}
|
|
186
220
|
|
|
187
221
|
/**
|
|
188
222
|
* Checks if an error is a timeout error (ActionTimeoutError or StepTimeoutError).
|
|
189
223
|
*/
|
|
190
224
|
export function isTimeoutError(error: unknown): error is ActionTimeoutError | StepTimeoutError {
|
|
191
|
-
|
|
225
|
+
const code = (error as any)?.code
|
|
226
|
+
return code === ERROR_CODES.ACTION_TIMEOUT || code === ERROR_CODES.STEP_TIMEOUT
|
|
192
227
|
}
|
|
193
228
|
|
|
194
229
|
/**
|
|
195
230
|
* Checks if an error is a cancel error (ActionCancelError or StepCancelError).
|
|
196
231
|
*/
|
|
197
232
|
export function isCancelError(error: unknown): error is ActionCancelError {
|
|
198
|
-
return error
|
|
233
|
+
return (error as any)?.code === ERROR_CODES.ACTION_CANCEL
|
|
199
234
|
}
|
|
200
235
|
|
|
201
236
|
export type SerializableError = {
|
|
202
237
|
name: string
|
|
203
238
|
message: string
|
|
239
|
+
code?: ErrorCode
|
|
240
|
+
nonRetriable?: boolean
|
|
204
241
|
cause?: unknown
|
|
205
242
|
stack?: string
|
|
206
243
|
}
|
|
@@ -209,25 +246,27 @@ export type SerializableError = {
|
|
|
209
246
|
* Serializes an error for storage in the database.
|
|
210
247
|
* Handles DuronError instances specially to preserve their type information.
|
|
211
248
|
*/
|
|
212
|
-
export function serializeError(error: unknown): {
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
} {
|
|
218
|
-
if (error instanceof StepTimeoutError || error instanceof ActionTimeoutError) {
|
|
249
|
+
export function serializeError(error: unknown): SerializableError {
|
|
250
|
+
const code = (error as any)?.code
|
|
251
|
+
const nonRetriable = (error as any)?.nonRetriable
|
|
252
|
+
|
|
253
|
+
if (isTimeoutError(error)) {
|
|
219
254
|
return {
|
|
220
255
|
name: error.name,
|
|
221
256
|
message: error.message,
|
|
257
|
+
code,
|
|
258
|
+
nonRetriable,
|
|
222
259
|
cause: error.cause,
|
|
223
260
|
stack: undefined,
|
|
224
261
|
}
|
|
225
262
|
}
|
|
226
263
|
|
|
227
|
-
if (error
|
|
264
|
+
if (isDuronError(error)) {
|
|
228
265
|
return {
|
|
229
266
|
name: error.name,
|
|
230
267
|
message: error.message,
|
|
268
|
+
code,
|
|
269
|
+
nonRetriable,
|
|
231
270
|
cause: error.cause,
|
|
232
271
|
stack: error.stack,
|
|
233
272
|
}
|
package/src/step-manager.ts
CHANGED
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
ActionCancelError,
|
|
18
18
|
isCancelError,
|
|
19
19
|
isNonRetriableError,
|
|
20
|
+
isTimeoutError,
|
|
20
21
|
NonRetriableError,
|
|
21
22
|
StepAlreadyExecutedError,
|
|
22
23
|
StepTimeoutError,
|
|
@@ -493,6 +494,7 @@ export class StepManager {
|
|
|
493
494
|
.catch(() => {
|
|
494
495
|
trackedChild.settled = true
|
|
495
496
|
// Swallow the error here - it will be re-thrown to the caller via the returned promise
|
|
497
|
+
// Note: sibling steps will be aborted when the error propagates to the action level
|
|
496
498
|
})
|
|
497
499
|
|
|
498
500
|
return childPromise
|
|
@@ -607,6 +609,15 @@ export class StepManager {
|
|
|
607
609
|
if (!delayed) {
|
|
608
610
|
throw new Error(`Failed to delay step "${name}" for job "${this.#jobId}" action "${this.#actionName}"`)
|
|
609
611
|
}
|
|
612
|
+
} else {
|
|
613
|
+
if (isTimeoutError(error)) {
|
|
614
|
+
;(error as any).nonRetriable = true
|
|
615
|
+
throw error
|
|
616
|
+
}
|
|
617
|
+
throw new NonRetriableError(
|
|
618
|
+
`Failed to execute step="${name}", jobId="${this.#jobId}", action="${this.#actionName}", parentStepId="${parentStepId}"`,
|
|
619
|
+
{ cause: error },
|
|
620
|
+
)
|
|
610
621
|
}
|
|
611
622
|
},
|
|
612
623
|
}).catch(async (error) => {
|
|
@@ -758,6 +769,7 @@ class ActionContext<TInput extends z.ZodObject, TOutput extends z.ZodObject, TVa
|
|
|
758
769
|
...this.#action.steps,
|
|
759
770
|
...options,
|
|
760
771
|
})
|
|
772
|
+
|
|
761
773
|
return this.#stepManager.push({
|
|
762
774
|
name,
|
|
763
775
|
cb,
|