@hotmeshio/hotmesh 0.1.8 → 0.1.9

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.
@@ -5,6 +5,7 @@ import { RedisClient, RedisMulti } from '../types/redis';
5
5
  import { StringAnyType } from '../types/serializer';
6
6
  import { StreamCode, StreamStatus } from '../types/stream';
7
7
  import { SystemHealth } from '../types/quorum';
8
+ export declare const hashOptions: (options: any) => string;
8
9
  export declare function getSystemHealth(): Promise<SystemHealth>;
9
10
  export declare function sleepFor(ms: number): Promise<unknown>;
10
11
  export declare function sleepImmediate(): Promise<void>;
@@ -3,8 +3,9 @@ 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.restoreHierarchy = exports.getValueByPath = exports.getIndexedHash = exports.getSymVal = exports.getSymKey = exports.formatISODate = exports.getTimeSeries = exports.getSubscriptionTopic = exports.findSubscriptionForTrigger = exports.findTopKey = exports.XSleepFor = exports.matchesStatus = exports.matchesStatusCode = exports.identifyRedisTypeFromClass = exports.polyfill = exports.identifyRedisType = exports.deterministicRandom = exports.guid = exports.deepCopy = exports.sleepImmediate = exports.sleepFor = exports.getSystemHealth = void 0;
6
+ exports.restoreHierarchy = exports.getValueByPath = exports.getIndexedHash = exports.getSymVal = exports.getSymKey = exports.formatISODate = exports.getTimeSeries = exports.getSubscriptionTopic = exports.findSubscriptionForTrigger = exports.findTopKey = exports.XSleepFor = exports.matchesStatus = exports.matchesStatusCode = exports.identifyRedisTypeFromClass = exports.polyfill = exports.identifyRedisType = exports.deterministicRandom = exports.guid = exports.deepCopy = exports.sleepImmediate = exports.sleepFor = exports.getSystemHealth = exports.hashOptions = void 0;
7
7
  const os_1 = __importDefault(require("os"));
8
+ const crypto_1 = require("crypto");
8
9
  const nanoid_1 = require("nanoid");
9
10
  const enums_1 = require("./enums");
10
11
  async function safeExecute(operation, defaultValue) {
@@ -16,10 +17,17 @@ async function safeExecute(operation, defaultValue) {
16
17
  return defaultValue;
17
18
  }
18
19
  }
