@hotmeshio/hotmesh 0.0.54 → 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 (182) hide show
  1. package/README.md +0 -3
  2. package/build/modules/enums.js +1 -10
  3. package/build/modules/key.d.ts +0 -38
  4. package/build/modules/key.js +4 -46
  5. package/build/modules/utils.d.ts +0 -8
  6. package/build/modules/utils.js +0 -14
  7. package/build/package.json +11 -4
  8. package/build/services/activities/activity.d.ts +0 -28
  9. package/build/services/activities/activity.js +1 -46
  10. package/build/services/activities/await.js +0 -4
  11. package/build/services/activities/cycle.d.ts +0 -7
  12. package/build/services/activities/cycle.js +1 -16
  13. package/build/services/activities/hook.d.ts +0 -6
  14. package/build/services/activities/hook.js +2 -12
  15. package/build/services/activities/interrupt.js +0 -8
  16. package/build/services/activities/signal.d.ts +0 -6
  17. package/build/services/activities/signal.js +0 -15
  18. package/build/services/activities/trigger.d.ts +0 -4
  19. package/build/services/activities/trigger.js +1 -7
  20. package/build/services/activities/worker.js +0 -4
  21. package/build/services/collator/index.d.ts +0 -70
  22. package/build/services/collator/index.js +1 -91
  23. package/build/services/compiler/deployer.js +6 -38
  24. package/build/services/compiler/index.d.ts +0 -15
  25. package/build/services/compiler/index.js +0 -20
  26. package/build/services/compiler/validator.d.ts +0 -3
  27. package/build/services/compiler/validator.js +0 -25
  28. package/build/services/connector/clients/ioredis.d.ts +2 -2
  29. package/build/services/connector/clients/ioredis.js +0 -2
  30. package/build/services/connector/clients/redis.d.ts +4 -4
  31. package/build/services/connector/clients/redis.js +1 -3
  32. package/build/services/connector/index.d.ts +1 -1
  33. package/build/services/connector/index.js +0 -2
  34. package/build/services/durable/client.d.ts +1 -26
  35. package/build/services/durable/client.js +0 -56
  36. package/build/services/durable/exporter.d.ts +0 -22
  37. package/build/services/durable/exporter.js +1 -30
  38. package/build/services/durable/handle.d.ts +0 -36
  39. package/build/services/durable/handle.js +0 -46
  40. package/build/services/durable/index.d.ts +0 -4
  41. package/build/services/durable/index.js +0 -4
  42. package/build/services/durable/schemas/factory.d.ts +0 -29
  43. package/build/services/durable/schemas/factory.js +0 -29
  44. package/build/services/durable/search.d.ts +1 -36
  45. package/build/services/durable/search.js +57 -56
  46. package/build/services/durable/worker.js +2 -22
  47. package/build/services/durable/workflow.d.ts +0 -114
  48. package/build/services/durable/workflow.js +4 -144
  49. package/build/services/engine/index.d.ts +1 -6
  50. package/build/services/engine/index.js +1 -43
  51. package/build/services/exporter/index.d.ts +0 -27
  52. package/build/services/exporter/index.js +0 -33
  53. package/build/services/hotmesh/index.d.ts +2 -2
  54. package/build/services/hotmesh/index.js +1 -9
  55. package/build/services/logger/index.js +0 -2
  56. package/build/services/mapper/index.d.ts +0 -14
  57. package/build/services/mapper/index.js +0 -14
  58. package/build/services/pipe/functions/date.d.ts +0 -7
  59. package/build/services/pipe/functions/date.js +0 -7
  60. package/build/services/pipe/functions/math.js +0 -2
  61. package/build/services/pipe/index.d.ts +0 -15
  62. package/build/services/pipe/index.js +2 -23
  63. package/build/services/quorum/index.d.ts +0 -7
  64. package/build/services/quorum/index.js +0 -21
  65. package/build/services/reporter/index.d.ts +0 -5
  66. package/build/services/reporter/index.js +0 -9
  67. package/build/services/router/index.d.ts +0 -9
  68. package/build/services/router/index.js +2 -38
  69. package/build/services/serializer/index.js +7 -26
  70. package/build/services/store/cache.d.ts +0 -18
  71. package/build/services/store/cache.js +0 -18
  72. package/build/services/store/clients/ioredis.d.ts +1 -1
  73. package/build/services/store/clients/ioredis.js +0 -1
  74. package/build/services/store/clients/redis.d.ts +1 -1
  75. package/build/services/store/index.d.ts +0 -55
  76. package/build/services/store/index.js +5 -81
  77. package/build/services/stream/clients/ioredis.d.ts +1 -1
  78. package/build/services/stream/clients/ioredis.js +1 -4
  79. package/build/services/stream/clients/redis.d.ts +1 -1
  80. package/build/services/sub/clients/ioredis.d.ts +1 -1
  81. package/build/services/sub/clients/redis.d.ts +1 -1
  82. package/build/services/task/index.d.ts +0 -9
  83. package/build/services/task/index.js +0 -31
  84. package/build/services/telemetry/index.d.ts +0 -7
  85. package/build/services/telemetry/index.js +1 -13
  86. package/build/services/worker/index.d.ts +0 -4
  87. package/build/services/worker/index.js +2 -6
  88. package/build/types/activity.d.ts +0 -81
  89. package/build/types/durable.d.ts +25 -177
  90. package/build/types/exporter.d.ts +0 -13
  91. package/build/types/hotmesh.d.ts +4 -16
  92. package/build/types/hotmesh.js +0 -3
  93. package/build/types/index.d.ts +4 -6
  94. package/build/types/index.js +4 -3
  95. package/build/types/job.d.ts +1 -86
  96. package/build/types/pipe.d.ts +0 -65
  97. package/build/types/quorum.d.ts +15 -10
  98. package/build/types/redis.d.ts +225 -7
  99. package/build/types/redis.js +9 -0
  100. package/build/types/stream.d.ts +0 -58
  101. package/build/types/stream.js +0 -4
  102. package/package.json +11 -4
  103. package/types/durable.ts +121 -3
  104. package/types/hotmesh.ts +3 -6
  105. package/types/index.ts +23 -10
  106. package/types/job.ts +1 -1
  107. package/types/quorum.ts +22 -0
  108. package/types/redis.ts +267 -18
  109. package/build/types/ioredisclient.d.ts +0 -5
  110. package/build/types/ioredisclient.js +0 -5
  111. package/build/types/redisclient.d.ts +0 -26
  112. package/build/types/redisclient.js +0 -2
  113. package/modules/enums.ts +0 -62
  114. package/modules/errors.ts +0 -280
  115. package/modules/key.ts +0 -101
  116. package/modules/storage.ts +0 -3
  117. package/modules/utils.ts +0 -242
  118. package/services/activities/activity.ts +0 -589
  119. package/services/activities/await.ts +0 -113
  120. package/services/activities/cycle.ts +0 -115
  121. package/services/activities/hook.ts +0 -197
  122. package/services/activities/index.ts +0 -19
  123. package/services/activities/interrupt.ts +0 -172
  124. package/services/activities/signal.ts +0 -148
  125. package/services/activities/trigger.ts +0 -295
  126. package/services/activities/worker.ts +0 -107
  127. package/services/collator/README.md +0 -102
  128. package/services/collator/index.ts +0 -291
  129. package/services/compiler/deployer.ts +0 -504
  130. package/services/compiler/index.ts +0 -98
  131. package/services/compiler/validator.ts +0 -158
  132. package/services/connector/clients/ioredis.ts +0 -57
  133. package/services/connector/clients/redis.ts +0 -72
  134. package/services/connector/index.ts +0 -42
  135. package/services/durable/client.ts +0 -266
  136. package/services/durable/connection.ts +0 -10
  137. package/services/durable/exporter.ts +0 -232
  138. package/services/durable/handle.ts +0 -160
  139. package/services/durable/index.ts +0 -27
  140. package/services/durable/schemas/factory.ts +0 -2358
  141. package/services/durable/search.ts +0 -196
  142. package/services/durable/worker.ts +0 -401
  143. package/services/durable/workflow.ts +0 -557
  144. package/services/engine/index.ts +0 -761
  145. package/services/exporter/index.ts +0 -146
  146. package/services/hotmesh/index.ts +0 -237
  147. package/services/logger/index.ts +0 -79
  148. package/services/mapper/index.ts +0 -89
  149. package/services/pipe/functions/array.ts +0 -78
  150. package/services/pipe/functions/bitwise.ts +0 -27
  151. package/services/pipe/functions/conditional.ts +0 -35
  152. package/services/pipe/functions/date.ts +0 -220
  153. package/services/pipe/functions/index.ts +0 -27
  154. package/services/pipe/functions/json.ts +0 -11
  155. package/services/pipe/functions/logical.ts +0 -11
  156. package/services/pipe/functions/math.ts +0 -217
  157. package/services/pipe/functions/number.ts +0 -75
  158. package/services/pipe/functions/object.ts +0 -98
  159. package/services/pipe/functions/string.ts +0 -86
  160. package/services/pipe/functions/symbol.ts +0 -39
  161. package/services/pipe/functions/unary.ts +0 -19
  162. package/services/pipe/index.ts +0 -216
  163. package/services/quorum/index.ts +0 -319
  164. package/services/reporter/index.ts +0 -387
  165. package/services/router/index.ts +0 -426
  166. package/services/serializer/README.md +0 -10
  167. package/services/serializer/index.ts +0 -285
  168. package/services/store/cache.ts +0 -172
  169. package/services/store/clients/ioredis.ts +0 -145
  170. package/services/store/clients/redis.ts +0 -191
  171. package/services/store/index.ts +0 -1091
  172. package/services/stream/clients/ioredis.ts +0 -157
  173. package/services/stream/clients/redis.ts +0 -158
  174. package/services/stream/index.ts +0 -58
  175. package/services/sub/clients/ioredis.ts +0 -83
  176. package/services/sub/clients/redis.ts +0 -74
  177. package/services/sub/index.ts +0 -25
  178. package/services/task/index.ts +0 -250
  179. package/services/telemetry/index.ts +0 -273
  180. package/services/worker/index.ts +0 -248
  181. package/types/ioredisclient.ts +0 -10
  182. package/types/redisclient.ts +0 -30
