@gesslar/toolkit 0.5.0 → 0.7.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.
Files changed (66) hide show
  1. package/package.json +8 -8
  2. package/src/lib/Collection.js +132 -17
  3. package/src/lib/Contract.js +1 -1
  4. package/src/lib/Data.js +27 -18
  5. package/src/lib/DirectoryObject.js +1 -1
  6. package/src/lib/FS.js +10 -0
  7. package/src/lib/Glog.js +27 -10
  8. package/src/lib/Logger.js +3 -0
  9. package/src/lib/Sass.js +6 -1
  10. package/src/lib/Tantrum.js +43 -0
  11. package/src/lib/TypeSpec.js +11 -7
  12. package/src/lib/Util.js +82 -0
  13. package/src/lib/Valid.js +24 -6
  14. package/src/types/Collection.d.ts +6 -1
  15. package/src/types/Contract.d.ts +27 -27
  16. package/src/types/Data.d.ts +23 -23
  17. package/src/types/FS.d.ts +3 -3
  18. package/src/types/Glog.d.ts +302 -49
  19. package/src/types/Sass.d.ts +1 -1
  20. package/src/types/Schemer.d.ts +29 -29
  21. package/src/types/Tantrum.d.ts +10 -10
  22. package/src/types/Term.d.ts +1 -1
  23. package/src/types/Terms.d.ts +21 -21
  24. package/src/types/Type.d.ts +1 -1
  25. package/src/types/Util.d.ts +20 -2
  26. package/src/types/index.d.ts +17 -23
  27. package/src/types/index.d.ts.map +1 -0
  28. package/src/types/lib/Cache.d.ts +28 -0
  29. package/src/types/lib/Cache.d.ts.map +1 -0
  30. package/src/types/lib/Collection.d.ts +246 -0
  31. package/src/types/lib/Collection.d.ts.map +1 -0
  32. package/src/types/lib/Contract.d.ts +72 -0
  33. package/src/types/lib/Contract.d.ts.map +1 -0
  34. package/src/types/lib/Data.d.ts +189 -0
  35. package/src/types/lib/Data.d.ts.map +1 -0
  36. package/src/types/lib/DirectoryObject.d.ts +148 -0
  37. package/src/types/lib/DirectoryObject.d.ts.map +1 -0
  38. package/src/types/lib/FS.d.ts +70 -0
  39. package/src/types/lib/FS.d.ts.map +1 -0
  40. package/src/types/lib/FileObject.d.ts +189 -0
  41. package/src/types/lib/FileObject.d.ts.map +1 -0
  42. package/src/types/lib/Glog.d.ts +113 -0
  43. package/src/types/lib/Glog.d.ts.map +1 -0
  44. package/src/types/lib/Logger.d.ts +46 -0
  45. package/src/types/lib/Logger.d.ts.map +1 -0
  46. package/src/types/lib/Sass.d.ts +62 -0
  47. package/src/types/lib/Sass.d.ts.map +1 -0
  48. package/src/types/lib/Schemer.d.ts +23 -0
  49. package/src/types/lib/Schemer.d.ts.map +1 -0
  50. package/src/types/lib/Tantrum.d.ts +50 -0
  51. package/src/types/lib/Tantrum.d.ts.map +1 -0
  52. package/src/types/lib/Term.d.ts +103 -0
  53. package/src/types/lib/Term.d.ts.map +1 -0
  54. package/src/types/lib/Terms.d.ts +24 -0
  55. package/src/types/lib/Terms.d.ts.map +1 -0
  56. package/src/types/lib/TypeSpec.d.ts +92 -0
  57. package/src/types/lib/TypeSpec.d.ts.map +1 -0
  58. package/src/types/lib/Util.d.ts +197 -0
  59. package/src/types/lib/Util.d.ts.map +1 -0
  60. package/src/types/lib/Valid.d.ts +33 -0
  61. package/src/types/lib/Valid.d.ts.map +1 -0
  62. package/src/lib/Action.js +0 -283
  63. package/src/lib/ActionBuilder.js +0 -144
  64. package/src/lib/ActionRunner.js +0 -79
  65. package/src/lib/Hooks.js +0 -194
  66. package/src/lib/Piper.js +0 -155
