@gesslar/toolkit 0.4.0 → 0.5.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/package.json +2 -1
- package/src/index.js +3 -0
- package/src/lib/Action.js +283 -0
- package/src/lib/ActionRunner.js +21 -51
- package/src/lib/Contract.js +257 -0
- package/src/lib/Data.js +1 -1
- package/src/lib/Glog.js +3 -2
- package/src/lib/Hooks.js +194 -0
- package/src/lib/Piper.js +74 -100
- package/src/lib/Schemer.js +89 -0
- package/src/lib/Terms.js +74 -0
- package/src/types/Contract.d.ts +162 -0
- package/src/types/Schemer.d.ts +179 -0
- package/src/types/Terms.d.ts +145 -0
- package/src/types/index.d.ts +3 -0
- package/src/lib/BaseActionManager.js +0 -246
- package/src/lib/BaseHookManager.js +0 -209
|
@@ -1,246 +0,0 @@
|
|
|
1
|
-
import Data from "./Data.js"
|
|
2
|
-
import Sass from "./Sass.js"
|
|
3
|
-
import ActionBuilder from "./ActionBuilder.js"
|
|
4
|
-
import ActionRunner from "./ActionRunner.js"
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Generic base class for managing actions with lifecycle hooks.
|
|
8
|
-
* Provides common functionality for action setup, execution, and cleanup.
|
|
9
|
-
* Designed to be extended by specific implementations.
|
|
10
|
-
*/
|
|
11
|
-
export default class BaseActionManager {
|
|
12
|
-
#action = null
|
|
13
|
-
#hookManager = null
|
|
14
|
-
#contract = null
|
|
15
|
-
#log = null
|
|
16
|
-
#debug = null
|
|
17
|
-
#file = null
|
|
18
|
-
#variables = null
|
|
19
|
-
#runner = null
|
|
20
|
-
#id = null
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* @param {object} config - Configuration object
|
|
24
|
-
* @param {object} config.actionDefinition - Action definition with action, file, and contract
|
|
25
|
-
* @param {object} config.logger - Logger instance
|
|
26
|
-
* @param {object} [config.variables] - Variables to pass to action
|
|
27
|
-
*/
|
|
28
|
-
constructor({actionDefinition, logger, variables}) {
|
|
29
|
-
this.#id = Symbol(performance.now())
|
|
30
|
-
this.#log = logger
|
|
31
|
-
this.#debug = this.#log.newDebug()
|
|
32
|
-
this.#variables = variables || {}
|
|
33
|
-
|
|
34
|
-
this.#initialize(actionDefinition)
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
get id() {
|
|
38
|
-
return this.#id
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
get action() {
|
|
42
|
-
return this.#action
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
get hookManager() {
|
|
46
|
-
return this.#hookManager
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
set hookManager(hookManager) {
|
|
50
|
-
if(this.hookManager)
|
|
51
|
-
throw new Error("Hook manager already set")
|
|
52
|
-
|
|
53
|
-
this.#hookManager = hookManager
|
|
54
|
-
this.#attachHooksToAction(hookManager)
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
get contract() {
|
|
58
|
-
return this.#contract
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
get meta() {
|
|
62
|
-
return this.#action?.meta
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
get log() {
|
|
66
|
-
return this.#log
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
get variables() {
|
|
70
|
-
return this.#variables
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
get runner() {
|
|
74
|
-
return this.#runner
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
get file() {
|
|
78
|
-
return this.#file
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Initialize the action manager with the provided definition.
|
|
83
|
-
* Override in subclasses to add specific validation or setup.
|
|
84
|
-
*
|
|
85
|
-
* @param {object} actionDefinition - Action definition
|
|
86
|
-
* @protected
|
|
87
|
-
*/
|
|
88
|
-
#initialize(actionDefinition) {
|
|
89
|
-
const debug = this.#debug
|
|
90
|
-
|
|
91
|
-
debug("Setting up action", 2)
|
|
92
|
-
|
|
93
|
-
const {action, file, contract} = actionDefinition
|
|
94
|
-
|
|
95
|
-
if(!action)
|
|
96
|
-
throw new Error("Action is required")
|
|
97
|
-
|
|
98
|
-
if(!contract)
|
|
99
|
-
throw new Error("Contract is required")
|
|
100
|
-
|
|
101
|
-
this.#action = action
|
|
102
|
-
this.#contract = contract
|
|
103
|
-
this.#file = file
|
|
104
|
-
|
|
105
|
-
debug("Action initialization complete", 2)
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Attach hooks to the action instance.
|
|
110
|
-
* Override in subclasses to customize hook attachment.
|
|
111
|
-
*
|
|
112
|
-
* @param {object} hookManager - Hook manager instance
|
|
113
|
-
* @protected
|
|
114
|
-
*/
|
|
115
|
-
#attachHooksToAction(hookManager) {
|
|
116
|
-
// Basic hook attachment - can be overridden by subclasses
|
|
117
|
-
this.action.hook = hookManager.on?.bind(hookManager)
|
|
118
|
-
this.action.hooks = hookManager.hooks
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* Setup the action by creating and configuring the runner.
|
|
123
|
-
* Override setupActionInstance() in subclasses for custom setup logic.
|
|
124
|
-
*
|
|
125
|
-
* @returns {Promise<void>}
|
|
126
|
-
*/
|
|
127
|
-
async setupAction() {
|
|
128
|
-
this.#debug("Setting up action for %s on %s", 2, this.action.meta?.kind, this.id)
|
|
129
|
-
|
|
130
|
-
await this.#setupHooks()
|
|
131
|
-
await this.#setupActionInstance()
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Setup the action instance and create the runner.
|
|
136
|
-
* Override in subclasses to customize action setup.
|
|
137
|
-
*
|
|
138
|
-
* @protected
|
|
139
|
-
*/
|
|
140
|
-
async #setupActionInstance() {
|
|
141
|
-
const actionInstance = new this.action()
|
|
142
|
-
const setup = actionInstance?.setup
|
|
143
|
-
|
|
144
|
-
// Setup is required for actions.
|
|
145
|
-
if(Data.typeOf(setup) === "Function") {
|
|
146
|
-
const builder = new ActionBuilder(actionInstance)
|
|
147
|
-
const configuredBuilder = setup(builder)
|
|
148
|
-
const buildResult = configuredBuilder.build()
|
|
149
|
-
const runner = new ActionRunner({
|
|
150
|
-
action: buildResult.action,
|
|
151
|
-
build: buildResult.build,
|
|
152
|
-
logger: this.#log
|
|
153
|
-
})
|
|
154
|
-
|
|
155
|
-
this.#runner = runner
|
|
156
|
-
} else {
|
|
157
|
-
throw Sass.new("Action setup must be a function.")
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* Run the action with the provided input.
|
|
163
|
-
*
|
|
164
|
-
* @param {unknown} result - Input to pass to the action
|
|
165
|
-
* @returns {Promise<unknown>} Action result
|
|
166
|
-
*/
|
|
167
|
-
async runAction(result) {
|
|
168
|
-
if(!this.#runner)
|
|
169
|
-
throw new Error("Action not set up. Call setupAction() first.")
|
|
170
|
-
|
|
171
|
-
return await this.#runner.run(result)
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
/**
|
|
175
|
-
* Cleanup the action and hooks.
|
|
176
|
-
*
|
|
177
|
-
* @returns {Promise<void>}
|
|
178
|
-
*/
|
|
179
|
-
async cleanupAction() {
|
|
180
|
-
this.#debug("Cleaning up action for %s on %s", 2, this.action.meta?.kind, this.id)
|
|
181
|
-
|
|
182
|
-
await this.#cleanupHooks()
|
|
183
|
-
await this.#cleanupActionInstance()
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
/**
|
|
187
|
-
* Setup hooks if hook manager is present.
|
|
188
|
-
* Override in subclasses to customize hook setup.
|
|
189
|
-
*
|
|
190
|
-
* @protected
|
|
191
|
-
*/
|
|
192
|
-
async #setupHooks() {
|
|
193
|
-
const setup = this.#hookManager?.setup
|
|
194
|
-
|
|
195
|
-
const type = Data.typeOf(setup)
|
|
196
|
-
|
|
197
|
-
// No hooks attached.
|
|
198
|
-
if(type === "Null" || type === "Undefined")
|
|
199
|
-
return
|
|
200
|
-
|
|
201
|
-
if(type !== "Function")
|
|
202
|
-
throw Sass.new("Hook setup must be a function.")
|
|
203
|
-
|
|
204
|
-
await setup.call(
|
|
205
|
-
this.hookManager.hooks, {
|
|
206
|
-
action: this.action,
|
|
207
|
-
variables: this.#variables,
|
|
208
|
-
log: this.#log
|
|
209
|
-
}
|
|
210
|
-
)
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
/**
|
|
214
|
-
* Cleanup hooks if hook manager is present.
|
|
215
|
-
* Override in subclasses to customize hook cleanup.
|
|
216
|
-
*
|
|
217
|
-
* @protected
|
|
218
|
-
*/
|
|
219
|
-
async #cleanupHooks() {
|
|
220
|
-
const cleanup = this.hookManager?.cleanup
|
|
221
|
-
|
|
222
|
-
if(!cleanup)
|
|
223
|
-
return
|
|
224
|
-
|
|
225
|
-
await cleanup.call(this.hookManager.hooks)
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
/**
|
|
229
|
-
* Cleanup the action instance.
|
|
230
|
-
* Override in subclasses to add custom cleanup logic.
|
|
231
|
-
*
|
|
232
|
-
* @protected
|
|
233
|
-
*/
|
|
234
|
-
async #cleanupActionInstance() {
|
|
235
|
-
const cleanup = this.action?.cleanup
|
|
236
|
-
|
|
237
|
-
if(!cleanup)
|
|
238
|
-
return
|
|
239
|
-
|
|
240
|
-
await cleanup.call(this.action)
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
toString() {
|
|
244
|
-
return `${this.#file?.module || "UNDEFINED"} (${this.meta?.action || "UNDEFINED"})`
|
|
245
|
-
}
|
|
246
|
-
}
|
|
@@ -1,209 +0,0 @@
|
|
|
1
|
-
import {setTimeout as timeoutPromise} from "timers/promises"
|
|
2
|
-
import Collection from "./Collection.js"
|
|
3
|
-
import Data from "./Data.js"
|
|
4
|
-
import Sass from "./Sass.js"
|
|
5
|
-
import Valid from "./Valid.js"
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Generic base class for managing hooks with configurable event types.
|
|
9
|
-
* Provides common functionality for hook registration, execution, and lifecycle management.
|
|
10
|
-
* Designed to be extended by specific implementations.
|
|
11
|
-
*/
|
|
12
|
-
export default class BaseHookManager {
|
|
13
|
-
#hooksFile = null
|
|
14
|
-
#log = null
|
|
15
|
-
#hooks = null
|
|
16
|
-
#action = null
|
|
17
|
-
#timeout = 1000 // Default 1 second timeout
|
|
18
|
-
#allowedEvents = []
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* @param {object} config - Configuration object
|
|
22
|
-
* @param {string|object} config.action - Action identifier or instance
|
|
23
|
-
* @param {object} config.hooksFile - File object containing hooks
|
|
24
|
-
* @param {object} config.logger - Logger instance
|
|
25
|
-
* @param {number} [config.timeOut] - Hook execution timeout in milliseconds
|
|
26
|
-
* @param {string[]} [config.allowedEvents] - Array of allowed event types for validation
|
|
27
|
-
*/
|
|
28
|
-
constructor({action, hooksFile, logger, timeOut = 1000, allowedEvents = []}) {
|
|
29
|
-
this.#action = action
|
|
30
|
-
this.#hooksFile = hooksFile
|
|
31
|
-
this.#log = logger
|
|
32
|
-
this.#timeout = timeOut
|
|
33
|
-
this.#allowedEvents = allowedEvents
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
get action() {
|
|
37
|
-
return this.#action
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
get hooksFile() {
|
|
41
|
-
return this.#hooksFile
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
get hooks() {
|
|
45
|
-
return this.#hooks
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
get log() {
|
|
49
|
-
return this.#log
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
get timeout() {
|
|
53
|
-
return this.#timeout
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
get allowedEvents() {
|
|
57
|
-
return this.#allowedEvents
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
get setup() {
|
|
61
|
-
return this.hooks?.setup || null
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
get cleanup() {
|
|
65
|
-
return this.hooks?.cleanup || null
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Static factory method to create and initialize a hook manager.
|
|
70
|
-
* Override loadHooks() in subclasses to customize hook loading logic.
|
|
71
|
-
*
|
|
72
|
-
* @param {object} config - Same as constructor config
|
|
73
|
-
* @returns {Promise<BaseHookManager|null>} Initialized hook manager or null if no hooks found
|
|
74
|
-
*/
|
|
75
|
-
static async new(config) {
|
|
76
|
-
const instance = new this(config)
|
|
77
|
-
const debug = instance.log.newDebug()
|
|
78
|
-
|
|
79
|
-
debug("Creating new HookManager instance with args: `%o`", 2, config)
|
|
80
|
-
|
|
81
|
-
const hooksFile = instance.hooksFile
|
|
82
|
-
|
|
83
|
-
debug("Loading hooks from `%s`", 2, hooksFile.uri)
|
|
84
|
-
|
|
85
|
-
debug("Checking hooks file exists: %j", 2, hooksFile)
|
|
86
|
-
|
|
87
|
-
try {
|
|
88
|
-
const hooksFileContent = await import(hooksFile.uri)
|
|
89
|
-
|
|
90
|
-
debug("Hooks file loaded successfully", 2)
|
|
91
|
-
|
|
92
|
-
if(!hooksFileContent)
|
|
93
|
-
throw new Error(`Hooks file is empty: ${hooksFile.uri}`)
|
|
94
|
-
|
|
95
|
-
const hooks = await instance.loadHooks(hooksFileContent)
|
|
96
|
-
|
|
97
|
-
if(Data.isEmpty(hooks))
|
|
98
|
-
return null
|
|
99
|
-
|
|
100
|
-
debug("Hooks found for action: `%s`", 2, instance.action)
|
|
101
|
-
|
|
102
|
-
if(!hooks)
|
|
103
|
-
return null
|
|
104
|
-
|
|
105
|
-
// Attach common properties to hooks
|
|
106
|
-
hooks.log = instance.log
|
|
107
|
-
hooks.timeout = instance.timeout
|
|
108
|
-
instance.#hooks = hooks
|
|
109
|
-
|
|
110
|
-
debug("Hooks loaded successfully for `%s`", 2, instance.action)
|
|
111
|
-
|
|
112
|
-
return instance
|
|
113
|
-
} catch(error) {
|
|
114
|
-
debug("Failed to load hooks: %s", 1, error.message)
|
|
115
|
-
|
|
116
|
-
return null
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Load hooks from the imported hooks file content.
|
|
122
|
-
* Override in subclasses to customize hook loading logic.
|
|
123
|
-
*
|
|
124
|
-
* @param {object} hooksFileContent - Imported hooks file content
|
|
125
|
-
* @returns {Promise<object|null>} Loaded hooks object or null if no hooks found
|
|
126
|
-
* @protected
|
|
127
|
-
*/
|
|
128
|
-
async loadHooks(hooksFileContent) {
|
|
129
|
-
const hooks = hooksFileContent.default || hooksFileContent.Hooks
|
|
130
|
-
|
|
131
|
-
if(!hooks)
|
|
132
|
-
throw new Error(`\`${this.hooksFile.uri}\` contains no hooks.`)
|
|
133
|
-
|
|
134
|
-
// Default implementation: look for hooks by action name
|
|
135
|
-
const hooksObj = hooks[this.action]
|
|
136
|
-
|
|
137
|
-
return hooksObj || null
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* Trigger a hook by event name.
|
|
142
|
-
*
|
|
143
|
-
* @param {string} event - The type of hook to trigger
|
|
144
|
-
* @param {object} args - The hook arguments as an object
|
|
145
|
-
* @returns {Promise<unknown>} The result of the hook
|
|
146
|
-
*/
|
|
147
|
-
async on(event, args) {
|
|
148
|
-
const debug = this.log.newDebug()
|
|
149
|
-
|
|
150
|
-
debug("Triggering hook for event `%s`", 4, event)
|
|
151
|
-
|
|
152
|
-
if(!event)
|
|
153
|
-
throw new Error("Event type is required for hook invocation")
|
|
154
|
-
|
|
155
|
-
// Validate event type if allowed events are configured
|
|
156
|
-
if(this.#allowedEvents.length > 0 && !this.#allowedEvents.includes(event))
|
|
157
|
-
throw new Error(`Invalid event type: ${event}. Allowed events: ${this.#allowedEvents.join(", ")}`)
|
|
158
|
-
|
|
159
|
-
const hook = this.hooks?.[event]
|
|
160
|
-
|
|
161
|
-
if(hook) {
|
|
162
|
-
Valid.type(hook, "function", `Hook "${event}" is not a function`)
|
|
163
|
-
|
|
164
|
-
const hookExecution = hook.call(this.hooks, args)
|
|
165
|
-
const hookTimeout = this.timeout
|
|
166
|
-
|
|
167
|
-
const expireAsync = () =>
|
|
168
|
-
timeoutPromise(
|
|
169
|
-
hookTimeout,
|
|
170
|
-
new Error(`Hook execution exceeded timeout of ${hookTimeout}ms`)
|
|
171
|
-
)
|
|
172
|
-
|
|
173
|
-
const result = await Promise.race([hookExecution, expireAsync()])
|
|
174
|
-
|
|
175
|
-
if(result?.status === "error")
|
|
176
|
-
throw Sass.new(result.error)
|
|
177
|
-
|
|
178
|
-
debug("Hook executed successfully for event: `%s`", 4, event)
|
|
179
|
-
|
|
180
|
-
return result
|
|
181
|
-
} else {
|
|
182
|
-
debug("No hook found for event: `%s`", 4, event)
|
|
183
|
-
|
|
184
|
-
return null
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
/**
|
|
189
|
-
* Check if a hook exists for the given event.
|
|
190
|
-
*
|
|
191
|
-
* @param {string} event - Event name to check
|
|
192
|
-
* @returns {boolean} True if hook exists
|
|
193
|
-
*/
|
|
194
|
-
hasHook(event) {
|
|
195
|
-
return !!(this.hooks?.[event])
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
/**
|
|
199
|
-
* Get all available hook events.
|
|
200
|
-
*
|
|
201
|
-
* @returns {string[]} Array of available hook event names
|
|
202
|
-
*/
|
|
203
|
-
getAvailableEvents() {
|
|
204
|
-
return this.hooks ? Object.keys(this.hooks).filter(key =>
|
|
205
|
-
typeof this.hooks[key] === "function" &&
|
|
206
|
-
!["setup", "cleanup", "log", "timeout"].includes(key)
|
|
207
|
-
) : []
|
|
208
|
-
}
|
|
209
|
-
}
|