@hotmeshio/hotmesh 0.0.52 → 0.0.54

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 +22 -18
  2. package/build/index.d.ts +1 -2
  3. package/build/index.js +1 -3
  4. package/build/modules/enums.d.ts +8 -3
  5. package/build/modules/enums.js +16 -8
  6. package/build/modules/errors.d.ts +58 -20
  7. package/build/modules/errors.js +90 -33
  8. package/build/package.json +7 -2
  9. package/build/services/activities/activity.d.ts +8 -0
  10. package/build/services/activities/activity.js +63 -14
  11. package/build/services/activities/await.js +6 -6
  12. package/build/services/activities/cycle.d.ts +2 -2
  13. package/build/services/activities/cycle.js +5 -5
  14. package/build/services/activities/hook.js +9 -5
  15. package/build/services/activities/interrupt.d.ts +3 -3
  16. package/build/services/activities/interrupt.js +15 -6
  17. package/build/services/activities/signal.d.ts +2 -2
  18. package/build/services/activities/signal.js +4 -4
  19. package/build/services/activities/trigger.d.ts +5 -2
  20. package/build/services/activities/trigger.js +34 -4
  21. package/build/services/activities/worker.js +6 -6
  22. package/build/services/compiler/deployer.js +33 -5
  23. package/build/services/compiler/validator.d.ts +2 -0
  24. package/build/services/compiler/validator.js +5 -1
  25. package/build/services/durable/client.d.ts +7 -1
  26. package/build/services/durable/client.js +57 -38
  27. package/build/services/durable/exporter.d.ts +27 -81
  28. package/build/services/durable/exporter.js +153 -325
  29. package/build/services/durable/handle.d.ts +13 -8
  30. package/build/services/durable/handle.js +61 -48
  31. package/build/services/durable/index.d.ts +0 -2
  32. package/build/services/durable/index.js +0 -2
  33. package/build/services/durable/schemas/factory.d.ts +33 -0
  34. package/build/services/durable/schemas/factory.js +2356 -0
  35. package/build/services/durable/search.js +8 -8
  36. package/build/services/durable/worker.js +117 -25
  37. package/build/services/durable/workflow.d.ts +67 -52
  38. package/build/services/durable/workflow.js +322 -306
  39. package/build/services/engine/index.d.ts +2 -2
  40. package/build/services/engine/index.js +5 -2
  41. package/build/services/exporter/index.d.ts +2 -4
  42. package/build/services/exporter/index.js +4 -5
  43. package/build/services/hotmesh/index.d.ts +2 -2
  44. package/build/services/hotmesh/index.js +2 -2
  45. package/build/services/mapper/index.d.ts +6 -2
  46. package/build/services/mapper/index.js +6 -2
  47. package/build/services/pipe/functions/array.d.ts +2 -10
  48. package/build/services/pipe/functions/array.js +30 -28
  49. package/build/services/pipe/functions/conditional.d.ts +1 -0
  50. package/build/services/pipe/functions/conditional.js +3 -0
  51. package/build/services/pipe/functions/date.d.ts +1 -0
  52. package/build/services/pipe/functions/date.js +4 -0
  53. package/build/services/pipe/functions/index.d.ts +2 -0
  54. package/build/services/pipe/functions/index.js +2 -0
  55. package/build/services/pipe/functions/logical.d.ts +5 -0
  56. package/build/services/pipe/functions/logical.js +12 -0
  57. package/build/services/pipe/functions/object.d.ts +3 -0
  58. package/build/services/pipe/functions/object.js +25 -7
  59. package/build/services/pipe/index.d.ts +20 -3
  60. package/build/services/pipe/index.js +82 -16
  61. package/build/services/router/index.js +14 -3
  62. package/build/services/serializer/index.d.ts +3 -2
  63. package/build/services/serializer/index.js +11 -4
  64. package/build/services/store/clients/ioredis.js +6 -6
  65. package/build/services/store/clients/redis.js +7 -7
  66. package/build/services/store/index.d.ts +2 -0
  67. package/build/services/store/index.js +4 -1
  68. package/build/services/stream/clients/ioredis.js +8 -8
  69. package/build/services/stream/clients/redis.js +1 -1
  70. package/build/types/activity.d.ts +60 -5
  71. package/build/types/durable.d.ts +183 -36
  72. package/build/types/error.d.ts +48 -0
  73. package/build/types/error.js +2 -0
  74. package/build/types/exporter.d.ts +35 -7
  75. package/build/types/index.d.ts +4 -3
  76. package/build/types/job.d.ts +93 -6
  77. package/build/types/pipe.d.ts +81 -3
  78. package/build/types/stream.d.ts +61 -1
  79. package/build/types/stream.js +4 -0
  80. package/index.ts +1 -2
  81. package/modules/enums.ts +16 -8
  82. package/modules/errors.ts +139 -34
  83. package/package.json +7 -2
  84. package/services/activities/activity.ts +63 -14
  85. package/services/activities/await.ts +6 -6
  86. package/services/activities/cycle.ts +7 -6
  87. package/services/activities/hook.ts +12 -5
  88. package/services/activities/interrupt.ts +19 -9
  89. package/services/activities/signal.ts +6 -5
  90. package/services/activities/trigger.ts +43 -6
  91. package/services/activities/worker.ts +7 -7
  92. package/services/compiler/deployer.ts +33 -6
  93. package/services/compiler/validator.ts +7 -3
  94. package/services/durable/client.ts +49 -22
  95. package/services/durable/exporter.ts +162 -349
  96. package/services/durable/handle.ts +66 -53
  97. package/services/durable/index.ts +0 -2
  98. package/services/durable/schemas/factory.ts +2358 -0
  99. package/services/durable/search.ts +8 -8
  100. package/services/durable/worker.ts +128 -29
  101. package/services/durable/workflow.ts +371 -322
  102. package/services/engine/index.ts +8 -3
  103. package/services/exporter/index.ts +10 -12
  104. package/services/hotmesh/index.ts +4 -3
  105. package/services/mapper/index.ts +6 -2
  106. package/services/pipe/functions/array.ts +24 -37
  107. package/services/pipe/functions/conditional.ts +4 -0
  108. package/services/pipe/functions/date.ts +6 -0
  109. package/services/pipe/functions/index.ts +7 -5
  110. package/services/pipe/functions/logical.ts +11 -0
  111. package/services/pipe/functions/object.ts +26 -7
  112. package/services/pipe/index.ts +99 -21
  113. package/services/quorum/index.ts +1 -3
  114. package/services/router/index.ts +14 -3
  115. package/services/serializer/index.ts +12 -5
  116. package/services/store/clients/ioredis.ts +6 -6
  117. package/services/store/clients/redis.ts +7 -7
  118. package/services/store/index.ts +4 -1
  119. package/services/stream/clients/ioredis.ts +8 -8
  120. package/services/stream/clients/redis.ts +1 -1
  121. package/types/activity.ts +87 -15
  122. package/types/durable.ts +263 -75
  123. package/types/error.ts +52 -0
  124. package/types/exporter.ts +43 -9
  125. package/types/index.ts +14 -8
  126. package/types/job.ts +157 -36
  127. package/types/pipe.ts +84 -3
  128. package/types/stream.ts +82 -23
  129. package/build/services/durable/factory.d.ts +0 -17
  130. package/build/services/durable/factory.js +0 -817
  131. package/build/services/durable/meshos.d.ts +0 -127
  132. package/build/services/durable/meshos.js +0 -380
  133. package/services/durable/factory.ts +0 -818
  134. package/services/durable/meshos.ts +0 -441
