@hotmeshio/hotmesh 0.1.15 → 0.1.17

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 (134) hide show
  1. package/README.md +623 -209
  2. package/build/index.d.ts +14 -3
  3. package/build/index.js +17 -4
  4. package/build/modules/enums.d.ts +12 -12
  5. package/build/modules/enums.js +15 -25
  6. package/build/modules/errors.d.ts +16 -16
  7. package/build/modules/errors.js +28 -28
  8. package/build/modules/key.d.ts +0 -37
  9. package/build/modules/key.js +4 -45
  10. package/build/modules/utils.d.ts +7 -15
  11. package/build/modules/utils.js +21 -44
  12. package/build/package.json +18 -15
  13. package/build/services/activities/activity.d.ts +0 -31
  14. package/build/services/activities/activity.js +1 -50
  15. package/build/services/activities/await.js +0 -4
  16. package/build/services/activities/cycle.d.ts +0 -7
  17. package/build/services/activities/cycle.js +1 -16
  18. package/build/services/activities/hook.d.ts +0 -6
  19. package/build/services/activities/hook.js +2 -12
  20. package/build/services/activities/interrupt.js +0 -8
  21. package/build/services/activities/signal.d.ts +0 -6
  22. package/build/services/activities/signal.js +0 -15
  23. package/build/services/activities/trigger.d.ts +4 -5
  24. package/build/services/activities/trigger.js +22 -16
  25. package/build/services/activities/worker.js +0 -4
  26. package/build/services/collator/index.d.ts +0 -70
  27. package/build/services/collator/index.js +1 -91
  28. package/build/services/compiler/deployer.js +6 -38
  29. package/build/services/compiler/index.d.ts +0 -15
  30. package/build/services/compiler/index.js +0 -20
  31. package/build/services/compiler/validator.d.ts +0 -3
  32. package/build/services/compiler/validator.js +0 -25
  33. package/build/services/connector/clients/ioredis.js +0 -2
  34. package/build/services/connector/clients/redis.js +0 -2
  35. package/build/services/connector/index.js +0 -2
  36. package/build/services/engine/index.d.ts +1 -10
  37. package/build/services/engine/index.js +1 -48
  38. package/build/services/exporter/index.d.ts +0 -27
  39. package/build/services/exporter/index.js +0 -33
  40. package/build/services/hotmesh/index.d.ts +8 -4
  41. package/build/services/hotmesh/index.js +20 -19
  42. package/build/services/logger/index.js +0 -2
  43. package/build/services/mapper/index.d.ts +0 -14
  44. package/build/services/mapper/index.js +0 -14
  45. package/build/services/meshcall/index.d.ts +21 -0
  46. package/build/services/meshcall/index.js +202 -0
  47. package/build/services/meshcall/schemas/factory.d.ts +2 -0
  48. package/build/services/meshcall/schemas/factory.js +179 -0
  49. package/build/services/meshdata/index.d.ts +75 -0
  50. package/build/services/meshdata/index.js +541 -0
  51. package/build/services/meshflow/client.d.ts +18 -0
  52. package/build/services/{durable → meshflow}/client.js +9 -40
  53. package/build/services/{durable → meshflow}/connection.d.ts +2 -1
  54. package/build/services/{durable → meshflow}/connection.js +1 -0
  55. package/build/services/meshflow/exporter.d.ts +29 -0
  56. package/build/services/{durable → meshflow}/exporter.js +0 -29
  57. package/build/services/meshflow/handle.d.ts +22 -0
  58. package/build/services/{durable → meshflow}/handle.js +0 -46
  59. package/build/services/meshflow/index.d.ts +17 -0
  60. package/build/services/meshflow/index.js +23 -0
  61. package/build/services/meshflow/schemas/factory.d.ts +4 -0
  62. package/build/services/{durable → meshflow}/schemas/factory.js +2 -30
  63. package/build/services/meshflow/search.d.ts +23 -0
  64. package/build/services/{durable → meshflow}/search.js +0 -99
  65. package/build/services/{durable → meshflow}/worker.d.ts +3 -2
  66. package/build/services/{durable → meshflow}/worker.js +23 -39
  67. package/build/services/meshflow/workflow.d.ts +27 -0
  68. package/build/services/{durable → meshflow}/workflow.js +27 -169
  69. package/build/services/pipe/functions/date.d.ts +0 -7
  70. package/build/services/pipe/functions/date.js +0 -7
  71. package/build/services/pipe/functions/math.js +0 -2
  72. package/build/services/pipe/index.d.ts +0 -15
  73. package/build/services/pipe/index.js +2 -23
  74. package/build/services/quorum/index.d.ts +1 -7
  75. package/build/services/quorum/index.js +0 -21
  76. package/build/services/reporter/index.d.ts +0 -5
  77. package/build/services/reporter/index.js +0 -9
  78. package/build/services/router/index.d.ts +0 -9
  79. package/build/services/router/index.js +2 -30
  80. package/build/services/serializer/index.js +6 -23
  81. package/build/services/store/cache.d.ts +0 -19
  82. package/build/services/store/cache.js +0 -19
  83. package/build/services/store/clients/ioredis.d.ts +0 -6
  84. package/build/services/store/clients/ioredis.js +0 -7
  85. package/build/services/store/clients/redis.d.ts +0 -6
  86. package/build/services/store/clients/redis.js +0 -6
  87. package/build/services/store/index.d.ts +0 -55
  88. package/build/services/store/index.js +14 -87
  89. package/build/services/stream/clients/ioredis.js +1 -4
  90. package/build/services/task/index.d.ts +0 -9
  91. package/build/services/task/index.js +0 -31
  92. package/build/services/telemetry/index.d.ts +0 -7
  93. package/build/services/telemetry/index.js +1 -13
  94. package/build/services/worker/index.d.ts +1 -4
  95. package/build/services/worker/index.js +0 -6
  96. package/build/types/activity.d.ts +0 -81
  97. package/build/types/error.d.ts +5 -5
  98. package/build/types/exporter.d.ts +1 -14
  99. package/build/types/hotmesh.d.ts +4 -12
  100. package/build/types/hotmesh.js +0 -3
  101. package/build/types/index.d.ts +5 -3
  102. package/build/types/index.js +1 -1
  103. package/build/types/job.d.ts +1 -95
  104. package/build/types/meshcall.d.ts +54 -0
  105. package/build/types/meshdata.d.ts +59 -0
  106. package/build/types/meshdata.js +2 -0
  107. package/build/types/meshflow.d.ts +202 -0
  108. package/build/types/meshflow.js +2 -0
  109. package/build/types/pipe.d.ts +0 -65
  110. package/build/types/quorum.d.ts +0 -12
  111. package/build/types/redis.d.ts +0 -6
  112. package/build/types/stream.d.ts +0 -59
  113. package/build/types/stream.js +0 -4
  114. package/index.ts +12 -3
  115. package/package.json +18 -15
  116. package/typedoc.json +38 -0
  117. package/types/error.ts +5 -5
  118. package/types/exporter.ts +1 -1
  119. package/types/hotmesh.ts +3 -2
  120. package/types/index.ts +25 -7
  121. package/types/job.ts +19 -1
  122. package/types/meshcall.ts +192 -0
  123. package/types/meshdata.ts +273 -0
  124. package/types/{durable.ts → meshflow.ts} +33 -9
  125. package/build/services/durable/client.d.ts +0 -49
  126. package/build/services/durable/exporter.d.ts +0 -51
  127. package/build/services/durable/handle.d.ts +0 -58
  128. package/build/services/durable/index.d.ts +0 -19
  129. package/build/services/durable/index.js +0 -25
  130. package/build/services/durable/schemas/factory.d.ts +0 -33
  131. package/build/services/durable/search.d.ts +0 -120
  132. package/build/services/durable/workflow.d.ts +0 -143
  133. package/build/types/durable.d.ts +0 -467
  134. /package/build/types/{durable.js → meshcall.js} +0 -0
