@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
@@ -1,419 +1,232 @@
1
+ import { restoreHierarchy } from '../../modules/utils';
1
2
  import { ILogger } from '../logger';
3
+ import { SerializerService } from '../serializer';
2
4
  import { StoreService } from '../store';
3
- import { StringStringType, Symbols } from "../../types";
4
- import { RedisClient, RedisMulti } from '../../types/redis';
5
5
  import {
6
- ActivityAction,
7
- DependencyExport,
8
- ExportItem,
9
6
  ExportOptions,
10
- JobAction,
11
- JobActionExport,
12
- DurableJobExport,
13
- JobTimeline } from '../../types/exporter';
14
- import { SerializerService } from '../serializer';
15
- import { restoreHierarchy } from '../../modules/utils';
16
- import { VALSEP } from '../../modules/key';
7
+ DurableJobExport,
8
+ TimelineType,
9
+ TransitionType,
10
+ ExportFields} from '../../types/exporter';
11
+ import { RedisClient, RedisMulti } from '../../types/redis';
12
+ import { StringAnyType, StringStringType, Symbols } from "../../types/serializer";
17
13
 
18
- /**
19
- * Downloads job data from Redis (hscan, hmget, hgetall)
20
- * Splits, Inflates, and Sorts the job data for use in durable contexts
21
- */
22
14
  class ExporterService {
23
15
  appId: string;
24
16
  logger: ILogger;
25
- serializer: SerializerService
26
17
  store: StoreService<RedisClient, RedisMulti>;
27
18
  symbols: Promise<Symbols> | Symbols;
28
-
29
- /**
30
- * Friendly names for the activity ids
31
- */
32
- activitySymbols: Symbols = {
33
- t1: 'trigger',
34
- a1: 'pivot',
35
- w1: 'worker',
36
- a592: 'sleeper',
37
- a594: 'awaiter',
38
- a599: 'retryer',
39
- c592: 'sleep_cycler',
40
- c594: 'await_cycler',
41
- c599: 'retry_cycler',
42
- s5: 'scrubber',
43
- sig: 'hook',
44
- siga1: 'hook_pivot',
45
- sigw1: 'hook_worker',
46
- siga592: 'hook_sleeper',
47
- siga594: 'hook_awaiter',
48
- siga599: 'hook_retryer',
49
- sigc592: 'hook_sleep_cycler',
50
- sigc594: 'hook_await_cycler',
51
- sigc599: 'hook_retry_cycler',
52
- }
53
-
54
- //adjacent transitions
55
- transitions = {
56
- trigger: ['pivot', 'hook'],
57
- pivot: ['worker'],
58
- worker: ['sleeper', 'awaiter', 'retryer', 'scrubber'],
59
- sleeper: ['sleep_cycler'],
60
- awaiter: ['await_cycler'],
61
- retryer: ['retry_cycler'],
62
- hook: ['hook_pivot'],
63
- hook_pivot: ['hook_worker'],
64
- hook_worker: ['hook_sleeper', 'hook_awaiter', 'hook_retryer'],
65
- hook_sleeper: ['hook_sleep_cycler'],
66
- hook_awaiter: ['hook_await_cycler'],
67
- hook_retryer: ['hook_retry_cycler'],
68
- };
69
-
70
- //goto transitions
71
- cycles = {
72
- sleep_cycler: ['pivot'],
73
- await_cycler: ['pivot'],
74
- retry_cycler: ['pivot'],
75
- hook_sleep_cycler: ['hook_pivot'],
76
- hook_await_cycler: ['hook_pivot'],
77
- hook_retry_cycler: ['hook_pivot'],
78
- }
19
+ private static symbols: Map<string, Symbols> = new Map();
79
20
 
80
21
  constructor(appId: string, store: StoreService<RedisClient, RedisMulti>, logger: ILogger) {
81
22
  this.appId = appId;
82
23
  this.logger = logger;
83
24
  this.store = store;
84
- this.serializer = new SerializerService();
85
25
  }
86
26
 
87
27
  /**
88
- * Convert the job hash and dependency list into a DurableJobExport object.
89
- * This object contains various facets that describe the interaction
90
- * in terms relevant to narrative storytelling.
28
+ * Convert the job hash from its compiles format into a DurableJobExport object with
29
+ * facets that describe the workflow in terms relevant to narrative storytelling.
91
30
  */
92
31
  async export(jobId: string, options: ExportOptions = {}): Promise<DurableJobExport> {
93
- if (!this.symbols) {
94
- this.symbols = this.store.getAllSymbols();
95
- this.symbols = await this.symbols;
32
+ if (!ExporterService.symbols.has(this.appId)) {
33
+ const symbols: Symbols | Promise<Symbols> = this.store.getAllSymbols();
34
+ ExporterService.symbols.set(this.appId, await symbols);
96
35
  }
97
- const depData = await this.store.getDependencies(jobId);
98
36
  const jobData = await this.store.getRaw(jobId);
99
- const jobExport = this.inflate(jobData, depData);
37
+ const jobExport = this.inflate(jobData, options);
100
38
  return jobExport;
101
39
  }
102
40
 
103
41
  /**
104
- * Interleave actions into the replay timeline to create
105
- * a time-ordered timeline of the entire interaction, beginning
106
- * with the entry trigger and concluding with the scrubber
107
- * activity. Using the returned timeline, it is possible to
108
- * create an animated narrative of the job, highlighting
109
- * activities in the graph according to the timeline's
110
- * activity-created (/ac) and activity-updated (/au) entries.
42
+ * Inflates the job data from Redis into a DurableJobExport object
43
+ * @param jobHash - the job data from Redis
44
+ * @param dependencyList - the list of dependencies for the job
45
+ * @returns - the inflated job data
111
46
  */
112
- createTimeline(replay: ExportItem[], actions: JobActionExport): JobTimeline[] {
113
- const timeline: JobTimeline[] = [];
114
- replay.forEach((item) => {
115
- const dimensions = item[0];
116
- const parts = dimensions.split('/');
117
- const activityName = item[1].split('/')[0];
118
- const duplex = item[1].endsWith('/ac') ? 'entry' : 'exit';
119
- const timestamp = item[2];
120
- let event: JobTimeline = {
121
- activity: activityName,
122
- duplex: duplex as 'entry' | 'exit',
123
- dimension: dimensions,
124
- timestamp,
125
- created: timestamp,
126
- updated: timestamp,
127
- };
128
- const prior = timeline[timeline.length - 1];
129
- if (prior && prior.activity === event.activity && prior.duplex !== event.duplex && prior.dimension === event.dimension) {
130
- if (event.duplex === 'exit') {
131
- prior.updated = event.timestamp;
132
- } else {
133
- prior.created = event.timestamp;
134
- }
135
- event = prior;
136
- } else {
137
- timeline.push(event);
138
- }
47
+ inflate(jobHash: StringStringType, options: ExportOptions): DurableJobExport {
48
+ const timeline: TimelineType[] = [];
49
+ const state: StringAnyType = {};
50
+ const data: StringStringType = {};
51
+ const transitionsObject: Record<string, TransitionType> = {};
52
+ const regex = /^([a-zA-Z]{3}),(\d+(?:,\d+)*)/;
53
+
54
+ Object.entries(jobHash).forEach(([key, value]) => {
55
+ const match = key.match(regex);
56
+
57
+ if (match) {
58
+ //transitions
59
+ this.inflateTransition(match, value, transitionsObject);
139
60
 
140
- if (this.isMainEntry(item[1])) {
141
- event.actions = [] as ActivityAction[];
142
- this.interleaveActions(actions.main, event.actions);
143
- } else if (this.isHookEntry(item[1])) {
144
- const hookDimension = `/${parts[1]}/${parts[2]}`;
145
- const hookActions = actions.hooks[hookDimension];
146
- event.actions = [] as ActivityAction[];
147
- this.interleaveActions(hookActions, event.actions);
61
+ } else if (key.length === 3) {
62
+ //state
63
+ state[this.inflateKey(key)] = SerializerService.fromString(value);
64
+
65
+ } else if (key.startsWith('_')) {
66
+ //data
67
+ data[key.substring(1)] = value;
68
+
69
+ } else if (key.startsWith('-')) {
70
+ //timeline
71
+ const keyParts = this.keyToObject(key); //key parts have meaning
72
+ timeline.push({
73
+ ...keyParts,
74
+ key,
75
+ value: this.resolveValue(value, options.values),
76
+ });
148
77
  }
149
78
  });
150
- return timeline;
79
+
80
+ return this.filterFields({
81
+ data: restoreHierarchy(data),
82
+ state: Object.entries(restoreHierarchy(state))[0][1],
83
+ status: parseInt(jobHash[':'], 10),
84
+ timeline: this.sortParts(timeline),
85
+ transitions: this.sortEntriesByCreated(transitionsObject),
86
+ }, options.block, options.allow);
151
87
  }
152
88
 
153
- /**
154
- * Interleave actions into the 'worker' and 'hook_worker'
155
- * activities (between their /ac and /au entries)
156
- */
157
- interleaveActions(target: JobAction, actions: ActivityAction[]) {
158
- if (target) {
159
- for (let i = target.cursor + 1; i < target.items.length; i++) {
160
- const [_, actionType, jobOrIndex] = target.items[i];
161
- actions.push({ action: actionType, target: jobOrIndex });
162
- target.cursor = i;
163
- if (this.isPausingAction(actionType)) {
164
- break;
165
- }
89
+ resolveValue(raw: string, withValues: boolean): Record<string, any> | string | number | null {
90
+ const resolved = SerializerService.fromString(raw);
91
+ if (withValues !== false) {
92
+ return resolved;
93
+ }
94
+ if (resolved && typeof resolved === 'object') {
95
+ if ('data' in resolved) {
96
+ resolved.data = {};
97
+ }
98
+ if ('$error' in resolved) {
99
+ resolved.$error = {};
166
100
  }
167
101
  }
168
- }
169
-
170
- isPausingAction(actionType: string): boolean {
171
- return actionType === 'sleep' || actionType === 'waitForSignal';
172
- }
173
-
174
- isMainEntry(key: string): boolean {
175
- return key.startsWith('worker/') && key.endsWith('/ac');
176
- }
177
-
178
- isHookEntry(key: string): boolean {
179
- return key.startsWith('hook_worker/') && key.endsWith('/ac');
102
+ return resolved;
180
103
  }
181
104
 
182
105
  /**
183
106
  * Inflates the key from Redis, 3-character symbol
184
107
  * into a human-readable JSON path, reflecting the
185
108
  * tree-like structure of the unidimensional Hash
109
+ * @private
186
110
  */
187
111
  inflateKey(key: string): string {
188
- if (key in this.symbols) {
189
- const path = this.symbols[key];
112
+ const symbols = ExporterService.symbols.get(this.appId);
113
+ if (key in symbols) {
114
+ const path = symbols[key];
190
115
  const parts = path.split('/');
191
- if (parts[0] in this.activitySymbols) {
192
- parts[0] = this.activitySymbols[parts[0]];
193
- }
194
116
  return parts.join('/');
195
117
  }
196
118
  return key;
197
119
  }
198
120
 
199
- /**
200
- * Inflates the dependency data from Redis into a DurableJobExport object by
201
- * organizing the dimensional isolate in sch a way asto interleave
202
- * into a story
203
- * @param data - the dependency data from Redis
204
- * @returns - the organized dependency data
205
- */
206
- inflateDependencyData(data: string[], actions: JobActionExport): DependencyExport[] {
207
- const hookReg = /([0-9,]+)-(\d+)$/;
208
- const flowReg = /-(\d+)$/;
209
- return data.map((dependency, index: number): DependencyExport => {
210
- const [action, topic, gid, _pd, ...jid] = dependency.split(VALSEP);
211
- const jobId = jid.join(VALSEP);
212
- const match = jobId.match(hookReg);
213
- let prefix: string;
214
- let type: 'hook' | 'flow' | 'other';
215
- let dimensionKey: string = '';
216
- if (match) {
217
- //hook-originating dependency
218
- const [_, dimension, counter] = match;
219
- dimensionKey = dimension.split(',').join('/');
220
- prefix = `${dimensionKey}[${counter}]`;
221
- type = 'hook';
222
- } else {
223
- const match = jobId.match(flowReg);
224
- if (match) {
225
- //main workflow-originating dependency
226
- const [_, counter] = match;
227
- prefix = `[${counter}]`;
228
- type = 'flow';
229
- } else {
230
- //'other' types like signal cleanup
231
- prefix = '/';
232
- type = 'other';
121
+ filterFields(fullObject: DurableJobExport, block: ExportFields[] = [], allow: ExportFields[] = []): Partial<DurableJobExport> {
122
+ let result: Partial<DurableJobExport> = {};
123
+ if (allow && allow.length > 0) {
124
+ allow.forEach(field => {
125
+ if (field in fullObject) {
126
+ result[field] = fullObject[field] as StringAnyType & number & TimelineType[] & TransitionType[];
233
127
  }
234
- }
235
- this.seedActions(
236
- type,
237
- action,
238
- topic,
239
- dependency,
240
- prefix,
241
- dimensionKey,
242
- actions,
243
- jobId,
244
- );
245
- return {
246
- type: action,
247
- topic,
248
- gid,
249
- jid: jobId,
250
- } as unknown as DependencyExport;
251
- });
252
- }
253
-
254
- /**
255
- * Adds historical actions (proxyActivity, executeChild)
256
- * using the `dependency list` to determine
257
- * after-the-fact what happened within the 'black-box'
258
- * worker function. This is necessary to interleave the
259
- * actions into the replay timeline, given that it isn't
260
- * really possible to know the inner-workings of the user's
261
- * function
262
- *
263
- */
264
- seedActions(type: 'flow'|'hook'|'other', action: string, topic: string, dep: string, prefix: string, dimensionKey: string, actions: JobActionExport, jobId: string) {
265
- if (type !== 'other' && action === 'expire-child') {
266
- let depType: string;
267
- if (topic == `${this.appId}.activity.execute`) {
268
- depType = 'proxyActivity';
269
- } else if (topic == `${this.appId}.execute`) {
270
- depType = 'executeChild';
271
- } else if (topic == `${this.appId}.wfsc.execute`) {
272
- depType = 'waitForSignal';
273
- }
274
-
275
- if (depType) {
276
- if (type === 'flow') {
277
- actions.main.items.push([prefix, depType, jobId]);
278
- } else if (type === 'hook') {
279
- if (!actions.hooks[dimensionKey]) {
280
- actions.hooks[dimensionKey] = {
281
- cursor: -1,
282
- items: [],
283
- };
284
- }
285
- actions.hooks[dimensionKey].items.push([prefix, depType, jobId]);
128
+ });
129
+ } else {
130
+ result = { ...fullObject };
131
+ }
132
+ if (block && block.length > 0) {
133
+ block.forEach(field => {
134
+ if (field in result) {
135
+ delete result[field];
286
136
  }
287
- }
137
+ });
288
138
  }
139
+ return result as DurableJobExport;
289
140
  }
290
141
 
291
- /**
292
- * Inflates the job data from Redis into a DurableJobExport object
293
- * @param jobHash - the job data from Redis
294
- * @param dependencyList - the list of dependencies for the job
295
- * @returns - the inflated job data
296
- */
297
- inflate(jobHash: StringStringType, dependencyList: string[]): DurableJobExport {
298
- //the list of actions taken in the workflow and hook functions
299
- const actions: JobActionExport = {
300
- hooks: {},
301
- main: { cursor: -1, items: [] },
302
- };
303
- const dependencies = this.inflateDependencyData(dependencyList, actions);
304
- const state: StringStringType = {};
305
- const data: StringStringType = {};
306
- const other: ExportItem[] = [];
307
- const replay: ExportItem[] = [];
308
- const regex = /^([a-zA-Z]{3}),(\d+(?:,\d+)*)/;
309
-
310
- Object.entries(jobHash).forEach(([key, value]) => {
311
- const match = key.match(regex);
312
- if (match) {
313
- //activity process state
314
- this.inflateProcess(match, value, replay);
315
- } else if (key.length === 3) {
316
- //job state
317
- state[this.inflateKey(key)] = this.serializer.fromString(value);
318
- } else if (key.startsWith('_')) {
319
- //job data
320
- data[key.substring(1)] = value;
321
- } else if (key.startsWith('-')) {
322
- //actions with side effect (replayable)
323
- this.inflateActions(key, value, actions);
142
+ inflateTransition(match: RegExpMatchArray, value: string, transitionsObject: Record<string, TransitionType>) {
143
+ const [_, letters, dimensions] = match;
144
+ const path = this.inflateKey(letters);
145
+ const parts = path.split('/');
146
+ const activity = parts[0];
147
+ const isCreate = path.endsWith('/output/metadata/ac');
148
+ const isUpdate = path.endsWith('/output/metadata/au');
149
+ //for now only export activity start/stop; activity data would also be interesting
150
+ if (isCreate || isUpdate) {
151
+ const targetName = `${activity},${dimensions}`;
152
+ let target = transitionsObject[targetName];
153
+ if (!target) {
154
+ transitionsObject[targetName] = {
155
+ activity,
156
+ dimensions,
157
+ created: isCreate ? value : null,
158
+ updated: isUpdate ? value : null,
159
+ };
324
160
  } else {
325
- //collator guids, etc
326
- other.push([null, key, value]);
161
+ target[isCreate ? 'created' : 'updated'] = value;
327
162
  }
328
- });
329
-
330
- replay.sort(this.dateSort)
331
- actions.main.items.sort(this.reverseSort);
332
- Object.entries(actions.hooks).forEach(([key, value]) => {
333
- value.items.sort(this.reverseSort);
334
- });
335
-
336
- return {
337
- data: restoreHierarchy(data),
338
- dependencies,
339
- state: Object.entries(restoreHierarchy(state))[0][1],
340
- status: jobHash[':'],
341
- timeline: this.createTimeline(replay, actions),
342
- transitions: { ...this.transitions },
343
- cycles: { ...this.cycles },
344
- };
163
+ }
345
164
  }
346
165
 
347
- inflateProcess(match: RegExpMatchArray, value: string, replay: ExportItem[]) {
348
- const [_, letters, numbers] = match;
349
- const path = this.inflateKey(letters);
350
- if (path.endsWith('/output/metadata/ac') ||
351
- path.endsWith('/output/metadata/au')) {
352
- const dimensions = `/${numbers.replace(/,/g, '/')}`;
353
- const resolved = this.serializer.fromString(value);
354
- replay.push([
355
- dimensions,
356
- path,
357
- resolved,
358
- ]);
359
- }
166
+ sortEntriesByCreated(obj: { [key: string]: TransitionType }): TransitionType[] {
167
+ const entriesArray: TransitionType[] = Object.values(obj);
168
+ entriesArray.sort((a, b) => {
169
+ return (a.created || a.updated).localeCompare(b.created || b.updated);
170
+ });
171
+ return entriesArray;
360
172
  }
361
173
 
362
- inflateActions(key: string, value: string, actions: JobActionExport) {
363
- let [_, dimensionalType, counter, subcounter] = key.split('-');
364
- if (subcounter) {
365
- counter = `${counter}.${subcounter}`;
366
- }
367
- const [type, ...dimensions] = dimensionalType.split(',');
368
- let dimensionKey = '';
369
- let isHook = false;
370
- if (dimensions.length > 0) {
371
- dimensionKey = `/${dimensions.join('/')}`;
372
- isHook = true;
373
- }
374
- let targetList: ExportItem[];
375
- if (isHook) {
376
- if (!actions.hooks[dimensionKey]) {
377
- actions.hooks[dimensionKey] = {
378
- cursor: -1,
379
- items: [],
380
- };
174
+ /**
175
+ * marker names are overloaded with details like sequence, type, etc
176
+ */
177
+ keyToObject(key: string): {index: number, dimension?: string, secondary?: number} {
178
+ function extractDimension(label: string): string {
179
+ const parts = label.split(',');
180
+ if (parts.length > 1) {
181
+ parts.shift();
182
+ return parts.join(',');
381
183
  }
382
- targetList = actions.hooks[dimensionKey].items;
383
- } else {
384
- targetList = actions.main.items;
385
184
  }
386
- targetList.push([
387
- `${dimensionKey}[${counter}]`,
388
- type,
389
- value,
390
- ]);
391
- }
392
-
393
- reverseSort(aKey: ExportItem, bKey: ExportItem) {
394
- if (aKey[0] > bKey[0]) {
395
- return 1;
396
- } else if (aKey[0] < bKey[0]) {
397
- return -1;
185
+ const parts = key.split('-');
186
+ if (parts.length === 4) {
187
+ //-proxy-5- -search-1-1-
188
+ return {
189
+ index: parseInt(parts[2], 10),
190
+ dimension: extractDimension(parts[1]),
191
+ }
398
192
  } else {
399
- if (aKey[1] > bKey[1]) {
400
- return 1;
401
- } else if (aKey[1] < bKey[1]) {
402
- return -1;
193
+ //-search,0,0-1-1- -proxy,0,0-1-
194
+ return {
195
+ index: parseInt(parts[2], 10),
196
+ secondary: parseInt(parts[3], 10),
197
+ dimension: extractDimension(parts[1]),
403
198
  }
404
- return 0;
405
199
  }
406
200
  }
407
201
 
408
- dateSort(aKey: ExportItem, bKey: ExportItem) {
409
- if (aKey[2] > bKey[2]) {
410
- return 1;
411
- } else if (aKey[2] < bKey[2]) {
412
- return -1;
413
- } else {
202
+ /**
203
+ * idem list has a complicated sort order based on indexes and dimensions
204
+ */
205
+ sortParts(parts: TimelineType[]): TimelineType[] {
206
+ return parts.sort((a, b) => {
207
+ const { dimension: aDim, index: aIdx, secondary: aSec } = a;
208
+ const { dimension: bDim, index: bIdx, secondary: bSec } = b;
209
+
210
+ if (aDim === undefined && bDim !== undefined) return -1;
211
+ if (aDim !== undefined && bDim === undefined) return 1;
212
+ if (aDim !== undefined && bDim !== undefined) {
213
+ if (aDim < bDim) return -1;
214
+ if (aDim > bDim) return 1;
215
+ }
216
+
217
+ if (aIdx < bIdx) return -1;
218
+ if (aIdx > bIdx) return 1;
219
+
220
+ if (aSec === undefined && bSec !== undefined) return -1;
221
+ if (aSec !== undefined && bSec === undefined) return 1;
222
+ if (aSec !== undefined && bSec !== undefined) {
223
+ if (aSec < bSec) return -1;
224
+ if (aSec > bSec) return 1;
225
+ }
226
+
414
227
  return 0;
415
- }
416
- }
228
+ });
229
+ };
417
230
  }
418
231
 
419
232
  export { ExporterService };