@hotmeshio/hotmesh 0.2.4 → 0.3.1

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 (52) hide show
  1. package/README.md +14 -14
  2. package/build/modules/enums.d.ts +1 -0
  3. package/build/modules/enums.js +2 -1
  4. package/build/modules/errors.d.ts +1 -0
  5. package/build/modules/errors.js +1 -0
  6. package/build/modules/utils.d.ts +1 -0
  7. package/build/modules/utils.js +6 -10
  8. package/build/package.json +1 -1
  9. package/build/services/activities/activity.d.ts +3 -0
  10. package/build/services/activities/activity.js +27 -8
  11. package/build/services/activities/await.js +10 -0
  12. package/build/services/activities/cycle.js +10 -0
  13. package/build/services/activities/hook.js +13 -2
  14. package/build/services/activities/interrupt.js +10 -0
  15. package/build/services/activities/signal.js +10 -0
  16. package/build/services/activities/trigger.js +15 -5
  17. package/build/services/activities/worker.js +10 -0
  18. package/build/services/collator/index.d.ts +2 -0
  19. package/build/services/collator/index.js +12 -0
  20. package/build/services/compiler/deployer.js +1 -0
  21. package/build/services/exporter/index.js +1 -1
  22. package/build/services/meshcall/index.js +3 -9
  23. package/build/services/meshdata/index.d.ts +2 -2
  24. package/build/services/meshdata/index.js +25 -20
  25. package/build/services/meshflow/client.js +3 -8
  26. package/build/services/meshflow/schemas/factory.d.ts +1 -1
  27. package/build/services/meshflow/schemas/factory.js +56 -10
  28. package/build/services/meshflow/worker.js +5 -6
  29. package/build/services/meshflow/workflow.js +10 -12
  30. package/build/services/store/clients/ioredis.d.ts +1 -0
  31. package/build/services/store/clients/ioredis.js +4 -0
  32. package/build/services/store/clients/redis.d.ts +1 -0
  33. package/build/services/store/clients/redis.js +5 -0
  34. package/build/services/store/index.d.ts +2 -3
  35. package/build/services/store/index.js +5 -36
  36. package/build/services/task/index.d.ts +1 -1
  37. package/build/services/task/index.js +3 -6
  38. package/build/types/activity.d.ts +2 -0
  39. package/build/types/error.d.ts +1 -0
  40. package/build/types/hook.d.ts +1 -0
  41. package/build/types/hotmesh.d.ts +1 -0
  42. package/build/types/job.d.ts +1 -1
  43. package/build/types/meshdata.d.ts +1 -1
  44. package/build/types/meshflow.d.ts +3 -0
  45. package/package.json +1 -1
  46. package/types/activity.ts +3 -1
  47. package/types/error.ts +1 -0
  48. package/types/hook.ts +6 -1
  49. package/types/hotmesh.ts +35 -0
  50. package/types/job.ts +4 -14
  51. package/types/meshdata.ts +9 -5
  52. package/types/meshflow.ts +12 -1
package/README.md CHANGED
@@ -10,13 +10,13 @@ npm install @hotmeshio/hotmesh
10
10
  You have a Redis instance? Good. You're ready to go.
11
11
 
12
12
  ## Learn
