@hotmeshio/hotmesh 0.0.16 → 0.0.18

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 (53) hide show
  1. package/build/modules/utils.d.ts +3 -0
  2. package/build/modules/utils.js +10 -1
  3. package/build/package.json +1 -1
  4. package/build/services/activities/activity.d.ts +4 -12
  5. package/build/services/activities/activity.js +19 -156
  6. package/build/services/activities/hook.d.ts +20 -0
  7. package/build/services/activities/hook.js +124 -0
  8. package/build/services/activities/index.d.ts +2 -0
  9. package/build/services/activities/index.js +2 -0
  10. package/build/services/activities/trigger.js +1 -1
  11. package/build/services/collator/index.js +0 -1
  12. package/build/services/compiler/deployer.d.ts +2 -0
  13. package/build/services/compiler/deployer.js +29 -2
  14. package/build/services/durable/client.d.ts +8 -1
  15. package/build/services/durable/client.js +47 -0
  16. package/build/services/durable/factory.js +88 -11
  17. package/build/services/durable/search.d.ts +15 -0
  18. package/build/services/durable/search.js +45 -0
  19. package/build/services/durable/workflow.d.ts +1 -0
  20. package/build/services/durable/workflow.js +36 -0
  21. package/build/services/engine/index.d.ts +7 -2
  22. package/build/services/engine/index.js +2 -1
  23. package/build/services/store/clients/ioredis.d.ts +1 -0
  24. package/build/services/store/clients/ioredis.js +12 -0
  25. package/build/services/store/clients/redis.d.ts +1 -0
  26. package/build/services/store/clients/redis.js +3 -0
  27. package/build/services/store/index.d.ts +1 -0
  28. package/build/services/telemetry/index.js +2 -1
  29. package/build/types/activity.d.ts +6 -3
  30. package/build/types/durable.d.ts +11 -1
  31. package/build/types/hook.d.ts +1 -0
  32. package/build/types/index.d.ts +2 -2
  33. package/modules/utils.ts +11 -0
  34. package/package.json +1 -1
  35. package/services/activities/activity.ts +20 -167
  36. package/services/activities/hook.ts +149 -0
  37. package/services/activities/index.ts +2 -0
  38. package/services/activities/trigger.ts +1 -1
  39. package/services/collator/index.ts +0 -1
  40. package/services/compiler/deployer.ts +32 -2
  41. package/services/durable/client.ts +51 -2
  42. package/services/durable/factory.ts +88 -11
  43. package/services/durable/search.ts +54 -0
  44. package/services/durable/workflow.ts +34 -1
  45. package/services/engine/index.ts +8 -4
  46. package/services/store/clients/ioredis.ts +13 -0
  47. package/services/store/clients/redis.ts +4 -0
  48. package/services/store/index.ts +1 -0
  49. package/services/telemetry/index.ts +2 -1
  50. package/types/activity.ts +7 -2
  51. package/types/durable.ts +9 -0
  52. package/types/hook.ts +1 -0
  53. package/types/index.ts +2 -0
@@ -76,6 +76,40 @@ class ClientService {
76
76
  await this.activateWorkflow(await hotMeshClient);
77
77
  return hotMeshClient;
78
78
  };