@@ -1,589 +0,0 @@
1
- import { HMSH_EXPIRE_DURATION } from '../../modules/enums';
2
- import {
3
- CollationError,
4
- GenerationalError,
5
- GetStateError,
6
- InactiveJobError } from '../../modules/errors';
7
- import {
8
- formatISODate,
9
- getValueByPath,
10
- guid,
11
- restoreHierarchy } from '../../modules/utils';
12
- import { CollatorService } from '../collator';
13
- import { EngineService } from '../engine';
14
- import { ILogger } from '../logger';
15
- import { MapperService } from '../mapper';
16
- import { Pipe } from '../pipe';
17
- import { MDATA_SYMBOLS } from '../serializer';
18
- import { StoreService } from '../store';
19
- import { TelemetryService } from '../telemetry';
20
- import {
21
- ActivityData,
22
- ActivityLeg,
23
- ActivityMetadata,
24
- ActivityType,
25
- Consumes } from '../../types/activity';
26
- import { JobState, JobStatus } from '../../types/job';
27
- import {
28
- MultiResponseFlags,
29
- RedisClient,
30
- RedisMulti } from '../../types/redis';
31
- import { StringAnyType, StringScalarType } from '../../types/serializer';
32
- import {
33
- StreamCode,
34
- StreamData,
35
- StreamDataType,
36
- StreamStatus } from '../../types/stream';
37
- import { TransitionRule } from '../../types/transition';
38
-
39
- /**
40
- * The base class for all activities
41
- */
42
- class Activity {
43
- config: ActivityType;
44
- data: ActivityData;
45
- hook: ActivityData;
46
- metadata: ActivityMetadata;
47
- store: StoreService<RedisClient, RedisMulti>
48
- context: JobState;
49
- engine: EngineService;
50
- logger: ILogger;
51
- status: StreamStatus = StreamStatus.SUCCESS;
52
- code: StreamCode = 200;
53
- leg: ActivityLeg;
54
- adjacencyList: StreamData[];
55
- adjacentIndex = 0;
56
-
57
- constructor(
58
- config: ActivityType,
59
- data: ActivityData,
60
- metadata: ActivityMetadata,
61
- hook: ActivityData | null,
62
- engine: EngineService,
63
- context?: JobState) {
64
- this.config = config;
65
- this.data = data;
66
- this.metadata = metadata;
67
- this.hook = hook;
68
- this.engine = engine;
69
- this.context = context || { data: {}, metadata: {} } as JobState;
70
- this.logger = engine.logger;
71
- this.store = engine.store;
72
- }
73
-
74
- setLeg(leg: ActivityLeg): void {
75
- this.leg = leg;
76
- }
77
-
78
- /**
79
- * Upon entering leg 1 of a duplexed activty, verify
80
- * all aspects of the entry including job and activty state
81
- */
82
- async verifyEntry() {
83
- this.setLeg(1);
84
- await this.getState();
85
- CollatorService.assertJobActive(
86
- this.context.metadata.js,
87
- this.context.metadata.jid,
88
- this.metadata.aid
89
- );
90
- await CollatorService.notarizeEntry(this);
91
- }
92
-
93
- /**
94
- * Upon entering leg 2 of a duplexed activty, verify
95
- * all aspects of the re-entry including job and activty state
96
- */
97
- async verifyReentry(): Promise<number> {
98
- const guid = this.context.metadata.guid;
99
- this.setLeg(2);
100
- await this.getState();
101
- CollatorService.assertJobActive(
102
- this.context.metadata.js,
103
- this.context.metadata.jid,
104
- this.metadata.aid
105
- );
106
- return await CollatorService.notarizeReentry(this, guid);
107
- }
108
-
109
- //******** DUPLEX RE-ENTRY POINT ********//
110
- async processEvent(status: StreamStatus = StreamStatus.SUCCESS, code: StreamCode = 200, type: 'hook' | 'output' = 'output'): Promise<void> {
111
- this.setLeg(2);
112
- const jid = this.context.metadata.jid;
113
- if (!jid) {
114
- this.logger.error('activity-process-event-error', { message: 'job id is undefined' });
115
- return;
116
- }
117
- const aid = this.metadata.aid;
118
- this.status = status;
119
- this.code = code;
120
- this.logger.debug('activity-process-event', { topic: this.config.subtype, jid, aid, status, code });
121
- let telemetry: TelemetryService;
122
-
123
- try {
124
- const collationKey = await this.verifyReentry();
125
-
126
- this.adjacentIndex = CollatorService.getDimensionalIndex(collationKey);
127
- telemetry = new TelemetryService(
128
- this.engine.appId,
129
- this.config,
130
- this.metadata,
131
- this.context,
132
- );
133
- telemetry.startActivitySpan(this.leg);
134
- let multiResponse: MultiResponseFlags;
135
-
136
- if (status === StreamStatus.PENDING) {
137
- multiResponse = await this.processPending(telemetry, type);
138
- } else if (status === StreamStatus.SUCCESS) {
139
- multiResponse = await this.processSuccess(telemetry, type);
140
- } else {
141
- multiResponse = await this.processError(telemetry, type);
142
- }
143
- this.transitionAdjacent(multiResponse, telemetry);
144
- } catch (error) {
145
- if (error instanceof CollationError) {
146
- this.logger.info('process-event-inactive-error', { ...error });
147
- return;
148
- } else if (error instanceof InactiveJobError) {
149
- this.logger.info('process-event-inactive-job-error', { ...error });
150
- return;
151
- } else if (error instanceof GenerationalError) {
152
- this.logger.info('process-event-generational-job-error', { ...error });
153
- return;
154
- } else if (error instanceof GetStateError) {
155
- this.logger.info('process-event-get-job-error', { ...error });
156
- return;
157
- }
158
- this.logger.error('activity-process-event-error', { ...error, message: error.message, stack: error.stack, name: error.name });
159
- telemetry && telemetry.setActivityError(error.message);
160
- throw error;
161
- } finally {
162
- telemetry?.endActivitySpan();
163
- this.logger.debug('activity-process-event-end', { jid, aid });
164
- }
165
- }
166
-
167
- async processPending(telemetry: TelemetryService, type: 'hook' | 'output'): Promise<MultiResponseFlags> {
168
- this.bindActivityData(type);
169
- this.adjacencyList = await this.filterAdjacent();
170
- this.mapJobData();
171
- const multi = this.store.getMulti();
172
- await this.setState(multi);
173
- await CollatorService.notarizeContinuation(this, multi);
174
-
175
- await this.setStatus(this.adjacencyList.length, multi);
176
- return await multi.exec() as MultiResponseFlags;
177
- }
178
-
179
- async processSuccess(telemetry: TelemetryService, type: 'hook' | 'output'): Promise<MultiResponseFlags> {
180
- this.bindActivityData(type);
181
- this.adjacencyList = await this.filterAdjacent();
182
- this.mapJobData();
183
- const multi = this.store.getMulti();
184
- await this.setState(multi);
185
- await CollatorService.notarizeCompletion(this, multi);
186
-
187
- await this.setStatus(this.adjacencyList.length - 1, multi);
188
- return await multi.exec() as MultiResponseFlags;
189
- }
190
-
191
- async processError(telemetry: TelemetryService, type: string): Promise<MultiResponseFlags> {
192
- this.bindActivityError(this.data);
193
- this.adjacencyList = await this.filterAdjacent();
194
- if (!this.adjacencyList.length) {
195
- this.bindJobError(this.data);
196
- }
197
- this.mapJobData();
198
- const multi = this.store.getMulti();
199
- await this.setState(multi);
200
- await CollatorService.notarizeCompletion(this, multi);
201
-
202
- await this.setStatus(this.adjacencyList.length - 1, multi);
203
- return await multi.exec() as MultiResponseFlags;
204
- }
205
-
206
- async transitionAdjacent(multiResponse: MultiResponseFlags, telemetry: TelemetryService): Promise<void> {
207
- telemetry.mapActivityAttributes();
208
- const jobStatus = this.resolveStatus(multiResponse);
209
- const attrs: StringScalarType = { 'app.job.jss': jobStatus };
210
- //adjacencyList membership has already been set at this point (according to activity status)
211
- const messageIds = await this.transition(this.adjacencyList, jobStatus);
212
- if (messageIds?.length) {
213
- attrs['app.activity.mids'] = messageIds.join(',')
214
- }
215
- telemetry.setActivityAttributes(attrs);
216
- }
217
-
218
- resolveStatus(multiResponse: MultiResponseFlags): number {
219
- const activityStatus = multiResponse[multiResponse.length - 1];
220
- if (Array.isArray(activityStatus)) {
221
- return Number(activityStatus[1]);
222
- } else {
223
- return Number(activityStatus);
224
- }
225
- }
226
-
227
- mapJobData(): void {
228
- if(this.config.job?.maps) {
229
- const mapper = new MapperService(this.config.job.maps, this.context);
230
- const output = mapper.mapRules();
231
- if (output) {
232
- for (const key in output) {
233
- const f1 = key.indexOf('[');
234
- //keys with array notation suffix `somekey[]` represent
235
- //dynamically-keyed mappings whose `value` must be moved to the output.
236
- //The `value` must be an object with keys appropriate to the
237
- //notation type: `somekey[0] (array)`, `somekey[-] (mark)`, OR `somekey[_] (search)`
238
- if (f1 > -1) {
239
- const amount = key.substring(f1 + 1).split(']')[0];
240
- if (!isNaN(Number(amount))) {
241
- const left = key.substring(0, f1);
242
- output[left] = output[key];
243
- delete output[key];
244
- } else if (amount === '-' || amount === '_') {
245
- const obj = output[key];
246
- Object.keys(obj).forEach((newKey) => {
247
- output[newKey] = obj[newKey];
248
- });
249
- }
250
- }
251
- }
252
- }
253
- this.context.data = output;
254
- }
255
- }
256
-
257
- mapInputData(): void {
258
- if(this.config.input?.maps) {
259
- const mapper = new MapperService(this.config.input.maps, this.context);
260
- this.context.data = mapper.mapRules();
261
- }
262
- }
263
-
264
- mapOutputData(): void {
265
- //activity YAML may include output map data that produces/extends activity output data.
266
- if(this.config.output?.maps) {
267
- const mapper = new MapperService(this.config.output.maps, this.context);
268
- const actOutData = mapper.mapRules();
269
- const activityId = this.metadata.aid;
270
- const data = { ...this.context[activityId].output, ...actOutData };
271
- this.context[activityId].output.data = data;
272
- }
273
- }
274
-
275
- async registerTimeout(): Promise<void> {
276
- //set timeout in support of hook and/or duplex
277
- }
278
-
279
- /**
280
- * Any StreamMessage with a status of ERROR is bound to the activity
281
- */
282
- bindActivityError(data: Record<string, unknown>): void {
283
- const md = this.context[this.metadata.aid].output.metadata;
284
- md.err = JSON.stringify(this.data);
285
- //(temporary...useful for mapping error parts in the app.yaml)
286
- md.$error = { ...data, is_stream_error: true };
287
- }
288
-
289
- /**
290
- * unhandled activity errors (activities that return an ERROR StreamMessage
291
- * status and have no adjacent children to transition to) are bound to the job
292
- */
293
- bindJobError(data: Record<string, unknown>): void {
294
- this.context.metadata.err = JSON.stringify({ ...data, is_stream_error: true });
295
- }
296
-
297
- async getTriggerConfig(): Promise<ActivityType> {
298
- return await this.store.getSchema(
299
- this.config.trigger,
300
- await this.engine.getVID()
301
- );
302
- }
303
-
304
- getJobStatus(): null | number {
305
- return null;
306
- }
307
-
308
- async setStatus(amount: number, multi?: RedisMulti): Promise<void> {
309
- const { id: appId } = await this.engine.getVID();
310
- await this.store.setStatus(
311
- amount,
312
- this.context.metadata.jid,
313
- appId,
314
- multi
315
- );
316
- }
317
-
318
- authorizeEntry(state: StringAnyType): string[] {
319
- //pre-authorize activity state to allow entry for adjacent activities
320
- return this.adjacencyList?.map((streamData) => {
321
- const { metadata: { aid } } = streamData;
322
- state[`${aid}/output/metadata/as`] = CollatorService.getSeed();
323
- return aid;
324
- }) ?? [];
325
- }
326
-
327
- bindDimensionalAddress(state: StringAnyType) {
328
- const dad = this.resolveDad();
329
- state[`${this.metadata.aid}/output/metadata/dad`] = dad;
330
- }
331
-
332
- async setState(multi?: RedisMulti): Promise<string> {
333
- const jobId = this.context.metadata.jid;
334
- this.bindJobMetadata();
335
- this.bindActivityMetadata();
336
- let state: StringAnyType = {};
337
- await this.bindJobState(state);
338
- const presets = this.authorizeEntry(state);
339
- this.bindDimensionalAddress(state);
340
- this.bindActivityState(state);
341
- //symbolNames holds symkeys
342
- const symbolNames = [
343
- `$${this.config.subscribes}`,
344
- this.metadata.aid,
345
- ...presets
346
- ];
347
- const dIds = CollatorService.getDimensionsById([...this.config.ancestors, this.metadata.aid], this.resolveDad());
348
- return await this.store.setState(state, this.getJobStatus(), jobId, symbolNames, dIds, multi);
349
- }
350
-
351
- bindJobMetadata(): void {
352
- //both legs of the most recently run activity (1 and 2) modify ju (job_updated)
353
- this.context.metadata.ju = formatISODate(new Date());
354
- }
355
-
356
- bindActivityMetadata(): void {
357
- const self: StringAnyType = this.context['$self'];
358
- if (!self.output.metadata) {
359
- self.output.metadata = {};
360
- }
361
- if (this.status === StreamStatus.ERROR) {
362
- self.output.metadata.err = JSON.stringify(this.data);
363
- }
364
- const ts = formatISODate(new Date());
365
- self.output.metadata.ac = ts;
366
- self.output.metadata.au = ts;
367
- self.output.metadata.atp = this.config.type;
368
- if (this.config.subtype) {
369
- self.output.metadata.stp = this.config.subtype;
370
- }
371
- self.output.metadata.aid = this.metadata.aid;
372
- }
373
-
374
- async bindJobState(state: StringAnyType): Promise<void> {
375
- const triggerConfig = await this.getTriggerConfig();
376
- const PRODUCES = [
377
- ...(triggerConfig.PRODUCES || []),
378
- ...this.bindJobMetadataPaths()
379
- ];
380
- for (const path of PRODUCES) {
381
- const value = getValueByPath(this.context, path);
382
- if (value !== undefined) {
383
- state[path] = value;
384
- }
385
- }
386
- for (let key in this.context?.data ?? {}) {
387
- if (key.startsWith('-') || key.startsWith('_')) {
388
- state[key] = this.context.data[key];
389
- }
390
- }
391
- TelemetryService.bindJobTelemetryToState(state, this.config, this.context);
392
- }
393
-
394
- bindActivityState(state: StringAnyType,): void {
395
- const produces = [
396
- ...this.config.produces,
397
- ...this.bindActivityMetadataPaths()
398
- ];
399
- for (const path of produces) {
400
- const prefixedPath = `${this.metadata.aid}/${path}`;
401
- const value = getValueByPath(this.context, prefixedPath);
402
- if (value !== undefined) {
403
- state[prefixedPath] = value;
404
- }
405
- }
406
- TelemetryService.bindActivityTelemetryToState(state, this.config, this.metadata, this.context, this.leg);
407
- }
408
-
409
- bindJobMetadataPaths(): string[] {
410
- return MDATA_SYMBOLS.JOB_UPDATE.KEYS.map((key) => `metadata/${key}`);
411
- }
412
-
413
- bindActivityMetadataPaths(): string[] {
414
- const keys_to_save = this.leg === 1 ? 'ACTIVITY': 'ACTIVITY_UPDATE'
415
- return MDATA_SYMBOLS[keys_to_save].KEYS.map((key) => `output/metadata/${key}`);
416
- }
417
-
418
- async getState() {
419
- const gid = this.context.metadata.gid;
420
- const jobSymbolHashName = `$${this.config.subscribes}`;
421
- const consumes: Consumes = {
422
- [jobSymbolHashName]: MDATA_SYMBOLS.JOB.KEYS.map((key) => `metadata/${key}`)
423
- };
424
- for (let [activityId, paths] of Object.entries(this.config.consumes)) {
425
- if(activityId === '$job') {
426
- for (const path of paths) {
427
- consumes[jobSymbolHashName].push(path);
428
- }
429
- } else {
430
- if (activityId === '$self') {
431
- activityId = this.metadata.aid;
432
- }
433
- if (!consumes[activityId]) {
434
- consumes[activityId] = [];
435
- }
436
- for (const path of paths) {
437
- consumes[activityId].push(`${activityId}/${path}`);
438
- }
439
- }
440
- }
441
- TelemetryService.addTargetTelemetryPaths(consumes, this.config, this.metadata, this.leg);
442
- let { dad, jid } = this.context.metadata;
443
- const dIds = CollatorService.getDimensionsById([...this.config.ancestors, this.metadata.aid], dad || '');
444
- //`state` is a unidimensional hash; context is a tree
445
- const [state, _status] = await this.store.getState(jid, consumes, dIds);
446
- this.context = restoreHierarchy(state) as JobState;
447
- this.assertGenerationalId(this.context?.metadata?.gid, gid);
448
- this.initDimensionalAddress(dad);
449
- this.initSelf(this.context);
450
- this.initPolicies(this.context);
451
- }
452
-
453
- /**
454
- * if the job is created/deleted/created with the same key,
455
- * the 'gid' ensures no stale messages (such as sleep delays)
456
- * enter the workstream. Any message with a mismatched gid
457
- * belongs to a prior job and can safely be ignored/dropped.
458
- */
459
- assertGenerationalId(jobGID: string, msgGID?: string) {
460
- if (msgGID !== jobGID) {
461
- throw new GenerationalError(
462
- jobGID,
463
- msgGID,
464
- this.context?.metadata?.jid ?? '',
465
- this.context?.metadata?.aid ?? '',
466
- this.context?.metadata?.dad ?? ''
467
- );
468
- }
469
- }
470
-
471
- initDimensionalAddress(dad: string): void {
472
- this.metadata.dad = dad;
473
- }
474
-
475
- initSelf(context: StringAnyType): JobState {
476
- const activityId = this.metadata.aid;
477
- if (!context[activityId]) {
478
- context[activityId] = { };
479
- }
480
- const self = context[activityId];
481
- if (!self.output) {
482
- self.output = { };
483
- }
484
- if (!self.input) {
485
- self.input = { };
486
- }
487
- if (!self.hook) {
488
- self.hook = { };
489
- }
490
- if (!self.output.metadata) {
491
- self.output.metadata = { };
492
- }
493
- //prebind the updated timestamp (mappings need the time)
494
- self.output.metadata.au = formatISODate(new Date());
495
- context['$self'] = self;
496
- context['$job'] = context; //NEVER call STRINGIFY! (now circular)
497
- return context as JobState;
498
- }
499
-
500
- initPolicies(context: JobState) {
501
- const expire = Pipe.resolve(
502
- this.config.expire ?? HMSH_EXPIRE_DURATION,
503
- context
504
- );
505
- context.metadata.expire = expire;
506
- }
507
-
508
- bindActivityData(type: 'output' | 'hook'): void {
509
- this.context[this.metadata.aid][type].data = this.data;
510
- }
511
-
512
- resolveDad(): string {
513
- let dad = this.metadata.dad;
514
- if (this.adjacentIndex > 0) {
515
- //if adjacent index > 0 the activity is cycling; replace last index with cycle index
516
- dad = `${dad.substring(0, dad.lastIndexOf(','))},${this.adjacentIndex}`
517
- }
518
- return dad;
519
- }
520
-
521
- resolveAdjacentDad(): string {
522
- //concat self and child dimension (all children (leg 1) begin life at 0)
523
- return `${this.resolveDad()}${CollatorService.getDimensionalSeed(0)}`;
524
- };
525
-
526
- async filterAdjacent(): Promise<StreamData[]> {
527
- const adjacencyList: StreamData[] = [];
528
- const transitions = await this.store.getTransitions(await this.engine.getVID());
529
- const transition = transitions[`.${this.metadata.aid}`];
530
- //resolve the dimensional address for adjacent children
531
- const adjacentDad = this.resolveAdjacentDad();
532
- if (transition) {
533
- for (const toActivityId in transition) {
534
- const transitionRule: boolean | TransitionRule = transition[toActivityId];
535
- if (MapperService.evaluate(transitionRule, this.context, this.code)) {
536
- adjacencyList.push({
537
- metadata: {
538
- guid: guid(),
539
- jid: this.context.metadata.jid,
540
- gid: this.context.metadata.gid,
541
- dad: adjacentDad,
542
- aid: toActivityId,
543
- spn: this.context['$self'].output.metadata?.l2s,
544
- trc: this.context.metadata.trc,
545
- },
546
- type: StreamDataType.TRANSITION,
547
- data: {}
548
- });
549
- }
550
- }
551
- }
552
- return adjacencyList;
553
- }
554
-
555
- async transition(adjacencyList: StreamData[], jobStatus: JobStatus): Promise<string[]> {
556
- if (this.jobWasInterrupted(jobStatus)) {
557
- return;
558
- }
559
- let mIds: string[] = [];
560
- let emit: boolean = false;
561
- if (this.config.emit) {
562
- emit = Pipe.resolve(this.config.emit, this.context);
563
- }
564
- if (jobStatus <= 0 || emit) {
565
- await this.engine.runJobCompletionTasks(
566
- this.context,
567
- { emit: jobStatus > 0 },
568
- );
569
- }
570
- if (adjacencyList.length && jobStatus > 0) {
571
- const multi = this.store.getMulti();
572
- for (const execSignal of adjacencyList) {
573
- await this.engine.router?.publishMessage(null, execSignal, multi);
574
- }
575
- mIds = (await multi.exec()) as string[];
576
- }
577
- return mIds;
578
- }
579
-
580
- /**
581
- * A job with a vale < -100_000_000 is considered interrupted,
582
- * as the interruption event decrements the job status by 1billion.
583
- */
584
- jobWasInterrupted(jobStatus: JobStatus): boolean {
585
- return jobStatus < -100_000_000;
586
- }
587
- }
588
-
589
- export { Activity, ActivityType };
@@ -1,113 +0,0 @@
1
- import {
2
- GenerationalError,
3
- GetStateError,
4
- InactiveJobError } from '../../modules/errors';
5
- import { guid } from '../../modules/utils';
6
- import { Activity } from './activity';
7
- import { CollatorService } from '../collator';
8
- import { EngineService } from '../engine';
9
- import { Pipe } from '../pipe';
10
- import { TelemetryService } from '../telemetry';
11
- import {
12
- ActivityData,
13
- ActivityMetadata,
14
- AwaitActivity,
15
- ActivityType } from '../../types/activity';
16
- import { JobState } from '../../types/job';
17
- import { MultiResponseFlags, RedisMulti } from '../../types/redis';
18
- import { StreamData, StreamDataType } from '../../types/stream';
19
-
20
- class Await extends Activity {
21
- config: AwaitActivity;
22
-
23
- constructor(
24
- config: ActivityType,
25
- data: ActivityData,
26
- metadata: ActivityMetadata,
27
- hook: ActivityData | null,
28
- engine: EngineService,
29
- context?: JobState) {
30
- super(config, data, metadata, hook, engine, context);
31
- }
32
-
33
- //******** INITIAL ENTRY POINT (A) ********//
34
- async process(): Promise<string> {
35
- this.logger.debug('await-process', { jid: this.context.metadata.jid, gid: this.context.metadata.gid, aid: this.metadata.aid });
36
- let telemetry: TelemetryService;
37
- try {
38
- await this.verifyEntry();
39
-
40
- telemetry = new TelemetryService(this.engine.appId, this.config, this.metadata, this.context);
41
- telemetry.startActivitySpan(this.leg);
42
- this.mapInputData();
43
-
44
- //save state and authorize reentry
45
- const multi = this.store.getMulti();
46
- //todo: await this.registerTimeout();
47
- const messageId = await this.execActivity(multi);
48
- await CollatorService.authorizeReentry(this, multi);
49
- await this.setState(multi);
50
- await this.setStatus(0, multi);
51
- const multiResponse = await multi.exec() as MultiResponseFlags;
52
-
53
- //telemetry
54
- telemetry.mapActivityAttributes();
55
- const jobStatus = this.resolveStatus(multiResponse);
56
- telemetry.setActivityAttributes({
57
- 'app.activity.mid': messageId,
58
- 'app.job.jss': jobStatus
59
- });
60
- return this.context.metadata.aid;
61
- } catch (error) {
62
- if (error instanceof InactiveJobError) {
63
- this.logger.error('await-inactive-job-error', { ...error });
64
- return;
65
- } else if (error instanceof GenerationalError) {
66
- this.logger.info('process-event-generational-job-error', { ...error });
67
- return;
68
- } else if (error instanceof GetStateError) {
69
- this.logger.error('await-get-state-error', { ...error });
70
- return;
71
- } else {
72
- this.logger.error('await-process-error', { ...error });
73
- }
74
- telemetry.setActivityError(error.message);
75
- throw error;
76
- } finally {
77
- telemetry?.endActivitySpan();
78
- this.logger.debug('await-process-end', { jid: this.context.metadata.jid, gid: this.context.metadata.gid, aid: this.metadata.aid });
79
- }
80
- }
81
-
82
- async execActivity(multi: RedisMulti): Promise<string> {
83
- const topic = Pipe.resolve(this.config.subtype, this.context);
84
- const streamData: StreamData = {
85
- metadata: {
86
- guid: guid(),
87
- jid: this.context.metadata.jid,
88
- gid: this.context.metadata.gid,
89
- dad: this.metadata.dad,
90
- aid: this.metadata.aid,
91
- topic,
92
- spn: this.context['$self'].output.metadata?.l1s,
93
- trc: this.context.metadata.trc,
94
- },
95
- type: StreamDataType.AWAIT,
96
- data: this.context.data
97
- };
98
- if (this.config.await !== true) {
99
- const doAwait = Pipe.resolve(this.config.await, this.context);
100
- if (doAwait === false) {
101
- streamData.metadata.await = false;
102
- }
103
- }
104
- if (this.config.retry) {
105
- streamData.policies = {
106
- retry: this.config.retry
107
- };
108
- }
109
- return (await this.engine.router?.publishMessage(null, streamData, multi)) as string;
110
- }
111
- }
112
-
113
- export { Await };