@gesslar/actioneer 2.0.2 → 2.2.0
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 +212 -13
- package/package.json +5 -5
- package/src/browser/lib/ActionBuilder.js +44 -7
- package/src/browser/lib/ActionRunner.js +139 -90
- package/src/browser/lib/ActionWrapper.js +52 -8
- package/src/browser/lib/Activity.js +39 -8
- package/src/browser/lib/Piper.js +2 -2
- package/src/lib/ActionHooks.js +8 -0
- package/src/types/browser/lib/ActionBuilder.d.ts +24 -8
- package/src/types/browser/lib/ActionBuilder.d.ts.map +1 -1
- package/src/types/browser/lib/ActionRunner.d.ts +7 -2
- package/src/types/browser/lib/ActionRunner.d.ts.map +1 -1
- package/src/types/browser/lib/ActionWrapper.d.ts +24 -5
- package/src/types/browser/lib/ActionWrapper.d.ts.map +1 -1
- package/src/types/browser/lib/Activity.d.ts +29 -8
- package/src/types/browser/lib/Activity.d.ts.map +1 -1
- package/src/types/lib/ActionBuilder.d.ts +7 -0
- package/src/types/lib/ActionHooks.d.ts +25 -1
- package/src/types/lib/ActionHooks.d.ts.map +1 -1
- package/src/types/lib/ActionWrapper.d.ts +7 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {Promised, Data, Sass, Valid} from "@gesslar/toolkit"
|
|
1
|
+
import {Promised, Data, Sass, Valid, Notify} from "@gesslar/toolkit"
|
|
2
2
|
|
|
3
3
|
import {ACTIVITY} from "./Activity.js"
|
|
4
4
|
import Piper from "./Piper.js"
|
|
@@ -7,10 +7,15 @@ import Piper from "./Piper.js"
|
|
|
7
7
|
* @typedef {(message: string, level?: number, ...args: Array<unknown>) => void} DebugFn
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* @typedef {import("./ActionBuilder.js").default} ActionBuilder
|
|
12
|
+
*/
|
|
13
|
+
|
|
10
14
|
/**
|
|
11
15
|
* @typedef {object} ActionRunnerOptions
|
|
12
16
|
* @property {DebugFn} [debug] Logger function.
|
|
13
17
|
*/
|
|
18
|
+
|
|
14
19
|
/**
|
|
15
20
|
* Orchestrates execution of {@link ActionBuilder}-produced pipelines.
|
|
16
21
|
*
|
|
@@ -31,6 +36,13 @@ export default class ActionRunner extends Piper {
|
|
|
31
36
|
*/
|
|
32
37
|
#debug = () => {}
|
|
33
38
|
|
|
39
|
+
/**
|
|
40
|
+
* Event emitter for cross-runner communication (BREAK/CONTINUE signals).
|
|
41
|
+
*
|
|
42
|
+
* @type {typeof Notify}
|
|
43
|
+
*/
|
|
44
|
+
#notify = Notify
|
|
45
|
+
|
|
34
46
|
/**
|
|
35
47
|
* Instantiate a runner over an optional action wrapper.
|
|
36
48
|
*
|
|
@@ -58,108 +70,146 @@ export default class ActionRunner extends Piper {
|
|
|
58
70
|
/**
|
|
59
71
|
* Executes the configured action pipeline.
|
|
60
72
|
* Builds the ActionWrapper on first run and caches it for subsequent calls.
|
|
61
|
-
* Supports WHILE, UNTIL,
|
|
73
|
+
* Supports WHILE, UNTIL, IF, SPLIT, BREAK, and CONTINUE activity kinds.
|
|
62
74
|
*
|
|
63
75
|
* @param {unknown} context - Seed value passed to the first activity.
|
|
76
|
+
* @param {import("./ActionWrapper.js").default|null} [parentWrapper] - Parent wrapper for BREAK/CONTINUE signaling.
|
|
64
77
|
* @returns {Promise<unknown>} Final value produced by the pipeline.
|
|
65
78
|
* @throws {Sass} When no activities are registered, conflicting activity kinds are used, or execution fails.
|
|
66
79
|
*/
|
|
67
|
-
async run(context) {
|
|
80
|
+
async run(context, parentWrapper=null) {
|
|
68
81
|
if(!this.#actionWrapper)
|
|
69
82
|
this.#actionWrapper = await this.#actionBuilder.build()
|
|
70
83
|
|
|
71
84
|
const actionWrapper = this.#actionWrapper
|
|
72
|
-
const activities = actionWrapper.activities
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
//
|
|
86
|
-
//
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
if(!await this.#hasPredicate(activity,predicate,context))
|
|
85
|
+
const activities = Array.from(actionWrapper.activities)
|
|
86
|
+
|
|
87
|
+
try {
|
|
88
|
+
for(
|
|
89
|
+
let cursor = 0, max = activities.length;
|
|
90
|
+
cursor < max && cursor !== -1;
|
|
91
|
+
cursor++
|
|
92
|
+
) {
|
|
93
|
+
const activity = activities[cursor]
|
|
94
|
+
|
|
95
|
+
try {
|
|
96
|
+
const kind = activity.kind
|
|
97
|
+
|
|
98
|
+
// If we have no kind, then it's just a once.
|
|
99
|
+
// Get it over and done with!
|
|
100
|
+
if(!kind) {
|
|
101
|
+
context = await this.#execute(activity, context)
|
|
102
|
+
} else {
|
|
103
|
+
const {UNTIL, WHILE, IF, SPLIT, BREAK, CONTINUE} = ACTIVITY
|
|
104
|
+
const kindUntil = kind === UNTIL
|
|
105
|
+
const kindWhile = kind === WHILE
|
|
106
|
+
const kindIf = kind === IF
|
|
107
|
+
const kindSplit = kind === SPLIT
|
|
108
|
+
const kindBreak = kind === BREAK
|
|
109
|
+
const kindContinue = kind === CONTINUE
|
|
110
|
+
|
|
111
|
+
if(kindBreak || kindContinue) {
|
|
112
|
+
if(!parentWrapper)
|
|
113
|
+
throw Sass.new(`Invalid use of control flow outside of context.`)
|
|
114
|
+
|
|
115
|
+
if(await this.#evalPredicate(activity, context)) {
|
|
116
|
+
if(kindBreak) {
|
|
117
|
+
this.#notify.emit("loop.break", parentWrapper)
|
|
106
118
|
break
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if(kindContinue)
|
|
122
|
+
cursor = max
|
|
123
|
+
}
|
|
124
|
+
} else if(kindIf) {
|
|
125
|
+
if(await this.#evalPredicate(activity, context))
|
|
126
|
+
context = await this.#execute(activity, context)
|
|
127
|
+
} else if(kindWhile || kindUntil) {
|
|
128
|
+
// Simple if, no loop, only gainz.
|
|
129
|
+
for(;;) {
|
|
130
|
+
if(kindWhile)
|
|
131
|
+
if(!await this.#evalPredicate(activity, context))
|
|
132
|
+
break
|
|
133
|
+
|
|
134
|
+
let weWereOnABreak = false
|
|
135
|
+
const breakReceiver = this.#notify.on("loop.break", wrapper => {
|
|
136
|
+
if(wrapper.id === actionWrapper.id) {
|
|
137
|
+
weWereOnABreak = true
|
|
138
|
+
}
|
|
139
|
+
})
|
|
140
|
+
context = await this.#execute(activity, context)
|
|
141
|
+
breakReceiver()
|
|
142
|
+
if(weWereOnABreak)
|
|
112
143
|
break
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
144
|
+
|
|
145
|
+
if(kindUntil)
|
|
146
|
+
if(await this.#evalPredicate(activity, context))
|
|
147
|
+
break
|
|
148
|
+
}
|
|
149
|
+
} else if(kindSplit) {
|
|
150
|
+
// SPLIT activity: parallel execution with splitter/rejoiner
|
|
151
|
+
// pattern
|
|
152
|
+
const {splitter, rejoiner} = activity
|
|
153
|
+
|
|
154
|
+
if(!splitter || !rejoiner)
|
|
155
|
+
throw Sass.new(
|
|
156
|
+
`SPLIT activity "${String(activity.name)}" requires both ` +
|
|
157
|
+
`splitter and rejoiner functions.`
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
const original = context
|
|
161
|
+
const splitContexts = await splitter.call(
|
|
162
|
+
activity.action,
|
|
163
|
+
context
|
|
123
164
|
)
|
|
124
165
|
|
|
125
|
-
|
|
126
|
-
const splitContexts = await splitter.call(activity.action, context)
|
|
166
|
+
let settled
|
|
127
167
|
|
|
128
|
-
|
|
168
|
+
if(activity.opKind === "ActionBuilder") {
|
|
169
|
+
if(activity.action)
|
|
170
|
+
activity.op.withAction(activity.action)
|
|
129
171
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
activity.op.withAction(activity.action)
|
|
172
|
+
if(activity.hooks)
|
|
173
|
+
activity.op.withHooks(activity.hooks)
|
|
133
174
|
|
|
134
|
-
|
|
135
|
-
|
|
175
|
+
const runner = new this.constructor(activity.op, {
|
|
176
|
+
debug: this.#debug, name: activity.name
|
|
177
|
+
})
|
|
136
178
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
179
|
+
settled = await runner.pipe(splitContexts)
|
|
180
|
+
} else {
|
|
181
|
+
// For plain functions, process each split context
|
|
182
|
+
settled = await Promised.settle(
|
|
183
|
+
splitContexts.map(ctx => this.#execute(activity, ctx))
|
|
184
|
+
)
|
|
185
|
+
}
|
|
140
186
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
settled = await Promised.settle(
|
|
146
|
-
splitContexts.map(ctx => this.#execute(activity, ctx))
|
|
187
|
+
const rejoined = await rejoiner.call(
|
|
188
|
+
activity.action,
|
|
189
|
+
original,
|
|
190
|
+
settled
|
|
147
191
|
)
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
const rejoined = await rejoiner.call(
|
|
151
|
-
activity.action,
|
|
152
|
-
original,
|
|
153
|
-
settled
|
|
154
|
-
)
|
|
155
192
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
193
|
+
context = rejoined
|
|
194
|
+
} else {
|
|
195
|
+
context = await this.#execute(activity, context)
|
|
196
|
+
}
|
|
159
197
|
}
|
|
198
|
+
} catch(error) {
|
|
199
|
+
throw Sass.new("ActionRunner running activity", error)
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
} finally {
|
|
203
|
+
// Execute done callback if registered - always runs, even on error
|
|
204
|
+
// Only run for top-level pipelines, not nested builders (inside loops)
|
|
205
|
+
if(actionWrapper.done && !parentWrapper) {
|
|
206
|
+
try {
|
|
207
|
+
context = await actionWrapper.done.call(
|
|
208
|
+
actionWrapper.action, context
|
|
209
|
+
)
|
|
210
|
+
} catch(error) {
|
|
211
|
+
throw Sass.new("ActionRunner running done callback", error)
|
|
160
212
|
}
|
|
161
|
-
} catch(error) {
|
|
162
|
-
throw Sass.new("ActionRunner running activity", error)
|
|
163
213
|
}
|
|
164
214
|
}
|
|
165
215
|
|
|
@@ -201,11 +251,11 @@ export default class ActionRunner extends Piper {
|
|
|
201
251
|
if(parallel) {
|
|
202
252
|
return await runner.pipe(context)
|
|
203
253
|
} else {
|
|
204
|
-
return await runner.run(context)
|
|
254
|
+
return await runner.run(context, activity.wrapper)
|
|
205
255
|
}
|
|
206
256
|
} else if(opKind === "Function") {
|
|
207
257
|
try {
|
|
208
|
-
const result = await activity.run(context)
|
|
258
|
+
const result = await activity.run(context, activity.wrapper)
|
|
209
259
|
|
|
210
260
|
if(Data.isType(result, "ActionBuilder")) {
|
|
211
261
|
if(activity.action)
|
|
@@ -221,7 +271,7 @@ export default class ActionRunner extends Piper {
|
|
|
221
271
|
if(parallel) {
|
|
222
272
|
return await runner.pipe(context)
|
|
223
273
|
} else {
|
|
224
|
-
return await runner.run(context)
|
|
274
|
+
return await runner.run(context, activity.wrapper)
|
|
225
275
|
}
|
|
226
276
|
} else {
|
|
227
277
|
return result
|
|
@@ -237,18 +287,17 @@ export default class ActionRunner extends Piper {
|
|
|
237
287
|
}
|
|
238
288
|
|
|
239
289
|
/**
|
|
240
|
-
* Evaluate the predicate for WHILE/UNTIL activity kinds.
|
|
290
|
+
* Evaluate the predicate for WHILE/UNTIL/IF/BREAK/CONTINUE activity kinds.
|
|
241
291
|
*
|
|
242
292
|
* @param {import("./Activity.js").default} activity Activity currently executing.
|
|
243
|
-
* @param {(context: unknown) => boolean|Promise<boolean>} predicate Predicate to evaluate.
|
|
244
293
|
* @param {unknown} context Current pipeline context.
|
|
245
|
-
* @returns {Promise<boolean>} True when the predicate
|
|
294
|
+
* @returns {Promise<boolean>} True when the predicate condition is met.
|
|
246
295
|
* @private
|
|
247
296
|
*/
|
|
248
|
-
async #
|
|
249
|
-
Valid.type(
|
|
297
|
+
async #evalPredicate(activity, context) {
|
|
298
|
+
Valid.type(activity?.pred, "Function")
|
|
250
299
|
|
|
251
|
-
return !!(await
|
|
300
|
+
return !!(await activity.pred.call(activity.action, context))
|
|
252
301
|
}
|
|
253
302
|
|
|
254
303
|
toString() {
|
|
@@ -11,7 +11,7 @@ import Activity from "./Activity.js"
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
|
-
* @typedef {Generator<Activity, void, unknown>} ActivityIterator
|
|
14
|
+
* @typedef {import("@gesslar/toolkit").Generator<Activity, void, unknown>} ActivityIterator
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
17
|
/**
|
|
@@ -24,6 +24,7 @@ export default class ActionWrapper {
|
|
|
24
24
|
* @type {Map<string|symbol, WrappedActivityConfig>}
|
|
25
25
|
*/
|
|
26
26
|
#activities = new Map()
|
|
27
|
+
|
|
27
28
|
/**
|
|
28
29
|
* Logger invoked for wrapper lifecycle events.
|
|
29
30
|
*
|
|
@@ -31,17 +32,37 @@ export default class ActionWrapper {
|
|
|
31
32
|
*/
|
|
32
33
|
#debug = () => {}
|
|
33
34
|
|
|
35
|
+
/** @type {import("./ActionHooks.js").default|null} */
|
|
34
36
|
#hooks = null
|
|
37
|
+
/** @type {((context: unknown) => unknown|Promise<unknown>)|null} */
|
|
38
|
+
#done = null
|
|
39
|
+
/** @type {unknown} */
|
|
40
|
+
#action = null
|
|
41
|
+
/** @type {symbol} */
|
|
42
|
+
#id = Symbol(performance.now())
|
|
35
43
|
|
|
36
44
|
/**
|
|
37
45
|
* Create a wrapper from the builder payload.
|
|
38
46
|
*
|
|
39
47
|
* @param {{activities: Map<string|symbol, WrappedActivityConfig>, debug: (message: string, level?: number, ...args: Array<unknown>) => void}} init Builder payload containing activities + logger.
|
|
40
48
|
*/
|
|
41
|
-
constructor({activities,hooks,debug}) {
|
|
49
|
+
constructor({activities,hooks,debug,done: doneCallback,action}) {
|
|
42
50
|
this.#debug = debug
|
|
43
51
|
this.#hooks = hooks
|
|
44
|
-
this.#
|
|
52
|
+
this.#done = doneCallback
|
|
53
|
+
this.#action = action
|
|
54
|
+
|
|
55
|
+
for(const [key, value] of activities) {
|
|
56
|
+
this.#activities.set(
|
|
57
|
+
key,
|
|
58
|
+
new Activity({
|
|
59
|
+
...value,
|
|
60
|
+
hooks: this.#hooks,
|
|
61
|
+
wrapper: this
|
|
62
|
+
})
|
|
63
|
+
)
|
|
64
|
+
}
|
|
65
|
+
|
|
45
66
|
this.#debug(
|
|
46
67
|
"Instantiating ActionWrapper with %o activities.",
|
|
47
68
|
2,
|
|
@@ -49,17 +70,40 @@ export default class ActionWrapper {
|
|
|
49
70
|
)
|
|
50
71
|
}
|
|
51
72
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
73
|
+
/**
|
|
74
|
+
* Unique identifier for this wrapper instance.
|
|
75
|
+
* Used by BREAK/CONTINUE to match events to the correct loop.
|
|
76
|
+
*
|
|
77
|
+
* @returns {symbol} Unique symbol identifier
|
|
78
|
+
*/
|
|
79
|
+
get id() {
|
|
80
|
+
return this.#id
|
|
55
81
|
}
|
|
56
82
|
|
|
57
83
|
/**
|
|
58
84
|
* Iterator over the registered activities.
|
|
59
85
|
*
|
|
60
|
-
* @returns {
|
|
86
|
+
* @returns {Iterator<Activity>} Iterator yielding Activity instances.
|
|
61
87
|
*/
|
|
62
88
|
get activities() {
|
|
63
|
-
return this.#
|
|
89
|
+
return this.#activities.values()
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Get the done callback if registered.
|
|
94
|
+
*
|
|
95
|
+
* @returns {((context: unknown) => unknown|Promise<unknown>)|null} Done callback or null.
|
|
96
|
+
*/
|
|
97
|
+
get done() {
|
|
98
|
+
return this.#done
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Get the action instance.
|
|
103
|
+
*
|
|
104
|
+
* @returns {unknown|null} Action instance or null.
|
|
105
|
+
*/
|
|
106
|
+
get action() {
|
|
107
|
+
return this.#action
|
|
64
108
|
}
|
|
65
109
|
}
|
|
@@ -8,14 +8,20 @@ import {Data} from "@gesslar/toolkit"
|
|
|
8
8
|
*
|
|
9
9
|
* @readonly
|
|
10
10
|
* @enum {number}
|
|
11
|
-
* @property {number} WHILE - Execute activity while predicate returns true
|
|
12
|
-
* @property {number} UNTIL - Execute activity until predicate returns true
|
|
13
|
-
* @property {number} SPLIT - Execute activity with split/rejoin pattern for parallel execution
|
|
11
|
+
* @property {number} WHILE - Execute activity while predicate returns true 1
|
|
12
|
+
* @property {number} UNTIL - Execute activity until predicate returns true 2
|
|
13
|
+
* @property {number} SPLIT - Execute activity with split/rejoin pattern for parallel execution 3
|
|
14
|
+
* @property {number} IF - Execute activity if predicate returns true 4
|
|
15
|
+
* @property {number} BREAK - Break out of a WHILE/UNTIL if predicate returns true 5
|
|
16
|
+
* @property {number} CONTINUE - Returns to the top of a WHILE/UNTIL if predicate returns true 6
|
|
14
17
|
*/
|
|
15
18
|
export const ACTIVITY = Object.freeze({
|
|
16
|
-
WHILE: 1
|
|
17
|
-
UNTIL:
|
|
18
|
-
SPLIT:
|
|
19
|
+
WHILE: 1,
|
|
20
|
+
UNTIL: 2,
|
|
21
|
+
SPLIT: 3,
|
|
22
|
+
IF: 4,
|
|
23
|
+
BREAK: 5,
|
|
24
|
+
CONTINUE: 6,
|
|
19
25
|
})
|
|
20
26
|
|
|
21
27
|
export default class Activity {
|
|
@@ -37,6 +43,10 @@ export default class Activity {
|
|
|
37
43
|
#rejoiner = null
|
|
38
44
|
/** @type {((context: unknown) => unknown)|null} */
|
|
39
45
|
#splitter = null
|
|
46
|
+
/** @type {import("./ActionWrapper.js").default|null} */
|
|
47
|
+
#wrapper = null
|
|
48
|
+
/** @type {symbol} */
|
|
49
|
+
#id = Symbol(performance.now())
|
|
40
50
|
|
|
41
51
|
/**
|
|
42
52
|
* Construct an Activity definition wrapper.
|
|
@@ -50,8 +60,9 @@ export default class Activity {
|
|
|
50
60
|
* @param {ActionHooks} [init.hooks] - Optional hooks instance
|
|
51
61
|
* @param {(context: unknown) => unknown} [init.splitter] - Optional splitter function for SPLIT activities
|
|
52
62
|
* @param {(originalContext: unknown, splitResults: unknown) => unknown} [init.rejoiner] - Optional rejoiner function for SPLIT activities
|
|
63
|
+
* @param {import("./ActionWrapper.js").default} [init.wrapper] - Optional wrapper containing this activity
|
|
53
64
|
*/
|
|
54
|
-
constructor({action,name,op,kind,pred,hooks,splitter,rejoiner}) {
|
|
65
|
+
constructor({action,name,op,kind,pred,hooks,splitter,rejoiner,wrapper}) {
|
|
55
66
|
this.#action = action
|
|
56
67
|
this.#hooks = hooks
|
|
57
68
|
this.#kind = kind
|
|
@@ -60,6 +71,16 @@ export default class Activity {
|
|
|
60
71
|
this.#pred = pred
|
|
61
72
|
this.#rejoiner = rejoiner
|
|
62
73
|
this.#splitter = splitter
|
|
74
|
+
this.#wrapper = wrapper ?? null
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Unique identifier for this activity instance.
|
|
79
|
+
*
|
|
80
|
+
* @returns {symbol} Unique symbol identifier
|
|
81
|
+
*/
|
|
82
|
+
get id() {
|
|
83
|
+
return this.#id
|
|
63
84
|
}
|
|
64
85
|
|
|
65
86
|
/**
|
|
@@ -81,7 +102,7 @@ export default class Activity {
|
|
|
81
102
|
}
|
|
82
103
|
|
|
83
104
|
/**
|
|
84
|
-
* The predicate function for WHILE/UNTIL flows.
|
|
105
|
+
* The predicate function for WHILE/UNTIL/IF flows.
|
|
85
106
|
*
|
|
86
107
|
* @returns {(context: unknown) => boolean|Promise<boolean>|undefined} - Predicate used to continue/stop loops
|
|
87
108
|
*/
|
|
@@ -143,6 +164,16 @@ export default class Activity {
|
|
|
143
164
|
return this.#action
|
|
144
165
|
}
|
|
145
166
|
|
|
167
|
+
/**
|
|
168
|
+
* Get the ActionWrapper containing this activity.
|
|
169
|
+
* Used by BREAK/CONTINUE to signal the parent loop.
|
|
170
|
+
*
|
|
171
|
+
* @returns {import("./ActionWrapper.js").default|null} The wrapper or null
|
|
172
|
+
*/
|
|
173
|
+
get wrapper() {
|
|
174
|
+
return this.#wrapper ?? null
|
|
175
|
+
}
|
|
176
|
+
|
|
146
177
|
/**
|
|
147
178
|
* Execute the activity with before/after hooks.
|
|
148
179
|
*
|
package/src/browser/lib/Piper.js
CHANGED
|
@@ -109,7 +109,7 @@ export default class Piper {
|
|
|
109
109
|
}
|
|
110
110
|
|
|
111
111
|
const setupResult = await Promised.settle(
|
|
112
|
-
[...this.#lifeCycle.get("setup")].map(e => e())
|
|
112
|
+
[...this.#lifeCycle.get("setup")].map(e => Promise.resolve(e()))
|
|
113
113
|
)
|
|
114
114
|
|
|
115
115
|
this.#processResult("Setting up the pipeline.", setupResult)
|
|
@@ -127,7 +127,7 @@ export default class Piper {
|
|
|
127
127
|
} finally {
|
|
128
128
|
// Run cleanup hooks
|
|
129
129
|
const teardownResult = await Promised.settle(
|
|
130
|
-
[...this.#lifeCycle.get("teardown")].map(e => e())
|
|
130
|
+
[...this.#lifeCycle.get("teardown")].map(e => Promise.resolve(e()))
|
|
131
131
|
)
|
|
132
132
|
|
|
133
133
|
this.#processResult("Tearing down the pipeline.", teardownResult)
|
package/src/lib/ActionHooks.js
CHANGED
|
@@ -41,6 +41,14 @@ export default class ActionHooks extends BrowserActionHooks {
|
|
|
41
41
|
return this.#hooksFile
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
/**
|
|
45
|
+
* @typedef {object} ActionHooksConfig
|
|
46
|
+
* @property {string} [hooksFile] Path to the hooks file
|
|
47
|
+
* @property {string} [hooksKind] Name of the hooks class to instantiate
|
|
48
|
+
* @property {object} [hooks] Pre-instantiated hooks object
|
|
49
|
+
* @property {number} [timeout] Timeout for hook execution
|
|
50
|
+
*/
|
|
51
|
+
|
|
44
52
|
/**
|
|
45
53
|
* Static factory method to create and initialize a hook manager.
|
|
46
54
|
* Loads hooks from the specified file and returns an initialized instance.
|
|
@@ -58,9 +58,10 @@ export default class ActionBuilder {
|
|
|
58
58
|
* Register an activity that the runner can execute.
|
|
59
59
|
*
|
|
60
60
|
* Overloads:
|
|
61
|
-
* - do(name, op)
|
|
62
|
-
* - do(name, kind, pred,
|
|
63
|
-
* - do(name, kind,
|
|
61
|
+
* - do(name, op) - Simple once-off activity
|
|
62
|
+
* - do(name, kind, pred) - BREAK/CONTINUE control flow (no op, just predicate)
|
|
63
|
+
* - do(name, kind, pred, opOrWrapper) - WHILE/UNTIL/IF with predicate and operation
|
|
64
|
+
* - do(name, kind, splitter, rejoiner, opOrWrapper) - SPLIT with parallel execution
|
|
64
65
|
*
|
|
65
66
|
* @overload
|
|
66
67
|
* @param {string|symbol} name Activity name
|
|
@@ -71,22 +72,30 @@ export default class ActionBuilder {
|
|
|
71
72
|
/**
|
|
72
73
|
* @overload
|
|
73
74
|
* @param {string|symbol} name Activity name
|
|
74
|
-
* @param {number} kind
|
|
75
|
+
* @param {number} kind ACTIVITY.BREAK or ACTIVITY.CONTINUE flag.
|
|
76
|
+
* @param {(context: unknown) => boolean|Promise<boolean>} pred Predicate to evaluate for control flow.
|
|
77
|
+
* @returns {ActionBuilder}
|
|
78
|
+
*/
|
|
79
|
+
do(name: string | symbol, kind: number, pred: (context: unknown) => boolean | Promise<boolean>): ActionBuilder;
|
|
80
|
+
/**
|
|
81
|
+
* @overload
|
|
82
|
+
* @param {string|symbol} name Activity name
|
|
83
|
+
* @param {number} kind Activity kind (WHILE, UNTIL, or IF) from {@link ActivityFlags}.
|
|
75
84
|
* @param {(context: unknown) => boolean|Promise<boolean>} pred Predicate executed before/after the op.
|
|
76
|
-
* @param {ActionFunction|
|
|
85
|
+
* @param {ActionFunction|ActionBuilder} op Operation or nested builder to execute.
|
|
77
86
|
* @returns {ActionBuilder}
|
|
78
87
|
*/
|
|
79
|
-
do(name: string | symbol, kind: number, pred: (context: unknown) => boolean | Promise<boolean>, op: ActionFunction |
|
|
88
|
+
do(name: string | symbol, kind: number, pred: (context: unknown) => boolean | Promise<boolean>, op: ActionFunction | ActionBuilder): ActionBuilder;
|
|
80
89
|
/**
|
|
81
90
|
* @overload
|
|
82
91
|
* @param {string|symbol} name Activity name
|
|
83
92
|
* @param {number} kind ACTIVITY.SPLIT flag.
|
|
84
93
|
* @param {(context: unknown) => unknown} splitter Splitter function for SPLIT mode.
|
|
85
94
|
* @param {(originalContext: unknown, splitResults: unknown) => unknown} rejoiner Rejoiner function for SPLIT mode.
|
|
86
|
-
* @param {ActionFunction|
|
|
95
|
+
* @param {ActionFunction|ActionBuilder} op Operation or nested builder to execute.
|
|
87
96
|
* @returns {ActionBuilder}
|
|
88
97
|
*/
|
|
89
|
-
do(name: string | symbol, kind: number, splitter: (context: unknown) => unknown, rejoiner: (originalContext: unknown, splitResults: unknown) => unknown, op: ActionFunction |
|
|
98
|
+
do(name: string | symbol, kind: number, splitter: (context: unknown) => unknown, rejoiner: (originalContext: unknown, splitResults: unknown) => unknown, op: ActionFunction | ActionBuilder): ActionBuilder;
|
|
90
99
|
/**
|
|
91
100
|
* Configure hooks to be loaded from a file when the action is built.
|
|
92
101
|
*
|
|
@@ -112,6 +121,13 @@ export default class ActionBuilder {
|
|
|
112
121
|
* @returns {ActionBuilder} The builder instance for chaining.
|
|
113
122
|
*/
|
|
114
123
|
withAction(action: ActionBuilderAction): ActionBuilder;
|
|
124
|
+
/**
|
|
125
|
+
* Register a callback to be executed after all activities complete.
|
|
126
|
+
*
|
|
127
|
+
* @param {ActionFunction} callback Function to execute at the end of the pipeline.
|
|
128
|
+
* @returns {ActionBuilder} The builder instance for chaining.
|
|
129
|
+
*/
|
|
130
|
+
done(callback: ActionFunction): ActionBuilder;
|
|
115
131
|
/**
|
|
116
132
|
* Finalises the builder and returns a payload that can be consumed by the
|
|
117
133
|
* runner.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ActionBuilder.d.ts","sourceRoot":"","sources":["../../../browser/lib/ActionBuilder.js"],"names":[],"mappings":"AAMA,kEAAkE;AAClE,uEAAuE;AAEvE;;GAEG;AAEH;;;;GAIG;AAEH;;;;GAIG;AAEH;;;;;;;;GAQG;AAEH;;GAEG;AAEH;;;;;;;;;;;;;;;;;;;GAmBG;AACH;
|
|
1
|
+
{"version":3,"file":"ActionBuilder.d.ts","sourceRoot":"","sources":["../../../browser/lib/ActionBuilder.js"],"names":[],"mappings":"AAMA,kEAAkE;AAClE,uEAAuE;AAEvE;;GAEG;AAEH;;;;GAIG;AAEH;;;;GAIG;AAEH;;;;;;;;GAQG;AAEH;;GAEG;AAEH;;;;;;;;;;;;;;;;;;;GAmBG;AACH;IAkBE;;;;;OAKG;IACH,qBAHW,mBAAmB,mBACnB,mBAAmB,EAe7B;IAED,yBAEC;;;;;;;;;;;;;;;IAWE,SACQ,MAAM,GAAC,MAAM,MACb,cAAc,GACZ,aAAa,CACzB;;;;;;;;IAGE,SACQ,MAAM,GAAC,MAAM,QACb,MAAM,QACN,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,GAAC,OAAO,CAAC,OAAO,CAAC,GAC5C,aAAa,CACzB;;;;;;;;;IAGE,SACQ,MAAM,GAAC,MAAM,QACb,MAAM,QACN,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,GAAC,OAAO,CAAC,OAAO,CAAC,MAC9C,cAAc,GAAC,aAAa,GAC1B,aAAa,CACzB;;;;;;;;;;IAGE,SACQ,MAAM,GAAC,MAAM,QACb,MAAM,YACN,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,YAC7B,CAAC,eAAe,EAAE,OAAO,EAAE,YAAY,EAAE,OAAO,KAAK,OAAO,MAC5D,cAAc,GAAC,aAAa,GAC1B,aAAa,CACzB;IAkED;;;;;;;OAOG;IACH,yBALW,MAAM,aACN,MAAM,GACJ,aAAa,CAYzB;IAED;;;;;;OAMG;IACH,iBAJW,OAAO,kBAAkB,EAAE,OAAO,GAChC,aAAa,CAgBzB;IAED;;;;;;OAMG;IACH,mBAHW,mBAAmB,GACjB,aAAa,CAczB;IAED;;;;;OAKG;IACH,eAHW,cAAc,GACZ,aAAa,CAOzB;IAeD;;;;;OAKG;IACH,SAFa,OAAO,CAAC,OAAO,oBAAoB,EAAE,OAAO,CAAC,CAqBzD;;CAqBF;2BAhVa,OAAO,mBAAmB,EAAE,OAAO;4BACnC,cAAc,eAAe,EAAE,QAAQ;sBAGxC,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,KAAK,CAAC,OAAO,CAAC,KAAK,IAAI;;;;;WAKjE,CAAC,OAAO,EAAE,aAAa,KAAK,IAAI;;;;;;;;;;;;;;;;;;;;YAYhC,mBAAmB,GAAC,IAAI;;;;WACxB,OAAO,GAAC,IAAI;;;;UACZ,MAAM,GAAC,MAAM;;;;QACb,cAAc,GAAC,OAAO,oBAAoB,EAAE,OAAO;;;;;;;;sBAEzC,OAAO,KAAK,OAAO,GAAC,OAAO,CAAC,OAAO,CAAC;;6BAI/C,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,GAAC,OAAO,CAAC,OAAO,CAAC"}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @typedef {(message: string, level?: number, ...args: Array<unknown>) => void} DebugFn
|
|
3
3
|
*/
|
|
4
|
+
/**
|
|
5
|
+
* @typedef {import("./ActionBuilder.js").default} ActionBuilder
|
|
6
|
+
*/
|
|
4
7
|
/**
|
|
5
8
|
* @typedef {object} ActionRunnerOptions
|
|
6
9
|
* @property {DebugFn} [debug] Logger function.
|
|
@@ -23,16 +26,18 @@ export default class ActionRunner extends Piper {
|
|
|
23
26
|
/**
|
|
24
27
|
* Executes the configured action pipeline.
|
|
25
28
|
* Builds the ActionWrapper on first run and caches it for subsequent calls.
|
|
26
|
-
* Supports WHILE, UNTIL,
|
|
29
|
+
* Supports WHILE, UNTIL, IF, SPLIT, BREAK, and CONTINUE activity kinds.
|
|
27
30
|
*
|
|
28
31
|
* @param {unknown} context - Seed value passed to the first activity.
|
|
32
|
+
* @param {import("./ActionWrapper.js").default|null} [parentWrapper] - Parent wrapper for BREAK/CONTINUE signaling.
|
|
29
33
|
* @returns {Promise<unknown>} Final value produced by the pipeline.
|
|
30
34
|
* @throws {Sass} When no activities are registered, conflicting activity kinds are used, or execution fails.
|
|
31
35
|
*/
|
|
32
|
-
run(context: unknown): Promise<unknown>;
|
|
36
|
+
run(context: unknown, parentWrapper?: import("./ActionWrapper.js").default | null): Promise<unknown>;
|
|
33
37
|
#private;
|
|
34
38
|
}
|
|
35
39
|
export type DebugFn = (message: string, level?: number, ...args: Array<unknown>) => void;
|
|
40
|
+
export type ActionBuilder = import("./ActionBuilder.js").default;
|
|
36
41
|
export type ActionRunnerOptions = {
|
|
37
42
|
/**
|
|
38
43
|
* Logger function.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ActionRunner.d.ts","sourceRoot":"","sources":["../../../browser/lib/ActionRunner.js"],"names":[],"mappings":"AAKA;;GAEG;AAEH;;;GAGG;
|
|
1
|
+
{"version":3,"file":"ActionRunner.d.ts","sourceRoot":"","sources":["../../../browser/lib/ActionRunner.js"],"names":[],"mappings":"AAKA;;GAEG;AAEH;;GAEG;AAEH;;;GAGG;AAEH;;;;;;GAMG;AACH;IAoBE;;;;;OAKG;IACH,2BAHW,OAAO,oBAAoB,EAAE,OAAO,GAAC,IAAI,cACzC,mBAAmB,EAkB7B;IAED;;;;;;;;;OASG;IACH,aALW,OAAO,kBACP,OAAO,oBAAoB,EAAE,OAAO,GAAC,IAAI,GACvC,OAAO,CAAC,OAAO,CAAC,CA4I5B;;CAyFF;sBA3SY,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,KAAK,CAAC,OAAO,CAAC,KAAK,IAAI;4BAIlE,OAAO,oBAAoB,EAAE,OAAO;;;;;;;kBAP/B,YAAY"}
|