@hotmeshio/hotmesh 0.0.10 → 0.0.11

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.
Files changed (42) hide show
  1. package/README.md +5 -4
  2. package/build/modules/errors.d.ts +17 -1
  3. package/build/modules/errors.js +29 -1
  4. package/build/package.json +2 -1
  5. package/build/services/activities/activity.d.ts +1 -0
  6. package/build/services/activities/activity.js +13 -2
  7. package/build/services/activities/cycle.js +6 -1
  8. package/build/services/activities/trigger.js +2 -3
  9. package/build/services/collator/index.d.ts +8 -0
  10. package/build/services/collator/index.js +11 -1
  11. package/build/services/durable/factory.d.ts +18 -1
  12. package/build/services/durable/factory.js +46 -4
  13. package/build/services/durable/handle.js +25 -7
  14. package/build/services/durable/worker.d.ts +3 -3
  15. package/build/services/durable/worker.js +16 -10
  16. package/build/services/durable/workflow.js +1 -1
  17. package/build/services/pipe/functions/math.d.ts +4 -0
  18. package/build/services/pipe/functions/math.js +73 -0
  19. package/build/services/pipe/functions/number.d.ts +0 -4
  20. package/build/services/pipe/functions/number.js +0 -73
  21. package/build/services/signaler/stream.js +6 -3
  22. package/build/types/durable.d.ts +7 -2
  23. package/build/types/index.d.ts +1 -0
  24. package/modules/errors.ts +42 -1
  25. package/package.json +2 -1
  26. package/services/activities/activity.ts +14 -2
  27. package/services/activities/cycle.ts +6 -1
  28. package/services/activities/trigger.ts +2 -3
  29. package/services/collator/index.ts +12 -1
  30. package/services/durable/factory.ts +46 -4
  31. package/services/durable/handle.ts +23 -8
  32. package/services/durable/worker.ts +27 -12
  33. package/services/durable/workflow.ts +1 -1
  34. package/services/pipe/functions/math.ts +74 -0
  35. package/services/pipe/functions/number.ts +0 -75
  36. package/services/signaler/stream.ts +6 -3
  37. package/types/durable.ts +11 -4
  38. package/types/index.ts +15 -0
  39. package/build/services/dimension/index.d.ts +0 -29
  40. package/build/services/dimension/index.js +0 -35
  41. package/services/dimension/README.md +0 -73
  42. package/services/dimension/index.ts +0 -39
package/README.md CHANGED
@@ -11,9 +11,9 @@ npm install @hotmeshio/hotmesh
11
11
  ```
12
12
 
13
13
  ## Design
14
- The HotMesh SDK is designed to keep your code front-and-center. Write functions as you normally would, then use the HotMesh to make them durable.
14
+ The HotMesh SDK is designed to keep your code front-and-center. Write functions as you normally would, then use HotMesh to make them durable.
15
15
 
16
- 1. Start by defining **activities**. Activities are those functions that will be invoked by your workflow. They are commonly used to read and write to databases and invoke external services. They can be written in any style, using any framework, and can even be legacy functions you've already written. The only requirement is that they return a Promise.
16
+ 1. Start by defining **activities**. Activities are those functions that will be invoked by your workflow. They are commonly used to read and write to databases and invoke external services. They can be written in any style, using any framework, and can even be legacy functions you've already written. The only requirement is that they return a Promise. *Note how the `saludar` example throws an error 50% of the time. It doesn't matter how unpredictable your functions are, HotMesh will retry as necessary until they succeed.*
17
17
  ```javascript
18
18
  //activities.ts
19
19
 
@@ -22,6 +22,7 @@ The HotMesh SDK is designed to keep your code front-and-center. Write functions
22
22
  }
23
23
 
24
24
  export async function saludar(nombre: string): Promise<string> {
25
+ Math.random() > 0.5 && throw new Error('Random error');
25
26
  return `¡Hola, ${nombre}!`;
26
27
  }
27
28
  ```
@@ -74,7 +75,7 @@ The HotMesh SDK is designed to keep your code front-and-center. Write functions
74
75
  }
75
76
  ```
76
77
 
