@hotmeshio/hotmesh 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (263) hide show
  1. package/LICENSE +214 -0
  2. package/README.md +241 -0
  3. package/build/index.d.ts +4 -0
  4. package/build/index.js +7 -0
  5. package/build/modules/errors.d.ts +28 -0
  6. package/build/modules/errors.js +50 -0
  7. package/build/modules/key.d.ts +75 -0
  8. package/build/modules/key.js +116 -0
  9. package/build/modules/utils.d.ts +34 -0
  10. package/build/modules/utils.js +173 -0
  11. package/build/package.json +73 -0
  12. package/build/services/activities/activity.d.ts +59 -0
  13. package/build/services/activities/activity.js +396 -0
  14. package/build/services/activities/await.d.ts +16 -0
  15. package/build/services/activities/await.js +143 -0
  16. package/build/services/activities/emit.d.ts +9 -0
  17. package/build/services/activities/emit.js +13 -0
  18. package/build/services/activities/index.d.ts +15 -0
  19. package/build/services/activities/index.js +16 -0
  20. package/build/services/activities/iterate.d.ts +9 -0
  21. package/build/services/activities/iterate.js +13 -0
  22. package/build/services/activities/trigger.d.ts +22 -0
  23. package/build/services/activities/trigger.js +161 -0
  24. package/build/services/activities/worker.d.ts +17 -0
  25. package/build/services/activities/worker.js +164 -0
  26. package/build/services/collator/index.d.ts +54 -0
  27. package/build/services/collator/index.js +171 -0
  28. package/build/services/compiler/deployer.d.ts +35 -0
  29. package/build/services/compiler/deployer.js +412 -0
  30. package/build/services/compiler/index.d.ts +30 -0
  31. package/build/services/compiler/index.js +111 -0
  32. package/build/services/compiler/validator.d.ts +32 -0
  33. package/build/services/compiler/validator.js +134 -0
  34. package/build/services/connector/clients/ioredis.d.ts +13 -0
  35. package/build/services/connector/clients/ioredis.js +50 -0
  36. package/build/services/connector/clients/redis.d.ts +13 -0
  37. package/build/services/connector/clients/redis.js +62 -0
  38. package/build/services/connector/index.d.ts +5 -0
  39. package/build/services/connector/index.js +31 -0
  40. package/build/services/dimension/index.d.ts +29 -0
  41. package/build/services/dimension/index.js +35 -0
  42. package/build/services/durable/asyncLocalStorage.d.ts +3 -0
  43. package/build/services/durable/asyncLocalStorage.js +5 -0
  44. package/build/services/durable/client.d.ts +15 -0
  45. package/build/services/durable/client.js +108 -0
  46. package/build/services/durable/connection.d.ts +4 -0
  47. package/build/services/durable/connection.js +51 -0
  48. package/build/services/durable/factory.d.ts +3 -0
  49. package/build/services/durable/factory.js +123 -0
  50. package/build/services/durable/handle.d.ts +8 -0
  51. package/build/services/durable/handle.js +38 -0
  52. package/build/services/durable/index.d.ts +57 -0
  53. package/build/services/durable/index.js +58 -0
  54. package/build/services/durable/native.d.ts +4 -0
  55. package/build/services/durable/native.js +47 -0
  56. package/build/services/durable/worker.d.ts +36 -0
  57. package/build/services/durable/worker.js +266 -0
  58. package/build/services/durable/workflow.d.ts +6 -0
  59. package/build/services/durable/workflow.js +135 -0
  60. package/build/services/engine/index.d.ts +82 -0
  61. package/build/services/engine/index.js +511 -0
  62. package/build/services/hotmesh/index.d.ts +45 -0
  63. package/build/services/hotmesh/index.js +134 -0
  64. package/build/services/logger/index.d.ts +17 -0
  65. package/build/services/logger/index.js +73 -0
  66. package/build/services/mapper/index.d.ts +24 -0
  67. package/build/services/mapper/index.js +72 -0
  68. package/build/services/pipe/functions/array.d.ts +24 -0
  69. package/build/services/pipe/functions/array.js +69 -0
  70. package/build/services/pipe/functions/bitwise.d.ts +9 -0
  71. package/build/services/pipe/functions/bitwise.js +24 -0
  72. package/build/services/pipe/functions/conditional.d.ts +10 -0
  73. package/build/services/pipe/functions/conditional.js +27 -0
  74. package/build/services/pipe/functions/date.d.ts +57 -0
  75. package/build/services/pipe/functions/date.js +167 -0
  76. package/build/services/pipe/functions/index.d.ts +25 -0
  77. package/build/services/pipe/functions/index.js +26 -0
  78. package/build/services/pipe/functions/json.d.ts +5 -0
  79. package/build/services/pipe/functions/json.js +12 -0
  80. package/build/services/pipe/functions/math.d.ts +38 -0
  81. package/build/services/pipe/functions/math.js +111 -0
  82. package/build/services/pipe/functions/number.d.ts +25 -0
  83. package/build/services/pipe/functions/number.js +133 -0
  84. package/build/services/pipe/functions/object.d.ts +22 -0
  85. package/build/services/pipe/functions/object.js +63 -0
  86. package/build/services/pipe/functions/string.d.ts +23 -0
  87. package/build/services/pipe/functions/string.js +69 -0
  88. package/build/services/pipe/functions/symbol.d.ts +12 -0
  89. package/build/services/pipe/functions/symbol.js +33 -0
  90. package/build/services/pipe/functions/unary.d.ts +7 -0
  91. package/build/services/pipe/functions/unary.js +18 -0
  92. package/build/services/pipe/index.d.ts +30 -0
  93. package/build/services/pipe/index.js +128 -0
  94. package/build/services/quorum/index.d.ts +34 -0
  95. package/build/services/quorum/index.js +147 -0
  96. package/build/services/reporter/index.d.ts +47 -0
  97. package/build/services/reporter/index.js +330 -0
  98. package/build/services/serializer/index.d.ts +36 -0
  99. package/build/services/serializer/index.js +222 -0
  100. package/build/services/signaler/store.d.ts +15 -0
  101. package/build/services/signaler/store.js +53 -0
  102. package/build/services/signaler/stream.d.ts +43 -0
  103. package/build/services/signaler/stream.js +317 -0
  104. package/build/services/store/cache.d.ts +66 -0
  105. package/build/services/store/cache.js +127 -0
  106. package/build/services/store/clients/ioredis.d.ts +27 -0
  107. package/build/services/store/clients/ioredis.js +96 -0
  108. package/build/services/store/clients/redis.d.ts +29 -0
  109. package/build/services/store/clients/redis.js +143 -0
  110. package/build/services/store/index.d.ts +88 -0
  111. package/build/services/store/index.js +657 -0
  112. package/build/services/stream/clients/ioredis.d.ts +23 -0
  113. package/build/services/stream/clients/ioredis.js +115 -0
  114. package/build/services/stream/clients/redis.d.ts +23 -0
  115. package/build/services/stream/clients/redis.js +119 -0
  116. package/build/services/stream/index.d.ts +21 -0
  117. package/build/services/stream/index.js +9 -0
  118. package/build/services/sub/clients/ioredis.d.ts +20 -0
  119. package/build/services/sub/clients/ioredis.js +72 -0
  120. package/build/services/sub/clients/redis.d.ts +20 -0
  121. package/build/services/sub/clients/redis.js +63 -0
  122. package/build/services/sub/index.d.ts +18 -0
  123. package/build/services/sub/index.js +9 -0
  124. package/build/services/task/index.d.ts +18 -0
  125. package/build/services/task/index.js +73 -0
  126. package/build/services/telemetry/index.d.ts +49 -0
  127. package/build/services/telemetry/index.js +223 -0
  128. package/build/services/worker/index.d.ts +30 -0
  129. package/build/services/worker/index.js +105 -0
  130. package/build/types/activity.d.ts +86 -0
  131. package/build/types/activity.js +2 -0
  132. package/build/types/app.d.ts +16 -0
  133. package/build/types/app.js +2 -0
  134. package/build/types/async.d.ts +5 -0
  135. package/build/types/async.js +2 -0
  136. package/build/types/cache.d.ts +1 -0
  137. package/build/types/cache.js +2 -0
  138. package/build/types/collator.d.ts +8 -0
  139. package/build/types/collator.js +11 -0
  140. package/build/types/durable.d.ts +59 -0
  141. package/build/types/durable.js +2 -0
  142. package/build/types/hook.d.ts +31 -0
  143. package/build/types/hook.js +9 -0
  144. package/build/types/hotmesh.d.ts +82 -0
  145. package/build/types/hotmesh.js +2 -0
  146. package/build/types/index.d.ts +20 -0
  147. package/build/types/index.js +21 -0
  148. package/build/types/ioredisclient.d.ts +5 -0
  149. package/build/types/ioredisclient.js +5 -0
  150. package/build/types/job.d.ts +50 -0
  151. package/build/types/job.js +2 -0
  152. package/build/types/logger.d.ts +6 -0
  153. package/build/types/logger.js +2 -0
  154. package/build/types/map.d.ts +4 -0
  155. package/build/types/map.js +2 -0
  156. package/build/types/pipe.d.ts +4 -0
  157. package/build/types/pipe.js +2 -0
  158. package/build/types/quorum.d.ts +46 -0
  159. package/build/types/quorum.js +2 -0
  160. package/build/types/redis.d.ts +8 -0
  161. package/build/types/redis.js +2 -0
  162. package/build/types/redisclient.d.ts +25 -0
  163. package/build/types/redisclient.js +2 -0
  164. package/build/types/serializer.d.ts +33 -0
  165. package/build/types/serializer.js +2 -0
  166. package/build/types/stats.d.ts +83 -0
  167. package/build/types/stats.js +2 -0
  168. package/build/types/stream.d.ts +67 -0
  169. package/build/types/stream.js +25 -0
  170. package/build/types/telemetry.d.ts +1 -0
  171. package/build/types/telemetry.js +11 -0
  172. package/build/types/transition.d.ts +17 -0
  173. package/build/types/transition.js +2 -0
  174. package/index.ts +5 -0
  175. package/modules/errors.ts +55 -0
  176. package/modules/key.ts +129 -0
  177. package/modules/utils.ts +170 -0
  178. package/package.json +73 -0
  179. package/services/activities/activity.ts +473 -0
  180. package/services/activities/await.ts +172 -0
  181. package/services/activities/emit.ts +25 -0
  182. package/services/activities/index.ts +15 -0
  183. package/services/activities/iterate.ts +26 -0
  184. package/services/activities/trigger.ts +196 -0
  185. package/services/activities/worker.ts +190 -0
  186. package/services/collator/README.md +102 -0
  187. package/services/collator/index.ts +182 -0
  188. package/services/compiler/deployer.ts +432 -0
  189. package/services/compiler/index.ts +98 -0
  190. package/services/compiler/validator.ts +154 -0
  191. package/services/connector/clients/ioredis.ts +57 -0
  192. package/services/connector/clients/redis.ts +72 -0
  193. package/services/connector/index.ts +44 -0
  194. package/services/dimension/README.md +73 -0
  195. package/services/dimension/index.ts +39 -0
  196. package/services/durable/asyncLocalStorage.ts +3 -0
  197. package/services/durable/client.ts +116 -0
  198. package/services/durable/connection.ts +50 -0
  199. package/services/durable/factory.ts +124 -0
  200. package/services/durable/handle.ts +43 -0
  201. package/services/durable/index.ts +60 -0
  202. package/services/durable/native.ts +46 -0
  203. package/services/durable/worker.ts +254 -0
  204. package/services/durable/workflow.ts +136 -0
  205. package/services/engine/index.ts +615 -0
  206. package/services/hotmesh/index.ts +182 -0
  207. package/services/logger/index.ts +79 -0
  208. package/services/mapper/index.ts +84 -0
  209. package/services/pipe/functions/array.ts +87 -0
  210. package/services/pipe/functions/bitwise.ts +27 -0
  211. package/services/pipe/functions/conditional.ts +31 -0
  212. package/services/pipe/functions/date.ts +214 -0
  213. package/services/pipe/functions/index.ts +25 -0
  214. package/services/pipe/functions/json.ts +11 -0
  215. package/services/pipe/functions/math.ts +143 -0
  216. package/services/pipe/functions/number.ts +150 -0
  217. package/services/pipe/functions/object.ts +79 -0
  218. package/services/pipe/functions/string.ts +86 -0
  219. package/services/pipe/functions/symbol.ts +39 -0
  220. package/services/pipe/functions/unary.ts +19 -0
  221. package/services/pipe/index.ts +138 -0
  222. package/services/quorum/index.ts +200 -0
  223. package/services/reporter/index.ts +379 -0
  224. package/services/serializer/README.md +10 -0
  225. package/services/serializer/index.ts +243 -0
  226. package/services/signaler/store.ts +61 -0
  227. package/services/signaler/stream.ts +354 -0
  228. package/services/store/cache.ts +172 -0
  229. package/services/store/clients/ioredis.ts +123 -0
  230. package/services/store/clients/redis.ts +169 -0
  231. package/services/store/index.ts +757 -0
  232. package/services/stream/clients/ioredis.ts +148 -0
  233. package/services/stream/clients/redis.ts +144 -0
  234. package/services/stream/index.ts +57 -0
  235. package/services/sub/clients/ioredis.ts +83 -0
  236. package/services/sub/clients/redis.ts +74 -0
  237. package/services/sub/index.ts +25 -0
  238. package/services/task/index.ts +86 -0
  239. package/services/telemetry/index.ts +267 -0
  240. package/services/worker/index.ts +165 -0
  241. package/types/activity.ts +115 -0
  242. package/types/app.ts +20 -0
  243. package/types/async.ts +7 -0
  244. package/types/cache.ts +1 -0
  245. package/types/collator.ts +9 -0
  246. package/types/durable.ts +81 -0
  247. package/types/hook.ts +32 -0
  248. package/types/hotmesh.ts +102 -0
  249. package/types/index.ts +138 -0
  250. package/types/ioredisclient.ts +10 -0
  251. package/types/job.ts +59 -0
  252. package/types/logger.ts +6 -0
  253. package/types/map.ts +5 -0
  254. package/types/ms.d.ts +7 -0
  255. package/types/pipe.ts +7 -0
  256. package/types/quorum.ts +59 -0
  257. package/types/redis.ts +27 -0
  258. package/types/redisclient.ts +29 -0
  259. package/types/serializer.ts +38 -0
  260. package/types/stats.ts +100 -0
  261. package/types/stream.ts +75 -0
  262. package/types/telemetry.ts +15 -0
  263. package/types/transition.ts +20 -0