79
+ /**
80
+ * For those deployments with a redis stack backend (with the FT module),
81
+ * this method will configure the search index for the workflow.
82
+ */
83
+ this.configureSearchIndex = async (hotMeshClient, search) => {
84
+ if (search) {
85
+ const store = hotMeshClient.engine.store;
86
+ const schema = [];
87
+ for (const [key, value] of Object.entries(search.schema)) {
88
+ //prefix with a comma (avoids collisions with hotmesh reserved words)
89
+ schema.push(`_${key}`);
90
+ schema.push(value.type);
91
+ if (value.sortable) {
92
+ schema.push('SORTABLE');
93
+ }
94
+ }
95
+ try {
96
+ const keyParams = {
97
+ appId: hotMeshClient.appId,
98
+ jobId: ''
99
+ };
100
+ const hotMeshPrefix = key_1.KeyService.mintKey(hotMeshClient.namespace, key_1.KeyType.JOB_STATE, keyParams);
101
+ const prefixes = search.prefix.map((prefix) => `${hotMeshPrefix}${prefix}`);
102
+ await store.exec('FT.CREATE', `${search.index}`, 'ON', 'HASH', 'PREFIX', prefixes.length, ...prefixes, 'SCHEMA', ...schema);
103
+ }
104
+ catch (err) {
105
+ hotMeshClient.engine.logger.info('durable-client-search-err', { err });
106
+ }
107
+ }
108
+ };
109
+ this.search = async (hotMeshClient, index, query) => {
110
+ const store = hotMeshClient.engine.store;
111
+ return await store.exec('FT.SEARCH', index, ...query);
112
+ };
79
113
  this.workflow = {
80
114
  start: async (options) => {
81
115
  const taskQueueName = options.taskQueue;
@@ -85,8 +119,10 @@ class ClientService {
85
119
  //topic is concat of taskQueue and workflowName
86
120
  const workflowTopic = `${taskQueueName}-${workflowName}`;
87
121
  const hotMeshClient = await this.getHotMeshClient(workflowTopic);
122
+ this.configureSearchIndex(hotMeshClient, options.search);
88
123
  const payload = {
89
124
  arguments: [...options.args],
125
+ parentWorkflowId: options.parentWorkflowId,
90
126
  workflowId: options.workflowId || (0, nanoid_1.nanoid)(),
91
127
  workflowTopic: workflowTopic,
92
128
  backoffCoefficient: options.config?.backoffCoefficient || factory_1.DEFAULT_COEFFICIENT,
@@ -102,6 +138,17 @@ class ClientService {
102
138
  const workflowTopic = `${taskQueue}-${workflowName}`;
103
139
  const hotMeshClient = await this.getHotMeshClient(workflowTopic);
104
140
  return new handle_1.WorkflowHandleService(hotMeshClient, workflowTopic, workflowId);
141
+ },
142
+ search: async (taskQueue, workflowName, index, ...query) => {
143
+ const workflowTopic = `${taskQueue}-${workflowName}`;
144
+ const hotMeshClient = await this.getHotMeshClient(workflowTopic);
145
+ try {
146
+ return await this.search(hotMeshClient, index, query);
147
+ }
148
+ catch (err) {
149
+ hotMeshClient.engine.logger.error('durable-client-search-err', { err });
150
+ throw err;
151
+ }
105
152
  }
106
153
  };
107
154
  this.connection = config.connection;
@@ -25,6 +25,8 @@ const getWorkflowYAML = (app, version) => {
25
25
  schema:
26
26
  type: object
27
27
  properties:
28
+ parentWorkflowId:
29
+ type: string
28
30
  workflowId:
29
31
  type: string
30
32
  arguments:
@@ -47,12 +49,17 @@ const getWorkflowYAML = (app, version) => {
47
49
  type: trigger
48
50
  stats:
49
51
  id: '{$self.input.data.workflowId}'
52
+ key: '{$self.input.data.parentWorkflowId}'
53
+ granularity: infinity
54
+ measures:
55
+ - measure: index
56
+ target: '{$self.input.data.parentWorkflowId}'
50
57
  job:
51
58
  maps:
52
59
  done: false
53
60
 
54
61
  a1:
55
- type: activity
62
+ type: hook
56
63
  cycle: true
57
64
  output:
58
65
  schema:
@@ -66,6 +73,7 @@ const getWorkflowYAML = (app, version) => {
66
73
  w1:
67
74
  type: worker
68
75
  topic: '{t1.output.data.workflowTopic}'
76
+ emit: '{$job.data.done}'
69
77
  retry:
70
78
  '599': [2]
71
79
  input:
@@ -123,6 +131,19 @@ const getWorkflowYAML = (app, version) => {
123
131
  response: '{$self.output.data.response}'
124
132
  done: '{$self.output.data.done}'
125
133
 
134
+ a2:
135
+ type: hook
136
+ title: Wait for cleanup signal
137
+ hook:
138
+ type: object
139
+ properties:
140
+ done:
141
+ type: boolean
142
+ job:
143
+ maps:
144
+ workflowId: '{t1.output.data.workflowId}'
145
+
146
+
126
147
  a594:
127
148
  title: Wait for signals
128
149
  type: await
@@ -227,7 +248,7 @@ const getWorkflowYAML = (app, version) => {
227
248
 
228
249
  a599:
229
250
  title: Sleep exponentially longer before retrying
230
- type: activity
251
+ type: hook
231
252
  sleep: '{a1.output.data.duration}'
232
253
 
233
254
  c599:
@@ -280,7 +301,7 @@ const getWorkflowYAML = (app, version) => {
280
301
  done: true
281
302
 
282
303
  s2:
283
- title: Awaken sleep flows so they end and self-clean
304
+ title: Awaken sleeping flows so they end and self-clean
284
305
  type: signal
285
306
  subtype: all
286
307
  key_name: parentWorkflowId
@@ -352,10 +373,55 @@ const getWorkflowYAML = (app, version) => {
352
373
  type: boolean
353
374
  maps:
354
375
  done: true
376
+
377
+ s4:
378
+ title: Awaken child FLOWS so they end and self-clean
379
+ type: signal
380
+ subtype: all
381
+ key_name: parentWorkflowId
382
+ key_value:
383
+ '@pipe':
384
+ - ['{$job.metadata.jid}', '-f']
385
+ - ['{@string.concat}']
386
+ topic: ${app}.childflow.awaken
387
+ resolver:
388
+ schema:
389
+ type: object
390
+ properties:
391
+ data:
392
+ type: object
393
+ properties:
394
+ parentWorkflowId:
395
+ type: string
396
+ scrub:
397
+ type: boolean
398
+ maps:
399
+ data:
400
+ parentWorkflowId:
401
+ '@pipe':
402
+ - ['{$job.metadata.jid}', '-f']
403
+ - ['{@string.concat}']
404
+ scrub: true
405
+ signal:
406
+ schema:
407
+ type: object
408
+ properties:
409
+ done:
410
+ type: boolean
411
+ maps:
412
+ done: true
355
413
 
356
414
  transitions:
357
415
  t1:
358
416
  - to: a1
417
+ - to: a2
418
+ conditions:
419
+ match:
420
+ - expected: true
421
+ actual:
422
+ '@pipe':
423
+ - ['{$job.metadata.key}', true, false]
424
+ - ['{@conditional.ternary}']
359
425
  a1:
360
426
  - to: w1
361
427
  w1:
@@ -377,11 +443,13 @@ const getWorkflowYAML = (app, version) => {
377
443
  - to: s3
378
444
  conditions:
379
445
  code: [200, 598, 597, 596]
446
+ - to: s4
447
+ conditions:
448
+ code: [200, 598, 597, 596]
380
449
  a594:
381
450
  - to: c594
382
451
  conditions:
383
452
  code: 202
384
-
385
453
  a595:
386
454
  - to: c595
387
455
  conditions:
@@ -389,6 +457,14 @@ const getWorkflowYAML = (app, version) => {
389
457
  a599:
390
458
  - to: c599
391
459
 
460
+ hooks:
461
+ ${app}.childflow.awaken:
462
+ - to: a2
463
+ conditions:
464
+ match:
465
+ - expected: '{t1.output.data.workflowId}'
466
+ actual: '{$self.hook.data.id}'
467
+
392
468
  - subscribes: ${app}.activity.execute
393
469
  publishes: ${app}.activity.executed
394
470
 
@@ -464,7 +540,7 @@ const getWorkflowYAML = (app, version) => {
464
540
  done: true
465
541
 
466
542
  s1a:
467
- type: activity
543
+ type: hook
468
544
  title: Wait for cleanup signal
469
545
  hook:
470
546
  type: object
@@ -484,6 +560,7 @@ const getWorkflowYAML = (app, version) => {
484
560
  hooks:
485
561
  ${app}.activity.awaken:
486
562
  - to: s1a
563
+ keep_alive: true
487
564
  conditions:
488
565
  match:
489
566
  - expected: '{t1a.output.data.workflowId}'
@@ -530,13 +607,13 @@ const getWorkflowYAML = (app, version) => {
530
607
  target: '{$self.input.data.parentWorkflowId}'
531
608
 
532
609
  a1s:
533
- type: activity
610
+ type: hook
534
611
  title: Sleep for a duration
535
612
  sleep: '{t1s.output.data.duration}'
536
613
  emit: true
537
614
 
538
615
  a2s:
539
- type: activity
616
+ type: hook
540
617
  title: Wait for cleanup signal
541
618
  hook:
542
619
  type: object
@@ -608,7 +685,7 @@ const getWorkflowYAML = (app, version) => {
608
685
 
609
686
  a1wc:
610
687
  title: Split signal data
611
- type: activity
688
+ type: hook
612
689
  cycle: true
613
690
  output:
614
691
  schema:
@@ -646,7 +723,7 @@ const getWorkflowYAML = (app, version) => {
646
723
  - ['{t1wc.output.data.signals}', 1]
647
724
  - ['{@array.slice}']
648
725
  a2wc:
649
- type: activity
726
+ type: hook
650
727
  output:
651
728
  schema:
652
729
  type: object
@@ -773,7 +850,7 @@ const getWorkflowYAML = (app, version) => {
773
850
  target: '{$self.input.data.parentWorkflowId}'
774
851
 
775
852
  a1ww:
776
- type: activity
853
+ type: hook
777
854
  title: Wait for custom signal
778
855
  emit: true
779
856
  hook:
@@ -788,7 +865,7 @@ const getWorkflowYAML = (app, version) => {
788
865
  signalId: '{t1ww.output.data.signalId}'
789
866
 
790
867
  a2ww:
791
- type: activity
868
+ type: hook
792
869
  title: Wait for cleanup signal
793
870
  hook:
794
871
  type: object
@@ -0,0 +1,15 @@
1
+ import { HotMeshService as HotMesh } from '../hotmesh';
2
+ import { RedisClient, RedisMulti } from '../../types/redis';
3
+ import { StoreService } from '../store';
4
+ export declare class Search {
5
+ jobId: string;
6
+ hotMeshClient: HotMesh;
7
+ store: StoreService<RedisClient, RedisMulti> | null;
8
+ safeKey(key: string): string;
9
+ constructor(workflowId: string, hotMeshClient: HotMesh);
10
+ set(key: string, value: string): Promise<void>;
11
+ get(key: string): Promise<string>;
12
+ del(key: string): Promise<void>;
13
+ incr(key: string, val: number): Promise<number>;
14
+ mult(key: string, val: number): Promise<number>;
15
+ }
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Search = void 0;
4
+ const key_1 = require("../../modules/key");
5
+ class Search {
6
+ safeKey(key) {
7
+ //note: protect the execution namespace with a prefix,
8
+ //so its design never conflicts with the hotmesh keyspace
9
+ return `_${key}`;
10
+ }
11
+ constructor(workflowId, hotMeshClient) {
12
+ const keyParams = {
13
+ appId: hotMeshClient.appId,
14
+ jobId: ''
15
+ };
16
+ const hotMeshPrefix = key_1.KeyService.mintKey(hotMeshClient.namespace, key_1.KeyType.JOB_STATE, keyParams);
17
+ this.jobId = `${hotMeshPrefix}${workflowId}`;
18
+ this.hotMeshClient = hotMeshClient;
19
+ this.store = hotMeshClient.engine.store;
20
+ }
21
+ async set(key, value) {
22
+ await this.store.exec('HSET', this.jobId, this.safeKey(key), value.toString());
23
+ }
24
+ async get(key) {
25
+ try {
26
+ return await this.store.exec('HGET', this.jobId, this.safeKey(key));
27
+ }
28
+ catch (err) {
29
+ this.hotMeshClient.logger.error('durable-search-get-error', { err });
30
+ return '';
31
+ }
32
+ }
33
+ async del(key) {
34
+ await this.store.exec('HDEL', this.jobId, this.safeKey(key));
35
+ }
36
+ async incr(key, val) {
37
+ return Number(await this.store.exec('HINCRBYFLOAT', this.jobId, this.safeKey(key), val.toString()));
38
+ }
39
+ async mult(key, val) {
40
+ const log = Math.log(val);
41
+ const logTotal = Number(await this.store.exec('HINCRBYFLOAT', this.jobId, this.safeKey(key), log.toString()));
42
+ return Math.exp(logTotal);
43
+ }
44
+ }
45
+ exports.Search = Search;
@@ -5,6 +5,7 @@ export declare class WorkflowService {
5
5
  */
6
6
  static executeChild<T>(options: WorkflowOptions): Promise<T>;
7
7
  static proxyActivities<ACT>(options?: ActivityConfig): ProxyType<ACT>;
8
+ static data(command: 'del' | 'get' | 'set' | 'incr' | 'mult', ...args: string[]): Promise<number | boolean | string>;
8
9
  static sleep(duration: string): Promise<number>;
9
10
  static waitForSignal(signals: string[], options?: Record<string, string>): Promise<Record<any, any>[]>;
10
11
  static wrapActivity<T>(activityName: string, options?: ActivityConfig): T;
@@ -11,6 +11,7 @@ const client_1 = require("./client");
11
11
  const connection_1 = require("./connection");
12
12
  const factory_1 = require("./factory");
13
13
  const errors_1 = require("../../modules/errors");
14
+ const search_1 = require("./search");
14
15
  /*
15
16
  `proxyActivities` returns a wrapped instance of the
16
17
  target activity, so that when the workflow calls a
@@ -54,6 +55,7 @@ class WorkflowService {
54
55
  const COUNTER = store.get('counter');
55
56
  const execIndex = COUNTER.counter = COUNTER.counter + 1;
56
57
  const childJobId = `${workflowId}-$${options.workflowName}-${execIndex}`;
58
+ const parentWorkflowId = `${workflowId}-f`;
57
59
  const client = new client_1.ClientService({
58
60
  connection: await connection_1.ConnectionService.connect(worker_1.WorkerService.connection),
59
61
  });
@@ -65,6 +67,7 @@ class WorkflowService {
65
67
  handle = await client.workflow.start({
66
68
  ...options,
67
69
  workflowId: childJobId,
70
+ parentWorkflowId,
68
71
  workflowTrace,
69
72
  workflowSpan,
70
73
  });
@@ -86,6 +89,39 @@ class WorkflowService {
86
89
  }
87
90
  return proxy;
88
91
  }
92
+ static async data(command, ...args) {
93
+ const store = asyncLocalStorage_1.asyncLocalStorage.getStore();
94
+ if (!store) {
95
+ throw new Error('durable-store-not-found');
96
+ }
97
+ const workflowId = store.get('workflowId');
98
+ const workflowTopic = store.get('workflowTopic');
99
+ try {
100
+ const hotMeshClient = await worker_1.WorkerService.getHotMesh(workflowTopic);
101
+ const search = new search_1.Search(workflowId, hotMeshClient);
102
+ if (command === 'get') {
103
+ return await search.get(args[0]);
104
+ }
105
+ else if (command === 'set') {
106
+ await search.set(args[0], args[1]);
107
+ return true;
108
+ }
109
+ else if (command === 'del') {
110
+ await search.del(args[0]);
111
+ return true;
112
+ }
113
+ else if (command === 'incr') {
114
+ return await search.incr(args[0], Number(args[1]));
115
+ }
116
+ else if (command === 'mult') {
117
+ return await search.mult(args[0], Number(args[1]));
118
+ }
119
+ }
120
+ catch (e) {
121
+ console.error(e);
122
+ return '';
123
+ }
124
+ }
89
125
  static async sleep(duration) {
90
126
  const seconds = (0, ms_1.default)(duration) / 1000;
91
127
  const store = asyncLocalStorage_1.asyncLocalStorage.getStore();
@@ -1,4 +1,9 @@
1
- import { Activity } from '../activities/activity';
1
+ import { Await } from '../activities/await';
2
+ import { Cycle } from '../activities/cycle';
3
+ import { Hook } from '../activities/hook';
4
+ import { Signal } from '../activities/signal';
5
+ import { Worker } from '../activities/worker';
6
+ import { Trigger } from '../activities/trigger';
2
7
  import { ILogger } from '../logger';
3
8
  import { StoreSignaler } from '../signaler/store';
4
9
  import { StreamSignaler } from '../signaler/stream';
@@ -44,7 +49,7 @@ declare class EngineService {
44
49
  processWebHooks(): Promise<void>;
45
50
  processTimeHooks(): Promise<void>;
46
51
  throttle(delayInMillis: number): Promise<void>;
47
- initActivity(topic: string, data?: JobData, context?: JobState): Promise<Activity>;
52
+ initActivity(topic: string, data?: JobData, context?: JobState): Promise<Await | Cycle | Hook | Signal | Trigger | Worker>;
48
53
  getSchema(topic: string): Promise<[activityId: string, schema: ActivityType]>;
49
54
  getSettings(): Promise<HotMeshSettings>;
50
55
  isPrivate(topic: string): boolean;
@@ -143,7 +143,8 @@ class EngineService {
143
143
  // ************* METADATA/MODEL METHODS *************
144
144
  async initActivity(topic, data = {}, context) {
145
145
  const [activityId, schema] = await this.getSchema(topic);
146
- const ActivityHandler = activities_1.default[schema.type];
146
+ utils_1.polyfill;
147
+ const ActivityHandler = activities_1.default[utils_1.polyfill.resolveActivityType(schema.type)];
147
148
  if (ActivityHandler) {
148
149
  const utc = (0, utils_1.formatISODate)(new Date());
149
150
  const metadata = {
@@ -14,6 +14,7 @@ declare class IORedisStoreService extends StoreService<RedisClientType, RedisMul
14
14
  serializer: Serializer;
15
15
  constructor(redisClient: RedisClientType);
16
16
  getMulti(): RedisMultiType;
17
+ exec(...args: any[]): Promise<string | string[] | string[][]>;
17
18
  hGetAllResult(result: any): any;
18
19
  addTaskQueues(keys: string[]): Promise<void>;
19
20
  publish(keyType: KeyType.QUORUM, message: Record<string, any>, appId: string, engineId?: string): Promise<boolean>;
@@ -10,6 +10,18 @@ class IORedisStoreService extends index_1.StoreService {
10
10
  getMulti() {
11
11
  return this.redisClient.multi();
12
12
  }
13
+ async exec(...args) {
14
+ const response = await this.redisClient.call.apply(this.redisClient, args);
15
+ if (typeof response === 'string') {
16
+ return response;
17
+ }
18
+ else if (Array.isArray(response)) {
19
+ if (Array.isArray(response[0])) {
20
+ return response;
21
+ }
22
+ return response;
23
+ }
24
+ }
13
25
  hGetAllResult(result) {
14
26
  //ioredis response signature is [null, {}] or [null, null]
15
27
  return result[1];
@@ -15,6 +15,7 @@ declare class RedisStoreService extends StoreService<RedisClientType, RedisMulti
15
15
  commands: Record<string, string>;
16
16
  constructor(redisClient: RedisClientType);
17
17
  getMulti(): RedisMultiType;
18
+ exec(...args: any[]): Promise<string | string[] | string[][]>;
18
19
  publish(keyType: KeyType.QUORUM, message: Record<string, any>, appId: string, engineId?: string): Promise<boolean>;
19
20
  zAdd(key: string, score: number | string, value: string | number, redisMulti?: RedisMultiType): Promise<any>;
20
21
  zRangeByScoreWithScores(key: string, score: number | string, value: string | number): Promise<string | null>;
@@ -36,6 +36,9 @@ class RedisStoreService extends index_1.StoreService {
36
36
  const multi = this.redisClient.MULTI();
37
37
  return multi;
38
38
  }
39
+ async exec(...args) {
40
+ return await this.redisClient.sendCommand(args);
41
+ }
39
42
  async publish(keyType, message, appId, engineId) {
40
43
  const topic = this.mintKey(keyType, { appId, engineId });
41
44
  const status = await this.redisClient.publish(topic, JSON.stringify(message));
@@ -22,6 +22,7 @@ declare abstract class StoreService<T, U extends AbstractRedisClient> {
22
22
  logger: ILogger;
23
23
  commands: Record<string, string>;
24
24
  abstract getMulti(): U;
25
+ abstract exec(...args: any[]): Promise<string | string[] | string[][]>;
25
26
  abstract publish(keyType: KeyType.QUORUM, message: Record<string, any>, appId: string, engineId?: string): Promise<boolean>;
26
27
  abstract xgroup(command: 'CREATE', key: string, groupName: string, id: string, mkStream?: 'MKSTREAM'): Promise<boolean>;
27
28
  abstract xadd(key: string, id: string, messageId: string, messageValue: string, multi?: U): Promise<string | U>;
@@ -8,6 +8,7 @@ const package_json_1 = __importDefault(require("../../package.json"));
8
8
  const mapper_1 = require("../mapper");
9
9
  const stream_1 = require("../../types/stream");
10
10
  const telemetry_1 = require("../../types/telemetry");
11
+ const utils_1 = require("../../modules/utils");
11
12
  class TelemetryService {
12
13
  constructor(appId, config, metadata, context) {
13
14
  this.leg = 1;
@@ -209,7 +210,7 @@ class TelemetryService {
209
210
  state[`${metadata.aid}/output/metadata/l1s`] = context['$self'].output.metadata.l1s;
210
211
  state[`${metadata.aid}/output/metadata/l2s`] = context['$self'].output.metadata.l2s;
211
212
  }
212
- else if (config.type === 'activity' && leg === 1) {
213
+ else if (utils_1.polyfill.resolveActivityType(config.type) === 'hook' && leg === 1) {
213
214
  //activities run non-duplexed and only have a single leg
214
215
  state[`${metadata.aid}/output/metadata/l1s`] = context['$self'].output.metadata.l1s;
215
216
  state[`${metadata.aid}/output/metadata/l2s`] = context['$self'].output.metadata.l1s;
@@ -1,6 +1,6 @@
1
1
  import { MetricTypes } from "./stats";
2
2
  import { StreamRetryPolicy } from "./stream";
3
- type ActivityExecutionType = 'trigger' | 'await' | 'worker' | 'activity' | 'emit' | 'iterate' | 'cycle' | 'signal';
3
+ type ActivityExecutionType = 'trigger' | 'await' | 'worker' | 'activity' | 'emit' | 'iterate' | 'cycle' | 'signal' | 'hook';
4
4
  type Consumes = Record<string, string[]>;
5
5
  interface BaseActivity {
6
6
  title?: string;
@@ -59,6 +59,9 @@ interface CycleActivity extends BaseActivity {
59
59
  type: 'cycle';
60
60
  ancestor: string;
61
61
  }
62
+ interface HookActivity extends BaseActivity {
63
+ type: 'hook';
64
+ }
62
65
  interface SignalActivity extends BaseActivity {
63
66
  type: 'signal';
64
67
  subtype: 'one' | 'all';
@@ -72,7 +75,7 @@ interface SignalActivity extends BaseActivity {
72
75
  interface IterateActivity extends BaseActivity {
73
76
  type: 'iterate';
74
77
  }
75
- type ActivityType = BaseActivity | TriggerActivity | AwaitActivity | WorkerActivity | IterateActivity;
78
+ type ActivityType = BaseActivity | TriggerActivity | AwaitActivity | WorkerActivity | IterateActivity | HookActivity;
76
79
  type ActivityData = Record<string, any>;
77
80
  type ActivityMetadata = {
78
81
  aid: string;
@@ -98,4 +101,4 @@ type ActivityDataType = {
98
101
  hook?: Record<string, unknown>;
99
102
  };
100
103
  type ActivityLeg = 1 | 2;
101
- export { ActivityContext, ActivityData, ActivityDataType, ActivityDuplex, ActivityLeg, ActivityMetadata, ActivityType, Consumes, TriggerActivityStats, AwaitActivity, CycleActivity, SignalActivity, BaseActivity, IterateActivity, TriggerActivity, WorkerActivity };
104
+ export { ActivityContext, ActivityData, ActivityDataType, ActivityDuplex, ActivityLeg, ActivityMetadata, ActivityType, Consumes, TriggerActivityStats, AwaitActivity, CycleActivity, HookActivity, SignalActivity, BaseActivity, IterateActivity, TriggerActivity, WorkerActivity };
@@ -5,13 +5,23 @@ type WorkflowConfig = {
5
5
  maximumInterval?: string;
6
6
  initialInterval?: string;
7
7
  };
8
+ type WorkflowSearchOptions = {
9
+ index: string;
10
+ prefix: string[];
11
+ schema: Record<string, {
12
+ type: 'TEXT' | 'NUMERIC' | 'TAG';
13
+ sortable: boolean;
14
+ }>;
15
+ };
8
16
  type WorkflowOptions = {
9
17
  taskQueue: string;
10
18
  args: any[];
11
19
  workflowId: string;
12
20
  workflowName?: string;
21
+ parentWorkflowId?: string;
13
22
  workflowTrace?: string;
14
23
  workflowSpan?: string;
24
+ search?: WorkflowSearchOptions;
15
25
  config?: WorkflowConfig;
16
26
  };
17
27
  type SignalOptions = {
@@ -72,4 +82,4 @@ type ActivityConfig = {
72
82
  maximumInterval: string;
73
83
  };
74
84
  };
75
- export { ActivityConfig, ActivityWorkflowDataType, ClientConfig, ContextType, ConnectionConfig, Connection, NativeConnection, ProxyType, Registry, SignalOptions, WorkerConfig, WorkflowConfig, WorkerOptions, WorkflowDataType, WorkflowOptions, };
85
+ export { ActivityConfig, ActivityWorkflowDataType, ClientConfig, ContextType, ConnectionConfig, Connection, NativeConnection, ProxyType, Registry, SignalOptions, WorkerConfig, WorkflowConfig, WorkerOptions, WorkflowSearchOptions, WorkflowDataType, WorkflowOptions, };
@@ -12,6 +12,7 @@ interface HookConditions {
12
12
  }
13
13
  interface HookRule {
14
14
  to: string;
15
+ keep_alive?: boolean;
15
16
  conditions: HookConditions;
16
17
  }
17
18
  interface HookRules {
@@ -1,9 +1,9 @@
1
- export { ActivityType, ActivityDataType, ActivityContext, ActivityData, ActivityDuplex, ActivityLeg, ActivityMetadata, Consumes, AwaitActivity, BaseActivity, CycleActivity, WorkerActivity, IterateActivity, SignalActivity, TriggerActivity, TriggerActivityStats } from './activity';
1
+ export { ActivityType, ActivityDataType, ActivityContext, ActivityData, ActivityDuplex, ActivityLeg, ActivityMetadata, Consumes, AwaitActivity, BaseActivity, CycleActivity, HookActivity, WorkerActivity, IterateActivity, SignalActivity, TriggerActivity, TriggerActivityStats } from './activity';
2
2
  export { App, AppVID, AppTransitions, AppSubscriptions } from './app';
3
3
  export { AsyncSignal } from './async';
4
4
  export { CacheMode } from './cache';
5
5
  export { CollationFaultType, CollationStage } from './collator';
6
- export { ActivityConfig, ActivityWorkflowDataType, ClientConfig, ContextType, ConnectionConfig, Connection, NativeConnection, ProxyType, Registry, WorkflowConfig, WorkerConfig, WorkerOptions, WorkflowDataType, WorkflowOptions, } from './durable';
6
+ export { ActivityConfig, ActivityWorkflowDataType, ClientConfig, ContextType, ConnectionConfig, Connection, NativeConnection, ProxyType, Registry, WorkflowConfig, WorkerConfig, WorkerOptions, WorkflowSearchOptions, WorkflowDataType, WorkflowOptions, } from './durable';
7
7
  export { HookCondition, HookConditions, HookGate, HookInterface, HookRule, HookRules, HookSignal } from './hook';
8
8
  export { RedisClientType as IORedisClientType, RedisMultiType as IORedisMultiType } from './ioredisclient';
9
9
  export { ILogger } from './logger';
package/modules/utils.ts CHANGED
@@ -23,6 +23,17 @@ export function identifyRedisType(redisInstance: any): 'redis' | 'ioredis' | nul
23
23
  return null;
24
24
  }
25
25
 
26
+ //todo: the polyfill methods will all be deleted in the `beta` release.
27
+ export const polyfill = {
28
+ resolveActivityType(activityType: string): string {
29
+ if (activityType === 'activity') {
30
+ return 'hook';
31
+ }
32
+ return activityType;
33
+ }
34
+ }
35
+
36
+
26
37
  export function identifyRedisTypeFromClass(redisClass: any): 'redis' | 'ioredis' | null {
27
38
  if (redisClass && redisClass.name === 'Redis' || redisClass.name === 'EventEmitter') {
28
39
  return 'ioredis';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hotmeshio/hotmesh",
3
- "version": "0.0.16",
3
+ "version": "0.0.18",
4
4
  "description": "Unbreakable Workflows",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",