@hotmeshio/hotmesh 0.0.53 → 0.0.55

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 (41) hide show
  1. package/README.md +0 -3
  2. package/build/modules/errors.d.ts +6 -48
  3. package/build/modules/errors.js +5 -5
  4. package/build/package.json +1 -1
  5. package/build/services/activities/hook.js +5 -1
  6. package/build/services/activities/trigger.d.ts +5 -2
  7. package/build/services/activities/trigger.js +22 -1
  8. package/build/services/durable/client.js +1 -8
  9. package/build/services/durable/exporter.d.ts +24 -13
  10. package/build/services/durable/exporter.js +145 -127
  11. package/build/services/durable/handle.d.ts +2 -2
  12. package/build/services/durable/handle.js +2 -2
  13. package/build/services/durable/worker.js +1 -1
  14. package/build/services/durable/workflow.d.ts +29 -17
  15. package/build/services/durable/workflow.js +117 -97
  16. package/build/services/engine/index.d.ts +2 -2
  17. package/build/services/engine/index.js +2 -2
  18. package/build/services/hotmesh/index.d.ts +2 -2
  19. package/build/services/hotmesh/index.js +2 -2
  20. package/build/types/durable.d.ts +15 -3
  21. package/build/types/error.d.ts +48 -0
  22. package/build/types/error.js +2 -0
  23. package/build/types/exporter.d.ts +26 -20
  24. package/build/types/index.d.ts +2 -1
  25. package/build/types/job.d.ts +24 -1
  26. package/modules/errors.ts +18 -55
  27. package/package.json +1 -1
  28. package/services/activities/hook.ts +8 -1
  29. package/services/activities/trigger.ts +27 -2
  30. package/services/durable/client.ts +2 -8
  31. package/services/durable/exporter.ts +149 -128
  32. package/services/durable/handle.ts +3 -3
  33. package/services/durable/worker.ts +1 -1
  34. package/services/durable/workflow.ts +137 -104
  35. package/services/engine/index.ts +4 -3
  36. package/services/hotmesh/index.ts +4 -3
  37. package/types/durable.ts +18 -3
  38. package/types/error.ts +52 -0
  39. package/types/exporter.ts +31 -23
  40. package/types/index.ts +8 -1
  41. package/types/job.ts +27 -0
@@ -9,8 +9,8 @@ class WorkflowHandleService {
9
9
  this.hotMesh = hotMesh;
10
10
  this.exporter = new exporter_1.ExporterService(this.hotMesh.appId, this.hotMesh.engine.store, this.hotMesh.engine.logger);
11
11
  }
