@hotmeshio/hotmesh 0.0.52 → 0.0.53
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 +22 -18
- package/build/index.d.ts +1 -2
- package/build/index.js +1 -3
- package/build/modules/enums.d.ts +8 -3
- package/build/modules/enums.js +16 -8
- package/build/modules/errors.d.ts +98 -18
- package/build/modules/errors.js +90 -33
- package/build/package.json +7 -2
- package/build/services/activities/activity.d.ts +8 -0
- package/build/services/activities/activity.js +63 -14
- package/build/services/activities/await.js +6 -6
- package/build/services/activities/cycle.d.ts +2 -2
- package/build/services/activities/cycle.js +5 -5
- package/build/services/activities/hook.js +4 -4
- package/build/services/activities/interrupt.d.ts +3 -3
- package/build/services/activities/interrupt.js +15 -6
- package/build/services/activities/signal.d.ts +2 -2
- package/build/services/activities/signal.js +4 -4
- package/build/services/activities/trigger.js +12 -3
- package/build/services/activities/worker.js +6 -6
- package/build/services/compiler/deployer.js +33 -5
- package/build/services/compiler/validator.d.ts +2 -0
- package/build/services/compiler/validator.js +5 -1
- package/build/services/durable/client.d.ts +7 -1
- package/build/services/durable/client.js +56 -30
- package/build/services/durable/exporter.d.ts +7 -72
- package/build/services/durable/exporter.js +105 -295
- package/build/services/durable/handle.d.ts +11 -6
- package/build/services/durable/handle.js +59 -46
- package/build/services/durable/index.d.ts +0 -2
- package/build/services/durable/index.js +0 -2
- package/build/services/durable/schemas/factory.d.ts +33 -0
- package/build/services/durable/schemas/factory.js +2356 -0
- package/build/services/durable/search.js +8 -8
- package/build/services/durable/worker.js +117 -25
- package/build/services/durable/workflow.d.ts +46 -43
- package/build/services/durable/workflow.js +273 -277
- package/build/services/engine/index.js +3 -0
- package/build/services/exporter/index.d.ts +2 -4
- package/build/services/exporter/index.js +4 -5
- package/build/services/mapper/index.d.ts +6 -2
- package/build/services/mapper/index.js +6 -2
- package/build/services/pipe/functions/array.d.ts +2 -10
- package/build/services/pipe/functions/array.js +30 -28
- package/build/services/pipe/functions/conditional.d.ts +1 -0
- package/build/services/pipe/functions/conditional.js +3 -0
- package/build/services/pipe/functions/date.d.ts +1 -0
- package/build/services/pipe/functions/date.js +4 -0
- package/build/services/pipe/functions/index.d.ts +2 -0
- package/build/services/pipe/functions/index.js +2 -0
- package/build/services/pipe/functions/logical.d.ts +5 -0
- package/build/services/pipe/functions/logical.js +12 -0
- package/build/services/pipe/functions/object.d.ts +3 -0
- package/build/services/pipe/functions/object.js +25 -7
- package/build/services/pipe/index.d.ts +20 -3
- package/build/services/pipe/index.js +82 -16
- package/build/services/router/index.js +14 -3
- package/build/services/serializer/index.d.ts +3 -2
- package/build/services/serializer/index.js +11 -4
- package/build/services/store/clients/ioredis.js +6 -6
- package/build/services/store/clients/redis.js +7 -7
- package/build/services/store/index.d.ts +2 -0
- package/build/services/store/index.js +4 -1
- package/build/services/stream/clients/ioredis.js +8 -8
- package/build/services/stream/clients/redis.js +1 -1
- package/build/types/activity.d.ts +60 -5
- package/build/types/durable.d.ts +168 -33
- package/build/types/exporter.d.ts +26 -4
- package/build/types/index.d.ts +2 -2
- package/build/types/job.d.ts +69 -5
- package/build/types/pipe.d.ts +81 -3
- package/build/types/stream.d.ts +61 -1
- package/build/types/stream.js +4 -0
- package/index.ts +1 -2
- package/modules/enums.ts +16 -8
- package/modules/errors.ts +174 -32
- package/package.json +7 -2
- package/services/activities/activity.ts +63 -14
- package/services/activities/await.ts +6 -6
- package/services/activities/cycle.ts +7 -6
- package/services/activities/hook.ts +4 -4
- package/services/activities/interrupt.ts +19 -9
- package/services/activities/signal.ts +6 -5
- package/services/activities/trigger.ts +16 -4
- package/services/activities/worker.ts +7 -7
- package/services/compiler/deployer.ts +33 -6
- package/services/compiler/validator.ts +7 -3
- package/services/durable/client.ts +47 -14
- package/services/durable/exporter.ts +110 -318
- package/services/durable/handle.ts +63 -50
- package/services/durable/index.ts +0 -2
- package/services/durable/schemas/factory.ts +2358 -0
- package/services/durable/search.ts +8 -8
- package/services/durable/worker.ts +128 -29
- package/services/durable/workflow.ts +304 -288
- package/services/engine/index.ts +4 -0
- package/services/exporter/index.ts +10 -12
- package/services/mapper/index.ts +6 -2
- package/services/pipe/functions/array.ts +24 -37
- package/services/pipe/functions/conditional.ts +4 -0
- package/services/pipe/functions/date.ts +6 -0
- package/services/pipe/functions/index.ts +7 -5
- package/services/pipe/functions/logical.ts +11 -0
- package/services/pipe/functions/object.ts +26 -7
- package/services/pipe/index.ts +99 -21
- package/services/quorum/index.ts +1 -3
- package/services/router/index.ts +14 -3
- package/services/serializer/index.ts +12 -5
- package/services/store/clients/ioredis.ts +6 -6
- package/services/store/clients/redis.ts +7 -7
- package/services/store/index.ts +4 -1
- package/services/stream/clients/ioredis.ts +8 -8
- package/services/stream/clients/redis.ts +1 -1
- package/types/activity.ts +87 -15
- package/types/durable.ts +246 -73
- package/types/exporter.ts +31 -5
- package/types/index.ts +6 -7
- package/types/job.ts +130 -36
- package/types/pipe.ts +84 -3
- package/types/stream.ts +82 -23
- package/build/services/durable/factory.d.ts +0 -17
- package/build/services/durable/factory.js +0 -817
- package/build/services/durable/meshos.d.ts +0 -127
- package/build/services/durable/meshos.js +0 -380
- package/services/durable/factory.ts +0 -818
- package/services/durable/meshos.ts +0 -441
package/build/types/stream.d.ts
CHANGED
|
@@ -1,18 +1,38 @@
|
|
|
1
|
+
/** Represents a policy for retrying stream operations based on error codes */
|
|
1
2
|
export interface StreamRetryPolicy {
|
|
2
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Key is error code, value is the retry profile.
|
|
5
|
+
* Tuple contains: [max retry count, retry type].
|
|
6
|
+
* 'x' denotes exponential backoff (default). Only 10, 100, 1000, 10000 are allowed retry intervals.
|
|
7
|
+
*/
|
|
8
|
+
[key: string]: [number, 'x'?];
|
|
3
9
|
}
|
|
10
|
+
/** A 3-digit status code representing the outcome of a stream operation */
|
|
4
11
|
export type StreamCode = number;
|
|
12
|
+
/** Describes the structure of a stream error */
|
|
5
13
|
export type StreamError = {
|
|
14
|
+
/** Descriptive message of the error */
|
|
6
15
|
message: string;
|
|
16
|
+
/** Numeric code corresponding to the type of error */
|
|
7
17
|
code: number;
|
|
18
|
+
/** Optional job identifier, used when communicating errors externally */
|
|
8
19
|
job_id?: string;
|
|
20
|
+
/** Stack trace of the error if unhandled */
|
|
9
21
|
stack?: string;
|
|
22
|
+
/** Name of the error if unhandled */
|
|
10
23
|
name?: string;
|
|
24
|
+
/** Custom user-defined error details */
|
|
11
25
|
error?: Record<string, unknown>;
|
|
26
|
+
/** True if originating via a standard transition message with an `error` status */
|
|
27
|
+
is_stream_error?: boolean;
|
|
12
28
|
};
|
|
29
|
+
/** Enumerated status values for stream operations */
|
|
13
30
|
export declare enum StreamStatus {
|
|
31
|
+
/** Indicates successful completion of the stream operation */
|
|
14
32
|
SUCCESS = "success",
|
|
33
|
+
/** Indicates an error occurred during the stream operation */
|
|
15
34
|
ERROR = "error",
|
|
35
|
+
/** Indicates the stream operation is still pending */
|
|
16
36
|
PENDING = "pending"
|
|
17
37
|
}
|
|
18
38
|
export declare enum StreamDataType {
|
|
@@ -26,27 +46,51 @@ export declare enum StreamDataType {
|
|
|
26
46
|
SIGNAL = "signal",
|
|
27
47
|
INTERRUPT = "interrupt"
|
|
28
48
|
}
|
|
49
|
+
/** Defines the structure of stream data used when passing stream messages (transitions) */
|
|
29
50
|
export interface StreamData {
|
|
51
|
+
/** Metadata associated with the stream data */
|
|
30
52
|
metadata: {
|
|
53
|
+
/** Globally unique identifier for the StreamData message to distinguish `retries` from new 'reentry/cycles' */
|
|
31
54
|
guid: string;
|
|
55
|
+
/** Workflow/job topic */
|
|
32
56
|
topic?: string;
|
|
57
|
+
/** Workflow/job ID */
|
|
33
58
|
jid?: string;
|
|
59
|
+
/** Workflow Generational ID (internal GUID) */
|
|
34
60
|
gid?: string;
|
|
61
|
+
/** Dimensional address indicating the message routing specifics */
|
|
35
62
|
dad?: string;
|
|
63
|
+
/** Activity ID */
|
|
36
64
|
aid: string;
|
|
65
|
+
/** OpenTelemetry Trace identifier */
|
|
37
66
|
trc?: string;
|
|
67
|
+
/** OpenTelemetry Span identifier */
|
|
38
68
|
spn?: string;
|
|
69
|
+
/** Current try count, used for retry logic */
|
|
39
70
|
try?: number;
|
|
71
|
+
/**
|
|
72
|
+
* Indicates if the message should wait for a response.
|
|
73
|
+
* If explicitly false, the connection is severed immediately
|
|
74
|
+
* upon verifying (and returning) the Job ID.
|
|
75
|
+
*/
|
|
40
76
|
await?: boolean;
|
|
41
77
|
};
|
|
78
|
+
/** Type of the data being streamed, optional */
|
|
42
79
|
type?: StreamDataType;
|
|
80
|
+
/** Actual data being transmitted as a record of key-value pairs */
|
|
43
81
|
data: Record<string, unknown>;
|
|
82
|
+
/** Policies related to retry logic, optional */
|
|
44
83
|
policies?: {
|
|
45
84
|
retry?: StreamRetryPolicy;
|
|
46
85
|
};
|
|
86
|
+
/** Status of the stream, default assumed as 'success' */
|
|
47
87
|
status?: StreamStatus;
|
|
88
|
+
/** HTTP-like status code for the stream, default assumed as 200 */
|
|
48
89
|
code?: number;
|
|
90
|
+
/** Error stack trace */
|
|
91
|
+
stack?: string;
|
|
49
92
|
}
|
|
93
|
+
/** Extends StreamData for responses, allowing for inheritance of the base properties */
|
|
50
94
|
export interface StreamDataResponse extends StreamData {
|
|
51
95
|
}
|
|
52
96
|
export declare enum StreamRole {
|
|
@@ -54,19 +98,35 @@ export declare enum StreamRole {
|
|
|
54
98
|
ENGINE = "engine",
|
|
55
99
|
SYSTEM = "system"
|
|
56
100
|
}
|
|
101
|
+
/**
|
|
102
|
+
* Represents a type for messages that have been reclaimed from a stream.
|
|
103
|
+
* Each item is a tuple containing a messageId and its details.
|
|
104
|
+
*/
|
|
57
105
|
export type ReclaimedMessageType = [
|
|
106
|
+
/** The stream ID, typically formatted as `<timestamp>-<count>` */
|
|
58
107
|
messageId: string,
|
|
108
|
+
/** Details of the message, consisting of a key and its value */
|
|
59
109
|
details: [
|
|
110
|
+
/** Key is always 'message' */
|
|
60
111
|
key: string,
|
|
112
|
+
/** Value is a stringified representation of StreamData */
|
|
61
113
|
value: string
|
|
62
114
|
]
|
|
63
115
|
][];
|
|
116
|
+
/** Configuration parameters for a stream */
|
|
64
117
|
export type StreamConfig = {
|
|
118
|
+
/** Namespace under which the stream operates */
|
|
65
119
|
namespace: string;
|
|
120
|
+
/** Application identifier */
|
|
66
121
|
appId: string;
|
|
122
|
+
/** Globally unique identifier for the stream */
|
|
67
123
|
guid: string;
|
|
124
|
+
/** Role associated with the stream */
|
|
68
125
|
role: StreamRole;
|
|
126
|
+
/** Optional topic for the stream */
|
|
69
127
|
topic?: string;
|
|
128
|
+
/** Delay before a message can be reclaimed, defaults to 60,000 milliseconds */
|
|
70
129
|
reclaimDelay?: number;
|
|
130
|
+
/** Maximum number of reclaims allowed, defaults to 3. Values greater throw an error */
|
|
71
131
|
reclaimCount?: number;
|
|
72
132
|
};
|
package/build/types/stream.js
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.StreamRole = exports.StreamDataType = exports.StreamStatus = void 0;
|
|
4
|
+
/** Enumerated status values for stream operations */
|
|
4
5
|
var StreamStatus;
|
|
5
6
|
(function (StreamStatus) {
|
|
7
|
+
/** Indicates successful completion of the stream operation */
|
|
6
8
|
StreamStatus["SUCCESS"] = "success";
|
|
9
|
+
/** Indicates an error occurred during the stream operation */
|
|
7
10
|
StreamStatus["ERROR"] = "error";
|
|
11
|
+
/** Indicates the stream operation is still pending */
|
|
8
12
|
StreamStatus["PENDING"] = "pending";
|
|
9
13
|
})(StreamStatus = exports.StreamStatus || (exports.StreamStatus = {}));
|
|
10
14
|
var StreamDataType;
|
package/index.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { Durable } from './services/durable';
|
|
2
|
-
import { MeshOSService as MeshOS } from './services/durable/meshos';
|
|
3
2
|
import { HotMeshService as HotMesh } from './services/hotmesh';
|
|
4
3
|
import { HotMeshConfig } from './types/hotmesh';
|
|
5
4
|
|
|
6
|
-
export { Durable, HotMesh, HotMeshConfig
|
|
5
|
+
export { Durable, HotMesh, HotMeshConfig };
|
|
7
6
|
export * as Types from './types';
|
package/modules/enums.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { LogLevel } from "../types/logger";
|
|
|
3
3
|
// HOTMESH SYSTEM
|
|
4
4
|
export const HMSH_LOGLEVEL = process.env.HMSH_LOGLEVEL as LogLevel || 'info';
|
|
5
5
|
|
|
6
|
-
// STATUS CODES
|
|
6
|
+
// HOTMESH STATUS CODES
|
|
7
7
|
export const HMSH_CODE_SUCCESS = 200;
|
|
8
8
|
export const HMSH_CODE_PENDING = 202;
|
|
9
9
|
export const HMSH_CODE_NOTFOUND = 404;
|
|
@@ -11,15 +11,18 @@ export const HMSH_CODE_INTERRUPT = 410;
|
|
|
11
11
|
export const HMSH_CODE_UNKNOWN = 500;
|
|
12
12
|
export const HMSH_CODE_TIMEOUT = 504;
|
|
13
13
|
export const HMSH_CODE_UNACKED = 999;
|
|
14
|
-
|
|
15
|
-
export const
|
|
16
|
-
export const
|
|
17
|
-
export const
|
|
14
|
+
// DURABLE STATUS CODES
|
|
15
|
+
export const HMSH_CODE_DURABLE_SLEEP = 588;
|
|
16
|
+
export const HMSH_CODE_DURABLE_ALL = 589;
|
|
17
|
+
export const HMSH_CODE_DURABLE_CHILD = 590;
|
|
18
|
+
export const HMSH_CODE_DURABLE_PROXY = 591;
|
|
19
|
+
export const HMSH_CODE_DURABLE_WAIT = 595;
|
|
18
20
|
export const HMSH_CODE_DURABLE_TIMEOUT = 596;
|
|
19
21
|
export const HMSH_CODE_DURABLE_MAXED = 597;
|
|
20
22
|
export const HMSH_CODE_DURABLE_FATAL = 598;
|
|
21
23
|
export const HMSH_CODE_DURABLE_RETRYABLE = 599;
|
|
22
24
|
|
|
25
|
+
// HOTMESH MESSAGES
|
|
23
26
|
export const HMSH_STATUS_UNKNOWN = 'unknown';
|
|
24
27
|
|
|
25
28
|
// QUORUM
|
|
@@ -36,8 +39,13 @@ export const HMSH_MAX_RETRIES = parseInt(process.env.HMSH_MAX_RETRIES, 10) || 3;
|
|
|
36
39
|
export const HMSH_MAX_TIMEOUT_MS = parseInt(process.env.HMSH_MAX_TIMEOUT_MS, 10) || 60000;
|
|
37
40
|
export const HMSH_GRADUATED_INTERVAL_MS = parseInt(process.env.HMSH_GRADUATED_INTERVAL_MS, 10) || 5000;
|
|
38
41
|
|
|
39
|
-
|
|
40
|
-
const
|
|
42
|
+
// DURABLE
|
|
43
|
+
export const HMSH_DURABLE_MAX_ATTEMPTS = 3;
|
|
44
|
+
export const HMSH_DURABLE_MAX_INTERVAL = '120s';
|
|
45
|
+
export const HMSH_DURABLE_EXP_BACKOFF = 10;
|
|
46
|
+
|
|
47
|
+
const BASE_BLOCK_DURATION = 10000;
|
|
48
|
+
const TEST_BLOCK_DURATION = 1000;
|
|
41
49
|
export const HMSH_BLOCK_TIME_MS = process.env.HMSH_BLOCK_TIME_MS ? parseInt(process.env.HMSH_BLOCK_TIME_MS, 10) : (process.env.NODE_ENV === 'test' ? TEST_BLOCK_DURATION : BASE_BLOCK_DURATION);
|
|
42
50
|
|
|
43
51
|
export const HMSH_XCLAIM_DELAY_MS = parseInt(process.env.HMSH_XCLAIM_DELAY_MS, 10) || 1000 * 60;
|
|
@@ -48,7 +56,7 @@ export const HMSH_XPENDING_COUNT = parseInt(process.env.HMSH_XPENDING_COUNT, 10)
|
|
|
48
56
|
export const HMSH_EXPIRE_DURATION = parseInt(process.env.HMSH_EXPIRE_DURATION, 10) || 1;
|
|
49
57
|
|
|
50
58
|
const BASE_FIDELITY_SECONDS = 5;
|
|
51
|
-
const TEST_FIDELITY_SECONDS =
|
|
59
|
+
const TEST_FIDELITY_SECONDS = 1;
|
|
52
60
|
export const HMSH_FIDELITY_SECONDS = process.env.HMSH_FIDELITY_SECONDS ? parseInt(process.env.HMSH_FIDELITY_SECONDS, 10) : (process.env.NODE_ENV === 'test' ? TEST_FIDELITY_SECONDS : BASE_FIDELITY_SECONDS);
|
|
53
61
|
|
|
54
62
|
export const HMSH_SCOUT_INTERVAL_SECONDS = parseInt(process.env.HMSH_SCOUT_INTERVAL_SECONDS, 10) || 60;
|
package/modules/errors.ts
CHANGED
|
@@ -4,11 +4,13 @@ import {
|
|
|
4
4
|
HMSH_CODE_DURABLE_MAXED,
|
|
5
5
|
HMSH_CODE_DURABLE_TIMEOUT,
|
|
6
6
|
HMSH_CODE_DURABLE_FATAL,
|
|
7
|
-
HMSH_CODE_DURABLE_INCOMPLETE,
|
|
8
7
|
HMSH_CODE_NOTFOUND,
|
|
9
8
|
HMSH_CODE_DURABLE_RETRYABLE,
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
HMSH_CODE_DURABLE_WAIT,
|
|
10
|
+
HMSH_CODE_DURABLE_PROXY,
|
|
11
|
+
HMSH_CODE_DURABLE_CHILD,
|
|
12
|
+
HMSH_CODE_DURABLE_ALL,
|
|
13
|
+
HMSH_CODE_DURABLE_SLEEP } from "./enums";
|
|
12
14
|
|
|
13
15
|
class GetStateError extends Error {
|
|
14
16
|
jobId: string;
|
|
@@ -24,65 +26,201 @@ class SetStateError extends Error {
|
|
|
24
26
|
}
|
|
25
27
|
}
|
|
26
28
|
|
|
27
|
-
|
|
28
|
-
//if a waitFor set has 'n' items, this can be thrown `n - 1` times
|
|
29
|
-
class DurableIncompleteSignalError extends Error {
|
|
29
|
+
class DurableWaitForError extends Error {
|
|
30
30
|
code: number;
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
signalId: string;
|
|
32
|
+
workflowId: string;
|
|
33
|
+
index: number;
|
|
34
|
+
workflowDimension: string; //hook workflowDimension (e.g., ',0,1,0') (use empty string for `null`)
|
|
35
|
+
constructor(params: {
|
|
36
|
+
signalId: string,
|
|
37
|
+
index: number,
|
|
38
|
+
workflowDimension: string
|
|
39
|
+
workflowId: string;
|
|
40
|
+
}) {
|
|
41
|
+
super(`Durable WaitFor Error [${params.workflowId}]`);
|
|
42
|
+
this.signalId = params.signalId;
|
|
43
|
+
this.index = params.index;
|
|
44
|
+
this.workflowDimension = params.workflowDimension;
|
|
45
|
+
this.code = HMSH_CODE_DURABLE_WAIT;
|
|
34
46
|
}
|
|
35
47
|
}
|
|
36
48
|
|
|
37
|
-
|
|
38
|
-
|
|
49
|
+
class DurableProxyError extends Error {
|
|
50
|
+
activityName: string;
|
|
51
|
+
arguments: string[];
|
|
52
|
+
backoffCoefficient: number;
|
|
39
53
|
code: number;
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
54
|
+
index: number;
|
|
55
|
+
maximumAttempts: number;
|
|
56
|
+
maximumInterval: number;
|
|
57
|
+
originJobId: string | null;
|
|
58
|
+
parentWorkflowId: string;
|
|
59
|
+
workflowDimension: string;
|
|
60
|
+
workflowId: string;
|
|
61
|
+
workflowTopic: string;
|
|
62
|
+
constructor(params: {
|
|
63
|
+
arguments: string[],
|
|
64
|
+
activityName: string,
|
|
65
|
+
backoffCoefficient?: number,
|
|
66
|
+
index: number,
|
|
67
|
+
maximumAttempts?: number,
|
|
68
|
+
maximumInterval?: number,
|
|
69
|
+
originJobId: string | null,
|
|
70
|
+
parentWorkflowId: string,
|
|
71
|
+
workflowDimension: string,
|
|
72
|
+
workflowId: string,
|
|
73
|
+
workflowTopic: string,
|
|
74
|
+
}) {
|
|
75
|
+
super(`Durable Proxy Activity Error [${params.parentWorkflowId}] => [${params.workflowId}]`);
|
|
76
|
+
this.arguments = params.arguments;
|
|
77
|
+
this.workflowId = params.workflowId;
|
|
78
|
+
this.workflowTopic = params.workflowTopic;
|
|
79
|
+
this.parentWorkflowId = params.parentWorkflowId;
|
|
80
|
+
this.originJobId = params.originJobId;
|
|
81
|
+
this.index = params.index;
|
|
82
|
+
this.activityName = params.activityName;
|
|
83
|
+
this.workflowDimension = params.workflowDimension;
|
|
84
|
+
this.backoffCoefficient = params.backoffCoefficient;
|
|
85
|
+
this.maximumAttempts = params.maximumAttempts;
|
|
86
|
+
this.maximumInterval = params.maximumInterval;
|
|
87
|
+
this.code = HMSH_CODE_DURABLE_PROXY;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
class DurableChildError extends Error {
|
|
92
|
+
await: boolean;
|
|
93
|
+
arguments: string[];
|
|
94
|
+
backoffCoefficient: number;
|
|
95
|
+
code: number;
|
|
96
|
+
workflowDimension: string;
|
|
97
|
+
index: number;
|
|
98
|
+
maximumAttempts: number;
|
|
99
|
+
maximumInterval: number;
|
|
100
|
+
originJobId: string | null;
|
|
101
|
+
parentWorkflowId: string;
|
|
102
|
+
workflowId: string;
|
|
103
|
+
workflowTopic: string;
|
|
104
|
+
constructor(params: {
|
|
105
|
+
arguments: string[],
|
|
106
|
+
await?: boolean,
|
|
107
|
+
backoffCoefficient?: number,
|
|
108
|
+
index: number,
|
|
109
|
+
maximumAttempts?: number,
|
|
110
|
+
maximumInterval?: number,
|
|
111
|
+
originJobId: string | null,
|
|
112
|
+
parentWorkflowId: string,
|
|
113
|
+
workflowDimension: string,
|
|
114
|
+
workflowId: string,
|
|
115
|
+
workflowTopic: string,
|
|
116
|
+
}) {
|
|
117
|
+
super(`Durable Child Error [${params.parentWorkflowId}] => [${params.workflowId}]`);
|
|
118
|
+
this.arguments = params.arguments;
|
|
119
|
+
this.workflowId = params.workflowId;
|
|
120
|
+
this.workflowTopic = params.workflowTopic;
|
|
121
|
+
this.parentWorkflowId = params.parentWorkflowId;
|
|
122
|
+
this.originJobId = params.originJobId;
|
|
123
|
+
this.index = params.index;
|
|
124
|
+
this.workflowDimension = params.workflowDimension;
|
|
125
|
+
this.code = HMSH_CODE_DURABLE_CHILD;
|
|
126
|
+
this.await = params.await;
|
|
127
|
+
this.backoffCoefficient = params.backoffCoefficient;
|
|
128
|
+
this.maximumAttempts = params.maximumAttempts;
|
|
129
|
+
this.maximumInterval = params.maximumInterval;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
class DurableWaitForAllError extends Error {
|
|
134
|
+
items: any[];
|
|
135
|
+
code: number;
|
|
136
|
+
workflowDimension: string;
|
|
137
|
+
size: number;
|
|
138
|
+
index: number;
|
|
139
|
+
originJobId: string | null;
|
|
140
|
+
parentWorkflowId: string;
|
|
141
|
+
workflowId: string;
|
|
142
|
+
workflowTopic: string;
|
|
143
|
+
constructor(params: {
|
|
144
|
+
items: string[],
|
|
145
|
+
workflowId: string,
|
|
146
|
+
workflowTopic: string,
|
|
147
|
+
parentWorkflowId: string,
|
|
148
|
+
originJobId: string | null,
|
|
149
|
+
size: number,
|
|
150
|
+
index: number,
|
|
151
|
+
workflowDimension: string
|
|
152
|
+
}) {
|
|
153
|
+
super(`Durable Wait for All Error [${params.parentWorkflowId}] => [${params.workflowId}]`);
|
|
154
|
+
this.items = params.items;
|
|
155
|
+
this.size = params.size;
|
|
156
|
+
this.workflowId = params.workflowId;
|
|
157
|
+
this.workflowTopic = params.workflowTopic;
|
|
158
|
+
this.parentWorkflowId = params.parentWorkflowId;
|
|
159
|
+
this.originJobId = params.originJobId;
|
|
160
|
+
this.index = params.index;
|
|
161
|
+
this.workflowDimension = params.workflowDimension;
|
|
162
|
+
this.code = HMSH_CODE_DURABLE_ALL;
|
|
45
163
|
}
|
|
46
164
|
}
|
|
47
165
|
|
|
48
|
-
class
|
|
166
|
+
class DurableSleepError extends Error {
|
|
167
|
+
workflowId: string;
|
|
49
168
|
code: number;
|
|
50
169
|
duration: number; //seconds
|
|
51
|
-
index: number;
|
|
52
|
-
|
|
53
|
-
constructor(
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
170
|
+
index: number;
|
|
171
|
+
workflowDimension: string; //empty string for null
|
|
172
|
+
constructor(params: {
|
|
173
|
+
duration: number,
|
|
174
|
+
index: number,
|
|
175
|
+
workflowDimension: string,
|
|
176
|
+
workflowId: string,
|
|
177
|
+
}) {
|
|
178
|
+
super(`Durable Sleep Error [${params.workflowId}]`);
|
|
179
|
+
this.duration = params.duration;
|
|
180
|
+
this.workflowId = params.workflowId;
|
|
181
|
+
this.index = params.index;
|
|
182
|
+
this.workflowDimension = params.workflowDimension;
|
|
183
|
+
this.code = HMSH_CODE_DURABLE_SLEEP;
|
|
59
184
|
}
|
|
60
185
|
}
|
|
186
|
+
|
|
61
187
|
class DurableTimeoutError extends Error {
|
|
62
188
|
code: number;
|
|
63
|
-
constructor(message: string) {
|
|
189
|
+
constructor(message: string, stack?: string) {
|
|
64
190
|
super(message);
|
|
191
|
+
if (this.stack) {
|
|
192
|
+
this.stack = stack;
|
|
193
|
+
}
|
|
65
194
|
this.code = HMSH_CODE_DURABLE_TIMEOUT;
|
|
66
195
|
}
|
|
67
196
|
}
|
|
68
197
|
class DurableMaxedError extends Error {
|
|
69
198
|
code: number;
|
|
70
|
-
constructor(message: string) {
|
|
199
|
+
constructor(message: string, stackTrace?: string) {
|
|
71
200
|
super(message);
|
|
201
|
+
if (stackTrace) {
|
|
202
|
+
this.stack = stackTrace;
|
|
203
|
+
}
|
|
72
204
|
this.code = HMSH_CODE_DURABLE_MAXED;
|
|
73
205
|
}
|
|
74
206
|
}
|
|
75
207
|
class DurableFatalError extends Error {
|
|
76
208
|
code: number;
|
|
77
|
-
constructor(message: string) {
|
|
209
|
+
constructor(message: string, stackTrace?: string) {
|
|
78
210
|
super(message);
|
|
211
|
+
if (stackTrace) {
|
|
212
|
+
this.stack = stackTrace;
|
|
213
|
+
}
|
|
79
214
|
this.code = HMSH_CODE_DURABLE_FATAL;
|
|
80
215
|
}
|
|
81
216
|
}
|
|
82
217
|
class DurableRetryError extends Error {
|
|
83
218
|
code: number;
|
|
84
|
-
constructor(message: string) {
|
|
219
|
+
constructor(message: string, stackTrace?: string) {
|
|
85
220
|
super(message);
|
|
221
|
+
if (stackTrace) {
|
|
222
|
+
this.stack = stackTrace;
|
|
223
|
+
}
|
|
86
224
|
this.code = HMSH_CODE_DURABLE_RETRYABLE;
|
|
87
225
|
}
|
|
88
226
|
}
|
|
@@ -100,8 +238,10 @@ class RegisterTimeoutError extends Error {
|
|
|
100
238
|
}
|
|
101
239
|
|
|
102
240
|
class DuplicateJobError extends Error {
|
|
241
|
+
jobId: string;
|
|
103
242
|
constructor(jobId: string) {
|
|
104
243
|
super("Duplicate job");
|
|
244
|
+
this.jobId = jobId;
|
|
105
245
|
this.message = `Duplicate job: ${jobId}`;
|
|
106
246
|
}
|
|
107
247
|
}
|
|
@@ -157,13 +297,15 @@ class CollationError extends Error {
|
|
|
157
297
|
|
|
158
298
|
export {
|
|
159
299
|
CollationError,
|
|
300
|
+
DurableChildError,
|
|
160
301
|
DurableFatalError,
|
|
161
|
-
DurableIncompleteSignalError,
|
|
162
302
|
DurableMaxedError,
|
|
303
|
+
DurableProxyError,
|
|
163
304
|
DurableRetryError,
|
|
164
|
-
|
|
305
|
+
DurableSleepError,
|
|
165
306
|
DurableTimeoutError,
|
|
166
|
-
|
|
307
|
+
DurableWaitForAllError,
|
|
308
|
+
DurableWaitForError,
|
|
167
309
|
DuplicateJobError,
|
|
168
310
|
ExecActivityError,
|
|
169
311
|
GenerationalError,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hotmeshio/hotmesh",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.53",
|
|
4
4
|
"description": "Unbreakable Workflows",
|
|
5
5
|
"main": "./build/index.js",
|
|
6
6
|
"types": "./build/index.d.ts",
|
|
@@ -21,13 +21,16 @@
|
|
|
21
21
|
"start": "ts-node src/index.ts",
|
|
22
22
|
"test": "NODE_ENV=test jest --detectOpenHandles --forceExit --verbose",
|
|
23
23
|
"test:hmsh": "NODE_ENV=test jest ./tests/functional/index.test.ts --detectOpenHandles --verbose",
|
|
24
|
+
"test:pipe": "NODE_ENV=test jest ./tests/unit/services/pipe/index.test.ts --detectOpenHandles --forceExit --verbose",
|
|
24
25
|
"test:compile": "NODE_ENV=test jest ./tests/functional/compile/index.test.ts --detectOpenHandles --forceExit --verbose",
|
|
25
26
|
"test:cycle": "NODE_ENV=test jest ./tests/functional/cycle/index.test.ts --detectOpenHandles --forceExit --verbose",
|
|
26
27
|
"test:trigger": "NODE_ENV=test jest ./tests/unit/services/activities/trigger.test.ts --detectOpenHandles --forceExit --verbose",
|
|
27
28
|
"test:connect": "NODE_ENV=test jest ./tests/unit/services/connector/index.test.ts --detectOpenHandles --forceExit --verbose",
|
|
28
29
|
"test:connect:redis": "NODE_ENV=test jest ./tests/unit/services/connector/clients/redis.test.ts --detectOpenHandles --forceExit --verbose",
|
|
29
30
|
"test:connect:ioredis": "NODE_ENV=test jest ./tests/unit/services/connector/clients/ioredis.test.ts --detectOpenHandles --forceExit --verbose",
|
|
31
|
+
"test:functional": "NODE_ENV=test jest ./tests/functional/*/index.test.ts --detectOpenHandles --forceExit --verbose",
|
|
30
32
|
"test:emit": "NODE_ENV=test jest ./tests/functional/emit/index.test.ts --detectOpenHandles --forceExit --verbose",
|
|
33
|
+
"test:reentrant": "NODE_ENV=test jest ./tests/functional/reentrant/index.test.ts --detectOpenHandles --forceExit --verbose",
|
|
31
34
|
"test:await": "NODE_ENV=test jest ./tests/functional/awaiter/index.test.ts --detectOpenHandles --forceExit --verbose",
|
|
32
35
|
"test:hook": "NODE_ENV=test jest ./tests/functional/hook/index.test.ts --detectOpenHandles --forceExit --verbose",
|
|
33
36
|
"test:signal": "NODE_ENV=test jest ./tests/functional/signal/index.test.ts --detectOpenHandles --forceExit --verbose",
|
|
@@ -46,13 +49,15 @@
|
|
|
46
49
|
"test:sub:redis": "NODE_ENV=test jest ./tests/functional/sub/clients/redis.test.ts --detectOpenHandles --forceExit --verbose",
|
|
47
50
|
"test:sub:ioredis": "NODE_ENV=test jest ./tests/functional/sub/clients/ioredis.test.ts --detectOpenHandles --forceExit --verbose",
|
|
48
51
|
"test:durable": "NODE_ENV=test jest ./tests/durable/*/index.test.ts --detectOpenHandles --forceExit --verbose",
|
|
49
|
-
"test:durable:
|
|
52
|
+
"test:durable:basic": "NODE_ENV=test jest ./tests/durable/basic/index.test.ts --detectOpenHandles --forceExit --verbose",
|
|
53
|
+
"test:durable:collision": "NODE_ENV=test jest ./tests/durable/collision/index.test.ts --detectOpenHandles --forceExit --verbose",
|
|
50
54
|
"test:durable:hello": "NODE_ENV=test jest ./tests/durable/helloworld/index.test.ts --detectOpenHandles --forceExit --verbose",
|
|
51
55
|
"test:durable:interrupt": "NODE_ENV=test jest ./tests/durable/interrupt/index.test.ts --detectOpenHandles --forceExit --verbose",
|
|
52
56
|
"test:durable:goodbye": "NODE_ENV=test jest ./tests/durable/goodbye/index.test.ts --detectOpenHandles --forceExit --verbose",
|
|
53
57
|
"test:durable:hook": "NODE_ENV=test jest ./tests/durable/hook/index.test.ts --detectOpenHandles --forceExit --verbose",
|
|
54
58
|
"test:durable:retry": "NODE_ENV=test jest ./tests/durable/retry/index.test.ts --detectOpenHandles --forceExit --verbose",
|
|
55
59
|
"test:durable:fatal": "NODE_ENV=test jest ./tests/durable/fatal/index.test.ts --detectOpenHandles --forceExit --verbose",
|
|
60
|
+
"test:durable:unknown": "NODE_ENV=test jest ./tests/durable/unknown/index.test.ts --detectOpenHandles --forceExit --verbose",
|
|
56
61
|
"test:durable:sleep": "NODE_ENV=test jest ./tests/durable/sleep/index.test.ts --detectOpenHandles --forceExit --verbose",
|
|
57
62
|
"test:durable:signal": "NODE_ENV=test jest ./tests/durable/signal/index.test.ts --detectOpenHandles --forceExit --verbose",
|
|
58
63
|
"test:durable:loopactivity": "NODE_ENV=test jest ./tests/durable/loopactivity/index.test.ts --detectOpenHandles --forceExit --verbose",
|
|
@@ -143,19 +143,19 @@ class Activity {
|
|
|
143
143
|
this.transitionAdjacent(multiResponse, telemetry);
|
|
144
144
|
} catch (error) {
|
|
145
145
|
if (error instanceof CollationError) {
|
|
146
|
-
this.logger.info('process-event-inactive-error', { error });
|
|
146
|
+
this.logger.info('process-event-inactive-error', { ...error });
|
|
147
147
|
return;
|
|
148
148
|
} else if (error instanceof InactiveJobError) {
|
|
149
|
-
this.logger.info('process-event-inactive-job-error', { error });
|
|
149
|
+
this.logger.info('process-event-inactive-job-error', { ...error });
|
|
150
150
|
return;
|
|
151
151
|
} else if (error instanceof GenerationalError) {
|
|
152
|
-
this.logger.info('process-event-generational-job-error', { error });
|
|
152
|
+
this.logger.info('process-event-generational-job-error', { ...error });
|
|
153
153
|
return;
|
|
154
154
|
} else if (error instanceof GetStateError) {
|
|
155
|
-
this.logger.info('process-event-get-job-error', {
|
|
155
|
+
this.logger.info('process-event-get-job-error', { ...error });
|
|
156
156
|
return;
|
|
157
157
|
}
|
|
158
|
-
this.logger.error('activity-process-event-error', { error });
|
|
158
|
+
this.logger.error('activity-process-event-error', { ...error, message: error.message, stack: error.stack, name: error.name });
|
|
159
159
|
telemetry && telemetry.setActivityError(error.message);
|
|
160
160
|
throw error;
|
|
161
161
|
} finally {
|
|
@@ -191,6 +191,10 @@ class Activity {
|
|
|
191
191
|
async processError(telemetry: TelemetryService, type: string): Promise<MultiResponseFlags> {
|
|
192
192
|
this.bindActivityError(this.data);
|
|
193
193
|
this.adjacencyList = await this.filterAdjacent();
|
|
194
|
+
if (!this.adjacencyList.length) {
|
|
195
|
+
this.bindJobError(this.data);
|
|
196
|
+
}
|
|
197
|
+
this.mapJobData();
|
|
194
198
|
const multi = this.store.getMulti();
|
|
195
199
|
await this.setState(multi);
|
|
196
200
|
await CollatorService.notarizeCompletion(this, multi);
|
|
@@ -205,7 +209,7 @@ class Activity {
|
|
|
205
209
|
const attrs: StringScalarType = { 'app.job.jss': jobStatus };
|
|
206
210
|
//adjacencyList membership has already been set at this point (according to activity status)
|
|
207
211
|
const messageIds = await this.transition(this.adjacencyList, jobStatus);
|
|
208
|
-
if (messageIds
|
|
212
|
+
if (messageIds?.length) {
|
|
209
213
|
attrs['app.activity.mids'] = messageIds.join(',')
|
|
210
214
|
}
|
|
211
215
|
telemetry.setActivityAttributes(attrs);
|
|
@@ -223,7 +227,30 @@ class Activity {
|
|
|
223
227
|
mapJobData(): void {
|
|
224
228
|
if(this.config.job?.maps) {
|
|
225
229
|
const mapper = new MapperService(this.config.job.maps, this.context);
|
|
226
|
-
|
|
230
|
+
const output = mapper.mapRules();
|
|
231
|
+
if (output) {
|
|
232
|
+
for (const key in output) {
|
|
233
|
+
const f1 = key.indexOf('[');
|
|
234
|
+
//keys with array notation suffix `somekey[]` represent
|
|
235
|
+
//dynamically-keyed mappings whose `value` must be moved to the output.
|
|
236
|
+
//The `value` must be an object with keys appropriate to the
|
|
237
|
+
//notation type: `somekey[0] (array)`, `somekey[-] (mark)`, OR `somekey[_] (search)`
|
|
238
|
+
if (f1 > -1) {
|
|
239
|
+
const amount = key.substring(f1 + 1).split(']')[0];
|
|
240
|
+
if (!isNaN(Number(amount))) {
|
|
241
|
+
const left = key.substring(0, f1);
|
|
242
|
+
output[left] = output[key];
|
|
243
|
+
delete output[key];
|
|
244
|
+
} else if (amount === '-' || amount === '_') {
|
|
245
|
+
const obj = output[key];
|
|
246
|
+
Object.keys(obj).forEach((newKey) => {
|
|
247
|
+
output[newKey] = obj[newKey];
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
this.context.data = output;
|
|
227
254
|
}
|
|
228
255
|
}
|
|
229
256
|
|
|
@@ -249,10 +276,22 @@ class Activity {
|
|
|
249
276
|
//set timeout in support of hook and/or duplex
|
|
250
277
|
}
|
|
251
278
|
|
|
279
|
+
/**
|
|
280
|
+
* Any StreamMessage with a status of ERROR is bound to the activity
|
|
281
|
+
*/
|
|
252
282
|
bindActivityError(data: Record<string, unknown>): void {
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
283
|
+
const md = this.context[this.metadata.aid].output.metadata;
|
|
284
|
+
md.err = JSON.stringify(this.data);
|
|
285
|
+
//(temporary...useful for mapping error parts in the app.yaml)
|
|
286
|
+
md.$error = { ...data, is_stream_error: true };
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* unhandled activity errors (activities that return an ERROR StreamMessage
|
|
291
|
+
* status and have no adjacent children to transition to) are bound to the job
|
|
292
|
+
*/
|
|
293
|
+
bindJobError(data: Record<string, unknown>): void {
|
|
294
|
+
this.context.metadata.err = JSON.stringify({ ...data, is_stream_error: true });
|
|
256
295
|
}
|
|
257
296
|
|
|
258
297
|
async getTriggerConfig(): Promise<ActivityType> {
|
|
@@ -322,9 +361,9 @@ class Activity {
|
|
|
322
361
|
if (this.status === StreamStatus.ERROR) {
|
|
323
362
|
self.output.metadata.err = JSON.stringify(this.data);
|
|
324
363
|
}
|
|
325
|
-
|
|
326
|
-
self.output.metadata.ac =
|
|
327
|
-
|
|
364
|
+
const ts = formatISODate(new Date());
|
|
365
|
+
self.output.metadata.ac = ts;
|
|
366
|
+
self.output.metadata.au = ts;
|
|
328
367
|
self.output.metadata.atp = this.config.type;
|
|
329
368
|
if (this.config.subtype) {
|
|
330
369
|
self.output.metadata.stp = this.config.subtype;
|
|
@@ -344,6 +383,11 @@ class Activity {
|
|
|
344
383
|
state[path] = value;
|
|
345
384
|
}
|
|
346
385
|
}
|
|
386
|
+
for (let key in this.context?.data ?? {}) {
|
|
387
|
+
if (key.startsWith('-') || key.startsWith('_')) {
|
|
388
|
+
state[key] = this.context.data[key];
|
|
389
|
+
}
|
|
390
|
+
}
|
|
347
391
|
TelemetryService.bindJobTelemetryToState(state, this.config, this.context);
|
|
348
392
|
}
|
|
349
393
|
|
|
@@ -398,7 +442,7 @@ class Activity {
|
|
|
398
442
|
let { dad, jid } = this.context.metadata;
|
|
399
443
|
const dIds = CollatorService.getDimensionsById([...this.config.ancestors, this.metadata.aid], dad || '');
|
|
400
444
|
//`state` is a unidimensional hash; context is a tree
|
|
401
|
-
const [state,
|
|
445
|
+
const [state, _status] = await this.store.getState(jid, consumes, dIds);
|
|
402
446
|
this.context = restoreHierarchy(state) as JobState;
|
|
403
447
|
this.assertGenerationalId(this.context?.metadata?.gid, gid);
|
|
404
448
|
this.initDimensionalAddress(dad);
|
|
@@ -443,6 +487,11 @@ class Activity {
|
|
|
443
487
|
if (!self.hook) {
|
|
444
488
|
self.hook = { };
|
|
445
489
|
}
|
|
490
|
+
if (!self.output.metadata) {
|
|
491
|
+
self.output.metadata = { };
|
|
492
|
+
}
|
|
493
|
+
//prebind the updated timestamp (mappings need the time)
|
|
494
|
+
self.output.metadata.au = formatISODate(new Date());
|
|
446
495
|
context['$self'] = self;
|
|
447
496
|
context['$job'] = context; //NEVER call STRINGIFY! (now circular)
|
|
448
497
|
return context as JobState;
|