@hotmeshio/hotmesh 0.0.55 → 0.0.56

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 (181) hide show
  1. package/build/modules/enums.js +1 -10
  2. package/build/modules/key.d.ts +0 -38
  3. package/build/modules/key.js +4 -46
  4. package/build/modules/utils.d.ts +0 -8
  5. package/build/modules/utils.js +0 -14
  6. package/build/package.json +11 -4
  7. package/build/services/activities/activity.d.ts +0 -28
  8. package/build/services/activities/activity.js +1 -46
  9. package/build/services/activities/await.js +0 -4
  10. package/build/services/activities/cycle.d.ts +0 -7
  11. package/build/services/activities/cycle.js +1 -16
  12. package/build/services/activities/hook.d.ts +0 -6
  13. package/build/services/activities/hook.js +2 -12
  14. package/build/services/activities/interrupt.js +0 -8
  15. package/build/services/activities/signal.d.ts +0 -6
  16. package/build/services/activities/signal.js +0 -15
  17. package/build/services/activities/trigger.d.ts +0 -4
  18. package/build/services/activities/trigger.js +1 -7
  19. package/build/services/activities/worker.js +0 -4
  20. package/build/services/collator/index.d.ts +0 -70
  21. package/build/services/collator/index.js +1 -91
  22. package/build/services/compiler/deployer.js +6 -38
  23. package/build/services/compiler/index.d.ts +0 -15
  24. package/build/services/compiler/index.js +0 -20
  25. package/build/services/compiler/validator.d.ts +0 -3
  26. package/build/services/compiler/validator.js +0 -25
  27. package/build/services/connector/clients/ioredis.d.ts +2 -2
  28. package/build/services/connector/clients/ioredis.js +0 -2
  29. package/build/services/connector/clients/redis.d.ts +4 -4
  30. package/build/services/connector/clients/redis.js +1 -3
  31. package/build/services/connector/index.d.ts +1 -1
  32. package/build/services/connector/index.js +0 -2
  33. package/build/services/durable/client.d.ts +1 -26
  34. package/build/services/durable/client.js +0 -56
  35. package/build/services/durable/exporter.d.ts +0 -22
  36. package/build/services/durable/exporter.js +1 -30
  37. package/build/services/durable/handle.d.ts +0 -36
  38. package/build/services/durable/handle.js +0 -46
  39. package/build/services/durable/index.d.ts +0 -4
  40. package/build/services/durable/index.js +0 -4
  41. package/build/services/durable/schemas/factory.d.ts +0 -29
  42. package/build/services/durable/schemas/factory.js +0 -29
  43. package/build/services/durable/search.d.ts +1 -36
  44. package/build/services/durable/search.js +57 -56
  45. package/build/services/durable/worker.js +2 -22
  46. package/build/services/durable/workflow.d.ts +0 -114
  47. package/build/services/durable/workflow.js +1 -141
  48. package/build/services/engine/index.d.ts +1 -6
  49. package/build/services/engine/index.js +1 -43
  50. package/build/services/exporter/index.d.ts +0 -27
  51. package/build/services/exporter/index.js +0 -33
  52. package/build/services/hotmesh/index.d.ts +2 -2
  53. package/build/services/hotmesh/index.js +1 -9
  54. package/build/services/logger/index.js +0 -2
  55. package/build/services/mapper/index.d.ts +0 -14
  56. package/build/services/mapper/index.js +0 -14
  57. package/build/services/pipe/functions/date.d.ts +0 -7
  58. package/build/services/pipe/functions/date.js +0 -7
  59. package/build/services/pipe/functions/math.js +0 -2
  60. package/build/services/pipe/index.d.ts +0 -15
  61. package/build/services/pipe/index.js +2 -23
  62. package/build/services/quorum/index.d.ts +0 -7
  63. package/build/services/quorum/index.js +0 -21
  64. package/build/services/reporter/index.d.ts +0 -5
  65. package/build/services/reporter/index.js +0 -9
  66. package/build/services/router/index.d.ts +0 -9
  67. package/build/services/router/index.js +2 -38
  68. package/build/services/serializer/index.js +7 -26
  69. package/build/services/store/cache.d.ts +0 -18
  70. package/build/services/store/cache.js +0 -18
  71. package/build/services/store/clients/ioredis.d.ts +1 -1
  72. package/build/services/store/clients/ioredis.js +0 -1
  73. package/build/services/store/clients/redis.d.ts +1 -1
  74. package/build/services/store/index.d.ts +0 -55
  75. package/build/services/store/index.js +5 -81
  76. package/build/services/stream/clients/ioredis.d.ts +1 -1
  77. package/build/services/stream/clients/ioredis.js +1 -4
  78. package/build/services/stream/clients/redis.d.ts +1 -1
  79. package/build/services/sub/clients/ioredis.d.ts +1 -1
  80. package/build/services/sub/clients/redis.d.ts +1 -1
  81. package/build/services/task/index.d.ts +0 -9
  82. package/build/services/task/index.js +0 -31
  83. package/build/services/telemetry/index.d.ts +0 -7
  84. package/build/services/telemetry/index.js +1 -13
  85. package/build/services/worker/index.d.ts +0 -4
  86. package/build/services/worker/index.js +2 -6
  87. package/build/types/activity.d.ts +0 -81
  88. package/build/types/durable.d.ts +25 -177
  89. package/build/types/exporter.d.ts +0 -13
  90. package/build/types/hotmesh.d.ts +4 -16
  91. package/build/types/hotmesh.js +0 -3
  92. package/build/types/index.d.ts +4 -6
  93. package/build/types/index.js +4 -3
  94. package/build/types/job.d.ts +1 -86
  95. package/build/types/pipe.d.ts +0 -65
  96. package/build/types/quorum.d.ts +15 -10
  97. package/build/types/redis.d.ts +225 -7
  98. package/build/types/redis.js +9 -0
  99. package/build/types/stream.d.ts +0 -58
  100. package/build/types/stream.js +0 -4
  101. package/package.json +11 -4
  102. package/types/durable.ts +121 -3
  103. package/types/hotmesh.ts +3 -6
  104. package/types/index.ts +23 -10
  105. package/types/job.ts +1 -1
  106. package/types/quorum.ts +22 -0
  107. package/types/redis.ts +267 -18
  108. package/build/types/ioredisclient.d.ts +0 -5
  109. package/build/types/ioredisclient.js +0 -5
  110. package/build/types/redisclient.d.ts +0 -26
  111. package/build/types/redisclient.js +0 -2
  112. package/modules/enums.ts +0 -62
  113. package/modules/errors.ts +0 -280
  114. package/modules/key.ts +0 -101
  115. package/modules/storage.ts +0 -3
  116. package/modules/utils.ts +0 -242
  117. package/services/activities/activity.ts +0 -589
  118. package/services/activities/await.ts +0 -113
  119. package/services/activities/cycle.ts +0 -115
  120. package/services/activities/hook.ts +0 -197
  121. package/services/activities/index.ts +0 -19
  122. package/services/activities/interrupt.ts +0 -172
  123. package/services/activities/signal.ts +0 -148
  124. package/services/activities/trigger.ts +0 -295
  125. package/services/activities/worker.ts +0 -107
  126. package/services/collator/README.md +0 -102
  127. package/services/collator/index.ts +0 -291
  128. package/services/compiler/deployer.ts +0 -504
  129. package/services/compiler/index.ts +0 -98
  130. package/services/compiler/validator.ts +0 -158
  131. package/services/connector/clients/ioredis.ts +0 -57
  132. package/services/connector/clients/redis.ts +0 -72
  133. package/services/connector/index.ts +0 -42
  134. package/services/durable/client.ts +0 -266
  135. package/services/durable/connection.ts +0 -10
  136. package/services/durable/exporter.ts +0 -232
  137. package/services/durable/handle.ts +0 -160
  138. package/services/durable/index.ts +0 -27
  139. package/services/durable/schemas/factory.ts +0 -2358
  140. package/services/durable/search.ts +0 -196
  141. package/services/durable/worker.ts +0 -401
  142. package/services/durable/workflow.ts +0 -557
  143. package/services/engine/index.ts +0 -761
  144. package/services/exporter/index.ts +0 -146
  145. package/services/hotmesh/index.ts +0 -237
  146. package/services/logger/index.ts +0 -79
  147. package/services/mapper/index.ts +0 -89
  148. package/services/pipe/functions/array.ts +0 -78
  149. package/services/pipe/functions/bitwise.ts +0 -27
  150. package/services/pipe/functions/conditional.ts +0 -35
  151. package/services/pipe/functions/date.ts +0 -220
  152. package/services/pipe/functions/index.ts +0 -27
  153. package/services/pipe/functions/json.ts +0 -11
  154. package/services/pipe/functions/logical.ts +0 -11
  155. package/services/pipe/functions/math.ts +0 -217
  156. package/services/pipe/functions/number.ts +0 -75
  157. package/services/pipe/functions/object.ts +0 -98
  158. package/services/pipe/functions/string.ts +0 -86
  159. package/services/pipe/functions/symbol.ts +0 -39
  160. package/services/pipe/functions/unary.ts +0 -19
  161. package/services/pipe/index.ts +0 -216
  162. package/services/quorum/index.ts +0 -319
  163. package/services/reporter/index.ts +0 -387
  164. package/services/router/index.ts +0 -426
  165. package/services/serializer/README.md +0 -10
  166. package/services/serializer/index.ts +0 -285
  167. package/services/store/cache.ts +0 -172
  168. package/services/store/clients/ioredis.ts +0 -145
  169. package/services/store/clients/redis.ts +0 -191
  170. package/services/store/index.ts +0 -1091
  171. package/services/stream/clients/ioredis.ts +0 -157
  172. package/services/stream/clients/redis.ts +0 -158
  173. package/services/stream/index.ts +0 -58
  174. package/services/sub/clients/ioredis.ts +0 -83
  175. package/services/sub/clients/redis.ts +0 -74
  176. package/services/sub/index.ts +0 -25
  177. package/services/task/index.ts +0 -250
  178. package/services/telemetry/index.ts +0 -273
  179. package/services/worker/index.ts +0 -248
  180. package/types/ioredisclient.ts +0 -10
  181. package/types/redisclient.ts +0 -30