13
- [SDK Docs](https://hotmeshio.github.io/sdk-typescript/) | [Samples](https://github.com/hotmeshio/samples-typescript) | [Intro Video](https://www.loom.com/share/211bd4b4038d42f0ba34374ef5b6f961?sid=7b889a56-f60f-4ccc-84e7-8c2697e548a9)
13
+ [📄 Docs](https://hotmeshio.github.io/sdk-typescript/) | [💼 Projects](https://github.com/hotmeshio/samples-typescript) | [🎥 Overview (3m)](https://www.loom.com/share/211bd4b4038d42f0ba34374ef5b6f961?sid=7b889a56-f60f-4ccc-84e7-8c2697e548a9) | [🎥 Transactional Workflow (9m)](https://www.loom.com/share/54ffd5266baf4ac6b287578abfd1d821?sid=0db2cef8-ef0d-4e02-a0b7-a1ee14f476ce)
14
14
 
15
15
  ## MeshCall | Connect Everything
16
- [MeshCall](https://hotmeshio.github.io/sdk-typescript/classes/services_meshcall.MeshCall.html) connects your functions to the Redis-backed mesh, exposing them as idempotent endpoints. Function responses are cacheable and functions can even run as idempotent cron jobs. Make blazing fast interservice calls that return in milliseconds without the overhead of HTTP.
16
+ [MeshCall](https://hotmeshio.github.io/sdk-typescript/classes/services_meshcall.MeshCall.html) connects your services as a singular mesh, exposing functions as idempotent endpoints. Function responses are cacheable and functions can even run as idempotent cron jobs. Make blazing fast interservice calls that return in milliseconds without the overhead of HTTP.
17
17
 
18
18
  <details style="padding: .5em">
19
- <summary style="font-size:1.25em;">Run an idempotent cron job</summary>
19
+ <summary style="font-size:1.25em;">Run an idempotent cron job <small>[more]</small></summary>
20
20
 
21
21
  ### Run a Cron
22
22
  This example demonstrates an *idempotent* cron that runs daily at midnight. The `id` makes each cron job unique and ensures that only one instance runs, despite repeated invocations. *The `cron` method returns `false` if a workflow is already running with the same `id`.*
@@ -54,7 +54,7 @@ You have a Redis instance? Good. You're ready to go.
54
54
  </details>
55
55
 
56
56
  <details style="padding: .5em">
57
- <summary style="font-size:1.25em;">Interrupt a cron job</summary>
57
+ <summary style="font-size:1.25em;">Interrupt a cron job <small>[more]</small></summary>
58
58
 
59
59
  ### Interrupt a Cron
60
60
  This example demonstrates how to cancel a running cron job.
@@ -76,7 +76,7 @@ You have a Redis instance? Good. You're ready to go.
76
76
  </details>
77
77
 
78
78
  <details style="padding: .5em">
79
- <summary style="font-size:1.25em;">Call any function in any service</summary>
79
+ <summary style="font-size:1.25em;">Call any function in any service <small>[more]</small></summary>
80
80
 
81
81
  ### Call a Function
82
82
  Make blazing fast interservice calls that behave like HTTP but without the setup and performance overhead. This example demonstrates how to connect a function to the mesh and call it from anywhere on the network.
@@ -129,7 +129,7 @@ You have a Redis instance? Good. You're ready to go.
129
129
  </details>
130
130
 
131
131
  <details style="padding: .5em">
132
- <summary style="font-size:1.25em;">Call and <b>cache</b> a function</summary>
132
+ <summary style="font-size:1.25em;">Call and <b>cache</b> a function <small>[more]</small></summary>
133
133
 
134
134
  ### Cache a Function
135
135
  Redis is great for unburdening stressed services. This solution builds upon the previous example, caching the response. The linked function will only be re/called when the cached result expires. Everything remains the same, except the caller which specifies an `id` and `ttl`.
@@ -172,7 +172,7 @@ You have a Redis instance? Good. You're ready to go.
172
172
  [MeshFlow](https://hotmeshio.github.io/sdk-typescript/classes/services_meshflow.MeshFlow.html) is a drop-in replacement for [Temporal.io](https://temporal.io). If you need to orchestrate your functions as durable workflows, MeshFlow combines the popular Temporal SDK with Redis' *in-memory execution speed*.
173
173
 
174
174
  <details style="padding: .5em">
175
- <summary style="font-size:1.25em;">Orchestrate unpredictable activities</summary>
175
+ <summary style="font-size:1.25em;">Orchestrate unpredictable activities <small>[more]</small></summary>
176
176
 
177
177
  ### Proxy Activities
178
178
  When an endpoint is unpredictable, use `proxyActivities`. HotMesh will retry as necessary until the call succeeds. This example demonstrates a workflow that greets a user in both English and Spanish. Even though both activities throw random errors, the workflow always returns a successful result.
@@ -263,7 +263,7 @@ When an endpoint is unpredictable, use `proxyActivities`. HotMesh will retry as
263
263
  </details>
264
264
 
265
265
  <details style="padding: .5em">
266
- <summary style="font-size:1.25em;">Pause and wait for a signal</summary>
266
+ <summary style="font-size:1.25em;">Pause and wait for a signal <small>[more]</small></summary>
267
267
 
268
268
  ### Wait for Signal
269
269
  Pause a function and only awaken when a matching signal is received from the outide.
@@ -356,7 +356,7 @@ Pause a function and only awaken when a matching signal is received from the out
356
356
  </details>
357
357
 
358
358
  <details style="padding: .5em">
359
- <summary style="font-size:1.25em;">Wait for multiple signals (collation)</summary>
359
+ <summary style="font-size:1.25em;">Wait for multiple signals (collation) <small>[more]</small></summary>
360
360
 
361
361
  ### Collate Multiple Signals
362
362
  Use a standard `Promise` to collate and cache multiple signals. HotMesh will only awaken once **all** signals have arrived. HotMesh will track up to 25 concurrent signals.
@@ -406,7 +406,7 @@ Use a standard `Promise` to collate and cache multiple signals. HotMesh will onl
406
406
  </details>
407
407
 
408
408
  <details style="padding: .5em">
409
- <summary style="font-size:1.25em;">Create a recurring, cyclical workflow</summary>
409
+ <summary style="font-size:1.25em;">Create a recurring, cyclical workflow <small>[more]</small></summary>
410
410
 
411
411
  ### Cyclical Workflow
412
412
  This example calls an activity and then sleeps for a week. It runs indefinitely until it's manually stopped. It takes advantage of durable execution and can safely sleep for months or years.
@@ -511,7 +511,7 @@ Deployments with the Redis `FT.SEARCH` module enabled can use the **MeshData** m
511
511
  *For those Redis deployments without the `FT.SEARCH` module, it's still useful to define a workflow schema. The MeshData class provides convenience methods for reading and writing hash field data to a workflow record (e.g., `get`, `del`, and `incr`).*
512
512
 
513
513
  <details style="padding: .5em">
514
- <summary style="font-size:1.25em;">Create a search index</summary>
514
+ <summary style="font-size:1.25em;">Create a search index <small>[more]</small></summary>
515
515
 
516
516
  ### Workflow Data Indexes
517
517
 
@@ -551,7 +551,7 @@ This example demonstrates how to define a schema and deploy an index for a 'user
551
551
  </details>
552
552
 
553
553
  <details style="padding: .5em">
554
- <summary style="font-size:1.25em;">Create an indexed, searchable record</summary>
554
+ <summary style="font-size:1.25em;">Create an indexed, searchable record <small>[more]</small></summary>
555
555
 
556
556
  ### Workflow Record Data
557
557
  This example demonstrates how to create a 'user' workflow backed by the searchable schema from the prior example.
@@ -631,7 +631,7 @@ This example demonstrates how to create a 'user' workflow backed by the searchab
631
631
  </details>
632
632
 
633
633
  <details style="padding: .5em">
634
- <summary style="font-size:1.25em;">Fetch record data</summary>
634
+ <summary style="font-size:1.25em;">Fetch record data <small>[more]</small></summary>
635
635
 
636
636
  ### Read Record Data
637
637
  This example demonstrates how to read data fields directly from a workflow.
@@ -662,7 +662,7 @@ This example demonstrates how to read data fields directly from a workflow.
662
662
  </details>
663
663
 
664
664
  <details style="padding: .5em">
665
- <summary style="font-size:1.25em;">Search record data</summary>
665
+ <summary style="font-size:1.25em;">Search record data <small>[more]</small></summary>
666
666
 
667
667
  ### Query Record Data
668
668
  This example demonstrates how to search for those workflows where a given condition exists in the data. This one searches for active users. *NOTE: The native Redis FT.SEARCH syntax is supported. The JSON abstraction shown here is a convenience method for straight-forward, one-dimensional queries.*
@@ -1,6 +1,7 @@
1
1
  import { LogLevel } from '../types/logger';
2
2
  export declare const HMSH_LOGLEVEL: LogLevel;
3
3
  export declare const HMSH_IS_CLUSTER: boolean;
4
+ export declare const HMSH_SIGNAL_EXPIRE = 3600;
4
5
  export declare const HMSH_CODE_SUCCESS = 200;
5
6
  export declare const HMSH_CODE_PENDING = 202;
6
7
  export declare const HMSH_CODE_NOTFOUND = 404;
@@ -1,8 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.HMSH_GUID_SIZE = 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_MESHFLOW_EXP_BACKOFF = exports.HMSH_MESHFLOW_MAX_INTERVAL = exports.HMSH_MESHFLOW_MAX_ATTEMPTS = exports.HMSH_GRADUATED_INTERVAL_MS = exports.HMSH_MAX_TIMEOUT_MS = exports.HMSH_MAX_RETRIES = exports.MAX_DELAY = exports.HMSH_EXPIRE_JOB_SECONDS = exports.HMSH_OTT_WAIT_TIME = exports.HMSH_ACTIVATION_MAX_RETRY = exports.HMSH_QUORUM_DELAY_MS = exports.HMSH_QUORUM_ROLLCALL_CYCLES = exports.HMSH_STATUS_UNKNOWN = exports.HMSH_CODE_MESHFLOW_RETRYABLE = exports.HMSH_CODE_MESHFLOW_FATAL = exports.HMSH_CODE_MESHFLOW_MAXED = exports.HMSH_CODE_MESHFLOW_TIMEOUT = exports.HMSH_CODE_MESHFLOW_WAIT = exports.HMSH_CODE_MESHFLOW_PROXY = exports.HMSH_CODE_MESHFLOW_CHILD = exports.HMSH_CODE_MESHFLOW_ALL = exports.HMSH_CODE_MESHFLOW_SLEEP = 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_IS_CLUSTER = exports.HMSH_LOGLEVEL = void 0;
3
+ exports.HMSH_GUID_SIZE = 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_MESHFLOW_EXP_BACKOFF = exports.HMSH_MESHFLOW_MAX_INTERVAL = exports.HMSH_MESHFLOW_MAX_ATTEMPTS = exports.HMSH_GRADUATED_INTERVAL_MS = exports.HMSH_MAX_TIMEOUT_MS = exports.HMSH_MAX_RETRIES = exports.MAX_DELAY = exports.HMSH_EXPIRE_JOB_SECONDS = exports.HMSH_OTT_WAIT_TIME = exports.HMSH_ACTIVATION_MAX_RETRY = exports.HMSH_QUORUM_DELAY_MS = exports.HMSH_QUORUM_ROLLCALL_CYCLES = exports.HMSH_STATUS_UNKNOWN = exports.HMSH_CODE_MESHFLOW_RETRYABLE = exports.HMSH_CODE_MESHFLOW_FATAL = exports.HMSH_CODE_MESHFLOW_MAXED = exports.HMSH_CODE_MESHFLOW_TIMEOUT = exports.HMSH_CODE_MESHFLOW_WAIT = exports.HMSH_CODE_MESHFLOW_PROXY = exports.HMSH_CODE_MESHFLOW_CHILD = exports.HMSH_CODE_MESHFLOW_ALL = exports.HMSH_CODE_MESHFLOW_SLEEP = 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_SIGNAL_EXPIRE = exports.HMSH_IS_CLUSTER = exports.HMSH_LOGLEVEL = void 0;
4
4
  exports.HMSH_LOGLEVEL = process.env.HMSH_LOGLEVEL || 'info';
5
5
  exports.HMSH_IS_CLUSTER = process.env.HMSH_IS_CLUSTER === 'true';
6
+ exports.HMSH_SIGNAL_EXPIRE = 3600;
6
7
  exports.HMSH_CODE_SUCCESS = 200;
7
8
  exports.HMSH_CODE_PENDING = 202;
8
9
  exports.HMSH_CODE_NOTFOUND = 404;
@@ -39,6 +39,7 @@ declare class MeshFlowChildError extends Error {
39
39
  backoffCoefficient: number;
40
40
  code: number;
41
41
  expire: number;
42
+ persistent: boolean;
42
43
  signalIn: boolean;
43
44
  workflowDimension: string;
44
45
  index: number;
@@ -53,6 +53,7 @@ class MeshFlowChildError extends Error {
53
53
  this.workflowTopic = params.workflowTopic;
54
54
  this.parentWorkflowId = params.parentWorkflowId;
55
55
  this.expire = params.expire;
56
+ this.persistent = params.persistent;
56
57
  this.signalIn = params.signalIn;
57
58
  this.originJobId = params.originJobId;
58
59
  this.index = params.index;
@@ -36,3 +36,4 @@ export declare function getValueByPath(obj: {
36
36
  }, path: string): any;
37
37
  export declare function restoreHierarchy(obj: StringAnyType): StringAnyType;
38
38
  export declare function isValidCron(cronExpression: string): boolean;
39
+ export declare const s: (input: string) => number;
@@ -3,20 +3,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.isValidCron = exports.restoreHierarchy = exports.getValueByPath = exports.getIndexedHash = exports.getSymVal = exports.getSymKey = exports.formatISODate = exports.getTimeSeries = exports.getSubscriptionTopic = exports.findSubscriptionForTrigger = exports.findTopKey = exports.matchesStatus = exports.matchesStatusCode = exports.identifyRedisTypeFromClass = exports.polyfill = exports.identifyRedisType = exports.XSleepFor = exports.sleepImmediate = exports.sleepFor = exports.guid = exports.deterministicRandom = exports.deepCopy = exports.getSystemHealth = exports.hashOptions = void 0;
6
+ exports.s = exports.isValidCron = exports.restoreHierarchy = exports.getValueByPath = exports.getIndexedHash = exports.getSymVal = exports.getSymKey = exports.formatISODate = exports.getTimeSeries = exports.getSubscriptionTopic = exports.findSubscriptionForTrigger = exports.findTopKey = exports.matchesStatus = exports.matchesStatusCode = exports.identifyRedisTypeFromClass = exports.polyfill = exports.identifyRedisType = exports.XSleepFor = exports.sleepImmediate = exports.sleepFor = exports.guid = exports.deterministicRandom = exports.deepCopy = exports.getSystemHealth = exports.hashOptions = void 0;
7
7
  const os_1 = __importDefault(require("os"));
8
8
  const crypto_1 = require("crypto");
9
9
  const nanoid_1 = require("nanoid");
10
+ const ms_1 = __importDefault(require("ms"));
10
11
  const enums_1 = require("./enums");
11
- async function safeExecute(operation, defaultValue) {
12
- try {
13
- return await operation;
14
- }
15
- catch (error) {
16
- console.error(`Operation Error: ${error}`);
17
- return defaultValue;
18
- }
19
- }
20
12
  const hashOptions = (options) => {
21
13
  const str = JSON.stringify(options);
22
14
  return (0, crypto_1.createHash)('sha256').update(str).digest('hex');
@@ -244,3 +236,7 @@ function isValidCron(cronExpression) {
244
236
  return cronRegex.test(cronExpression);
245
237
  }
246
238
  exports.isValidCron = isValidCron;
239
+ const s = (input) => {
240
+ return (0, ms_1.default)(input) / 1000;
241
+ };
242
+ exports.s = s;
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hotmeshio/hotmesh",
3
- "version": "0.2.4",
3
+ "version": "0.3.1",
4
4
  "description": "Unbreakable Workflows",
5
5
  "main": "./build/index.js",
6
6
  "types": "./build/index.d.ts",
@@ -59,6 +59,9 @@ declare class Activity {
59
59
  resolveDad(): string;
60
60
  resolveAdjacentDad(): string;
61
61
  filterAdjacent(): Promise<StreamData[]>;
62
+ isJobComplete(jobStatus: JobStatus): boolean;
63
+ shouldEmit(): boolean;
64
+ shouldPersistJob(): boolean;
62
65
  transition(adjacencyList: StreamData[], jobStatus: JobStatus): Promise<string[]>;
63
66
  jobWasInterrupted(jobStatus: JobStatus): boolean;
64
67
  }
@@ -44,6 +44,7 @@ class Activity {
44
44
  collator_1.CollatorService.assertJobActive(this.context.metadata.js, this.context.metadata.jid, this.metadata.aid, threshold);
45
45
  }
46
46
  catch (error) {
47
+ await collator_1.CollatorService.notarizeEntry(this);
47
48
  if (threshold > 0) {
48
49
  if (this.context.metadata.js === threshold) {
49
50
  const status = await this.setStatus(-threshold);
@@ -55,6 +56,7 @@ class Activity {
55
56
  else {
56
57
  throw error;
57
58
  }
59
+ return;
58
60
  }
59
61
  await collator_1.CollatorService.notarizeEntry(this);
60
62
  }
@@ -104,7 +106,7 @@ class Activity {
104
106
  }
105
107
  catch (error) {
106
108
  if (error instanceof errors_1.CollationError) {
107
- this.logger.info('process-event-inactive-error', { ...error });
109
+ this.logger.info(`process-event-${error.fault}-error`, { ...error });
108
110
  return;
109
111
  }
110
112
  else if (error instanceof errors_1.InactiveJobError) {
@@ -405,6 +407,10 @@ class Activity {
405
407
  initPolicies(context) {
406
408
  const expire = pipe_1.Pipe.resolve(this.config.expire ?? enums_1.HMSH_EXPIRE_DURATION, context);
407
409
  context.metadata.expire = expire;
410
+ if (this.config.persistent != undefined) {
411
+ const persistent = pipe_1.Pipe.resolve(this.config.persistent ?? false, context);
412
+ context.metadata.persistent = persistent;
413
+ }
408
414
  }
409
415
  bindActivityData(type) {
410
416
  this.context[this.metadata.aid][type].data = this.data;
@@ -446,21 +452,34 @@ class Activity {
446
452
  }
447
453
  return adjacencyList;
448
454
  }
455
+ isJobComplete(jobStatus) {
456
+ return jobStatus <= 0;
457
+ }
458
+ shouldEmit() {
459
+ if (this.config.emit) {
460
+ return pipe_1.Pipe.resolve(this.config.emit, this.context) === true;
461
+ }
462
+ return false;
463
+ }
464
+ shouldPersistJob() {
465
+ if (this.config.persist !== undefined) {
466
+ return pipe_1.Pipe.resolve(this.config.persist, this.context) === true;
467
+ }
468
+ return false;
469
+ }
449
470
  async transition(adjacencyList, jobStatus) {
450
471
  if (this.jobWasInterrupted(jobStatus)) {
451
472
  return;
452
473
  }
453
474
  let mIds = [];
454
- let emit = false;
455
- if (this.config.emit) {
456
- emit = pipe_1.Pipe.resolve(this.config.emit, this.context);
457
- }
458
- if (jobStatus <= 0 || emit) {
475
+ if (this.shouldEmit() ||
476
+ this.isJobComplete(jobStatus) ||
477
+ this.shouldPersistJob()) {
459
478
  await this.engine.runJobCompletionTasks(this.context, {
460
- emit: jobStatus > 0,
479
+ emit: !this.isJobComplete(jobStatus) && !this.shouldPersistJob(),
461
480
  });
462
481
  }
463
- if (adjacencyList.length && jobStatus > 0) {
482
+ if (adjacencyList.length && !this.isJobComplete(jobStatus)) {
464
483
  const multi = this.store.getMulti();
465
484
  for (const execSignal of adjacencyList) {
466
485
  await this.engine.router?.publishMessage(null, execSignal, multi);
@@ -51,6 +51,16 @@ class Await extends activity_1.Activity {
51
51
  this.logger.error('await-get-state-error', { ...error });
52
52
  return;
53
53
  }
54
+ else if (error instanceof errors_1.CollationError) {
55
+ if (error.fault === 'duplicate') {
56
+ this.logger.info('await-collation-overage', {
57
+ job_id: this.context.metadata.jid,
58
+ guid: this.context.metadata.guid,
59
+ });
60
+ return;
61
+ }
62
+ this.logger.error('await-collation-error', { ...error });
63
+ }
54
64
  else {
55
65
  this.logger.error('await-process-error', { ...error });
56
66
  }
@@ -51,6 +51,16 @@ class Cycle extends activity_1.Activity {
51
51
  this.logger.error('cycle-get-state-error', { ...error });
52
52
  return;
53
53
  }
54
+ else if (error instanceof errors_1.CollationError) {
55
+ if (error.fault === 'duplicate') {
56
+ this.logger.info('cycle-collation-overage', {
57
+ job_id: this.context.metadata.jid,
58
+ guid: this.context.metadata.guid,
59
+ });
60
+ return;
61
+ }
62
+ this.logger.error('cycle-collation-error', { ...error });
63
+ }
54
64
  else {
55
65
  this.logger.error('cycle-process-error', { ...error });
56
66
  }
@@ -7,6 +7,7 @@ const pipe_1 = require("../pipe");
7
7
  const task_1 = require("../task");
8
8
  const telemetry_1 = require("../telemetry");
9
9
  const stream_1 = require("../../types/stream");
10
+ const enums_1 = require("../../modules/enums");
10
11
  const activity_1 = require("./activity");
11
12
  class Hook extends activity_1.Activity {
12
13
  constructor(config, data, metadata, hook, engine, context) {
@@ -44,6 +45,16 @@ class Hook extends activity_1.Activity {
44
45
  this.logger.error('hook-get-state-error', { ...error });
45
46
  return;
46
47
  }
48
+ else if (error instanceof errors_1.CollationError) {
49
+ if (error.fault === 'duplicate') {
50
+ this.logger.info('hook-collation-overage', {
51
+ job_id: this.context.metadata.jid,
52
+ guid: this.context.metadata.guid,
53
+ });
54
+ return;
55
+ }
56
+ this.logger.error('hook-collation-error', { ...error });
57
+ }
47
58
  else {
48
59
  this.logger.error('hook-process-error', { ...error });
49
60
  }
@@ -68,7 +79,7 @@ class Hook extends activity_1.Activity {
68
79
  }
69
80
  async doHook(telemetry) {
70
81
  const multi = this.store.getMulti();
71
- await this.registerHook(multi);
82
+ await this.registerHook(enums_1.HMSH_IS_CLUSTER ? undefined : multi);
72
83
  this.mapOutputData();
73
84
  this.mapJobData();
74
85
  await this.setState(multi);
@@ -102,7 +113,7 @@ class Hook extends activity_1.Activity {
102
113
  }
103
114
  async registerHook(multi) {
104
115
  if (this.config.hook?.topic) {
105
- return await this.engine.taskService.registerWebHook(this.config.hook.topic, this.context, this.resolveDad(), multi);
116
+ return await this.engine.taskService.registerWebHook(this.config.hook.topic, this.context, this.resolveDad(), this.context.metadata.expire, multi);
106
117
  }
107
118
  else if (this.config.sleep) {
108
119
  const duration = pipe_1.Pipe.resolve(this.config.sleep, this.context);
@@ -41,6 +41,16 @@ class Interrupt extends activity_1.Activity {
41
41
  this.logger.error('interrupt-get-state-error', { ...error });
42
42
  return;
43
43
  }
44
+ else if (error instanceof errors_1.CollationError) {
45
+ if (error.fault === 'duplicate') {
46
+ this.logger.info('interrupt-collation-overage', {
47
+ job_id: this.context.metadata.jid,
48
+ guid: this.context.metadata.guid,
49
+ });
50
+ return;
51
+ }
52
+ this.logger.error('interrupt-collation-error', { ...error });
53
+ }
44
54
  else {
45
55
  this.logger.error('interrupt-process-error', { ...error });
46
56
  }
@@ -59,6 +59,16 @@ class Signal extends activity_1.Activity {
59
59
  this.logger.error('signal-get-state-error', { ...error });
60
60
  return;
61
61
  }
62
+ else if (error instanceof errors_1.CollationError) {
63
+ if (error.fault === 'duplicate') {
64
+ this.logger.info('signal-collation-overage', {
65
+ job_id: this.context.metadata.jid,
66
+ guid: this.context.metadata.guid,
67
+ });
68
+ return;
69
+ }
70
+ this.logger.error('signal-collation-error', { ...error });
71
+ }
62
72
  else {
63
73
  this.logger.error('signal-process-error', { ...error });
64
74
  }
@@ -40,6 +40,7 @@ class Trigger extends activity_1.Activity {
40
40
  else {
41
41
  await this.registerJobDependency(multi);
42
42
  }
43
+ await collator_1.CollatorService.notarizeInception(this, this.context.metadata.guid, multi);
43
44
  await multi.exec();
44
45
  this.execAdjacentParent();
45
46
  telemetry.mapActivityAttributes();
@@ -51,13 +52,24 @@ class Trigger extends activity_1.Activity {
51
52
  return this.context.metadata.jid;
52
53
  }
53
54
  catch (error) {
55
+ telemetry?.setActivityError(error.message);
54
56
  if (error instanceof errors_1.DuplicateJobError) {
55
- this.logger.error('duplicate-job-error', { job_id: error.jobId });
57
+ const isOverage = await collator_1.CollatorService.isInceptionOverage(this, this.context.metadata.guid);
58
+ if (isOverage) {
59
+ this.logger.info('trigger-collation-overage', {
60
+ job_id: error.jobId,
61
+ guid: this.context.metadata.guid,
62
+ });
63
+ return;
64
+ }
65
+ this.logger.error('duplicate-job-error', {
66
+ job_id: error.jobId,
67
+ guid: this.context.metadata.guid,
68
+ });
56
69
  }
57
70
  else {
58
71
  this.logger.error('trigger-process-error', { ...error });
59
72
  }
60
- telemetry?.setActivityError(error.message);
61
73
  throw error;
62
74
  }
63
75
  finally {
@@ -82,9 +94,6 @@ class Trigger extends activity_1.Activity {
82
94
  if (options.pending) {
83
95
  return -1;
84
96
  }
85
- else if (options.statusThreshold) {
86
- return 1000000 - options.statusThreshold + count;
87
- }
88
97
  return count;
89
98
  }
90
99
  async setExpired(seconds, multi) {
@@ -166,6 +175,7 @@ class Trigger extends activity_1.Activity {
166
175
  tpc: this.config.subscribes,
167
176
  trc: this.context.metadata.trc,
168
177
  spn: this.context.metadata.spn,
178
+ guid: this.context.metadata.guid,
169
179
  jid: jobId,
170
180
  dad: collator_1.CollatorService.getDimensionalSeed(),
171
181
  key: jobKey,
@@ -50,6 +50,16 @@ class Worker extends activity_1.Activity {
50
50
  this.logger.error('worker-get-state-error', { ...error });
51
51
  return;
52
52
  }
53
+ else if (error instanceof errors_1.CollationError) {
54
+ if (error.fault === 'duplicate') {
55
+ this.logger.info('worker-collation-overage', {
56
+ job_id: this.context.metadata.jid,
57
+ guid: this.context.metadata.guid,
58
+ });
59
+ return;
60
+ }
61
+ this.logger.error('worker-collation-error', { ...error });
62
+ }
53
63
  else {
54
64
  this.logger.error('worker-process-error', { ...error });
55
65
  }
@@ -13,6 +13,8 @@ declare class CollatorService {
13
13
  static authorizeReentry(activity: Activity, multi?: RedisMulti): Promise<number>;
14
14
  static notarizeEarlyExit(activity: Activity, multi?: RedisMulti): Promise<number>;
15
15
  static notarizeEarlyCompletion(activity: Activity, multi?: RedisMulti): Promise<number>;
16
+ static notarizeInception(activity: Activity, guid: string, multi: RedisMulti): Promise<void>;
17
+ static isInceptionOverage(activity: Activity, guid: string): Promise<boolean>;
16
18
  static notarizeReentry(activity: Activity, guid: string, multi?: RedisMulti): Promise<number>;
17
19
  static notarizeContinuation(activity: Activity, multi?: RedisMulti): Promise<number>;
18
20
  static notarizeCompletion(activity: Activity, multi?: RedisMulti): Promise<number>;
@@ -43,6 +43,18 @@ class CollatorService {
43
43
  : 11000000000000;
44
44
  return await activity.store.collate(activity.context.metadata.jid, activity.metadata.aid, 1000001 - decrement, this.getDimensionalAddress(activity), multi);
45
45
  }
46
+ static async notarizeInception(activity, guid, multi) {
47
+ if (guid) {
48
+ await activity.store.collateSynthetic(activity.context.metadata.jid, guid, 1000000, multi);
49
+ }
50
+ }
51
+ static async isInceptionOverage(activity, guid) {
52
+ if (guid) {
53
+ const amount = await activity.store.collateSynthetic(activity.context.metadata.jid, guid, 1000000);
54
+ return amount > 1000000;
55
+ }
56
+ return false;
57
+ }
46
58
  static async notarizeReentry(activity, guid, multi) {
47
59
  const jid = activity.context.metadata.jid;
48
60
  const localMulti = multi || activity.store.getMulti();
@@ -125,6 +125,7 @@ class Deployer {
125
125
  activities[activityKey].publishes = graph.publishes;
126
126
  }
127
127
  activities[activityKey].expire = graph.expire ?? undefined;
128
+ activities[activityKey].persistent = graph.persistent ?? undefined;
128
129
  }
129
130
  }
130
131
  }
@@ -15,7 +15,7 @@ class ExporterService {
15
15
  this.symbols = this.store.getAllSymbols();
16
16
  this.symbols = await this.symbols;
17
17
  }
18
- const depData = await this.store.getDependencies(jobId);
18
+ const depData = [];
19
19
  const jobData = await this.store.getRaw(jobId);
20
20
  const jobExport = this.inflate(jobData, depData);
21
21
  return jobExport;
@@ -1,11 +1,7 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
2
  var _a;
6
3
  Object.defineProperty(exports, "__esModule", { value: true });
7
4
  exports.MeshCall = void 0;
8
- const ms_1 = __importDefault(require("ms"));
9
5
  const hotmesh_1 = require("../hotmesh");
10
6
  const enums_1 = require("../../modules/enums");
11
7
  const utils_1 = require("../../modules/utils");
@@ -128,7 +124,7 @@ class MeshCall {
128
124
  }
129
125
  let expire = 1;
130
126
  if (params.options?.ttl) {
131
- expire = (0, ms_1.default)(params.options.ttl) / 1000;
127
+ expire = (0, utils_1.s)(params.options.ttl);
132
128
  }
133
129
  const jobOutput = await hotMeshInstance.pubsub(TOPIC, { id, expire, topic: params.topic, args: params.args }, null, 30000);
134
130
  return jobOutput?.data?.response;
@@ -159,11 +155,9 @@ class MeshCall {
159
155
  delay = nextDelay > 0 ? nextDelay : undefined;
160
156
  }
161
157
  else {
162
- const seconds = (0, ms_1.default)(params.options.interval) / 1000;
158
+ const seconds = (0, utils_1.s)(params.options.interval);
163
159
  interval = Math.max(seconds, enums_1.HMSH_FIDELITY_SECONDS);
164
- delay = params.options.delay
165
- ? (0, ms_1.default)(params.options.delay) / 1000
166
- : undefined;
160
+ delay = params.options.delay ? (0, utils_1.s)(params.options.delay) : undefined;
167
161
  }
168
162
  try {
169
163
  const hotMeshInstance = await MeshCall.getInstance(params.namespace, params.redis, { readonly: params.callback ? false : true, guid: params.guid });
@@ -1,6 +1,6 @@
1
1
  import { HotMesh } from '../hotmesh';
2
2
  import { WorkflowOptions, WorkflowSearchOptions, FindJobsOptions, FindOptions, FindWhereOptions, SearchResults, FindWhereQuery } from '../../types/meshflow';
3
- import { CallOptions, ConnectionInput, ConnectOptions, ExecInput, HookInput } from '../../types/meshdata';
3
+ import { CallOptions, ConnectionInput, ExecInput, HookInput } from '../../types/meshdata';
4
4
  import { RedisClass, RedisOptions } from '../../types/redis';
5
5
  import { StringAnyType, StringStringType } from '../../types/serializer';
6
6
  import { JobInterruptOptions, JobOutput } from '../../types/job';
@@ -45,7 +45,7 @@ declare class MeshData {
45
45
  unsub: (callback: QuorumMessageCallback, options?: SubscriptionOptions) => Promise<void>;
46
46
  };
47
47
  connect<T>({ entity, target, options, }: ConnectionInput<T>): Promise<boolean>;
48
- bindCallOptions(args: any[], options: ConnectOptions, callOptions?: CallOptions): StringAnyType;
48
+ bindCallOptions(args: any[], callOptions?: CallOptions): StringAnyType;
49
49
  pauseForTTL<T>(result: T, options: CallOptions): Promise<void>;
50
50
  publishDone<T>(result: T, hotMesh: HotMesh, options: CallOptions): Promise<void>;
51
51
  flush(entity: string, id: string, namespace?: string): Promise<string | void>;