12
- async export() {
13
- return this.exporter.export(this.workflowId);
12
+ async export(options) {
13
+ return this.exporter.export(this.workflowId, options);
14
14
  }
15
15
  /**
16
16
  * Sends a signal to the workflow. This is a way to send
@@ -231,7 +231,7 @@ class WorkerService {
231
231
  const workflowInput = data.data;
232
232
  const execIndex = counter.counter - interruptionRegistry.length + 1;
233
233
  const { workflowId, workflowTopic, workflowDimension, originJobId } = workflowInput;
234
- const collatorFlowId = `-${workflowId}-$COLLATOR${workflowDimension || ''}-${execIndex}`;
234
+ const collatorFlowId = `-${workflowId}-$${workflowDimension || ''}-$${execIndex}`;
235
235
  return {
236
236
  status: stream_1.StreamStatus.SUCCESS,
237
237
  code: enums_1.HMSH_CODE_DURABLE_ALL,
@@ -2,17 +2,33 @@ import { Search } from './search';
2
2
  import { HotMeshService as HotMesh } from '../hotmesh';
3
3
  import { ActivityConfig, HookOptions, ProxyType, WorkflowContext, WorkflowOptions } from "../../types/durable";
4
4
  import { JobInterruptOptions } from '../../types/job';
5
+ import { DurableChildErrorType, DurableProxyErrorType } from '../../types/error';
5
6
  export declare class WorkflowService {
6
7
  /**
7
- * Return a handle to the hotmesh client currently running the workflow
8
- * @returns {Promise<HotMesh>} - a hotmesh client
8
+ * Returns the synchronous output from the activity (replay)
9
+ * if available locally, revealing whether or not the activity already
10
+ * ran during a prior execution cycle
11
+ * @param {string} prefix - one of: proxy, child, start, wait etc
12
+ * @returns
9
13
  */
10
- static getHotMesh(): Promise<HotMesh>;
14
+ static didRun(prefix: string): Promise<[boolean, number, any]>;
15
+ /**
16
+ * Those methods that may only be called once must be protected by flagging
17
+ * their execution with a unique key (the key is stored in the HASH alongside
18
+ * process state and job state)
19
+ * @private
20
+ */
21
+ static isSideEffectAllowed(hotMeshClient: HotMesh, prefix: string): Promise<boolean>;
11
22
  /**
12
23
  * Returns the current workflow context restored
13
24
  * from Redis
14
25
  */
15
26
  static getContext(): WorkflowContext;
27
+ /**
28
+ * Return a handle to the hotmesh client hosting the workflow execution
29
+ * @returns {Promise<HotMesh>} - a hotmesh client
30
+ */
31
+ static getHotMesh(): Promise<HotMesh>;
16
32
  /**
17
33
  * Spawns a child workflow and awaits the return.
18
34
  * @template T - the result type
@@ -22,6 +38,11 @@ export declare class WorkflowService {
22
38
  * const result = await Durable.workflow.execChild<typeof resultType>({ ...options });
23
39
  */
24
40
  static execChild<T>(options: WorkflowOptions): Promise<T>;
41
+ /**
42
+ * constructs the payload necessary to spawn a child job
43
+ * @private
44
+ */
45
+ static getChildInterruptPayload(context: WorkflowContext, options: WorkflowOptions, execIndex: number): DurableChildErrorType;
25
46
  /**
26
47
  * Spawns a child workflow and returns the child Job ID.
27
48
  * This method guarantees the spawned child has reserved the Job ID,
@@ -53,26 +74,17 @@ export declare class WorkflowService {
53
74
  */
54
75
  static proxyActivities<ACT>(options?: ActivityConfig): ProxyType<ACT>;
55
76
  static wrapActivity<T>(activityName: string, options?: ActivityConfig): T;
77
+ /**
78
+ * constructs the payload necessary to spawn a proxyActivity job
79
+ * @private
80
+ */
81
+ static getProxyInterruptPayload(context: WorkflowContext, activityName: string, execIndex: number, args: any[], options?: ActivityConfig): DurableProxyErrorType;
56
82
  /**
57
83
  * Returns a search session for use when reading/writing to the workflow HASH.
58
84
  * The search session provides access to methods like `get`, `mget`, `set`, `del`, and `incr`.
59
85
  * @returns {Promise<Search>} - a search session
60
86
  */
61
87
  static search(): Promise<Search>;
62
- /**
63
- * Returns the synchronous output from the activity (replay)
64
- * if available locally
65
- * @param {string} prefix - one of: proxy, child, start, wait etc
66
- * @returns
67
- */
68
- static didRun(prefix: string): Promise<[boolean, number, any]>;
69
- /**
70
- * Those methods that may only be called once must be protected by flagging
71
- * their execution with a unique key (the key is stored in the HASH alongside
72
- * process state and job state)
73
- * @private
74
- */
75
- static isSideEffectAllowed(hotMeshClient: HotMesh, prefix: string): Promise<boolean>;
76
88
  /**
77
89
  * Returns a random number between 0 and 1. This number is deterministic
78
90
  * and will never vary for a given seed. This is useful for randomizing
@@ -11,19 +11,51 @@ const storage_1 = require("../../modules/storage");
11
11
  const utils_1 = require("../../modules/utils");
12
12
  const search_1 = require("./search");
13
13
  const worker_1 = require("./worker");
14
+ const serializer_1 = require("../serializer");
14
15
  const stream_1 = require("../../types/stream");
15
16
  const enums_1 = require("../../modules/enums");
16
- const serializer_1 = require("../serializer");
17
17
  class WorkflowService {
18
18
  /**
19
- * Return a handle to the hotmesh client currently running the workflow
20
- * @returns {Promise<HotMesh>} - a hotmesh client
19
+ * Returns the synchronous output from the activity (replay)
20
+ * if available locally, revealing whether or not the activity already
21
+ * ran during a prior execution cycle
22
+ * @param {string} prefix - one of: proxy, child, start, wait etc
23
+ * @returns
21
24
  */
22
- static async getHotMesh() {
25
+ static async didRun(prefix) {
26
+ const { COUNTER, replay, workflowDimension, } = WorkflowService.getContext();
27
+ const execIndex = COUNTER.counter = COUNTER.counter + 1;
28
+ const sessionId = `-${prefix}${workflowDimension}-${execIndex}-`;
29
+ if (sessionId in replay) {
30
+ const restored = serializer_1.SerializerService.fromString(replay[sessionId]);
31
+ return [true, execIndex, restored];
32
+ }
33
+ return [false, execIndex, null];
34
+ }
35
+ /**
36
+ * Those methods that may only be called once must be protected by flagging
37
+ * their execution with a unique key (the key is stored in the HASH alongside
38
+ * process state and job state)
39
+ * @private
40
+ */
41
+ static async isSideEffectAllowed(hotMeshClient, prefix) {
23
42
  const store = storage_1.asyncLocalStorage.getStore();
24
- const workflowTopic = store.get('workflowTopic');
25
- const namespace = store.get('namespace');
26
- return await worker_1.WorkerService.getHotMesh(workflowTopic, { namespace });
43
+ const workflowId = store.get('workflowId');
44
+ const workflowDimension = store.get('workflowDimension') ?? '';
45
+ const COUNTER = store.get('counter');
46
+ const execIndex = COUNTER.counter = COUNTER.counter + 1;
47
+ const sessionId = `-${prefix}${workflowDimension}-${execIndex}-`;
48
+ const replay = store.get('replay');
49
+ if (sessionId in replay) {
50
+ return false;
51
+ }
52
+ const keyParams = {
53
+ appId: hotMeshClient.appId,
54
+ jobId: workflowId
55
+ };
56
+ const workflowGuid = key_1.KeyService.mintKey(hotMeshClient.namespace, key_1.KeyType.JOB_STATE, keyParams);
57
+ const guidValue = Number(await hotMeshClient.engine.store.exec('HINCRBYFLOAT', workflowGuid, sessionId, '1'));
58
+ return guidValue === 1;
27
59
  }
28
60
  /**
29
61
  * Returns the current workflow context restored
@@ -34,9 +66,11 @@ class WorkflowService {
34
66
  const workflowId = store.get('workflowId');
35
67
  const replay = store.get('replay');
36
68
  const cursor = store.get('cursor');
69
+ const interruptionRegistry = store.get('interruptionRegistry');
37
70
  const workflowDimension = store.get('workflowDimension') ?? '';
38
71
  const workflowTopic = store.get('workflowTopic');
39
72
  const namespace = store.get('namespace');
73
+ const originJobId = store.get('originJobId');
40
74
  const workflowTrace = store.get('workflowTrace');
41
75
  const canRetry = store.get('canRetry');
42
76
  const workflowSpan = store.get('workflowSpan');
@@ -47,7 +81,9 @@ class WorkflowService {
47
81
  COUNTER,
48
82
  counter: COUNTER.counter,
49
83
  cursor,
84
+ interruptionRegistry,
50
85
  namespace,
86
+ originJobId,
51
87
  raw,
52
88
  replay,
53
89
  workflowId,
@@ -57,6 +93,16 @@ class WorkflowService {
57
93
  workflowSpan,
58
94
  };
59
95
  }
96
+ /**
97
+ * Return a handle to the hotmesh client hosting the workflow execution
98
+ * @returns {Promise<HotMesh>} - a hotmesh client
99
+ */
100
+ static async getHotMesh() {
101
+ const store = storage_1.asyncLocalStorage.getStore();
102
+ const workflowTopic = store.get('workflowTopic');
103
+ const namespace = store.get('namespace');
104
+ return await worker_1.WorkerService.getHotMesh(workflowTopic, { namespace });
105
+ }
60
106
  /**
61
107
  * Spawns a child workflow and awaits the return.
62
108
  * @template T - the result type
@@ -69,13 +115,14 @@ class WorkflowService {
69
115
  //SYNC
70
116
  //check if the activity already ran (check $error/done)
71
117
  const isStartChild = options.await === false;
72
- const [didRun, execIndex, result] = await WorkflowService.didRun(isStartChild ? 'start' : 'child');
73
- const store = storage_1.asyncLocalStorage.getStore();
74
- const canRetry = store.get('canRetry');
118
+ const prefix = isStartChild ? 'start' : 'child';
119
+ const [didRun, execIndex, result] = await WorkflowService.didRun(prefix);
120
+ const context = WorkflowService.getContext();
121
+ const { canRetry, interruptionRegistry } = context;
75
122
  if (didRun) {
76
123
  if (result?.$error && (!result.$error.is_stream_error || (result.$error.is_stream_error && !canRetry))) {
77
124
  if (options?.config?.throwOnError !== false) {
78
- //rethrow remote execution error (simulates throw)
125
+ //rethrow remote execution error (simulates local failure)
79
126
  const code = result.$error.code;
80
127
  const message = result.$error.message;
81
128
  const stack = result.$error.stack;
@@ -98,18 +145,38 @@ class WorkflowService {
98
145
  return result.data;
99
146
  }
100
147
  }
101
- //package the interruption inputs
102
- const interruptionRegistry = store.get('interruptionRegistry');
103
- const workflowId = store.get('workflowId');
104
- const originJobId = store.get('originJobId');
105
- const workflowDimension = store.get('workflowDimension') ?? '';
106
- const entityOrEmptyString = options.entity ?? '';
107
- const childJobId = options.workflowId ?? `${entityOrEmptyString}-${workflowId}-$${options.entity ?? options.workflowName}${workflowDimension}-${execIndex}`;
148
+ const interruptionMessage = WorkflowService.getChildInterruptPayload(context, options, execIndex);
149
+ //push the packaged inputs to the registry
150
+ interruptionRegistry.push({
151
+ code: enums_1.HMSH_CODE_DURABLE_CHILD,
152
+ ...interruptionMessage,
153
+ });
154
+ //ASYNC
155
+ //sleep (allow others to be packaged / registered) and throw the error
156
+ await (0, utils_1.sleepFor)(0);
157
+ throw new errors_1.DurableChildError(interruptionMessage);
158
+ }
159
+ /**
160
+ * constructs the payload necessary to spawn a child job
161
+ * @private
162
+ */
163
+ static getChildInterruptPayload(context, options, execIndex) {
164
+ const { workflowId, originJobId, workflowDimension } = context;
165
+ let childJobId;
166
+ if (options.workflowId) {
167
+ childJobId = options.workflowId;
168
+ }
169
+ else if (options.entity) {
170
+ childJobId = `${options.entity}-${workflowId.substring(0, 7)}-${(0, utils_1.guid)()}-${workflowDimension}-${execIndex}`;
171
+ }
172
+ else {
173
+ childJobId = `-${options.workflowName}-${(0, utils_1.guid)()}-${workflowDimension}-${execIndex}`;
174
+ }
108
175
  const parentWorkflowId = workflowId;
109
176
  const taskQueueName = options.entity ?? options.taskQueue;
110
177
  const workflowName = options.entity ?? options.workflowName;
111
178
  const workflowTopic = `${taskQueueName}-${workflowName}`;
112
- const interruptionMessage = {
179
+ return {
113
180
  arguments: [...(options.args || [])],
114
181
  await: options?.await ?? true,
115
182
  backoffCoefficient: options?.config?.backoffCoefficient ?? enums_1.HMSH_DURABLE_EXP_BACKOFF,
@@ -122,15 +189,6 @@ class WorkflowService {
122
189
  workflowId: childJobId,
123
190
  workflowTopic,
124
191
  };
125
- //push the packaged inputs to the registry
126
- interruptionRegistry.push({
127
- code: enums_1.HMSH_CODE_DURABLE_CHILD,
128
- ...interruptionMessage,
129
- });
130
- //ASYNC
131
- //sleep (allow others to be packaged / registered) and throw the error
132
- await (0, utils_1.sleepFor)(0);
133
- throw new errors_1.DurableChildError(interruptionMessage);
134
192
  }
135
193
  /**
136
194
  * Spawns a child workflow and returns the child Job ID.
@@ -144,7 +202,7 @@ class WorkflowService {
144
202
  * const childJobId = await Durable.workflow.startChild({ ...options });
145
203
  */
146
204
  static async startChild(options) {
147
- return this.execChild({ ...options, await: false });
205
+ return WorkflowService.execChild({ ...options, await: false });
148
206
  }
149
207
  /**
150
208
  * Wraps activities in a proxy that durably runs/re-runs them to completion.
@@ -204,31 +262,9 @@ class WorkflowService {
204
262
  return result.data;
205
263
  }
206
264
  //package the interruption inputs
207
- const store = storage_1.asyncLocalStorage.getStore();
208
- const interruptionRegistry = store.get('interruptionRegistry');
209
- const workflowDimension = store.get('workflowDimension') ?? '';
210
- const workflowId = store.get('workflowId');
211
- const originJobId = store.get('originJobId');
212
- const workflowTopic = store.get('workflowTopic');
213
- const activityTopic = `${workflowTopic}-activity`;
214
- const activityJobId = `-${workflowId}-$${activityName}${workflowDimension}-${execIndex}`;
215
- let maximumInterval;
216
- if (options.retryPolicy?.maximumInterval) {
217
- maximumInterval = (0, ms_1.default)(options.retryPolicy.maximumInterval) / 1000;
218
- }
219
- const interruptionMessage = {
220
- arguments: Array.from(arguments),
221
- workflowDimension: workflowDimension,
222
- index: execIndex,
223
- originJobId: originJobId || workflowId,
224
- parentWorkflowId: workflowId,
225
- workflowId: activityJobId,
226
- workflowTopic: activityTopic,
227
- activityName,
228
- backoffCoefficient: options?.retryPolicy?.backoffCoefficient ?? undefined,
229
- maximumAttempts: options?.retryPolicy?.maximumAttempts ?? undefined,
230
- maximumInterval: maximumInterval ?? undefined,
231
- };
265
+ const context = WorkflowService.getContext();
266
+ const { interruptionRegistry } = context;
267
+ const interruptionMessage = WorkflowService.getProxyInterruptPayload(context, activityName, execIndex, Array.from(arguments), options);
232
268
  //push the packaged inputs to the registry
233
269
  interruptionRegistry.push({
234
270
  code: enums_1.HMSH_CODE_DURABLE_PROXY,
@@ -240,6 +276,32 @@ class WorkflowService {
240
276
  throw new errors_1.DurableProxyError(interruptionMessage);
241
277
  };
242
278
  }
279
+ /**
280
+ * constructs the payload necessary to spawn a proxyActivity job
281
+ * @private
282
+ */
283
+ static getProxyInterruptPayload(context, activityName, execIndex, args, options) {
284
+ const { workflowDimension, workflowId, originJobId, workflowTopic } = context;
285
+ const activityTopic = `${workflowTopic}-activity`;
286
+ const activityJobId = `-${workflowId}-$${activityName}${workflowDimension}-${execIndex}`;
287
+ let maximumInterval;
288
+ if (options.retryPolicy?.maximumInterval) {
289
+ maximumInterval = (0, ms_1.default)(options.retryPolicy.maximumInterval) / 1000;
290
+ }
291
+ return {
292
+ arguments: args,
293
+ workflowDimension: workflowDimension,
294
+ index: execIndex,
295
+ originJobId: originJobId || workflowId,
296
+ parentWorkflowId: workflowId,
297
+ workflowId: activityJobId,
298
+ workflowTopic: activityTopic,
299
+ activityName,
300
+ backoffCoefficient: options?.retryPolicy?.backoffCoefficient ?? undefined,
301
+ maximumAttempts: options?.retryPolicy?.maximumAttempts ?? undefined,
302
+ maximumInterval: maximumInterval ?? undefined,
303
+ };
304
+ }
243
305
  /**
244
306
  * Returns a search session for use when reading/writing to the workflow HASH.
245
307
  * The search session provides access to methods like `get`, `mget`, `set`, `del`, and `incr`.
@@ -258,46 +320,6 @@ class WorkflowService {
258
320
  const searchSessionId = `-search${workflowDimension}-${execIndex}`;
259
321
  return new search_1.Search(workflowId, hotMeshClient, searchSessionId);
260
322
  }
261
- /**
262
- * Returns the synchronous output from the activity (replay)
263
- * if available locally
264
- * @param {string} prefix - one of: proxy, child, start, wait etc
265
- * @returns
266
- */
267
- static async didRun(prefix) {
268
- const { COUNTER, replay, workflowDimension, } = WorkflowService.getContext();
269
- const execIndex = COUNTER.counter = COUNTER.counter + 1;
270
- const sessionId = `-${prefix}${workflowDimension}-${execIndex}-`;
271
- if (sessionId in replay) {
272
- return [true, execIndex, serializer_1.SerializerService.fromString(replay[sessionId])];
273
- }
274
- return [false, execIndex, null];
275
- }
276
- /**
277
- * Those methods that may only be called once must be protected by flagging
278
- * their execution with a unique key (the key is stored in the HASH alongside
279
- * process state and job state)
280
- * @private
281
- */
282
- static async isSideEffectAllowed(hotMeshClient, prefix) {
283
- const store = storage_1.asyncLocalStorage.getStore();
284
- const workflowId = store.get('workflowId');
285
- const workflowDimension = store.get('workflowDimension') ?? '';
286
- const COUNTER = store.get('counter');
287
- const execIndex = COUNTER.counter = COUNTER.counter + 1;
288
- const sessionId = `-${prefix}${workflowDimension}-${execIndex}-`;
289
- const replay = store.get('replay');
290
- if (sessionId in replay) {
291
- return false;
292
- }
293
- const keyParams = {
294
- appId: hotMeshClient.appId,
295
- jobId: workflowId
296
- };
297
- const workflowGuid = key_1.KeyService.mintKey(hotMeshClient.namespace, key_1.KeyType.JOB_STATE, keyParams);
298
- const guidValue = Number(await hotMeshClient.engine.store.exec('HINCRBYFLOAT', workflowGuid, sessionId, '1'));
299
- return guidValue === 1;
300
- }
301
323
  /**
302
324
  * Returns a random number between 0 and 1. This number is deterministic
303
325
  * and will never vary for a given seed. This is useful for randomizing
@@ -388,9 +410,7 @@ class WorkflowService {
388
410
  * Interrupts a running job
389
411
  */
390
412
  static async interrupt(jobId, options = {}) {
391
- const store = storage_1.asyncLocalStorage.getStore();
392
- const workflowTopic = store.get('workflowTopic');
393
- const namespace = store.get('namespace');
413
+ const { workflowTopic, namespace } = WorkflowService.getContext();
394
414
  const hotMeshClient = await worker_1.WorkerService.getHotMesh(workflowTopic, { namespace });
395
415
  if (await WorkflowService.isSideEffectAllowed(hotMeshClient, 'interrupt')) {
396
416
  return await hotMeshClient.interrupt(`${hotMeshClient.appId}.execute`, jobId, options);
@@ -15,7 +15,7 @@ import { TaskService } from '../task';
15
15
  import { AppVID } from '../../types/app';
16
16
  import { ActivityType } from '../../types/activity';
17
17
  import { CacheMode } from '../../types/cache';
18
- import { JobState, JobData, JobMetadata, JobOutput, JobStatus, JobInterruptOptions, JobCompletionOptions } from '../../types/job';
18
+ import { JobState, JobData, JobMetadata, JobOutput, JobStatus, JobInterruptOptions, JobCompletionOptions, ExtensionType } from '../../types/job';
19
19
  import { HotMeshApps, HotMeshConfig, HotMeshManifest, HotMeshSettings } from '../../types/hotmesh';
20
20
  import { JobMessageCallback } from '../../types/quorum';
21
21
  import { RedisClient, RedisMulti } from '../../types/redis';
@@ -72,7 +72,7 @@ declare class EngineService {
72
72
  hook(topic: string, data: JobData, status?: StreamStatus, code?: StreamCode): Promise<string>;
73
73
  hookTime(jobId: string, gId: string, topicOrActivity: string, type?: WorkListTaskType): Promise<string | void>;
74
74
  hookAll(hookTopic: string, data: JobData, keyResolver: JobStatsInput, queryFacets?: string[]): Promise<string[]>;
75
- pub(topic: string, data: JobData, context?: JobState): Promise<string>;
75
+ pub(topic: string, data: JobData, context?: JobState, extended?: ExtensionType): Promise<string>;
76
76
  sub(topic: string, callback: JobMessageCallback): Promise<void>;
77
77
  unsub(topic: string): Promise<void>;
78
78
  psub(wild: string, callback: JobMessageCallback): Promise<void>;
@@ -408,10 +408,10 @@ class EngineService {
408
408
  }
409
409
  // ********************** PUB/SUB ENTRY POINT **********************
410
410
  //publish (returns just the job id)
411
- async pub(topic, data, context) {
411
+ async pub(topic, data, context, extended) {
412
412
  const activityHandler = await this.initActivity(topic, data, context);
413
413
  if (activityHandler) {
414
- return await activityHandler.process();
414
+ return await activityHandler.process(extended);
415
415
  }
416
416
  else {
417
417
  throw new Error(`unable to process activity for topic ${topic}`);
@@ -2,7 +2,7 @@ import { EngineService } from '../engine';
2
2
  import { ILogger } from '../logger';
3
3
  import { QuorumService } from '../quorum';
4
4
  import { WorkerService } from '../worker';
5
- import { JobState, JobData, JobOutput, JobStatus, JobInterruptOptions } from '../../types/job';
5
+ import { JobState, JobData, JobOutput, JobStatus, JobInterruptOptions, ExtensionType } from '../../types/job';
6
6
  import { HotMeshConfig, HotMeshManifest } from '../../types/hotmesh';
7
7
  import { JobMessageCallback, QuorumProfile, ThrottleOptions } from '../../types/quorum';
8
8
  import { JobStatsInput, GetStatsOptions, IdsResponse, StatsResponse } from '../../types/stats';
@@ -25,7 +25,7 @@ declare class HotMeshService {
25
25
  initEngine(config: HotMeshConfig, logger: ILogger): Promise<void>;
26
26
  initQuorum(config: HotMeshConfig, engine: EngineService, logger: ILogger): Promise<void>;
27
27
  doWork(config: HotMeshConfig, logger: ILogger): Promise<void>;
28
- pub(topic: string, data?: JobData, context?: JobState): Promise<string>;
28
+ pub(topic: string, data?: JobData, context?: JobState, extended?: ExtensionType): Promise<string>;
29
29
  sub(topic: string, callback: JobMessageCallback): Promise<void>;
30
30
  unsub(topic: string): Promise<void>;
31
31
  psub(wild: string, callback: JobMessageCallback): Promise<void>;
@@ -68,8 +68,8 @@ class HotMeshService {
68
68
  this.workers = await worker_1.WorkerService.init(this.namespace, this.appId, this.guid, config, logger);
69
69
  }
70
70
  // ************* PUB/SUB METHODS *************
71
- async pub(topic, data = {}, context) {
72
- return await this.engine?.pub(topic, data, context);
71
+ async pub(topic, data = {}, context, extended) {
72
+ return await this.engine?.pub(topic, data, context, extended);
73
73
  }
74
74
  async sub(topic, callback) {
75
75
  return await this.engine?.sub(topic, callback);
@@ -1,6 +1,6 @@
1
1
  import { LogLevel } from './logger';
2
2
  import { RedisClass, RedisOptions } from './redis';
3
- import { StringStringType } from './serializer';
3
+ import { StringAnyType, StringStringType } from './serializer';
4
4
  import { StreamData, StreamError } from './stream';
5
5
  /**
6
6
  * Type definition for workflow configuration.
@@ -56,6 +56,14 @@ type WorkflowContext = {
56
56
  * the HotMesh App namespace. `durable` is the default.
57
57
  */
58
58
  namespace: string;
59
+ /**
60
+ * holds list of interruption payloads; if list is longer than 1 when the error is thrown, a `collator` subflow will be used
61
+ */
62
+ interruptionRegistry: any[];
63
+ /**
64
+ * entry point ancestor flow; might be the parent; will never be self
65
+ */
66
+ originJobId: string;
59
67
  /**
60
68
  * the workflow/job ID
61
69
  */
@@ -94,7 +102,7 @@ type WorkflowSearchOptions = {
94
102
  sortable?: boolean;
95
103
  }>;
96
104
  /** Additional data as a key-value record */
97
- data?: Record<string, string>;
105
+ data?: StringStringType;
98
106
  };
99
107
  type WorkflowOptions = {
100
108
  /**
@@ -141,6 +149,10 @@ type WorkflowOptions = {
141
149
  * the full-text-search (RediSearch) options for the workflow
142
150
  */
143
151
  search?: WorkflowSearchOptions;
152
+ /**
153
+ * marker data (begins with a -)
154
+ */
155
+ marker?: StringStringType;
144
156
  /**
145
157
  * the workflow configuration object
146
158
  */
@@ -191,7 +203,7 @@ type SignalOptions = {
191
203
  /**
192
204
  * Input data for the signal (any serializable object)
193
205
  */
194
- data: Record<string, any>;
206
+ data: StringAnyType;
195
207
  /**
196
208
  * Execution ID, also known as the job ID
197
209
  */
@@ -0,0 +1,48 @@
1
+ export type DurableChildErrorType = {
2
+ arguments: string[];
3
+ await?: boolean;
4
+ backoffCoefficient?: number;
5
+ index: number;
6
+ maximumAttempts?: number;
7
+ maximumInterval?: number;
8
+ originJobId: string | null;
9
+ parentWorkflowId: string;
10
+ workflowDimension: string;
11
+ workflowId: string;
12
+ workflowTopic: string;
13
+ };
14
+ export type DurableWaitForAllErrorType = {
15
+ items: string[];
16
+ workflowId: string;
17
+ workflowTopic: string;
18
+ parentWorkflowId: string;
19
+ originJobId: string | null;
20
+ size: number;
21
+ index: number;
22
+ workflowDimension: string;
23
+ };
24
+ export type DurableProxyErrorType = {
25
+ arguments: string[];
26
+ activityName: string;
27
+ backoffCoefficient?: number;
28
+ index: number;
29
+ maximumAttempts?: number;
30
+ maximumInterval?: number;
31
+ originJobId: string | null;
32
+ parentWorkflowId: string;
33
+ workflowDimension: string;
34
+ workflowId: string;
35
+ workflowTopic: string;
36
+ };
37
+ export type DurableWaitForErrorType = {
38
+ signalId: string;
39
+ index: number;
40
+ workflowDimension: string;
41
+ workflowId: string;
42
+ };
43
+ export type DurableSleepErrorType = {
44
+ duration: number;
45
+ index: number;
46
+ workflowDimension: string;
47
+ workflowId: string;
48
+ };
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -1,6 +1,23 @@
1
1
  import { StringAnyType } from "./serializer";
2
2
  export type ExportItem = [(string | null), string, any];
3
+ /**
4
+ * job export data can be large, particularly transitions the timeline
5
+ */
6
+ export type ExportFields = 'data' | 'state' | 'status' | 'timeline' | 'transitions';
3
7
  export interface ExportOptions {
8
+ /**
9
+ * limit the export byte size by specifying an allowlist
10
+ */
11
+ allow?: Array<ExportFields>;
12
+ /**
13
+ * limit the export byte size by specifying a block list
14
+ */
15
+ block?: Array<ExportFields>;
16
+ /**
17
+ * If false, do not return timeline values (like child job response, proxy activity response, etc)
18
+ * @default true
19
+ */
20
+ values?: boolean;
4
21
  }
5
22
  export type JobAction = {
6
23
  cursor: number;
@@ -37,36 +54,25 @@ export interface ExportTransitions {
37
54
  export interface ExportCycles {
38
55
  [key: string]: string[];
39
56
  }
40
- export type IdemParts = {
57
+ export type TimelineType = {
58
+ key: string;
59
+ value: Record<string, any> | string | number | null;
41
60
  index: number;
42
61
  secondary?: number;
43
62
  dimension?: string;
44
63
  };
45
- export type IdemType = {
46
- key: string;
47
- value: string;
48
- parts: IdemParts;
49
- };
50
- export interface TimelineEntry {
51
- activity: string;
52
- dimensions: string;
53
- created: string;
54
- updated: string;
55
- }
56
- export interface TimestampParts {
64
+ export interface TransitionType {
57
65
  activity: string;
58
66
  dimensions: string;
59
67
  created: string;
60
68
  updated: string;
61
69
  }
62
70
  export interface DurableJobExport {
63
- data: StringAnyType;
64
- dependencies?: Record<string, any>[];
65
- state: StringAnyType;
66
- status: string;
67
- timeline?: JobTimeline[];
68
- idempotents: IdemType[];
69
- replay: TimestampParts[];
71
+ data?: StringAnyType;
72
+ state?: StringAnyType;
73
+ status?: number;
74
+ timeline?: TimelineType[];
75
+ transitions?: TransitionType[];
70
76
  }
71
77
  export interface JobExport {
72
78
  dependencies: DependencyExport[];