@hotmeshio/hotmesh 0.0.41 → 0.0.43

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 (44) hide show
  1. package/build/modules/enums.d.ts +2 -0
  2. package/build/modules/enums.js +4 -1
  3. package/build/modules/errors.d.ts +1 -8
  4. package/build/modules/errors.js +1 -12
  5. package/build/modules/utils.js +1 -1
  6. package/build/package.json +1 -1
  7. package/build/services/activities/activity.d.ts +8 -1
  8. package/build/services/activities/activity.js +17 -12
  9. package/build/services/collator/index.d.ts +20 -2
  10. package/build/services/collator/index.js +41 -7
  11. package/build/services/durable/client.d.ts +2 -1
  12. package/build/services/durable/client.js +17 -3
  13. package/build/services/durable/factory.d.ts +0 -1
  14. package/build/services/durable/factory.js +0 -138
  15. package/build/services/durable/meshos.js +3 -0
  16. package/build/services/durable/worker.js +0 -15
  17. package/build/services/durable/workflow.d.ts +0 -9
  18. package/build/services/durable/workflow.js +0 -29
  19. package/build/services/engine/index.d.ts +1 -1
  20. package/build/services/engine/index.js +5 -8
  21. package/build/services/quorum/index.d.ts +5 -2
  22. package/build/services/quorum/index.js +32 -15
  23. package/build/services/store/clients/redis.js +1 -0
  24. package/build/services/store/index.d.ts +13 -1
  25. package/build/services/store/index.js +22 -6
  26. package/build/types/hotmesh.d.ts +1 -1
  27. package/build/types/job.d.ts +1 -0
  28. package/modules/enums.ts +4 -0
  29. package/modules/errors.ts +0 -15
  30. package/modules/utils.ts +1 -1
  31. package/package.json +1 -1
  32. package/services/activities/activity.ts +30 -15
  33. package/services/collator/index.ts +41 -8
  34. package/services/durable/client.ts +19 -4
  35. package/services/durable/factory.ts +0 -138
  36. package/services/durable/meshos.ts +3 -0
  37. package/services/durable/worker.ts +0 -16
  38. package/services/durable/workflow.ts +0 -32
  39. package/services/engine/index.ts +5 -6
  40. package/services/quorum/index.ts +35 -12
  41. package/services/store/clients/redis.ts +1 -0
  42. package/services/store/index.ts +25 -7
  43. package/types/hotmesh.ts +1 -1
  44. package/types/job.ts +1 -0
@@ -15,6 +15,8 @@ export declare const HMSH_CODE_DURABLE_MAXED = 597;
15
15
  export declare const HMSH_CODE_DURABLE_FATAL = 598;
16
16
  export declare const HMSH_CODE_DURABLE_RETRYABLE = 599;
17
17
  export declare const HMSH_STATUS_UNKNOWN = "unknown";
18
+ export declare const HMSH_QUORUM_DELAY_MS = 250;
19
+ export declare const HMSH_ACTIVATION_MAX_RETRY = 3;
18
20
  export declare const HMSH_OTT_WAIT_TIME: number;
19
21
  export declare const HMSH_EXPIRE_JOB_SECONDS: number;
20
22
  export declare const HMSH_MAX_RETRIES: number;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.HMSH_SCOUT_INTERVAL_SECONDS = exports.HMSH_FIDELITY_SECONDS = exports.HMSH_EXPIRE_DURATION = exports.HMSH_XPENDING_COUNT = exports.HMSH_XCLAIM_COUNT = exports.HMSH_XCLAIM_DELAY_MS = exports.HMSH_BLOCK_TIME_MS = exports.HMSH_GRADUATED_INTERVAL_MS = exports.HMSH_MAX_TIMEOUT_MS = exports.HMSH_MAX_RETRIES = exports.HMSH_EXPIRE_JOB_SECONDS = exports.HMSH_OTT_WAIT_TIME = exports.HMSH_STATUS_UNKNOWN = exports.HMSH_CODE_DURABLE_RETRYABLE = exports.HMSH_CODE_DURABLE_FATAL = exports.HMSH_CODE_DURABLE_MAXED = exports.HMSH_CODE_DURABLE_TIMEOUT = exports.HMSH_CODE_DURABLE_WAITFOR = exports.HMSH_CODE_DURABLE_INCOMPLETE = exports.HMSH_CODE_DURABLE_SLEEPFOR = exports.HMSH_CODE_UNACKED = exports.HMSH_CODE_TIMEOUT = exports.HMSH_CODE_UNKNOWN = exports.HMSH_CODE_INTERRUPT = exports.HMSH_CODE_NOTFOUND = exports.HMSH_CODE_PENDING = exports.HMSH_CODE_SUCCESS = exports.HMSH_LOGLEVEL = void 0;
