@hotmeshio/hotmesh 0.0.52 → 0.0.54
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 +58 -20
- 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 +9 -5
- 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.d.ts +5 -2
- package/build/services/activities/trigger.js +34 -4
- 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 +57 -38
- package/build/services/durable/exporter.d.ts +27 -81
- package/build/services/durable/exporter.js +153 -325
- package/build/services/durable/handle.d.ts +13 -8
- package/build/services/durable/handle.js +61 -48
- 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 +67 -52
- package/build/services/durable/workflow.js +322 -306
- package/build/services/engine/index.d.ts +2 -2
- package/build/services/engine/index.js +5 -2
- package/build/services/exporter/index.d.ts +2 -4
- package/build/services/exporter/index.js +4 -5
- package/build/services/hotmesh/index.d.ts +2 -2
- package/build/services/hotmesh/index.js +2 -2
- 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 +183 -36
- package/build/types/error.d.ts +48 -0
- package/build/types/error.js +2 -0
- package/build/types/exporter.d.ts +35 -7
- package/build/types/index.d.ts +4 -3
- package/build/types/job.d.ts +93 -6
- 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 +139 -34
- 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 +12 -5
- package/services/activities/interrupt.ts +19 -9
- package/services/activities/signal.ts +6 -5
- package/services/activities/trigger.ts +43 -6
- 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 +49 -22
- package/services/durable/exporter.ts +162 -349
- package/services/durable/handle.ts +66 -53
- 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 +371 -322
- package/services/engine/index.ts +8 -3
- package/services/exporter/index.ts +10 -12
- package/services/hotmesh/index.ts +4 -3
- 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 +263 -75
- package/types/error.ts +52 -0
- package/types/exporter.ts +43 -9
- package/types/index.ts +14 -8
- package/types/job.ts +157 -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/services/engine/index.ts
CHANGED
|
@@ -51,7 +51,8 @@ import {
|
|
|
51
51
|
PartialJobState,
|
|
52
52
|
JobStatus,
|
|
53
53
|
JobInterruptOptions,
|
|
54
|
-
JobCompletionOptions
|
|
54
|
+
JobCompletionOptions,
|
|
55
|
+
ExtensionType} from '../../types/job';
|
|
55
56
|
import {
|
|
56
57
|
HotMeshApps,
|
|
57
58
|
HotMeshConfig,
|
|
@@ -450,6 +451,7 @@ class EngineService {
|
|
|
450
451
|
streamData.status = StreamStatus.ERROR;
|
|
451
452
|
streamData.data = error;
|
|
452
453
|
streamData.code = error.code;
|
|
454
|
+
streamData.stack = error.stack;
|
|
453
455
|
} else if (emit) {
|
|
454
456
|
streamData.status = StreamStatus.PENDING;
|
|
455
457
|
streamData.code = HMSH_CODE_PENDING;
|
|
@@ -474,7 +476,10 @@ class EngineService {
|
|
|
474
476
|
|
|
475
477
|
// ****************** `INTERRUPT` ACTIVE JOBS *****************
|
|
476
478
|
async interrupt(topic: string, jobId: string, options: JobInterruptOptions = {}): Promise<string> {
|
|
479
|
+
//immediately interrupt the job, going directly to the data source
|
|
477
480
|
await this.store.interrupt(topic, jobId, options);
|
|
481
|
+
|
|
482
|
+
//now that the job is interrupted, we can clean up
|
|
478
483
|
const context = await this.getState(topic, jobId) as JobState;
|
|
479
484
|
const completionOpts: JobCompletionOptions = {
|
|
480
485
|
interrupt: options.descend,
|
|
@@ -562,10 +567,10 @@ class EngineService {
|
|
|
562
567
|
|
|
563
568
|
// ********************** PUB/SUB ENTRY POINT **********************
|
|
564
569
|
//publish (returns just the job id)
|
|
565
|
-
async pub(topic: string, data: JobData, context?: JobState): Promise<string> {
|
|
570
|
+
async pub(topic: string, data: JobData, context?: JobState, extended?: ExtensionType): Promise<string> {
|
|
566
571
|
const activityHandler = await this.initActivity(topic, data, context);
|
|
567
572
|
if (activityHandler) {
|
|
568
|
-
return await activityHandler.process();
|
|
573
|
+
return await activityHandler.process(extended);
|
|
569
574
|
} else {
|
|
570
575
|
throw new Error(`unable to process activity for topic ${topic}`);
|
|
571
576
|
}
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
+
import { VALSEP } from '../../modules/key';
|
|
1
2
|
import { ILogger } from '../logger';
|
|
3
|
+
import { restoreHierarchy } from '../../modules/utils';
|
|
4
|
+
import { SerializerService } from '../serializer';
|
|
2
5
|
import { StoreService } from '../store';
|
|
3
|
-
import {
|
|
4
|
-
StringAnyType,
|
|
5
|
-
StringStringType,
|
|
6
|
-
Symbols } from "../../types/serializer";
|
|
7
|
-
import { RedisClient, RedisMulti } from '../../types/redis';
|
|
8
6
|
import {
|
|
9
7
|
DependencyExport,
|
|
10
8
|
ExportOptions,
|
|
11
9
|
JobActionExport,
|
|
12
10
|
JobExport } from '../../types/exporter';
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
|
|
11
|
+
import { RedisClient, RedisMulti } from '../../types/redis';
|
|
12
|
+
import {
|
|
13
|
+
StringAnyType,
|
|
14
|
+
StringStringType,
|
|
15
|
+
Symbols } from "../../types/serializer";
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* Downloads job data from Redis (hscan, hmget, hgetall)
|
|
@@ -21,7 +21,6 @@ import { VALSEP } from '../../modules/key';
|
|
|
21
21
|
class ExporterService {
|
|
22
22
|
appId: string;
|
|
23
23
|
logger: ILogger;
|
|
24
|
-
serializer: SerializerService
|
|
25
24
|
store: StoreService<RedisClient, RedisMulti>;
|
|
26
25
|
symbols: Promise<Symbols> | Symbols;
|
|
27
26
|
|
|
@@ -29,7 +28,6 @@ class ExporterService {
|
|
|
29
28
|
this.appId = appId;
|
|
30
29
|
this.logger = logger;
|
|
31
30
|
this.store = store;
|
|
32
|
-
this.serializer = new SerializerService();
|
|
33
31
|
}
|
|
34
32
|
|
|
35
33
|
/**
|
|
@@ -83,11 +81,11 @@ class ExporterService {
|
|
|
83
81
|
const [_, letters, numbers] = match;
|
|
84
82
|
const path = this.inflateKey(letters);
|
|
85
83
|
const dimensions = `${numbers.replace(/,/g, '/')}`;
|
|
86
|
-
const resolved =
|
|
84
|
+
const resolved = SerializerService.fromString(value);
|
|
87
85
|
process[`${dimensions}/${path}`] = resolved;
|
|
88
86
|
} else if (key.length === 3) {
|
|
89
87
|
//job state
|
|
90
|
-
process[this.inflateKey(key)] =
|
|
88
|
+
process[this.inflateKey(key)] = SerializerService.fromString(value);
|
|
91
89
|
}
|
|
92
90
|
});
|
|
93
91
|
|
|
@@ -12,7 +12,8 @@ import {
|
|
|
12
12
|
JobData,
|
|
13
13
|
JobOutput,
|
|
14
14
|
JobStatus,
|
|
15
|
-
JobInterruptOptions
|
|
15
|
+
JobInterruptOptions,
|
|
16
|
+
ExtensionType} from '../../types/job';
|
|
16
17
|
import {
|
|
17
18
|
HotMeshConfig,
|
|
18
19
|
HotMeshManifest } from '../../types/hotmesh';
|
|
@@ -115,8 +116,8 @@ class HotMeshService {
|
|
|
115
116
|
}
|
|
116
117
|
|
|
117
118
|
// ************* PUB/SUB METHODS *************
|
|
118
|
-
async pub(topic: string, data: JobData = {}, context?: JobState): Promise<string> {
|
|
119
|
-
return await this.engine?.pub(topic, data, context);
|
|
119
|
+
async pub(topic: string, data: JobData = {}, context?: JobState, extended?: ExtensionType): Promise<string> {
|
|
120
|
+
return await this.engine?.pub(topic, data, context, extended);
|
|
120
121
|
}
|
|
121
122
|
async sub(topic: string, callback: JobMessageCallback): Promise<void> {
|
|
122
123
|
return await this.engine?.sub(topic, callback);
|
package/services/mapper/index.ts
CHANGED
|
@@ -36,7 +36,7 @@ class MapperService {
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
/**
|
|
39
|
-
*
|
|
39
|
+
* resolves a pipe expression of the form: { @pipe: [["{data.foo.bar}", 2, false, "hello world"]] }
|
|
40
40
|
* @param value
|
|
41
41
|
* @returns
|
|
42
42
|
*/
|
|
@@ -46,7 +46,7 @@ class MapperService {
|
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
/**
|
|
49
|
-
*
|
|
49
|
+
* resolves a mapping expression in the form: "{data.foo.bar}" or 2 or false or "hello world"
|
|
50
50
|
* @param value
|
|
51
51
|
* @returns
|
|
52
52
|
*/
|
|
@@ -55,6 +55,10 @@ class MapperService {
|
|
|
55
55
|
return pipe.process();
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
/**
|
|
59
|
+
* Evaluates a transition rule against the current job state and incoming Stream message
|
|
60
|
+
* to determine which (if any) transition should be taken.
|
|
61
|
+
*/
|
|
58
62
|
static evaluate(transitionRule: TransitionRule | boolean, context: JobState, code: StreamCode): boolean {
|
|
59
63
|
if (typeof transitionRule === 'boolean') {
|
|
60
64
|
return transitionRule;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
class ArrayHandler {
|
|
2
2
|
get(array: any[], index: number): any {
|
|
3
|
-
return array[index];
|
|
3
|
+
return array?.[index || 0];
|
|
4
4
|
}
|
|
5
5
|
|
|
6
6
|
length(array: any[]): any {
|
|
@@ -11,26 +11,6 @@ class ArrayHandler {
|
|
|
11
11
|
return array1.concat(array2);
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
every(array: any[], callback: (value: any, index: number, array: any[]) => boolean): boolean {
|
|
15
|
-
return array.every(callback);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
filter(array: any[], callback: (value: any, index: number, array: any[]) => boolean): any[] {
|
|
19
|
-
return array.filter(callback);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
find(array: any[], callback: (value: any, index: number, array: any[]) => boolean): any {
|
|
23
|
-
return array.find(callback);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
findIndex(array: any[], callback: (value: any, index: number, array: any[]) => boolean): number {
|
|
27
|
-
return array.findIndex(callback);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
forEach(array: any[], callback: (value: any, index: number, array: any[]) => void): void {
|
|
31
|
-
array.forEach(callback);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
14
|
indexOf(array: any[], searchElement: any, fromIndex?: number): number {
|
|
35
15
|
return array.indexOf(searchElement, fromIndex);
|
|
36
16
|
}
|
|
@@ -43,20 +23,13 @@ class ArrayHandler {
|
|
|
43
23
|
return array.lastIndexOf(searchElement, fromIndex);
|
|
44
24
|
}
|
|
45
25
|
|
|
46
|
-
map(array: any[], callback: (value: any, index: number, array: any[]) => any): any[] {
|
|
47
|
-
return array.map(callback);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
26
|
pop(array: any[]): any {
|
|
51
27
|
return array.pop();
|
|
52
28
|
}
|
|
53
29
|
|
|
54
|
-
push(array: any[], ...items: any[]):
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
reduce(array: any[], callback: (accumulator: any, currentValue: any, currentIndex: number, array: any[]) => any, initialValue?: any): any {
|
|
59
|
-
return array.reduce(callback, initialValue);
|
|
30
|
+
push(array: any[], ...items: any[]): any[] {
|
|
31
|
+
array.push(...items);
|
|
32
|
+
return array;
|
|
60
33
|
}
|
|
61
34
|
|
|
62
35
|
reverse(array: any[]): any[] {
|
|
@@ -71,12 +44,26 @@ class ArrayHandler {
|
|
|
71
44
|
return array.slice(start, end);
|
|
72
45
|
}
|
|
73
46
|
|
|
74
|
-
|
|
75
|
-
return array.
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
47
|
+
sort(array: any[], order: 'ASCENDING' | 'DESCENDING' = 'ASCENDING'): any[] {
|
|
48
|
+
return array.sort((a, b) => {
|
|
49
|
+
if (order === 'ASCENDING') {
|
|
50
|
+
if (a === b) return 0;
|
|
51
|
+
if (a === null || a === undefined) return -1;
|
|
52
|
+
if (b === null || b === undefined) return 1;
|
|
53
|
+
if (typeof a === 'string' && typeof b === 'string') {
|
|
54
|
+
return a.localeCompare(b);
|
|
55
|
+
}
|
|
56
|
+
return a < b ? -1 : 1;
|
|
57
|
+
} else {
|
|
58
|
+
if (a === b) return 0;
|
|
59
|
+
if (a === null || a === undefined) return 1;
|
|
60
|
+
if (b === null || b === undefined) return -1;
|
|
61
|
+
if (typeof a === 'string' && typeof b === 'string') {
|
|
62
|
+
return b.localeCompare(a);
|
|
63
|
+
}
|
|
64
|
+
return a > b ? -1 : 1;
|
|
65
|
+
}
|
|
66
|
+
});
|
|
80
67
|
}
|
|
81
68
|
|
|
82
69
|
splice(array: any[], start: number, deleteCount?: number, ...items: any[]): any[] {
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { formatISODate } from "../../../modules/utils";
|
|
2
|
+
|
|
1
3
|
type DateInput = Date | string | number;
|
|
2
4
|
|
|
3
5
|
class DateHandler {
|
|
@@ -181,6 +183,10 @@ class DateHandler {
|
|
|
181
183
|
return DateHandler.getDateInstance(date).toISOString();
|
|
182
184
|
}
|
|
183
185
|
|
|
186
|
+
toISOXString(date?: DateInput): string {
|
|
187
|
+
return formatISODate(date ? DateHandler.getDateInstance(date) : new Date());
|
|
188
|
+
}
|
|
189
|
+
|
|
184
190
|
toJSON(date: DateInput): string {
|
|
185
191
|
return DateHandler.getDateInstance(date).toJSON();
|
|
186
192
|
}
|
|
@@ -3,6 +3,7 @@ import { BitwiseHandler } from './bitwise';
|
|
|
3
3
|
import { ConditionalHandler } from './conditional';
|
|
4
4
|
import { DateHandler } from './date';
|
|
5
5
|
import { JsonHandler } from './json';
|
|
6
|
+
import { LogicalHandler } from './logical';
|
|
6
7
|
import { MathHandler } from './math';
|
|
7
8
|
import { NumberHandler } from './number';
|
|
8
9
|
import { ObjectHandler } from './object';
|
|
@@ -11,11 +12,12 @@ import { SymbolHandler } from './symbol';
|
|
|
11
12
|
import { UnaryHandler } from './unary';
|
|
12
13
|
|
|
13
14
|
export default {
|
|
14
|
-
array: new ArrayHandler(),
|
|
15
|
-
bitwise: new BitwiseHandler(),
|
|
16
|
-
conditional: new ConditionalHandler(),
|
|
17
|
-
date: new DateHandler(),
|
|
18
|
-
json: new JsonHandler(),
|
|
15
|
+
array: new ArrayHandler(),
|
|
16
|
+
bitwise: new BitwiseHandler(),
|
|
17
|
+
conditional: new ConditionalHandler(),
|
|
18
|
+
date: new DateHandler(),
|
|
19
|
+
json: new JsonHandler(),
|
|
20
|
+
logical: new LogicalHandler(),
|
|
19
21
|
math: new MathHandler(),
|
|
20
22
|
number: new NumberHandler(),
|
|
21
23
|
object: new ObjectHandler(),
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
class LogicalHandler {
|
|
2
|
+
and(firstValue: boolean, secondValue: boolean): boolean {
|
|
3
|
+
return firstValue && secondValue;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
or(firstValue: boolean, secondValue: boolean): boolean {
|
|
7
|
+
return firstValue || secondValue;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export { LogicalHandler };
|
|
@@ -1,14 +1,33 @@
|
|
|
1
1
|
class ObjectHandler {
|
|
2
|
+
get(obj: object, prop: string | symbol): any {
|
|
3
|
+
return obj?.[prop];
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
set(obj: object, prop: string | symbol, value: any): any {
|
|
7
|
+
if (!obj) obj = {};
|
|
8
|
+
obj[prop] = value;
|
|
9
|
+
return obj;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
create(...args: any[]): object {
|
|
13
|
+
const obj = {};
|
|
14
|
+
if (args.length === 0) return obj;
|
|
15
|
+
for (let i = 0; i < args.length; i += 2) {
|
|
16
|
+
obj[args[i]] = args[i + 1];
|
|
17
|
+
}
|
|
18
|
+
return obj;
|
|
19
|
+
}
|
|
20
|
+
|
|
2
21
|
keys(obj: object): string[] {
|
|
3
|
-
return Object.keys(obj);
|
|
22
|
+
return obj && Object.keys(obj) || [];
|
|
4
23
|
}
|
|
5
24
|
|
|
6
25
|
values(obj: object): any[] {
|
|
7
|
-
return Object.values(obj);
|
|
26
|
+
return obj && Object.values(obj) || [];
|
|
8
27
|
}
|
|
9
28
|
|
|
10
29
|
entries(obj: object): [string, any][] {
|
|
11
|
-
return Object.entries(obj);
|
|
30
|
+
return obj && Object.entries(obj) || [];
|
|
12
31
|
}
|
|
13
32
|
|
|
14
33
|
fromEntries(iterable: Iterable<[string, any]>): object {
|
|
@@ -16,19 +35,19 @@ class ObjectHandler {
|
|
|
16
35
|
}
|
|
17
36
|
|
|
18
37
|
assign(target: object, ...sources: object[]): object {
|
|
19
|
-
return Object.assign(target, ...sources);
|
|
38
|
+
return Object.assign(target || {}, ...sources);
|
|
20
39
|
}
|
|
21
40
|
|
|
22
41
|
getOwnPropertyNames(obj: object): string[] {
|
|
23
|
-
return Object.getOwnPropertyNames(obj);
|
|
42
|
+
return Object.getOwnPropertyNames(obj || {});
|
|
24
43
|
}
|
|
25
44
|
|
|
26
45
|
getOwnPropertySymbols(obj: object): symbol[] {
|
|
27
|
-
return Object.getOwnPropertySymbols(obj);
|
|
46
|
+
return Object.getOwnPropertySymbols(obj || {});
|
|
28
47
|
}
|
|
29
48
|
|
|
30
49
|
getOwnPropertyDescriptor(obj: object, prop: string | symbol): PropertyDescriptor | undefined {
|
|
31
|
-
return Object.getOwnPropertyDescriptor(obj, prop);
|
|
50
|
+
return Object.getOwnPropertyDescriptor(obj || {}, prop);
|
|
32
51
|
}
|
|
33
52
|
|
|
34
53
|
defineProperty(obj: object, prop: string | symbol, descriptor: PropertyDescriptor): object {
|
package/services/pipe/index.ts
CHANGED
|
@@ -1,21 +1,34 @@
|
|
|
1
1
|
import FUNCTIONS from './functions'
|
|
2
2
|
import { JobState, JobData, JobsData } from '../../types/job';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
PipeContext,
|
|
5
|
+
PipeItem,
|
|
6
|
+
PipeItems,
|
|
7
|
+
PipeObject,
|
|
8
|
+
Pipe as PipeType,
|
|
9
|
+
ReduceObject } from '../../types/pipe';
|
|
4
10
|
|
|
5
11
|
class Pipe {
|
|
6
12
|
rules: PipeType;
|
|
7
13
|
jobData: JobData;
|
|
14
|
+
context: PipeContext;
|
|
8
15
|
|
|
9
|
-
constructor(rules: PipeType, jobData: JobData) {
|
|
16
|
+
constructor(rules: PipeType, jobData: JobData, context?: PipeContext) {
|
|
10
17
|
this.rules = rules;
|
|
11
18
|
this.jobData = jobData;
|
|
19
|
+
this.context = context;
|
|
12
20
|
}
|
|
13
21
|
|
|
14
|
-
private isPipeType(currentRow: PipeItem[]|PipeType): currentRow is PipeType {
|
|
22
|
+
private isPipeType(currentRow: PipeItem[] | PipeType | PipeObject | ReduceObject): currentRow is PipeType {
|
|
15
23
|
return !Array.isArray(currentRow) && '@pipe' in currentRow;
|
|
16
24
|
}
|
|
17
25
|
|
|
18
|
-
|
|
26
|
+
private isreduceType(currentRow: PipeItem[] | PipeType | PipeObject | ReduceObject): currentRow is PipeType {
|
|
27
|
+
return !Array.isArray(currentRow) && '@reduce' in currentRow;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
static isPipeObject(obj: { [key: string]: unknown } | PipeItem): boolean {
|
|
19
32
|
return typeof obj === 'object' && obj !== null && !Array.isArray(obj) && '@pipe' in obj;
|
|
20
33
|
}
|
|
21
34
|
|
|
@@ -33,36 +46,82 @@ class Pipe {
|
|
|
33
46
|
* loop through each PipeItem row in this Pipe, resolving and transforming line by line
|
|
34
47
|
* @returns {any} the result of the pipe
|
|
35
48
|
*/
|
|
36
|
-
process(): any {
|
|
37
|
-
let
|
|
49
|
+
process(resolved: unknown[] | null = null): any {
|
|
50
|
+
let index = 0;
|
|
51
|
+
if (!(resolved || this.isPipeType(this.rules[0]) || this.isreduceType(this.rules[0]))) {
|
|
52
|
+
resolved = this.processCells(this.rules[0] as PipeItem[]); // Add type assertion
|
|
53
|
+
index = 1;
|
|
54
|
+
}
|
|
38
55
|
const len = this.rules.length;
|
|
39
|
-
|
|
40
|
-
|
|
56
|
+
const subPipeQueue = [];
|
|
57
|
+
for (let i = index; i < len; i++) {
|
|
58
|
+
resolved = this.processRow(this.rules[i], resolved, subPipeQueue);
|
|
41
59
|
}
|
|
42
60
|
return resolved[0];
|
|
43
61
|
}
|
|
44
62
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
63
|
+
/**
|
|
64
|
+
* Transforms iterable `input` into a single value. Vars $output, $item, $key
|
|
65
|
+
* and $input are available. The final statement in the iterator (the reduction)
|
|
66
|
+
* is assumed to be the return value. A default $output object may be provided
|
|
67
|
+
* to the iterator by placing the the second cell of the preceding row. Otherwise,
|
|
68
|
+
* construct the object during first run and ensure it is the first cell of the
|
|
69
|
+
* last row of the iterator, so it is returned as the $output for the next cycle
|
|
70
|
+
* @param {unknown[]} input
|
|
71
|
+
* @returns {unknown}
|
|
72
|
+
* @private
|
|
73
|
+
*/
|
|
74
|
+
reduce(input: Array<unknown[]>): unknown {
|
|
75
|
+
let resolved = input[1] ?? null;
|
|
76
|
+
|
|
77
|
+
if (Array.isArray(input[0])) {
|
|
78
|
+
for (let index = 0; index < input[0].length; index++) {
|
|
79
|
+
this.context = { $input: input[0], $output: resolved, $item: input[0][index], $key: index.toString(), $index: index };
|
|
80
|
+
resolved = this.process([resolved]);
|
|
81
|
+
}
|
|
82
|
+
} else {
|
|
83
|
+
let index = -1;
|
|
84
|
+
for (let $key in (input[0] as Record<string, unknown>)) {
|
|
85
|
+
index++;
|
|
86
|
+
this.context = { $input: input[0], $output: resolved, $item: input[0][$key], $key, $index: index };
|
|
87
|
+
resolved = this.process([resolved]);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return [resolved];
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
private processRow(currentRow: PipeItem[] | PipeType | PipeObject | ReduceObject , resolvedPriorRow: unknown[]|null, subPipeQueue: unknown[]): PipeItem[] {
|
|
94
|
+
if (resolvedPriorRow && this.isreduceType(currentRow)) {
|
|
95
|
+
//reduce the resolvedPriorRow and return the output from the reducer
|
|
96
|
+
const subPipe = new Pipe(currentRow['@reduce'], this.jobData);
|
|
97
|
+
const reduced = subPipe.reduce(resolvedPriorRow as Array<unknown[]>)
|
|
98
|
+
return reduced as PipeItem[];
|
|
99
|
+
} else if (this.isPipeType(currentRow)) {
|
|
100
|
+
//process subPipe and push to subPipeQueue; echo resolvedPriorRow
|
|
101
|
+
const subPipe = new Pipe(currentRow['@pipe'], this.jobData, this.context);
|
|
49
102
|
subPipeQueue.push(subPipe.process());
|
|
50
|
-
//return prior row as if nothing happened
|
|
51
103
|
return resolvedPriorRow as PipeItem[];
|
|
52
104
|
} else {
|
|
105
|
+
//pivot the subPipeQueue into the arguments array (resolvedPriorRow)
|
|
53
106
|
if (subPipeQueue.length > 0) {
|
|
54
|
-
//if items in subPipeQueue, flush and use as resolvedPriorRow
|
|
55
107
|
resolvedPriorRow = [...subPipeQueue];
|
|
56
108
|
subPipeQueue.length = 0;
|
|
57
|
-
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (!resolvedPriorRow) {
|
|
58
112
|
//if no prior row, use current row as prior row
|
|
59
|
-
return [].concat(this.processCells([...currentRow]));
|
|
113
|
+
return [].concat(this.processCells(Array.isArray(currentRow) ? [...currentRow] as PipeItem[] : []));
|
|
60
114
|
} else {
|
|
61
|
-
const [functionName, ...params] = currentRow;
|
|
115
|
+
const [functionName, ...params] = currentRow as PipeItem[]; // Add type assertion
|
|
62
116
|
//use resolved values from prior row (n - 1) as input params to cell 1 function
|
|
63
|
-
|
|
117
|
+
let resolvedValue: unknown;
|
|
118
|
+
if (this.isContextVariable(functionName)) {
|
|
119
|
+
resolvedValue = this.resolveContextValue(functionName as string);
|
|
120
|
+
} else {
|
|
121
|
+
resolvedValue = Pipe.resolveFunction(functionName as string)(...resolvedPriorRow);
|
|
122
|
+
}
|
|
64
123
|
//resolve remaining cells in row and return concatenated with resolvedValue
|
|
65
|
-
return [resolvedValue].concat(this.processCells([...params]));
|
|
124
|
+
return [(resolvedValue as PipeItem)].concat(this.processCells([...params]));
|
|
66
125
|
}
|
|
67
126
|
}
|
|
68
127
|
}
|
|
@@ -83,8 +142,10 @@ class Pipe {
|
|
|
83
142
|
|
|
84
143
|
processCells(cells: PipeItems): unknown[] {
|
|
85
144
|
const resolved = [];
|
|
86
|
-
|
|
87
|
-
|
|
145
|
+
if (Array.isArray(cells)) {
|
|
146
|
+
for (const currentCell of cells) {
|
|
147
|
+
resolved.push(this.resolveCellValue(currentCell));
|
|
148
|
+
}
|
|
88
149
|
}
|
|
89
150
|
return resolved;
|
|
90
151
|
}
|
|
@@ -93,6 +154,12 @@ class Pipe {
|
|
|
93
154
|
return typeof currentCell === 'string' && currentCell.startsWith('{@') && currentCell.endsWith('}');
|
|
94
155
|
}
|
|
95
156
|
|
|
157
|
+
private isContextVariable(currentCell: PipeItem): boolean {
|
|
158
|
+
if(typeof currentCell === 'string' && currentCell.endsWith('}')) {
|
|
159
|
+
return currentCell.startsWith('{$item') || currentCell.startsWith('{$key') || currentCell.startsWith('{$index') || currentCell.startsWith('{$input') || currentCell.startsWith('{$output');
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
96
163
|
private isMappable(currentCell: PipeItem): boolean {
|
|
97
164
|
return typeof currentCell === 'string' && currentCell.startsWith('{') && currentCell.endsWith('}');
|
|
98
165
|
}
|
|
@@ -101,6 +168,8 @@ class Pipe {
|
|
|
101
168
|
if (this.isFunction(currentCell)) {
|
|
102
169
|
const fn = Pipe.resolveFunction(currentCell as string);
|
|
103
170
|
return fn.call();
|
|
171
|
+
} else if (this.isContextVariable(currentCell)) {
|
|
172
|
+
return this.resolveContextValue(currentCell as string);
|
|
104
173
|
} else if (this.isMappable(currentCell)) {
|
|
105
174
|
return this.resolveMappableValue(currentCell as string);
|
|
106
175
|
} else {
|
|
@@ -126,6 +195,15 @@ class Pipe {
|
|
|
126
195
|
return this.getNestedProperty(this.jobData, term);
|
|
127
196
|
}
|
|
128
197
|
|
|
198
|
+
resolveContextValue(currentCell: string): unknown {
|
|
199
|
+
const term = this.resolveContextTerm(currentCell);
|
|
200
|
+
return this.getNestedProperty(this.context, term);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
resolveContextTerm(currentCell: string): string {
|
|
204
|
+
return currentCell.substring(1, currentCell.length - 1);
|
|
205
|
+
}
|
|
206
|
+
|
|
129
207
|
resolveFunctionTerm(currentCell: string): string {
|
|
130
208
|
return currentCell.substring(2, currentCell.length - 1);
|
|
131
209
|
}
|
package/services/quorum/index.ts
CHANGED
|
@@ -25,9 +25,7 @@ import {
|
|
|
25
25
|
QuorumMessageCallback,
|
|
26
26
|
QuorumProfile,
|
|
27
27
|
RollCallMessage,
|
|
28
|
-
SubscriptionCallback
|
|
29
|
-
ThrottleMessage
|
|
30
|
-
} from '../../types/quorum';
|
|
28
|
+
SubscriptionCallback } from '../../types/quorum';
|
|
31
29
|
import { RedisClient, RedisMulti } from '../../types/redis';
|
|
32
30
|
import { RedisClientType } from '../../types/redisclient';
|
|
33
31
|
|
package/services/router/index.ts
CHANGED
|
@@ -194,7 +194,7 @@ class Router {
|
|
|
194
194
|
try {
|
|
195
195
|
output = await callback(input);
|
|
196
196
|
} catch (error) {
|
|
197
|
-
this.logger.error(`stream-call-function-error`, { error });
|
|
197
|
+
this.logger.error(`stream-call-function-error`, { ...error, input: input, stack: error.stack, message: error.message, name: error.name, stream, id });
|
|
198
198
|
output = this.structureUnhandledError(input, error);
|
|
199
199
|
}
|
|
200
200
|
return output as StreamDataResponse;
|
|
@@ -233,10 +233,19 @@ class Router {
|
|
|
233
233
|
}
|
|
234
234
|
|
|
235
235
|
shouldRetry(input: StreamData, output: StreamDataResponse): [boolean, number] {
|
|
236
|
+
//const isUnhandledEngineError = output.code === 500;
|
|
236
237
|
const policies = input.policies?.retry;
|
|
237
238
|
const errorCode = output.code.toString();
|
|
238
239
|
const policy = policies?.[errorCode];
|
|
239
240
|
const maxRetries = policy?.[0];
|
|
241
|
+
// if (isUnhandledEngineError && !policy) {
|
|
242
|
+
// //if main goes down, replicas take over within 5s
|
|
243
|
+
// //if this is not system/platform related, the exponential
|
|
244
|
+
// //backoff will be applied and eventually slow to a crawl while
|
|
245
|
+
// //the root cause is identified
|
|
246
|
+
// input.policies = { retry: { [errorCode]: [10] } };
|
|
247
|
+
// return [true, 0];
|
|
248
|
+
// }
|
|
240
249
|
const tryCount = Math.min(input.metadata.try || 0, HMSH_MAX_RETRIES);
|
|
241
250
|
//only possible values for maxRetries are 1, 2, 3
|
|
242
251
|
//only possible values for tryCount are 0, 1, 2
|
|
@@ -287,13 +296,15 @@ class Router {
|
|
|
287
296
|
const message = output.data?.message ? output.data?.message.toString() : HMSH_STATUS_UNKNOWN;
|
|
288
297
|
const statusCode = output.code || output.data?.code;
|
|
289
298
|
const code = isNaN(statusCode as number) ? HMSH_CODE_UNKNOWN : parseInt(statusCode.toString());
|
|
290
|
-
const
|
|
299
|
+
const stack = output.data?.stack ? output.data?.stack.toString() : undefined;
|
|
300
|
+
const data: StreamError = { message, code, stack };
|
|
291
301
|
if (typeof output.data?.error === 'object') {
|
|
292
302
|
data.error = { ...output.data.error };
|
|
293
303
|
}
|
|
294
304
|
return {
|
|
295
305
|
status: StreamStatus.ERROR,
|
|
296
306
|
code,
|
|
307
|
+
stack,
|
|
297
308
|
metadata: { ...input.metadata, guid: guid() },
|
|
298
309
|
data
|
|
299
310
|
} as StreamDataResponse;
|
|
@@ -362,7 +373,7 @@ class Router {
|
|
|
362
373
|
//The stream activity was not processed within established limits. Possibilities Include:
|
|
363
374
|
// 1) user error: the workers were not properly configured and are timing out
|
|
364
375
|
// 2a) system error: JSON is corrupt
|
|
365
|
-
// i)
|
|
376
|
+
// i) unwitting actor
|
|
366
377
|
// ii) corrupt hardware/network/transport/etc
|
|
367
378
|
// 3b) system error: Redis unable to accept `xadd` request
|
|
368
379
|
// 4c) system error: Redis unable to accept `xdel`/`xack` request
|