@gesslar/actioneer 0.2.0 → 0.2.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/package.json +1 -1
- package/src/lib/ActionBuilder.js +106 -22
- package/src/lib/ActionHooks.js +46 -30
- package/src/lib/ActionRunner.js +123 -42
- package/src/lib/ActionWrapper.js +17 -7
- package/src/lib/Activity.js +74 -12
- package/src/lib/Piper.js +105 -55
- package/src/types/lib/ActionBuilder.d.ts +42 -2
- package/src/types/lib/ActionBuilder.d.ts.map +1 -1
- package/src/types/lib/ActionHooks.d.ts +23 -24
- package/src/types/lib/ActionHooks.d.ts.map +1 -1
- package/src/types/lib/ActionRunner.d.ts +4 -2
- package/src/types/lib/ActionRunner.d.ts.map +1 -1
- package/src/types/lib/ActionWrapper.d.ts +19 -5
- package/src/types/lib/ActionWrapper.d.ts.map +1 -1
- package/src/types/lib/Activity.d.ts +54 -19
- package/src/types/lib/Activity.d.ts.map +1 -1
- package/src/types/lib/Piper.d.ts +24 -7
- package/src/types/lib/Piper.d.ts.map +1 -1
package/src/lib/Activity.js
CHANGED
|
@@ -8,32 +8,58 @@ import {Data} from "@gesslar/toolkit"
|
|
|
8
8
|
*
|
|
9
9
|
* @readonly
|
|
10
10
|
* @enum {number}
|
|
11
|
+
* @property {number} WHILE - Execute activity while predicate returns true (2)
|
|
12
|
+
* @property {number} UNTIL - Execute activity until predicate returns false (4)
|
|
13
|
+
* @property {number} SPLIT - Execute activity with split/rejoin pattern for parallel execution (8)
|
|
11
14
|
*/
|
|
12
15
|
export const ACTIVITY = Object.freeze({
|
|
13
16
|
WHILE: 1<<1,
|
|
14
17
|
UNTIL: 1<<2,
|
|
18
|
+
SPLIT: 1<<3,
|
|
15
19
|
})
|
|
16
20
|
|
|
17
21
|
export default class Activity {
|
|
22
|
+
/** @type {unknown} */
|
|
18
23
|
#action = null
|
|
24
|
+
/** @type {unknown} */
|
|
25
|
+
#context = null
|
|
26
|
+
/** @type {ActionHooks|null} */
|
|
27
|
+
#hooks = null
|
|
28
|
+
/** @type {number|null} */
|
|
29
|
+
#kind = null
|
|
30
|
+
/** @type {string|symbol} */
|
|
19
31
|
#name = null
|
|
32
|
+
/** @type {((context: unknown) => unknown|Promise<unknown>)|import("./ActionBuilder.js").default} */
|
|
20
33
|
#op = null
|
|
21
|
-
|
|
34
|
+
/** @type {((context: unknown) => boolean|Promise<boolean>)|null} */
|
|
22
35
|
#pred = null
|
|
23
|
-
|
|
36
|
+
/** @type {((originalContext: unknown, splitResults: unknown) => unknown)|null} */
|
|
37
|
+
#rejoiner = null
|
|
38
|
+
/** @type {((context: unknown) => unknown)|null} */
|
|
39
|
+
#splitter = null
|
|
24
40
|
|
|
25
41
|
/**
|
|
26
42
|
* Construct an Activity definition wrapper.
|
|
27
43
|
*
|
|
28
|
-
* @param {
|
|
44
|
+
* @param {object} init - Initial properties describing the activity operation, loop semantics, and predicate
|
|
45
|
+
* @param {unknown} init.action - Parent action instance
|
|
46
|
+
* @param {string|symbol} init.name - Activity identifier
|
|
47
|
+
* @param {(context: unknown) => unknown|Promise<unknown>|import("./ActionBuilder.js").default} init.op - Operation to execute
|
|
48
|
+
* @param {number} [init.kind] - Optional loop semantics flags
|
|
49
|
+
* @param {(context: unknown) => boolean|Promise<boolean>} [init.pred] - Optional predicate for WHILE/UNTIL
|
|
50
|
+
* @param {ActionHooks} [init.hooks] - Optional hooks instance
|
|
51
|
+
* @param {(context: unknown) => unknown} [init.splitter] - Optional splitter function for SPLIT activities
|
|
52
|
+
* @param {(originalContext: unknown, splitResults: unknown) => unknown} [init.rejoiner] - Optional rejoiner function for SPLIT activities
|
|
29
53
|
*/
|
|
30
|
-
constructor({action,name,op,kind,pred,hooks}) {
|
|
54
|
+
constructor({action,name,op,kind,pred,hooks,splitter,rejoiner}) {
|
|
55
|
+
this.#action = action
|
|
56
|
+
this.#hooks = hooks
|
|
57
|
+
this.#kind = kind
|
|
31
58
|
this.#name = name
|
|
32
59
|
this.#op = op
|
|
33
|
-
this.#kind = kind
|
|
34
|
-
this.#action = action
|
|
35
60
|
this.#pred = pred
|
|
36
|
-
this.#
|
|
61
|
+
this.#rejoiner = rejoiner
|
|
62
|
+
this.#splitter = splitter
|
|
37
63
|
}
|
|
38
64
|
|
|
39
65
|
/**
|
|
@@ -64,7 +90,16 @@ export default class Activity {
|
|
|
64
90
|
}
|
|
65
91
|
|
|
66
92
|
/**
|
|
67
|
-
* The
|
|
93
|
+
* The current context (if set).
|
|
94
|
+
*
|
|
95
|
+
* @returns {unknown} Current context value
|
|
96
|
+
*/
|
|
97
|
+
get context() {
|
|
98
|
+
return this.#context
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* The operator kind name (Function or ActionBuilder).
|
|
68
103
|
*
|
|
69
104
|
* @returns {string} - Kind name extracted via Data.typeOf
|
|
70
105
|
*/
|
|
@@ -73,14 +108,32 @@ export default class Activity {
|
|
|
73
108
|
}
|
|
74
109
|
|
|
75
110
|
/**
|
|
76
|
-
* The operator to execute (function or nested
|
|
111
|
+
* The operator to execute (function or nested ActionBuilder).
|
|
77
112
|
*
|
|
78
|
-
* @returns {unknown} - Activity operation
|
|
113
|
+
* @returns {(context: unknown) => unknown|Promise<unknown>|import("./ActionBuilder.js").default} - Activity operation
|
|
79
114
|
*/
|
|
80
115
|
get op() {
|
|
81
116
|
return this.#op
|
|
82
117
|
}
|
|
83
118
|
|
|
119
|
+
/**
|
|
120
|
+
* The splitter function for SPLIT activities.
|
|
121
|
+
*
|
|
122
|
+
* @returns {((context: unknown) => unknown)|null} Splitter function or null
|
|
123
|
+
*/
|
|
124
|
+
get splitter() {
|
|
125
|
+
return this.#splitter
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* The rejoiner function for SPLIT activities.
|
|
130
|
+
*
|
|
131
|
+
* @returns {((originalContext: unknown, splitResults: unknown) => unknown)|null} Rejoiner function or null
|
|
132
|
+
*/
|
|
133
|
+
get rejoiner() {
|
|
134
|
+
return this.#rejoiner
|
|
135
|
+
}
|
|
136
|
+
|
|
84
137
|
/**
|
|
85
138
|
* The action instance this activity belongs to.
|
|
86
139
|
*
|
|
@@ -94,7 +147,7 @@ export default class Activity {
|
|
|
94
147
|
* Execute the activity with before/after hooks.
|
|
95
148
|
*
|
|
96
149
|
* @param {unknown} context - Mutable context flowing through the pipeline
|
|
97
|
-
* @returns {Promise<
|
|
150
|
+
* @returns {Promise<unknown>} - Activity result
|
|
98
151
|
*/
|
|
99
152
|
async run(context) {
|
|
100
153
|
// before hook
|
|
@@ -112,7 +165,7 @@ export default class Activity {
|
|
|
112
165
|
/**
|
|
113
166
|
* Attach hooks to this activity instance.
|
|
114
167
|
*
|
|
115
|
-
* @param {
|
|
168
|
+
* @param {ActionHooks} hooks - Hooks instance with optional before$/after$ methods
|
|
116
169
|
* @returns {this} - This activity for chaining
|
|
117
170
|
*/
|
|
118
171
|
setActionHooks(hooks) {
|
|
@@ -121,4 +174,13 @@ export default class Activity {
|
|
|
121
174
|
|
|
122
175
|
return this
|
|
123
176
|
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Get the hooks instance attached to this activity.
|
|
180
|
+
*
|
|
181
|
+
* @returns {ActionHooks|null} The hooks instance or null
|
|
182
|
+
*/
|
|
183
|
+
get hooks() {
|
|
184
|
+
return this.#hooks
|
|
185
|
+
}
|
|
124
186
|
}
|
package/src/lib/Piper.js
CHANGED
|
@@ -2,18 +2,20 @@
|
|
|
2
2
|
* Generic Pipeline - Process items through a series of steps with concurrency control
|
|
3
3
|
*
|
|
4
4
|
* This abstraction handles:
|
|
5
|
-
* - Concurrent processing with configurable limits
|
|
6
|
-
* - Pipeline of processing steps
|
|
7
|
-
* - Result categorization (success/warning/error)
|
|
5
|
+
* - Concurrent processing with configurable limits using worker pool pattern
|
|
6
|
+
* - Pipeline of processing steps executed sequentially per item
|
|
8
7
|
* - Setup/cleanup lifecycle hooks
|
|
9
8
|
* - Error handling and reporting
|
|
9
|
+
* - Dynamic worker spawning to maintain concurrency
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import {Sass, Tantrum, Util} from "@gesslar/toolkit"
|
|
13
13
|
|
|
14
14
|
export default class Piper {
|
|
15
|
+
/** @type {(message: string, level?: number, ...args: Array<unknown>) => void} */
|
|
15
16
|
#debug
|
|
16
17
|
|
|
18
|
+
/** @type {Map<string, Set<unknown>>} */
|
|
17
19
|
#lifeCycle = new Map([
|
|
18
20
|
["setup", new Set()],
|
|
19
21
|
["process", new Set()],
|
|
@@ -30,18 +32,23 @@ export default class Piper {
|
|
|
30
32
|
}
|
|
31
33
|
|
|
32
34
|
/**
|
|
33
|
-
* Add a processing step to the pipeline
|
|
35
|
+
* Add a processing step to the pipeline.
|
|
36
|
+
* Each step is executed sequentially per item.
|
|
34
37
|
*
|
|
35
38
|
* @param {(context: unknown) => Promise<unknown>|unknown} fn Function that processes an item
|
|
36
|
-
* @param {{name
|
|
39
|
+
* @param {{name: string, required?: boolean}} options Step options (name is required)
|
|
37
40
|
* @param {unknown} [newThis] Optional this binding
|
|
38
41
|
* @returns {Piper} The pipeline instance (for chaining)
|
|
42
|
+
* @throws {Sass} If name is not provided in options
|
|
39
43
|
*/
|
|
40
44
|
addStep(fn, options = {}, newThis) {
|
|
45
|
+
if(options.name == null)
|
|
46
|
+
throw Sass.new("Missing name for step.")
|
|
47
|
+
|
|
41
48
|
this.#lifeCycle.get("process").add({
|
|
42
49
|
fn: fn.bind(newThis ?? this),
|
|
43
50
|
name: options.name || `Step ${this.#lifeCycle.get("process").size + 1}`,
|
|
44
|
-
required:
|
|
51
|
+
required: options.required ?? true,
|
|
45
52
|
...options
|
|
46
53
|
})
|
|
47
54
|
|
|
@@ -75,33 +82,70 @@ export default class Piper {
|
|
|
75
82
|
}
|
|
76
83
|
|
|
77
84
|
/**
|
|
78
|
-
* Process items through the pipeline with concurrency control
|
|
85
|
+
* Process items through the pipeline with concurrency control using a worker pool pattern.
|
|
86
|
+
* Workers are spawned up to maxConcurrent limit, and as workers complete, new workers
|
|
87
|
+
* are spawned to maintain concurrency until all items are processed.
|
|
88
|
+
*
|
|
89
|
+
* This implementation uses dynamic worker spawning to maintain concurrency:
|
|
90
|
+
* - Initial workers are spawned up to maxConcurrent limit
|
|
91
|
+
* - As each worker completes (success OR failure), a replacement worker is spawned if items remain
|
|
92
|
+
* - Worker spawning occurs in finally block to ensure resilience to individual worker failures
|
|
93
|
+
* - All results are collected with {ok, value} or {ok: false, error} structure
|
|
94
|
+
* - Processing continues even if individual workers fail, collecting all errors
|
|
79
95
|
*
|
|
80
96
|
* @param {Array<unknown>|unknown} items - Items to process
|
|
81
|
-
* @param {number} maxConcurrent - Maximum concurrent items to process
|
|
82
|
-
* @returns {Promise<Array<unknown>>} -
|
|
97
|
+
* @param {number} [maxConcurrent] - Maximum concurrent items to process (default: 10)
|
|
98
|
+
* @returns {Promise<Array<{ok: boolean, value?: unknown, error?: Sass}>>} - Results with success/failure status
|
|
99
|
+
* @throws {Sass} If setup or teardown fails
|
|
83
100
|
*/
|
|
84
101
|
async pipe(items, maxConcurrent = 10) {
|
|
85
102
|
items = Array.isArray(items)
|
|
86
103
|
? items
|
|
87
104
|
: [items]
|
|
88
105
|
|
|
89
|
-
|
|
90
|
-
|
|
106
|
+
const pipeResult = []
|
|
107
|
+
|
|
108
|
+
let pendingCount = 0
|
|
109
|
+
let resolveAll
|
|
110
|
+
const allDone = new Promise(resolve => {
|
|
111
|
+
resolveAll = resolve
|
|
112
|
+
})
|
|
91
113
|
|
|
114
|
+
/**
|
|
115
|
+
* Worker function that processes one item and potentially spawns a replacement.
|
|
116
|
+
* Uses shift() to atomically retrieve items from the queue, ensuring no duplicate processing.
|
|
117
|
+
* Spawns replacement workers in the finally block to guarantee resilience to errors.
|
|
118
|
+
*
|
|
119
|
+
* @private
|
|
120
|
+
*/
|
|
92
121
|
const processWorker = async() => {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
122
|
+
if(items.length === 0) {
|
|
123
|
+
pendingCount--
|
|
124
|
+
|
|
125
|
+
if(pendingCount === 0)
|
|
126
|
+
resolveAll()
|
|
127
|
+
|
|
128
|
+
return
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const item = items.shift()
|
|
132
|
+
|
|
133
|
+
try {
|
|
134
|
+
const result = await this.#processWorker(item)
|
|
135
|
+
pipeResult.push({ok: true, value: result})
|
|
136
|
+
} catch(error) {
|
|
137
|
+
pipeResult.push({ok: false, error: Sass.new("Processing pipeline item.", error)})
|
|
138
|
+
} finally {
|
|
139
|
+
// Spawn a replacement worker if there are more items
|
|
140
|
+
if(items.length > 0) {
|
|
141
|
+
pendingCount++
|
|
142
|
+
processWorker() // Don't await - let it run in parallel
|
|
104
143
|
}
|
|
144
|
+
|
|
145
|
+
if(--pendingCount === 0)
|
|
146
|
+
resolveAll()
|
|
147
|
+
|
|
148
|
+
this.#debug("pendingCount = %o", 2, pendingCount)
|
|
105
149
|
}
|
|
106
150
|
}
|
|
107
151
|
|
|
@@ -110,49 +154,40 @@ export default class Piper {
|
|
|
110
154
|
)
|
|
111
155
|
this.#processResult("Setting up the pipeline.", setupResult)
|
|
112
156
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
const teardownResult = await Util.settleAll(
|
|
126
|
-
[...this.#lifeCycle.get("teardown")].map(e => e())
|
|
127
|
-
)
|
|
128
|
-
this.#processResult("Tearing down the pipeline.", teardownResult)
|
|
129
|
-
|
|
130
|
-
return allResults
|
|
131
|
-
}
|
|
157
|
+
try {
|
|
158
|
+
// Start workers up to maxConcurrent limit
|
|
159
|
+
const workerCount = Math.min(maxConcurrent, items.length)
|
|
160
|
+
pendingCount = workerCount
|
|
161
|
+
|
|
162
|
+
if(workerCount === 0) {
|
|
163
|
+
resolveAll() // No items to process
|
|
164
|
+
} else {
|
|
165
|
+
for(let i = 0; i < workerCount; i++) {
|
|
166
|
+
processWorker() // Don't await - let them all run in parallel
|
|
167
|
+
}
|
|
168
|
+
}
|
|
132
169
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
*/
|
|
140
|
-
#processResult(message, settled) {
|
|
141
|
-
if(settled.some(r => r.status === "rejected"))
|
|
142
|
-
throw Tantrum.new(
|
|
143
|
-
message,
|
|
144
|
-
settled.filter(r => r.status==="rejected").map(r => r.reason)
|
|
170
|
+
// Wait for all workers to complete
|
|
171
|
+
await allDone
|
|
172
|
+
} finally {
|
|
173
|
+
// Run cleanup hooks
|
|
174
|
+
const teardownResult = await Util.settleAll(
|
|
175
|
+
[...this.#lifeCycle.get("teardown")].map(e => e())
|
|
145
176
|
)
|
|
177
|
+
this.#processResult("Tearing down the pipeline.", teardownResult)
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return pipeResult
|
|
146
181
|
}
|
|
147
182
|
|
|
148
183
|
/**
|
|
149
|
-
* Process a single item through all pipeline steps
|
|
184
|
+
* Process a single item through all pipeline steps.
|
|
150
185
|
*
|
|
151
186
|
* @param {unknown} item The item to process
|
|
152
187
|
* @returns {Promise<unknown>} Result from the final step
|
|
153
188
|
* @private
|
|
154
189
|
*/
|
|
155
|
-
async #
|
|
190
|
+
async #processWorker(item) {
|
|
156
191
|
try {
|
|
157
192
|
// Execute each step in sequence
|
|
158
193
|
let result = item
|
|
@@ -168,4 +203,19 @@ export default class Piper {
|
|
|
168
203
|
throw Sass.new("Processing an item.", error)
|
|
169
204
|
}
|
|
170
205
|
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Validate settleAll results and throw a combined error when rejected.
|
|
209
|
+
*
|
|
210
|
+
* @param {string} message Context message
|
|
211
|
+
* @param {Array<unknown>} settled Results from settleAll
|
|
212
|
+
* @private
|
|
213
|
+
*/
|
|
214
|
+
#processResult(message, settled) {
|
|
215
|
+
if(settled.some(r => r.status === "rejected"))
|
|
216
|
+
throw Tantrum.new(
|
|
217
|
+
message,
|
|
218
|
+
settled.filter(r => r.status==="rejected").map(r => r.reason)
|
|
219
|
+
)
|
|
220
|
+
}
|
|
171
221
|
}
|
|
@@ -21,6 +21,8 @@
|
|
|
21
21
|
* @property {ActionFunction|import("./ActionWrapper.js").default} op Operation to execute.
|
|
22
22
|
* @property {number} [kind] Optional kind flags from {@link ActivityFlags}.
|
|
23
23
|
* @property {(context: unknown) => boolean|Promise<boolean>} [pred] Loop predicate.
|
|
24
|
+
* @property {(context: unknown) => unknown} [splitter] Function to split context for parallel execution (SPLIT activities).
|
|
25
|
+
* @property {(originalContext: unknown, splitResults: unknown) => unknown} [rejoiner] Function to rejoin split results (SPLIT activities).
|
|
24
26
|
*/
|
|
25
27
|
/**
|
|
26
28
|
* @typedef {(context: unknown) => unknown|Promise<unknown>} ActionFunction
|
|
@@ -49,10 +51,16 @@ export default class ActionBuilder {
|
|
|
49
51
|
/**
|
|
50
52
|
* Creates a new ActionBuilder instance with the provided action callback.
|
|
51
53
|
*
|
|
52
|
-
* @param {ActionBuilderAction} [action] Base action invoked by the runner when a block satisfies the configured structure.
|
|
53
|
-
* @param {ActionBuilderConfig} [config] Options
|
|
54
|
+
* @param {ActionBuilderAction} [action] - Base action invoked by the runner when a block satisfies the configured structure.
|
|
55
|
+
* @param {ActionBuilderConfig} [config] - Options
|
|
54
56
|
*/
|
|
55
57
|
constructor(action?: ActionBuilderAction, { tag, debug }?: ActionBuilderConfig)
|
|
58
|
+
/**
|
|
59
|
+
* Get the builder's tag symbol.
|
|
60
|
+
*
|
|
61
|
+
* @returns {symbol|null} The tag symbol for this builder instance
|
|
62
|
+
*/
|
|
63
|
+
get tag(): symbol | null
|
|
56
64
|
/**
|
|
57
65
|
* Register an activity that the runner can execute.
|
|
58
66
|
*
|
|
@@ -75,6 +83,16 @@ export default class ActionBuilder {
|
|
|
75
83
|
* @returns {ActionBuilder}
|
|
76
84
|
*/
|
|
77
85
|
do(name: string | symbol, kind: number, pred: (context: unknown) => boolean | Promise<boolean>, op: ActionFunction | import('./ActionWrapper.js').default): ActionBuilder
|
|
86
|
+
/**
|
|
87
|
+
* @overload
|
|
88
|
+
* @param {string|symbol} name Activity name
|
|
89
|
+
* @param {number} kind Kind bitfield (ACTIVITY.SPLIT).
|
|
90
|
+
* @param {(context: unknown) => unknown} splitter Function to split context for parallel execution.
|
|
91
|
+
* @param {(originalContext: unknown, splitResults: unknown) => unknown} rejoiner Function to rejoin split results with original context.
|
|
92
|
+
* @param {ActionFunction|ActionBuilder} op Operation or nested ActionBuilder to execute on split context.
|
|
93
|
+
* @returns {ActionBuilder}
|
|
94
|
+
*/
|
|
95
|
+
do(name: string | symbol, kind: number, splitter: (context: unknown) => unknown, rejoiner: (originalContext: unknown, splitResults: unknown) => unknown, op: ActionFunction | ActionBuilder): ActionBuilder
|
|
78
96
|
/**
|
|
79
97
|
* Configure hooks to be loaded from a file when the action is built.
|
|
80
98
|
*
|
|
@@ -92,6 +110,14 @@ export default class ActionBuilder {
|
|
|
92
110
|
* @throws {Sass} If hooks have already been configured.
|
|
93
111
|
*/
|
|
94
112
|
withHooks(hooks: import('./ActionHooks.js').default): ActionBuilder
|
|
113
|
+
/**
|
|
114
|
+
* Configure hooks using an ActionHooks instance directly (typically used internally).
|
|
115
|
+
*
|
|
116
|
+
* @param {import("./ActionHooks.js").default} actionHooks Pre-configured ActionHooks instance.
|
|
117
|
+
* @returns {ActionBuilder} The builder instance for chaining.
|
|
118
|
+
* @throws {Sass} If hooks have already been configured.
|
|
119
|
+
*/
|
|
120
|
+
withActionHooks(actionHooks: import('./ActionHooks.js').default): ActionBuilder
|
|
95
121
|
/**
|
|
96
122
|
* Finalises the builder and returns a payload that can be consumed by the
|
|
97
123
|
* runner.
|
|
@@ -99,6 +125,12 @@ export default class ActionBuilder {
|
|
|
99
125
|
* @returns {Promise<import("./ActionWrapper.js").default>} Payload consumed by the {@link ActionRunner} constructor.
|
|
100
126
|
*/
|
|
101
127
|
build(): Promise<import('./ActionWrapper.js').default>
|
|
128
|
+
/**
|
|
129
|
+
* Check if this builder has ActionHooks configured.
|
|
130
|
+
*
|
|
131
|
+
* @returns {boolean} True if ActionHooks have been configured.
|
|
132
|
+
*/
|
|
133
|
+
get hasActionHooks(): boolean
|
|
102
134
|
#private
|
|
103
135
|
}
|
|
104
136
|
export type ActionRunner = import('./ActionRunner.js').default
|
|
@@ -149,6 +181,14 @@ export type ActivityDefinition = {
|
|
|
149
181
|
* Loop predicate.
|
|
150
182
|
*/
|
|
151
183
|
pred?: ((context: unknown) => boolean | Promise<boolean>) | undefined;
|
|
184
|
+
/**
|
|
185
|
+
* Function to split context for parallel execution (SPLIT activities).
|
|
186
|
+
*/
|
|
187
|
+
splitter?: ((context: unknown) => unknown) | undefined;
|
|
188
|
+
/**
|
|
189
|
+
* Function to rejoin split results (SPLIT activities).
|
|
190
|
+
*/
|
|
191
|
+
rejoiner?: ((originalContext: unknown, splitResults: unknown) => unknown) | undefined;
|
|
152
192
|
}
|
|
153
193
|
export type ActionFunction = (context: unknown) => unknown | Promise<unknown>
|
|
154
194
|
//# sourceMappingURL=ActionBuilder.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ActionBuilder.d.ts","sourceRoot":"","sources":["../../lib/ActionBuilder.js"],"names":[],"mappings":"AAKA,kEAAkE;AAClE,uEAAuE;AAEvE;;GAEG;AAEH;;;;GAIG;AAEH;;;;GAIG;AAEH
|
|
1
|
+
{"version":3,"file":"ActionBuilder.d.ts","sourceRoot":"","sources":["../../lib/ActionBuilder.js"],"names":[],"mappings":"AAKA,kEAAkE;AAClE,uEAAuE;AAEvE;;GAEG;AAEH;;;;GAIG;AAEH;;;;GAIG;AAEH;;;;;;;;;;GAUG;AAEH;;GAEG;AAEH;;;;;;;;;;;;;;;;;;;GAmBG;AACH;IA2BE;;;;;OAKG;IACH,qBAHW,mBAAmB,mBACnB,mBAAmB,EAe7B;IA5BD;;;;OAIG;IACH,WAFa,MAAM,GAAC,IAAI,CAIvB;;;;;;;;;;;;;IA8BE,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,MAC9C,cAAc,GAAC,OAAO,oBAAoB,EAAE,OAAO,GACjD,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;IAsDD;;;;;;;OAOG;IACH,yBALW,MAAM,aACN,MAAM,GACJ,aAAa,CAUzB;IAED;;;;;;OAMG;IACH,iBAJW,OAAO,kBAAkB,EAAE,OAAO,GAChC,aAAa,CASzB;IAED;;;;;;OAMG;IACH,6BAJW,OAAO,kBAAkB,EAAE,OAAO,GAChC,aAAa,CASzB;IA2BD;;;;;OAKG;IACH,SAFa,OAAO,CAAC,OAAO,oBAAoB,EAAE,OAAO,CAAC,CAiBzD;IAED;;;;OAIG;IACH,sBAFa,OAAO,CAInB;;CAiCF;2BAlUa,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;;;;0BACpC,OAAO,KAAK,OAAO;;;;kCACX,OAAO,gBAAgB,OAAO,KAAK,OAAO;;6BAI7D,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,GAAC,OAAO,CAAC,OAAO,CAAC"}
|
|
@@ -3,11 +3,10 @@
|
|
|
3
3
|
*/
|
|
4
4
|
/**
|
|
5
5
|
* @typedef {object} ActionHooksConfig
|
|
6
|
-
* @property {string} actionKind Action identifier shared between runner and hooks.
|
|
7
|
-
* @property {FileObject} hooksFile File handle used to import the hooks module.
|
|
8
|
-
* @property {unknown} [
|
|
6
|
+
* @property {string} [actionKind] Action identifier shared between runner and hooks.
|
|
7
|
+
* @property {FileObject|string} [hooksFile] File handle or path used to import the hooks module.
|
|
8
|
+
* @property {unknown} [hooksObject] Already-instantiated hooks implementation (skips loading).
|
|
9
9
|
* @property {number} [hookTimeout] Timeout applied to hook execution in milliseconds.
|
|
10
|
-
* @property {DebugFn} debug Logger to emit diagnostics.
|
|
11
10
|
*/
|
|
12
11
|
/**
|
|
13
12
|
* @typedef {Record<string, (context: unknown) => Promise<unknown>|unknown>} HookModule
|
|
@@ -21,37 +20,38 @@ export default class ActionHooks {
|
|
|
21
20
|
/**
|
|
22
21
|
* Static factory method to create and initialize a hook manager.
|
|
23
22
|
* Loads hooks from the specified file and returns an initialized instance.
|
|
24
|
-
*
|
|
23
|
+
* If a hooksObject is provided in config, it's used directly; otherwise, hooks are loaded from file.
|
|
25
24
|
*
|
|
26
|
-
* @param {ActionHooksConfig} config
|
|
25
|
+
* @param {ActionHooksConfig} config Configuration object with hooks settings
|
|
27
26
|
* @param {DebugFn} debug The debug function.
|
|
28
|
-
* @returns {Promise<ActionHooks
|
|
27
|
+
* @returns {Promise<ActionHooks>} Initialized hook manager
|
|
29
28
|
*/
|
|
30
|
-
static 'new'(config: ActionHooksConfig, debug: DebugFn): Promise<ActionHooks
|
|
29
|
+
static 'new'(config: ActionHooksConfig, debug: DebugFn): Promise<ActionHooks>
|
|
31
30
|
/**
|
|
32
31
|
* Creates a new ActionHook instance.
|
|
33
32
|
*
|
|
34
33
|
* @param {ActionHooksConfig} config Configuration values describing how to load the hooks.
|
|
34
|
+
* @param {(message: string, level?: number, ...args: Array<unknown>) => void} debug Debug function
|
|
35
35
|
*/
|
|
36
|
-
constructor({ actionKind, hooksFile,
|
|
36
|
+
constructor({ actionKind, hooksFile, hooksObject, hookTimeout }: ActionHooksConfig, debug: (message: string, level?: number, ...args: Array<unknown>) => void)
|
|
37
37
|
/**
|
|
38
38
|
* Gets the action identifier.
|
|
39
39
|
*
|
|
40
|
-
* @returns {string} Action identifier or instance
|
|
40
|
+
* @returns {string|null} Action identifier or instance
|
|
41
41
|
*/
|
|
42
|
-
get actionKind(): string
|
|
42
|
+
get actionKind(): string | null
|
|
43
43
|
/**
|
|
44
44
|
* Gets the hooks file object.
|
|
45
45
|
*
|
|
46
|
-
* @returns {FileObject} File object containing hooks
|
|
46
|
+
* @returns {FileObject|null} File object containing hooks
|
|
47
47
|
*/
|
|
48
|
-
get hooksFile(): FileObject
|
|
48
|
+
get hooksFile(): FileObject | null
|
|
49
49
|
/**
|
|
50
50
|
* Gets the loaded hooks object.
|
|
51
51
|
*
|
|
52
|
-
* @returns {
|
|
52
|
+
* @returns {HookModule|null} Hooks object or null if not loaded
|
|
53
53
|
*/
|
|
54
|
-
get hooks():
|
|
54
|
+
get hooks(): HookModule | null
|
|
55
55
|
/**
|
|
56
56
|
* Gets the hook execution timeout in milliseconds.
|
|
57
57
|
*
|
|
@@ -71,12 +71,15 @@ export default class ActionHooks {
|
|
|
71
71
|
*/
|
|
72
72
|
get cleanup(): (args: object) => unknown | null
|
|
73
73
|
/**
|
|
74
|
-
* Invoke a dynamically-named hook such as `before$foo`.
|
|
74
|
+
* Invoke a dynamically-named hook such as `before$foo` or `after$foo`.
|
|
75
|
+
* The hook name is constructed by combining the kind with the activity name.
|
|
76
|
+
* Symbols are converted to their description. Non-alphanumeric characters are filtered out.
|
|
75
77
|
*
|
|
76
78
|
* @param {'before'|'after'|'setup'|'cleanup'|string} kind Hook namespace.
|
|
77
79
|
* @param {string|symbol} activityName Activity identifier.
|
|
78
80
|
* @param {unknown} context Pipeline context supplied to the hook.
|
|
79
81
|
* @returns {Promise<void>}
|
|
82
|
+
* @throws {Sass} If the hook execution fails or exceeds timeout.
|
|
80
83
|
*/
|
|
81
84
|
callHook(kind: 'before' | 'after' | 'setup' | 'cleanup' | string, activityName: string | symbol, context: unknown): Promise<void>
|
|
82
85
|
#private
|
|
@@ -86,23 +89,19 @@ export type ActionHooksConfig = {
|
|
|
86
89
|
/**
|
|
87
90
|
* Action identifier shared between runner and hooks.
|
|
88
91
|
*/
|
|
89
|
-
actionKind
|
|
92
|
+
actionKind?: string | undefined;
|
|
90
93
|
/**
|
|
91
|
-
* File handle used to import the hooks module.
|
|
94
|
+
* File handle or path used to import the hooks module.
|
|
92
95
|
*/
|
|
93
|
-
hooksFile
|
|
96
|
+
hooksFile?: string | FileObject | undefined;
|
|
94
97
|
/**
|
|
95
98
|
* Already-instantiated hooks implementation (skips loading).
|
|
96
99
|
*/
|
|
97
|
-
|
|
100
|
+
hooksObject?: unknown;
|
|
98
101
|
/**
|
|
99
102
|
* Timeout applied to hook execution in milliseconds.
|
|
100
103
|
*/
|
|
101
104
|
hookTimeout?: number | undefined;
|
|
102
|
-
/**
|
|
103
|
-
* Logger to emit diagnostics.
|
|
104
|
-
*/
|
|
105
|
-
debug: DebugFn;
|
|
106
105
|
}
|
|
107
106
|
export type HookModule = Record<string, (context: unknown) => Promise<unknown> | unknown>
|
|
108
107
|
import { FileObject } from '@gesslar/toolkit'
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ActionHooks.d.ts","sourceRoot":"","sources":["../../lib/ActionHooks.js"],"names":[],"mappings":"AAGA;;GAEG;AAEH
|
|
1
|
+
{"version":3,"file":"ActionHooks.d.ts","sourceRoot":"","sources":["../../lib/ActionHooks.js"],"names":[],"mappings":"AAGA;;GAEG;AAEH;;;;;;GAMG;AAEH;;GAEG;AAEH;;;;GAIG;AACH;IAmFE;;;;;;;;OAQG;IACH,qBAJW,iBAAiB,SACjB,OAAO,GACL,OAAO,CAAC,WAAW,CAAC,CA2ChC;IAzHD;;;;;OAKG;IACH,iEAHW,iBAAiB,SACjB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,KAAK,CAAC,OAAO,CAAC,KAAK,IAAI,EAW5E;IAED;;;;OAIG;IACH,kBAFa,MAAM,GAAC,IAAI,CAIvB;IAED;;;;OAIG;IACH,iBAFa,UAAU,GAAC,IAAI,CAI3B;IAED;;;;OAIG;IACH,aAFa,UAAU,GAAC,IAAI,CAI3B;IAED;;;;OAIG;IACH,eAFa,MAAM,CAIlB;IAED;;;;OAIG;IACH,aAFa,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,GAAC,IAAI,CAI1C;IAED;;;;OAIG;IACH,eAFa,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,GAAC,IAAI,CAI1C;IAsDD;;;;;;;;;;OAUG;IACH,eANW,QAAQ,GAAC,OAAO,GAAC,OAAO,GAAC,SAAS,GAAC,MAAM,gBACzC,MAAM,GAAC,MAAM,WACb,OAAO,GACL,OAAO,CAAC,IAAI,CAAC,CAyDzB;;CA6BF;sBAzPY,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,KAAK,CAAC,OAAO,CAAC,KAAK,IAAI;;;;;;;;;;;;;kBAOjE,OAAO;;;;;;yBAKR,MAAM,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,GAAC,OAAO,CAAC;2BAfzB,kBAAkB"}
|
|
@@ -22,10 +22,12 @@ export default class ActionRunner extends Piper {
|
|
|
22
22
|
constructor(actionBuilder: import('./ActionBuilder.js').default | null, { debug }?: ActionRunnerOptions)
|
|
23
23
|
/**
|
|
24
24
|
* Executes the configured action pipeline.
|
|
25
|
+
* Builds the ActionWrapper on first run and caches it for subsequent calls.
|
|
26
|
+
* Supports WHILE, UNTIL, and SPLIT activity kinds.
|
|
25
27
|
*
|
|
26
28
|
* @param {unknown} context - Seed value passed to the first activity.
|
|
27
|
-
* @returns {Promise<unknown>} Final value produced by the pipeline
|
|
28
|
-
* @throws {Sass} When no activities are registered
|
|
29
|
+
* @returns {Promise<unknown>} Final value produced by the pipeline.
|
|
30
|
+
* @throws {Sass} When no activities are registered, conflicting activity kinds are used, or execution fails.
|
|
29
31
|
*/
|
|
30
32
|
run(context: unknown): Promise<unknown>
|
|
31
33
|
#private
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ActionRunner.d.ts","sourceRoot":"","sources":["../../lib/ActionRunner.js"],"names":[],"mappings":"AAMA;;GAEG;AAEH;;;GAGG;AACH;;;;;;GAMG;AACH;
|
|
1
|
+
{"version":3,"file":"ActionRunner.d.ts","sourceRoot":"","sources":["../../lib/ActionRunner.js"],"names":[],"mappings":"AAMA;;GAEG;AAEH;;;GAGG;AACH;;;;;;GAMG;AACH;IAaE;;;;;OAKG;IACH,2BAHW,OAAO,oBAAoB,EAAE,OAAO,GAAC,IAAI,cACzC,mBAAmB,EAkB7B;IAED;;;;;;;;OAQG;IACH,aAJW,OAAO,GACL,OAAO,CAAC,OAAO,CAAC,CA4E5B;;CAwFF;sBA7NY,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,KAAK,CAAC,OAAO,CAAC,KAAK,IAAI;;;;;;;kBAH7D,YAAY"}
|