@@ -1,79 +0,0 @@
1
- import ActionBuilder, {ACTIVITY} from "./ActionBuilder.js"
2
- import Piper from "./Piper.js"
3
- import Sass from "./Sass.js"
4
- /**
5
- * Orchestrates execution of {@link ActionBuilder}-produced pipelines.
6
- *
7
- * Activities run in insertion order, with support for once-off work, repeated
8
- * loops, and nested parallel pipelines. Each activity receives a mutable
9
- * context object under `result.value` that can be replaced or enriched.
10
- */
11
- export default class ActionRunner {
12
- #action = null
13
- #build = null
14
- #hooks = null
15
-
16
- constructor({action, build}, hooks) {
17
- this.#action = action
18
- this.#build = build
19
- this.#hooks = hooks
20
- }
21
-
22
- /**
23
- * Executes the configured action pipeline.
24
- *
25
- * @param {unknown} content Seed value passed to the first activity.
26
- * @returns {Promise<unknown>} Final value produced by the pipeline, or null when a parallel stage reports failures.
27
- * @throws {Sass} When no activities are registered or required parallel builders are missing.
28
- */
29
- async run(content) {
30
- const activities = this.#build.activities
31
-
32
- if(!activities.size)
33
- throw Sass.new("No activities defined in action.")
34
-
35
- const result = content
36
- const action = this.#action
37
-
38
- for(const [name,activity] of activities) {
39
- const {op} = activity
40
-
41
- if(activity.kind === ACTIVITY.ONCE) {
42
- this.#hooks && await this.#hooks.callHook("before", name, result)
43
-
44
- if(!await op.call(action, result))
45
- break
46
-
47
- this.#hooks && await this.#hooks.callHook("after", name, result)
48
- } else if(activity.kind == ACTIVITY.MANY) {
49
- for(;;) {
50
-
51
- this.#hooks && await this.#hooks.callHook("before", name, result)
52
-
53
- if(!await op.call(action, result))
54
- break
55
-
56
- this.#hooks && await this.#hooks.callHook("after", name, result)
57
- }
58
- } else if(activity.kind === ACTIVITY.PARALLEL) {
59
- if(op === undefined)
60
- throw Sass.new("Missing action builder. Did you return the builder?")
61
-
62
- if(!op)
63
- throw Sass.new("Okay, cheeky monkey, you need to return the builder for this to work.")
64
-
65
- const builder = op.build()
66
- const piper = new Piper()
67
- .addStep(item => {
68
- const runner = new ActionRunner(builder, this.#hooks)
69
-
70
- return runner.run(item)
71
- }, {name: `Process Parallel ActionRunner Activity`,})
72
-
73
- await piper.pipe(result.value)
74
- }
75
- }
76
-
77
- return result
78
- }
79
- }
package/src/lib/Hooks.js DELETED
@@ -1,194 +0,0 @@
1
- import {setTimeout as timeout} from "timers/promises"
2
-
3
- import FileObject from "./FileObject.js"
4
- import Sass from "./Sass.js"
5
- import Util from "./Util.js"
6
- import Valid from "./Valid.js"
7
-
8
- /**
9
- * Generic base class for managing hooks with configurable event types.
10
- * Provides common functionality for hook registration, execution, and lifecycle management.
11
- * Designed to be extended by specific implementations.
12
- */
13
- export default class Hooks {
14
- #hooksFile = null
15
- #hooks = null
16
- #actionKind = null
17
- #timeout = 1000 // Default 1 second timeout
18
- #debug = null
19
-
20
- /**
21
- * Creates a new BaseHookManager instance.
22
- *
23
- * @param {object} config - Configuration object
24
- * @param {string} config.actionKind - Action identifier
25
- * @param {FileObject} config.hooksFile - File object containing hooks with uri property
26
- * @param {number} [config.hookTimeout] - Hook execution timeout in milliseconds
27
- * @param {unknown} [config.hooks] - The hooks object
28
- * @param {import('../types.js').DebugFunction} debug - Debug function from Glog.
29
- */
30
- constructor({actionKind, hooksFile, hooks, hookTimeout = 1000}, debug) {
31
- this.#actionKind = actionKind
32
- this.#hooksFile = hooksFile
33
- this.#hooks = hooks
34
- this.#timeout = hookTimeout
35
- this.#debug = debug
36
- }
37
-
38
- /**
39
- * Gets the action identifier.
40
- *
41
- * @returns {string} Action identifier or instance
42
- */
43
- get actionKind() {
44
- return this.#actionKind
45
- }
46
-
47
- /**
48
- * Gets the hooks file object.
49
- *
50
- * @returns {FileObject} File object containing hooks
51
- */
52
- get hooksFile() {
53
- return this.#hooksFile
54
- }
55
-
56
- /**
57
- * Gets the loaded hooks object.
58
- *
59
- * @returns {object|null} Hooks object or null if not loaded
60
- */
61
- get hooks() {
62
- return this.#hooks
63
- }
64
-
65
- /**
66
- * Gets the hook execution timeout in milliseconds.
67
- *
68
- * @returns {number} Timeout in milliseconds
69
- */
70
- get timeout() {
71
- return this.#timeout
72
- }
73
-
74
- /**
75
- * Gets the setup hook function if available.
76
- *
77
- * @returns {(args: object) => unknown|null} Setup hook function or null
78
- */
79
- get setup() {
80
- return this.hooks?.setup || null
81
- }
82
-
83
- /**
84
- * Gets the cleanup hook function if available.
85
- *
86
- * @returns {(args: object) => unknown|null} Cleanup hook function or null
87
- */
88
- get cleanup() {
89
- return this.hooks?.cleanup || null
90
- }
91
-
92
- /**
93
- * Static factory method to create and initialize a hook manager.
94
- * Loads hooks from the specified file and returns an initialized instance.
95
- * Override loadHooks() in subclasses to customize hook loading logic.
96
- *
97
- * @param {object} config - Same configuration object as constructor
98
- * @param {string|object} config.actionKind - Action identifier or instance
99
- * @param {FileObject} config.hooksFile - File object containing hooks with uri property
100
- * @param {number} [config.timeOut] - Hook execution timeout in milliseconds
101
- * @param {import('../types.js').DebugFunction} debug - The debug function.
102
- * @returns {Promise<Hooks|null>} Initialized hook manager or null if no hooks found
103
- */
104
- static async new(config, debug) {
105
- debug("Creating new HookManager instance with args: %o", 2, config)
106
-
107
- const instance = new this(config, debug)
108
- const hooksFile = instance.hooksFile
109
-
110
- debug("Loading hooks from %o", 2, hooksFile.uri)
111
-
112
- debug("Checking hooks file exists: %o", 2, hooksFile.uri)
113
- if(!await hooksFile.exists)
114
- throw Sass.new(`No such hooks file, ${hooksFile.uri}`)
115
-
116
- try {
117
- const hooksImport = await hooksFile.import()
118
-
119
- if(!hooksImport)
120
- return null
121
-
122
- debug("Hooks file imported successfully as a module", 2)
123
-
124
- const actionKind = instance.actionKind
125
- if(!hooksImport[actionKind])
126
- return null
127
-
128
- const hooks = new hooksImport[actionKind]({debug})
129
-
130
- debug(hooks.constructor.name, 4)
131
-
132
- // Attach common properties to hooks
133
- instance.#hooks = hooks
134
-
135
- debug("Hooks %o loaded successfully for %o", 2, hooksFile.uri, instance.actionKind)
136
-
137
- return instance
138
- } catch(error) {
139
- debug("Failed to load hooks %o: %o", 1, hooksFile.uri, error.message)
140
-
141
- return null
142
- }
143
- }
144
-
145
- async callHook(kind, activityName, context) {
146
- try {
147
- const debug = this.#debug
148
- const hooks = this.#hooks
149
-
150
- if(!hooks)
151
- return
152
-
153
- const hookName = `${kind}$${activityName}`
154
-
155
- debug("Looking for hook: %o", 4, hookName)
156
-
157
- const hook = hooks[hookName]
158
- if(!hook)
159
- return
160
-
161
- debug("Triggering hook: %o", 4, hookName)
162
- Valid.type(hook, "Function", `Hook "${hookName}" is not a function`)
163
-
164
- const hookFunction = async() => {
165
- debug("Hook function starting execution: %o", 4, hookName)
166
-
167
- const duration = (await Util.time(() => hook.call(this.#hooks, context))).cost
168
-
169
- debug("Hook function completed successfully: %o, after %oms", 4, hookName, duration)
170
- }
171
-
172
- const hookTimeout = this.timeout
173
- const expireAsync = (async() => {
174
- await timeout(hookTimeout)
175
- throw Sass.new(`Hook ${hookName} execution exceeded timeout of ${hookTimeout}ms`)
176
- })()
177
-
178
- try {
179
- debug("Starting Promise race for hook: %o", 4, hookName)
180
- await Util.race([
181
- hookFunction(),
182
- expireAsync
183
- ])
184
- } catch(error) {
185
- throw Sass.new(`Processing hook ${kind}$${activityName}`, error)
186
- }
187
-
188
- debug("We made it throoough the wildernessss", 4)
189
-
190
- } catch(error) {
191
- throw Sass.new(`Processing hook ${kind}$${activityName}`, error)
192
- }
193
- }
194
- }
package/src/lib/Piper.js DELETED
@@ -1,155 +0,0 @@
1
- /**
2
- * Generic Pipeline - Process items through a series of steps with concurrency control
3
- *
4
- * This abstraction handles:
5
- * - Concurrent processing with configurable limits
6
- * - Pipeline of processing steps
7
- * - Result categorization (success/warning/error)
8
- * - Setup/cleanup lifecycle hooks
9
- * - Error handling and reporting
10
- */
11
-
12
- import Glog from "./Glog.js"
13
- import Sass from "./Sass.js"
14
- import Tantrum from "./Tantrum.js"
15
- import Util from "./Util.js"
16
-
17
- export default class Piper {
18
- #debug
19
-
20
- #lifeCycle = new Map([
21
- ["setup", new Set()],
22
- ["process", new Set()],
23
- ["teardown", new Set()]
24
- ])
25
-
26
- constructor(arg) {
27
- this.#debug = arg?.debug ?? new Glog().newDebug("[PIPER]")
28
- }
29
-
30
- /**
31
- * Add a processing step to the pipeline
32
- *
33
- * @param {(context: object) => Promise<object>} fn - Function that processes an item: (context) => Promise<result>
34
- * @param {object} options - Step options (name, required, etc.)
35
- * @returns {Piper} The pipeline instance (for chaining)
36
- */
37
- addStep(fn, options = {}) {
38
- this.#lifeCycle.get("process").add({
39
- fn,
40
- name: options.name || `Step ${this.#lifeCycle.get("process").size + 1}`,
41
- required: !!options.required, // Default to required
42
- ...options
43
- })
44
-
45
- return this
46
- }
47
-
48
- /**
49
- * Add setup hook that runs before processing starts
50
- *
51
- * @param {() => Promise<void>} fn - Setup function: () => Promise<void>
52
- * @returns {Piper} The pipeline instance (for chaining)
53
- */
54
- addSetup(fn) {
55
- this.#lifeCycle.get("setup").add(fn)
56
-
57
- return this
58
- }
59
-
60
- /**
61
- * Add cleanup hook that runs after processing completes
62
- *
63
- * @param {() => Promise<void>} fn - Cleanup function: () => Promise<void>
64
- * @returns {Piper} The pipeline instance (for chaining)
65
- */
66
- addCleanup(fn) {
67
- this.#lifeCycle.get("teardown").add(fn)
68
-
69
- return this
70
- }
71
-
72
- /**
73
- * Process items through the pipeline with concurrency control
74
- *
75
- * @param {Array} items - Items to process
76
- * @param {number} maxConcurrent - Maximum concurrent items to process
77
- * @returns {Promise<object>} - Results object with succeeded, warned, errored arrays
78
- */
79
- async pipe(items, maxConcurrent = 10) {
80
- items.forEach(item => item.pipeStamp = Symbol(performance.now()))
81
-
82
- let itemIndex = 0
83
- const allResults = []
84
-
85
- const processWorker = async() => {
86
- while(true) {
87
- const currentIndex = itemIndex++
88
- if(currentIndex >= items.length)
89
- break
90
-
91
- const item = items[currentIndex]
92
- try {
93
- const result = await this.#processItem(item)
94
- allResults.push(result)
95
- } catch(error) {
96
- throw Sass.new("Processing pipeline item.", error)
97
- }
98
- }
99
- }
100
-
101
- const setupResult = await Util.settleAll([...this.#lifeCycle.get("setup")].map(e => e()))
102
- this.#processResult("Setting up the pipeline.", setupResult)
103
-
104
- // Start workers up to maxConcurrent limit
105
- const workers = []
106
- const workerCount = Math.min(maxConcurrent, items.length)
107
-
108
- for(let i = 0; i < workerCount; i++)
109
- workers.push(processWorker())
110
-
111
- // Wait for all workers to complete
112
- const processResult = await Util.settleAll(workers)
113
- this.#processResult("Processing pipeline.", processResult)
114
-
115
- // Run cleanup hooks
116
- const teardownResult = await Util.settleAll([...this.#lifeCycle.get("teardown")].map(e => e()))
117
- this.#processResult("Tearing down the pipeline.", teardownResult)
118
-
119
- return allResults
120
- }
121
-
122
- #processResult(message, settled) {
123
- if(settled.some(r => r.status === "rejected"))
124
- throw Tantrum.new(
125
- message,
126
- settled.filter(r => r.status==="rejected").map(r => r.reason)
127
- )
128
- }
129
-
130
- /**
131
- * Process a single item through all pipeline steps
132
- *
133
- * @param {object} item - The item to process
134
- * @returns {Promise<object>} Result object with status and data
135
- * @private
136
- */
137
- async #processItem(item) {
138
- const debug = this.#debug
139
-
140
- try {
141
- // Execute each step in sequence
142
- let result = item
143
-
144
- for(const step of this.#lifeCycle.get("process")) {
145
- debug("Executing step: %o", 2, step.name)
146
-
147
- result = await step.fn(result) ?? result
148
- }
149
-
150
- return result
151
- } catch(error) {
152
- throw Sass.new("Processing an item.", error)
153
- }
154
- }
155
- }