@@ -1,158 +0,0 @@
1
- import { Pipe } from "../pipe";
2
- import { StoreService } from '../store';
3
- import { MappingStatements } from "../../types/map";
4
- import { HotMeshManifest } from "../../types/hotmesh";
5
- import { RedisClient, RedisMulti } from "../../types/redis";
6
-
7
- class Validator {
8
- manifest: HotMeshManifest | null = null;
9
- activityIds: string[] = [];
10
- mappingStatements: MappingStatements = {};
11
- store: StoreService<RedisClient, RedisMulti> | null = null;
12
-
13
- static SYS_VARS = ['$app', '$self', '$graph', '$job'];
14
- static CONTEXT_VARS = ['{$input}', '{$output}', '{$item}', '{$key}', '{$index}'];
15
-
16
- constructor(manifest: HotMeshManifest) {
17
- this.manifest = manifest;
18
- }
19
-
20
- /**
21
- * validate the manifest file
22
- */
23
- async validate(store: StoreService<RedisClient, RedisMulti>) {
24
- this.store = store;
25
- this.getMappingStatements();
26
- this.validateActivityIds();
27
- this.validateReferencedActivityIds();
28
- this.validateMappingStatements();
29
- this.validateTransitions();
30
- this.validateTransitionConditions();
31
- this.validateStats();
32
- this.validateSchemas();
33
- this.validateUniqueHandledTopics();
34
- this.validateGraphPublishSubscribe();
35
- this.validateHooks();
36
- this.validateConditionalStatements();
37
- }
38
-
39
- // 1.1) Validate the manifest file activity ids are unique (no duplicates)
40
- validateActivityIds() {
41
- const activityIdsSet: Set<string> = new Set();
42
- this.manifest.app.graphs.forEach((graph) => {
43
- const ids = Object.keys(graph.activities);
44
- // Check for duplicates and add ids to the set
45
- ids.forEach((id) => {
46
- if (activityIdsSet.has(id)) {
47
- throw new Error(`Duplicate activity id found: ${id}`);
48
- } else {
49
- activityIdsSet.add(id);
50
- }
51
- });
52
- });
53
- this.activityIds = Array.from(activityIdsSet);
54
- }
55
-
56
- isMappingStatement(value: string): boolean {
57
- return typeof value === 'string' && value.startsWith('{') && value.endsWith('}');
58
- }
59
-
60
- extractMappingStatements(obj: any, result: MappingStatements, currentActivityId: string): void {
61
- for (const key in obj) {
62
- if (typeof obj[key] === 'object' && obj[key] !== null) {
63
- this.extractMappingStatements(obj[key], result, currentActivityId);
64
- } else if (this.isMappingStatement(obj[key])) {
65
- if (!result[currentActivityId]) {
66
- result[currentActivityId] = [];
67
- }
68
- result[currentActivityId].push(obj[key]);
69
- }
70
- }
71
- }
72
-
73
- getMappingStatements() {
74
- const mappingStatements: MappingStatements = {};
75
- this.manifest.app.graphs.forEach((graph) => {
76
- const activities = graph.activities;
77
- for (const activityId in activities) {
78
- const activity = activities[activityId];
79
- this.extractMappingStatements(activity, mappingStatements, activityId);
80
- }
81
- });
82
- this.mappingStatements = mappingStatements;
83
- }
84
-
85
- // 1.2) Validate no activity ids are referenced that don't exist
86
- validateReferencedActivityIds() {
87
- // get list of all mapping statements and validate
88
- const mappingStatements = this.mappingStatements;
89
- const activityIds = this.activityIds;
90
- for (const activity in mappingStatements) {
91
- const statements = mappingStatements[activity];
92
- statements.forEach((statement) => {
93
- if (statement.startsWith('{') && statement.endsWith('}')) {
94
- const statementParts = statement.slice(1, -1).split('.');
95
- const referencedActivityId = statementParts[0];
96
- if (!(Validator.SYS_VARS.includes(referencedActivityId) || activityIds.includes(referencedActivityId) || this.isFunction(statement) || this.isContextVariable(statement))) {
97
- throw new Error(`Mapping statement references non-existent activity: ${statement}`);
98
- }
99
- }
100
- });
101
- }
102
- }
103
-
104
- isFunction(value: string): boolean {
105
- return value.startsWith('{@') && Pipe.resolveFunction(value);
106
- }
107
-
108
- isContextVariable(value: string): boolean {
109
- return ['{$input}', '{$output}', '{$item}', '{$key}', '{$index}'].includes(value);
110
- }
111
-
112
- // 1.3) Validate the mapping/@pipe statements are valid
113
- validateMappingStatements() {
114
- // Implement the method content
115
- }
116
-
117
- // 1.4) Validate the transitions are valid
118
- validateTransitions() {
119
- // Implement the method content
120
- }
121
-
122
- // 1.5) Validate the transition conditions are valid
123
- validateTransitionConditions() {
124
- // Implement the method content
125
- }
126
-
127
- // 1.6) Validate the stats
128
- validateStats() {
129
- // Implement the method content
130
- }
131
-
132
- // 1.7) Validate the schemas
133
- validateSchemas() {
134
- // Implement the method content
135
- }
136
-
137
- // 1.8) Validate the topics are unique and handled
138
- validateUniqueHandledTopics() {
139
- // Implement the method content
140
- }
141
-
142
- // 1.9) Validate that every graph has publishes and subscribes
143
- validateGraphPublishSubscribe() {
144
- // Implement the method content
145
- }
146
-
147
- // 1.10) Validate hooks, including mapping statements
148
- validateHooks() {
149
- // Implement the method content
150
- }
151
-
152
- // 1.11) Validate conditional statements
153
- validateConditionalStatements() {
154
- // Implement the method content
155
- }
156
- }
157
-
158
- export { Validator };
@@ -1,57 +0,0 @@
1
- import {
2
- RedisClientOptions,
3
- RedisClassType,
4
- RedisClientType } from '../../../types/ioredisclient';
5
-
6
- class RedisConnection {
7
- private connection: any | null = null;
8
- private static instances: Map<string, RedisConnection> = new Map();
9
- private id: string | null = null;
10
-
11
- private static clientOptions: RedisClientOptions = {
12
- host: 'localhost',
13
- port: 6379,
14
- //password: config.REDIS_PASSWORD,
15
- //db: config.REDIS_DATABASE,
16
- };
17
-
18
- private async createConnection(Redis: RedisClassType, options: RedisClientOptions): Promise<any> {
19
- return new Redis(options);
20
- }
21
-
22
- public getClient(): RedisClientType {
23
- if (!this.connection) {
24
- throw new Error('Redis client is not connected');
25
- }
26
- return this.connection;
27
- }
28
-
29
- public async disconnect(): Promise<void> {
30
- if (this.connection) {
31
- await this.connection.quit();
32
- this.connection = null;
33
- }
34
- if (this.id) {
35
- RedisConnection.instances.delete(this.id);
36
- }
37
- }
38
-
39
- public static async connect(id: string, Redis: RedisClassType, options?: RedisClientOptions): Promise<RedisConnection> {
40
- if (this.instances.has(id)) {
41
- return this.instances.get(id) as RedisConnection;
42
- }
43
- const instance = new RedisConnection();
44
- const opts = options ? { ...options } : { ...this.clientOptions };
45
- instance.connection = await instance.createConnection(Redis, opts);
46
- instance.id = id;
47
- this.instances.set(id, instance);
48
- return instance;
49
- }
50
-
51
- public static async disconnectAll(): Promise<void> {
52
- await Promise.all(Array.from(this.instances.values()).map((instance) => instance.disconnect()));
53
- this.instances.clear();
54
- }
55
- }
56
-
57
- export { RedisConnection };
@@ -1,72 +0,0 @@
1
- import {
2
- RedisClientType,
3
- RedisClientOptions,
4
- RedisClassType } from '../../../types/redisclient';
5
-
6
- class RedisConnection {
7
- private connection: RedisClientType | null = null;
8
- private static instances: Map<string, RedisConnection> = new Map();
9
- private id: string | null = null;
10
-
11
- private static clientOptions: RedisClientOptions = {
12
- socket: {
13
- host: 'localhost',
14
- port: 6379,
15
- tls: false,
16
- },
17
- //password: config.REDIS_PASSWORD,
18
- //database: config.REDIS_DATABASE,
19
- };
20
-
21
- private async createConnection(Redis: RedisClassType, options: RedisClientOptions): Promise<RedisClientType> {
22
- return new Promise((resolve, reject) => {
23
- const client = Redis.createClient(options);
24
-
25
- client.on('error', (error: any) => {
26
- reject(error);
27
- });
28
-
29
- client.on('ready', () => {
30
- resolve(client);
31
- });
32
-
33
- client.connect();
34
- });
35
- }
36
-
37
- public getClient(): RedisClientType {
38
- if (!this.connection) {
39
- throw new Error('Redis client is not connected');
40
- }
41
- return this.connection;
42
- }
43
-
44
- public async disconnect(): Promise<void> {
45
- if (this.connection) {
46
- await this.connection.quit();
47
- this.connection = null;
48
- }
49
- if (this.id) {
50
- RedisConnection.instances.delete(this.id);
51
- }
52
- }
53
-
54
- public static async connect(id: string, Redis: RedisClassType, options?: RedisClientOptions): Promise<RedisConnection> {
55
- if (this.instances.has(id)) {
56
- return this.instances.get(id)!;
57
- }
58
- const instance = new RedisConnection();
59
- const opts = options ? { ...options } : { ...this.clientOptions };
60
- instance.connection = await instance.createConnection(Redis, opts);
61
- instance.id = id;
62
- this.instances.set(id, instance);
63
- return instance;
64
- }
65
-
66
- public static async disconnectAll(): Promise<void> {
67
- await Promise.all(Array.from(this.instances.values()).map((instance) => instance.disconnect()));
68
- this.instances.clear();
69
- }
70
- }
71
-
72
- export { RedisConnection, RedisClientType };
@@ -1,42 +0,0 @@
1
- import { guid, identifyRedisTypeFromClass } from '../../modules/utils';
2
- import { RedisConnection as IORedisConnection } from '../connector/clients/ioredis';
3
- import { RedisConnection } from '../connector/clients/redis';
4
- import {
5
- RedisClassType as IORedisClassType,
6
- RedisClientOptions as IORedisClientOptions } from '../../types/ioredisclient';
7
- import {
8
- HotMeshEngine,
9
- HotMeshWorker } from '../../types/hotmesh';
10
- import { RedisClass, RedisOptions } from '../../types/redis';
11
- import {
12
- RedisClassType,
13
- RedisClientOptions } from '../../types/redisclient';
14
-
15
- export class ConnectorService {
16
- //1) Initialize `store`, `stream`, and `subscription` Redis clients.
17
- //2) Bind to the target if not already present
18
- static async initRedisClients(Redis: RedisClass, options: RedisOptions, target: HotMeshEngine | HotMeshWorker): Promise<void> {
19
- if (!target.store || !target.stream || !target.sub) {
20
- const instances = [];
21
- if (identifyRedisTypeFromClass(Redis) === 'redis') {
22
- for (let i = 1; i <= 3; i++) {
23
- instances.push(RedisConnection.connect(
24
- guid(),
25
- Redis as RedisClassType,
26
- options as RedisClientOptions));
27
- }
28
- } else {
29
- for (let i = 1; i <= 3; i++) {
30
- instances.push(IORedisConnection.connect(
31
- guid(),
32
- Redis as IORedisClassType,
33
- options as IORedisClientOptions));
34
- }
35
- }
36
- const [store, stream, sub] = await Promise.all(instances);
37
- target.store = target.store || store.getClient();
38
- target.stream = target.stream || stream.getClient();
39
- target.sub = target.sub || sub.getClient();
40
- }
41
- }
42
- }
@@ -1,266 +0,0 @@
1
- import ms from 'ms';
2
-
3
- import {
4
- APP_ID,
5
- APP_VERSION,
6
- getWorkflowYAML } from './schemas/factory';
7
- import {
8
- HMSH_LOGLEVEL,
9
- HMSH_EXPIRE_JOB_SECONDS,
10
- HMSH_QUORUM_DELAY_MS,
11
- HMSH_DURABLE_EXP_BACKOFF,
12
- HMSH_DURABLE_MAX_ATTEMPTS,
13
- HMSH_DURABLE_MAX_INTERVAL } from '../../modules/enums';
14
- import { sleepFor } from '../../modules/utils';
15
- import { WorkflowHandleService } from './handle';
16
- import { HotMeshService as HotMesh } from '../hotmesh';
17
- import {
18
- ClientConfig,
19
- Connection,
20
- HookOptions,
21
- WorkflowOptions,
22
- WorkflowSearchOptions} from '../../types/durable';
23
- import { JobState } from '../../types/job';
24
- import { KeyService, KeyType } from '../../modules/key';
25
- import { Search } from './search';
26
- import { StreamStatus } from '../../types';
27
-
28
- export class ClientService {
29
-
30
- connection: Connection;
31
- options: WorkflowOptions;
32
- static topics: string[] = [];
33
- static instances = new Map<string, HotMesh | Promise<HotMesh>>();
34
-
35
- constructor(config: ClientConfig) {
36
- this.connection = config.connection;
37
- }
38
-
39
- getHotMeshClient = async (workflowTopic: string, namespace?: string) => {
40
- const targetNS = namespace ?? APP_ID;
41
- if (ClientService.instances.has(targetNS)) {
42
- const hotMeshClient = await ClientService.instances.get(targetNS);
43
- await this.verifyWorkflowActive(hotMeshClient, targetNS);
44
- if (!ClientService.topics.includes(workflowTopic)) {
45
- ClientService.topics.push(workflowTopic);
46
- await ClientService.createStream(hotMeshClient, workflowTopic, namespace);
47
- }
48
- return hotMeshClient;
49
- }
50
-
51
- //create and cache an instance
52
- const hotMeshClient = HotMesh.init({
53
- appId: targetNS,
54
- logLevel: HMSH_LOGLEVEL,
55
- engine: {
56
- redis: {
57
- class: this.connection.class,
58
- options: this.connection.options,
59
- }
60
- }
61
- });
62
- ClientService.instances.set(targetNS, hotMeshClient);
63
- await ClientService.createStream(await hotMeshClient, workflowTopic, namespace);
64
- await this.activateWorkflow(await hotMeshClient, targetNS);
65
- return hotMeshClient;
66
- }
67
-
68
- /**
69
- * Creates a stream (Redis `XGROUP.CREATE`) where events can be published (XADD).
70
- * It is possible that the worker that will read from this stream channel
71
- * has not yet been initialized, so this call ensures that the channel
72
- * exists and is ready to serve as a container for events.
73
- */
74
- static createStream = async(hotMeshClient: HotMesh, workflowTopic: string, namespace?: string) => {
75
- const store = hotMeshClient.engine.store;
76
- const params = { appId: namespace ?? APP_ID, topic: workflowTopic };
77
- const streamKey = store.mintKey(KeyType.STREAMS, params);
78
- try {
79
- await store.xgroup('CREATE', streamKey, 'WORKER', '$', 'MKSTREAM');
80
- } catch (err) {
81
- //ignore if already exists
82
- }
83
- }
84
-
85
- /**
86
- * It is possible for a client to invoke a workflow without first
87
- * creating the stream. This method will verify that the stream
88
- * exists and if not, create it.
89
- */
90
- static verifyStream = async(workflowTopic: string, namespace?: string) => {
91
- const targetNS = namespace ?? APP_ID;
92
- if (ClientService.instances.has(targetNS)) {
93
- const hotMeshClient = await ClientService.instances.get(targetNS);
94
- if (!ClientService.topics.includes(workflowTopic)) {
95
- ClientService.topics.push(workflowTopic);
96
- await ClientService.createStream(hotMeshClient, workflowTopic, namespace);
97
- }
98
- return hotMeshClient;
99
- }
100
- }
101
-
102
- /**
103
- * For those deployments with a redis stack backend (with the FT module),
104
- * this method will configure the search index for the workflow.
105
- */
106
- configureSearchIndex = async (hotMeshClient: HotMesh, search?: WorkflowSearchOptions): Promise<void> => {
107
- if (search?.schema) {
108
- const store = hotMeshClient.engine.store;
109
- const schema: string[] = [];
110
- for (const [key, value] of Object.entries(search.schema)) {
111
- //prefix with an underscore (avoids collisions with hotmesh reserved symbols)
112
- schema.push(`_${key}`);
113
- schema.push(value.type);
114
- if (value.sortable) {
115
- schema.push('SORTABLE');
116
- }
117
- }
118
- try {
119
- const keyParams = {
120
- appId: hotMeshClient.appId,
121
- jobId: ''
122
- }
123
- const hotMeshPrefix = KeyService.mintKey(hotMeshClient.namespace, KeyType.JOB_STATE, keyParams);
124
- const prefixes = search.prefix.map((prefix) => `${hotMeshPrefix}${prefix}`);
125
- await store.exec('FT.CREATE', `${search.index}`, 'ON', 'HASH', 'PREFIX', prefixes.length, ...prefixes, 'SCHEMA', ...schema);
126
- } catch (error) {
127
- hotMeshClient.engine.logger.info('durable-client-search-err', { ...error });
128
- }
129
- }
130
- }
131
-
132
- search = async (hotMeshClient: HotMesh, index: string, query: string[]): Promise<string[]> => {
133
- const store = hotMeshClient.engine.store;
134
- if (query[0]?.startsWith('FT.')) {
135
- return await store.exec(...query) as string[];
136
- }
137
- return await store.exec('FT.SEARCH', index, ...query) as string[];
138
- }
139
-
140
- workflow = {
141
- start: async (options: WorkflowOptions): Promise<WorkflowHandleService> => {
142
- const taskQueueName = options.entity ?? options.taskQueue;
143
- const workflowName = options.entity ?? options.workflowName;
144
- const trc = options.workflowTrace;
145
- const spn = options.workflowSpan;
146
- //NOTE: HotMesh 'workflowTopic' is a created by concatenating
147
- // the taskQueue and workflowName used by the Durable module
148
- const workflowTopic = `${taskQueueName}-${workflowName}`;
149
- const hotMeshClient = await this.getHotMeshClient(workflowTopic, options.namespace);
150
- this.configureSearchIndex(hotMeshClient, options.search);
151
- const payload = {
152
- arguments: [...options.args],
153
- originJobId: options.originJobId,
154
- expire: options.expire ?? HMSH_EXPIRE_JOB_SECONDS,
155
- parentWorkflowId: options.parentWorkflowId,
156
- workflowId: options.workflowId || HotMesh.guid(),
157
- workflowTopic: workflowTopic,
158
- backoffCoefficient: options.config?.backoffCoefficient || HMSH_DURABLE_EXP_BACKOFF,
159
- maximumAttempts: options.config?.maximumAttempts || HMSH_DURABLE_MAX_ATTEMPTS,
160
- maximumInterval: ms(options.config?.maximumInterval || HMSH_DURABLE_MAX_INTERVAL) / 1000,
161
- }
162
-
163
- const context = { metadata: { trc, spn }, data: {}};
164
- const jobId = await hotMeshClient.pub(
165
- `${options.namespace ?? APP_ID}.execute`,
166
- payload,
167
- context as JobState,
168
- { search: options?.search?.data, marker: options?.marker},
169
- );
170
- return new WorkflowHandleService(hotMeshClient, workflowTopic, jobId);
171
- },
172
-
173
- /**
174
- * send a message to a running workflow that is paused and awaiting the signal
175
- */
176
- signal: async (signalId: string, data: Record<any, any>, namespace?: string): Promise<string> => {
177
- const topic = `${namespace ?? APP_ID}.wfs.signal`;
178
- return await (await this.getHotMeshClient(topic, namespace)).hook(topic, { id: signalId, data });
179
- },
180
-
181
- /**
182
- * send a message to spawn an parallel in-process thread of execution
183
- * with the same job state as the main thread but bound to a different
184
- * handler function. All job state will be journaled to the same hash
185
- * as is used by the main thread.
186
- */
187
- hook: async (options: HookOptions): Promise<string> => {
188
- const workflowTopic = `${options.taskQueue}-${options.workflowName}`;
189
- const payload = {
190
- arguments: [...options.args],
191
- id: options.workflowId,
192
- workflowTopic,
193
- backoffCoefficient: options.config?.backoffCoefficient || HMSH_DURABLE_EXP_BACKOFF,
194
- maximumAttempts: options.config?.maximumAttempts || HMSH_DURABLE_MAX_ATTEMPTS,
195
- maximumInterval: ms(options.config?.maximumInterval || HMSH_DURABLE_MAX_INTERVAL) / 1000,
196
- }
197
- //seed search data if presentthe hook before entering
198
- const hotMeshClient = await this.getHotMeshClient(workflowTopic, options.namespace);
199
- const msgId = await hotMeshClient.hook(`${hotMeshClient.appId}.flow.signal`, payload, StreamStatus.PENDING, 202);
200
- if (options.search?.data) {
201
- const searchSessionId = `-search-${HotMesh.guid()}-0`;
202
- const search = new Search(options.workflowId, hotMeshClient, searchSessionId);
203
- const entries = Object.entries(options.search.data).flat();
204
- await search.set(...entries);
205
- }
206
- return msgId;
207
- },
208
-
209
- getHandle: async (taskQueue: string, workflowName: string, workflowId: string, namespace?: string): Promise<WorkflowHandleService> => {
210
- const workflowTopic = `${taskQueue}-${workflowName}`;
211
- const hotMeshClient = await this.getHotMeshClient(workflowTopic, namespace);
212
- return new WorkflowHandleService(hotMeshClient, workflowTopic, workflowId);
213
- },
214
-
215
- search: async (taskQueue: string, workflowName: string, namespace: null | string, index: string, ...query: string[]): Promise<string[]> => {
216
- const workflowTopic = `${taskQueue}-${workflowName}`;
217
- const hotMeshClient = await this.getHotMeshClient(workflowTopic, namespace);
218
- try {
219
- return await this.search(hotMeshClient, index, query);
220
- } catch (error) {
221
- hotMeshClient.engine.logger.error('durable-client-search-err', { ...error });
222
- throw error;
223
- }
224
- }
225
- }
226
-
227
- async verifyWorkflowActive(hotMesh: HotMesh, appId = APP_ID, count = 0): Promise<boolean> {
228
- const app = await hotMesh.engine.store.getApp(appId);
229
- const appVersion = app?.version as unknown as number;
230
- if(isNaN(appVersion)) {
231
- if (count > 10) {
232
- throw new Error('Workflow failed to activate');
233
- }
234
- await sleepFor(HMSH_QUORUM_DELAY_MS * 2);
235
- return await this.verifyWorkflowActive(hotMesh, appId, count + 1);
236
- }
237
- return true;
238
- }
239
-
240
- async activateWorkflow(hotMesh: HotMesh, appId = APP_ID, version = APP_VERSION): Promise<void> {
241
- const app = await hotMesh.engine.store.getApp(appId);
242
- const appVersion = app?.version as unknown as number;
243
- if(isNaN(appVersion)) {
244
- try {
245
- await hotMesh.deploy(getWorkflowYAML(appId, version));
246
- await hotMesh.activate(version);
247
- } catch (error) {
248
- hotMesh.engine.logger.error('durable-client-deploy-activate-err', { ...error });
249
- throw error;
250
- }
251
- } else if(app && !app.active) {
252
- try {
253
- await hotMesh.activate(version);
254
- } catch (error) {
255
- hotMesh.engine.logger.error('durable-client-activate-err', { error});
256
- throw error;
257
- }
258
- }
259
- }
260
-
261
- static async shutdown(): Promise<void> {
262
- for (const [_, hotMeshInstance] of ClientService.instances) {
263
- (await hotMeshInstance).stop();
264
- }
265
- }
266
- }
@@ -1,10 +0,0 @@
1
- import { Connection, ConnectionConfig } from "../../types/durable";
2
-
3
- export class ConnectionService {
4
- static async connect(config: ConnectionConfig): Promise<Connection> {
5
- return {
6
- class: config.class,
7
- options: { ...config.options },
8
- } as Connection;
9
- }
10
- }