20
+ const hashOptions = (options) => {
21
+ const str = JSON.stringify(options);
22
+ return (0, crypto_1.createHash)('sha256').update(str).digest('hex');
23
+ };
24
+ exports.hashOptions = hashOptions;
19
25
  async function getSystemHealth() {
20
26
  const totalMemory = os_1.default.totalmem();
21
27
  const freeMemory = os_1.default.freemem();
22
28
  const usedMemory = totalMemory - freeMemory;
29
+ //NOTE: enable the following if desired; for now, only
30
+ // `memory` is emitted when system health is requested
23
31
  //const cpus = os.cpus();
24
32
  // CPU load calculation remains unchanged
25
33
  // const cpuLoad = cpus.map((cpu, i) => {
@@ -28,9 +36,8 @@ async function getSystemHealth() {
28
36
  // const usage = ((total - idle) / total) * 100;
29
37
  // return { [`CPU ${i} Usage`]: `${usage.toFixed(2)}%` };
30
38
  // });
31
- // Wrap each systeminformation call with safeExecute
39
+ // Wrap each systeminformation call with safeExecute (systeminformation npm package)
32
40
  //const networkStats = await safeExecute(si.networkStats(), []);
33
- // Construct the system health object with error handling in mind
34
41
  const systemHealth = {
35
42
  TotalMemoryGB: `${(totalMemory / 1024 / 1024 / 1024).toFixed(2)} GB`,
36
43
  FreeMemoryGB: `${(freeMemory / 1024 / 1024 / 1024).toFixed(2)} GB`,
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hotmeshio/hotmesh",
3
- "version": "0.1.8",
3
+ "version": "0.1.9",
4
4
  "description": "Unbreakable Workflows",
5
5
  "main": "./build/index.js",
6
6
  "types": "./build/index.d.ts",
@@ -84,7 +84,6 @@
84
84
  "js-yaml": "^4.1.0",
85
85
  "ms": "^2.1.3",
86
86
  "nanoid": "^3.3.6",
87
- "systeminformation": "^5.22.2",
88
87
  "winston": "^3.8.2"
89
88
  },
90
89
  "devDependencies": {
@@ -27,19 +27,17 @@ declare class Activity {
27
27
  constructor(config: ActivityType, data: ActivityData, metadata: ActivityMetadata, hook: ActivityData | null, engine: EngineService, context?: JobState);
28
28
  setLeg(leg: ActivityLeg): void;
29
29
  /**
30
- * Upon entering leg 1 of a duplexed activty, verify
31
- * all aspects of the entry including job and activty state
30
+ * Upon entering leg 1 of a duplexed activity
32
31
  */
33
32
  verifyEntry(): Promise<void>;
34
33
  /**
35
- * Upon entering leg 2 of a duplexed activty, verify
36
- * all aspects of the re-entry including job and activty state
34
+ * Upon entering leg 2 of a duplexed activity
37
35
  */
38
36
  verifyReentry(): Promise<number>;
39
37
  processEvent(status?: StreamStatus, code?: StreamCode, type?: 'hook' | 'output'): Promise<void>;
40
- processPending(telemetry: TelemetryService, type: 'hook' | 'output'): Promise<MultiResponseFlags>;
41
- processSuccess(telemetry: TelemetryService, type: 'hook' | 'output'): Promise<MultiResponseFlags>;
42
- processError(telemetry: TelemetryService, type: string): Promise<MultiResponseFlags>;
38
+ processPending(type: 'hook' | 'output'): Promise<MultiResponseFlags>;
39
+ processSuccess(type: 'hook' | 'output'): Promise<MultiResponseFlags>;
40
+ processError(): Promise<MultiResponseFlags>;
43
41
  transitionAdjacent(multiResponse: MultiResponseFlags, telemetry: TelemetryService): Promise<void>;
44
42
  resolveStatus(multiResponse: MultiResponseFlags): number;
45
43
  mapJobData(): void;
@@ -31,8 +31,7 @@ class Activity {
31
31
  this.leg = leg;
32
32
  }
33
33
  /**
34
- * Upon entering leg 1 of a duplexed activty, verify
35
- * all aspects of the entry including job and activty state
34
+ * Upon entering leg 1 of a duplexed activity
36
35
  */
37
36
  async verifyEntry() {
38
37
  this.setLeg(1);
@@ -41,8 +40,7 @@ class Activity {
41
40
  await collator_1.CollatorService.notarizeEntry(this);
42
41
  }
43
42
  /**
44
- * Upon entering leg 2 of a duplexed activty, verify
45
- * all aspects of the re-entry including job and activty state
43
+ * Upon entering leg 2 of a duplexed activity
46
44
  */
47
45
  async verifyReentry() {
48
46
  const guid = this.context.metadata.guid;
@@ -79,13 +77,13 @@ class Activity {
79
77
  telemetry.startActivitySpan(this.leg);
80
78
  let multiResponse;
81
79
  if (status === stream_1.StreamStatus.PENDING) {
82
- multiResponse = await this.processPending(telemetry, type);
80
+ multiResponse = await this.processPending(type);
83
81
  }
84
82
  else if (status === stream_1.StreamStatus.SUCCESS) {
85
- multiResponse = await this.processSuccess(telemetry, type);
83
+ multiResponse = await this.processSuccess(type);
86
84
  }
87
85
  else {
88
- multiResponse = await this.processError(telemetry, type);
86
+ multiResponse = await this.processError();
89
87
  }
90
88
  this.transitionAdjacent(multiResponse, telemetry);
91
89
  }
@@ -120,7 +118,7 @@ class Activity {
120
118
  this.logger.debug('activity-process-event-end', { jid, aid });
121
119
  }
122
120
  }
123
- async processPending(telemetry, type) {
121
+ async processPending(type) {
124
122
  this.bindActivityData(type);
125
123
  this.adjacencyList = await this.filterAdjacent();
126
124
  this.mapJobData();
@@ -130,7 +128,7 @@ class Activity {
130
128
  await this.setStatus(this.adjacencyList.length, multi);
131
129
  return (await multi.exec());
132
130
  }
133
- async processSuccess(telemetry, type) {
131
+ async processSuccess(type) {
134
132
  this.bindActivityData(type);
135
133
  this.adjacencyList = await this.filterAdjacent();
136
134
  this.mapJobData();
@@ -140,7 +138,7 @@ class Activity {
140
138
  await this.setStatus(this.adjacencyList.length - 1, multi);
141
139
  return (await multi.exec());
142
140
  }
143
- async processError(telemetry, type) {
141
+ async processError() {
144
142
  this.bindActivityError(this.data);
145
143
  this.adjacencyList = await this.filterAdjacent();
146
144
  if (!this.adjacencyList.length) {
@@ -20,7 +20,7 @@ export declare class ClientService {
20
20
  * creating the stream. This method will verify that the stream
21
21
  * exists and if not, create it.
22
22
  */
23
- static verifyStream: (workflowTopic: string, namespace?: string) => Promise<HotMesh>;
23
+ verifyStream: (hotMeshClient: HotMesh, workflowTopic: string, namespace?: string) => Promise<void>;
24
24
  search: (hotMeshClient: HotMesh, index: string, query: string[]) => Promise<string[]>;
25
25
  workflow: {
26
26
  start: (options: WorkflowOptions) => Promise<WorkflowHandleService>;
@@ -17,15 +17,14 @@ const factory_1 = require("./schemas/factory");
17
17
  class ClientService {
18
18
  constructor(config) {
19
19
  this.getHotMeshClient = async (workflowTopic, namespace) => {
20
+ //namespace isolation requires the connection options to be hashed
21
+ //as multiple intersecting databases can be used by the same service
22
+ const optionsHash = (0, utils_1.hashOptions)(this.connection.options);
20
23
  const targetNS = namespace ?? factory_1.APP_ID;
21
- if (ClientService.instances.has(targetNS)) {
22
- const targetTopic = `${namespace ?? factory_1.APP_ID}.${workflowTopic}`;
23
- const hotMeshClient = await ClientService.instances.get(targetNS);
24
+ const connectionNS = `${optionsHash}.${targetNS}`;
25
+ if (ClientService.instances.has(connectionNS)) {
26
+ const hotMeshClient = await ClientService.instances.get(connectionNS);
24
27
  await this.verifyWorkflowActive(hotMeshClient, targetNS);
25
- if (!ClientService.topics.includes(targetTopic)) {
26
- ClientService.topics.push(targetTopic);
27
- await ClientService.createStream(hotMeshClient, workflowTopic, namespace);
28
- }
29
28
  return hotMeshClient;
30
29
  }
31
30
  //create and cache an instance
@@ -39,11 +38,24 @@ class ClientService {
39
38
  },
40
39
  },
41
40
  });
42
- ClientService.instances.set(targetNS, hotMeshClient);
43
- await ClientService.createStream(await hotMeshClient, workflowTopic, namespace);
41
+ ClientService.instances.set(connectionNS, hotMeshClient);
44
42
  await this.activateWorkflow(await hotMeshClient, targetNS);
45
43
  return hotMeshClient;
46
44
  };
45
+ /**
46
+ * It is possible for a client to invoke a workflow without first
47
+ * creating the stream. This method will verify that the stream
48
+ * exists and if not, create it.
49
+ */
50
+ this.verifyStream = async (hotMeshClient, workflowTopic, namespace) => {
51
+ const optionsHash = (0, utils_1.hashOptions)(this.connection.options);
52
+ const targetNS = namespace ?? factory_1.APP_ID;
53
+ const targetTopic = `${optionsHash}.${targetNS}.${workflowTopic}`;
54
+ if (!ClientService.topics.includes(targetTopic)) {
55
+ ClientService.topics.push(targetTopic);
56
+ await ClientService.createStream(hotMeshClient, workflowTopic, namespace);
57
+ }
58
+ };
47
59
  this.search = async (hotMeshClient, index, query) => {
48
60
  const store = hotMeshClient.engine.store;
49
61
  if (query[0]?.startsWith('FT.')) {
@@ -57,10 +69,11 @@ class ClientService {
57
69
  const workflowName = options.entity ?? options.workflowName;
58
70
  const trc = options.workflowTrace;
59
71
  const spn = options.workflowSpan;
60
- //NOTE: HotMesh 'workflowTopic' is a created by concatenating
61
- // the taskQueue and workflowName used by the Durable module
72
+ //hotmesh topic is a combination of the durable queue+workflowname
62
73
  const workflowTopic = `${taskQueueName}-${workflowName}`;
63
74
  const hotMeshClient = await this.getHotMeshClient(workflowTopic, options.namespace);
75
+ //verify that the stream channel exists before enqueueing
76
+ await this.verifyStream(hotMeshClient, workflowTopic, options.namespace);
64
77
  const payload = {
65
78
  arguments: [...options.args],
66
79
  originJobId: options.originJobId,
@@ -200,21 +213,4 @@ ClientService.createStream = async (hotMeshClient, workflowTopic, namespace) =>
200
213
  //ignore if already exists
201
214
  }
202
215
  };
203
- /**
204
- * It is possible for a client to invoke a workflow without first
205
- * creating the stream. This method will verify that the stream
206
- * exists and if not, create it.
207
- */
208
- ClientService.verifyStream = async (workflowTopic, namespace) => {
209
- const targetTopic = `${namespace ?? factory_1.APP_ID}.${workflowTopic}`;
210
- const targetNS = namespace ?? factory_1.APP_ID;
211
- if (ClientService.instances.has(targetNS)) {
212
- const hotMeshClient = await ClientService.instances.get(targetNS);
213
- if (!ClientService.topics.includes(targetTopic)) {
214
- ClientService.topics.push(targetTopic);
215
- await ClientService.createStream(hotMeshClient, workflowTopic, namespace);
216
- }
217
- return hotMeshClient;
218
- }
219
- };
220
216
  exports.ClientService = ClientService;
@@ -1,13 +1,11 @@
1
1
  import { HotMeshService as HotMesh } from '../hotmesh';
2
- import { Connection, Registry, WorkerConfig, WorkerOptions } from '../../types/durable';
2
+ import { Registry, WorkerConfig, WorkerOptions } from '../../types/durable';
3
3
  export declare class WorkerService {
4
4
  static activityRegistry: Registry;
5
- static connections: Map<string, import("../../types/durable").ConnectionConfig>;
6
5
  static instances: Map<string, HotMesh | Promise<HotMesh>>;
7
6
  workflowRunner: HotMesh;
8
7
  activityRunner: HotMesh;
9
8
  static getHotMesh: (workflowTopic: string, config?: Partial<WorkerConfig>, options?: WorkerOptions) => Promise<HotMesh>;
10
- static findConnectionByNamespace: (namespace: string) => Connection;
11
9
  static activateWorkflow(hotMesh: HotMesh): Promise<void>;
12
10
  static registerActivities<ACT>(activities: ACT): Registry;
13
11
  static create(config: WorkerConfig): Promise<WorkerService>;
@@ -58,8 +58,6 @@ class WorkerService {
58
58
  return WorkerService.activityRegistry;
59
59
  }
60
60
  static async create(config) {
61
- const targetNamespace = config.namespace ?? factory_1.APP_ID;
62
- WorkerService.connections.set(targetNamespace, config.connection);
63
61
  const workflow = config.workflow;
64
62
  const [workflowFunctionName, workflowFunction] = WorkerService.resolveWorkflowTarget(workflow);
65
63
  const baseTopic = `${config.taskQueue}-${workflowFunctionName}`;
@@ -94,9 +92,12 @@ class WorkerService {
94
92
  class: config.connection.class,
95
93
  options: config.connection.options,
96
94
  };
95
+ const targetNamespace = config?.namespace ?? factory_1.APP_ID;
96
+ const optionsHash = (0, utils_1.hashOptions)(config?.connection?.options);
97
+ const targetTopic = `${optionsHash}.${targetNamespace}.${activityTopic}`;
97
98
  const hotMeshWorker = await hotmesh_1.HotMeshService.init({
98
99
  logLevel: config.options?.logLevel ?? enums_1.HMSH_LOGLEVEL,
99
- appId: config.namespace ?? factory_1.APP_ID,
100
+ appId: targetNamespace,
100
101
  engine: { redis: redisConfig },
101
102
  workers: [
102
103
  {
@@ -106,10 +107,9 @@ class WorkerService {
106
107
  },
107
108
  ],
108
109
  });
109
- WorkerService.instances.set(activityTopic, hotMeshWorker);
110
+ WorkerService.instances.set(targetTopic, hotMeshWorker);
110
111
  return hotMeshWorker;
111
112
  }
112
- //this is the linked worker function in the reentrant workflow test
113
113
  wrapActivityFunctions() {
114
114
  return async (data) => {
115
115
  try {
@@ -172,6 +172,9 @@ class WorkerService {
172
172
  class: config.connection.class,
173
173
  options: config.connection.options,
174
174
  };
175
+ const targetNamespace = config?.namespace ?? factory_1.APP_ID;
176
+ const optionsHash = (0, utils_1.hashOptions)(config?.connection?.options);
177
+ const targetTopic = `${optionsHash}.${targetNamespace}.${workflowTopic}`;
175
178
  const hotMeshWorker = await hotmesh_1.HotMeshService.init({
176
179
  logLevel: config.options?.logLevel ?? enums_1.HMSH_LOGLEVEL,
177
180
  appId: config.namespace ?? factory_1.APP_ID,
@@ -184,7 +187,7 @@ class WorkerService {
184
187
  },
185
188
  ],
186
189
  });
187
- WorkerService.instances.set(workflowTopic, hotMeshWorker);
190
+ WorkerService.instances.set(targetTopic, hotMeshWorker);
188
191
  return hotMeshWorker;
189
192
  }
190
193
  wrapWorkflowFunction(workflowFunction, workflowTopic, config) {
@@ -199,6 +202,7 @@ class WorkerService {
199
202
  context.set('canRetry', workflowInput.canRetry);
200
203
  context.set('counter', counter);
201
204
  context.set('interruptionRegistry', interruptionRegistry);
205
+ context.set('connection', config.connection);
202
206
  context.set('namespace', config.namespace ?? factory_1.APP_ID);
203
207
  context.set('raw', data);
204
208
  context.set('workflowId', workflowInput.workflowId);
@@ -372,11 +376,11 @@ class WorkerService {
372
376
  }
373
377
  _a = WorkerService;
374
378
  WorkerService.activityRegistry = {}; //user's activities
375
- WorkerService.connections = new Map();
376
379
  WorkerService.instances = new Map();
377
380
  WorkerService.getHotMesh = async (workflowTopic, config, options) => {
378
381
  const targetNamespace = config?.namespace ?? factory_1.APP_ID;
379
- const targetTopic = `${targetNamespace}.${workflowTopic}`;
382
+ const optionsHash = (0, utils_1.hashOptions)(config?.connection?.options);
383
+ const targetTopic = `${optionsHash}.${targetNamespace}.${workflowTopic}`;
380
384
  if (WorkerService.instances.has(targetTopic)) {
381
385
  return await WorkerService.instances.get(targetTopic);
382
386
  }
@@ -384,25 +388,13 @@ WorkerService.getHotMesh = async (workflowTopic, config, options) => {
384
388
  logLevel: options?.logLevel ?? enums_1.HMSH_LOGLEVEL,
385
389
  appId: targetNamespace,
386
390
  engine: {
387
- redis: { ...WorkerService.findConnectionByNamespace(targetNamespace) },
391
+ redis: { ...config?.connection },
388
392
  },
389
393
  });
390
394
  WorkerService.instances.set(targetTopic, hotMeshClient);
391
395
  await WorkerService.activateWorkflow(await hotMeshClient);
392
396
  return hotMeshClient;
393
397
  };
394
- WorkerService.findConnectionByNamespace = (namespace) => {
395
- let defaultConnection;
396
- for (const [ns, value] of WorkerService.connections) {
397
- if (ns === namespace) {
398
- return value;
399
- }
400
- else if (!defaultConnection) {
401
- defaultConnection = value;
402
- }
403
- }
404
- return defaultConnection;
405
- };
406
398
  WorkerService.Context = {
407
399
  info: () => {
408
400
  return {
@@ -69,6 +69,7 @@ class WorkflowService {
69
69
  const interruptionRegistry = store.get('interruptionRegistry');
70
70
  const workflowDimension = store.get('workflowDimension') ?? '';
71
71
  const workflowTopic = store.get('workflowTopic');
72
+ const connection = store.get('connection');
72
73
  const namespace = store.get('namespace');
73
74
  const originJobId = store.get('originJobId');
74
75
  const workflowTrace = store.get('workflowTrace');
@@ -82,6 +83,7 @@ class WorkflowService {
82
83
  counter: COUNTER.counter,
83
84
  cursor,
84
85
  interruptionRegistry,
86
+ connection,
85
87
  namespace,
86
88
  originJobId,
87
89
  raw,
@@ -100,8 +102,9 @@ class WorkflowService {
100
102
  static async getHotMesh() {
101
103
  const store = storage_1.asyncLocalStorage.getStore();
102
104
  const workflowTopic = store.get('workflowTopic');
105
+ const connection = store.get('connection');
103
106
  const namespace = store.get('namespace');
104
- return await worker_1.WorkerService.getHotMesh(workflowTopic, { namespace });
107
+ return await worker_1.WorkerService.getHotMesh(workflowTopic, { connection, namespace });
105
108
  }
106
109
  /**
107
110
  * Spawns a child workflow and awaits the return.
@@ -315,10 +318,12 @@ class WorkflowService {
315
318
  const workflowId = store.get('workflowId');
316
319
  const workflowDimension = store.get('workflowDimension') ?? '';
317
320
  const workflowTopic = store.get('workflowTopic');
321
+ const connection = store.get('connection');
318
322
  const namespace = store.get('namespace');
319
323
  const COUNTER = store.get('counter');
320
324
  const execIndex = COUNTER.counter = COUNTER.counter + 1;
321
325
  const hotMeshClient = await worker_1.WorkerService.getHotMesh(workflowTopic, {
326
+ connection,
322
327
  namespace,
323
328
  });
324
329
  //this ID is used as a item key with a hash (dash prefix ensures no collision)
@@ -347,8 +352,10 @@ class WorkflowService {
347
352
  static async signal(signalId, data) {
348
353
  const store = storage_1.asyncLocalStorage.getStore();
349
354
  const workflowTopic = store.get('workflowTopic');
355
+ const connection = store.get('connection');
350
356
  const namespace = store.get('namespace');
351
357
  const hotMeshClient = await worker_1.WorkerService.getHotMesh(workflowTopic, {
358
+ connection,
352
359
  namespace,
353
360
  });
354
361
  if (await WorkflowService.isSideEffectAllowed(hotMeshClient, 'signal')) {
@@ -365,8 +372,9 @@ class WorkflowService {
365
372
  * @param {HookOptions} options - the hook options
366
373
  */
367
374
  static async hook(options) {
368
- const { workflowId, namespace, workflowTopic } = WorkflowService.getContext();
375
+ const { workflowId, connection, namespace, workflowTopic } = WorkflowService.getContext();
369
376
  const hotMeshClient = await worker_1.WorkerService.getHotMesh(workflowTopic, {
377
+ connection,
370
378
  namespace,
371
379
  });
372
380
  if (await WorkflowService.isSideEffectAllowed(hotMeshClient, 'hook')) {
@@ -395,13 +403,14 @@ class WorkflowService {
395
403
  * @template T - the result type
396
404
  */
397
405
  static async once(fn, ...args) {
398
- const { COUNTER, namespace, workflowId, workflowTopic, workflowDimension, replay, } = WorkflowService.getContext();
406
+ const { COUNTER, connection, namespace, workflowId, workflowTopic, workflowDimension, replay, } = WorkflowService.getContext();
399
407
  const execIndex = COUNTER.counter = COUNTER.counter + 1;
400
408
  const sessionId = `-once${workflowDimension}-${execIndex}-`;
401
409
  if (sessionId in replay) {
402
410
  return serializer_1.SerializerService.fromString(replay[sessionId]).data;
403
411
  }
404
412
  const hotMeshClient = await worker_1.WorkerService.getHotMesh(workflowTopic, {
413
+ connection,
405
414
  namespace,
406
415
  });
407
416
  const keyParams = {
@@ -424,8 +433,9 @@ class WorkflowService {
424
433
  * Interrupts a running job
425
434
  */
426
435
  static async interrupt(jobId, options = {}) {
427
- const { workflowTopic, namespace } = WorkflowService.getContext();
436
+ const { workflowTopic, connection, namespace, } = WorkflowService.getContext();
428
437
  const hotMeshClient = await worker_1.WorkerService.getHotMesh(workflowTopic, {
438
+ connection,
429
439
  namespace,
430
440
  });
431
441
  if (await WorkflowService.isSideEffectAllowed(hotMeshClient, 'interrupt')) {
@@ -88,6 +88,10 @@ type WorkflowContext = {
88
88
  * the native HotMesh message that encapsulates the arguments, metadata, and raw data for the workflow
89
89
  */
90
90
  raw: StreamData;
91
+ /**
92
+ * the HotMesh connection configuration (io/redis NPM package reference and login credentials)
93
+ */
94
+ connection: Connection;
91
95
  };
92
96
  /**
93
97
  * The schema for the full-text-search (RediSearch) index.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hotmeshio/hotmesh",
3
- "version": "0.1.8",
3
+ "version": "0.1.9",
4
4
  "description": "Unbreakable Workflows",
5
5
  "main": "./build/index.js",
6
6
  "types": "./build/index.d.ts",
@@ -84,7 +84,6 @@
84
84
  "js-yaml": "^4.1.0",
85
85
  "ms": "^2.1.3",
86
86
  "nanoid": "^3.3.6",
87
- "systeminformation": "^5.22.2",
88
87
  "winston": "^3.8.2"
89
88
  },
90
89
  "devDependencies": {
package/types/durable.ts CHANGED
@@ -106,6 +106,11 @@ type WorkflowContext = {
106
106
  * the native HotMesh message that encapsulates the arguments, metadata, and raw data for the workflow
107
107
  */
108
108
  raw: StreamData;
109
+
110
+ /**
111
+ * the HotMesh connection configuration (io/redis NPM package reference and login credentials)
112
+ */
113
+ connection: Connection;
109
114
  };
110
115
 
111
116
  /**