77
- 4. The last step is to create a **worker** that executes the workflow. Workers listen for tasks on their assigned channel, executing their assigned workflow function until it succeeds.
78
+ 4. The last step is to create a **worker** and link it to your workflow function. Workers listen for tasks on their assigned channel, executing the linked workflow function until it succeeds.
78
79
  ```javascript
79
80
  //worker.ts
80
81
 
@@ -232,7 +233,7 @@ For a deep dive into HotMesh's distributed orchestration philosophy, refer to th
232
233
  HotMesh is a distributed orchestration engine. Refer to the [Distributed Orchestration Guide](https://github.com/hotmeshio/sdk-typescript/blob/main/docs/distributed_orchestration.md) for a detailed breakdown of the approach.
233
234
 
234
235
  ## System Lifecycle
235
- Gain insight into the HotMesh's monitoring, exception handling, and alarm configurations via the [System Lifecycle Guide](https://github.com/hotmeshio/sdk-typescript/blob/main/docs/system_lifecycle.md).
236
+ Gain insight into HotMesh's monitoring, exception handling, and alarm configurations via the [System Lifecycle Guide](https://github.com/hotmeshio/sdk-typescript/blob/main/docs/system_lifecycle.md).
236
237
 
237
238
  ## Alpha Release
238
239
  So what exacty is an [alpha release](https://github.com/hotmeshio/sdk-typescript/blob/main/docs/alpha.md)?!
@@ -6,6 +6,22 @@ declare class GetStateError extends Error {
6
6
  declare class SetStateError extends Error {
7
7
  constructor();
8
8
  }
9
+ declare class DurableTimeoutError extends Error {
10
+ code: number;
11
+ constructor(message: string);
12
+ }
13
+ declare class DurableMaxedError extends Error {
14
+ code: number;
15
+ constructor(message: string);
16
+ }
17
+ declare class DurableFatalError extends Error {
18
+ code: number;
19
+ constructor(message: string);
20
+ }
21
+ declare class DurableRetryError extends Error {
22
+ code: number;
23
+ constructor(message: string);
24
+ }
9
25
  declare class MapDataError extends Error {
10
26
  constructor();
11
27
  }
@@ -25,4 +41,4 @@ declare class CollationError extends Error {
25
41
  fault: CollationFaultType;
26
42
  constructor(status: number, leg: ActivityDuplex, stage: CollationStage, fault?: CollationFaultType);
27
43
  }
28
- export { CollationError, DuplicateJobError, GetStateError, SetStateError, MapDataError, RegisterTimeoutError, ExecActivityError };
44
+ export { CollationError, DurableTimeoutError, DurableMaxedError, DurableFatalError, DurableRetryError, DuplicateJobError, GetStateError, SetStateError, MapDataError, RegisterTimeoutError, ExecActivityError };
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ExecActivityError = exports.RegisterTimeoutError = exports.MapDataError = exports.SetStateError = exports.GetStateError = exports.DuplicateJobError = exports.CollationError = void 0;
3
+ exports.ExecActivityError = exports.RegisterTimeoutError = exports.MapDataError = exports.SetStateError = exports.GetStateError = exports.DuplicateJobError = exports.DurableRetryError = exports.DurableFatalError = exports.DurableMaxedError = exports.DurableTimeoutError = exports.CollationError = void 0;
4
4
  class GetStateError extends Error {
5
5
  constructor() {
6
6
  super("Error occurred while getting job state");
@@ -13,6 +13,34 @@ class SetStateError extends Error {
13
13
  }
14
14
  }
15
15
  exports.SetStateError = SetStateError;
16
+ class DurableTimeoutError extends Error {
17
+ constructor(message) {
18
+ super(message);
19
+ this.code = 596;
20
+ }
21
+ }
22
+ exports.DurableTimeoutError = DurableTimeoutError;
23
+ class DurableMaxedError extends Error {
24
+ constructor(message) {
25
+ super(message);
26
+ this.code = 597;
27
+ }
28
+ }
29
+ exports.DurableMaxedError = DurableMaxedError;
30
+ class DurableFatalError extends Error {
31
+ constructor(message) {
32
+ super(message);
33
+ this.code = 598;
34
+ }
35
+ }
36
+ exports.DurableFatalError = DurableFatalError;
37
+ class DurableRetryError extends Error {
38
+ constructor(message) {
39
+ super(message);
40
+ this.code = 599;
41
+ }
42
+ }
43
+ exports.DurableRetryError = DurableRetryError;
16
44
  class MapDataError extends Error {
17
45
  constructor() {
18
46
  super("Error occurred while mapping data");
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hotmeshio/hotmesh",
3
- "version": "0.0.10",
3
+ "version": "0.0.11",
4
4
  "description": "Durable Workflows",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -43,6 +43,7 @@
43
43
  "test:durable:hello": "NODE_ENV=test jest ./tests/durable/helloworld/index.test.ts --detectOpenHandles --forceExit --verbose",
44
44
  "test:durable:goodbye": "NODE_ENV=test jest ./tests/durable/goodbye/index.test.ts --detectOpenHandles --forceExit --verbose",
45
45
  "test:durable:retry": "NODE_ENV=test jest ./tests/durable/retry/index.test.ts --detectOpenHandles --forceExit --verbose",
46
+ "test:durable:fatal": "NODE_ENV=test jest ./tests/durable/fatal/index.test.ts --detectOpenHandles --forceExit --verbose",
46
47
  "test:durable:loopactivity": "NODE_ENV=test jest ./tests/durable/loopactivity/index.test.ts --detectOpenHandles --forceExit --verbose",
47
48
  "test:durable:nested": "NODE_ENV=test jest ./tests/durable/nested/index.test.ts --detectOpenHandles --forceExit --verbose"
48
49
  },
@@ -40,6 +40,7 @@ declare class Activity {
40
40
  resolveStatus(multiResponse: MultiResponseFlags): number;
41
41
  mapJobData(): void;
42
42
  mapInputData(): void;
43
+ mapOutputData(): void;
43
44
  registerTimeout(): Promise<void>;
44
45
  bindActivityError(data: Record<string, unknown>): void;
45
46
  getTriggerConfig(): Promise<ActivityType>;
@@ -4,7 +4,6 @@ exports.Activity = void 0;
4
4
  const errors_1 = require("../../modules/errors");
5
5
  const utils_1 = require("../../modules/utils");
6
6
  const collator_1 = require("../collator");
7
- const dimension_1 = require("../dimension");
8
7
  const mapper_1 = require("../mapper");
9
8
  const pipe_1 = require("../pipe");
10
9
  const serializer_1 = require("../serializer");
@@ -43,6 +42,7 @@ class Activity {
43
42
  if (this.doesHook()) {
44
43
  //sleep and wait to awaken upon a signal
45
44
  await this.registerHook(multi);
45
+ this.mapOutputData();
46
46
  this.mapJobData();
47
47
  await this.setState(multi);
48
48
  await collator_1.CollatorService.authorizeReentry(this, multi);
@@ -53,6 +53,7 @@ class Activity {
53
53
  else {
54
54
  //end the activity and transition to its children
55
55
  this.adjacencyList = await this.filterAdjacent();
56
+ this.mapOutputData();
56
57
  this.mapJobData();
57
58
  await this.setState(multi);
58
59
  await collator_1.CollatorService.notarizeEarlyCompletion(this, multi);
@@ -265,6 +266,16 @@ class Activity {
265
266
  this.context.data = mapper.mapRules();
266
267
  }
267
268
  }
269
+ mapOutputData() {
270
+ //activity YAML may include output map data that produces/extends activity output data.
271
+ if (this.config.output?.maps) {
272
+ const mapper = new mapper_1.MapperService(this.config.output.maps, this.context);
273
+ const actOutData = mapper.mapRules();
274
+ const activityId = this.metadata.aid;
275
+ const data = { ...this.context[activityId].output, ...actOutData };
276
+ this.context[activityId].output.data = data;
277
+ }
278
+ }
268
279
  async registerTimeout() {
269
280
  //set timeout in support of hook and/or duplex
270
281
  }
@@ -442,7 +453,7 @@ class Activity {
442
453
  }
443
454
  resolveAdjacentDad() {
444
455
  //concat self and child dimension (all children (leg 1) begin life at 0)
445
- return `${this.resolveDad()}${dimension_1.DimensionService.getSeed(0)}`;
456
+ return `${this.resolveDad()}${collator_1.CollatorService.getDimensionalSeed(0)}`;
446
457
  }
447
458
  ;
448
459
  async filterAdjacent() {
@@ -63,13 +63,18 @@ class Cycle extends activity_1.Activity {
63
63
  * pattern allows for retries without violating the DAG.
64
64
  */
65
65
  async cycleAncestorActivity(multi) {
66
+ //Cycle activity L1 is a standin for the target ancestor L1.
67
+ //Input data mapping (mapInputData) allows for the
68
+ //next dimensonal thread to execute with different
69
+ //input data than the current dimensional thread
70
+ this.mapInputData();
66
71
  const streamData = {
67
72
  metadata: {
68
73
  dad: collator_1.CollatorService.resolveReentryDimension(this),
69
74
  jid: this.context.metadata.jid,
70
75
  aid: this.config.ancestor,
71
76
  },
72
- data: {} //todo: verify immutability, before enabling: `this.context.data`
77
+ data: this.context.data
73
78
  };
74
79
  return (await this.engine.streamSignaler?.publishMessage(null, streamData, multi));
75
80
  }
@@ -6,7 +6,6 @@ const errors_1 = require("../../modules/errors");
6
6
  const utils_1 = require("../../modules/utils");
7
7
  const activity_1 = require("./activity");
8
8
  const collator_1 = require("../collator");
9
- const dimension_1 = require("../dimension");
10
9
  const pipe_1 = require("../pipe");
11
10
  const reporter_1 = require("../reporter");
12
11
  const serializer_1 = require("../serializer");
@@ -80,7 +79,7 @@ class Trigger extends activity_1.Activity {
80
79
  const jobKey = this.resolveJobKey(inputContext);
81
80
  const utc = (0, utils_1.formatISODate)(new Date());
82
81
  const { id, version } = await this.engine.getVID();
83
- this.initDimensionalAddress(dimension_1.DimensionService.getSeed());
82
+ this.initDimensionalAddress(collator_1.CollatorService.getDimensionalSeed());
84
83
  const activityMetadata = {
85
84
  ...this.metadata,
86
85
  jid: jobId,
@@ -100,7 +99,7 @@ class Trigger extends activity_1.Activity {
100
99
  trc: this.context.metadata.trc,
101
100
  spn: this.context.metadata.spn,
102
101
  jid: jobId,
103
- dad: dimension_1.DimensionService.getSeed(),
102
+ dad: collator_1.CollatorService.getDimensionalSeed(),
104
103
  key: jobKey,
105
104
  jc: utc,
106
105
  ju: utc,
@@ -69,5 +69,13 @@ declare class CollatorService {
69
69
  */
70
70
  static bindAncestorArray(graphs: HotMeshGraph[]): void;
71
71
  static isActivityComplete(status: number): boolean;
72
+ /**
73
+ * All activities exist on a dimensional plane. Zero
74
+ * is the default. A value of
75
+ * `AxY,0,0,0,0,1,0,0` would reflect that
76
+ * an ancestor activity was dimensionalized beyond
77
+ * the default.
78
+ */
79
+ static getDimensionalSeed(index?: number): string;
72
80
  }
73
81
  export { CollatorService };
@@ -75,7 +75,7 @@ class CollatorService {
75
75
  static async notarizeCompletion(activity, multi) {
76
76
  //1) ALWAYS actualize leg2 dimension (+1)
77
77
  //2) IF the activity is used in a cycle, don't close leg 2!
78
- const decrement = activity.config.cycle ? 0 : -1000000000000;
78
+ const decrement = activity.config.cycle ? 0 : 1000000000000;
79
79
  return await activity.store.collate(activity.context.metadata.jid, activity.metadata.aid, 1 - decrement, this.getDimensionalAddress(activity), multi);
80
80
  }
81
81
  ;
@@ -218,6 +218,16 @@ class CollatorService {
218
218
  static isActivityComplete(status) {
219
219
  return (status - 0) <= 0;
220
220
  }
221
+ /**
222
+ * All activities exist on a dimensional plane. Zero
223
+ * is the default. A value of
224
+ * `AxY,0,0,0,0,1,0,0` would reflect that
225
+ * an ancestor activity was dimensionalized beyond
226
+ * the default.
227
+ */
228
+ static getDimensionalSeed(index = 0) {
229
+ return `,${index}`;
230
+ }
221
231
  }
222
232
  exports.CollatorService = CollatorService;
223
233
  //max int digit count that supports `hincrby`
@@ -1,3 +1,20 @@
1
- declare const getWorkflowYAML: (topic: string, version?: string) => string;
1
+ /**
2
+ * 1) `maxSystemRetries` | can be 0 to 3 and represents milliseconds;
3
+ * if there is an error, the workflow will retry up to `maxSystemRetries` times
4
+ * delaying by 10, 100, and 1000ms; this is a system level retry
5
+ * and is not configurable. It exists to handle intermittent network
6
+ * errors. (NOTE: each retry spawns a new transition stream)
7
+ *
8
+ * 2) `backoffExponent` | can be any number and represents `seconds` when applied;
9
+ * retries will happen indefinitely and adhere to the
10
+ * exponential backoff algorithm by multiplying by `backoffExponent`.
11
+ * For example, if `backoffExponent` is 10, the workflow will retry
12
+ * in 10s, 100s, 1000s, 10000s, etc.
13
+ *
14
+ * EXAMPLE | Using `maxSystemRetries = 3` and `backoffExponent = 10`, errant
15
+ * workflows will be retried on the following schedule (8 times in 27 hours):
16
+ * => 10ms, 100ms, 1000ms, 10s, 100s, 1_000s, 10_000s, 100_000s
17
+ */
18
+ declare const getWorkflowYAML: (topic: string, version?: string, maxSystemRetries?: number, backoffExponent?: number) => string;
2
19
  declare const getActivityYAML: (topic: string, version?: string) => string;
3
20
  export { getActivityYAML, getWorkflowYAML };
@@ -1,7 +1,24 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getWorkflowYAML = exports.getActivityYAML = void 0;
4
- const getWorkflowYAML = (topic, version = '1') => {
4
+ /**
5
+ * 1) `maxSystemRetries` | can be 0 to 3 and represents milliseconds;
6
+ * if there is an error, the workflow will retry up to `maxSystemRetries` times
7
+ * delaying by 10, 100, and 1000ms; this is a system level retry
8
+ * and is not configurable. It exists to handle intermittent network
9
+ * errors. (NOTE: each retry spawns a new transition stream)
10
+ *
11
+ * 2) `backoffExponent` | can be any number and represents `seconds` when applied;
12
+ * retries will happen indefinitely and adhere to the
13
+ * exponential backoff algorithm by multiplying by `backoffExponent`.
14
+ * For example, if `backoffExponent` is 10, the workflow will retry
15
+ * in 10s, 100s, 1000s, 10000s, etc.
16
+ *
17
+ * EXAMPLE | Using `maxSystemRetries = 3` and `backoffExponent = 10`, errant
18
+ * workflows will be retried on the following schedule (8 times in 27 hours):
19
+ * => 10ms, 100ms, 1000ms, 10s, 100s, 1_000s, 10_000s, 100_000s
20
+ */
21
+ const getWorkflowYAML = (topic, version = '1', maxSystemRetries = 2, backoffExponent = 10) => {
5
22
  return `app:
