@gesslar/actioneer 2.1.0 → 2.3.1
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 +4 -4
- package/src/browser/lib/ActionBuilder.js +91 -59
- package/src/browser/lib/ActionHooks.js +19 -21
- package/src/browser/lib/ActionRunner.js +104 -65
- package/src/browser/lib/ActionWrapper.js +37 -12
- package/src/browser/lib/Activity.js +52 -17
- package/src/browser/lib/Piper.js +37 -17
- package/src/types/browser/lib/ActionBuilder.d.ts +82 -60
- package/src/types/browser/lib/ActionBuilder.d.ts.map +1 -1
- package/src/types/browser/lib/ActionHooks.d.ts +24 -25
- package/src/types/browser/lib/ActionHooks.d.ts.map +1 -1
- package/src/types/browser/lib/ActionRunner.d.ts +10 -7
- package/src/types/browser/lib/ActionRunner.d.ts.map +1 -1
- package/src/types/browser/lib/ActionWrapper.d.ts +22 -7
- package/src/types/browser/lib/ActionWrapper.d.ts.map +1 -1
- package/src/types/browser/lib/Activity.d.ts +45 -18
- package/src/types/browser/lib/Activity.d.ts.map +1 -1
- package/src/types/browser/lib/Piper.d.ts +7 -5
- package/src/types/browser/lib/Piper.d.ts.map +1 -1
|
@@ -4,16 +4,14 @@ import {Data, Sass, Promised, Time, Util, Valid} from "@gesslar/toolkit"
|
|
|
4
4
|
* @typedef {(message: string, level?: number, ...args: Array<unknown>) => void} DebugFn
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
/**
|
|
8
|
-
* @typedef {object} ActionHooksConfig
|
|
9
|
-
* @property {string} actionKind Action identifier shared between runner and hooks.
|
|
10
|
-
* @property {unknown} hooks Already-instantiated hooks implementation.
|
|
11
|
-
* @property {number} [hookTimeout] Timeout applied to hook execution in milliseconds.
|
|
12
|
-
* @property {DebugFn} debug Logger to emit diagnostics.
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
7
|
/**
|
|
16
8
|
* @typedef {Record<string, (context: unknown) => Promise<unknown>|unknown>} HookModule
|
|
9
|
+
*
|
|
10
|
+
* @typedef {object} ActionHooksConfig
|
|
11
|
+
* @property {string} actionKind - Action identifier shared between runner and hooks.
|
|
12
|
+
* @property {unknown} hooks - Already-instantiated hooks implementation.
|
|
13
|
+
* @property {number} [hookTimeout] - Timeout applied to hook execution in milliseconds.
|
|
14
|
+
* @property {DebugFn} debug - Logger to emit diagnostics.
|
|
17
15
|
*/
|
|
18
16
|
|
|
19
17
|
/**
|
|
@@ -24,19 +22,19 @@ import {Data, Sass, Promised, Time, Util, Valid} from "@gesslar/toolkit"
|
|
|
24
22
|
* Browser version: Requires pre-instantiated hooks. File-based loading is not supported.
|
|
25
23
|
*/
|
|
26
24
|
export default class ActionHooks {
|
|
27
|
-
/** @type {HookModule
|
|
25
|
+
/** @type {HookModule?} */
|
|
28
26
|
#hooks = null
|
|
29
|
-
/** @type {string
|
|
27
|
+
/** @type {string?} */
|
|
30
28
|
#actionKind = null
|
|
31
29
|
/** @type {number} */
|
|
32
30
|
#timeout = 1_000 // Default 1 second timeout
|
|
33
|
-
/** @type {DebugFn
|
|
31
|
+
/** @type {DebugFn?} */
|
|
34
32
|
#debug = null
|
|
35
33
|
|
|
36
34
|
/**
|
|
37
35
|
* Creates a new ActionHook instance.
|
|
38
36
|
*
|
|
39
|
-
* @param {ActionHooksConfig} config Configuration values describing how to load the hooks.
|
|
37
|
+
* @param {ActionHooksConfig} config - Configuration values describing how to load the hooks.
|
|
40
38
|
*/
|
|
41
39
|
constructor({actionKind, hooks, hookTimeout = 1_000, debug}) {
|
|
42
40
|
this.#actionKind = actionKind
|
|
@@ -57,7 +55,7 @@ export default class ActionHooks {
|
|
|
57
55
|
/**
|
|
58
56
|
* Gets the loaded hooks object.
|
|
59
57
|
*
|
|
60
|
-
* @returns {object
|
|
58
|
+
* @returns {object?} Hooks object or null if not loaded
|
|
61
59
|
*/
|
|
62
60
|
get hooks() {
|
|
63
61
|
return this.#hooks
|
|
@@ -75,7 +73,7 @@ export default class ActionHooks {
|
|
|
75
73
|
/**
|
|
76
74
|
* Gets the setup hook function if available.
|
|
77
75
|
*
|
|
78
|
-
* @returns {(args: object) => unknown
|
|
76
|
+
* @returns {(args: object) => unknown} Setup hook function or null
|
|
79
77
|
*/
|
|
80
78
|
get setup() {
|
|
81
79
|
return this.hooks?.setup || null
|
|
@@ -84,7 +82,7 @@ export default class ActionHooks {
|
|
|
84
82
|
/**
|
|
85
83
|
* Gets the cleanup hook function if available.
|
|
86
84
|
*
|
|
87
|
-
* @returns {(args: object) => unknown
|
|
85
|
+
* @returns {(args: object) => unknown} Cleanup hook function or null
|
|
88
86
|
*/
|
|
89
87
|
get cleanup() {
|
|
90
88
|
return this.hooks?.cleanup || null
|
|
@@ -94,9 +92,9 @@ export default class ActionHooks {
|
|
|
94
92
|
* Static factory method to create and initialize a hook manager.
|
|
95
93
|
* Browser version: Only works with pre-instantiated hooks passed via config.hooks.
|
|
96
94
|
*
|
|
97
|
-
* @param {ActionHooksConfig} config Configuration object with hooks property
|
|
98
|
-
* @param {DebugFn} debug The debug function.
|
|
99
|
-
* @returns {Promise<ActionHooks
|
|
95
|
+
* @param {ActionHooksConfig} config - Configuration object with hooks property
|
|
96
|
+
* @param {DebugFn} debug - The debug function.
|
|
97
|
+
* @returns {Promise<ActionHooks?>} Initialized hook manager or null if no hooks provided
|
|
100
98
|
*/
|
|
101
99
|
static async new(config, debug) {
|
|
102
100
|
debug("Creating new HookManager instance with args: %o", 2, config)
|
|
@@ -117,9 +115,9 @@ export default class ActionHooks {
|
|
|
117
115
|
/**
|
|
118
116
|
* Invoke a dynamically-named hook such as `before$foo`.
|
|
119
117
|
*
|
|
120
|
-
* @param {
|
|
121
|
-
* @param {string|symbol} activityName Activity identifier.
|
|
122
|
-
* @param {unknown} context Pipeline context supplied to the hook.
|
|
118
|
+
* @param {string} kind - Hook namespace.
|
|
119
|
+
* @param {string|symbol} activityName - Activity identifier.
|
|
120
|
+
* @param {unknown} context - Pipeline context supplied to the hook.
|
|
123
121
|
* @returns {Promise<void>}
|
|
124
122
|
*/
|
|
125
123
|
async callHook(kind, activityName, context) {
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import {Promised, Data, Sass, Valid} from "@gesslar/toolkit"
|
|
1
|
+
import {Promised, Data, Sass, Tantrum, Valid} from "@gesslar/toolkit"
|
|
2
2
|
|
|
3
3
|
import {ACTIVITY} from "./Activity.js"
|
|
4
4
|
import Piper from "./Piper.js"
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
7
|
+
* Types
|
|
8
|
+
*
|
|
9
|
+
* @import {default as ActionBuilder} from "./ActionBuilder.js"
|
|
10
|
+
* @import {default as ActionWrapper} from "./ActionWrapper.js"
|
|
8
11
|
*/
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* @typedef {import("./ActionBuilder.js").default} ActionBuilder
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
12
|
/**
|
|
13
|
+
* @typedef {(message: string, level?: number, ...args: Array<unknown>) => void} DebugFn
|
|
14
|
+
*
|
|
15
15
|
* @typedef {object} ActionRunnerOptions
|
|
16
16
|
* @property {DebugFn} [debug] Logger function.
|
|
17
17
|
*/
|
|
@@ -24,9 +24,9 @@ import Piper from "./Piper.js"
|
|
|
24
24
|
* context object under `result.value` that can be replaced or enriched.
|
|
25
25
|
*/
|
|
26
26
|
export default class ActionRunner extends Piper {
|
|
27
|
-
/** @type {
|
|
27
|
+
/** @type {ActionBuilder?} */
|
|
28
28
|
#actionBuilder = null
|
|
29
|
-
/** @type {
|
|
29
|
+
/** @type {ActionWrapper?} */
|
|
30
30
|
#actionWrapper = null
|
|
31
31
|
|
|
32
32
|
/**
|
|
@@ -63,24 +63,32 @@ export default class ActionRunner extends Piper {
|
|
|
63
63
|
/**
|
|
64
64
|
* Executes the configured action pipeline.
|
|
65
65
|
* Builds the ActionWrapper on first run and caches it for subsequent calls.
|
|
66
|
-
* Supports WHILE, UNTIL,
|
|
66
|
+
* Supports WHILE, UNTIL, IF, SPLIT, BREAK, and CONTINUE activity kinds.
|
|
67
67
|
*
|
|
68
68
|
* @param {unknown} context - Seed value passed to the first activity.
|
|
69
|
+
* @param {import("./ActionWrapper.js").default|null} [parentWrapper] - Parent wrapper for BREAK/CONTINUE signaling.
|
|
69
70
|
* @returns {Promise<unknown>} Final value produced by the pipeline.
|
|
70
71
|
* @throws {Sass} When no activities are registered, conflicting activity kinds are used, or execution fails.
|
|
72
|
+
* @throws {Tantrum} When both an activity and the done callback fail.
|
|
71
73
|
*/
|
|
72
|
-
async run(context) {
|
|
74
|
+
async run(context, parentWrapper=null) {
|
|
73
75
|
if(!this.#actionWrapper)
|
|
74
|
-
this.#actionWrapper = await this.#actionBuilder.build()
|
|
76
|
+
this.#actionWrapper = await this.#actionBuilder.build(this)
|
|
75
77
|
|
|
76
78
|
const actionWrapper = this.#actionWrapper
|
|
77
|
-
const activities = actionWrapper.activities
|
|
79
|
+
const activities = Array.from(actionWrapper.activities)
|
|
80
|
+
|
|
81
|
+
let caughtError = null
|
|
78
82
|
|
|
79
83
|
try {
|
|
80
|
-
for(
|
|
81
|
-
|
|
82
|
-
|
|
84
|
+
for(
|
|
85
|
+
let cursor = 0, max = activities.length;
|
|
86
|
+
cursor < max && cursor !== -1;
|
|
87
|
+
cursor++
|
|
88
|
+
) {
|
|
89
|
+
const activity = activities[cursor]
|
|
83
90
|
|
|
91
|
+
try {
|
|
84
92
|
const kind = activity.kind
|
|
85
93
|
|
|
86
94
|
// If we have no kind, then it's just a once.
|
|
@@ -88,33 +96,50 @@ export default class ActionRunner extends Piper {
|
|
|
88
96
|
if(!kind) {
|
|
89
97
|
context = await this.#execute(activity, context)
|
|
90
98
|
} else {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
99
|
+
const {UNTIL, WHILE, IF, SPLIT, BREAK, CONTINUE} = ACTIVITY
|
|
100
|
+
const kindUntil = kind === UNTIL
|
|
101
|
+
const kindWhile = kind === WHILE
|
|
102
|
+
const kindIf = kind === IF
|
|
103
|
+
const kindSplit = kind === SPLIT
|
|
104
|
+
const kindBreak = kind === BREAK
|
|
105
|
+
const kindContinue = kind === CONTINUE
|
|
106
|
+
|
|
107
|
+
if(kindBreak || kindContinue) {
|
|
108
|
+
if(!parentWrapper)
|
|
109
|
+
throw Sass.new(`Invalid use of control flow outside of context.`)
|
|
110
|
+
|
|
111
|
+
if(await this.#evalPredicate(activity, context)) {
|
|
112
|
+
if(kindBreak) {
|
|
113
|
+
this.emit("loop.break", parentWrapper)
|
|
114
|
+
break
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if(kindContinue)
|
|
118
|
+
cursor = max
|
|
119
|
+
}
|
|
120
|
+
} else if(kindIf) {
|
|
121
|
+
if(await this.#evalPredicate(activity, context))
|
|
122
|
+
context = await this.#execute(activity, context)
|
|
123
|
+
} else if(kindWhile || kindUntil) {
|
|
124
|
+
// Simple if, no loop, only gainz.
|
|
108
125
|
for(;;) {
|
|
109
|
-
|
|
110
126
|
if(kindWhile)
|
|
111
|
-
if(!await this.#
|
|
127
|
+
if(!await this.#evalPredicate(activity, context))
|
|
112
128
|
break
|
|
113
129
|
|
|
114
|
-
|
|
130
|
+
let weWereOnABreak = false
|
|
131
|
+
const breakReceiver = this.on("loop.break", wrapper => {
|
|
132
|
+
if(wrapper.id === actionWrapper.id) {
|
|
133
|
+
weWereOnABreak = true
|
|
134
|
+
}
|
|
135
|
+
})
|
|
136
|
+
context = await this.#execute(activity, context)
|
|
137
|
+
breakReceiver()
|
|
138
|
+
if(weWereOnABreak)
|
|
139
|
+
break
|
|
115
140
|
|
|
116
141
|
if(kindUntil)
|
|
117
|
-
if(await this.#
|
|
142
|
+
if(await this.#evalPredicate(activity, context))
|
|
118
143
|
break
|
|
119
144
|
}
|
|
120
145
|
} else if(kindSplit) {
|
|
@@ -129,9 +154,8 @@ export default class ActionRunner extends Piper {
|
|
|
129
154
|
)
|
|
130
155
|
|
|
131
156
|
const original = context
|
|
132
|
-
const splitContexts = await splitter.call(
|
|
133
|
-
|
|
134
|
-
)
|
|
157
|
+
const splitContexts = await splitter.call(activity.action,context)
|
|
158
|
+
|
|
135
159
|
let settled
|
|
136
160
|
|
|
137
161
|
if(activity.opKind === "ActionBuilder") {
|
|
@@ -145,7 +169,6 @@ export default class ActionRunner extends Piper {
|
|
|
145
169
|
debug: this.#debug, name: activity.name
|
|
146
170
|
})
|
|
147
171
|
|
|
148
|
-
// pipe() returns settled results with concurrency control
|
|
149
172
|
settled = await runner.pipe(splitContexts)
|
|
150
173
|
} else {
|
|
151
174
|
// For plain functions, process each split context
|
|
@@ -169,19 +192,28 @@ export default class ActionRunner extends Piper {
|
|
|
169
192
|
throw Sass.new("ActionRunner running activity", error)
|
|
170
193
|
}
|
|
171
194
|
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
195
|
+
} catch(err) {
|
|
196
|
+
caughtError = err
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Execute done callback if registered - always runs, even on error
|
|
200
|
+
// Only run for top-level pipelines, not nested builders (inside loops)
|
|
201
|
+
if(actionWrapper.done && !parentWrapper) {
|
|
202
|
+
try {
|
|
203
|
+
context = await actionWrapper.done.call(
|
|
204
|
+
actionWrapper.action, caughtError ?? context
|
|
205
|
+
)
|
|
206
|
+
} catch(error) {
|
|
207
|
+
if(caughtError)
|
|
208
|
+
caughtError = new Tantrum("ActionRunner running done callback", [caughtError, error])
|
|
209
|
+
else
|
|
210
|
+
caughtError = Sass.new("ActionRunner running done callback", error)
|
|
182
211
|
}
|
|
183
212
|
}
|
|
184
213
|
|
|
214
|
+
if(caughtError)
|
|
215
|
+
throw caughtError
|
|
216
|
+
|
|
185
217
|
return context
|
|
186
218
|
}
|
|
187
219
|
|
|
@@ -217,14 +249,24 @@ export default class ActionRunner extends Piper {
|
|
|
217
249
|
debug: this.#debug, name: activity.name
|
|
218
250
|
})
|
|
219
251
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
252
|
+
// Forward loop.break events from nested runner to this runner
|
|
253
|
+
// so that parent WHILE/UNTIL loops can receive break signals.
|
|
254
|
+
const forwarder = runner.on("loop.break",
|
|
255
|
+
wrapper => this.emit("loop.break", wrapper)
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
try {
|
|
259
|
+
if(parallel) {
|
|
260
|
+
return await runner.pipe(context)
|
|
261
|
+
} else {
|
|
262
|
+
return await runner.run(context, activity.wrapper)
|
|
263
|
+
}
|
|
264
|
+
} finally {
|
|
265
|
+
forwarder()
|
|
224
266
|
}
|
|
225
267
|
} else if(opKind === "Function") {
|
|
226
268
|
try {
|
|
227
|
-
const result = await activity.run(context)
|
|
269
|
+
const result = await activity.run(context, activity.wrapper)
|
|
228
270
|
|
|
229
271
|
if(Data.isType(result, "ActionBuilder")) {
|
|
230
272
|
if(activity.action)
|
|
@@ -240,7 +282,7 @@ export default class ActionRunner extends Piper {
|
|
|
240
282
|
if(parallel) {
|
|
241
283
|
return await runner.pipe(context)
|
|
242
284
|
} else {
|
|
243
|
-
return await runner.run(context)
|
|
285
|
+
return await runner.run(context, activity.wrapper)
|
|
244
286
|
}
|
|
245
287
|
} else {
|
|
246
288
|
return result
|
|
@@ -250,24 +292,21 @@ export default class ActionRunner extends Piper {
|
|
|
250
292
|
}
|
|
251
293
|
}
|
|
252
294
|
|
|
253
|
-
console.log(activity.opKind + " " + JSON.stringify(activity))
|
|
254
|
-
|
|
255
295
|
throw Sass.new("We buy Functions and ActionBuilders. Only. Not whatever that was.")
|
|
256
296
|
}
|
|
257
297
|
|
|
258
298
|
/**
|
|
259
|
-
* Evaluate the predicate for WHILE/UNTIL activity kinds.
|
|
299
|
+
* Evaluate the predicate for WHILE/UNTIL/IF/BREAK/CONTINUE activity kinds.
|
|
260
300
|
*
|
|
261
301
|
* @param {import("./Activity.js").default} activity Activity currently executing.
|
|
262
|
-
* @param {(context: unknown) => boolean|Promise<boolean>} predicate Predicate to evaluate.
|
|
263
302
|
* @param {unknown} context Current pipeline context.
|
|
264
|
-
* @returns {Promise<boolean>} True when the predicate
|
|
303
|
+
* @returns {Promise<boolean>} True when the predicate condition is met.
|
|
265
304
|
* @private
|
|
266
305
|
*/
|
|
267
|
-
async #
|
|
268
|
-
Valid.type(
|
|
306
|
+
async #evalPredicate(activity, context) {
|
|
307
|
+
Valid.type(activity?.pred, "Function")
|
|
269
308
|
|
|
270
|
-
return !!(await
|
|
309
|
+
return !!(await activity.pred.call(activity.action, context))
|
|
271
310
|
}
|
|
272
311
|
|
|
273
312
|
toString() {
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import Activity from "./Activity.js"
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Type imports
|
|
5
|
+
*
|
|
6
|
+
* @import {default as ActionHooks} from "./ActionHooks.js"
|
|
7
|
+
* @import {default as ActionRunner} from "./ActionRunner.js"
|
|
8
|
+
*/
|
|
9
|
+
|
|
3
10
|
/**
|
|
4
11
|
* @typedef {object} WrappedActivityConfig
|
|
5
12
|
* @property {string|symbol} name Activity identifier used by hooks/logs.
|
|
@@ -10,10 +17,6 @@ import Activity from "./Activity.js"
|
|
|
10
17
|
* @property {(message: string, level?: number, ...args: Array<unknown>) => void} [debug] Optional logger reference.
|
|
11
18
|
*/
|
|
12
19
|
|
|
13
|
-
/**
|
|
14
|
-
* @typedef {import("@gesslar/toolkit").Generator<Activity, void, unknown>} ActivityIterator
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
20
|
/**
|
|
18
21
|
* Thin wrapper that materialises {@link Activity} instances on demand.
|
|
19
22
|
*/
|
|
@@ -24,6 +27,7 @@ export default class ActionWrapper {
|
|
|
24
27
|
* @type {Map<string|symbol, WrappedActivityConfig>}
|
|
25
28
|
*/
|
|
26
29
|
#activities = new Map()
|
|
30
|
+
|
|
27
31
|
/**
|
|
28
32
|
* Logger invoked for wrapper lifecycle events.
|
|
29
33
|
*
|
|
@@ -31,11 +35,16 @@ export default class ActionWrapper {
|
|
|
31
35
|
*/
|
|
32
36
|
#debug = () => {}
|
|
33
37
|
|
|
38
|
+
/** @type {ActionHooks} */
|
|
34
39
|
#hooks = null
|
|
35
|
-
|
|
40
|
+
/** @type {((context: unknown) => unknown|Promise<unknown>)|null} */
|
|
36
41
|
#done = null
|
|
37
|
-
|
|
42
|
+
/** @type {unknown} */
|
|
38
43
|
#action = null
|
|
44
|
+
/** @type {symbol} */
|
|
45
|
+
#id = Symbol(performance.now())
|
|
46
|
+
/** @type {ActionRunner} */
|
|
47
|
+
#runner
|
|
39
48
|
|
|
40
49
|
/**
|
|
41
50
|
* Create a wrapper from the builder payload.
|
|
@@ -47,7 +56,18 @@ export default class ActionWrapper {
|
|
|
47
56
|
this.#hooks = hooks
|
|
48
57
|
this.#done = doneCallback
|
|
49
58
|
this.#action = action
|
|
50
|
-
|
|
59
|
+
|
|
60
|
+
for(const [key, value] of activities) {
|
|
61
|
+
this.#activities.set(
|
|
62
|
+
key,
|
|
63
|
+
new Activity({
|
|
64
|
+
...value,
|
|
65
|
+
hooks: this.#hooks,
|
|
66
|
+
wrapper: this
|
|
67
|
+
})
|
|
68
|
+
)
|
|
69
|
+
}
|
|
70
|
+
|
|
51
71
|
this.#debug(
|
|
52
72
|
"Instantiating ActionWrapper with %o activities.",
|
|
53
73
|
2,
|
|
@@ -55,18 +75,23 @@ export default class ActionWrapper {
|
|
|
55
75
|
)
|
|
56
76
|
}
|
|
57
77
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
78
|
+
/**
|
|
79
|
+
* Unique identifier for this wrapper instance.
|
|
80
|
+
* Used by BREAK/CONTINUE to match events to the correct loop.
|
|
81
|
+
*
|
|
82
|
+
* @returns {symbol} Unique symbol identifier
|
|
83
|
+
*/
|
|
84
|
+
get id() {
|
|
85
|
+
return this.#id
|
|
61
86
|
}
|
|
62
87
|
|
|
63
88
|
/**
|
|
64
89
|
* Iterator over the registered activities.
|
|
65
90
|
*
|
|
66
|
-
* @returns {
|
|
91
|
+
* @returns {Iterator<Activity>} Iterator yielding Activity instances.
|
|
67
92
|
*/
|
|
68
93
|
get activities() {
|
|
69
|
-
return this.#
|
|
94
|
+
return this.#activities.values()
|
|
70
95
|
}
|
|
71
96
|
|
|
72
97
|
/**
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import {Data} from "@gesslar/toolkit"
|
|
2
2
|
|
|
3
|
-
/**
|
|
3
|
+
/**
|
|
4
|
+
* @import {default as ActionBuilder} from "./ActionBuilder.js"
|
|
5
|
+
* @import {default as ActionHooks} from "./ActionHooks.js"
|
|
6
|
+
* @import {default as ActionWrapper} from "./ActionWrapper.js"
|
|
7
|
+
**/
|
|
4
8
|
|
|
5
9
|
/**
|
|
6
10
|
* Activity bit flags recognised by the builder. The flag decides
|
|
@@ -8,14 +12,20 @@ import {Data} from "@gesslar/toolkit"
|
|
|
8
12
|
*
|
|
9
13
|
* @readonly
|
|
10
14
|
* @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
|
|
15
|
+
* @property {number} WHILE - Execute activity while predicate returns true 1
|
|
16
|
+
* @property {number} UNTIL - Execute activity until predicate returns true 2
|
|
17
|
+
* @property {number} SPLIT - Execute activity with split/rejoin pattern for parallel execution 3
|
|
18
|
+
* @property {number} IF - Execute activity if predicate returns true 4
|
|
19
|
+
* @property {number} BREAK - Break out of a WHILE/UNTIL if predicate returns true 5
|
|
20
|
+
* @property {number} CONTINUE - Returns to the top of a WHILE/UNTIL if predicate returns true 6
|
|
14
21
|
*/
|
|
15
22
|
export const ACTIVITY = Object.freeze({
|
|
16
|
-
WHILE: 1
|
|
17
|
-
UNTIL:
|
|
18
|
-
SPLIT:
|
|
23
|
+
WHILE: 1,
|
|
24
|
+
UNTIL: 2,
|
|
25
|
+
SPLIT: 3,
|
|
26
|
+
IF: 4,
|
|
27
|
+
BREAK: 5,
|
|
28
|
+
CONTINUE: 6,
|
|
19
29
|
})
|
|
20
30
|
|
|
21
31
|
export default class Activity {
|
|
@@ -23,13 +33,13 @@ export default class Activity {
|
|
|
23
33
|
#action = null
|
|
24
34
|
/** @type {unknown} */
|
|
25
35
|
#context = null
|
|
26
|
-
/** @type {ActionHooks
|
|
36
|
+
/** @type {ActionHooks?} */
|
|
27
37
|
#hooks = null
|
|
28
|
-
/** @type {number
|
|
38
|
+
/** @type {number?} */
|
|
29
39
|
#kind = null
|
|
30
40
|
/** @type {string|symbol} */
|
|
31
41
|
#name = null
|
|
32
|
-
/** @type {((context: unknown) => unknown|Promise<unknown>)|
|
|
42
|
+
/** @type {((context: unknown) => unknown|Promise<unknown>)|ActionBuilder} */
|
|
33
43
|
#op = null
|
|
34
44
|
/** @type {((context: unknown) => boolean|Promise<boolean>)|null} */
|
|
35
45
|
#pred = null
|
|
@@ -37,6 +47,10 @@ export default class Activity {
|
|
|
37
47
|
#rejoiner = null
|
|
38
48
|
/** @type {((context: unknown) => unknown)|null} */
|
|
39
49
|
#splitter = null
|
|
50
|
+
/** @type {ActionWrapper?} */
|
|
51
|
+
#wrapper = null
|
|
52
|
+
/** @type {symbol} */
|
|
53
|
+
#id = Symbol(performance.now())
|
|
40
54
|
|
|
41
55
|
/**
|
|
42
56
|
* Construct an Activity definition wrapper.
|
|
@@ -44,14 +58,15 @@ export default class Activity {
|
|
|
44
58
|
* @param {object} init - Initial properties describing the activity operation, loop semantics, and predicate
|
|
45
59
|
* @param {unknown} init.action - Parent action instance
|
|
46
60
|
* @param {string|symbol} init.name - Activity identifier
|
|
47
|
-
* @param {(context: unknown) => unknown|Promise<unknown>|
|
|
61
|
+
* @param {(context: unknown) => unknown|Promise<unknown>|ActionBuilder} init.op - Operation to execute
|
|
48
62
|
* @param {number} [init.kind] - Optional loop semantics flags
|
|
49
63
|
* @param {(context: unknown) => boolean|Promise<boolean>} [init.pred] - Optional predicate for WHILE/UNTIL
|
|
50
64
|
* @param {ActionHooks} [init.hooks] - Optional hooks instance
|
|
51
65
|
* @param {(context: unknown) => unknown} [init.splitter] - Optional splitter function for SPLIT activities
|
|
52
66
|
* @param {(originalContext: unknown, splitResults: unknown) => unknown} [init.rejoiner] - Optional rejoiner function for SPLIT activities
|
|
67
|
+
* @param {ActionWrapper} [init.wrapper] - Optional wrapper containing this activity
|
|
53
68
|
*/
|
|
54
|
-
constructor({action,name,op,kind,pred,hooks,splitter,rejoiner}) {
|
|
69
|
+
constructor({action,name,op,kind,pred,hooks,splitter,rejoiner,wrapper}) {
|
|
55
70
|
this.#action = action
|
|
56
71
|
this.#hooks = hooks
|
|
57
72
|
this.#kind = kind
|
|
@@ -60,6 +75,16 @@ export default class Activity {
|
|
|
60
75
|
this.#pred = pred
|
|
61
76
|
this.#rejoiner = rejoiner
|
|
62
77
|
this.#splitter = splitter
|
|
78
|
+
this.#wrapper = wrapper ?? null
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Unique identifier for this activity instance.
|
|
83
|
+
*
|
|
84
|
+
* @returns {symbol} Unique symbol identifier
|
|
85
|
+
*/
|
|
86
|
+
get id() {
|
|
87
|
+
return this.#id
|
|
63
88
|
}
|
|
64
89
|
|
|
65
90
|
/**
|
|
@@ -81,7 +106,7 @@ export default class Activity {
|
|
|
81
106
|
}
|
|
82
107
|
|
|
83
108
|
/**
|
|
84
|
-
* The predicate function for WHILE/UNTIL flows.
|
|
109
|
+
* The predicate function for WHILE/UNTIL/IF flows.
|
|
85
110
|
*
|
|
86
111
|
* @returns {(context: unknown) => boolean|Promise<boolean>|undefined} - Predicate used to continue/stop loops
|
|
87
112
|
*/
|
|
@@ -110,7 +135,7 @@ export default class Activity {
|
|
|
110
135
|
/**
|
|
111
136
|
* The operator to execute (function or nested ActionBuilder).
|
|
112
137
|
*
|
|
113
|
-
* @returns {(context: unknown) => unknown|Promise<unknown>|
|
|
138
|
+
* @returns {(context: unknown) => unknown|Promise<unknown>|ActionBuilder} - Activity operation
|
|
114
139
|
*/
|
|
115
140
|
get op() {
|
|
116
141
|
return this.#op
|
|
@@ -119,7 +144,7 @@ export default class Activity {
|
|
|
119
144
|
/**
|
|
120
145
|
* The splitter function for SPLIT activities.
|
|
121
146
|
*
|
|
122
|
-
* @returns {((context: unknown) => unknown)
|
|
147
|
+
* @returns {((context: unknown) => unknown)?} Splitter function or null
|
|
123
148
|
*/
|
|
124
149
|
get splitter() {
|
|
125
150
|
return this.#splitter
|
|
@@ -128,7 +153,7 @@ export default class Activity {
|
|
|
128
153
|
/**
|
|
129
154
|
* The rejoiner function for SPLIT activities.
|
|
130
155
|
*
|
|
131
|
-
* @returns {((originalContext: unknown, splitResults: unknown) => unknown)
|
|
156
|
+
* @returns {((originalContext: unknown, splitResults: unknown) => unknown)?} Rejoiner function or null
|
|
132
157
|
*/
|
|
133
158
|
get rejoiner() {
|
|
134
159
|
return this.#rejoiner
|
|
@@ -143,6 +168,16 @@ export default class Activity {
|
|
|
143
168
|
return this.#action
|
|
144
169
|
}
|
|
145
170
|
|
|
171
|
+
/**
|
|
172
|
+
* Get the ActionWrapper containing this activity.
|
|
173
|
+
* Used by BREAK/CONTINUE to signal the parent loop.
|
|
174
|
+
*
|
|
175
|
+
* @returns {ActionWrapper?} The wrapper or null
|
|
176
|
+
*/
|
|
177
|
+
get wrapper() {
|
|
178
|
+
return this.#wrapper ?? null
|
|
179
|
+
}
|
|
180
|
+
|
|
146
181
|
/**
|
|
147
182
|
* Execute the activity with before/after hooks.
|
|
148
183
|
*
|
|
@@ -178,7 +213,7 @@ export default class Activity {
|
|
|
178
213
|
/**
|
|
179
214
|
* Get the hooks instance attached to this activity.
|
|
180
215
|
*
|
|
181
|
-
* @returns {ActionHooks
|
|
216
|
+
* @returns {ActionHooks?} The hooks instance or null
|
|
182
217
|
*/
|
|
183
218
|
get hooks() {
|
|
184
219
|
return this.#hooks
|