@hotmeshio/hotmesh 0.0.51 → 0.0.53
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.
- package/README.md +13 -9
- package/build/index.d.ts +1 -2
- package/build/index.js +1 -3
- package/build/modules/enums.d.ts +8 -3
- package/build/modules/enums.js +16 -8
- package/build/modules/errors.d.ts +98 -18
- package/build/modules/errors.js +90 -33
- package/build/package.json +7 -2
- package/build/services/activities/activity.d.ts +8 -0
- package/build/services/activities/activity.js +65 -16
- package/build/services/activities/await.js +6 -6
- package/build/services/activities/cycle.d.ts +2 -2
- package/build/services/activities/cycle.js +5 -5
- package/build/services/activities/hook.js +4 -4
- package/build/services/activities/interrupt.d.ts +3 -3
- package/build/services/activities/interrupt.js +15 -6
- package/build/services/activities/signal.d.ts +2 -2
- package/build/services/activities/signal.js +4 -4
- package/build/services/activities/trigger.js +12 -3
- package/build/services/activities/worker.js +6 -6
- package/build/services/compiler/deployer.js +33 -5
- package/build/services/compiler/validator.d.ts +2 -0
- package/build/services/compiler/validator.js +5 -1
- package/build/services/durable/client.d.ts +7 -1
- package/build/services/durable/client.js +56 -30
- package/build/services/durable/exporter.d.ts +7 -72
- package/build/services/durable/exporter.js +105 -295
- package/build/services/durable/handle.d.ts +11 -6
- package/build/services/durable/handle.js +59 -46
- package/build/services/durable/index.d.ts +0 -2
- package/build/services/durable/index.js +0 -2
- package/build/services/durable/schemas/factory.d.ts +33 -0
- package/build/services/durable/schemas/factory.js +2356 -0
- package/build/services/durable/search.js +8 -8
- package/build/services/durable/worker.js +117 -25
- package/build/services/durable/workflow.d.ts +46 -43
- package/build/services/durable/workflow.js +273 -277
- package/build/services/engine/index.js +3 -0
- package/build/services/exporter/index.d.ts +2 -4
- package/build/services/exporter/index.js +4 -5
- package/build/services/mapper/index.d.ts +6 -2
- package/build/services/mapper/index.js +6 -2
- package/build/services/pipe/functions/array.d.ts +2 -10
- package/build/services/pipe/functions/array.js +30 -28
- package/build/services/pipe/functions/conditional.d.ts +1 -0
- package/build/services/pipe/functions/conditional.js +3 -0
- package/build/services/pipe/functions/date.d.ts +1 -0
- package/build/services/pipe/functions/date.js +4 -0
- package/build/services/pipe/functions/index.d.ts +2 -0
- package/build/services/pipe/functions/index.js +2 -0
- package/build/services/pipe/functions/logical.d.ts +5 -0
- package/build/services/pipe/functions/logical.js +12 -0
- package/build/services/pipe/functions/object.d.ts +3 -0
- package/build/services/pipe/functions/object.js +25 -7
- package/build/services/pipe/index.d.ts +20 -3
- package/build/services/pipe/index.js +82 -16
- package/build/services/router/index.js +14 -3
- package/build/services/serializer/index.d.ts +3 -2
- package/build/services/serializer/index.js +11 -4
- package/build/services/store/clients/ioredis.js +6 -6
- package/build/services/store/clients/redis.js +7 -7
- package/build/services/store/index.d.ts +2 -0
- package/build/services/store/index.js +4 -1
- package/build/services/stream/clients/ioredis.js +8 -8
- package/build/services/stream/clients/redis.js +1 -1
- package/build/types/activity.d.ts +60 -5
- package/build/types/durable.d.ts +168 -33
- package/build/types/exporter.d.ts +26 -4
- package/build/types/index.d.ts +2 -2
- package/build/types/job.d.ts +69 -5
- package/build/types/pipe.d.ts +81 -3
- package/build/types/stream.d.ts +61 -1
- package/build/types/stream.js +4 -0
- package/index.ts +1 -2
- package/modules/enums.ts +16 -8
- package/modules/errors.ts +174 -32
- package/package.json +7 -2
- package/services/activities/activity.ts +67 -18
- package/services/activities/await.ts +6 -6
- package/services/activities/cycle.ts +7 -6
- package/services/activities/hook.ts +4 -4
- package/services/activities/interrupt.ts +19 -9
- package/services/activities/signal.ts +6 -5
- package/services/activities/trigger.ts +16 -4
- package/services/activities/worker.ts +7 -7
- package/services/compiler/deployer.ts +33 -6
- package/services/compiler/validator.ts +7 -3
- package/services/durable/client.ts +47 -14
- package/services/durable/exporter.ts +110 -318
- package/services/durable/handle.ts +63 -50
- package/services/durable/index.ts +0 -2
- package/services/durable/schemas/factory.ts +2358 -0
- package/services/durable/search.ts +8 -8
- package/services/durable/worker.ts +128 -29
- package/services/durable/workflow.ts +304 -288
- package/services/engine/index.ts +4 -0
- package/services/exporter/index.ts +10 -12
- package/services/mapper/index.ts +6 -2
- package/services/pipe/functions/array.ts +24 -37
- package/services/pipe/functions/conditional.ts +4 -0
- package/services/pipe/functions/date.ts +6 -0
- package/services/pipe/functions/index.ts +7 -5
- package/services/pipe/functions/logical.ts +11 -0
- package/services/pipe/functions/object.ts +26 -7
- package/services/pipe/index.ts +99 -21
- package/services/quorum/index.ts +1 -3
- package/services/router/index.ts +14 -3
- package/services/serializer/index.ts +12 -5
- package/services/store/clients/ioredis.ts +6 -6
- package/services/store/clients/redis.ts +7 -7
- package/services/store/index.ts +4 -1
- package/services/stream/clients/ioredis.ts +8 -8
- package/services/stream/clients/redis.ts +1 -1
- package/types/activity.ts +87 -15
- package/types/durable.ts +246 -73
- package/types/exporter.ts +31 -5
- package/types/index.ts +6 -7
- package/types/job.ts +130 -36
- package/types/pipe.ts +84 -3
- package/types/stream.ts +82 -23
- package/build/services/durable/factory.d.ts +0 -17
- package/build/services/durable/factory.js +0 -817
- package/build/services/durable/meshos.d.ts +0 -127
- package/build/services/durable/meshos.js +0 -380
- package/services/durable/factory.ts +0 -818
- package/services/durable/meshos.ts +0 -441
|
@@ -1,159 +1,28 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ExporterService = void 0;
|
|
4
|
-
const serializer_1 = require("../serializer");
|
|
5
|
-
const utils_1 = require("../../modules/utils");
|
|
6
4
|
const key_1 = require("../../modules/key");
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
* Splits, Inflates, and Sorts the job data for use in durable contexts
|
|
10
|
-
*/
|
|
5
|
+
const utils_1 = require("../../modules/utils");
|
|
6
|
+
const serializer_1 = require("../serializer");
|
|
11
7
|
class ExporterService {
|
|
12
8
|
constructor(appId, store, logger) {
|
|
13
|
-
/**
|
|
14
|
-
* Friendly names for the activity ids
|
|
15
|
-
*/
|
|
16
|
-
this.activitySymbols = {
|
|
17
|
-
t1: 'trigger',
|
|
18
|
-
a1: 'pivot',
|
|
19
|
-
w1: 'worker',
|
|
20
|
-
a592: 'sleeper',
|
|
21
|
-
a594: 'awaiter',
|
|
22
|
-
a599: 'retryer',
|
|
23
|
-
c592: 'sleep_cycler',
|
|
24
|
-
c594: 'await_cycler',
|
|
25
|
-
c599: 'retry_cycler',
|
|
26
|
-
s5: 'scrubber',
|
|
27
|
-
sig: 'hook',
|
|
28
|
-
siga1: 'hook_pivot',
|
|
29
|
-
sigw1: 'hook_worker',
|
|
30
|
-
siga592: 'hook_sleeper',
|
|
31
|
-
siga594: 'hook_awaiter',
|
|
32
|
-
siga599: 'hook_retryer',
|
|
33
|
-
sigc592: 'hook_sleep_cycler',
|
|
34
|
-
sigc594: 'hook_await_cycler',
|
|
35
|
-
sigc599: 'hook_retry_cycler',
|
|
36
|
-
};
|
|
37
|
-
//adjacent transitions
|
|
38
|
-
this.transitions = {
|
|
39
|
-
trigger: ['pivot', 'hook'],
|
|
40
|
-
pivot: ['worker'],
|
|
41
|
-
worker: ['sleeper', 'awaiter', 'retryer', 'scrubber'],
|
|
42
|
-
sleeper: ['sleep_cycler'],
|
|
43
|
-
awaiter: ['await_cycler'],
|
|
44
|
-
retryer: ['retry_cycler'],
|
|
45
|
-
hook: ['hook_pivot'],
|
|
46
|
-
hook_pivot: ['hook_worker'],
|
|
47
|
-
hook_worker: ['hook_sleeper', 'hook_awaiter', 'hook_retryer'],
|
|
48
|
-
hook_sleeper: ['hook_sleep_cycler'],
|
|
49
|
-
hook_awaiter: ['hook_await_cycler'],
|
|
50
|
-
hook_retryer: ['hook_retry_cycler'],
|
|
51
|
-
};
|
|
52
|
-
//goto transitions
|
|
53
|
-
this.cycles = {
|
|
54
|
-
sleep_cycler: ['pivot'],
|
|
55
|
-
await_cycler: ['pivot'],
|
|
56
|
-
retry_cycler: ['pivot'],
|
|
57
|
-
hook_sleep_cycler: ['hook_pivot'],
|
|
58
|
-
hook_await_cycler: ['hook_pivot'],
|
|
59
|
-
hook_retry_cycler: ['hook_pivot'],
|
|
60
|
-
};
|
|
61
9
|
this.appId = appId;
|
|
62
10
|
this.logger = logger;
|
|
63
11
|
this.store = store;
|
|
64
|
-
this.serializer = new serializer_1.SerializerService();
|
|
65
12
|
}
|
|
66
13
|
/**
|
|
67
|
-
* Convert the job hash
|
|
68
|
-
*
|
|
69
|
-
* in terms relevant to narrative storytelling.
|
|
14
|
+
* Convert the job hash from its compiles format into a DurableJobExport object with
|
|
15
|
+
* facets that describe the workflow in terms relevant to narrative storytelling.
|
|
70
16
|
*/
|
|
71
17
|
async export(jobId, options = {}) {
|
|
72
18
|
if (!this.symbols) {
|
|
73
19
|
this.symbols = this.store.getAllSymbols();
|
|
74
20
|
this.symbols = await this.symbols;
|
|
75
21
|
}
|
|
76
|
-
const depData = await this.store.getDependencies(jobId);
|
|
77
22
|
const jobData = await this.store.getRaw(jobId);
|
|
78
|
-
const jobExport = this.inflate(jobData
|
|
23
|
+
const jobExport = this.inflate(jobData /*, depData*/);
|
|
79
24
|
return jobExport;
|
|
80
25
|
}
|
|
81
|
-
/**
|
|
82
|
-
* Interleave actions into the replay timeline to create
|
|
83
|
-
* a time-ordered timeline of the entire interaction, beginning
|
|
84
|
-
* with the entry trigger and concluding with the scrubber
|
|
85
|
-
* activity. Using the returned timeline, it is possible to
|
|
86
|
-
* create an animated narrative of the job, highlighting
|
|
87
|
-
* activities in the graph according to the timeline's
|
|
88
|
-
* activity-created (/ac) and activity-updated (/au) entries.
|
|
89
|
-
*/
|
|
90
|
-
createTimeline(replay, actions) {
|
|
91
|
-
const timeline = [];
|
|
92
|
-
replay.forEach((item) => {
|
|
93
|
-
const dimensions = item[0];
|
|
94
|
-
const parts = dimensions.split('/');
|
|
95
|
-
const activityName = item[1].split('/')[0];
|
|
96
|
-
const duplex = item[1].endsWith('/ac') ? 'entry' : 'exit';
|
|
97
|
-
const timestamp = item[2];
|
|
98
|
-
let event = {
|
|
99
|
-
activity: activityName,
|
|
100
|
-
duplex: duplex,
|
|
101
|
-
dimension: dimensions,
|
|
102
|
-
timestamp,
|
|
103
|
-
created: timestamp,
|
|
104
|
-
updated: timestamp,
|
|
105
|
-
};
|
|
106
|
-
const prior = timeline[timeline.length - 1];
|
|
107
|
-
if (prior && prior.activity === event.activity && prior.duplex !== event.duplex && prior.dimension === event.dimension) {
|
|
108
|
-
if (event.duplex === 'exit') {
|
|
109
|
-
prior.updated = event.timestamp;
|
|
110
|
-
}
|
|
111
|
-
else {
|
|
112
|
-
prior.created = event.timestamp;
|
|
113
|
-
}
|
|
114
|
-
event = prior;
|
|
115
|
-
}
|
|
116
|
-
else {
|
|
117
|
-
timeline.push(event);
|
|
118
|
-
}
|
|
119
|
-
if (this.isMainEntry(item[1])) {
|
|
120
|
-
event.actions = [];
|
|
121
|
-
this.interleaveActions(actions.main, event.actions);
|
|
122
|
-
}
|
|
123
|
-
else if (this.isHookEntry(item[1])) {
|
|
124
|
-
const hookDimension = `/${parts[1]}/${parts[2]}`;
|
|
125
|
-
const hookActions = actions.hooks[hookDimension];
|
|
126
|
-
event.actions = [];
|
|
127
|
-
this.interleaveActions(hookActions, event.actions);
|
|
128
|
-
}
|
|
129
|
-
});
|
|
130
|
-
return timeline;
|
|
131
|
-
}
|
|
132
|
-
/**
|
|
133
|
-
* Interleave actions into the 'worker' and 'hook_worker'
|
|
134
|
-
* activities (between their /ac and /au entries)
|
|
135
|
-
*/
|
|
136
|
-
interleaveActions(target, actions) {
|
|
137
|
-
if (target) {
|
|
138
|
-
for (let i = target.cursor + 1; i < target.items.length; i++) {
|
|
139
|
-
const [_, actionType, jobOrIndex] = target.items[i];
|
|
140
|
-
actions.push({ action: actionType, target: jobOrIndex });
|
|
141
|
-
target.cursor = i;
|
|
142
|
-
if (this.isPausingAction(actionType)) {
|
|
143
|
-
break;
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
isPausingAction(actionType) {
|
|
149
|
-
return actionType === 'sleep' || actionType === 'waitForSignal';
|
|
150
|
-
}
|
|
151
|
-
isMainEntry(key) {
|
|
152
|
-
return key.startsWith('worker/') && key.endsWith('/ac');
|
|
153
|
-
}
|
|
154
|
-
isHookEntry(key) {
|
|
155
|
-
return key.startsWith('hook_worker/') && key.endsWith('/ac');
|
|
156
|
-
}
|
|
157
26
|
/**
|
|
158
27
|
* Inflates the key from Redis, 3-character symbol
|
|
159
28
|
* into a human-readable JSON path, reflecting the
|
|
@@ -163,9 +32,6 @@ class ExporterService {
|
|
|
163
32
|
if (key in this.symbols) {
|
|
164
33
|
const path = this.symbols[key];
|
|
165
34
|
const parts = path.split('/');
|
|
166
|
-
if (parts[0] in this.activitySymbols) {
|
|
167
|
-
parts[0] = this.activitySymbols[parts[0]];
|
|
168
|
-
}
|
|
169
35
|
return parts.join('/');
|
|
170
36
|
}
|
|
171
37
|
return key;
|
|
@@ -177,101 +43,32 @@ class ExporterService {
|
|
|
177
43
|
* @param data - the dependency data from Redis
|
|
178
44
|
* @returns - the organized dependency data
|
|
179
45
|
*/
|
|
180
|
-
inflateDependencyData(data
|
|
181
|
-
const hookReg = /([0-9,]+)-(\d+)$/;
|
|
182
|
-
const flowReg = /-(\d+)$/;
|
|
46
|
+
inflateDependencyData(data) {
|
|
183
47
|
return data.map((dependency, index) => {
|
|
184
|
-
const [action, topic, gid,
|
|
185
|
-
const
|
|
186
|
-
const match = jobId.match(hookReg);
|
|
187
|
-
let prefix;
|
|
188
|
-
let type;
|
|
189
|
-
let dimensionKey = '';
|
|
190
|
-
if (match) {
|
|
191
|
-
//hook-originating dependency
|
|
192
|
-
const [_, dimension, counter] = match;
|
|
193
|
-
dimensionKey = dimension.split(',').join('/');
|
|
194
|
-
prefix = `${dimensionKey}[${counter}]`;
|
|
195
|
-
type = 'hook';
|
|
196
|
-
}
|
|
197
|
-
else {
|
|
198
|
-
const match = jobId.match(flowReg);
|
|
199
|
-
if (match) {
|
|
200
|
-
//main workflow-originating dependency
|
|
201
|
-
const [_, counter] = match;
|
|
202
|
-
prefix = `[${counter}]`;
|
|
203
|
-
type = 'flow';
|
|
204
|
-
}
|
|
205
|
-
else {
|
|
206
|
-
//'other' types like signal cleanup
|
|
207
|
-
prefix = '/';
|
|
208
|
-
type = 'other';
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
this.seedActions(type, action, topic, dependency, prefix, dimensionKey, actions, jobId);
|
|
48
|
+
const [action, topic, gid, dimension, ...jid] = dependency.split(key_1.VALSEP);
|
|
49
|
+
const job_id = jid.join(key_1.VALSEP);
|
|
212
50
|
return {
|
|
213
|
-
|
|
51
|
+
index,
|
|
52
|
+
action,
|
|
214
53
|
topic,
|
|
215
54
|
gid,
|
|
216
|
-
|
|
55
|
+
dimension,
|
|
56
|
+
job_id,
|
|
217
57
|
};
|
|
218
58
|
});
|
|
219
59
|
}
|
|
220
|
-
/**
|
|
221
|
-
* Adds historical actions (proxyActivity, executeChild)
|
|
222
|
-
* using the `dependency list` to determine
|
|
223
|
-
* after-the-fact what happened within the 'black-box'
|
|
224
|
-
* worker function. This is necessary to interleave the
|
|
225
|
-
* actions into the replay timeline, given that it isn't
|
|
226
|
-
* really possible to know the inner-workings of the user's
|
|
227
|
-
* function
|
|
228
|
-
*
|
|
229
|
-
*/
|
|
230
|
-
seedActions(type, action, topic, dep, prefix, dimensionKey, actions, jobId) {
|
|
231
|
-
if (type !== 'other' && action === 'expire-child') {
|
|
232
|
-
let depType;
|
|
233
|
-
if (topic == `${this.appId}.activity.execute`) {
|
|
234
|
-
depType = 'proxyActivity';
|
|
235
|
-
}
|
|
236
|
-
else if (topic == `${this.appId}.execute`) {
|
|
237
|
-
depType = 'executeChild';
|
|
238
|
-
}
|
|
239
|
-
else if (topic == `${this.appId}.wfsc.execute`) {
|
|
240
|
-
depType = 'waitForSignal';
|
|
241
|
-
}
|
|
242
|
-
if (depType) {
|
|
243
|
-
if (type === 'flow') {
|
|
244
|
-
actions.main.items.push([prefix, depType, jobId]);
|
|
245
|
-
}
|
|
246
|
-
else if (type === 'hook') {
|
|
247
|
-
if (!actions.hooks[dimensionKey]) {
|
|
248
|
-
actions.hooks[dimensionKey] = {
|
|
249
|
-
cursor: -1,
|
|
250
|
-
items: [],
|
|
251
|
-
};
|
|
252
|
-
}
|
|
253
|
-
actions.hooks[dimensionKey].items.push([prefix, depType, jobId]);
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
60
|
/**
|
|
259
61
|
* Inflates the job data from Redis into a DurableJobExport object
|
|
260
62
|
* @param jobHash - the job data from Redis
|
|
261
63
|
* @param dependencyList - the list of dependencies for the job
|
|
262
64
|
* @returns - the inflated job data
|
|
263
65
|
*/
|
|
264
|
-
inflate(jobHash
|
|
265
|
-
|
|
266
|
-
const actions = {
|
|
267
|
-
hooks: {},
|
|
268
|
-
main: { cursor: -1, items: [] },
|
|
269
|
-
};
|
|
270
|
-
const dependencies = this.inflateDependencyData(dependencyList, actions);
|
|
66
|
+
inflate(jobHash) {
|
|
67
|
+
const idempotents = [];
|
|
271
68
|
const state = {};
|
|
272
69
|
const data = {};
|
|
273
70
|
const other = [];
|
|
274
|
-
const replay =
|
|
71
|
+
const replay = {};
|
|
275
72
|
const regex = /^([a-zA-Z]{3}),(\d+(?:,\d+)*)/;
|
|
276
73
|
Object.entries(jobHash).forEach(([key, value]) => {
|
|
277
74
|
const match = key.match(regex);
|
|
@@ -281,7 +78,7 @@ class ExporterService {
|
|
|
281
78
|
}
|
|
282
79
|
else if (key.length === 3) {
|
|
283
80
|
//job state
|
|
284
|
-
state[this.inflateKey(key)] =
|
|
81
|
+
state[this.inflateKey(key)] = serializer_1.SerializerService.fromString(value);
|
|
285
82
|
}
|
|
286
83
|
else if (key.startsWith('_')) {
|
|
287
84
|
//job data
|
|
@@ -289,99 +86,112 @@ class ExporterService {
|
|
|
289
86
|
}
|
|
290
87
|
else if (key.startsWith('-')) {
|
|
291
88
|
//actions with side effect (replayable)
|
|
292
|
-
|
|
89
|
+
idempotents.push({
|
|
90
|
+
key,
|
|
91
|
+
value: serializer_1.SerializerService.fromString(value),
|
|
92
|
+
parts: extractParts(key),
|
|
93
|
+
});
|
|
293
94
|
}
|
|
294
95
|
else {
|
|
295
96
|
//collator guids, etc
|
|
296
97
|
other.push([null, key, value]);
|
|
297
98
|
}
|
|
298
99
|
});
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
100
|
+
const sortEntriesByCreated = (obj) => {
|
|
101
|
+
const entriesArray = Object.values(obj);
|
|
102
|
+
entriesArray.sort((a, b) => {
|
|
103
|
+
return (a.created || a.updated).localeCompare(b.created || b.updated);
|
|
104
|
+
});
|
|
105
|
+
return entriesArray;
|
|
106
|
+
};
|
|
107
|
+
/**
|
|
108
|
+
* idem list has a complicated sort order based on indexes and dimensions
|
|
109
|
+
*/
|
|
110
|
+
const sortParts = (parts) => {
|
|
111
|
+
return parts.sort((a, b) => {
|
|
112
|
+
const { dimension: aDim, index: aIdx, secondary: aSec } = a.parts;
|
|
113
|
+
const { dimension: bDim, index: bIdx, secondary: bSec } = b.parts;
|
|
114
|
+
if (aDim === undefined && bDim !== undefined)
|
|
115
|
+
return -1;
|
|
116
|
+
if (aDim !== undefined && bDim === undefined)
|
|
117
|
+
return 1;
|
|
118
|
+
if (aDim !== undefined && bDim !== undefined) {
|
|
119
|
+
if (aDim < bDim)
|
|
120
|
+
return -1;
|
|
121
|
+
if (aDim > bDim)
|
|
122
|
+
return 1;
|
|
123
|
+
}
|
|
124
|
+
if (aIdx < bIdx)
|
|
125
|
+
return -1;
|
|
126
|
+
if (aIdx > bIdx)
|
|
127
|
+
return 1;
|
|
128
|
+
if (aSec === undefined && bSec !== undefined)
|
|
129
|
+
return -1;
|
|
130
|
+
if (aSec !== undefined && bSec === undefined)
|
|
131
|
+
return 1;
|
|
132
|
+
if (aSec !== undefined && bSec !== undefined) {
|
|
133
|
+
if (aSec < bSec)
|
|
134
|
+
return -1;
|
|
135
|
+
if (aSec > bSec)
|
|
136
|
+
return 1;
|
|
137
|
+
}
|
|
138
|
+
return 0;
|
|
139
|
+
});
|
|
140
|
+
};
|
|
141
|
+
function extractParts(key) {
|
|
142
|
+
function extractDimension(label) {
|
|
143
|
+
const parts = label.split(',');
|
|
144
|
+
if (parts.length > 1) {
|
|
145
|
+
parts.shift();
|
|
146
|
+
return parts.join(',');
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
const parts = key.split('-');
|
|
150
|
+
if (parts.length === 4) {
|
|
151
|
+
//-proxy-5- -search-1-1-
|
|
152
|
+
return {
|
|
153
|
+
index: parseInt(parts[2], 10),
|
|
154
|
+
dimension: extractDimension(parts[1]),
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
//-search,0,0-1-1- -proxy,0,0-1-
|
|
159
|
+
return {
|
|
160
|
+
index: parseInt(parts[2], 10),
|
|
161
|
+
secondary: parseInt(parts[3], 10),
|
|
162
|
+
dimension: extractDimension(parts[1]),
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
}
|
|
304
166
|
return {
|
|
305
167
|
data: (0, utils_1.restoreHierarchy)(data),
|
|
306
|
-
|
|
168
|
+
idempotents: sortParts(idempotents),
|
|
307
169
|
state: Object.entries((0, utils_1.restoreHierarchy)(state))[0][1],
|
|
308
170
|
status: jobHash[':'],
|
|
309
|
-
|
|
310
|
-
transitions: { ...this.transitions },
|
|
311
|
-
cycles: { ...this.cycles },
|
|
171
|
+
replay: sortEntriesByCreated(replay),
|
|
312
172
|
};
|
|
313
173
|
}
|
|
314
174
|
inflateProcess(match, value, replay) {
|
|
315
|
-
const [_, letters,
|
|
175
|
+
const [_, letters, dimensions] = match;
|
|
316
176
|
const path = this.inflateKey(letters);
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
if (subcounter) {
|
|
331
|
-
counter = `${counter}.${subcounter}`;
|
|
332
|
-
}
|
|
333
|
-
const [type, ...dimensions] = dimensionalType.split(',');
|
|
334
|
-
let dimensionKey = '';
|
|
335
|
-
let isHook = false;
|
|
336
|
-
if (dimensions.length > 0) {
|
|
337
|
-
dimensionKey = `/${dimensions.join('/')}`;
|
|
338
|
-
isHook = true;
|
|
339
|
-
}
|
|
340
|
-
let targetList;
|
|
341
|
-
if (isHook) {
|
|
342
|
-
if (!actions.hooks[dimensionKey]) {
|
|
343
|
-
actions.hooks[dimensionKey] = {
|
|
344
|
-
cursor: -1,
|
|
345
|
-
items: [],
|
|
177
|
+
const parts = path.split('/');
|
|
178
|
+
const activity = parts[0];
|
|
179
|
+
const isCreate = path.endsWith('/output/metadata/ac');
|
|
180
|
+
const isUpdate = path.endsWith('/output/metadata/au');
|
|
181
|
+
if (isCreate || isUpdate) {
|
|
182
|
+
const targetName = `${activity},${dimensions}`;
|
|
183
|
+
let target = replay[targetName];
|
|
184
|
+
if (!target) {
|
|
185
|
+
replay[targetName] = {
|
|
186
|
+
activity,
|
|
187
|
+
dimensions,
|
|
188
|
+
created: isCreate ? value : null,
|
|
189
|
+
updated: isUpdate ? value : null,
|
|
346
190
|
};
|
|
347
191
|
}
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
else {
|
|
351
|
-
targetList = actions.main.items;
|
|
352
|
-
}
|
|
353
|
-
targetList.push([
|
|
354
|
-
`${dimensionKey}[${counter}]`,
|
|
355
|
-
type,
|
|
356
|
-
value,
|
|
357
|
-
]);
|
|
358
|
-
}
|
|
359
|
-
reverseSort(aKey, bKey) {
|
|
360
|
-
if (aKey[0] > bKey[0]) {
|
|
361
|
-
return 1;
|
|
362
|
-
}
|
|
363
|
-
else if (aKey[0] < bKey[0]) {
|
|
364
|
-
return -1;
|
|
365
|
-
}
|
|
366
|
-
else {
|
|
367
|
-
if (aKey[1] > bKey[1]) {
|
|
368
|
-
return 1;
|
|
369
|
-
}
|
|
370
|
-
else if (aKey[1] < bKey[1]) {
|
|
371
|
-
return -1;
|
|
192
|
+
else {
|
|
193
|
+
target[isCreate ? 'created' : 'updated'] = value;
|
|
372
194
|
}
|
|
373
|
-
return 0;
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
dateSort(aKey, bKey) {
|
|
377
|
-
if (aKey[2] > bKey[2]) {
|
|
378
|
-
return 1;
|
|
379
|
-
}
|
|
380
|
-
else if (aKey[2] < bKey[2]) {
|
|
381
|
-
return -1;
|
|
382
|
-
}
|
|
383
|
-
else {
|
|
384
|
-
return 0;
|
|
385
195
|
}
|
|
386
196
|
}
|
|
387
197
|
}
|
|
@@ -2,6 +2,7 @@ import { ExporterService } from './exporter';
|
|
|
2
2
|
import { HotMeshService as HotMesh } from '../hotmesh';
|
|
3
3
|
import { DurableJobExport } from '../../types/exporter';
|
|
4
4
|
import { JobInterruptOptions } from '../../types/job';
|
|
5
|
+
import { StreamError } from '../../types/stream';
|
|
5
6
|
export declare class WorkflowHandleService {
|
|
6
7
|
exporter: ExporterService;
|
|
7
8
|
hotMesh: HotMesh;
|
|
@@ -12,8 +13,8 @@ export declare class WorkflowHandleService {
|
|
|
12
13
|
/**
|
|
13
14
|
* Sends a signal to the workflow. This is a way to send
|
|
14
15
|
* a message to a workflow that is paused due to having
|
|
15
|
-
* executed
|
|
16
|
-
*
|
|
16
|
+
* executed `Durable.workflow.waitFor`. The workflow
|
|
17
|
+
* will awaken if no other signals are pending.
|
|
17
18
|
*/
|
|
18
19
|
signal(signalId: string, data: Record<any, any>): Promise<void>;
|
|
19
20
|
/**
|
|
@@ -45,9 +46,13 @@ export declare class WorkflowHandleService {
|
|
|
45
46
|
*/
|
|
46
47
|
interrupt(options?: JobInterruptOptions): Promise<string>;
|
|
47
48
|
/**
|
|
48
|
-
*
|
|
49
|
-
* the workflow
|
|
50
|
-
*
|
|
49
|
+
* Waits for the workflow to complete and returns the result. If
|
|
50
|
+
* the workflow response includes an error, this method will rethrow
|
|
51
|
+
* the error, including the stack trace if available.
|
|
52
|
+
* Wrap calls in a try/catch as necessary to avoid unhandled exceptions.
|
|
51
53
|
*/
|
|
52
|
-
result(
|
|
54
|
+
result<T>(config?: {
|
|
55
|
+
state?: boolean;
|
|
56
|
+
throwOnError?: boolean;
|
|
57
|
+
}): Promise<T | StreamError>;
|
|
53
58
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.WorkflowHandleService = void 0;
|
|
4
|
-
const enums_1 = require("../../modules/enums");
|
|
5
4
|
const exporter_1 = require("./exporter");
|
|
6
5
|
class WorkflowHandleService {
|
|
7
6
|
constructor(hotMesh, workflowTopic, workflowId) {
|
|
@@ -16,8 +15,8 @@ class WorkflowHandleService {
|
|
|
16
15
|
/**
|
|
17
16
|
* Sends a signal to the workflow. This is a way to send
|
|
18
17
|
* a message to a workflow that is paused due to having
|
|
19
|
-
* executed
|
|
20
|
-
*
|
|
18
|
+
* executed `Durable.workflow.waitFor`. The workflow
|
|
19
|
+
* will awaken if no other signals are pending.
|
|
21
20
|
*/
|
|
22
21
|
async signal(signalId, data) {
|
|
23
22
|
await this.hotMesh.hook(`${this.hotMesh.appId}.wfs.signal`, { id: signalId, data });
|
|
@@ -63,68 +62,82 @@ class WorkflowHandleService {
|
|
|
63
62
|
return await this.hotMesh.interrupt(`${this.hotMesh.appId}.execute`, this.workflowId, options);
|
|
64
63
|
}
|
|
65
64
|
/**
|
|
66
|
-
*
|
|
67
|
-
* the workflow
|
|
68
|
-
*
|
|
65
|
+
* Waits for the workflow to complete and returns the result. If
|
|
66
|
+
* the workflow response includes an error, this method will rethrow
|
|
67
|
+
* the error, including the stack trace if available.
|
|
68
|
+
* Wrap calls in a try/catch as necessary to avoid unhandled exceptions.
|
|
69
69
|
*/
|
|
70
|
-
async result(
|
|
71
|
-
if (loadState) {
|
|
72
|
-
const state = await this.hotMesh.getState(`${this.hotMesh.appId}.execute`, this.workflowId);
|
|
73
|
-
if (!state.data && state.metadata.err) {
|
|
74
|
-
throw new Error(JSON.parse(state.metadata.err));
|
|
75
|
-
}
|
|
76
|
-
if (state?.data?.done) {
|
|
77
|
-
//child flows are never 'done'; they use a hook
|
|
78
|
-
//that only closes upon parent flow completion.
|
|
79
|
-
return state.data.response;
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
let status = await this.hotMesh.getStatus(this.workflowId);
|
|
70
|
+
async result(config) {
|
|
83
71
|
const topic = `${this.hotMesh.appId}.executed.${this.workflowId}`;
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
72
|
+
let isResolved = false;
|
|
73
|
+
return new Promise(async (resolve, reject) => {
|
|
74
|
+
/**
|
|
75
|
+
* rejects/resolves the promise based on the `throwOnError`
|
|
76
|
+
* default behavior is to throw if error
|
|
77
|
+
*/
|
|
78
|
+
const safeReject = (err) => {
|
|
79
|
+
if (config?.throwOnError === false) {
|
|
80
|
+
return resolve(err);
|
|
81
|
+
}
|
|
82
|
+
reject(err);
|
|
83
|
+
};
|
|
84
|
+
/**
|
|
85
|
+
* Common completion function that unsubscribes from the topic/returns
|
|
86
|
+
*/
|
|
87
87
|
const complete = async (response, err) => {
|
|
88
88
|
if (isResolved)
|
|
89
89
|
return;
|
|
90
90
|
isResolved = true;
|
|
91
|
-
this.hotMesh.unsub(topic);
|
|
92
91
|
if (err) {
|
|
93
|
-
return
|
|
92
|
+
return safeReject(err);
|
|
94
93
|
}
|
|
95
94
|
else if (!response) {
|
|
96
95
|
const state = await this.hotMesh.getState(`${this.hotMesh.appId}.execute`, this.workflowId);
|
|
97
|
-
if (state.
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
96
|
+
if (state.data?.done && !state.data?.$error) {
|
|
97
|
+
return resolve(state.data.response);
|
|
98
|
+
}
|
|
99
|
+
else if (state.data?.$error) {
|
|
100
|
+
return safeReject(state.data.$error);
|
|
101
|
+
}
|
|
102
|
+
else if (state.metadata.err) {
|
|
103
|
+
return safeReject(JSON.parse(state.metadata.err));
|
|
102
104
|
}
|
|
103
105
|
response = state.data?.response;
|
|
104
106
|
}
|
|
105
107
|
resolve(response);
|
|
106
108
|
};
|
|
107
|
-
//
|
|
108
|
-
if (
|
|
109
|
-
|
|
109
|
+
//more expensive; fetches the entire job, not just the `status`
|
|
110
|
+
if (config?.state) {
|
|
111
|
+
const state = await this.hotMesh.getState(`${this.hotMesh.appId}.execute`, this.workflowId);
|
|
112
|
+
if (state?.data?.done && !state.data?.$error) {
|
|
113
|
+
return complete(state.data.response);
|
|
114
|
+
}
|
|
115
|
+
else if (state.data?.$error) {
|
|
116
|
+
return complete(null, state.data.$error);
|
|
117
|
+
}
|
|
118
|
+
else if (state.metadata.err) {
|
|
119
|
+
return complete(null, JSON.parse(state.metadata.err));
|
|
120
|
+
}
|
|
110
121
|
}
|
|
111
|
-
//subscribe to topic
|
|
112
|
-
this.hotMesh.sub(topic, async (
|
|
113
|
-
|
|
122
|
+
//subscribe to 'done' topic
|
|
123
|
+
this.hotMesh.sub(topic, async (_topic, state) => {
|
|
124
|
+
this.hotMesh.unsub(topic);
|
|
125
|
+
if (state.data.done && !state.data?.$error) {
|
|
126
|
+
await complete(state.data?.response);
|
|
127
|
+
}
|
|
128
|
+
else if (state.data?.$error) {
|
|
129
|
+
return complete(null, state.data.$error);
|
|
130
|
+
}
|
|
131
|
+
else if (state.metadata.err) {
|
|
114
132
|
const error = JSON.parse(state.metadata.err);
|
|
115
|
-
|
|
116
|
-
return await complete(null, state.metadata.err);
|
|
117
|
-
}
|
|
133
|
+
return await complete(null, error);
|
|
118
134
|
}
|
|
119
|
-
await complete(state.data?.response);
|
|
120
135
|
});
|
|
121
|
-
//
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
}
|
|
127
|
-
}, 0);
|
|
136
|
+
//check state in case completed during wiring
|
|
137
|
+
const status = await this.hotMesh.getStatus(this.workflowId);
|
|
138
|
+
if (status <= 0) {
|
|
139
|
+
await complete();
|
|
140
|
+
}
|
|
128
141
|
});
|
|
129
142
|
}
|
|
130
143
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { ClientService } from './client';
|
|
2
2
|
import { ConnectionService } from './connection';
|
|
3
|
-
import { MeshOSService } from './meshos';
|
|
4
3
|
import { Search } from './search';
|
|
5
4
|
import { WorkerService } from './worker';
|
|
6
5
|
import { WorkflowService } from './workflow';
|
|
@@ -9,7 +8,6 @@ export declare const Durable: {
|
|
|
9
8
|
Client: typeof ClientService;
|
|
10
9
|
Connection: typeof ConnectionService;
|
|
11
10
|
Search: typeof Search;
|
|
12
|
-
MeshOS: typeof MeshOSService;
|
|
13
11
|
Worker: typeof WorkerService;
|
|
14
12
|
workflow: typeof WorkflowService;
|
|
15
13
|
/**
|