6
23
  id: ${topic}
7
24
  version: '${version}'
@@ -33,10 +50,20 @@ const getWorkflowYAML = (topic, version = '1') => {
33
50
  a1:
34
51
  type: activity
35
52
  cycle: true
36
-
53
+ output:
54
+ schema:
55
+ type: object
56
+ properties:
57
+ duration:
58
+ type: number
59
+ maps:
60
+ duration: ${backoffExponent}
61
+
37
62
  w1:
38
63
  type: worker
39
64
  topic: ${topic}
65
+ retry:
66
+ '599': [${maxSystemRetries}]
40
67
  input:
41
68
  schema:
42
69
  type: object
@@ -58,18 +85,33 @@ const getWorkflowYAML = (topic, version = '1') => {
58
85
  maps:
59
86
  response: '{$self.output.data.response}'
60
87
 
88
+ a599:
89
+ title: Sleep before trying again
90
+ type: activity
91
+ sleep: "{a1.output.data.duration}"
92
+
61
93
  c1:
94
+ title: Goto Activity a1
62
95
  type: cycle
63
96
  ancestor: a1
97
+ input:
98
+ maps:
99
+ duration:
100
+ '@pipe':
101
+ - ['{a1.output.data.duration}', ${backoffExponent}]
102
+ - ['{@math.multiply}']
103
+
64
104
  transitions:
65
105
  t1:
66
106
  - to: a1
67
107
  a1:
68
108
  - to: w1
69
109
  w1:
70
- - to: c1
110
+ - to: a599
71
111
  conditions:
72
- code: 500
112
+ code: 599
113
+ a599:
114
+ - to: c1
73
115
  `;
74
116
  };
75
117
  exports.getWorkflowYAML = getWorkflowYAML;
@@ -10,22 +10,40 @@ class WorkflowHandleService {
10
10
  async result() {
11
11
  let status = await this.hotMesh.getStatus(this.workflowId);
12
12
  const topic = `${this.workflowTopic}.${this.workflowId}`;
13
- if (status == 0) {
14
- return (await this.hotMesh.getState(this.workflowTopic, this.workflowId)).data?.response;
15
- }
16
13
  return new Promise((resolve, reject) => {
17
14
  let isResolved = false;
18
15
  //common fulfill/unsubscribe
19
- const complete = async (response) => {
16
+ const complete = async (response, err) => {
20
17
  if (isResolved)
21
18
  return;
22
19
  isResolved = true;
23
20
  this.hotMesh.unsub(topic);
24
- resolve(response || (await this.hotMesh.getState(this.workflowTopic, this.workflowId)).data?.response);
21
+ if (err) {
22
+ return reject(JSON.parse(err));
23
+ }
24
+ else if (!response) {
25
+ const state = await this.hotMesh.getState(this.workflowTopic, this.workflowId);
26
+ if (!state.data && state.metadata.err) {
27
+ return reject(JSON.parse(state.metadata.err));
28
+ }
29
+ response = state.data?.response;
30
+ }
31
+ resolve(response);
25
32
  };
26
- this.hotMesh.sub(topic, async (topic, message) => {
27
- await complete(message.data?.response);
33
+ //check for done
34
+ if (status == 0) {
35
+ return complete();
36
+ }
37
+ //subscribe to topic
38
+ this.hotMesh.sub(topic, async (topic, state) => {
39
+ if (!state.data && state.metadata.err) {
40
+ await complete(null, state.metadata.err);
41
+ }
42
+ else {
43
+ await complete(state.data?.response);
44
+ }
28
45
  });
46
+ //resolve for race condition
29
47
  setTimeout(async () => {
30
48
  status = await this.hotMesh.getStatus(this.workflowId);
31
49
  if (status == 0) {
@@ -1,13 +1,13 @@
1
1
  import { HotMeshService as HotMesh } from '../hotmesh';
2
- import { Connection, Registry, WorkerConfig } from "../../types/durable";
2
+ import { Connection, Registry, WorkerConfig, WorkerOptions } from "../../types/durable";
3
3
  export declare class WorkerService {
4
4
  static activityRegistry: Registry;
5
5
  static connection: Connection;
6
6
  static instances: Map<string, HotMesh | Promise<HotMesh>>;
7
7
  workflowRunner: HotMesh;
8
8
  activityRunner: HotMesh;
9
- static getHotMesh: (worflowTopic: string) => Promise<HotMesh>;
10
- static activateWorkflow(hotMesh: HotMesh, topic: string, factory: Function): Promise<void>;
9
+ static getHotMesh: (worflowTopic: string, options?: WorkerOptions) => Promise<HotMesh>;
10
+ static activateWorkflow(hotMesh: HotMesh, topic: string, dagFactory: Function, options?: WorkerOptions): Promise<void>;
11
11
  /**
12
12
  * The `worker` calls `registerActivities` immediately BEFORE
13
13
  * dynamically importing the user's workflow module. That file
@@ -6,6 +6,7 @@ const asyncLocalStorage_1 = require("./asyncLocalStorage");
6
6
  const hotmesh_1 = require("../hotmesh");
7
7
  const stream_1 = require("../../types/stream");
8
8
  const factory_1 = require("./factory");
9
+ const errors_1 = require("../../modules/errors");
9
10
  /*
10
11
  Here is an example of how the methods in this file are used:
11
12
 
@@ -39,13 +40,13 @@ run().catch((err) => {
39
40
  });
40
41
  */
41
42
  class WorkerService {
42
- static async activateWorkflow(hotMesh, topic, factory) {
43
+ static async activateWorkflow(hotMesh, topic, dagFactory, options = {}) {
43
44
  const version = '1';
44
45
  const app = await hotMesh.engine.store.getApp(topic);
45
46
  const appVersion = app?.version;
46
47
  if (!appVersion) {
47
48
  try {
48
- await hotMesh.deploy(factory(topic, version));
49
+ await hotMesh.deploy(dagFactory(topic, version, options.maxSystemRetries, options.backoffExponent));
49
50
  await hotMesh.activate(version);
50
51
  }
51
52
  catch (err) {
@@ -99,7 +100,7 @@ class WorkerService {
99
100
  worker.activityRunner = await worker.initActivityWorkflow(config, activityTopic);
100
101
  await WorkerService.activateWorkflow(worker.activityRunner, activityTopic, factory_1.getActivityYAML);
101
102
  worker.workflowRunner = await worker.initWorkerWorkflow(config, workflowTopic, workflowFunction);
102
- await WorkerService.activateWorkflow(worker.workflowRunner, workflowTopic, factory_1.getWorkflowYAML);
103
+ await WorkerService.activateWorkflow(worker.workflowRunner, workflowTopic, factory_1.getWorkflowYAML, config.options);
103
104
  return worker;
104
105
  }
105
106
  static resolveWorkflowTarget(workflow) {
@@ -151,12 +152,16 @@ class WorkerService {
151
152
  }
152
153
  catch (err) {
153
154
  this.activityRunner.engine.logger.error('durable-worker-activity-err', err);
155
+ if (!(err instanceof errors_1.DurableTimeoutError) &&
156
+ !(err instanceof errors_1.DurableMaxedError) &&
157
+ !(err instanceof errors_1.DurableFatalError)) {
158
+ err = new errors_1.DurableRetryError(err.message);
159
+ }
154
160
  return {
155
161
  status: stream_1.StreamStatus.ERROR,
156
- code: 500,
157
- message: err.message,
162
+ code: err.code,
158
163
  metadata: { ...data.metadata },
159
- data: { error: err }
164
+ data: { message: err.message }
160
165
  };
161
166
  }
162
167
  };
@@ -227,11 +232,12 @@ class WorkerService {
227
232
  };
228
233
  }
229
234
  catch (err) {
235
+ // 59* - Durable*Error
230
236
  return {
231
237
  status: stream_1.StreamStatus.ERROR,
232
- code: 500,
238
+ code: err.code || new errors_1.DurableRetryError(err.message).code,
233
239
  metadata: { ...data.metadata },
234
- data: { error: err }
240
+ data: { message: err.message, type: err.name }
235
241
  };
236
242
  }
237
243
  };
@@ -246,7 +252,7 @@ class WorkerService {
246
252
  _a = WorkerService;
247
253
  WorkerService.activityRegistry = {}; //user's activities
248
254
  WorkerService.instances = new Map();
249
- WorkerService.getHotMesh = async (worflowTopic) => {
255
+ WorkerService.getHotMesh = async (worflowTopic, options) => {
250
256
  if (WorkerService.instances.has(worflowTopic)) {
251
257
  return await WorkerService.instances.get(worflowTopic);
252
258
  }
@@ -255,7 +261,7 @@ WorkerService.getHotMesh = async (worflowTopic) => {
255
261
  engine: { redis: { ...WorkerService.connection } }
256
262
  });
257
263
  WorkerService.instances.set(worflowTopic, hotMesh);
258
- await WorkerService.activateWorkflow(await hotMesh, worflowTopic, factory_1.getWorkflowYAML);
264
+ await WorkerService.activateWorkflow(await hotMesh, worflowTopic, factory_1.getWorkflowYAML, options);
259
265
  return hotMesh;
260
266
  };
261
267
  WorkerService.Context = {
@@ -62,7 +62,7 @@ class WorkflowService {
62
62
  const client = new client_1.ClientService({
63
63
  connection: await connection_1.ConnectionService.connect(worker_1.WorkerService.connection),
64
64
  });
65
- //todo: should I allow-cross/app callback (pj:'@DURABLE@hello-world@<pjid>'/pa: <paid>/pd: <pdad>)
65
+ //todo: allow cross/app callback (pj:'@DURABLE@hello-world@<pjid>'/pa: <paid>/pd: <pdad>)
66
66
  const handle = await client.workflow.start({
67
67
  ...options,
68
68
  workflowId: `${workflowId}${options.workflowId}`,
@@ -1,4 +1,8 @@
1
1
  declare class MathHandler {
2
+ add(...operands: (number | number[])[]): number;
3
+ subtract(...operands: (number | number[])[]): number;
4
+ multiply(...operands: (number | number[])[]): number;
5
+ divide(...operands: (number | number[])[]): number;
2
6
  abs(x: number): number;
3
7
  acos(x: number): number;
4
8
  acosh(x: number): number;
@@ -2,6 +2,79 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.MathHandler = void 0;
4
4
  class MathHandler {
5
+ add(...operands) {
6
+ // @ts-ignore
7
+ return operands.reduce((a, b) => {
8
+ if (Array.isArray(b)) {
9
+ return a + this.add(...b);
10
+ }
11
+ else {
12
+ return a + b;
13
+ }
14
+ }, 0);
15
+ }
16
+ subtract(...operands) {
17
+ if (operands.length === 0) {
18
+ throw new Error('At least one operand is required.');
19
+ }
20
+ let flatOperands = [];
21
+ operands.forEach((op) => {
22
+ if (Array.isArray(op)) {
23
+ flatOperands = [...flatOperands, ...op];
24
+ }
25
+ else {
26
+ flatOperands.push(op);
27
+ }
28
+ });
29
+ if (flatOperands.length === 0) {
30
+ throw new Error('At least one operand is required after flattening.');
31
+ }
32
+ const result = flatOperands.reduce((a, b, i) => {
33
+ return i === 0 ? a : a - b;
34
+ });
35
+ return result;
36
+ }
37
+ multiply(...operands) {
38
+ if (operands.length === 0) {
39
+ throw new Error('At least one operand is required.');
40
+ }
41
+ // @ts-ignore
42
+ return operands.reduce((a, b) => {
43
+ if (Array.isArray(b)) {
44
+ return a * this.multiply(...b);
45
+ }
46
+ else {
47
+ return a * b;
48
+ }
49
+ }, 1);
50
+ }
51
+ divide(...operands) {
52
+ if (operands.length === 0) {
53
+ throw new Error('At least one operand is required.');
54
+ }
55
+ let flatOperands = [];
56
+ operands.forEach((op) => {
57
+ if (Array.isArray(op)) {
58
+ flatOperands = [...flatOperands, ...op];
59
+ }
60
+ else {
61
+ flatOperands.push(op);
62
+ }
63
+ });
64
+ if (flatOperands.length === 0) {
65
+ throw new Error('At least one operand is required after flattening.');
66
+ }
67
+ const result = flatOperands.reduce((a, b, i) => {
68
+ if (b === 0) {
69
+ return NaN;
70
+ }
71
+ return i === 0 ? a : a / b;
72
+ });
73
+ if (isNaN(result)) {
74
+ return NaN;
75
+ }
76
+ return result;
77
+ }
5
78
  abs(x) {
6
79
  return Math.abs(x);
7
80
  }
@@ -17,9 +17,5 @@ declare class NumberHandler {
17
17
  min(...values: number[]): number;
18
18
  pow(base: number, exponent: number): number;
19
19
  round(input: number): number;
20
- add(...operands: (number | number[])[]): number;
21
- subtract(...operands: (number | number[])[]): number;
22
- multiply(...operands: (number | number[])[]): number;
23
- divide(...operands: (number | number[])[]): number;
24
20
  }
25
21
  export { NumberHandler };