@hotmeshio/hotmesh 0.0.17 → 0.0.19
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.
- package/build/modules/utils.d.ts +3 -0
- package/build/modules/utils.js +17 -1
- package/build/package.json +1 -1
- package/build/services/activities/activity.d.ts +4 -12
- package/build/services/activities/activity.js +14 -156
- package/build/services/activities/hook.d.ts +20 -0
- package/build/services/activities/hook.js +124 -0
- package/build/services/activities/index.d.ts +2 -0
- package/build/services/activities/index.js +2 -0
- package/build/services/collator/index.js +0 -1
- package/build/services/compiler/deployer.d.ts +2 -0
- package/build/services/compiler/deployer.js +29 -2
- package/build/services/durable/client.d.ts +8 -1
- package/build/services/durable/client.js +54 -40
- package/build/services/durable/factory.js +11 -10
- package/build/services/durable/search.d.ts +15 -0
- package/build/services/durable/search.js +45 -0
- package/build/services/durable/worker.d.ts +6 -1
- package/build/services/durable/worker.js +34 -30
- package/build/services/durable/workflow.d.ts +2 -0
- package/build/services/durable/workflow.js +11 -28
- package/build/services/engine/index.d.ts +7 -2
- package/build/services/engine/index.js +2 -1
- package/build/services/store/clients/ioredis.d.ts +1 -0
- package/build/services/store/clients/ioredis.js +12 -0
- package/build/services/store/clients/redis.d.ts +1 -0
- package/build/services/store/clients/redis.js +3 -0
- package/build/services/store/index.d.ts +1 -0
- package/build/services/telemetry/index.js +2 -1
- package/build/types/activity.d.ts +6 -3
- package/build/types/durable.d.ts +12 -1
- package/build/types/hook.d.ts +1 -0
- package/build/types/index.d.ts +2 -2
- package/modules/utils.ts +17 -0
- package/package.json +1 -1
- package/services/activities/activity.ts +15 -167
- package/services/activities/hook.ts +149 -0
- package/services/activities/index.ts +2 -0
- package/services/collator/index.ts +0 -1
- package/services/compiler/deployer.ts +32 -2
- package/services/durable/client.ts +58 -43
- package/services/durable/factory.ts +11 -10
- package/services/durable/search.ts +54 -0
- package/services/durable/worker.ts +36 -32
- package/services/durable/workflow.ts +14 -30
- package/services/engine/index.ts +8 -4
- package/services/store/clients/ioredis.ts +13 -0
- package/services/store/clients/redis.ts +4 -0
- package/services/store/index.ts +1 -0
- package/services/telemetry/index.ts +2 -1
- package/types/activity.ts +7 -2
- package/types/durable.ts +10 -0
- package/types/hook.ts +1 -0
- package/types/index.ts +2 -0
|
@@ -58,7 +58,7 @@ const getWorkflowYAML = (app: string, version: string) => {
|
|
|
58
58
|
done: false
|
|
59
59
|
|
|
60
60
|
a1:
|
|
61
|
-
type:
|
|
61
|
+
type: hook
|
|
62
62
|
cycle: true
|
|
63
63
|
output:
|
|
64
64
|
schema:
|
|
@@ -131,7 +131,7 @@ const getWorkflowYAML = (app: string, version: string) => {
|
|
|
131
131
|
done: '{$self.output.data.done}'
|
|
132
132
|
|
|
133
133
|
a2:
|
|
134
|
-
type:
|
|
134
|
+
type: hook
|
|
135
135
|
title: Wait for cleanup signal
|
|
136
136
|
hook:
|
|
137
137
|
type: object
|
|
@@ -247,7 +247,7 @@ const getWorkflowYAML = (app: string, version: string) => {
|
|
|
247
247
|
|
|
248
248
|
a599:
|
|
249
249
|
title: Sleep exponentially longer before retrying
|
|
250
|
-
type:
|
|
250
|
+
type: hook
|
|
251
251
|
sleep: '{a1.output.data.duration}'
|
|
252
252
|
|
|
253
253
|
c599:
|
|
@@ -539,7 +539,7 @@ const getWorkflowYAML = (app: string, version: string) => {
|
|
|
539
539
|
done: true
|
|
540
540
|
|
|
541
541
|
s1a:
|
|
542
|
-
type:
|
|
542
|
+
type: hook
|
|
543
543
|
title: Wait for cleanup signal
|
|
544
544
|
hook:
|
|
545
545
|
type: object
|
|
@@ -559,6 +559,7 @@ const getWorkflowYAML = (app: string, version: string) => {
|
|
|
559
559
|
hooks:
|
|
560
560
|
${app}.activity.awaken:
|
|
561
561
|
- to: s1a
|
|
562
|
+
keep_alive: true
|
|
562
563
|
conditions:
|
|
563
564
|
match:
|
|
564
565
|
- expected: '{t1a.output.data.workflowId}'
|
|
@@ -605,13 +606,13 @@ const getWorkflowYAML = (app: string, version: string) => {
|
|
|
605
606
|
target: '{$self.input.data.parentWorkflowId}'
|
|
606
607
|
|
|
607
608
|
a1s:
|
|
608
|
-
type:
|
|
609
|
+
type: hook
|
|
609
610
|
title: Sleep for a duration
|
|
610
611
|
sleep: '{t1s.output.data.duration}'
|
|
611
612
|
emit: true
|
|
612
613
|
|
|
613
614
|
a2s:
|
|
614
|
-
type:
|
|
615
|
+
type: hook
|
|
615
616
|
title: Wait for cleanup signal
|
|
616
617
|
hook:
|
|
617
618
|
type: object
|
|
@@ -683,7 +684,7 @@ const getWorkflowYAML = (app: string, version: string) => {
|
|
|
683
684
|
|
|
684
685
|
a1wc:
|
|
685
686
|
title: Split signal data
|
|
686
|
-
type:
|
|
687
|
+
type: hook
|
|
687
688
|
cycle: true
|
|
688
689
|
output:
|
|
689
690
|
schema:
|
|
@@ -721,7 +722,7 @@ const getWorkflowYAML = (app: string, version: string) => {
|
|
|
721
722
|
- ['{t1wc.output.data.signals}', 1]
|
|
722
723
|
- ['{@array.slice}']
|
|
723
724
|
a2wc:
|
|
724
|
-
type:
|
|
725
|
+
type: hook
|
|
725
726
|
output:
|
|
726
727
|
schema:
|
|
727
728
|
type: object
|
|
@@ -848,7 +849,7 @@ const getWorkflowYAML = (app: string, version: string) => {
|
|
|
848
849
|
target: '{$self.input.data.parentWorkflowId}'
|
|
849
850
|
|
|
850
851
|
a1ww:
|
|
851
|
-
type:
|
|
852
|
+
type: hook
|
|
852
853
|
title: Wait for custom signal
|
|
853
854
|
emit: true
|
|
854
855
|
hook:
|
|
@@ -863,7 +864,7 @@ const getWorkflowYAML = (app: string, version: string) => {
|
|
|
863
864
|
signalId: '{t1ww.output.data.signalId}'
|
|
864
865
|
|
|
865
866
|
a2ww:
|
|
866
|
-
type:
|
|
867
|
+
type: hook
|
|
867
868
|
title: Wait for cleanup signal
|
|
868
869
|
hook:
|
|
869
870
|
type: object
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { HotMeshService as HotMesh } from '../hotmesh'
|
|
2
|
+
import { RedisClient, RedisMulti } from '../../types/redis';
|
|
3
|
+
import { StoreService } from '../store';
|
|
4
|
+
import { KeyService, KeyType } from '../../modules/key';
|
|
5
|
+
|
|
6
|
+
export class Search {
|
|
7
|
+
jobId: string;
|
|
8
|
+
hotMeshClient: HotMesh;
|
|
9
|
+
store: StoreService<RedisClient, RedisMulti> | null;
|
|
10
|
+
|
|
11
|
+
safeKey(key:string): string {
|
|
12
|
+
//note: protect the execution namespace with a prefix,
|
|
13
|
+
//so its design never conflicts with the hotmesh keyspace
|
|
14
|
+
return `_${key}`;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
constructor(workflowId: string, hotMeshClient: HotMesh) {
|
|
18
|
+
const keyParams = {
|
|
19
|
+
appId: hotMeshClient.appId,
|
|
20
|
+
jobId: ''
|
|
21
|
+
}
|
|
22
|
+
const hotMeshPrefix = KeyService.mintKey(hotMeshClient.namespace, KeyType.JOB_STATE, keyParams);
|
|
23
|
+
this.jobId = `${hotMeshPrefix}${workflowId}`;
|
|
24
|
+
this.hotMeshClient = hotMeshClient;
|
|
25
|
+
this.store = hotMeshClient.engine.store as StoreService<RedisClient, RedisMulti>;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async set(key: string, value: string): Promise<void> {
|
|
29
|
+
await this.store.exec('HSET', this.jobId, this.safeKey(key), value.toString());
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async get(key: string): Promise<string> {
|
|
33
|
+
try {
|
|
34
|
+
return await this.store.exec('HGET',this.jobId, this.safeKey(key)) as string;
|
|
35
|
+
} catch (err) {
|
|
36
|
+
this.hotMeshClient.logger.error('durable-search-get-error', { err });
|
|
37
|
+
return '';
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async del(key: string): Promise<void> {
|
|
42
|
+
await this.store.exec('HDEL', this.jobId, this.safeKey(key));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async incr(key: string, val: number): Promise<number> {
|
|
46
|
+
return Number(await this.store.exec('HINCRBYFLOAT', this.jobId, this.safeKey(key), val.toString()) as string);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async mult(key: string, val: number): Promise<number> {
|
|
50
|
+
const log = Math.log(val);
|
|
51
|
+
const logTotal = Number(await this.store.exec('HINCRBYFLOAT', this.jobId, this.safeKey(key), log.toString()) as string);
|
|
52
|
+
return Math.exp(logTotal);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -15,43 +15,14 @@ import {
|
|
|
15
15
|
Registry,
|
|
16
16
|
WorkerConfig,
|
|
17
17
|
WorkerOptions,
|
|
18
|
-
WorkflowDataType
|
|
18
|
+
WorkflowDataType,
|
|
19
|
+
WorkflowSearchOptions} from "../../types/durable";
|
|
19
20
|
import { RedisClass, RedisOptions } from '../../types/redis';
|
|
20
21
|
import {
|
|
21
22
|
StreamData,
|
|
22
23
|
StreamDataResponse,
|
|
23
24
|
StreamStatus } from '../../types/stream';
|
|
24
|
-
|
|
25
|
-
/*
|
|
26
|
-
Here is an example of how the methods in this file are used:
|
|
27
|
-
|
|
28
|
-
./worker.ts
|
|
29
|
-
|
|
30
|
-
import { Durable } from '@hotmeshio/hotmesh';
|
|
31
|
-
import Redis from 'ioredis'; //OR `import * as Redis from 'redis';`
|
|
32
|
-
|
|
33
|
-
import * as workflows from './workflows';
|
|
34
|
-
|
|
35
|
-
async function run() {
|
|
36
|
-
const worker = await Durable.Worker.create({
|
|
37
|
-
connection: {
|
|
38
|
-
class: Redis,
|
|
39
|
-
options: {
|
|
40
|
-
host: 'localhost',
|
|
41
|
-
port: 6379,
|
|
42
|
-
},
|
|
43
|
-
},
|
|
44
|
-
taskQueue: 'hello-world',
|
|
45
|
-
workflow: workflows.example,
|
|
46
|
-
});
|
|
47
|
-
await worker.run();
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
run().catch((err) => {
|
|
51
|
-
console.error(err);
|
|
52
|
-
process.exit(1);
|
|
53
|
-
});
|
|
54
|
-
*/
|
|
25
|
+
import { KeyService, KeyType } from '../../modules/key';
|
|
55
26
|
|
|
56
27
|
export class WorkerService {
|
|
57
28
|
static activityRegistry: Registry = {}; //user's activities
|
|
@@ -112,6 +83,38 @@ export class WorkerService {
|
|
|
112
83
|
return WorkerService.activityRegistry;
|
|
113
84
|
}
|
|
114
85
|
|
|
86
|
+
/**
|
|
87
|
+
* For those deployments with a redis stack backend (with the FT module),
|
|
88
|
+
* this method will configure the search index for the workflow.
|
|
89
|
+
*/
|
|
90
|
+
//todo: bind this to the Search service; update constructor to expect hotMeshClient as first param (id is optional
|
|
91
|
+
//refactor and delete other one as well)
|
|
92
|
+
static async configureSearchIndex(hotMeshClient: HotMesh, search?: WorkflowSearchOptions): Promise<void> {
|
|
93
|
+
if (search?.schema) {
|
|
94
|
+
const store = hotMeshClient.engine.store;
|
|
95
|
+
const schema: string[] = [];
|
|
96
|
+
for (const [key, value] of Object.entries(search.schema)) {
|
|
97
|
+
//prefix with a comma (avoids collisions with hotmesh reserved words)
|
|
98
|
+
schema.push(`_${key}`);
|
|
99
|
+
schema.push(value.type);
|
|
100
|
+
if (value.sortable) {
|
|
101
|
+
schema.push('SORTABLE');
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
try {
|
|
105
|
+
const keyParams = {
|
|
106
|
+
appId: hotMeshClient.appId,
|
|
107
|
+
jobId: ''
|
|
108
|
+
}
|
|
109
|
+
const hotMeshPrefix = KeyService.mintKey(hotMeshClient.namespace, KeyType.JOB_STATE, keyParams);
|
|
110
|
+
const prefixes = search.prefix.map((prefix) => `${hotMeshPrefix}${prefix}`);
|
|
111
|
+
await store.exec('FT.CREATE', `${search.index}`, 'ON', 'HASH', 'PREFIX', prefixes.length, ...prefixes, 'SCHEMA', ...schema);
|
|
112
|
+
} catch (err) {
|
|
113
|
+
hotMeshClient.engine.logger.info('durable-client-search-err', { err });
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
115
118
|
static async create(config: WorkerConfig) {
|
|
116
119
|
//always call `registerActivities` before `import`
|
|
117
120
|
WorkerService.connection = config.connection;
|
|
@@ -125,6 +128,7 @@ export class WorkerService {
|
|
|
125
128
|
const worker = new WorkerService();
|
|
126
129
|
worker.activityRunner = await worker.initActivityWorker(config, activityTopic);
|
|
127
130
|
worker.workflowRunner = await worker.initWorkflowWorker(config, workflowTopic, workflowFunction);
|
|
131
|
+
WorkerService.configureSearchIndex(worker.workflowRunner, config.search)
|
|
128
132
|
await WorkerService.activateWorkflow(worker.workflowRunner);
|
|
129
133
|
return worker;
|
|
130
134
|
}
|
|
@@ -4,39 +4,11 @@ import { asyncLocalStorage } from './asyncLocalStorage';
|
|
|
4
4
|
import { WorkerService } from './worker';
|
|
5
5
|
import { ClientService as Client } from './client';
|
|
6
6
|
import { ConnectionService as Connection } from './connection';
|
|
7
|
-
import { ActivityConfig, ProxyType, WorkflowOptions } from "../../types/durable";
|
|
7
|
+
import { ActivityConfig, ProxyType, WorkflowConfig, WorkflowOptions, WorkflowSearchOptions } from "../../types/durable";
|
|
8
8
|
import { JobOutput, JobState } from '../../types';
|
|
9
9
|
import { ACTIVITY_PUBLISHES_TOPIC, ACTIVITY_SUBSCRIBES_TOPIC, SLEEP_SUBSCRIBES_TOPIC, WFS_SUBSCRIBES_TOPIC } from './factory';
|
|
10
10
|
import { DurableIncompleteSignalError, DurableSleepError, DurableWaitForSignalError } from '../../modules/errors';
|
|
11
|
-
|
|
12
|
-
/*
|
|
13
|
-
`proxyActivities` returns a wrapped instance of the
|
|
14
|
-
target activity, so that when the workflow calls a
|
|
15
|
-
proxied activity, it is actually calling the proxy
|
|
16
|
-
function, which in turn calls the activity function.
|
|
17
|
-
|
|
18
|
-
Here is an example of how the methods in this file are used:
|
|
19
|
-
|
|
20
|
-
./workflows.ts
|
|
21
|
-
|
|
22
|
-
import { Durable } from '@hotmeshio/hotmesh';
|
|
23
|
-
import * as activities from './activities';
|
|
24
|
-
|
|
25
|
-
const { greet } = Durable.workflow.proxyActivities<typeof activities>({
|
|
26
|
-
activities: activities,
|
|
27
|
-
startToCloseTimeout: '1 minute',
|
|
28
|
-
retryPolicy: {
|
|
29
|
-
initialInterval: '5 seconds', // Initial delay between retries
|
|
30
|
-
maximumAttempts: 3, // Max number of retry attempts
|
|
31
|
-
backoffCoefficient: 2.0, // Backoff factor for delay between retries: delay = initialInterval * (backoffCoefficient ^ retry_attempt)
|
|
32
|
-
maximumInterval: '30 seconds', // Max delay between retries
|
|
33
|
-
},
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
export async function example(name: string): Promise<string> {
|
|
37
|
-
return await greet(name);
|
|
38
|
-
}
|
|
39
|
-
*/
|
|
11
|
+
import { Search } from './search';
|
|
40
12
|
|
|
41
13
|
export class WorkflowService {
|
|
42
14
|
|
|
@@ -97,6 +69,18 @@ export class WorkflowService {
|
|
|
97
69
|
return proxy;
|
|
98
70
|
}
|
|
99
71
|
|
|
72
|
+
static async search(): Promise<Search> {
|
|
73
|
+
const store = asyncLocalStorage.getStore();
|
|
74
|
+
if (!store) {
|
|
75
|
+
throw new Error('durable-store-not-found');
|
|
76
|
+
}
|
|
77
|
+
const workflowId = store.get('workflowId');
|
|
78
|
+
const workflowTopic = store.get('workflowTopic');
|
|
79
|
+
|
|
80
|
+
const hotMeshClient = await WorkerService.getHotMesh(workflowTopic);
|
|
81
|
+
return new Search(workflowId, hotMeshClient);
|
|
82
|
+
}
|
|
83
|
+
|
|
100
84
|
static async sleep(duration: string): Promise<number> {
|
|
101
85
|
const seconds = ms(duration) / 1000;
|
|
102
86
|
|
package/services/engine/index.ts
CHANGED
|
@@ -3,10 +3,13 @@ import {
|
|
|
3
3
|
formatISODate,
|
|
4
4
|
getSubscriptionTopic,
|
|
5
5
|
identifyRedisType,
|
|
6
|
+
polyfill,
|
|
6
7
|
restoreHierarchy } from '../../modules/utils';
|
|
7
8
|
import Activities from '../activities';
|
|
8
|
-
import { Activity } from '../activities/activity';
|
|
9
9
|
import { Await } from '../activities/await';
|
|
10
|
+
import { Cycle } from '../activities/cycle';
|
|
11
|
+
import { Hook } from '../activities/hook';
|
|
12
|
+
import { Signal } from '../activities/signal';
|
|
10
13
|
import { Worker } from '../activities/worker';
|
|
11
14
|
import { Trigger } from '../activities/trigger';
|
|
12
15
|
import { CompilerService } from '../compiler';
|
|
@@ -235,9 +238,10 @@ class EngineService {
|
|
|
235
238
|
}
|
|
236
239
|
|
|
237
240
|
// ************* METADATA/MODEL METHODS *************
|
|
238
|
-
async initActivity(topic: string, data: JobData = {}, context?: JobState): Promise<
|
|
241
|
+
async initActivity(topic: string, data: JobData = {}, context?: JobState): Promise<Await|Cycle|Hook|Signal|Trigger|Worker> {
|
|
239
242
|
const [activityId, schema] = await this.getSchema(topic);
|
|
240
|
-
|
|
243
|
+
polyfill
|
|
244
|
+
const ActivityHandler = Activities[polyfill.resolveActivityType(schema.type)];
|
|
241
245
|
if (ActivityHandler) {
|
|
242
246
|
const utc = formatISODate(new Date());
|
|
243
247
|
const metadata: ActivityMetadata = {
|
|
@@ -334,7 +338,7 @@ class EngineService {
|
|
|
334
338
|
data: streamData.data,
|
|
335
339
|
};
|
|
336
340
|
if (streamData.type === StreamDataType.TIMEHOOK || streamData.type === StreamDataType.WEBHOOK || streamData.type === StreamDataType.TRANSITION) {
|
|
337
|
-
const activityHandler = await this.initActivity(`.${streamData.metadata.aid}`, context.data, context as JobState) as
|
|
341
|
+
const activityHandler = await this.initActivity(`.${streamData.metadata.aid}`, context.data, context as JobState) as Hook;
|
|
338
342
|
if (streamData.type === StreamDataType.TIMEHOOK) {
|
|
339
343
|
await activityHandler.processTimeHookEvent(streamData.metadata.jid);
|
|
340
344
|
} else if (streamData.type === StreamDataType.TRANSITION) {
|
|
@@ -5,6 +5,7 @@ import { Cache } from '../cache';
|
|
|
5
5
|
import { StoreService } from '../index';
|
|
6
6
|
import { RedisClientType, RedisMultiType } from '../../../types/ioredisclient';
|
|
7
7
|
import { ReclaimedMessageType } from '../../../types/stream';
|
|
8
|
+
import { type } from 'os';
|
|
8
9
|
|
|
9
10
|
class IORedisStoreService extends StoreService<RedisClientType, RedisMultiType> {
|
|
10
11
|
redisClient: RedisClientType;
|
|
@@ -22,6 +23,18 @@ class IORedisStoreService extends StoreService<RedisClientType, RedisMultiType>
|
|
|
22
23
|
return this.redisClient.multi();
|
|
23
24
|
}
|
|
24
25
|
|
|
26
|
+
async exec(...args: any[]): Promise<string|string[]|string[][]> {
|
|
27
|
+
const response = await this.redisClient.call.apply(this.redisClient, args as any);
|
|
28
|
+
if (typeof response === 'string') {
|
|
29
|
+
return response as string;
|
|
30
|
+
} else if (Array.isArray(response)) {
|
|
31
|
+
if (Array.isArray(response[0])) {
|
|
32
|
+
return response as string[][];
|
|
33
|
+
}
|
|
34
|
+
return response as string[];
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
25
38
|
hGetAllResult(result: any) {
|
|
26
39
|
//ioredis response signature is [null, {}] or [null, null]
|
|
27
40
|
return result[1];
|
|
@@ -50,6 +50,10 @@ class RedisStoreService extends StoreService<RedisClientType, RedisMultiType> {
|
|
|
50
50
|
return multi as unknown as RedisMultiType;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
+
async exec(...args: any[]): Promise<string|string[]|string[][]> {
|
|
54
|
+
return await this.redisClient.sendCommand(args);
|
|
55
|
+
}
|
|
56
|
+
|
|
53
57
|
async publish(keyType: KeyType.QUORUM, message: Record<string, any>, appId: string, engineId?: string): Promise<boolean> {
|
|
54
58
|
const topic = this.mintKey(keyType, { appId, engineId });
|
|
55
59
|
const status: number = await this.redisClient.publish(topic, JSON.stringify(message));
|
package/services/store/index.ts
CHANGED
|
@@ -70,6 +70,7 @@ abstract class StoreService<T, U extends AbstractRedisClient> {
|
|
|
70
70
|
|
|
71
71
|
//todo: standardize signatures and move concrete methods to this class
|
|
72
72
|
abstract getMulti(): U;
|
|
73
|
+
abstract exec(...args: any[]): Promise<string|string[]|string[][]>;
|
|
73
74
|
abstract publish(
|
|
74
75
|
keyType: KeyType.QUORUM,
|
|
75
76
|
message: Record<string, any>,
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
Context,
|
|
19
19
|
context,
|
|
20
20
|
SpanStatusCode } from '../../types/telemetry';
|
|
21
|
+
import { polyfill } from '../../modules/utils';
|
|
21
22
|
|
|
22
23
|
class TelemetryService {
|
|
23
24
|
span: Span;
|
|
@@ -253,7 +254,7 @@ class TelemetryService {
|
|
|
253
254
|
if (config.type === 'trigger') {
|
|
254
255
|
state[`${metadata.aid}/output/metadata/l1s`] = context['$self'].output.metadata.l1s;
|
|
255
256
|
state[`${metadata.aid}/output/metadata/l2s`] = context['$self'].output.metadata.l2s;
|
|
256
|
-
} else if (config.type === '
|
|
257
|
+
} else if (polyfill.resolveActivityType(config.type) === 'hook' && leg === 1) {
|
|
257
258
|
//activities run non-duplexed and only have a single leg
|
|
258
259
|
state[`${metadata.aid}/output/metadata/l1s`] = context['$self'].output.metadata.l1s;
|
|
259
260
|
state[`${metadata.aid}/output/metadata/l2s`] = context['$self'].output.metadata.l1s;
|
package/types/activity.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { MetricTypes } from "./stats";
|
|
2
2
|
import { StreamRetryPolicy } from "./stream";
|
|
3
3
|
|
|
4
|
-
type ActivityExecutionType = 'trigger' | 'await' | 'worker' | 'activity' | 'emit' | 'iterate' | 'cycle' | 'signal';
|
|
4
|
+
type ActivityExecutionType = 'trigger' | 'await' | 'worker' | 'activity' | 'emit' | 'iterate' | 'cycle' | 'signal' | 'hook';
|
|
5
5
|
|
|
6
6
|
type Consumes = Record<string, string[]>;
|
|
7
7
|
|
|
@@ -65,6 +65,10 @@ interface CycleActivity extends BaseActivity {
|
|
|
65
65
|
ancestor: string; //ancestor activity id
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
+
interface HookActivity extends BaseActivity {
|
|
69
|
+
type: 'hook';
|
|
70
|
+
}
|
|
71
|
+
|
|
68
72
|
interface SignalActivity extends BaseActivity {
|
|
69
73
|
type: 'signal'; //signal activities call hook/hookAll
|
|
70
74
|
subtype: 'one' | 'all'; //trigger: hook(One) or hookAll
|
|
@@ -80,7 +84,7 @@ interface IterateActivity extends BaseActivity {
|
|
|
80
84
|
type: 'iterate';
|
|
81
85
|
}
|
|
82
86
|
|
|
83
|
-
type ActivityType = BaseActivity | TriggerActivity | AwaitActivity | WorkerActivity | IterateActivity;
|
|
87
|
+
type ActivityType = BaseActivity | TriggerActivity | AwaitActivity | WorkerActivity | IterateActivity | HookActivity;
|
|
84
88
|
|
|
85
89
|
type ActivityData = Record<string, any>;
|
|
86
90
|
type ActivityMetadata = {
|
|
@@ -124,6 +128,7 @@ export {
|
|
|
124
128
|
TriggerActivityStats,
|
|
125
129
|
AwaitActivity,
|
|
126
130
|
CycleActivity,
|
|
131
|
+
HookActivity,
|
|
127
132
|
SignalActivity,
|
|
128
133
|
BaseActivity,
|
|
129
134
|
IterateActivity,
|
package/types/durable.ts
CHANGED
|
@@ -7,6 +7,13 @@ type WorkflowConfig = {
|
|
|
7
7
|
initialInterval?: string; //default 1s
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
+
type WorkflowSearchOptions = {
|
|
11
|
+
index?: string; //FT index name (myapp:myindex)
|
|
12
|
+
prefix?: string[]; //FT prefixes (['myapp:myindex:prefix1', 'myapp:myindex:prefix2'])
|
|
13
|
+
schema?: Record<string, {type: 'TEXT' | 'NUMERIC' | 'TAG', sortable: boolean}>;
|
|
14
|
+
data?: Record<string, string>;
|
|
15
|
+
}
|
|
16
|
+
|
|
10
17
|
type WorkflowOptions = {
|
|
11
18
|
taskQueue: string;
|
|
12
19
|
args: any[]; //input arguments to pass in
|
|
@@ -15,6 +22,7 @@ type WorkflowOptions = {
|
|
|
15
22
|
parentWorkflowId?: string; //system reserved; the id of the parent; if present the flow will not self-clean until the parent that spawned it self-cleans
|
|
16
23
|
workflowTrace?: string;
|
|
17
24
|
workflowSpan?: string;
|
|
25
|
+
search?: WorkflowSearchOptions
|
|
18
26
|
config?: WorkflowConfig;
|
|
19
27
|
}
|
|
20
28
|
|
|
@@ -59,6 +67,7 @@ type WorkerConfig = {
|
|
|
59
67
|
taskQueue: string; //`subscribes` in the YAML (e.g, 'hello-world')
|
|
60
68
|
workflow: Function; //target function to run
|
|
61
69
|
options?: WorkerOptions;
|
|
70
|
+
search?: WorkflowSearchOptions;
|
|
62
71
|
}
|
|
63
72
|
|
|
64
73
|
type WorkerOptions = {
|
|
@@ -101,6 +110,7 @@ export {
|
|
|
101
110
|
WorkerConfig,
|
|
102
111
|
WorkflowConfig,
|
|
103
112
|
WorkerOptions,
|
|
113
|
+
WorkflowSearchOptions,
|
|
104
114
|
WorkflowDataType,
|
|
105
115
|
WorkflowOptions,
|
|
106
116
|
};
|
package/types/hook.ts
CHANGED
package/types/index.ts
CHANGED
|
@@ -10,6 +10,7 @@ export {
|
|
|
10
10
|
AwaitActivity,
|
|
11
11
|
BaseActivity,
|
|
12
12
|
CycleActivity,
|
|
13
|
+
HookActivity,
|
|
13
14
|
WorkerActivity,
|
|
14
15
|
IterateActivity,
|
|
15
16
|
SignalActivity,
|
|
@@ -39,6 +40,7 @@ export {
|
|
|
39
40
|
WorkflowConfig,
|
|
40
41
|
WorkerConfig,
|
|
41
42
|
WorkerOptions,
|
|
43
|
+
WorkflowSearchOptions,
|
|
42
44
|
WorkflowDataType,
|
|
43
45
|
WorkflowOptions,
|
|
44
46
|
}from './durable'
|