@hotmeshio/hotmesh 0.1.9 → 0.1.10

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hotmeshio/hotmesh",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "description": "Unbreakable Workflows",
5
5
  "main": "./build/index.js",
6
6
  "types": "./build/index.d.ts",
@@ -26,6 +26,12 @@ declare class Activity {
26
26
  adjacentIndex: number;
27
27
  constructor(config: ActivityType, data: ActivityData, metadata: ActivityMetadata, hook: ActivityData | null, engine: EngineService, context?: JobState);
28
28
  setLeg(leg: ActivityLeg): void;
29
+ /**
30
+ * A job is assumed to be complete when its status (a semaphore)
31
+ * reaches `0`. A different threshold can be set in the
32
+ * activity YAML, in support of Dynamic Activation Control.
33
+ */
34
+ mapStatusThreshold(): number;
29
35
  /**
30
36
  * Upon entering leg 1 of a duplexed activity
31
37
  */
@@ -55,7 +61,7 @@ declare class Activity {
55
61
  bindJobError(data: Record<string, unknown>): void;
56
62
  getTriggerConfig(): Promise<ActivityType>;
57
63
  getJobStatus(): null | number;
58
- setStatus(amount: number, multi?: RedisMulti): Promise<void>;
64
+ setStatus(amount: number, multi?: RedisMulti): Promise<void | any>;
59
65
  authorizeEntry(state: StringAnyType): string[];
60
66
  bindDimensionalAddress(state: StringAnyType): void;
61
67
  setState(multi?: RedisMulti): Promise<string>;
@@ -30,13 +30,44 @@ class Activity {
30
30
  setLeg(leg) {
31
31
  this.leg = leg;
32
32
  }
33
+ /**
34
+ * A job is assumed to be complete when its status (a semaphore)
35
+ * reaches `0`. A different threshold can be set in the
36
+ * activity YAML, in support of Dynamic Activation Control.
37
+ */
38
+ mapStatusThreshold() {
39
+ if (this.config.statusThreshold !== undefined) {
40
+ const threshold = pipe_1.Pipe.resolve(this.config.statusThreshold, this.context);
41
+ if (threshold !== undefined && !isNaN(Number(threshold))) {
42
+ return threshold;
43
+ }
44
+ }
45
+ return 0;
46
+ }
33
47
  /**
34
48
  * Upon entering leg 1 of a duplexed activity
35
49
  */
36
50
  async verifyEntry() {
37
51
  this.setLeg(1);
38
52
  await this.getState();
39
- collator_1.CollatorService.assertJobActive(this.context.metadata.js, this.context.metadata.jid, this.metadata.aid);
53
+ const threshold = this.mapStatusThreshold();
54
+ try {
55
+ collator_1.CollatorService.assertJobActive(this.context.metadata.js, this.context.metadata.jid, this.metadata.aid, threshold);
56
+ }
57
+ catch (error) {
58
+ if (threshold > 0) {
59
+ if (this.context.metadata.js === threshold) {
60
+ //conclude job EXACTLY ONCE
61
+ const status = await this.setStatus(-threshold);
62
+ if (Number(status) === 0) {
63
+ await this.engine.runJobCompletionTasks(this.context);
64
+ }
65
+ }
66
+ }
67
+ else {
68
+ throw error;
69
+ }
70
+ }
40
71
  await collator_1.CollatorService.notarizeEntry(this);
41
72
  }
42
73
  /**
@@ -247,7 +278,7 @@ class Activity {
247
278
  }
248
279
  async setStatus(amount, multi) {
249
280
  const { id: appId } = await this.engine.getVID();
250
- await this.store.setStatus(amount, this.context.metadata.jid, appId, multi);
281
+ return await this.store.setStatus(amount, this.context.metadata.jid, appId, multi);
251
282
  }
252
283
  authorizeEntry(state) {
253
284
  //pre-authorize activity state to allow entry for adjacent activities
@@ -9,7 +9,7 @@ declare class CollatorService {
9
9
  /**
10
10
  * Upon re/entry, verify that the job status is active
11
11
  */
12
- static assertJobActive(status: number, jobId: string, activityId: string): void;
12
+ static assertJobActive(status: number, jobId: string, activityId: string, threshold?: number): void;
13
13
  /**
14
14
  * returns the dimensional address (dad) for the target; due
15
15
  * to the nature of the notary system, the dad for leg 2 entry
@@ -7,8 +7,8 @@ class CollatorService {
7
7
  /**
8
8
  * Upon re/entry, verify that the job status is active
9
9
  */
10
- static assertJobActive(status, jobId, activityId) {
11
- if (status <= 0) {
10
+ static assertJobActive(status, jobId, activityId, threshold = 0) {
11
+ if (status <= threshold) {
12
12
  throw new errors_1.InactiveJobError(jobId, status, activityId);
13
13
  }
14
14
  }
@@ -78,6 +78,7 @@ class ClientService {
78
78
  arguments: [...options.args],
79
79
  originJobId: options.originJobId,
80
80
  expire: options.expire ?? enums_1.HMSH_EXPIRE_JOB_SECONDS,
81
+ signalIn: options.signalIn,
81
82
  parentWorkflowId: options.parentWorkflowId,
82
83
  workflowId: options.workflowId || hotmesh_1.HotMeshService.guid(),
83
84
  workflowTopic: workflowTopic,
@@ -21,7 +21,7 @@
21
21
  * * Service Meshes
22
22
  * * Master Data Management systems
23
23
  */
24
- declare const APP_VERSION = "1";
24
+ declare const APP_VERSION = "2";
25
25
  declare const APP_ID = "durable";
26
26
  /**
27
27
  * returns a new durable workflow schema
@@ -24,7 +24,7 @@
24
24
  */
25
25
  Object.defineProperty(exports, "__esModule", { value: true });
26
26
  exports.APP_ID = exports.APP_VERSION = exports.getWorkflowYAML = void 0;
27
- const APP_VERSION = '1';
27
+ const APP_VERSION = '2';
28
28
  exports.APP_VERSION = APP_VERSION;
29
29
  const APP_ID = 'durable';
30
30
  exports.APP_ID = APP_ID;
@@ -81,6 +81,9 @@ const getWorkflowYAML = (app, version) => {
81
81
  expire:
82
82
  description: the time in seconds to expire the workflow in Redis once it completes
83
83
  type: number
84
+ signalIn:
85
+ description: if false, the job will not support subordinated hooks
86
+ type: boolean
84
87
 
85
88
  output:
86
89
  schema:
@@ -806,6 +809,7 @@ const getWorkflowYAML = (app, version) => {
806
809
  signaler:
807
810
  title: Signal-In Reentry point for subordinated hook flows
808
811
  type: hook
812
+ statusThreshold: 1
809
813
  hook:
810
814
  type: object
811
815
  properties:
@@ -1462,6 +1466,13 @@ const getWorkflowYAML = (app, version) => {
1462
1466
  trigger:
1463
1467
  - to: cycle_hook
1464
1468
  - to: signaler
1469
+ conditions:
1470
+ match:
1471
+ - expected: true
1472
+ actual:
1473
+ '@pipe':
1474
+ - ['{$self.output.data.signalIn}', true]
1475
+ - ['{@conditional.nullish}']
1465
1476
  ## MAIN PROCESS TRANSITIONS ##
1466
1477
  cycle_hook:
1467
1478
  - to: throttler
@@ -2,6 +2,8 @@ declare class ConditionalHandler {
2
2
  ternary(condition: boolean, valueIfTrue: any, valueIfFalse: any): any;
3
3
  equality(value1: any, value2: any): boolean;
4
4
  strict_equality(value1: any, value2: any): boolean;
5
+ inequality(value1: any, value2: any): boolean;
6
+ strict_inequality(value1: any, value2: any): boolean;
5
7
  greater_than(value1: number, value2: number): boolean;
6
8
  less_than(value1: number, value2: number): boolean;
7
9
  greater_than_or_equal(value1: number, value2: number): boolean;
@@ -11,6 +11,12 @@ class ConditionalHandler {
11
11
  strict_equality(value1, value2) {
12
12
  return value1 === value2;
13
13
  }
14
+ inequality(value1, value2) {
15
+ return value1 != value2;
16
+ }
17
+ strict_inequality(value1, value2) {
18
+ return value1 !== value2;
19
+ }
14
20
  greater_than(value1, value2) {
15
21
  return value1 > value2;
16
22
  }
@@ -6,6 +6,8 @@ interface BaseActivity {
6
6
  title?: string;
7
7
  type?: ActivityExecutionType;
8
8
  subtype?: string;
9
+ statusThreshold?: number;
10
+ statusThresholdType?: 'stop' | 'throw' | 'stall';
9
11
  input?: Record<string, any>;
10
12
  output?: Record<string, any>;
11
13
  settings?: Record<string, any>;
@@ -260,6 +260,10 @@ type WorkflowOptions = {
260
260
  * sets the number of seconds a workflow may exist after completion. As the process engine is an in-memory cache, the default policy is to expire and scrub the job hash as soon as it completes.
261
261
  */
262
262
  expire?: number;
263
+ /**
264
+ * default is true; set to false to optimize workflows that do not require a `signal in`
265
+ */
266
+ signalIn?: boolean;
263
267
  /**
264
268
  * default is true; if false, will not await the execution
265
269
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hotmeshio/hotmesh",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "description": "Unbreakable Workflows",
5
5
  "main": "./build/index.js",
6
6
  "types": "./build/index.d.ts",
package/types/activity.ts CHANGED
@@ -18,6 +18,8 @@ interface BaseActivity {
18
18
  title?: string;
19
19
  type?: ActivityExecutionType;
20
20
  subtype?: string;
21
+ statusThreshold?: number; //default is 0; set to 1 to ensure not last standing; message will be ignored as if too late to process
22
+ statusThresholdType?: 'stop' | 'throw' | 'stall'; //default is stop; must explicitly set to throw to throw an error or stall to stall
21
23
  input?: Record<string, any>;
22
24
  output?: Record<string, any>;
23
25
  settings?: Record<string, any>;
package/types/durable.ts CHANGED
@@ -314,6 +314,11 @@ type WorkflowOptions = {
314
314
  */
315
315
  expire?: number;
316
316
 
317
+ /**
318
+ * default is true; set to false to optimize workflows that do not require a `signal in`
319
+ */
320
+ signalIn?: boolean;
321
+
317
322
  /**
318
323
  * default is true; if false, will not await the execution
319
324
  */