@@ -0,0 +1,46 @@
1
+ import { Connection, ConnectionConfig } from "../../types/durable";
2
+
3
+ /*
4
+
5
+ Here is an example of how the methods in this file are used:
6
+
7
+ ./worker.ts
8
+
9
+ import { Durable: { NativeConnection, Worker } } from '@hotmeshio/hotmesh';
10
+ import Redis from 'ioredis'; //OR `import * as Redis from 'redis';`
11
+
12
+ import * as activities from './activities';
13
+
14
+ async function run() {
15
+ const connection = await NativeConnection.connect({
16
+ class: Redis,
17
+ options: {
18
+ host: 'localhost',
19
+ port: 6379,
20
+ },
21
+ });
22
+ const worker = await Worker.create({
23
+ connection,
24
+ namespace: 'default',
25
+ taskQueue: 'hello-world',
26
+ workflowsPath: require.resolve('./workflows'),
27
+ activities,
28
+ });
29
+ await worker.run();
30
+ }
31
+
32
+ run().catch((err) => {
33
+ console.error(err);
34
+ process.exit(1);
35
+ });
36
+
37
+ */
38
+
39
+ export class NativeConnectionService {
40
+ static async connect(config: ConnectionConfig): Promise<Connection> {
41
+ return {
42
+ class: config.class,
43
+ options: { ...config.options },
44
+ } as Connection;
45
+ }
46
+ }
@@ -0,0 +1,254 @@
1
+ import { asyncLocalStorage } from './asyncLocalStorage';
2
+ import { HotMeshService as HotMesh } from '../hotmesh';
3
+ import { RedisClass, RedisOptions } from '../../types/redis';
4
+ import { StreamData, StreamDataResponse, StreamStatus } from '../../types/stream';
5
+ import { ActivityDataType, Connection, Registry, WorkerConfig, WorkflowDataType } from "../../types/durable";
6
+ import { getWorkflowYAML, getActivityYAML } from './factory';
7
+
8
+ /*
9
+ Here is an example of how the methods in this file are used:
10
+
11
+ ./worker.ts
12
+
13
+ import { Durable: { NativeConnection, Worker } } from '@hotmeshio/hotmesh';
14
+ import Redis from 'ioredis'; //OR `import * as Redis from 'redis';`
15
+
16
+ import * as activities from './activities';
17
+
18
+ async function run() {
19
+ const connection = await NativeConnection.connect({
20
+ class: Redis,
21
+ options: {
22
+ host: 'localhost',
23
+ port: 6379,
24
+ },
25
+ });
26
+ const worker = await Worker.create({
27
+ connection,
28
+ namespace: 'default',
29
+ taskQueue: 'hello-world',
30
+ workflowsPath: require.resolve('./workflows'),
31
+ activities,
32
+ });
33
+ await worker.run();
34
+ }
35
+
36
+ run().catch((err) => {
37
+ console.error(err);
38
+ process.exit(1);
39
+ });
40
+ */
41
+
42
+ export class WorkerService {
43
+ static activityRegistry: Registry = {}; //user's activities
44
+ static connection: Connection;
45
+ static instances = new Map<string, HotMesh | Promise<HotMesh>>();
46
+ workflowRunner: HotMesh;
47
+
48
+ static getHotMesh = async (worflowTopic: string) => {
49
+ if (WorkerService.instances.has(worflowTopic)) {
50
+ return await WorkerService.instances.get(worflowTopic);
51
+ }
52
+ const hotMesh = HotMesh.init({
53
+ appId: worflowTopic,
54
+ engine: { redis: { ...WorkerService.connection } }
55
+ });
56
+ WorkerService.instances.set(worflowTopic, hotMesh);
57
+ await WorkerService.activateWorkflow(await hotMesh, worflowTopic, getWorkflowYAML);
58
+ return hotMesh;
59
+ }
60
+
61
+ static async activateWorkflow(hotMesh: HotMesh, topic: string, factory: Function) {
62
+ const version = '1';
63
+ const app = await hotMesh.engine.store.getApp(topic);
64
+ const appVersion = app?.version;
65
+ if(!appVersion) {
66
+ try {
67
+ await hotMesh.deploy(factory(topic, version));
68
+ await hotMesh.activate(version);
69
+ } catch (err) {
70
+ hotMesh.engine.logger.error('durable-worker-workflow-activation-error', err);
71
+ throw err;
72
+ }
73
+ }
74
+ }
75
+
76
+ /**
77
+ * The `worker` calls `registerActivities` immediately BEFORE
78
+ * dynamically importing the user's workflow module. That file
79
+ * contains a call, `proxyActivities`, which needs this info.
80
+ *
81
+ * NOTE: The `worker` and `client` both call `proxyActivities`,
82
+ * as a natural result of importing worflows.ts. However,
83
+ * because the worker imports the workflows dynamically AFTER
84
+ * the activities are loaded, there will be items in the registry,
85
+ * allowing proxyActivities to succeed.
86
+ */
87
+ static registerActivities<ACT>(activities: ACT): Registry {
88
+ Object.keys(activities).forEach(key => {
89
+ WorkerService.activityRegistry[key] = (activities as any)[key];
90
+ });
91
+ return WorkerService.activityRegistry;
92
+ }
93
+
94
+ static async create(config: WorkerConfig) {
95
+ WorkerService.connection = config.connection;
96
+ //pre-cache user activity functions
97
+ WorkerService.registerActivities<typeof config.activities>(config.activities);
98
+ //import the user's workflow file (triggers activity functions to be wrapped)
99
+ const workflow = await import(config.workflowsPath);
100
+ const workflowFunctionNames = Object.keys(workflow);
101
+ const workflowFunctionName = workflowFunctionNames[workflowFunctionNames.length - 1];
102
+ const workflowFunction = workflow[workflowFunctionName];
103
+ const baseTopic = `${config.taskQueue}-${workflowFunctionName}`;
104
+ const activityTopic = `${baseTopic}-activity`;
105
+ const workflowTopic = `${baseTopic}`;
106
+
107
+ //initialize supporting workflows
108
+ const worker = new WorkerService();
109
+
110
+ const activityRunner = await worker.initActivityWorkflow(config, activityTopic);
111
+ await WorkerService.activateWorkflow(activityRunner, activityTopic, getActivityYAML);
112
+ worker.workflowRunner = await worker.initWorkerWorkflow(config, workflowTopic, workflowFunction);
113
+ await WorkerService.activateWorkflow(worker.workflowRunner, workflowTopic, getWorkflowYAML);
114
+ return worker;
115
+ }
116
+
117
+ async run() {
118
+ if (this.workflowRunner) {
119
+ this.workflowRunner.engine.logger.info('WorkerService is running');
120
+ } else {
121
+ console.log('WorkerService is running');
122
+ }
123
+ }
124
+
125
+ async initActivityWorkflow(config: WorkerConfig, activityTopic: string): Promise<HotMesh> {
126
+ const redisConfig = {
127
+ class: config.connection.class as RedisClass,
128
+ options: config.connection.options as RedisOptions
129
+ };
130
+ const hmshInstance = await HotMesh.init({
131
+ appId: activityTopic,
132
+ engine: { redis: redisConfig },
133
+ workers: [
134
+ { topic: activityTopic,
135
+ redis: redisConfig,
136
+ callback: this.wrapActivityFunctions().bind(this)
137
+ }
138
+ ]
139
+ });
140
+ WorkerService.instances.set(activityTopic, hmshInstance);
141
+ return hmshInstance;
142
+ }
143
+
144
+ wrapActivityFunctions(): Function {
145
+ return async (data: StreamData): Promise<StreamDataResponse> => {
146
+ try {
147
+ //always run the activity function when instructed; return the response
148
+ const activityInput = data.data as unknown as ActivityDataType;
149
+ const activityName = activityInput.activityName;
150
+ const activityFunction = WorkerService.activityRegistry[activityName];
151
+ const pojoResponse = await activityFunction.apply(this, activityInput.arguments);
152
+
153
+ return {
154
+ status: StreamStatus.SUCCESS,
155
+ metadata: { ...data.metadata },
156
+ data: { response: pojoResponse }
157
+ };
158
+ } catch (err) {
159
+ console.error(err);
160
+ //todo (make retry configurable)
161
+ return {
162
+ status: StreamStatus.PENDING,
163
+ metadata: { ...data.metadata },
164
+ data: { error: err }
165
+ } as StreamDataResponse;
166
+ }
167
+ }
168
+ }
169
+
170
+ async activateActivityWorkflow(hotMesh: HotMesh, activityTopic: string) {
171
+ const version = '1';
172
+ const app = await hotMesh.engine.store.getApp(activityTopic);
173
+ const appVersion = app?.version as unknown as number;
174
+ if(isNaN(appVersion)) {
175
+ try {
176
+ await hotMesh.deploy(getActivityYAML(activityTopic, version));
177
+ await hotMesh.activate(version);
178
+ } catch (err) {
179
+ console.log('durable-worker-activity-workflow-activation-error', err);
180
+ throw err;
181
+ }
182
+ }
183
+ }
184
+
185
+ async initWorkerWorkflow(config: WorkerConfig, workflowTopic: string, workflowFunction: Function): Promise<HotMesh> {
186
+ const redisConfig = {
187
+ class: config.connection.class as RedisClass,
188
+ options: config.connection.options as RedisOptions
189
+ };
190
+ const hmshInstance = await HotMesh.init({
191
+ appId: workflowTopic,
192
+ engine: { redis: redisConfig },
193
+ workers: [
194
+ { topic: workflowTopic,
195
+ redis: redisConfig,
196
+ callback: this.wrapWorkflowFunction(workflowFunction, workflowTopic).bind(this)
197
+ }
198
+ ]
199
+ });
200
+ WorkerService.instances.set(workflowTopic, hmshInstance);
201
+ return hmshInstance;
202
+ }
203
+
204
+ static Context = {
205
+ info: () => {
206
+ return {
207
+ workflowId: '',
208
+ workflowTopic: '',
209
+ }
210
+ },
211
+ };
212
+
213
+ wrapWorkflowFunction(workflowFunction: Function, workflowTopic: string): Function {
214
+ return async (data: StreamData): Promise<StreamDataResponse> => {
215
+ try {
216
+ //incoming data payload has arguments and workflowId
217
+ const workflowInput = data.data as unknown as WorkflowDataType;
218
+ const context = new Map();
219
+ const counter = { counter: 0 };
220
+ context.set('counter', counter);
221
+ context.set('workflowId', workflowInput.workflowId);
222
+ context.set('workflowTopic', workflowTopic);
223
+ context.set('workflowName', workflowTopic.split('-').pop());
224
+ context.set('workflowTrace', data.metadata.trc);
225
+ context.set('workflowSpan', data.metadata.spn);
226
+ const workflowResponse = await asyncLocalStorage.run(context, async () => {
227
+ return await workflowFunction.apply(this, workflowInput.arguments);
228
+ });
229
+
230
+ return {
231
+ code: 200,
232
+ status: StreamStatus.SUCCESS,
233
+ metadata: { ...data.metadata },
234
+ data: { response: workflowResponse }
235
+ };
236
+ } catch (err) {
237
+ //todo: (retryable error types)
238
+ return {
239
+ code: 500,
240
+ status: StreamStatus.PENDING,
241
+ metadata: { ...data.metadata },
242
+ data: { error: err }
243
+ } as StreamDataResponse;
244
+ }
245
+ }
246
+ }
247
+
248
+ static async shutdown(): Promise<void> {
249
+ for (const [key, value] of WorkerService.instances) {
250
+ const hotMesh = await value;
251
+ await hotMesh.stop();
252
+ }
253
+ }
254
+ }
@@ -0,0 +1,136 @@
1
+ import ms from 'ms';
2
+
3
+ import { asyncLocalStorage } from './asyncLocalStorage';
4
+ import { WorkerService } from './worker';
5
+ import { ClientService as Client } from './client';
6
+ import { ConnectionService as Connection } from './connection';
7
+ import { ActivityConfig, ProxyType, WorkflowOptions } from "../../types/durable";
8
+ import { JobOutput, JobState } from '../../types';
9
+
10
+ /*
11
+ `proxyActivities` returns a wrapped instance of the
12
+ target activity, so that when the workflow calls a
13
+ proxied activity, it is actually calling the proxy
14
+ function, which in turn calls the activity function.
15
+
16
+ `proxyActivities` must be called AFTER the activities
17
+ have been registered in order to work properly.
18
+ If the activities are not already registered,
19
+ `proxyActivities` will throw an error. This is OK.
20
+
21
+ The `client` (client.ts) is equivalent to the
22
+ HotMesh `engine`. The jobs it creates will be
23
+ put in the taskQueue. When the `worker` (worker.ts)
24
+ is eventually initialized (if it happens to be inited later),
25
+ it will see the items in the queue and process them. If it happens
26
+ to already be inited, the jobs will immediately be dequeued and
27
+ processed. In either case, the jobs will be processed.
28
+
29
+ Here is an example of how the methods in this file are used:
30
+
31
+ ./workflows.ts
32
+
33
+ import { Durable } from '@hotmeshio/hotmesh';
34
+ import type * as activities from './activities';
35
+ const { greet } = Durable.workflow.proxyActivities<typeof activities>({
36
+ startToCloseTimeout: '1 minute',
37
+ retryPolicy: {
38
+ initialInterval: '5 seconds', // Initial delay between retries
39
+ maximumAttempts: 3, // Max number of retry attempts
40
+ backoffCoefficient: 2.0, // Backoff factor for delay between retries: delay = initialInterval * (backoffCoefficient ^ retry_attempt)
41
+ maximumInterval: '30 seconds', // Max delay between retries
42
+ },
43
+ });
44
+
45
+ export async function example(name: string): Promise<string> {
46
+ return await greet(name);
47
+ }
48
+ */
49
+
50
+ export class WorkflowService {
51
+ static async executeChild<T>(options: WorkflowOptions): Promise<T> {
52
+ const store = asyncLocalStorage.getStore();
53
+ if (!store) {
54
+ throw new Error('durable-store-not-found');
55
+ }
56
+ const workflowId = store.get('workflowId');
57
+ const workflowTrace = store.get('workflowTrace');
58
+ const workflowSpan = store.get('workflowSpan');
59
+
60
+ const client = new Client({
61
+ connection: await Connection.connect(WorkerService.connection),
62
+ });
63
+ //todo: should I allow-cross/app callback (pj:'@DURABLE@hello-world@<pjid>'/pa: <paid>/pd: <pdad>)
64
+ const handle = await client.workflow.start({
65
+ ...options,
66
+ workflowId: `${workflowId}${options.workflowId}`, //concat
67
+ workflowTrace,
68
+ workflowSpan,
69
+ });
70
+ const result = await handle.result();
71
+ return result as T;
72
+ }
73
+
74
+ static proxyActivities<ACT>(options?: ActivityConfig): ProxyType<ACT> {
75
+ const proxy: any = {};
76
+ const keys = Object.keys(WorkerService.activityRegistry);
77
+ if (keys.length) {
78
+ keys.forEach((key: string) => {
79
+ const activityFunction = WorkerService.activityRegistry[key];
80
+ proxy[key] = WorkflowService.wrapActivity<typeof activityFunction>(key, options);
81
+ });
82
+ }
83
+ return proxy;
84
+ }
85
+
86
+ static wrapActivity<T>(activityName: string, options?: ActivityConfig): T {
87
+ return async function() {
88
+ const store = asyncLocalStorage.getStore();
89
+ if (!store) {
90
+ throw new Error('durable-store-not-found');
91
+ }
92
+ const COUNTER = store.get('counter');
93
+ //increment by state (not value) to avoid race conditions
94
+ const execIndex = COUNTER.counter = COUNTER.counter + 1;
95
+ const workflowId = store.get('workflowId');
96
+ const workflowTopic = store.get('workflowTopic');
97
+ const trc = store.get('workflowTrace');
98
+ const spn = store.get('workflowSpan');
99
+ const activityTopic = `${workflowTopic}-activity`;
100
+ const activityJobId = `${workflowId}-${activityName}-${execIndex}`;
101
+
102
+ let activityState: JobOutput
103
+ try {
104
+ const hmshInstance = await WorkerService.getHotMesh(activityTopic);
105
+ activityState = await hmshInstance.getState(activityTopic, activityJobId);
106
+ if (activityState.metadata.js == 1) {
107
+ //return immediately
108
+ return activityState.data?.response as T;
109
+ }
110
+ //one time subscription
111
+ return await new Promise((resolve, reject) => {
112
+ hmshInstance.sub(activityTopic, async (topic, message) => {
113
+ const response = message.data?.response;
114
+ hmshInstance.unsub(activityTopic);
115
+ // Resolve the Promise when the callback is triggered with a message
116
+ resolve(response);
117
+ });
118
+ });
119
+ } catch (e) {
120
+ //expected; thrown by `getState` when the job cannot be found
121
+ const duration = ms(options?.startToCloseTimeout || '1 minute');
122
+ const payload = {
123
+ arguments: Array.from(arguments),
124
+ workflowId: activityJobId,
125
+ workflowTopic,
126
+ activityName,
127
+ };
128
+ //start the job
129
+ const hmshInstance = await WorkerService.getHotMesh(activityTopic);
130
+ const context = { metadata: { trc, spn }, data: {}};
131
+ const jobOutput = await hmshInstance.pubsub(activityTopic, payload, context as JobState, duration);
132
+ return jobOutput.data.response as T;
133
+ }
134
+ } as T;
135
+ }
136
+ }