@@ -0,0 +1,541 @@
1
+ "use strict";
2
+ var _a;
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.MeshData = void 0;
5
+ const meshflow_1 = require("../meshflow");
6
+ const hotmesh_1 = require("../hotmesh");
7
+ const hotmesh_2 = require("../../types/hotmesh");
8
+ class MeshData {
9
+ constructor(redisClass, redisOptions, search) {
10
+ this.connectionSignatures = {};
11
+ this.instances = new Map();
12
+ this.mesh = {
13
+ sub: async (callback, options = {}) => {
14
+ const hotMesh = await this.getHotMesh(options.namespace || 'durable');
15
+ const callbackWrapper = (topic, message) => {
16
+ if (message.type === 'pong' && !message.originator) {
17
+ if (message.profile?.worker_topic) {
18
+ const [entity] = message.profile.worker_topic.split('-');
19
+ if (entity) {
20
+ message.profile.entity = message.entity = entity;
21
+ if (this.connectionSignatures[entity]) {
22
+ message.profile.signature = this.connectionSignatures[entity];
23
+ }
24
+ }
25
+ }
26
+ }
27
+ else if (message?.topic) {
28
+ const [entity] = message.topic.split('-');
29
+ if (entity) {
30
+ message.entity = entity;
31
+ }
32
+ }
33
+ callback(topic, message);
34
+ };
35
+ await hotMesh.quorum?.sub(callbackWrapper);
36
+ },
37
+ pub: async (message, options = {}) => {
38
+ const hotMesh = await this.getHotMesh(options.namespace || 'durable');
39
+ await hotMesh.quorum?.pub(message);
40
+ },
41
+ unsub: async (callback, options = {}) => {
42
+ const hotMesh = await this.getHotMesh(options.namespace || 'durable');
43
+ await hotMesh.quorum?.unsub(callback);
44
+ }
45
+ };
46
+ this.redisClass = redisClass;
47
+ this.redisOptions = redisOptions;
48
+ if (search) {
49
+ this.search = search;
50
+ }
51
+ }
52
+ validate(entity) {
53
+ if (entity.includes(':') ||
54
+ entity.includes('$') ||
55
+ entity.includes(' ')) {
56
+ throw "Invalid string [':','$',' ' not allowed]";
57
+ }
58
+ }
59
+ async getConnection() {
60
+ return await meshflow_1.MeshFlow.Connection.connect({
61
+ class: this.redisClass,
62
+ options: this.redisOptions,
63
+ });
64
+ }
65
+ getClient() {
66
+ return new meshflow_1.MeshFlow.Client({
67
+ connection: {
68
+ class: this.redisClass,
69
+ options: this.redisOptions,
70
+ }
71
+ });
72
+ }
73
+ safeKey(key) {
74
+ return `_${key}`;
75
+ }
76
+ arrayToHash(input) {
77
+ const [_count, ...rest] = input;
78
+ const max = rest.length / 2;
79
+ const hashes = [];
80
+ for (let i = 0; i < max * 2; i += 2) {
81
+ const fields = rest[i + 1];
82
+ const hash = { '$': rest[i] };
83
+ for (let j = 0; j < fields.length; j += 2) {
84
+ const fieldKey = fields[j].replace(/^_/, '');
85
+ const fieldValue = fields[j + 1];
86
+ hash[fieldKey] = fieldValue;
87
+ }
88
+ hashes.push(hash);
89
+ }
90
+ return hashes;
91
+ }
92
+ toString(value) {
93
+ switch (typeof value) {
94
+ case 'string':
95
+ break;
96
+ case 'boolean':
97
+ value = value ? '/t' : '/f';
98
+ break;
99
+ case 'number':
100
+ value = '/d' + value.toString();
101
+ break;
102
+ case 'undefined':
103
+ return undefined;
104
+ case 'object':
105
+ if (value === null) {
106
+ value = '/n';
107
+ }
108
+ else {
109
+ value = '/s' + JSON.stringify(value);
110
+ }
111
+ break;
112
+ }
113
+ return value;
114
+ }
115
+ static mintGuid(entity, id) {
116
+ if (!id && !entity) {
117
+ throw "Invalid arguments [entity and id are both null]";
118
+ }
119
+ else if (!id) {
120
+ id = hotmesh_1.HotMesh.guid();
121
+ }
122
+ else if (entity) {
123
+ entity = `${entity}-`;
124
+ }
125
+ else {
126
+ entity = '';
127
+ }
128
+ return `${entity}${id}`;
129
+ }
130
+ async getHotMesh(namespace = 'durable') {
131
+ let hotMesh = await this.instances.get(namespace);
132
+ if (!hotMesh) {
133
+ hotMesh = hotmesh_1.HotMesh.init({
134
+ appId: namespace,
135
+ engine: {
136
+ redis: {
137
+ class: this.redisClass,
138
+ options: this.redisOptions,
139
+ }
140
+ }
141
+ });
142
+ this.instances.set(namespace, hotMesh);
143
+ hotMesh = await hotMesh;
144
+ this.instances.set(namespace, hotMesh);
145
+ }
146
+ return hotMesh;
147
+ }
148
+ async mintKey(entity, workflowId, namespace) {
149
+ const handle = await this.getClient().workflow.getHandle(entity, entity, workflowId, namespace);
150
+ const store = handle.hotMesh.engine?.store;
151
+ return store?.mintKey(hotmesh_2.KeyType.JOB_STATE, { jobId: workflowId, appId: handle.hotMesh.engine?.appId });
152
+ }
153
+ async connect({ entity, target, options = {} }) {
154
+ this.validate(entity);
155
+ this.connectionSignatures[entity] = target.toString();
156
+ const targetFunction = { [entity]: async (...args) => {
157
+ const { callOptions } = this.bindCallOptions(args, options);
158
+ const result = await target.apply(target, args);
159
+ await this.pauseForTTL(result, callOptions);
160
+ return result;
161
+ } };
162
+ await meshflow_1.MeshFlow.Worker.create({
163
+ namespace: options.namespace,
164
+ options: options.options,
165
+ connection: await this.getConnection(),
166
+ taskQueue: options.taskQueue ?? entity,
167
+ workflow: targetFunction,
168
+ search: options.search,
169
+ });
170
+ return true;
171
+ }
172
+ bindCallOptions(args, options, callOptions = {}) {
173
+ if (args.length) {
174
+ const lastArg = args[args.length - 1];
175
+ if (lastArg instanceof Object && lastArg?.$type === 'exec') {
176
+ callOptions = args.pop();
177
+ if (options.ttl === 'infinity') {
178
+ if (!callOptions) {
179
+ callOptions = { ttl: 'infinity' };
180
+ }
181
+ else {
182
+ callOptions.ttl = 'infinity';
183
+ }
184
+ }
185
+ }
186
+ else if (lastArg instanceof Object && lastArg.$type === 'hook') {
187
+ callOptions = args.pop();
188
+ delete callOptions.ttl;
189
+ }
190
+ }
191
+ return { callOptions };
192
+ }
193
+ async pauseForTTL(result, options) {
194
+ if (options?.ttl && options.$type === 'exec') {
195
+ const { counter, replay, workflowDimension, workflowId } = MeshData.workflow.getContext();
196
+ const prefix = options.ttl === 'infinity' ? 'wait' : 'sleep';
197
+ if (`-${prefix}${workflowDimension}-${counter + 1}-` in replay) {
198
+ return;
199
+ }
200
+ await new Promise(resolve => setImmediate(resolve));
201
+ options.$guid = options.$guid ?? workflowId;
202
+ const hotMesh = await MeshData.workflow.getHotMesh();
203
+ const store = hotMesh.engine?.store;
204
+ const jobKey = store?.mintKey(hotmesh_2.KeyType.JOB_STATE, { jobId: options.$guid, appId: hotMesh.engine?.appId });
205
+ const jobResponse = ['aAa', '/t', 'aBa', this.toString(result)];
206
+ await store?.exec('HSET', jobKey, ...jobResponse);
207
+ await this.publishDone(result, hotMesh, options);
208
+ if (options.ttl === 'infinity') {
209
+ await MeshData.workflow.waitFor(`flush-${options.$guid}`);
210
+ }
211
+ else {
212
+ await MeshData.workflow.sleepFor(options.ttl);
213
+ }
214
+ }
215
+ }
216
+ async publishDone(result, hotMesh, options) {
217
+ await hotMesh.engine?.store?.publish(hotmesh_2.KeyType.QUORUM, {
218
+ type: 'job',
219
+ topic: `${hotMesh.engine.appId}.executed`,
220
+ job: {
221
+ metadata: {
222
+ tpc: `${hotMesh.engine.appId}.execute`,
223
+ app: hotMesh.engine.appId,
224
+ vrs: '1',
225
+ jid: options.$guid,
226
+ aid: 't1',
227
+ ts: '0',
228
+ js: 0
229
+ },
230
+ data: {
231
+ done: true,
232
+ response: result,
233
+ workflowId: options.$guid
234
+ }
235
+ }
236
+ }, hotMesh.engine.appId, `${hotMesh.engine.appId}.executed.${options.$guid}`);
237
+ }
238
+ async flush(entity, id, namespace) {
239
+ const workflowId = MeshData.mintGuid(entity, id);
240
+ await this.getClient().workflow.signal(`flush-${workflowId}`, {}, namespace);
241
+ await new Promise(resolve => setTimeout(resolve, 1000));
242
+ await this.interrupt(entity, id, {
243
+ descend: true,
244
+ suppress: true,
245
+ expire: 1
246
+ }, namespace);
247
+ }
248
+ async interrupt(entity, id, options = {}, namespace) {
249
+ const workflowId = MeshData.mintGuid(entity, id);
250
+ try {
251
+ const handle = await this.getClient().workflow.getHandle(entity, entity, workflowId, namespace);
252
+ const hotMesh = handle.hotMesh;
253
+ await hotMesh.interrupt(`${hotMesh.appId}.execute`, workflowId, options);
254
+ }
255
+ catch (e) {
256
+ }
257
+ }
258
+ async signal(guid, payload, namespace) {
259
+ return await this.getClient().workflow.signal(guid, payload, namespace);
260
+ }
261
+ async rollCall(options = {}) {
262
+ return (await this.getHotMesh(options.namespace || 'durable')).rollCall(options.delay || 1000);
263
+ }
264
+ async throttle(options) {
265
+ return (await this.getHotMesh(options.namespace || 'durable')).throttle(options);
266
+ }
267
+ async hook({ entity, id, hookEntity, hookArgs, options = {} }) {
268
+ const workflowId = MeshData.mintGuid(entity, id);
269
+ this.validate(workflowId);
270
+ return await this.getClient().workflow.hook({
271
+ namespace: options.namespace,
272
+ args: [...hookArgs, { ...options, $guid: workflowId, $type: 'hook' }],
273
+ taskQueue: options.taskQueue ?? hookEntity,
274
+ workflowName: hookEntity,
275
+ workflowId: options.workflowId ?? workflowId,
276
+ config: options.config ?? undefined,
277
+ });
278
+ }
279
+ async exec({ entity, args = [], options = {} }) {
280
+ const workflowId = MeshData.mintGuid(options.prefix ?? entity, options.id);
281
+ this.validate(workflowId);
282
+ const client = this.getClient();
283
+ try {
284
+ const handle = await client.workflow.getHandle(entity, entity, workflowId, options.namespace);
285
+ const state = await handle.hotMesh.getState(`${handle.hotMesh.appId}.execute`, handle.workflowId);
286
+ if (state?.data?.done) {
287
+ return state.data.response;
288
+ }
289
+ return await handle.result();
290
+ }
291
+ catch (e) {
292
+ const optionsClone = { ...options };
293
+ delete optionsClone.search;
294
+ delete optionsClone.config;
295
+ const handle = await client.workflow.start({
296
+ args: [...args, { ...optionsClone, $guid: workflowId, $type: 'exec' }],
297
+ taskQueue: options.taskQueue ?? entity,
298
+ workflowName: entity,
299
+ workflowId: options.workflowId ?? workflowId,
300
+ config: options.config ?? undefined,
301
+ search: options.search,
302
+ workflowTrace: options.workflowTrace,
303
+ workflowSpan: options.workflowSpan,
304
+ namespace: options.namespace,
305
+ await: options.await,
306
+ marker: options.marker,
307
+ pending: options.pending,
308
+ expire: options.expire,
309
+ signalIn: options.signalIn,
310
+ });
311
+ if (options.await === false) {
312
+ return handle.workflowId;
313
+ }
314
+ return await handle.result();
315
+ }
316
+ }
317
+ async info(entity, id, options = {}) {
318
+ const workflowId = MeshData.mintGuid(options.prefix ?? entity, id);
319
+ this.validate(workflowId);
320
+ const handle = await this.getClient().workflow.getHandle(options.taskQueue ?? entity, entity, workflowId, options.namespace);
321
+ return await handle.hotMesh.getState(`${handle.hotMesh.appId}.execute`, handle.workflowId);
322
+ }
323
+ async export(entity, id, options, namespace) {
324
+ const workflowId = MeshData.mintGuid(entity, id);
325
+ const handle = await this.getClient().workflow.getHandle(entity, entity, workflowId, namespace);
326
+ return await handle.export(options);
327
+ }
328
+ async get(entity, id, options = {}) {
329
+ const workflowId = MeshData.mintGuid(options.prefix ?? entity, id);
330
+ this.validate(workflowId);
331
+ let prefixedFields = [];
332
+ if (Array.isArray(options.fields)) {
333
+ prefixedFields = options.fields.map(field => `_${field}`);
334
+ }
335
+ else if (this.search?.schema) {
336
+ prefixedFields = Object.entries(this.search.schema).map(([key, value]) => {
337
+ return 'fieldName' in value ? value.fieldName.toString() : `_${key}`;
338
+ });
339
+ }
340
+ else {
341
+ return await this.all(entity, id, options);
342
+ }
343
+ const handle = await this.getClient().workflow.getHandle(entity, entity, workflowId, options.namespace);
344
+ const store = handle.hotMesh.engine?.store;
345
+ const jobKey = await this.mintKey(entity, workflowId, options.namespace);
346
+ const vals = await store?.exec('HMGET', jobKey, ...prefixedFields);
347
+ const result = prefixedFields.reduce((obj, field, index) => {
348
+ obj[field.substring(1)] = vals?.[index];
349
+ return obj;
350
+ }, {});
351
+ return result;
352
+ }
353
+ async all(entity, id, options = {}) {
354
+ const rawResponse = await this.raw(entity, id, options);
355
+ const responseObj = {};
356
+ for (let key in rawResponse) {
357
+ if (key.startsWith('_')) {
358
+ responseObj[key.substring(1)] = rawResponse[key];
359
+ }
360
+ }
361
+ return responseObj;
362
+ }
363
+ async raw(entity, id, options = {}) {
364
+ const workflowId = MeshData.mintGuid(options.prefix ?? entity, id);
365
+ this.validate(workflowId);
366
+ const handle = await this.getClient().workflow.getHandle(entity, entity, workflowId, options.namespace);
367
+ const store = handle.hotMesh.engine?.store;
368
+ const jobKey = await this.mintKey(entity, workflowId, options.namespace);
369
+ const rawResponse = await store?.exec('HGETALL', jobKey);
370
+ const responseObj = {};
371
+ for (let i = 0; i < rawResponse.length; i += 2) {
372
+ responseObj[rawResponse[i]] = rawResponse[i + 1];
373
+ }
374
+ return responseObj;
375
+ }
376
+ async set(entity, id, options = {}) {
377
+ const workflowId = MeshData.mintGuid(options.prefix ?? entity, id);
378
+ this.validate(workflowId);
379
+ const handle = await this.getClient().workflow.getHandle(entity, entity, workflowId, options.namespace);
380
+ const store = handle.hotMesh.engine?.store;
381
+ const jobId = await this.mintKey(entity, workflowId, options.namespace);
382
+ const safeArgs = [];
383
+ for (let key in options.search?.data) {
384
+ safeArgs.push(this.safeKey(key), options.search?.data[key].toString());
385
+ }
386
+ return await store?.exec('HSET', jobId, ...safeArgs);
387
+ }
388
+ async incr(entity, id, field, amount, options = {}) {
389
+ const workflowId = MeshData.mintGuid(options.prefix ?? entity, id);
390
+ this.validate(workflowId);
391
+ const handle = await this.getClient().workflow.getHandle(entity, entity, workflowId, options.namespace);
392
+ const store = handle.hotMesh.engine?.store;
393
+ const jobId = await this.mintKey(entity, workflowId, options.namespace);
394
+ const result = await store?.exec('HINCRBYFLOAT', jobId, this.safeKey(field), amount.toString());
395
+ return Number(result);
396
+ }
397
+ async del(entity, id, options) {
398
+ const workflowId = MeshData.mintGuid(options.prefix ?? entity, id);
399
+ this.validate(workflowId);
400
+ if (!Array.isArray(options.fields)) {
401
+ throw "Invalid arguments [options.fields is not an array]";
402
+ }
403
+ const prefixedFields = options.fields.map(field => `_${field}`);
404
+ const handle = await this.getClient().workflow.getHandle(entity, entity, workflowId, options.namespace);
405
+ const store = handle.hotMesh.engine?.store;
406
+ const jobKey = await this.mintKey(entity, workflowId, options.namespace);
407
+ const count = await store?.exec('HDEL', jobKey, ...prefixedFields);
408
+ return Number(count);
409
+ }
410
+ async findJobs(options = {}) {
411
+ const hotMesh = await this.getHotMesh(options.namespace);
412
+ return await hotMesh.engine?.store?.findJobs(options.match, options.limit, options.batch, options.cursor);
413
+ }
414
+ async find(entity, options, ...args) {
415
+ return await this.getClient().workflow.search(options.taskQueue ?? entity, options.workflowName ?? entity, options.namespace || 'durable', options.index ?? options.search?.index ?? this.search.index ?? '', ...args);
416
+ }
417
+ async findWhere(entity, options) {
418
+ const targetSearch = options.options?.search ?? this.search;
419
+ const args = [this.generateSearchQuery(options.query, targetSearch)];
420
+ if (options.count) {
421
+ args.push('LIMIT', '0', '0');
422
+ }
423
+ else {
424
+ args.push('RETURN');
425
+ args.push(((options.return?.length ?? 0) + 1).toString());
426
+ args.push('$');
427
+ options.return?.forEach(returnField => {
428
+ if (returnField.startsWith('"')) {
429
+ args.push(returnField.slice(1, -1));
430
+ }
431
+ else {
432
+ args.push(`_${returnField}`);
433
+ }
434
+ });
435
+ if (options.limit) {
436
+ args.push('LIMIT', options.limit.start.toString(), options.limit.size.toString());
437
+ }
438
+ }
439
+ const FTResults = await this.find(entity, options.options ?? {}, ...args);
440
+ const count = FTResults[0];
441
+ const sargs = `FT.SEARCH ${options.options?.index ?? targetSearch?.index} ${args.join(' ')}`;
442
+ if (options.count) {
443
+ return !isNaN(count) || count > 0 ? count : 0;
444
+ }
445
+ else if (count === 0) {
446
+ return { count, query: sargs, data: [] };
447
+ }
448
+ const hashes = this.arrayToHash(FTResults);
449
+ return { count, query: sargs, data: hashes };
450
+ }
451
+ generateSearchQuery(query, search) {
452
+ if (!Array.isArray(query) || query.length === 0) {
453
+ return typeof (query) === 'string' ? query : '*';
454
+ }
455
+ let queryString = query.map(q => {
456
+ const { field, is, value, type } = q;
457
+ let prefixedFieldName;
458
+ if (search?.schema && field in search.schema) {
459
+ if ('fieldName' in search.schema[field]) {
460
+ prefixedFieldName = `@${search.schema[field].fieldName}`;
461
+ }
462
+ else {
463
+ prefixedFieldName = `@_${field}`;
464
+ }
465
+ }
466
+ else {
467
+ prefixedFieldName = `@${field}`;
468
+ }
469
+ const fieldType = search?.schema?.[field]?.type ?? type ?? 'TEXT';
470
+ switch (fieldType) {
471
+ case 'TAG':
472
+ return `${prefixedFieldName}:{${value}}`;
473
+ case 'TEXT':
474
+ return `${prefixedFieldName}:"${value}"`;
475
+ case 'NUMERIC':
476
+ let range = '';
477
+ if (is.startsWith('=')) {
478
+ range = `[${value} ${value}]`;
479
+ }
480
+ else if (is.startsWith('<')) {
481
+ range = `[-inf ${value}]`;
482
+ }
483
+ else if (is.startsWith('>')) {
484
+ range = `[${value} +inf]`;
485
+ }
486
+ else if (is === '[]') {
487
+ range = `[${value[0]} ${value[1]}]`;
488
+ }
489
+ return `${prefixedFieldName}:${range}`;
490
+ default:
491
+ return '';
492
+ }
493
+ }).join(' ');
494
+ return queryString;
495
+ }
496
+ async createSearchIndex(entity, options = {}, searchOptions) {
497
+ const workflowTopic = `${options.taskQueue ?? entity}-${entity}`;
498
+ const hotMeshClient = await this.getClient().getHotMeshClient(workflowTopic, options.namespace);
499
+ return await meshflow_1.MeshFlow.Search.configureSearchIndex(hotMeshClient, searchOptions ?? this.search);
500
+ }
501
+ async listSearchIndexes() {
502
+ const hotMeshClient = await this.getHotMesh();
503
+ return await meshflow_1.MeshFlow.Search.listSearchIndexes(hotMeshClient);
504
+ }
505
+ static async shutdown() {
506
+ await meshflow_1.MeshFlow.shutdown();
507
+ }
508
+ }
509
+ exports.MeshData = MeshData;
510
+ _a = MeshData;
511
+ MeshData.workflow = {
512
+ sleep: meshflow_1.MeshFlow.workflow.sleepFor,
513
+ sleepFor: meshflow_1.MeshFlow.workflow.sleepFor,
514
+ signal: meshflow_1.MeshFlow.workflow.signal,
515
+ hook: meshflow_1.MeshFlow.workflow.hook,
516
+ waitForSignal: meshflow_1.MeshFlow.workflow.waitFor,
517
+ waitFor: meshflow_1.MeshFlow.workflow.waitFor,
518
+ getHotMesh: meshflow_1.MeshFlow.workflow.getHotMesh,
519
+ random: meshflow_1.MeshFlow.workflow.random,
520
+ search: meshflow_1.MeshFlow.workflow.search,
521
+ getContext: meshflow_1.MeshFlow.workflow.getContext,
522
+ once: meshflow_1.MeshFlow.workflow.once,
523
+ interrupt: async (entity, id, options = {}) => {
524
+ const jobId = MeshData.mintGuid(entity, id);
525
+ await meshflow_1.MeshFlow.workflow.interrupt(jobId, options);
526
+ },
527
+ execChild: async (options = {}) => {
528
+ const pluckOptions = { ...options, args: [...options.args, { $type: 'exec' }] };
529
+ return meshflow_1.MeshFlow.workflow.execChild(pluckOptions);
530
+ },
531
+ executeChild: async (options = {}) => {
532
+ const pluckOptions = { ...options, args: [...options.args, { $type: 'exec' }] };
533
+ return meshflow_1.MeshFlow.workflow.execChild(pluckOptions);
534
+ },
535
+ startChild: async (options = {}) => {
536
+ const pluckOptions = { ...options, args: [...options.args, { $type: 'exec' }] };
537
+ return meshflow_1.MeshFlow.workflow.startChild(pluckOptions);
538
+ },
539
+ };
540
+ MeshData.proxyActivities = meshflow_1.MeshFlow.workflow.proxyActivities;
541
+ ;
@@ -0,0 +1,18 @@
1
+ import { HotMesh } from '../hotmesh';
2
+ import { ClientConfig, ClientWorkflow, Connection, WorkflowOptions } from '../../types/meshflow';
3
+ export declare class ClientService {
4
+ connection: Connection;
5
+ options: WorkflowOptions;
6
+ static topics: string[];
7
+ static instances: Map<string, HotMesh | Promise<HotMesh>>;
8
+ constructor(config: ClientConfig);
9
+ getHotMeshClient: (workflowTopic: string | null, namespace?: string) => Promise<HotMesh>;
10
+ static createStream: (hotMeshClient: HotMesh, workflowTopic: string, namespace?: string) => Promise<void>;
11
+ verifyStream: (hotMeshClient: HotMesh, workflowTopic: string, namespace?: string) => Promise<void>;
12
+ search: (hotMeshClient: HotMesh, index: string, query: string[]) => Promise<string[]>;
13
+ workflow: ClientWorkflow;
14
+ deployAndActivate(namespace?: string, version?: string): Promise<void>;
15
+ verifyWorkflowActive(hotMesh: HotMesh, appId?: string, count?: number): Promise<boolean>;
16
+ activateWorkflow(hotMesh: HotMesh, appId?: string, version?: string): Promise<void>;
17
+ static shutdown(): Promise<void>;
18
+ }
@@ -17,8 +17,6 @@ const factory_1 = require("./schemas/factory");
17
17
  class ClientService {
18
18
  constructor(config) {
19
19
  this.getHotMeshClient = async (workflowTopic, namespace) => {
20
- //namespace isolation requires the connection options to be hashed
21
- //as multiple intersecting databases can be used by the same service
22
20
  const optionsHash = (0, utils_1.hashOptions)(this.connection.options);
23
21
  const targetNS = namespace ?? factory_1.APP_ID;
24
22
  const connectionNS = `${optionsHash}.${targetNS}`;
@@ -27,8 +25,7 @@ class ClientService {
27
25
  await this.verifyWorkflowActive(hotMeshClient, targetNS);
28
26
  return hotMeshClient;
29
27
  }
30
- //create and cache an instance
31
- const hotMeshClient = hotmesh_1.HotMeshService.init({
28
+ const hotMeshClient = hotmesh_1.HotMesh.init({
32
29
  appId: targetNS,
33
30
  logLevel: enums_1.HMSH_LOGLEVEL,
34
31
  engine: {
@@ -42,11 +39,6 @@ class ClientService {
42
39
  await this.activateWorkflow(await hotMeshClient, targetNS);
43
40
  return hotMeshClient;
44
41
  };
45
- /**
46
- * It is possible for a client to invoke a workflow without first
47
- * creating the stream. This method will verify that the stream
48
- * exists and if not, create it.
49
- */
50
42
  this.verifyStream = async (hotMeshClient, workflowTopic, namespace) => {
51
43
  const optionsHash = (0, utils_1.hashOptions)(this.connection.options);
52
44
  const targetNS = namespace ?? factory_1.APP_ID;
@@ -69,10 +61,8 @@ class ClientService {
69
61
  const workflowName = options.entity ?? options.workflowName;
70
62
  const trc = options.workflowTrace;
71
63
  const spn = options.workflowSpan;
72
- //hotmesh topic is a combination of the durable queue+workflowname
73
64
  const workflowTopic = `${taskQueueName}-${workflowName}`;
74
65
  const hotMeshClient = await this.getHotMeshClient(workflowTopic, options.namespace);
75
- //verify that the stream channel exists before enqueueing
76
66
  await this.verifyStream(hotMeshClient, workflowTopic, options.namespace);
77
67
  const payload = {
78
68
  arguments: [...options.args],
@@ -80,11 +70,11 @@ class ClientService {
80
70
  expire: options.expire ?? enums_1.HMSH_EXPIRE_JOB_SECONDS,
81
71
  signalIn: options.signalIn,
82
72
  parentWorkflowId: options.parentWorkflowId,
83
- workflowId: options.workflowId || hotmesh_1.HotMeshService.guid(),
73
+ workflowId: options.workflowId || hotmesh_1.HotMesh.guid(),
84
74
  workflowTopic: workflowTopic,
85
- backoffCoefficient: options.config?.backoffCoefficient || enums_1.HMSH_DURABLE_EXP_BACKOFF,
86
- maximumAttempts: options.config?.maximumAttempts || enums_1.HMSH_DURABLE_MAX_ATTEMPTS,
87
- maximumInterval: (0, ms_1.default)(options.config?.maximumInterval || enums_1.HMSH_DURABLE_MAX_INTERVAL) /
75
+ backoffCoefficient: options.config?.backoffCoefficient || enums_1.HMSH_MESHFLOW_EXP_BACKOFF,
76
+ maximumAttempts: options.config?.maximumAttempts || enums_1.HMSH_MESHFLOW_MAX_ATTEMPTS,
77
+ maximumInterval: (0, ms_1.default)(options.config?.maximumInterval || enums_1.HMSH_MESHFLOW_MAX_INTERVAL) /
88
78
  1000,
89
79
  };
90
80
  const context = { metadata: { trc, spn }, data: {} };
@@ -95,35 +85,25 @@ class ClientService {
95
85
  });
96
86
  return new handle_1.WorkflowHandleService(hotMeshClient, workflowTopic, jobId);
97
87
  },
98
- /**
99
- * send a message to a running workflow that is paused and awaiting the signal
100
- */
101
88
  signal: async (signalId, data, namespace) => {
102
89
  const topic = `${namespace ?? factory_1.APP_ID}.wfs.signal`;
103
90
  return await (await this.getHotMeshClient(topic, namespace)).hook(topic, { id: signalId, data });
104
91
  },
105
- /**
106
- * send a message to spawn an parallel in-process thread of execution
107
- * with the same job state as the main thread but bound to a different
108
- * handler function. All job state will be journaled to the same hash
109
- * as is used by the main thread.
110
- */
111
92
  hook: async (options) => {
112
93
  const workflowTopic = `${options.taskQueue}-${options.workflowName}`;
113
94
  const payload = {
114
95
  arguments: [...options.args],
115
96
  id: options.workflowId,
116
97
  workflowTopic,
117
- backoffCoefficient: options.config?.backoffCoefficient || enums_1.HMSH_DURABLE_EXP_BACKOFF,
118
- maximumAttempts: options.config?.maximumAttempts || enums_1.HMSH_DURABLE_MAX_ATTEMPTS,
119
- maximumInterval: (0, ms_1.default)(options.config?.maximumInterval || enums_1.HMSH_DURABLE_MAX_INTERVAL) /
98
+ backoffCoefficient: options.config?.backoffCoefficient || enums_1.HMSH_MESHFLOW_EXP_BACKOFF,
99
+ maximumAttempts: options.config?.maximumAttempts || enums_1.HMSH_MESHFLOW_MAX_ATTEMPTS,
100
+ maximumInterval: (0, ms_1.default)(options.config?.maximumInterval || enums_1.HMSH_MESHFLOW_MAX_INTERVAL) /
120
101
  1000,
121
102
  };
122
- //seed search data if presentthe hook before entering
123
103
  const hotMeshClient = await this.getHotMeshClient(workflowTopic, options.namespace);
124
104
  const msgId = await hotMeshClient.hook(`${hotMeshClient.appId}.flow.signal`, payload, types_1.StreamStatus.PENDING, 202);
125
105
  if (options.search?.data) {
126
- const searchSessionId = `-search-${hotmesh_1.HotMeshService.guid()}-0`;
106
+ const searchSessionId = `-search-${hotmesh_1.HotMesh.guid()}-0`;
127
107
  const search = new search_1.Search(options.workflowId, hotMeshClient, searchSessionId);
128
108
  const entries = Object.entries(options.search.data).flat();
129
109
  await search.set(...entries);
@@ -151,10 +131,6 @@ class ClientService {
151
131
  };
152
132
  this.connection = config.connection;
153
133
  }
154
- /**
155
- * Any point of presence can be used to deploy and activate the HotMesh
156
- * distributed executable to the active quorum.
157
- */
158
134
  async deployAndActivate(namespace = factory_1.APP_ID, version = factory_1.APP_VERSION) {
159
135
  if (isNaN(Number(version))) {
160
136
  throw new Error('Invalid version number');
@@ -208,12 +184,6 @@ class ClientService {
208
184
  _a = ClientService;
209
185
  ClientService.topics = [];
210
186
  ClientService.instances = new Map();
211
- /**
212
- * Creates a stream (Redis `XGROUP.CREATE`) where events can be published (XADD).
213
- * It is possible that the worker that will read from this stream channel
214
- * has not yet been initialized, so this call ensures that the channel
215
- * exists and is ready to serve as a container for events.
216
- */
217
187
  ClientService.createStream = async (hotMeshClient, workflowTopic, namespace) => {
218
188
  const store = hotMeshClient.engine.store;
219
189
  const params = { appId: namespace ?? factory_1.APP_ID, topic: workflowTopic };
@@ -222,7 +192,6 @@ ClientService.createStream = async (hotMeshClient, workflowTopic, namespace) =>
222
192
  await store.xgroup('CREATE', streamKey, 'WORKER', '$', 'MKSTREAM');
223
193
  }
224
194
  catch (err) {
225
- //ignore if already exists
226
195
  }
227
196
  };
228
197
  exports.ClientService = ClientService;