@@ -83,22 +83,22 @@ class Activity {
83
83
  }
84
84
  catch (error) {
85
85
  if (error instanceof errors_1.CollationError) {
86
- this.logger.info('process-event-inactive-error', { error });
86
+ this.logger.info('process-event-inactive-error', { ...error });
87
87
  return;
88
88
  }
89
89
  else if (error instanceof errors_1.InactiveJobError) {
90
- this.logger.info('process-event-inactive-job-error', { error });
90
+ this.logger.info('process-event-inactive-job-error', { ...error });
91
91
  return;
92
92
  }
93
93
  else if (error instanceof errors_1.GenerationalError) {
94
- this.logger.info('process-event-generational-job-error', { error });
94
+ this.logger.info('process-event-generational-job-error', { ...error });
95
95
  return;
96
96
  }
97
97
  else if (error instanceof errors_1.GetStateError) {
98
- this.logger.info('process-event-get-job-error', { error });
98
+ this.logger.info('process-event-get-job-error', { ...error });
99
99
  return;
100
100
  }
101
- this.logger.error('activity-process-event-error', { error });
101
+ this.logger.error('activity-process-event-error', { ...error, message: error.message, stack: error.stack, name: error.name });
102
102
  telemetry && telemetry.setActivityError(error.message);
103
103
  throw error;
104
104
  }