3
+ exports.HMSH_SCOUT_INTERVAL_SECONDS = exports.HMSH_FIDELITY_SECONDS = exports.HMSH_EXPIRE_DURATION = exports.HMSH_XPENDING_COUNT = exports.HMSH_XCLAIM_COUNT = exports.HMSH_XCLAIM_DELAY_MS = exports.HMSH_BLOCK_TIME_MS = exports.HMSH_GRADUATED_INTERVAL_MS = exports.HMSH_MAX_TIMEOUT_MS = exports.HMSH_MAX_RETRIES = exports.HMSH_EXPIRE_JOB_SECONDS = exports.HMSH_OTT_WAIT_TIME = exports.HMSH_ACTIVATION_MAX_RETRY = exports.HMSH_QUORUM_DELAY_MS = exports.HMSH_STATUS_UNKNOWN = exports.HMSH_CODE_DURABLE_RETRYABLE = exports.HMSH_CODE_DURABLE_FATAL = exports.HMSH_CODE_DURABLE_MAXED = exports.HMSH_CODE_DURABLE_TIMEOUT = exports.HMSH_CODE_DURABLE_WAITFOR = exports.HMSH_CODE_DURABLE_INCOMPLETE = exports.HMSH_CODE_DURABLE_SLEEPFOR = exports.HMSH_CODE_UNACKED = exports.HMSH_CODE_TIMEOUT = exports.HMSH_CODE_UNKNOWN = exports.HMSH_CODE_INTERRUPT = exports.HMSH_CODE_NOTFOUND = exports.HMSH_CODE_PENDING = exports.HMSH_CODE_SUCCESS = exports.HMSH_LOGLEVEL = void 0;
4
4
  // HOTMESH SYSTEM
5
5
  exports.HMSH_LOGLEVEL = process.env.HMSH_LOGLEVEL || 'info';
6
6
  // STATUS CODES AND MESSAGES
@@ -19,6 +19,9 @@ exports.HMSH_CODE_DURABLE_MAXED = 597;
19
19
  exports.HMSH_CODE_DURABLE_FATAL = 598;
20
20
  exports.HMSH_CODE_DURABLE_RETRYABLE = 599;
21
21
  exports.HMSH_STATUS_UNKNOWN = 'unknown';
22
+ // QUORUM
23
+ exports.HMSH_QUORUM_DELAY_MS = 250;
24
+ exports.HMSH_ACTIVATION_MAX_RETRY = 3;
22
25
  // ENGINE
23
26
  exports.HMSH_OTT_WAIT_TIME = parseInt(process.env.HMSH_OTT_WAIT_TIME, 10) || 1000;
24
27
  exports.HMSH_EXPIRE_JOB_SECONDS = parseInt(process.env.HMSH_EXPIRE_JOB_SECONDS, 10) || 1;
@@ -23,13 +23,6 @@ declare class DurableWaitForSignalError extends Error {
23
23
  index: number;
24
24
  }[]);
25
25
  }
