@mnci/az-durable 0.1.2 → 0.1.4
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/dist/eslint-plugin.cjs.js +218 -219
- package/dist/index.cjs.js +160 -997
- package/dist/testing.cjs.js +130 -153
- package/package.json +3 -2
package/dist/testing.cjs.js
CHANGED
|
@@ -1,15 +1,5 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
function _instanceof(left, right) {
|
|
4
|
-
"@swc/helpers - instanceof";
|
|
5
|
-
if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) {
|
|
6
|
-
return !!right[Symbol.hasInstance](left);
|
|
7
|
-
} else return left instanceof right;
|
|
8
|
-
}
|
|
9
|
-
function _type_of(obj) {
|
|
10
|
-
"@swc/helpers - typeof";
|
|
11
|
-
return obj && typeof Symbol !== "undefined" && obj.constructor === Symbol ? "symbol" : typeof obj;
|
|
12
|
-
}
|
|
13
3
|
/**
|
|
14
4
|
* Runs an orchestration against stubbed activities, with no Azure running.
|
|
15
5
|
*
|
|
@@ -37,101 +27,89 @@ function _type_of(obj) {
|
|
|
37
27
|
* @throws Error when an activity is called with no stub registered for it.
|
|
38
28
|
* @typeParam TInput - The orchestration's input type.
|
|
39
29
|
* @typeParam TOutput - The orchestration's output type.
|
|
40
|
-
*/
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
};
|
|
57
|
-
};
|
|
58
|
-
var context = {
|
|
59
|
-
df: {
|
|
60
|
-
instanceId: (_stub_instanceId = stub.instanceId) !== null && _stub_instanceId !== void 0 ? _stub_instanceId : 'test-instance',
|
|
61
|
-
isReplaying: false,
|
|
62
|
-
currentUtcDateTime: clock,
|
|
63
|
-
callActivity: schedule,
|
|
64
|
-
callActivityWithRetry: function callActivityWithRetry(name, _retry, i) {
|
|
65
|
-
return schedule(name, i);
|
|
66
|
-
},
|
|
67
|
-
callSubOrchestrator: function callSubOrchestrator(name, i) {
|
|
68
|
-
return schedule(name, i);
|
|
69
|
-
},
|
|
70
|
-
callSubOrchestratorWithRetry: function callSubOrchestratorWithRetry(name, _retry, i) {
|
|
71
|
-
return schedule(name, i);
|
|
72
|
-
},
|
|
73
|
-
waitForExternalEvent: function waitForExternalEvent(name) {
|
|
74
|
-
return schedule(name, undefined);
|
|
75
|
-
},
|
|
76
|
-
// Timers complete immediately: there is no real time to wait for, and a
|
|
77
|
-
// harness that blocked on one would be useless. `cancel` is real,
|
|
78
|
-
// because an orchestration that correctly cancels its losing timer must
|
|
79
|
-
// not crash in a test for doing the right thing.
|
|
80
|
-
createTimer: function createTimer(fireAt) {
|
|
81
|
-
var timer = schedule('__timer', fireAt.toISOString());
|
|
82
|
-
timer.isCanceled = false;
|
|
83
|
-
timer.cancel = function() {
|
|
84
|
-
timer.isCanceled = true;
|
|
85
|
-
};
|
|
86
|
-
return timer;
|
|
87
|
-
},
|
|
88
|
-
setCustomStatus: function setCustomStatus(value) {
|
|
89
|
-
statuses.push(String(value));
|
|
90
|
-
},
|
|
91
|
-
continueAsNew: function continueAsNew(next) {
|
|
92
|
-
continuedAsNew = next;
|
|
93
|
-
},
|
|
94
|
-
Task: {
|
|
95
|
-
all: function all(tasks) {
|
|
96
|
-
return {
|
|
97
|
-
isCompleted: false,
|
|
98
|
-
isFaulted: false,
|
|
99
|
-
__all: tasks
|
|
100
|
-
};
|
|
101
|
-
},
|
|
102
|
-
// A marker, not a winner. `Task.any` resolves to the winning TASK, not
|
|
103
|
-
// to its value, so choosing here would hand the orchestration the
|
|
104
|
-
// wrong kind of thing — which is exactly the bug the reconstructed
|
|
105
|
-
// workflows found.
|
|
106
|
-
any: function any(tasks) {
|
|
107
|
-
return {
|
|
108
|
-
isCompleted: false,
|
|
109
|
-
isFaulted: false,
|
|
110
|
-
__any: tasks
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
}
|
|
30
|
+
*/
|
|
31
|
+
function runWorkflow(orchestration, input, stub) {
|
|
32
|
+
const calls = [];
|
|
33
|
+
const statuses = [];
|
|
34
|
+
let continuedAsNew;
|
|
35
|
+
const clock = stub.now ?? new Date(0);
|
|
36
|
+
const schedule = (name, scheduledInput) => {
|
|
37
|
+
calls.push({
|
|
38
|
+
name,
|
|
39
|
+
input: scheduledInput
|
|
40
|
+
});
|
|
41
|
+
return {
|
|
42
|
+
isCompleted: false,
|
|
43
|
+
isFaulted: false,
|
|
44
|
+
__name: name,
|
|
45
|
+
__input: scheduledInput
|
|
115
46
|
};
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
47
|
+
};
|
|
48
|
+
const context = {
|
|
49
|
+
df: {
|
|
50
|
+
instanceId: stub.instanceId ?? 'test-instance',
|
|
51
|
+
isReplaying: false,
|
|
52
|
+
currentUtcDateTime: clock,
|
|
53
|
+
callActivity: schedule,
|
|
54
|
+
callActivityWithRetry: (name, _retry, i) => schedule(name, i),
|
|
55
|
+
callSubOrchestrator: (name, i) => schedule(name, i),
|
|
56
|
+
callSubOrchestratorWithRetry: (name, _retry, i) => schedule(name, i),
|
|
57
|
+
waitForExternalEvent: name => schedule(name, undefined),
|
|
58
|
+
// Timers complete immediately: there is no real time to wait for, and a
|
|
59
|
+
// harness that blocked on one would be useless. `cancel` is real,
|
|
60
|
+
// because an orchestration that correctly cancels its losing timer must
|
|
61
|
+
// not crash in a test for doing the right thing.
|
|
62
|
+
createTimer: fireAt => {
|
|
63
|
+
const timer = schedule('__timer', fireAt.toISOString());
|
|
64
|
+
timer.isCanceled = false;
|
|
65
|
+
timer.cancel = () => {
|
|
66
|
+
timer.isCanceled = true;
|
|
67
|
+
};
|
|
68
|
+
return timer;
|
|
69
|
+
},
|
|
70
|
+
setCustomStatus: value => {
|
|
71
|
+
statuses.push(String(value));
|
|
72
|
+
},
|
|
73
|
+
continueAsNew: next => {
|
|
74
|
+
continuedAsNew = next;
|
|
75
|
+
},
|
|
76
|
+
Task: {
|
|
77
|
+
all: tasks => ({
|
|
78
|
+
isCompleted: false,
|
|
79
|
+
isFaulted: false,
|
|
80
|
+
__all: tasks
|
|
81
|
+
}),
|
|
82
|
+
// A marker, not a winner. `Task.any` resolves to the winning TASK, not
|
|
83
|
+
// to its value, so choosing here would hand the orchestration the
|
|
84
|
+
// wrong kind of thing — which is exactly the bug the reconstructed
|
|
85
|
+
// workflows found.
|
|
86
|
+
any: tasks => ({
|
|
87
|
+
isCompleted: false,
|
|
88
|
+
isFaulted: false,
|
|
89
|
+
__any: tasks
|
|
90
|
+
})
|
|
91
|
+
}
|
|
124
92
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
93
|
+
};
|
|
94
|
+
const generator = orchestration.handler(context, input);
|
|
95
|
+
let step = generator.next();
|
|
96
|
+
while (!step.done) {
|
|
97
|
+
const resumed = resolve(step.value, stub);
|
|
98
|
+
// INTO the generator, not out of the driver. Throwing here instead is the
|
|
99
|
+
// bug the reconstructed workflows found: every compensation branch was
|
|
100
|
+
// unreachable, while the docstring promised the opposite.
|
|
101
|
+
step = isThrowRequest(resumed) ? generator.throw(resumed.__throw) : generator.next(resumed);
|
|
102
|
+
}
|
|
103
|
+
return continuedAsNew === undefined ? {
|
|
104
|
+
result: step.value,
|
|
105
|
+
calls,
|
|
106
|
+
statuses
|
|
107
|
+
} : {
|
|
108
|
+
result: step.value,
|
|
109
|
+
calls,
|
|
110
|
+
statuses,
|
|
111
|
+
continuedAsNew
|
|
112
|
+
};
|
|
135
113
|
}
|
|
136
114
|
/**
|
|
137
115
|
* Produces the value the driver resumes a yielded task with.
|
|
@@ -145,37 +123,39 @@ function _type_of(obj) {
|
|
|
145
123
|
* @returns The value to resume with, or a {@link ThrowRequest} for the driver to inject.
|
|
146
124
|
* @throws Error naming an activity with no stub registered.
|
|
147
125
|
* @typeParam None - this function has no generic type parameters.
|
|
148
|
-
*/
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
126
|
+
*/
|
|
127
|
+
function resolve(task, stub) {
|
|
128
|
+
const fanOut = task.__all;
|
|
129
|
+
if (fanOut !== undefined) {
|
|
130
|
+
return fanOut.map(t => resolve(t, stub));
|
|
131
|
+
}
|
|
132
|
+
const race = task.__any;
|
|
133
|
+
if (race !== undefined) {
|
|
134
|
+
return resolveRace(race, stub);
|
|
135
|
+
}
|
|
136
|
+
const {
|
|
137
|
+
__name: name,
|
|
138
|
+
__input: input
|
|
139
|
+
} = task;
|
|
140
|
+
if (name === '__timer') {
|
|
141
|
+
return undefined;
|
|
142
|
+
}
|
|
143
|
+
const activity = stub.activities[name];
|
|
144
|
+
if (activity === undefined) {
|
|
145
|
+
// Naming the activity matters: the alternative is `undefined` flowing into
|
|
146
|
+
// the orchestration and failing somewhere unrelated.
|
|
147
|
+
throw new Error(`No stub registered for '${name}'. Add it to stub.activities to run this workflow.`);
|
|
148
|
+
}
|
|
149
|
+
const result = activity(input);
|
|
150
|
+
if (result instanceof Error) {
|
|
151
|
+
// A returned Error becomes a THROWN error inside the orchestration, which
|
|
152
|
+
// is what makes failure branches testable at all. Handed back as a request
|
|
153
|
+
// so the DRIVER injects it; see {@link ThrowRequest}.
|
|
154
|
+
return {
|
|
155
|
+
__throw: result
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
return result;
|
|
179
159
|
}
|
|
180
160
|
/**
|
|
181
161
|
* Settles a `Task.any` race and returns the winning TASK.
|
|
@@ -195,23 +175,18 @@ function _type_of(obj) {
|
|
|
195
175
|
* @returns The winning task, completed and carrying its result.
|
|
196
176
|
* @throws Error when `raceWinner` names a task that is not racing.
|
|
197
177
|
* @typeParam None - this function has no generic type parameters.
|
|
198
|
-
*/
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
}
|
|
211
|
-
var mutable = winner;
|
|
212
|
-
mutable.result = winner.__name === '__timer' ? undefined : resolve(winner, stub);
|
|
213
|
-
mutable.isCompleted = true;
|
|
214
|
-
return winner;
|
|
178
|
+
*/
|
|
179
|
+
function resolveRace(candidates, stub) {
|
|
180
|
+
const names = candidates.map(c => c.__name);
|
|
181
|
+
const chosen = stub.raceWinner?.(names) ?? names[0];
|
|
182
|
+
const winner = candidates.find(c => c.__name === chosen);
|
|
183
|
+
if (winner === undefined) {
|
|
184
|
+
throw new Error(`raceWinner chose '${String(chosen)}', which is not racing. Candidates: ${names.join(', ')}.`);
|
|
185
|
+
}
|
|
186
|
+
const mutable = winner;
|
|
187
|
+
mutable.result = winner.__name === '__timer' ? undefined : resolve(winner, stub);
|
|
188
|
+
mutable.isCompleted = true;
|
|
189
|
+
return winner;
|
|
215
190
|
}
|
|
216
191
|
/**
|
|
217
192
|
* Whether a resolved value is a request to throw inside the orchestration.
|
|
@@ -220,8 +195,10 @@ function _type_of(obj) {
|
|
|
220
195
|
* @returns `true` when the driver should call `generator.throw`.
|
|
221
196
|
* @throws Never - a type guard.
|
|
222
197
|
* @typeParam None - this function has no generic type parameters.
|
|
223
|
-
*/
|
|
224
|
-
|
|
198
|
+
*/
|
|
199
|
+
function isThrowRequest(value) {
|
|
200
|
+
return typeof value === 'object' && value !== null && '__throw' in value;
|
|
225
201
|
}
|
|
226
202
|
|
|
227
203
|
exports.runWorkflow = runWorkflow;
|
|
204
|
+
//# sourceMappingURL=testing.cjs.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mnci/az-durable",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"main": "./dist/index.cjs.js",
|
|
5
5
|
"types": "./dist/src/index.d.ts",
|
|
6
6
|
"exports": {
|
|
@@ -27,7 +27,8 @@
|
|
|
27
27
|
"files": [
|
|
28
28
|
"dist",
|
|
29
29
|
"!**/*.tsbuildinfo",
|
|
30
|
-
"!**/*.d.ts.map"
|
|
30
|
+
"!**/*.d.ts.map",
|
|
31
|
+
"!**/*.js.map"
|
|
31
32
|
],
|
|
32
33
|
"publishConfig": {
|
|
33
34
|
"access": "public"
|