@hotmeshio/hotmesh 0.1.15 → 0.1.16
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 +623 -209
- package/build/index.d.ts +14 -3
- package/build/index.js +17 -4
- package/build/modules/enums.d.ts +12 -12
- package/build/modules/enums.js +15 -25
- package/build/modules/errors.d.ts +16 -16
- package/build/modules/errors.js +28 -28
- package/build/modules/key.d.ts +0 -37
- package/build/modules/key.js +4 -45
- package/build/modules/utils.d.ts +7 -15
- package/build/modules/utils.js +21 -44
- package/build/package.json +18 -15
- package/build/services/activities/activity.d.ts +0 -31
- package/build/services/activities/activity.js +1 -50
- package/build/services/activities/await.js +0 -4
- package/build/services/activities/cycle.d.ts +0 -7
- package/build/services/activities/cycle.js +1 -16
- package/build/services/activities/hook.d.ts +0 -6
- package/build/services/activities/hook.js +2 -12
- package/build/services/activities/interrupt.js +0 -8
- package/build/services/activities/signal.d.ts +0 -6
- package/build/services/activities/signal.js +0 -15
- package/build/services/activities/trigger.d.ts +4 -5
- package/build/services/activities/trigger.js +22 -16
- package/build/services/activities/worker.js +0 -4
- package/build/services/collator/index.d.ts +0 -70
- package/build/services/collator/index.js +1 -91
- package/build/services/compiler/deployer.js +6 -38
- package/build/services/compiler/index.d.ts +0 -15
- package/build/services/compiler/index.js +0 -20
- package/build/services/compiler/validator.d.ts +0 -3
- package/build/services/compiler/validator.js +0 -25
- package/build/services/connector/clients/ioredis.js +0 -2
- package/build/services/connector/clients/redis.js +0 -2
- package/build/services/connector/index.js +0 -2
- package/build/services/engine/index.d.ts +1 -10
- package/build/services/engine/index.js +1 -48
- package/build/services/exporter/index.d.ts +0 -27
- package/build/services/exporter/index.js +0 -33
- package/build/services/hotmesh/index.d.ts +8 -4
- package/build/services/hotmesh/index.js +20 -19
- package/build/services/logger/index.js +0 -2
- package/build/services/mapper/index.d.ts +0 -14
- package/build/services/mapper/index.js +0 -14
- package/build/services/meshcall/index.d.ts +21 -0
- package/build/services/meshcall/index.js +202 -0
- package/build/services/meshcall/schemas/factory.d.ts +2 -0
- package/build/services/meshcall/schemas/factory.js +179 -0
- package/build/services/meshdata/index.d.ts +75 -0
- package/build/services/meshdata/index.js +541 -0
- package/build/services/meshflow/client.d.ts +18 -0
- package/build/services/{durable → meshflow}/client.js +9 -40
- package/build/services/{durable → meshflow}/connection.d.ts +2 -1
- package/build/services/{durable → meshflow}/connection.js +1 -0
- package/build/services/meshflow/exporter.d.ts +29 -0
- package/build/services/{durable → meshflow}/exporter.js +0 -29
- package/build/services/meshflow/handle.d.ts +22 -0
- package/build/services/{durable → meshflow}/handle.js +0 -46
- package/build/services/meshflow/index.d.ts +17 -0
- package/build/services/meshflow/index.js +23 -0
- package/build/services/meshflow/schemas/factory.d.ts +4 -0
- package/build/services/{durable → meshflow}/schemas/factory.js +2 -30
- package/build/services/meshflow/search.d.ts +23 -0
- package/build/services/{durable → meshflow}/search.js +0 -99
- package/build/services/{durable → meshflow}/worker.d.ts +3 -2
- package/build/services/{durable → meshflow}/worker.js +23 -39
- package/build/services/meshflow/workflow.d.ts +27 -0
- package/build/services/{durable → meshflow}/workflow.js +27 -169
- package/build/services/pipe/functions/date.d.ts +0 -7
- package/build/services/pipe/functions/date.js +0 -7
- package/build/services/pipe/functions/math.js +0 -2
- package/build/services/pipe/index.d.ts +0 -15
- package/build/services/pipe/index.js +2 -23
- package/build/services/quorum/index.d.ts +1 -7
- package/build/services/quorum/index.js +0 -21
- package/build/services/reporter/index.d.ts +0 -5
- package/build/services/reporter/index.js +0 -9
- package/build/services/router/index.d.ts +0 -9
- package/build/services/router/index.js +2 -30
- package/build/services/serializer/index.js +6 -23
- package/build/services/store/cache.d.ts +0 -19
- package/build/services/store/cache.js +0 -19
- package/build/services/store/clients/ioredis.d.ts +0 -6
- package/build/services/store/clients/ioredis.js +0 -7
- package/build/services/store/clients/redis.d.ts +0 -6
- package/build/services/store/clients/redis.js +0 -6
- package/build/services/store/index.d.ts +0 -55
- package/build/services/store/index.js +14 -87
- package/build/services/stream/clients/ioredis.js +1 -4
- package/build/services/task/index.d.ts +0 -9
- package/build/services/task/index.js +0 -31
- package/build/services/telemetry/index.d.ts +0 -7
- package/build/services/telemetry/index.js +1 -13
- package/build/services/worker/index.d.ts +1 -4
- package/build/services/worker/index.js +0 -6
- package/build/types/activity.d.ts +0 -81
- package/build/types/error.d.ts +5 -5
- package/build/types/exporter.d.ts +1 -14
- package/build/types/hotmesh.d.ts +4 -12
- package/build/types/hotmesh.js +0 -3
- package/build/types/index.d.ts +5 -3
- package/build/types/index.js +1 -1
- package/build/types/job.d.ts +1 -95
- package/build/types/meshcall.d.ts +54 -0
- package/build/types/meshdata.d.ts +59 -0
- package/build/types/meshdata.js +2 -0
- package/build/types/meshflow.d.ts +202 -0
- package/build/types/meshflow.js +2 -0
- package/build/types/pipe.d.ts +0 -65
- package/build/types/quorum.d.ts +0 -12
- package/build/types/redis.d.ts +0 -6
- package/build/types/stream.d.ts +0 -59
- package/build/types/stream.js +0 -4
- package/index.ts +22 -3
- package/package.json +18 -15
- package/typedoc.json +38 -0
- package/types/error.ts +5 -5
- package/types/exporter.ts +1 -1
- package/types/hotmesh.ts +3 -2
- package/types/index.ts +25 -7
- package/types/job.ts +19 -1
- package/types/meshcall.ts +123 -0
- package/types/meshdata.ts +273 -0
- package/types/{durable.ts → meshflow.ts} +33 -9
- package/build/services/durable/client.d.ts +0 -49
- package/build/services/durable/exporter.d.ts +0 -51
- package/build/services/durable/handle.d.ts +0 -58
- package/build/services/durable/index.d.ts +0 -19
- package/build/services/durable/index.js +0 -25
- package/build/services/durable/schemas/factory.d.ts +0 -33
- package/build/services/durable/search.d.ts +0 -120
- package/build/services/durable/workflow.d.ts +0 -143
- package/build/types/durable.d.ts +0 -467
- /package/build/types/{durable.js → meshcall.js} +0 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { ILogger } from '../logger';
|
|
2
|
+
import { StoreService } from '../store';
|
|
3
|
+
import { ExportOptions, MeshFlowJobExport, TimelineType, TransitionType, ExportFields } from '../../types/exporter';
|
|
4
|
+
import { RedisClient, RedisMulti } from '../../types/redis';
|
|
5
|
+
import { StringStringType, Symbols } from '../../types/serializer';
|
|
6
|
+
declare class ExporterService {
|
|
7
|
+
appId: string;
|
|
8
|
+
logger: ILogger;
|
|
9
|
+
store: StoreService<RedisClient, RedisMulti>;
|
|
10
|
+
symbols: Promise<Symbols> | Symbols;
|
|
11
|
+
private static symbols;
|
|
12
|
+
constructor(appId: string, store: StoreService<RedisClient, RedisMulti>, logger: ILogger);
|
|
13
|
+
export(jobId: string, options?: ExportOptions): Promise<MeshFlowJobExport>;
|
|
14
|
+
inflate(jobHash: StringStringType, options: ExportOptions): MeshFlowJobExport;
|
|
15
|
+
resolveValue(raw: string, withValues: boolean): Record<string, any> | string | number | null;
|
|
16
|
+
inflateKey(key: string): string;
|
|
17
|
+
filterFields(fullObject: MeshFlowJobExport, block?: ExportFields[], allow?: ExportFields[]): Partial<MeshFlowJobExport>;
|
|
18
|
+
inflateTransition(match: RegExpMatchArray, value: string, transitionsObject: Record<string, TransitionType>): void;
|
|
19
|
+
sortEntriesByCreated(obj: {
|
|
20
|
+
[key: string]: TransitionType;
|
|
21
|
+
}): TransitionType[];
|
|
22
|
+
keyToObject(key: string): {
|
|
23
|
+
index: number;
|
|
24
|
+
dimension?: string;
|
|
25
|
+
secondary?: number;
|
|
26
|
+
};
|
|
27
|
+
sortParts(parts: TimelineType[]): TimelineType[];
|
|
28
|
+
}
|
|
29
|
+
export { ExporterService };
|
|
@@ -9,10 +9,6 @@ class ExporterService {
|
|
|
9
9
|
this.logger = logger;
|
|
10
10
|
this.store = store;
|
|
11
11
|
}
|
|
12
|
-
/**
|
|
13
|
-
* Convert the job hash from its compiles format into a DurableJobExport object with
|
|
14
|
-
* facets that describe the workflow in terms relevant to narrative storytelling.
|
|
15
|
-
*/
|
|
16
12
|
async export(jobId, options = {}) {
|
|
17
13
|
if (!ExporterService.symbols.has(this.appId)) {
|
|
18
14
|
const symbols = this.store.getAllSymbols();
|
|
@@ -22,12 +18,6 @@ class ExporterService {
|
|
|
22
18
|
const jobExport = this.inflate(jobData, options);
|
|
23
19
|
return jobExport;
|
|
24
20
|
}
|
|
25
|
-
/**
|
|
26
|
-
* Inflates the job data from Redis into a DurableJobExport object
|
|
27
|
-
* @param jobHash - the job data from Redis
|
|
28
|
-
* @param dependencyList - the list of dependencies for the job
|
|
29
|
-
* @returns - the inflated job data
|
|
30
|
-
*/
|
|
31
21
|
inflate(jobHash, options) {
|
|
32
22
|
const timeline = [];
|
|
33
23
|
const state = {};
|
|
@@ -37,15 +27,12 @@ class ExporterService {
|
|
|
37
27
|
Object.entries(jobHash).forEach(([key, value]) => {
|
|
38
28
|
const match = key.match(regex);
|
|
39
29
|
if (match) {
|
|
40
|
-
//transitions
|
|
41
30
|
this.inflateTransition(match, value, transitionsObject);
|
|
42
31
|
}
|
|
43
32
|
else if (key.startsWith('_')) {
|
|
44
|
-
//data
|
|
45
33
|
data[key.substring(1)] = value;
|
|
46
34
|
}
|
|
47
35
|
else if (key.startsWith('-')) {
|
|
48
|
-
//timeline
|
|
49
36
|
const keyParts = this.keyToObject(key);
|
|
50
37
|
timeline.push({
|
|
51
38
|
...keyParts,
|
|
@@ -54,7 +41,6 @@ class ExporterService {
|
|
|
54
41
|
});
|
|
55
42
|
}
|
|
56
43
|
else if (key.length === 3) {
|
|
57
|
-
//state
|
|
58
44
|
state[this.inflateKey(key)] = serializer_1.SerializerService.fromString(value);
|
|
59
45
|
}
|
|
60
46
|
});
|
|
@@ -81,12 +67,6 @@ class ExporterService {
|
|
|
81
67
|
}
|
|
82
68
|
return resolved;
|
|
83
69
|
}
|
|
84
|
-
/**
|
|
85
|
-
* Inflates the key from Redis, 3-character symbol
|
|
86
|
-
* into a human-readable JSON path, reflecting the
|
|
87
|
-
* tree-like structure of the unidimensional Hash
|
|
88
|
-
* @private
|
|
89
|
-
*/
|
|
90
70
|
inflateKey(key) {
|
|
91
71
|
const symbols = ExporterService.symbols.get(this.appId);
|
|
92
72
|
if (key in symbols) {
|
|
@@ -124,7 +104,6 @@ class ExporterService {
|
|
|
124
104
|
const activity = parts[0];
|
|
125
105
|
const isCreate = path.endsWith('/output/metadata/ac');
|
|
126
106
|
const isUpdate = path.endsWith('/output/metadata/au');
|
|
127
|
-
//for now only export activity start/stop; activity data would also be interesting
|
|
128
107
|
if (isCreate || isUpdate) {
|
|
129
108
|
const targetName = `${activity},${dimensions}`;
|
|
130
109
|
const target = transitionsObject[targetName];
|
|
@@ -148,9 +127,6 @@ class ExporterService {
|
|
|
148
127
|
});
|
|
149
128
|
return entriesArray;
|
|
150
129
|
}
|
|
151
|
-
/**
|
|
152
|
-
* marker names are overloaded with details like sequence, type, etc
|
|
153
|
-
*/
|
|
154
130
|
keyToObject(key) {
|
|
155
131
|
function extractDimension(label) {
|
|
156
132
|
const parts = label.split(',');
|
|
@@ -161,14 +137,12 @@ class ExporterService {
|
|
|
161
137
|
}
|
|
162
138
|
const parts = key.split('-');
|
|
163
139
|
if (parts.length === 4) {
|
|
164
|
-
//-proxy-5- -search-1-1-
|
|
165
140
|
return {
|
|
166
141
|
index: parseInt(parts[2], 10),
|
|
167
142
|
dimension: extractDimension(parts[1]),
|
|
168
143
|
};
|
|
169
144
|
}
|
|
170
145
|
else {
|
|
171
|
-
//-search,0,0-1-1- -proxy,0,0-1-
|
|
172
146
|
return {
|
|
173
147
|
index: parseInt(parts[2], 10),
|
|
174
148
|
secondary: parseInt(parts[3], 10),
|
|
@@ -176,9 +150,6 @@ class ExporterService {
|
|
|
176
150
|
};
|
|
177
151
|
}
|
|
178
152
|
}
|
|
179
|
-
/**
|
|
180
|
-
* idem list has a complicated sort order based on indexes and dimensions
|
|
181
|
-
*/
|
|
182
153
|
sortParts(parts) {
|
|
183
154
|
return parts.sort((a, b) => {
|
|
184
155
|
const { dimension: aDim, index: aIdx, secondary: aSec } = a;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { HotMesh } from '../hotmesh';
|
|
2
|
+
import { MeshFlowJobExport, ExportOptions } from '../../types/exporter';
|
|
3
|
+
import { JobInterruptOptions } from '../../types/job';
|
|
4
|
+
import { StreamError } from '../../types/stream';
|
|
5
|
+
import { ExporterService } from './exporter';
|
|
6
|
+
export declare class WorkflowHandleService {
|
|
7
|
+
exporter: ExporterService;
|
|
8
|
+
hotMesh: HotMesh;
|
|
9
|
+
workflowTopic: string;
|
|
10
|
+
workflowId: string;
|
|
11
|
+
constructor(hotMesh: HotMesh, workflowTopic: string, workflowId: string);
|
|
12
|
+
export(options?: ExportOptions): Promise<MeshFlowJobExport>;
|
|
13
|
+
signal(signalId: string, data: Record<any, any>): Promise<void>;
|
|
14
|
+
state(metadata?: boolean): Promise<Record<string, any>>;
|
|
15
|
+
queryState(fields: string[]): Promise<Record<string, any>>;
|
|
16
|
+
status(): Promise<number>;
|
|
17
|
+
interrupt(options?: JobInterruptOptions): Promise<string>;
|
|
18
|
+
result<T>(config?: {
|
|
19
|
+
state?: boolean;
|
|
20
|
+
throwOnError?: boolean;
|
|
21
|
+
}): Promise<T | StreamError>;
|
|
22
|
+
}
|
|
@@ -12,24 +12,12 @@ class WorkflowHandleService {
|
|
|
12
12
|
async export(options) {
|
|
13
13
|
return this.exporter.export(this.workflowId, options);
|
|
14
14
|
}
|
|
15
|
-
/**
|
|
16
|
-
* Sends a signal to the workflow. This is a way to send
|
|
17
|
-
* a message to a workflow that is paused due to having
|
|
18
|
-
* executed `Durable.workflow.waitFor`. The workflow
|
|
19
|
-
* will awaken if no other signals are pending.
|
|
20
|
-
*/
|
|
21
15
|
async signal(signalId, data) {
|
|
22
16
|
await this.hotMesh.hook(`${this.hotMesh.appId}.wfs.signal`, {
|
|
23
17
|
id: signalId,
|
|
24
18
|
data,
|
|
25
19
|
});
|
|
26
20
|
}
|
|
27
|
-
/**
|
|
28
|
-
* Returns the job state of the workflow. If the workflow has completed
|
|
29
|
-
* this is also the job output. If the workflow is still running, this
|
|
30
|
-
* is the current state of the job, but it may change depending upon
|
|
31
|
-
* the activities that remain.
|
|
32
|
-
*/
|
|
33
21
|
async state(metadata = false) {
|
|
34
22
|
const state = await this.hotMesh.getState(`${this.hotMesh.appId}.execute`, this.workflowId);
|
|
35
23
|
if (!state.data && state.metadata.err) {
|
|
@@ -37,56 +25,25 @@ class WorkflowHandleService {
|
|
|
37
25
|
}
|
|
38
26
|
return metadata ? state : state.data;
|
|
39
27
|
}
|
|
40
|
-
/**
|
|
41
|
-
* Returns the current search state of the workflow. This is
|
|
42
|
-
* different than the job state or individual activity state.
|
|
43
|
-
* Search state represents name/value pairs that were added
|
|
44
|
-
* to the workflow. As the workflow is stored in a Redis hash,
|
|
45
|
-
* this is a way to store additional data that is indexed
|
|
46
|
-
* and searchable using the RediSearch module.
|
|
47
|
-
*/
|
|
48
28
|
async queryState(fields) {
|
|
49
29
|
return await this.hotMesh.getQueryState(this.workflowId, fields);
|
|
50
30
|
}
|
|
51
|
-
/**
|
|
52
|
-
* Returns the current status of the workflow. This is a semaphore
|
|
53
|
-
* value that represents the current state of the workflow, where
|
|
54
|
-
* 0 is complete and a negative value represents that the flow was
|
|
55
|
-
* interrupted.
|
|
56
|
-
*/
|
|
57
31
|
async status() {
|
|
58
32
|
return await this.hotMesh.getStatus(this.workflowId);
|
|
59
33
|
}
|
|
60
|
-
/**
|
|
61
|
-
* Interrupts a running workflow. Standard Job Completion tasks will
|
|
62
|
-
* run. Subscribers will be notified and the job hash will be expired.
|
|
63
|
-
*/
|
|
64
34
|
async interrupt(options) {
|
|
65
35
|
return await this.hotMesh.interrupt(`${this.hotMesh.appId}.execute`, this.workflowId, options);
|
|
66
36
|
}
|
|
67
|
-
/**
|
|
68
|
-
* Waits for the workflow to complete and returns the result. If
|
|
69
|
-
* the workflow response includes an error, this method will rethrow
|
|
70
|
-
* the error, including the stack trace if available.
|
|
71
|
-
* Wrap calls in a try/catch as necessary to avoid unhandled exceptions.
|
|
72
|
-
*/
|
|
73
37
|
async result(config) {
|
|
74
38
|
const topic = `${this.hotMesh.appId}.executed.${this.workflowId}`;
|
|
75
39
|
let isResolved = false;
|
|
76
40
|
return new Promise(async (resolve, reject) => {
|
|
77
|
-
/**
|
|
78
|
-
* rejects/resolves the promise based on the `throwOnError`
|
|
79
|
-
* default behavior is to throw if error
|
|
80
|
-
*/
|
|
81
41
|
const safeReject = (err) => {
|
|
82
42
|
if (config?.throwOnError === false) {
|
|
83
43
|
return resolve(err);
|
|
84
44
|
}
|
|
85
45
|
reject(err);
|
|
86
46
|
};
|
|
87
|
-
/**
|
|
88
|
-
* Common completion function that unsubscribes from the topic/returns
|
|
89
|
-
*/
|
|
90
47
|
const complete = async (response, err) => {
|
|
91
48
|
if (isResolved)
|
|
92
49
|
return;
|
|
@@ -109,7 +66,6 @@ class WorkflowHandleService {
|
|
|
109
66
|
}
|
|
110
67
|
resolve(response);
|
|
111
68
|
};
|
|
112
|
-
//more expensive; fetches the entire job, not just the `status`
|
|
113
69
|
if (config?.state) {
|
|
114
70
|
const state = await this.hotMesh.getState(`${this.hotMesh.appId}.execute`, this.workflowId);
|
|
115
71
|
if (state?.data?.done && !state.data?.$error) {
|
|
@@ -122,7 +78,6 @@ class WorkflowHandleService {
|
|
|
122
78
|
return complete(null, JSON.parse(state.metadata.err));
|
|
123
79
|
}
|
|
124
80
|
}
|
|
125
|
-
//subscribe to 'done' topic
|
|
126
81
|
this.hotMesh.sub(topic, async (_topic, state) => {
|
|
127
82
|
this.hotMesh.unsub(topic);
|
|
128
83
|
if (state.data.done && !state.data?.$error) {
|
|
@@ -136,7 +91,6 @@ class WorkflowHandleService {
|
|
|
136
91
|
return await complete(null, error);
|
|
137
92
|
}
|
|
138
93
|
});
|
|
139
|
-
//check state in case completed during wiring
|
|
140
94
|
const status = await this.hotMesh.getStatus(this.workflowId);
|
|
141
95
|
if (status <= 0) {
|
|
142
96
|
await complete();
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ContextType } from '../../types/meshflow';
|
|
2
|
+
import { ClientService } from './client';
|
|
3
|
+
import { ConnectionService } from './connection';
|
|
4
|
+
import { Search } from './search';
|
|
5
|
+
import { WorkerService } from './worker';
|
|
6
|
+
import { WorkflowService } from './workflow';
|
|
7
|
+
declare class MeshFlowClass {
|
|
8
|
+
constructor();
|
|
9
|
+
static Client: typeof ClientService;
|
|
10
|
+
static Connection: typeof ConnectionService;
|
|
11
|
+
static Search: typeof Search;
|
|
12
|
+
static Worker: typeof WorkerService;
|
|
13
|
+
static workflow: typeof WorkflowService;
|
|
14
|
+
static shutdown(): Promise<void>;
|
|
15
|
+
}
|
|
16
|
+
export { MeshFlowClass as MeshFlow };
|
|
17
|
+
export type { ContextType };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MeshFlow = void 0;
|
|
4
|
+
const hotmesh_1 = require("../hotmesh");
|
|
5
|
+
const client_1 = require("./client");
|
|
6
|
+
const connection_1 = require("./connection");
|
|
7
|
+
const search_1 = require("./search");
|
|
8
|
+
const worker_1 = require("./worker");
|
|
9
|
+
const workflow_1 = require("./workflow");
|
|
10
|
+
class MeshFlowClass {
|
|
11
|
+
constructor() { }
|
|
12
|
+
static async shutdown() {
|
|
13
|
+
await MeshFlowClass.Client.shutdown();
|
|
14
|
+
await MeshFlowClass.Worker.shutdown();
|
|
15
|
+
await hotmesh_1.HotMesh.stop();
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
exports.MeshFlow = MeshFlowClass;
|
|
19
|
+
MeshFlowClass.Client = client_1.ClientService;
|
|
20
|
+
MeshFlowClass.Connection = connection_1.ConnectionService;
|
|
21
|
+
MeshFlowClass.Search = search_1.Search;
|
|
22
|
+
MeshFlowClass.Worker = worker_1.WorkerService;
|
|
23
|
+
MeshFlowClass.workflow = workflow_1.WorkflowService;
|
|
@@ -1,39 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
*********** HOTMESH 'DURABLE' MODULE APPLICATION GRAPH **********
|
|
4
|
-
*
|
|
5
|
-
* This HotMesh application spec uses 50 activities and 25 transitions
|
|
6
|
-
* to model and emulate the Temporal Application & Query servers using
|
|
7
|
-
* Redis as the backend.
|
|
8
|
-
*
|
|
9
|
-
* It's particularly useful for organizations with high-speed, high-volume
|
|
10
|
-
* use cases as it uses in-memory Redis Streams for transactional,
|
|
11
|
-
* workflow processing, while adhering to Temporal's developer-friendly syntax.
|
|
12
|
-
*
|
|
13
|
-
* This YAML file can also serve as a useful starting point for building
|
|
14
|
-
* Integration/BPM/Workflow servers in general (MuleSoft, etc) without the need
|
|
15
|
-
* for a physical application server.
|
|
16
|
-
*
|
|
17
|
-
* Possible use cases include:
|
|
18
|
-
* * Orchestration servers
|
|
19
|
-
* * Integration servers
|
|
20
|
-
* * BPMN engines
|
|
21
|
-
* * Reentrant process servers
|
|
22
|
-
* * Service Meshes
|
|
23
|
-
* * Master Data Management systems
|
|
24
|
-
*/
|
|
25
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
3
|
exports.APP_ID = exports.APP_VERSION = exports.getWorkflowYAML = void 0;
|
|
27
4
|
const APP_VERSION = '3';
|
|
28
5
|
exports.APP_VERSION = APP_VERSION;
|
|
29
6
|
const APP_ID = 'durable';
|
|
30
7
|
exports.APP_ID = APP_ID;
|
|
31
|
-
/**
|
|
32
|
-
* returns a new durable workflow schema
|
|
33
|
-
* @param {string} app - app name (e.g., 'durable')
|
|
34
|
-
* @param {string} version - number as string (e.g., '1')
|
|
35
|
-
* @returns {string} HotMesh App YAML
|
|
36
|
-
*/
|
|
37
8
|
const getWorkflowYAML = (app, version) => {
|
|
38
9
|
return `app:
|
|
39
10
|
id: ${app}
|
|
@@ -41,7 +12,7 @@ const getWorkflowYAML = (app, version) => {
|
|
|
41
12
|
graphs:
|
|
42
13
|
|
|
43
14
|
###################################################
|
|
44
|
-
# THE
|
|
15
|
+
# THE MESHFLOW-REENTRANT-WORKFLOW #
|
|
45
16
|
# #
|
|
46
17
|
- subscribes: ${app}.execute
|
|
47
18
|
publishes: ${app}.executed
|
|
@@ -785,6 +756,7 @@ const getWorkflowYAML = (app, version) => {
|
|
|
785
756
|
type: signal
|
|
786
757
|
subtype: one
|
|
787
758
|
topic: ${app}.flow.signal
|
|
759
|
+
statusThreshold: 1
|
|
788
760
|
signal:
|
|
789
761
|
schema:
|
|
790
762
|
type: object
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { HotMesh } from '../hotmesh';
|
|
2
|
+
import { RedisClient, RedisMulti } from '../../types/redis';
|
|
3
|
+
import { StoreService } from '../store';
|
|
4
|
+
import { WorkflowSearchOptions } from '../../types/meshflow';
|
|
5
|
+
export declare class Search {
|
|
6
|
+
jobId: string;
|
|
7
|
+
searchSessionId: string;
|
|
8
|
+
searchSessionIndex: number;
|
|
9
|
+
hotMeshClient: HotMesh;
|
|
10
|
+
store: StoreService<RedisClient, RedisMulti> | null;
|
|
11
|
+
cachedFields: Record<string, string>;
|
|
12
|
+
constructor(workflowId: string, hotMeshClient: HotMesh, searchSessionId: string);
|
|
13
|
+
safeKey(key: string): string;
|
|
14
|
+
static configureSearchIndex(hotMeshClient: HotMesh, search?: WorkflowSearchOptions): Promise<void>;
|
|
15
|
+
static listSearchIndexes(hotMeshClient: HotMesh): Promise<string[]>;
|
|
16
|
+
getSearchSessionGuid(): string;
|
|
17
|
+
set(...args: string[]): Promise<number>;
|
|
18
|
+
get(key: string): Promise<string>;
|
|
19
|
+
mget(...args: string[]): Promise<string[]>;
|
|
20
|
+
del(...args: string[]): Promise<number | void>;
|
|
21
|
+
incr(key: string, val: number): Promise<number>;
|
|
22
|
+
mult(key: string, val: number): Promise<number>;
|
|
23
|
+
}
|
|
@@ -16,38 +16,12 @@ class Search {
|
|
|
16
16
|
this.hotMeshClient = hotMeshClient;
|
|
17
17
|
this.store = hotMeshClient.engine.store;
|
|
18
18
|
}
|
|
19
|
-
/**
|
|
20
|
-
* Prefixes the key with an underscore to keep separate from the
|
|
21
|
-
* activity and job history (and searchable via HKEYS)
|
|
22
|
-
* @param {string} key - the key to be sanitized. Wrap in quotes to avoid sanitization.
|
|
23
|
-
* @returns {string} - the sanitized key
|
|
24
|
-
* @private
|
|
25
|
-
*/
|
|
26
19
|
safeKey(key) {
|
|
27
20
|
if (key.startsWith('"')) {
|
|
28
21
|
return key.slice(1, -1);
|
|
29
22
|
}
|
|
30
23
|
return `_${key}`;
|
|
31
24
|
}
|
|
32
|
-
/**
|
|
33
|
-
* For those deployments with a redis stack backend (with the FT module),
|
|
34
|
-
* this method will configure the search index for the workflow. For all
|
|
35
|
-
* others, this method will exit/fail gracefully and not index
|
|
36
|
-
* the fields in the HASH. All values are searchable via HKEYS/HSC/HGET
|
|
37
|
-
* @param {HotMesh} hotMeshClient - the hotmesh client
|
|
38
|
-
* @param {WorkflowSearchOptions} search - the search options
|
|
39
|
-
* @returns {Promise<void>}
|
|
40
|
-
* @example
|
|
41
|
-
* const search = {
|
|
42
|
-
* index: 'my_search_index',
|
|
43
|
-
* prefix: ['my_workflow_prefix'],
|
|
44
|
-
* schema: {
|
|
45
|
-
* field1: { type: 'TEXT', sortable: true },
|
|
46
|
-
* field2: { type: 'NUMERIC', sortable: true }
|
|
47
|
-
* }
|
|
48
|
-
* }
|
|
49
|
-
* await Search.configureSearchIndex(hotMeshClient, search);
|
|
50
|
-
*/
|
|
51
25
|
static async configureSearchIndex(hotMeshClient, search) {
|
|
52
26
|
if (search?.schema) {
|
|
53
27
|
const store = hotMeshClient.engine.store;
|
|
@@ -85,15 +59,6 @@ class Search {
|
|
|
85
59
|
}
|
|
86
60
|
}
|
|
87
61
|
}
|
|
88
|
-
/**
|
|
89
|
-
* For those deployments with a redis stack backend (with the FT module),
|
|
90
|
-
* this method will list all search indexes.
|
|
91
|
-
*
|
|
92
|
-
* @param {HotMesh} hotMeshClient - the hotmesh client
|
|
93
|
-
* @returns {Promise<string[]>} - the list of search indexes
|
|
94
|
-
* @example
|
|
95
|
-
* const searchIndexes = await Search.listSearchIndexes(hotMeshClient);
|
|
96
|
-
*/
|
|
97
62
|
static async listSearchIndexes(hotMeshClient) {
|
|
98
63
|
try {
|
|
99
64
|
const store = hotMeshClient.engine.store;
|
|
@@ -107,25 +72,9 @@ class Search {
|
|
|
107
72
|
return [];
|
|
108
73
|
}
|
|
109
74
|
}
|
|
110
|
-
/**
|
|
111
|
-
* increments the index to return a unique search session guid when
|
|
112
|
-
* calling any method that produces side effects (changes the value)
|
|
113
|
-
* @private
|
|
114
|
-
*/
|
|
115
75
|
getSearchSessionGuid() {
|
|
116
|
-
//return the search session as it would exist in the search session index
|
|
117
76
|
return `${this.searchSessionId}-${this.searchSessionIndex++}-`;
|
|
118
77
|
}
|
|
119
|
-
/**
|
|
120
|
-
* Sets the fields listed in args. Returns the
|
|
121
|
-
* count of new fields that were set (does not
|
|
122
|
-
* count fields that were updated)
|
|
123
|
-
* @param args
|
|
124
|
-
* @returns {number}
|
|
125
|
-
* @example
|
|
126
|
-
* const search = new Search();
|
|
127
|
-
* const count = await search.set('field1', 'value1', 'field2', 'value2');
|
|
128
|
-
*/
|
|
129
78
|
async set(...args) {
|
|
130
79
|
const ssGuid = this.getSearchSessionGuid();
|
|
131
80
|
const store = storage_1.asyncLocalStorage.getStore();
|
|
@@ -142,18 +91,9 @@ class Search {
|
|
|
142
91
|
return Number(replay[ssGuid]);
|
|
143
92
|
}
|
|
144
93
|
const fieldCount = await this.store.exec('HSET', this.jobId, ...safeArgs);
|
|
145
|
-
//no need to wait; set this interim value in the replay
|
|
146
94
|
this.store.exec('HSET', this.jobId, ssGuid, fieldCount.toString());
|
|
147
95
|
return Number(fieldCount);
|
|
148
96
|
}
|
|
149
|
-
/**
|
|
150
|
-
* Returns the value of the field in the HASH stored at key.
|
|
151
|
-
* @param key
|
|
152
|
-
* @returns {string}
|
|
153
|
-
* @example
|
|
154
|
-
* const search = new Search();
|
|
155
|
-
* const value = await search.get('field1');
|
|
156
|
-
*/
|
|
157
97
|
async get(key) {
|
|
158
98
|
try {
|
|
159
99
|
if (key in this.cachedFields) {
|
|
@@ -168,11 +108,6 @@ class Search {
|
|
|
168
108
|
return '';
|
|
169
109
|
}
|
|
170
110
|
}
|
|
171
|
-
/**
|
|
172
|
-
* Returns the values of all specified fields in the HASH stored at key.
|
|
173
|
-
* @param args
|
|
174
|
-
* @returns
|
|
175
|
-
*/
|
|
176
111
|
async mget(...args) {
|
|
177
112
|
let isCached = true;
|
|
178
113
|
const values = [];
|
|
@@ -205,16 +140,6 @@ class Search {
|
|
|
205
140
|
return [];
|
|
206
141
|
}
|
|
207
142
|
}
|
|
208
|
-
/**
|
|
209
|
-
* Deletes the fields provided as args. Returns the
|
|
210
|
-
* count of fields that were deleted.
|
|
211
|
-
*
|
|
212
|
-
* @param args
|
|
213
|
-
* @returns {number}
|
|
214
|
-
* @example
|
|
215
|
-
* sont search = new Search();
|
|
216
|
-
* const count = await search.del('field1', 'field2', 'field3');
|
|
217
|
-
*/
|
|
218
143
|
async del(...args) {
|
|
219
144
|
const ssGuid = this.getSearchSessionGuid();
|
|
220
145
|
const store = storage_1.asyncLocalStorage.getStore();
|
|
@@ -235,18 +160,6 @@ class Search {
|
|
|
235
160
|
this.store.exec('HSET', this.jobId, ssGuid, formattedResponse.toString());
|
|
236
161
|
return formattedResponse;
|
|
237
162
|
}
|
|
238
|
-
/**
|
|
239
|
-
* Increments the value of a float field by the given amount. Returns the
|
|
240
|
-
* new value of the field after the increment. Pass a negative
|
|
241
|
-
* number to decrement the value.
|
|
242
|
-
*
|
|
243
|
-
* @param key - the key to increment
|
|
244
|
-
* @param val - the value to increment by
|
|
245
|
-
* @returns {number} - the new value
|
|
246
|
-
* @example
|
|
247
|
-
* const search = new Search();
|
|
248
|
-
* const count = await search.incr('field1', 1.5);
|
|
249
|
-
*/
|
|
250
163
|
async incr(key, val) {
|
|
251
164
|
delete this.cachedFields[key];
|
|
252
165
|
const ssGuid = this.getSearchSessionGuid();
|
|
@@ -259,18 +172,6 @@ class Search {
|
|
|
259
172
|
this.store.exec('HSET', this.jobId, ssGuid, num.toString());
|
|
260
173
|
return Number(num);
|
|
261
174
|
}
|
|
262
|
-
/**
|
|
263
|
-
* Multiplies the value of a field by the given amount. Returns the
|
|
264
|
-
* new value of the field after the multiplication. NOTE:
|
|
265
|
-
* this is exponential multiplication.
|
|
266
|
-
*
|
|
267
|
-
* @param key - the key to multiply
|
|
268
|
-
* @param val - the value to multiply by
|
|
269
|
-
* @returns {number} - the new product of the multiplication
|
|
270
|
-
* @example
|
|
271
|
-
* const search = new Search();
|
|
272
|
-
* const product = await search.mult('field1', 1.5);
|
|
273
|
-
*/
|
|
274
175
|
async mult(key, val) {
|
|
275
176
|
delete this.cachedFields[key];
|
|
276
177
|
const ssGuid = this.getSearchSessionGuid();
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Registry, WorkerConfig, WorkerOptions } from '../../types/
|
|
1
|
+
import { HotMesh } from '../hotmesh';
|
|
2
|
+
import { Registry, WorkerConfig, WorkerOptions } from '../../types/meshflow';
|
|
3
3
|
export declare class WorkerService {
|
|
4
4
|
static activityRegistry: Registry;
|
|
5
5
|
static instances: Map<string, HotMesh | Promise<HotMesh>>;
|
|
6
6
|
workflowRunner: HotMesh;
|
|
7
7
|
activityRunner: HotMesh;
|
|
8
8
|
static getHotMesh: (workflowTopic: string, config?: Partial<WorkerConfig>, options?: WorkerOptions) => Promise<HotMesh>;
|
|
9
|
+
constructor();
|
|
9
10
|
static activateWorkflow(hotMesh: HotMesh): Promise<void>;
|
|
10
11
|
static registerActivities<ACT>(activities: ACT): Registry;
|
|
11
12
|
static create(config: WorkerConfig): Promise<WorkerService>;
|