26
- declare class DurableSleepError extends Error {
27
- code: number;
28
- duration: number;
29
- index: number;
30
- dimension: string;
31
- constructor(message: string, duration: number, index: number, dimension: string);
32
- }
33
26
  declare class DurableSleepForError extends Error {
34
27
  code: number;
35
28
  duration: number;
@@ -86,4 +79,4 @@ declare class CollationError extends Error {
86
79
  fault: CollationFaultType;
87
80
  constructor(status: number, leg: ActivityDuplex, stage: CollationStage, fault?: CollationFaultType);
88
81
  }
89
- export { CollationError, DurableFatalError, DurableIncompleteSignalError, DurableMaxedError, DurableRetryError, DurableSleepError, DurableSleepForError, DurableTimeoutError, DurableWaitForSignalError, DuplicateJobError, ExecActivityError, GenerationalError, GetStateError, InactiveJobError, MapDataError, RegisterTimeoutError, SetStateError, };
82
+ export { CollationError, DurableFatalError, DurableIncompleteSignalError, DurableMaxedError, DurableRetryError, DurableSleepForError, DurableTimeoutError, DurableWaitForSignalError, DuplicateJobError, ExecActivityError, GenerationalError, GetStateError, InactiveJobError, MapDataError, RegisterTimeoutError, SetStateError, };
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.SetStateError = exports.RegisterTimeoutError = exports.MapDataError = exports.InactiveJobError = exports.GetStateError = exports.GenerationalError = exports.ExecActivityError = exports.DuplicateJobError = exports.DurableWaitForSignalError = exports.DurableTimeoutError = exports.DurableSleepForError = exports.DurableSleepError = exports.DurableRetryError = exports.DurableMaxedError = exports.DurableIncompleteSignalError = exports.DurableFatalError = exports.CollationError = void 0;
3
+ exports.SetStateError = exports.RegisterTimeoutError = exports.MapDataError = exports.InactiveJobError = exports.GetStateError = exports.GenerationalError = exports.ExecActivityError = exports.DuplicateJobError = exports.DurableWaitForSignalError = exports.DurableTimeoutError = exports.DurableSleepForError = exports.DurableRetryError = exports.DurableMaxedError = exports.DurableIncompleteSignalError = exports.DurableFatalError = exports.CollationError = void 0;
4
4
  const enums_1 = require("./enums");
5
5
  class GetStateError extends Error {
6
6
  constructor(jobId) {
@@ -34,17 +34,6 @@ class DurableWaitForSignalError extends Error {
34
34
  }
35
35
  }
36
36
  exports.DurableWaitForSignalError = DurableWaitForSignalError;
37
- /* @deprecated */
38
- class DurableSleepError extends Error {
39
- constructor(message, duration, index, dimension) {
40
- super(message);
41
- this.duration = duration;
42
- this.index = index;
43
- this.dimension = dimension;
44
- this.code = 595;
45
- }
46
- }
47
- exports.DurableSleepError = DurableSleepError;
48
37
  class DurableSleepForError extends Error {
49
38
  constructor(message, duration, index, dimension) {
50
39
  super(message);
@@ -7,7 +7,7 @@ async function sleepFor(ms) {
7
7
  }
8
8
  exports.sleepFor = sleepFor;
9
9
  function guid() {
10
- return (0, nanoid_1.nanoid)();
10
+ return (0, nanoid_1.nanoid)().replace(/[_-]/g, '0');
11
11
  }
12
12
  exports.guid = guid;
13
13
  function deterministicRandom(seed) {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hotmeshio/hotmesh",
3
- "version": "0.0.41",
3
+ "version": "0.0.43",
4
4
  "description": "Unbreakable Workflows",
5
5
  "main": "./build/index.js",
6
6
  "types": "./build/index.d.ts",
@@ -31,6 +31,11 @@ declare class Activity {
31
31
  * all aspects of the entry including job and activty state
32
32
  */
33
33
  verifyEntry(): Promise<void>;
34
+ /**
35
+ * Upon entering leg 2 of a duplexed activty, verify
36
+ * all aspects of the re-entry including job and activty state
37
+ */
38
+ verifyReentry(): Promise<number>;
34
39
  processEvent(status?: StreamStatus, code?: StreamCode, type?: 'hook' | 'output'): Promise<void>;
35
40
  processPending(telemetry: TelemetryService, type: 'hook' | 'output'): Promise<MultiResponseFlags>;
36
41
  processSuccess(telemetry: TelemetryService, type: 'hook' | 'output'): Promise<MultiResponseFlags>;
@@ -57,7 +62,9 @@ declare class Activity {
57
62
  getState(): Promise<void>;
58
63
  /**
59
64
  * if the job is created/deleted/created with the same key,
60
- * the 'gid' ensures no stale messages enter the stream
65
+ * the 'gid' ensures no stale messages (such as sleep delays)
66
+ * enter the workstream. Any message with a mismatched gid
67
+ * belongs to a prior job and can safely be ignored/dropped.
61
68
  */
62
69
  assertGenerationalId(jobGID: string, msgGID?: string): void;
63
70
  initDimensionalAddress(dad: string): void;
@@ -40,6 +40,17 @@ class Activity {
40
40
  collator_1.CollatorService.assertJobActive(this.context.metadata.js, this.context.metadata.jid, this.metadata.aid);
41
41
  await collator_1.CollatorService.notarizeEntry(this);
42
42
  }
43
+ /**
44
+ * Upon entering leg 2 of a duplexed activty, verify
45
+ * all aspects of the re-entry including job and activty state
46
+ */
47
+ async verifyReentry() {
48
+ const guid = this.context.metadata.guid;
49
+ this.setLeg(2);
50
+ await this.getState();
51
+ collator_1.CollatorService.assertJobActive(this.context.metadata.js, this.context.metadata.jid, this.metadata.aid);
52
+ return await collator_1.CollatorService.notarizeReentry(this, guid);
53
+ }
43
54
  //******** DUPLEX RE-ENTRY POINT ********//
44
55
  async processEvent(status = stream_1.StreamStatus.SUCCESS, code = 200, type = 'output') {
45
56
  this.setLeg(2);
@@ -54,17 +65,9 @@ class Activity {
54
65
  this.logger.debug('activity-process-event', { topic: this.config.subtype, jid, aid, status, code });
55
66
  let telemetry;
56
67
  try {
57
- await this.getState();
58
- collator_1.CollatorService.assertJobActive(this.context.metadata.js, this.context.metadata.jid, this.metadata.aid);
59
- const aState = await collator_1.CollatorService.notarizeReentry(this);
60
- this.adjacentIndex = collator_1.CollatorService.getDimensionalIndex(aState);
68
+ const collationKey = await this.verifyReentry();
69
+ this.adjacentIndex = collator_1.CollatorService.getDimensionalIndex(collationKey);
61
70
  telemetry = new telemetry_1.TelemetryService(this.engine.appId, this.config, this.metadata, this.context);
62
- let isComplete = collator_1.CollatorService.isActivityComplete(this.context.metadata.js);
63
- if (isComplete) {
64
- this.logger.warn('activity-process-event-duplicate', { jid, aid });
65
- this.logger.debug('activity-process-event-duplicate-resolution', { resolution: 'Increase HotMesh config `reclaimDelay` timeout.' });
66
- return;
67
- }
68
71
  telemetry.startActivitySpan(this.leg);
69
72
  let multiResponse;
70
73
  if (status === stream_1.StreamStatus.PENDING) {
@@ -306,7 +309,7 @@ class Activity {
306
309
  telemetry_1.TelemetryService.addTargetTelemetryPaths(consumes, this.config, this.metadata, this.leg);
307
310
  let { dad, jid } = this.context.metadata;
308
311
  const dIds = collator_1.CollatorService.getDimensionsById([...this.config.ancestors, this.metadata.aid], dad || '');
309
- //`state` is a flat hash; context is a tree
312
+ //`state` is a unidimensional hash; context is a tree
310
313
  const [state, status] = await this.store.getState(jid, consumes, dIds);
311
314
  this.context = (0, utils_1.restoreHierarchy)(state);
312
315
  this.assertGenerationalId(this.context.metadata.gid, gid);
@@ -316,7 +319,9 @@ class Activity {
316
319
  }
317
320
  /**
318
321
  * if the job is created/deleted/created with the same key,
319
- * the 'gid' ensures no stale messages enter the stream
322
+ * the 'gid' ensures no stale messages (such as sleep delays)
323
+ * enter the workstream. Any message with a mismatched gid
324
+ * belongs to a prior job and can safely be ignored/dropped.
320
325
  */
321
326
  assertGenerationalId(jobGID, msgGID) {
322
327
  if (msgGID !== jobGID) {
@@ -30,7 +30,17 @@ declare class CollatorService {
30
30
  static authorizeReentry(activity: Activity, multi?: RedisMulti): Promise<number>;
31
31
  static notarizeEarlyExit(activity: Activity, multi?: RedisMulti): Promise<number>;
32
32
  static notarizeEarlyCompletion(activity: Activity, multi?: RedisMulti): Promise<number>;
33
- static notarizeReentry(activity: Activity, multi?: RedisMulti): Promise<number>;
33
+ /**
34
+ * verifies both the concrete and synthetic keys for the activity; concrete keys
35
+ * exist in the original model and are effectively the 'real' keys. In reality,
36
+ * hook activities are atomized during compilation to create a synthetic DAG that
37
+ * is used to track the status of the graph in a distributed environment. The
38
+ * synthetic key represents different dimensional realities and is used to
39
+ * track re-entry overages (it distinguishes between the original and re-entry).
40
+ * The essential challenge is: is this a re-entry that is purposeful in
41
+ * order to induce cycles, or is the re-entry due to a failure in the system?
42
+ */
43
+ static notarizeReentry(activity: Activity, guid: string, multi?: RedisMulti): Promise<number>;
34
44
  static notarizeContinuation(activity: Activity, multi?: RedisMulti): Promise<number>;
35
45
  static notarizeCompletion(activity: Activity, multi?: RedisMulti): Promise<number>;
36
46
  static getDigitAtIndex(num: number, targetDigitIndex: number): number | null;
@@ -38,6 +48,15 @@ declare class CollatorService {
38
48
  static isDuplicate(num: number, targetDigitIndex: number): boolean;
39
49
  static isInactive(num: number): boolean;
40
50
  static isPrimed(amount: number, leg: ActivityDuplex): boolean;
51
+ /**
52
+ * During compilation, the graphs are compiled into structures necessary
53
+ * for distributed processing; these are referred to as 'synthetic DAGs',
54
+ * because they are not part of the original graph, but are used to track
55
+ * the status of the graph in a distributed environment. This check ensures
56
+ * that the 'synthetic key' is not a duplicate. (which is different than
57
+ * saying the 'key' is not a duplicate)
58
+ */
59
+ static verifySyntheticInteger(amount: number): void;
41
60
  static verifyInteger(amount: number, leg: ActivityDuplex, stage: CollationStage): void;
42
61
  static getDimensionsById(ancestors: string[], dad: string): Record<string, string>;
43
62
  /**
@@ -72,7 +91,6 @@ declare class CollatorService {
72
91
  *
73
92
  */
74
93
  static bindAncestorArray(graphs: HotMeshGraph[]): void;
75
- static isActivityComplete(status: number): boolean;
76
94
  /**
77
95
  * All activities exist on a dimensional plane. Zero
78
96
  * is the default. A value of
@@ -67,11 +67,28 @@ class CollatorService {
67
67
  return await activity.store.collate(activity.context.metadata.jid, activity.metadata.aid, 1000001 - decrement, this.getDimensionalAddress(activity), multi);
68
68
  }
69
69
  ;
70
- static async notarizeReentry(activity, multi) {
70
+ /**
71
+ * verifies both the concrete and synthetic keys for the activity; concrete keys
72
+ * exist in the original model and are effectively the 'real' keys. In reality,
73
+ * hook activities are atomized during compilation to create a synthetic DAG that
74
+ * is used to track the status of the graph in a distributed environment. The
75
+ * synthetic key represents different dimensional realities and is used to
76
+ * track re-entry overages (it distinguishes between the original and re-entry).
77
+ * The essential challenge is: is this a re-entry that is purposeful in
78
+ * order to induce cycles, or is the re-entry due to a failure in the system?
79
+ */
80
+ static async notarizeReentry(activity, guid, multi) {
81
+ const jid = activity.context.metadata.jid;
82
+ const localMulti = multi || activity.store.getMulti();
71
83
  //increment by 1_000_000 (indicates re-entry and is used to drive the 'dimensional address' for adjacent activities (minus 1))
72
- const amount = await activity.store.collate(activity.context.metadata.jid, activity.metadata.aid, 1000000, this.getDimensionalAddress(activity, true), multi);
73
- this.verifyInteger(amount, 2, 'enter');
74
- return amount;
84
+ await activity.store.collate(jid, activity.metadata.aid, 1000000, this.getDimensionalAddress(activity, true), localMulti);
85
+ await activity.store.collateSynthetic(jid, guid, 1000000, localMulti);
86
+ const [_amountConcrete, _amountSynthetic] = await localMulti.exec();
87
+ const amountConcrete = Array.isArray(_amountConcrete) ? _amountConcrete[1] : _amountConcrete;
88
+ const amountSynthetic = Array.isArray(_amountSynthetic) ? _amountSynthetic[1] : _amountSynthetic;
89
+ this.verifyInteger(amountConcrete, 2, 'enter');
90
+ this.verifySyntheticInteger(amountSynthetic);
91
+ return amountConcrete;
75
92
  }
76
93
  ;
77
94
  static async notarizeContinuation(activity, multi) {
@@ -119,6 +136,26 @@ class CollatorService {
119
136
  this.getDigitAtIndex(amount, 1) < 9;
120
137
  }
121
138
  }
139
+ /**
140
+ * During compilation, the graphs are compiled into structures necessary
141
+ * for distributed processing; these are referred to as 'synthetic DAGs',
142
+ * because they are not part of the original graph, but are used to track
143
+ * the status of the graph in a distributed environment. This check ensures
144
+ * that the 'synthetic key' is not a duplicate. (which is different than
145
+ * saying the 'key' is not a duplicate)
146
+ */
147
+ static verifySyntheticInteger(amount) {
148
+ const samount = amount.toString();
149
+ const isCompletedValue = parseInt(samount[samount.length - 1], 10);
150
+ if (isCompletedValue > 0) {
151
+ //already done error (ack/delete clearly failed; this is a duplicate)
152
+ throw new errors_1.CollationError(amount, 2, 'enter', collator_1.CollationFaultType.INACTIVE);
153
+ }
154
+ else if (amount >= 2000000) {
155
+ //duplicate synthetic key (todo: need to resolve/fix this!!)
156
+ throw new errors_1.CollationError(amount, 2, 'enter', collator_1.CollationFaultType.DUPLICATE);
157
+ }
158
+ }
122
159
  static verifyInteger(amount, leg, stage) {
123
160
  let faultType;
124
161
  if (leg === 1 && stage === 'enter') {
@@ -222,9 +259,6 @@ class CollatorService {
222
259
  dfs(startingNode, []);
223
260
  });
224
261
  }
225
- static isActivityComplete(status) {
226
- return (status - 0) <= 0;
227
- }
228
262
  /**
229
263
  * All activities exist on a dimensional plane. Zero
230
264
  * is the default. A value of
@@ -3,8 +3,8 @@ import { HotMeshService as HotMesh } from '../hotmesh';
3
3
  import { ClientConfig, Connection, HookOptions, WorkflowOptions, WorkflowSearchOptions } from '../../types/durable';
4
4
  export declare class ClientService {
5
5
  connection: Connection;
6
- topics: string[];
7
6
  options: WorkflowOptions;
7
+ static topics: string[];
8
8
  static instances: Map<string, HotMesh | Promise<HotMesh>>;
9
9
  constructor(config: ClientConfig);
10
10
  getHotMeshClient: (workflowTopic: string, namespace?: string) => Promise<HotMesh>;
@@ -37,6 +37,7 @@ export declare class ClientService {
37
37
  getHandle: (taskQueue: string, workflowName: string, workflowId: string, namespace?: string) => Promise<WorkflowHandleService>;
38
38
  search: (taskQueue: string, workflowName: string, namespace: null | string, index: string, ...query: string[]) => Promise<string[]>;
39
39
  };
40
+ verifyWorkflowActive(hotMesh: HotMesh, appId?: string, count?: number): Promise<boolean>;
40
41
  activateWorkflow(hotMesh: HotMesh, appId?: string, version?: string): Promise<void>;
41
42
  static shutdown(): Promise<void>;
42
43
  }
@@ -8,16 +8,17 @@ const key_1 = require("../../modules/key");
8
8
  const search_1 = require("./search");
9
9
  const types_1 = require("../../types");
10
10
  const enums_1 = require("../../modules/enums");
11
+ const utils_1 = require("../../modules/utils");
11
12
  class ClientService {
12
13
  constructor(config) {
13
- this.topics = [];
14
14
  this.getHotMeshClient = async (workflowTopic, namespace) => {
15
15
  //use the cached instance
16
16
  const instanceId = 'SINGLETON';
17
17
  if (ClientService.instances.has(instanceId)) {
18
18
  const hotMeshClient = await ClientService.instances.get(instanceId);
19
- if (!this.topics.includes(workflowTopic)) {
20
- this.topics.push(workflowTopic);
19
+ await this.verifyWorkflowActive(hotMeshClient, namespace ?? factory_1.APP_ID);
20
+ if (!ClientService.topics.includes(workflowTopic)) {
21
+ ClientService.topics.push(workflowTopic);
21
22
  await this.createStream(hotMeshClient, workflowTopic, namespace);
22
23
  }
23
24
  return hotMeshClient;
@@ -174,6 +175,18 @@ class ClientService {
174
175
  };
175
176
  this.connection = config.connection;
176
177
  }
178
+ async verifyWorkflowActive(hotMesh, appId = factory_1.APP_ID, count = 0) {
179
+ const app = await hotMesh.engine.store.getApp(appId);
180
+ const appVersion = app?.version;
181
+ if (isNaN(appVersion)) {
182
+ if (count > 10) {
183
+ throw new Error('Workflow failed to activate');
184
+ }
185
+ await (0, utils_1.sleepFor)(enums_1.HMSH_QUORUM_DELAY_MS * 2);
186
+ return await this.verifyWorkflowActive(hotMesh, appId, count + 1);
187
+ }
188
+ return true;
189
+ }
177
190
  async activateWorkflow(hotMesh, appId = factory_1.APP_ID, version = factory_1.APP_VERSION) {
178
191
  const app = await hotMesh.engine.store.getApp(appId);
179
192
  const appVersion = app?.version;
@@ -203,5 +216,6 @@ class ClientService {
203
216
  }
204
217
  }
205
218
  }
219
+ ClientService.topics = [];
206
220
  ClientService.instances = new Map();
207
221
  exports.ClientService = ClientService;
@@ -7,7 +7,6 @@
7
7
  * ERROR CODES:
8
8
  * 594: waitforsignal
9
9
  * 592: sleepFor
10
- * 595: sleep (deprecated)
11
10
  * 596, 597, 598: fatal
12
11
  * 599: retry
13
12
  */
@@ -10,7 +10,6 @@ exports.DEFAULT_COEFFICIENT = exports.APP_ID = exports.APP_VERSION = exports.get
10
10
  * ERROR CODES:
11
11
  * 594: waitforsignal
12
12
  * 592: sleepFor
13
- * 595: sleep (deprecated)
14
13
  * 596, 597, 598: fatal
15
14
  * 599: retry
16
15
  */
@@ -125,19 +124,6 @@ const getWorkflowYAML = (app, version) => {
125
124
  maps:
126
125
  index: '{$self.output.data.index}'
127
126
  signals: '{$self.output.data.signals}'
128
- 595:
129
- schema:
130
- type: object
131
- properties:
132
- duration:
133
- type: number
134
- description: sleep duration in seconds
135
- index:
136
- type: number
137
- description: the current index
138
- maps:
139
- duration: '{$self.output.data.duration}'
140
- index: '{$self.output.data.index}'
141
127
  592:
142
128
  schema:
143
129
  type: object
@@ -232,19 +218,6 @@ const getWorkflowYAML = (app, version) => {
232
218
  maps:
233
219
  index: '{$self.output.data.index}'
234
220
  signals: '{$self.output.data.signals}'
235
- 595:
236
- schema:
237
- type: object
238
- properties:
239
- duration:
240
- type: number
241
- description: sleep duration in seconds
242
- index:
243
- type: number
244
- description: the current index
245
- maps:
246
- duration: '{$self.output.data.duration}'
247
- index: '{$self.output.data.index}'
248
221
  592:
249
222
  schema:
250
223
  type: object
@@ -323,57 +296,6 @@ const getWorkflowYAML = (app, version) => {
323
296
  maps:
324
297
  duration: '{siga1.output.data.duration}'
325
298
 
326
- siga595:
327
- title: Signal In - Sleep before trying again
328
- type: await
329
- topic: ${app}.sleep.execute
330
- input:
331
- schema:
332
- type: object
333
- properties:
334
- duration:
335
- type: number
336
- index:
337
- type: number
338
- workflowId:
339
- type: string
340
- parentWorkflowId:
341
- type: string
342
- originJobId:
343
- type: string
344
- maps:
345
- duration: '{sigw1.output.data.duration}'
346
- index: '{sigw1.output.data.index}'
347
- parentWorkflowId:
348
- '@pipe':
349
- - ['{$job.metadata.jid}', '-s']
350
- - ['{@string.concat}']
351
- originJobId:
352
- '@pipe':
353
- - ['{t1.output.data.originJobId}', '{t1.output.data.originJobId}', '{$job.metadata.jid}']
354
- - ['{@conditional.ternary}']
355
-
356
- workflowId:
357
- '@pipe':
358
- - ['-', '{$job.metadata.jid}', '-$sleep', '{sig.output.metadata.dad}', '-', '{sigw1.output.data.index}']
359
- - ['{@string.concat}']
360
- output:
361
- schema:
362
- type: object
363
- properties:
364
- done:
365
- type: boolean
366
- maps:
367
- done: '{sigw1.output.data.done}'
368
-
369
- sigc595:
370
- title: Signal In - Goto Activity siga1
371
- type: cycle
372
- ancestor: siga1
373
- input:
374
- maps:
375
- duration: '{siga1.output.data.duration}'
376
-
377
299
  siga592:
378
300
  title: Signal In - Sleep For a duration and then cycle/goto
379
301
  type: hook
@@ -467,56 +389,6 @@ const getWorkflowYAML = (app, version) => {
467
389
  maps:
468
390
  duration: '{a1.output.data.duration}'
469
391
 
470
- a595:
471
- title: Sleep before trying again
472
- type: await
473
- topic: ${app}.sleep.execute
474
- input:
475
- schema:
476
- type: object
477
- properties:
478
- duration:
479
- type: number
480
- index:
481
- type: number
482
- workflowId:
483
- type: string
484
- parentWorkflowId:
485
- type: string
486
- originJobId:
487
- type: string
488
- maps:
489
- duration: '{w1.output.data.duration}'
490
- index: '{w1.output.data.index}'
491
- parentWorkflowId:
492
- '@pipe':
493
- - ['{$job.metadata.jid}', '-s']
494
- - ['{@string.concat}']
495
- originJobId:
496
- '@pipe':
497
- - ['{t1.output.data.originJobId}', '{t1.output.data.originJobId}', '{$job.metadata.jid}']
498
- - ['{@conditional.ternary}']
499
- workflowId:
500
- '@pipe':
501
- - ['-', '{$job.metadata.jid}', '-$sleep-', '{w1.output.data.index}']
502
- - ['{@string.concat}']
503
- output:
504
- schema:
505
- type: object
506
- properties:
507
- done:
508
- type: boolean
509
- maps:
510
- done: '{w1.output.data.done}'
511
-
512
- c595:
513
- title: Goto Activity a1
514
- type: cycle
515
- ancestor: a1
516
- input:
517
- maps:
518
- duration: '{a1.output.data.duration}'
519
-
520
392
  a592:
521
393
  title: Sleep For a duration and then cycle/goto
522
394
  type: hook
@@ -574,9 +446,6 @@ const getWorkflowYAML = (app, version) => {
574
446
  - to: siga594
575
447
  conditions:
576
448
  code: 594
577
- - to: siga595
578
- conditions:
579
- code: 595
580
449
  - to: siga592
581
450
  conditions:
582
451
  code: 592
@@ -585,8 +454,6 @@ const getWorkflowYAML = (app, version) => {
585
454
  code: 599
586
455
  siga594:
587
456
  - to: sigc594
588
- siga595:
589
- - to: sigc595
590
457
  siga592:
591
458
  - to: sigc592
592
459
  siga599:
@@ -597,9 +464,6 @@ const getWorkflowYAML = (app, version) => {
597
464
  - to: a594
598
465
  conditions:
599
466
  code: 594
600
- - to: a595
601
- conditions:
602
- code: 595
603
467
  - to: a592
604
468
  conditions:
605
469
  code: 592
@@ -611,8 +475,6 @@ const getWorkflowYAML = (app, version) => {
611
475
  code: [200, 598, 597, 596]
612
476
  a594:
613
477
  - to: c594
614
- a595:
615
- - to: c595
616
478
  a592:
617
479
  - to: c592
618
480
  a599:
@@ -194,6 +194,9 @@ class MeshOSService {
194
194
  return await this.find(options.options ?? {}, ...args);
195
195
  }
196
196
  static generateSearchQuery(query) {
197
+ if (!Array.isArray(query) || query.length === 0) {
198
+ return '*';
199
+ }
197
200
  const my = new this();
198
201
  let queryString = query.map(q => {
199
202
  const { field, is, value, type } = q;
@@ -187,21 +187,6 @@ class WorkerService {
187
187
  catch (err) {
188
188
  //not an error...just a trigger to sleep
189
189
  if (err instanceof errors_1.DurableSleepForError) {
190
- return {
191
- status: stream_1.StreamStatus.SUCCESS,
192
- code: err.code,
193
- metadata: { ...data.metadata },
194
- data: {
195
- code: err.code,
196
- message: JSON.stringify({ duration: err.duration, index: err.index, dimension: err.dimension }),
197
- duration: err.duration,
198
- index: err.index,
199
- dimension: err.dimension
200
- }
201
- };
202
- //deprecated format; not an error...just a trigger to sleep
203
- }
204
- else if (err instanceof errors_1.DurableSleepError) {
205
190
  return {
206
191
  status: stream_1.StreamStatus.SUCCESS,
207
192
  code: err.code,
@@ -112,15 +112,6 @@ export declare class WorkflowService {
112
112
  * @returns {Promise<number>}
113
113
  */
114
114
  static sleepFor(duration: string): Promise<number>;
115
- /**
116
- * Sleeps the workflow for a duration. As the function is reentrant,
117
- * upon reentry, the function will traverse prior execution paths up
118
- * until the sleep command and then resume execution from that point.
119
- * @param {string} duration - for example: '1 minute', '2 hours', '3 days'
120
- * @returns {Promise<number>}
121
- * @deprecated - use `sleepFor` instead
122
- */
123
- static sleep(duration: string): Promise<number>;
124
115
  /**
125
116
  * Waits for a signal to awaken
126
117
  * @param {string[]} signals - the signals to wait for
@@ -338,35 +338,6 @@ class WorkflowService {
338
338
  }
339
339
  return seconds;
340
340
  }
341
- /**
342
- * Sleeps the workflow for a duration. As the function is reentrant,
343
- * upon reentry, the function will traverse prior execution paths up
344
- * until the sleep command and then resume execution from that point.
345
- * @param {string} duration - for example: '1 minute', '2 hours', '3 days'
346
- * @returns {Promise<number>}
347
- * @deprecated - use `sleepFor` instead
348
- */
349
- static async sleep(duration) {
350
- const seconds = (0, ms_1.default)(duration) / 1000;
351
- const store = storage_1.asyncLocalStorage.getStore();
352
- const COUNTER = store.get('counter');
353
- const execIndex = COUNTER.counter = COUNTER.counter + 1;
354
- const workflowId = store.get('workflowId');
355
- const workflowTopic = store.get('workflowTopic');
356
- const workflowDimension = store.get('workflowDimension') ?? '';
357
- const namespace = store.get('namespace');
358
- const sleepJobId = `-${workflowId}-$sleep${workflowDimension}-${execIndex}`;
359
- try {
360
- const hotMeshClient = await worker_1.WorkerService.getHotMesh(workflowTopic, { namespace });
361
- await hotMeshClient.getState(`${hotMeshClient.appId}.sleep.execute`, sleepJobId);
362
- //if no error is thrown, we've already slept, return the delay
363
- return seconds;
364
- }
365
- catch (e) {
366
- // spawn a new sleep job if error code 595 is thrown by the worker)
367
- throw new errors_1.DurableSleepError(workflowId, seconds, execIndex, workflowDimension);
368
- }
369
- }
370
341
  /**
371
342
  * Waits for a signal to awaken
372
343
  * @param {string[]} signals - the signals to wait for
@@ -66,7 +66,7 @@ declare class EngineService {
66
66
  interrupt(topic: string, jobId: string, options?: JobInterruptOptions): Promise<string>;
67
67
  scrub(jobId: string): Promise<void>;
68
68
  hook(topic: string, data: JobData, status?: StreamStatus, code?: StreamCode): Promise<string>;
69
- hookTime(jobId: string, gId: string, activityId: string, type?: WorkListTaskType): Promise<string | void>;
69
+ hookTime(jobId: string, gId: string, topicOrActivity: string, type?: WorkListTaskType): Promise<string | void>;
70
70
  hookAll(hookTopic: string, data: JobData, keyResolver: JobStatsInput, queryFacets?: string[]): Promise<string[]>;
71
71
  pub(topic: string, data: JobData, context?: JobState): Promise<string>;
72
72
  sub(topic: string, callback: JobMessageCallback): Promise<void>;