@@ -130,6 +130,10 @@ class Activity {
130
130
  async processError(telemetry, type) {
131
131
  this.bindActivityError(this.data);
132
132
  this.adjacencyList = await this.filterAdjacent();
133
+ if (!this.adjacencyList.length) {
134
+ this.bindJobError(this.data);
135
+ }
136
+ this.mapJobData();
133
137
  const multi = this.store.getMulti();
134
138
  await this.setState(multi);
135
139
  await collator_1.CollatorService.notarizeCompletion(this, multi);
@@ -142,7 +146,7 @@ class Activity {
142
146
  const attrs = { 'app.job.jss': jobStatus };
143
147
  //adjacencyList membership has already been set at this point (according to activity status)
144
148
  const messageIds = await this.transition(this.adjacencyList, jobStatus);
145
- if (messageIds.length) {
149
+ if (messageIds?.length) {
146
150
  attrs['app.activity.mids'] = messageIds.join(',');
147
151
  }
148
152
  telemetry.setActivityAttributes(attrs);
@@ -159,7 +163,31 @@ class Activity {
159
163
  mapJobData() {
160
164
  if (this.config.job?.maps) {
161
165
  const mapper = new mapper_1.MapperService(this.config.job.maps, this.context);
162
- this.context.data = mapper.mapRules();
166
+ const output = mapper.mapRules();
167
+ if (output) {
168
+ for (const key in output) {
169
+ const f1 = key.indexOf('[');
170
+ //keys with array notation suffix `somekey[]` represent
171
+ //dynamically-keyed mappings whose `value` must be moved to the output.
172
+ //The `value` must be an object with keys appropriate to the
173
+ //notation type: `somekey[0] (array)`, `somekey[-] (mark)`, OR `somekey[_] (search)`
174
+ if (f1 > -1) {
175
+ const amount = key.substring(f1 + 1).split(']')[0];
176
+ if (!isNaN(Number(amount))) {
177
+ const left = key.substring(0, f1);
178
+ output[left] = output[key];
179
+ delete output[key];
180
+ }
181
+ else if (amount === '-' || amount === '_') {
182
+ const obj = output[key];
183
+ Object.keys(obj).forEach((newKey) => {
184
+ output[newKey] = obj[newKey];
185
+ });
186
+ }
187
+ }
188
+ }
189
+ }
190
+ this.context.data = output;
163
191
  }
164
192
  }
165
193
  mapInputData() {
@@ -181,10 +209,21 @@ class Activity {
181
209
  async registerTimeout() {
182
210
  //set timeout in support of hook and/or duplex
183
211
  }
212
+ /**
213
+ * Any StreamMessage with a status of ERROR is bound to the activity
214
+ */
184
215
  bindActivityError(data) {
185
- //todo: map activity error data into the job error (if defined)
186
- // map job status via: (500: [3**, 4**, 5**], 202: [$pending])
187
- this.context.metadata.err = JSON.stringify(data);
216
+ const md = this.context[this.metadata.aid].output.metadata;
217
+ md.err = JSON.stringify(this.data);
218
+ //(temporary...useful for mapping error parts in the app.yaml)
219
+ md.$error = { ...data, is_stream_error: true };
220
+ }
221
+ /**
222
+ * unhandled activity errors (activities that return an ERROR StreamMessage
223
+ * status and have no adjacent children to transition to) are bound to the job
224
+ */
225
+ bindJobError(data) {
226
+ this.context.metadata.err = JSON.stringify({ ...data, is_stream_error: true });
188
227
  }
189
228
  async getTriggerConfig() {
190
229
  return await this.store.getSchema(this.config.trigger, await this.engine.getVID());
@@ -238,9 +277,9 @@ class Activity {
238
277
  if (this.status === stream_1.StreamStatus.ERROR) {
239
278
  self.output.metadata.err = JSON.stringify(this.data);
240
279
  }
241
- //todo: verify leg2 never overwrites leg1 `ac`
242
- self.output.metadata.ac =
243
- self.output.metadata.au = (0, utils_1.formatISODate)(new Date());
280
+ const ts = (0, utils_1.formatISODate)(new Date());
281
+ self.output.metadata.ac = ts;
282
+ self.output.metadata.au = ts;
244
283
  self.output.metadata.atp = this.config.type;
245
284
  if (this.config.subtype) {
246
285
  self.output.metadata.stp = this.config.subtype;
@@ -259,6 +298,11 @@ class Activity {
259
298
  state[path] = value;
260
299
  }
261
300
  }
301
+ for (let key in this.context?.data ?? {}) {
302
+ if (key.startsWith('-') || key.startsWith('_')) {
303
+ state[key] = this.context.data[key];
304
+ }
305
+ }
262
306
  telemetry_1.TelemetryService.bindJobTelemetryToState(state, this.config, this.context);
263
307
  }
264
308
  bindActivityState(state) {
@@ -310,7 +354,7 @@ class Activity {
310
354
  let { dad, jid } = this.context.metadata;
311
355
  const dIds = collator_1.CollatorService.getDimensionsById([...this.config.ancestors, this.metadata.aid], dad || '');
312
356
  //`state` is a unidimensional hash; context is a tree
313
- const [state, status] = await this.store.getState(jid, consumes, dIds);
357
+ const [state, _status] = await this.store.getState(jid, consumes, dIds);
314
358
  this.context = (0, utils_1.restoreHierarchy)(state);
315
359
  this.assertGenerationalId(this.context?.metadata?.gid, gid);
316
360
  this.initDimensionalAddress(dad);
@@ -346,6 +390,11 @@ class Activity {
346
390
  if (!self.hook) {
347
391
  self.hook = {};
348
392
  }
393
+ if (!self.output.metadata) {
394
+ self.output.metadata = {};
395
+ }
396
+ //prebind the updated timestamp (mappings need the time)
397
+ self.output.metadata.au = (0, utils_1.formatISODate)(new Date());
349
398
  context['$self'] = self;
350
399
  context['$job'] = context; //NEVER call STRINGIFY! (now circular)
351
400
  return context;
@@ -2,12 +2,12 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Await = void 0;
4
4
  const errors_1 = require("../../modules/errors");
5
+ const utils_1 = require("../../modules/utils");
5
6
  const activity_1 = require("./activity");
6
7
  const collator_1 = require("../collator");
8
+ const pipe_1 = require("../pipe");
7
9
  const telemetry_1 = require("../telemetry");
8
10
  const stream_1 = require("../../types/stream");
9
- const pipe_1 = require("../pipe");
10
- const utils_1 = require("../../modules/utils");
11
11
  class Await extends activity_1.Activity {
12
12
  constructor(config, data, metadata, hook, engine, context) {
13
13
  super(config, data, metadata, hook, engine, context);
@@ -40,19 +40,19 @@ class Await extends activity_1.Activity {
40
40
  }
41
41
  catch (error) {
42
42
  if (error instanceof errors_1.InactiveJobError) {
43
- this.logger.error('await-inactive-job-error', { error });
43
+ this.logger.error('await-inactive-job-error', { ...error });
44
44
  return;
45
45
  }
46
46
  else if (error instanceof errors_1.GenerationalError) {
47
- this.logger.info('process-event-generational-job-error', { error });
47
+ this.logger.info('process-event-generational-job-error', { ...error });
48
48
  return;
49
49
  }
50
50
  else if (error instanceof errors_1.GetStateError) {
51
- this.logger.error('await-get-state-error', { error });
51
+ this.logger.error('await-get-state-error', { ...error });
52
52
  return;
53
53
  }
54
54
  else {
55
- this.logger.error('await-process-error', { error });
55
+ this.logger.error('await-process-error', { ...error });
56
56
  }
57
57
  telemetry.setActivityError(error.message);
58
58
  throw error;
@@ -1,6 +1,6 @@
1
1
  import { EngineService } from '../engine';
2
- import { Activity, ActivityType } from './activity';
3
- import { ActivityData, ActivityMetadata, CycleActivity } from '../../types/activity';
2
+ import { Activity } from './activity';
3
+ import { ActivityData, ActivityMetadata, ActivityType, CycleActivity } from '../../types/activity';
4
4
  import { JobState } from '../../types/job';
5
5
  import { RedisMulti } from '../../types/redis';
6
6
  declare class Cycle extends Activity {
@@ -2,10 +2,10 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Cycle = void 0;
4
4
  const errors_1 = require("../../modules/errors");
5
+ const utils_1 = require("../../modules/utils");
5
6
  const collator_1 = require("../collator");
6
7
  const activity_1 = require("./activity");
7
8
  const telemetry_1 = require("../telemetry");
8
- const utils_1 = require("../../modules/utils");
9
9
  class Cycle extends activity_1.Activity {
10
10
  constructor(config, data, metadata, hook, engine, context) {
11
11
  super(config, data, metadata, hook, engine, context);
@@ -40,19 +40,19 @@ class Cycle extends activity_1.Activity {
40
40
  }
41
41
  catch (error) {
42
42
  if (error instanceof errors_1.InactiveJobError) {
43
- this.logger.error('cycle-inactive-job-error', { error });
43
+ this.logger.error('cycle-inactive-job-error', { ...error });
44
44
  return;
45
45
  }
46
46
  else if (error instanceof errors_1.GenerationalError) {
47
- this.logger.info('process-event-generational-job-error', { error });
47
+ this.logger.info('process-event-generational-job-error', { ...error });
48
48
  return;
49
49
  }
50
50
  else if (error instanceof errors_1.GetStateError) {
51
- this.logger.error('cycle-get-state-error', { error });
51
+ this.logger.error('cycle-get-state-error', { ...error });
52
52
  return;
53
53
  }
54
54
  else {
55
- this.logger.error('cycle-process-error', { error });
55
+ this.logger.error('cycle-process-error', { ...error });
56
56
  }
57
57
  telemetry.setActivityError(error.message);
58
58
  throw error;
@@ -39,19 +39,19 @@ class Hook extends activity_1.Activity {
39
39
  }
40
40
  catch (error) {
41
41
  if (error instanceof errors_1.InactiveJobError) {
42
- this.logger.error('hook-inactive-job-error', { error });
42
+ this.logger.error('hook-inactive-job-error', { ...error });
43
43
  return;
44
44
  }
45
45
  else if (error instanceof errors_1.GenerationalError) {
46
- this.logger.info('process-event-generational-job-error', { error });
46
+ this.logger.info('process-event-generational-job-error', { ...error });
47
47
  return;
48
48
  }
49
49
  else if (error instanceof errors_1.GetStateError) {
50
- this.logger.error('hook-get-state-error', { error });
50
+ this.logger.error('hook-get-state-error', { ...error });
51
51
  return;
52
52
  }
53
53
  else {
54
- this.logger.error('hook-process-error', { error });
54
+ this.logger.error('hook-process-error', { ...error });
55
55
  }
56
56
  telemetry.setActivityError(error.message);
57
57
  throw error;
@@ -65,7 +65,11 @@ class Hook extends activity_1.Activity {
65
65
  * does this activity use a time-hook or web-hook
66
66
  */
67
67
  doesHook() {
68
- return !!(this.config.hook?.topic || this.config.sleep);
68
+ if (this.config.sleep) {
69
+ const duration = pipe_1.Pipe.resolve(this.config.sleep, this.context);
70
+ return !isNaN(duration) && Number(duration) > 0;
71
+ }
72
+ return !!this.config.hook?.topic;
69
73
  }
70
74
  async doHook(telemetry) {
71
75
  const multi = this.store.getMulti();
@@ -1,8 +1,8 @@
1
+ import { Activity } from './activity';
1
2
  import { EngineService } from '../engine';
2
- import { Activity, ActivityType } from './activity';
3
- import { ActivityData, ActivityMetadata, InterruptActivity } from '../../types/activity';
4
- import { JobInterruptOptions, JobState } from '../../types/job';
5
3
  import { TelemetryService } from '../telemetry';
4
+ import { ActivityData, ActivityMetadata, ActivityType, InterruptActivity } from '../../types/activity';
5
+ import { JobInterruptOptions, JobState } from '../../types/job';
6
6
  declare class Interrupt extends Activity {
7
7
  config: InterruptActivity;
8
8
  constructor(config: ActivityType, data: ActivityData, metadata: ActivityMetadata, hook: ActivityData | null, engine: EngineService, context?: JobState);
@@ -2,10 +2,10 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Interrupt = void 0;
4
4
  const errors_1 = require("../../modules/errors");
5
- const activity_1 = require("./activity");
6
5
  const collator_1 = require("../collator");
7
- const telemetry_1 = require("../telemetry");
6
+ const activity_1 = require("./activity");
8
7
  const pipe_1 = require("../pipe");
8
+ const telemetry_1 = require("../telemetry");
9
9
  class Interrupt extends activity_1.Activity {
10
10
  constructor(config, data, metadata, hook, engine, context) {
11
11
  super(config, data, metadata, hook, engine, context);
@@ -27,19 +27,19 @@ class Interrupt extends activity_1.Activity {
27
27
  }
28
28
  catch (error) {
29
29
  if (error instanceof errors_1.InactiveJobError) {
30
- this.logger.error('interrupt-inactive-job-error', { error });
30
+ this.logger.error('interrupt-inactive-job-error', { ...error });
31
31
  return;
32
32
  }
33
33
  else if (error instanceof errors_1.GenerationalError) {
34
- this.logger.info('process-event-generational-job-error', { error });
34
+ this.logger.info('process-event-generational-job-error', { ...error });
35
35
  return;
36
36
  }
37
37
  else if (error instanceof errors_1.GetStateError) {
38
- this.logger.error('interrupt-get-state-error', { error });
38
+ this.logger.error('interrupt-get-state-error', { ...error });
39
39
  return;
40
40
  }
41
41
  else {
42
- this.logger.error('interrupt-process-error', { error });
42
+ this.logger.error('interrupt-process-error', { ...error });
43
43
  }
44
44
  telemetry.setActivityError(error.message);
45
45
  throw error;
@@ -116,6 +116,15 @@ class Interrupt extends activity_1.Activity {
116
116
  descend: this.config.descend !== undefined
117
117
  ? pipe_1.Pipe.resolve(this.config.descend, this.context)
118
118
  : undefined,
119
+ code: this.config.code !== undefined
120
+ ? pipe_1.Pipe.resolve(this.config.code, this.context)
121
+ : undefined,
122
+ expire: this.config.expire !== undefined
123
+ ? pipe_1.Pipe.resolve(this.config.expire, this.context)
124
+ : undefined,
125
+ stack: this.config.stack !== undefined
126
+ ? pipe_1.Pipe.resolve(this.config.stack, this.context)
127
+ : undefined,
119
128
  };
120
129
  }
121
130
  async interrupt() {
@@ -1,6 +1,6 @@
1
- import { Activity, ActivityType } from './activity';
1
+ import { Activity } from './activity';
2
2
  import { EngineService } from '../engine';
3
- import { ActivityData, ActivityMetadata, SignalActivity } from '../../types/activity';
3
+ import { ActivityData, ActivityMetadata, ActivityType, SignalActivity } from '../../types/activity';
4
4
  import { JobState } from '../../types/job';
5
5
  declare class Signal extends Activity {
6
6
  config: SignalActivity;
@@ -48,19 +48,19 @@ class Signal extends activity_1.Activity {
48
48
  }
49
49
  catch (error) {
50
50
  if (error instanceof errors_1.InactiveJobError) {
51
- this.logger.error('signal-inactive-job-error', { error });
51
+ this.logger.error('signal-inactive-job-error', { ...error });
52
52
  return;
53
53
  }
54
54
  else if (error instanceof errors_1.GenerationalError) {
55
- this.logger.info('process-event-generational-job-error', { error });
55
+ this.logger.info('process-event-generational-job-error', { ...error });
56
56
  return;
57
57
  }
58
58
  else if (error instanceof errors_1.GetStateError) {
59
- this.logger.error('signal-get-state-error', { error });
59
+ this.logger.error('signal-get-state-error', { ...error });
60
60
  return;
61
61
  }
62
62
  else {
63
- this.logger.error('signal-process-error', { error });
63
+ this.logger.error('signal-process-error', { ...error });
64
64
  }
65
65
  telemetry.setActivityError(error.message);
66
66
  throw error;
@@ -1,12 +1,15 @@
1
1
  import { Activity } from './activity';
2
2
  import { EngineService } from '../engine';
3
3
  import { ActivityData, ActivityMetadata, ActivityType, TriggerActivity } from '../../types/activity';
4
- import { JobState } from '../../types/job';
4
+ import { JobState, ExtensionType } from '../../types/job';
5
5
  import { RedisMulti } from '../../types/redis';
6
6
  declare class Trigger extends Activity {
7
7
  config: TriggerActivity;
8
8
  constructor(config: ActivityType, data: ActivityData, metadata: ActivityMetadata, hook: ActivityData | null, engine: EngineService, context?: JobState);
9
- process(): Promise<string>;
9
+ process(options?: ExtensionType): Promise<string>;
10
+ safeKey(key: string): string;
11
+ bindSearchData(options?: ExtensionType): void;
12
+ bindMarkerData(options?: ExtensionType): void;
10
13
  setStatus(amount: number): Promise<void>;
11
14
  execAdjacentParent(): Promise<void>;
12
15
  createInputContext(): Partial<JobState>;
@@ -13,7 +13,7 @@ class Trigger extends activity_1.Activity {
13
13
  constructor(config, data, metadata, hook, engine, context) {
14
14
  super(config, data, metadata, hook, engine, context);
15
15
  }
16
- async process() {
16
+ async process(options) {
17
17
  this.logger.debug('trigger-process', { subscribes: this.config.subscribes });
18
18
  let telemetry;
19
19
  try {
@@ -26,6 +26,8 @@ class Trigger extends activity_1.Activity {
26
26
  await this.setStateNX();
27
27
  this.adjacencyList = await this.filterAdjacent();
28
28
  await this.setStatus(this.adjacencyList.length);
29
+ this.bindSearchData(options);
30
+ this.bindMarkerData(options);
29
31
  const multi = this.store.getMulti();
30
32
  await this.setState(multi);
31
33
  await this.setStats(multi);
@@ -47,10 +49,10 @@ class Trigger extends activity_1.Activity {
47
49
  }
48
50
  catch (error) {
49
51
  if (error instanceof errors_1.DuplicateJobError) {
50
- this.logger.error('duplicate-job-error', { error });
52
+ this.logger.error('duplicate-job-error', { job_id: error.jobId });
51
53
  }
52
54
  else {
53
- this.logger.error('trigger-process-error', { error });
55
+ this.logger.error('trigger-process-error', { ...error });
54
56
  }
55
57
  telemetry.setActivityError(error.message);
56
58
  throw error;
@@ -61,12 +63,40 @@ class Trigger extends activity_1.Activity {
61
63
  this.logger.debug('trigger-process-end', { subscribes: this.config.subscribes, jid: this.context.metadata.jid, gid: this.context.metadata.gid });
62
64
  }
63
65
  }
66
+ safeKey(key) {
67
+ return `_${key}`;
68
+ }
69
+ bindSearchData(options) {
70
+ if (options?.search) {
71
+ Object.keys(options.search).forEach((key) => {
72
+ this.context.data[this.safeKey(key)] = options.search[key].toString();
73
+ });
74
+ }
75
+ }
76
+ bindMarkerData(options) {
77
+ if (options?.marker) {
78
+ Object.keys(options.marker).forEach((key) => {
79
+ if (key.startsWith('-')) {
80
+ this.context.data[key] = options.marker[key].toString();
81
+ }
82
+ });
83
+ }
84
+ }
64
85
  async setStatus(amount) {
65
86
  this.context.metadata.js = amount;
66
87
  }
67
88
  async execAdjacentParent() {
68
89
  if (this.context.metadata.px) {
69
- await this.engine.execAdjacentParent(this.context, { metadata: this.context.metadata, data: { job_id: this.context.metadata.jid } });
90
+ const timestamp = (0, utils_1.formatISODate)(new Date());
91
+ const jobStartedConfirmationMessage = {
92
+ metadata: this.context.metadata,
93
+ data: {
94
+ job_id: this.context.metadata.jid,
95
+ jc: timestamp,
96
+ ju: timestamp,
97
+ }
98
+ };
99
+ await this.engine.execAdjacentParent(this.context, jobStartedConfirmationMessage);
70
100
  }
71
101
  }
72
102
  createInputContext() {
@@ -2,11 +2,11 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Worker = void 0;
4
4
  const errors_1 = require("../../modules/errors");
5
+ const utils_1 = require("../../modules/utils");
5
6
  const activity_1 = require("./activity");
6
7
  const collator_1 = require("../collator");
7
- const telemetry_1 = require("../telemetry");
8
8
  const pipe_1 = require("../pipe");
9
- const utils_1 = require("../../modules/utils");
9
+ const telemetry_1 = require("../telemetry");
10
10
  class Worker extends activity_1.Activity {
11
11
  constructor(config, data, metadata, hook, engine, context) {
12
12
  super(config, data, metadata, hook, engine, context);
@@ -39,19 +39,19 @@ class Worker extends activity_1.Activity {
39
39
  }
40
40
  catch (error) {
41
41
  if (error instanceof errors_1.InactiveJobError) {
42
- this.logger.error('await-inactive-job-error', { error });
42
+ this.logger.error('await-inactive-job-error', { ...error });
43
43
  return;
44
44
  }
45
45
  else if (error instanceof errors_1.GenerationalError) {
46
- this.logger.info('process-event-generational-job-error', { error });
46
+ this.logger.info('process-event-generational-job-error', { ...error });
47
47
  return;
48
48
  }
49
49
  else if (error instanceof errors_1.GetStateError) {
50
- this.logger.error('worker-get-state-error', { error });
50
+ this.logger.error('worker-get-state-error', { ...error });
51
51
  return;
52
52
  }
53
53
  else {
54
- this.logger.error('worker-process-error', { error });
54
+ this.logger.error('worker-process-error', { ...error });
55
55
  }
56
56
  telemetry.setActivityError(error.message);
57
57
  throw error;
@@ -6,6 +6,7 @@ const utils_1 = require("../../modules/utils");
6
6
  const collator_1 = require("../collator");
7
7
  const serializer_1 = require("../serializer");
8
8
  const pipe_1 = require("../pipe");
9
+ const validator_1 = require("./validator");
9
10
  const DEFAULT_METADATA_RANGE_SIZE = 26; //metadata is 26 slots ([a-z] * 1)
10
11
  const DEFAULT_DATA_RANGE_SIZE = 260; //data is 260 slots ([a-zA-Z] * 5)
11
12
  const DEFAULT_RANGE_SIZE = DEFAULT_METADATA_RANGE_SIZE + DEFAULT_DATA_RANGE_SIZE;
@@ -185,6 +186,9 @@ class Deployer {
185
186
  //DAGs have one parent; easy to optimize for
186
187
  graph.activities[to].parent = fromActivity;
187
188
  }
189
+ //temporarily bind the transitions to the parent activity,
190
+ // so the consumer/producer registrar picks up the bindings
191
+ graph.activities[fromActivity].transitions = toTransitions;
188
192
  }
189
193
  }
190
194
  }
@@ -241,9 +245,30 @@ class Deployer {
241
245
  traverse(obj[key], newPath);
242
246
  }
243
247
  else {
244
- const finalPath = `data/${[...path, key].join('/')}`;
245
- if (!result.includes(finalPath)) {
246
- result.push(finalPath);
248
+ //wildcard mapping (e.g., 'friends[25]')
249
+ //when this is resolved, it will be expanded to
250
+ //`'friends/0', ..., 'friends/24'`, providing 25 dynamic
251
+ //slots in the flow's output data
252
+ const pathName = [...path, key].join('/');
253
+ if (!pathName.includes('[')) {
254
+ const finalPath = `data/${pathName}`;
255
+ if (!result.includes(finalPath)) {
256
+ result.push(finalPath);
257
+ }
258
+ }
259
+ else {
260
+ const [left, right] = pathName.split('[');
261
+ //check if this variable isLiteralKeyType (#, -, or _)
262
+ const [amount, _] = right.split(']');
263
+ if (!isNaN(parseInt(amount))) {
264
+ //loop to create all possible paths (0 to amount)
265
+ for (let i = 0; i < parseInt(amount); i++) {
266
+ const finalPath = `data/${left}/${i}`;
267
+ if (!result.includes(finalPath)) {
268
+ result.push(finalPath);
269
+ }
270
+ }
271
+ } //else ignore (amount might be '-' or '_') `-` is marker data; `_` is job data;
247
272
  }
248
273
  }
249
274
  }
@@ -271,7 +296,7 @@ class Deployer {
271
296
  if (typeof obj[key] === 'string') {
272
297
  const stringValue = obj[key];
273
298
  const dynamicMappingRuleMatch = stringValue.match(/^\{[^@].*}$/);
274
- if (dynamicMappingRuleMatch) {
299
+ if (dynamicMappingRuleMatch && !validator_1.Validator.CONTEXT_VARS.includes(stringValue)) {
275
300
  if (stringValue.split('.')[1] !== 'input') {
276
301
  dynamicMappingRules.push(stringValue);
277
302
  consumes.push(stringValue);
@@ -340,7 +365,10 @@ class Deployer {
340
365
  for (const graph of graphs) {
341
366
  const activities = graph.activities;
342
367
  for (const activityKey in activities) {
343
- activitySchemas[activityKey] = activities[activityKey];
368
+ const target = activities[activityKey];
369
+ //remove transitions; no longer necessary for runtime
370
+ delete target.transitions;
371
+ activitySchemas[activityKey] = target;
344
372
  }
345
373
  }
346
374
  await this.store.setSchemas(activitySchemas, this.getVID());
@@ -8,6 +8,7 @@ declare class Validator {
8
8
  mappingStatements: MappingStatements;
9
9
  store: StoreService<RedisClient, RedisMulti> | null;
10
10
  static SYS_VARS: string[];
11
+ static CONTEXT_VARS: string[];
11
12
  constructor(manifest: HotMeshManifest);
12
13
  /**
13
14
  * validate the manifest file
@@ -19,6 +20,7 @@ declare class Validator {
19
20
  getMappingStatements(): void;
20
21
  validateReferencedActivityIds(): void;
21
22
  isFunction(value: string): boolean;
23
+ isContextVariable(value: string): boolean;
22
24
  validateMappingStatements(): void;
23
25
  validateTransitions(): void;
24
26
  validateTransitionConditions(): void;
@@ -83,7 +83,7 @@ class Validator {
83
83
  if (statement.startsWith('{') && statement.endsWith('}')) {
84
84
  const statementParts = statement.slice(1, -1).split('.');
85
85
  const referencedActivityId = statementParts[0];
86
- if (!(Validator.SYS_VARS.includes(referencedActivityId) || activityIds.includes(referencedActivityId) || this.isFunction(statement))) {
86
+ if (!(Validator.SYS_VARS.includes(referencedActivityId) || activityIds.includes(referencedActivityId) || this.isFunction(statement) || this.isContextVariable(statement))) {
87
87
  throw new Error(`Mapping statement references non-existent activity: ${statement}`);
88
88
  }
89
89
  }
@@ -93,6 +93,9 @@ class Validator {
93
93
  isFunction(value) {
94
94
  return value.startsWith('{@') && pipe_1.Pipe.resolveFunction(value);
95
95
  }
96
+ isContextVariable(value) {
97
+ return ['{$input}', '{$output}', '{$item}', '{$key}', '{$index}'].includes(value);
98
+ }
96
99
  // 1.3) Validate the mapping/@pipe statements are valid
97
100
  validateMappingStatements() {
98
101
  // Implement the method content
@@ -132,3 +135,4 @@ class Validator {
132
135
  }
133
136
  exports.Validator = Validator;
134
137
  Validator.SYS_VARS = ['$app', '$self', '$graph', '$job'];
138
+ Validator.CONTEXT_VARS = ['{$input}', '{$output}', '{$item}', '{$key}', '{$index}'];
@@ -14,7 +14,13 @@ export declare class ClientService {
14
14
  * has not yet been initialized, so this call ensures that the channel
15
15
  * exists and is ready to serve as a container for events.
16
16
  */
17
- createStream: (hotMeshClient: HotMesh, workflowTopic: string, namespace?: string) => Promise<void>;
17
+ static createStream: (hotMeshClient: HotMesh, workflowTopic: string, namespace?: string) => Promise<void>;
18
+ /**
19
+ * It is possible for a client to invoke a workflow without first
20
+ * creating the stream. This method will verify that the stream
21
+ * exists and if not, create it.
22
+ */
23
+ static verifyStream: (workflowTopic: string, namespace?: string) => Promise<HotMesh>;
18
24
  /**
19
25
  * For those deployments with a redis stack backend (with the FT module),
20
26
  * this method will configure the search index for the workflow.