@unrdf/hooks 26.4.7 → 26.5.5
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 +1 -1
- package/examples/hook-chains/node_modules/.bin/jiti +2 -2
- package/examples/hook-chains/node_modules/.bin/msw +4 -4
- package/examples/hook-chains/node_modules/.bin/terser +2 -2
- package/examples/hook-chains/node_modules/.bin/tsc +2 -2
- package/examples/hook-chains/node_modules/.bin/tsserver +2 -2
- package/examples/hook-chains/node_modules/.bin/tsx +2 -2
- package/examples/hook-chains/node_modules/.bin/validate-hooks +2 -2
- package/examples/hook-chains/node_modules/.bin/vite +4 -4
- package/examples/hook-chains/node_modules/.bin/vitest +2 -2
- package/examples/hook-chains/node_modules/.bin/yaml +4 -4
- package/examples/hook-chains/package.json +3 -3
- package/examples/hooks-marketplace.mjs +10 -10
- package/examples/knowledge-hook-manager-usage.mjs +1 -1
- package/examples/policy-hooks/node_modules/.bin/jiti +2 -2
- package/examples/policy-hooks/node_modules/.bin/msw +4 -4
- package/examples/policy-hooks/node_modules/.bin/terser +2 -2
- package/examples/policy-hooks/node_modules/.bin/tsc +2 -2
- package/examples/policy-hooks/node_modules/.bin/tsserver +2 -2
- package/examples/policy-hooks/node_modules/.bin/tsx +2 -2
- package/examples/policy-hooks/node_modules/.bin/validate-hooks +2 -2
- package/examples/policy-hooks/node_modules/.bin/vite +4 -4
- package/examples/policy-hooks/node_modules/.bin/vitest +2 -2
- package/examples/policy-hooks/node_modules/.bin/yaml +4 -4
- package/examples/policy-hooks/package.json +3 -3
- package/examples/production-policy-chain.mjs +54 -0
- package/examples/semantic-inference-hook.mjs +66 -0
- package/examples/validate-hooks.mjs +1 -1
- package/package.json +10 -5
- package/src/define.mjs +3 -0
- package/src/hooks/condition-evaluator.mjs +2 -0
- package/src/hooks/dependency-graph.mjs +388 -0
- package/src/hooks/observability.mjs +67 -74
- package/src/hooks/parallel-executor.mjs +295 -0
- package/src/hooks/policy-pack.mjs +2 -2
- package/src/hooks/schemas.mjs +1 -0
- package/src/hooks/wasm4pm-conformance-bridge.mjs +25 -0
- package/src/hooks/worker-pool.mjs +478 -0
- package/examples/hook-chains/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
- package/examples/hook-chains/unrdf-hooks-example-chains-5.0.0.tgz +0 -0
- package/examples/policy-hooks/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
- package/examples/policy-hooks/unrdf-hooks-example-policy-5.0.0.tgz +0 -0
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Parallel Hook Executor - Execute hooks in parallel using dependency graph and worker pool
|
|
3
|
+
* @module hooks/parallel-executor
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { DependencyGraph, buildExecutionLayers } from './dependency-graph.mjs';
|
|
7
|
+
import { WorkerPool, createWorkerPool } from './worker-pool.mjs';
|
|
8
|
+
import { z } from 'zod';
|
|
9
|
+
|
|
10
|
+
/* ========================================================================= */
|
|
11
|
+
/* Zod Schemas */
|
|
12
|
+
/* ========================================================================= */
|
|
13
|
+
|
|
14
|
+
export const ParallelExecutorConfigSchema = z.object({
|
|
15
|
+
maxWorkers: z.number().int().positive().optional().default(4),
|
|
16
|
+
taskTimeout: z.number().int().positive().optional().default(30000),
|
|
17
|
+
enableCycleDetection: z.boolean().optional().default(true),
|
|
18
|
+
fallbackToSequential: z.boolean().optional().default(true),
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
export const HookExecutionTaskSchema = z.object({
|
|
22
|
+
hook: z.object({
|
|
23
|
+
id: z.string(),
|
|
24
|
+
name: z.string().optional(),
|
|
25
|
+
run: z.function(),
|
|
26
|
+
condition: z.any().optional(),
|
|
27
|
+
dependsOn: z.array(z.string()).optional(),
|
|
28
|
+
}),
|
|
29
|
+
store: z.any(),
|
|
30
|
+
delta: z.object({
|
|
31
|
+
adds: z.array(z.any()).optional(),
|
|
32
|
+
deletes: z.array(z.any()).optional(),
|
|
33
|
+
}),
|
|
34
|
+
options: z.object({
|
|
35
|
+
env: z.any().optional(),
|
|
36
|
+
debug: z.boolean().optional(),
|
|
37
|
+
}).optional(),
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
/* ========================================================================= */
|
|
41
|
+
/* Parallel Executor Class */
|
|
42
|
+
/* ========================================================================= */
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Parallel hook executor using dependency graph and worker pool.
|
|
46
|
+
*
|
|
47
|
+
* @class ParallelHookExecutor
|
|
48
|
+
*/
|
|
49
|
+
export class ParallelHookExecutor {
|
|
50
|
+
/**
|
|
51
|
+
* Create a new parallel hook executor.
|
|
52
|
+
*
|
|
53
|
+
* @param {Object} [config={}] - Executor configuration
|
|
54
|
+
* @param {number} [config.maxWorkers=4] - Maximum worker threads
|
|
55
|
+
* @param {number} [config.taskTimeout=30000] - Task timeout in ms
|
|
56
|
+
* @param {boolean} [config.enableCycleDetection=true] - Enable cycle detection
|
|
57
|
+
* @param {boolean} [config.fallbackToSequential=true] - Fallback to sequential if cycle detected
|
|
58
|
+
*/
|
|
59
|
+
constructor(config = {}) {
|
|
60
|
+
const validatedConfig = ParallelExecutorConfigSchema.parse(config);
|
|
61
|
+
|
|
62
|
+
this.maxWorkers = validatedConfig.maxWorkers;
|
|
63
|
+
this.taskTimeout = validatedConfig.taskTimeout;
|
|
64
|
+
this.enableCycleDetection = validatedConfig.enableCycleDetection;
|
|
65
|
+
this.fallbackToSequential = validatedConfig.fallbackToSequential;
|
|
66
|
+
|
|
67
|
+
// Create worker pool
|
|
68
|
+
this.workerPool = createWorkerPool({
|
|
69
|
+
minWorkers: 1,
|
|
70
|
+
maxWorkers: this.maxWorkers,
|
|
71
|
+
taskTimeout: this.taskTimeout,
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
/** @type {DependencyGraph} - Dependency graph for hooks */
|
|
75
|
+
this.dependencyGraph = new DependencyGraph();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Register hooks for execution.
|
|
80
|
+
*
|
|
81
|
+
* @param {Array<{id: string, name?: string, run: Function, condition?: any, dependsOn?: string[]}>} hooks
|
|
82
|
+
* - Hook definitions
|
|
83
|
+
*/
|
|
84
|
+
registerHooks(hooks) {
|
|
85
|
+
// Clear existing graph
|
|
86
|
+
this.dependencyGraph.clear();
|
|
87
|
+
|
|
88
|
+
// Add hooks to dependency graph
|
|
89
|
+
for (const hook of hooks) {
|
|
90
|
+
this.dependencyGraph.addNode(hook.id, hook.dependsOn || []);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Execute hooks in parallel based on dependency graph.
|
|
96
|
+
*
|
|
97
|
+
* @param {Array<{id: string, name?: string, run: Function, condition?: any, dependsOn?: string[]}>} hooks
|
|
98
|
+
* - Hooks to execute
|
|
99
|
+
* @param {any} store - N3 Store instance
|
|
100
|
+
* @param {{adds?: any[], deletes?: any[]}} delta - Proposed changes
|
|
101
|
+
* @param {Object} [options={}] - Execution options
|
|
102
|
+
* @returns {Promise<Array<{hookId: string, success: boolean, result?: any, error?: string}>>}
|
|
103
|
+
*/
|
|
104
|
+
async execute(hooks, store, delta, options = {}) {
|
|
105
|
+
// Build dependency graph
|
|
106
|
+
this.registerHooks(hooks);
|
|
107
|
+
|
|
108
|
+
// Build execution layers
|
|
109
|
+
const { layers, hasCycle, cycle } = this.dependencyGraph.topologicalSort();
|
|
110
|
+
|
|
111
|
+
if (hasCycle) {
|
|
112
|
+
if (this.fallbackToSequential) {
|
|
113
|
+
console.warn('Cycle detected in hook dependencies, falling back to sequential execution:', cycle);
|
|
114
|
+
return this._executeSequential(hooks, store, delta, options);
|
|
115
|
+
} else {
|
|
116
|
+
throw new Error('Cycle detected in hook dependencies: ' + cycle.join(' -> '));
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Execute each layer in parallel
|
|
121
|
+
/** @type {Array<{hookId: string, success: boolean, result?: any, error?: string}>} */
|
|
122
|
+
const results = [];
|
|
123
|
+
|
|
124
|
+
for (const layer of layers) {
|
|
125
|
+
const layerResults = await this._executeLayer(hooks, layer, store, delta, options);
|
|
126
|
+
results.push(...layerResults);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return results;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Execute a single layer of hooks in parallel.
|
|
134
|
+
*
|
|
135
|
+
* @private
|
|
136
|
+
* @param {Array} hooks - All registered hooks
|
|
137
|
+
* @param {string[]} layer - Hook IDs in this layer
|
|
138
|
+
* @param {any} store - N3 Store instance
|
|
139
|
+
* @param {{adds?: any[], deletes?: any[]}} delta - Proposed changes
|
|
140
|
+
* @param {Object} options - Execution options
|
|
141
|
+
* @returns {Promise<Array<{hookId: string, success: boolean, result?: any, error?: string}>>}
|
|
142
|
+
*/
|
|
143
|
+
async _executeLayer(hooks, layer, store, delta, options) {
|
|
144
|
+
// Create lookup map for hooks
|
|
145
|
+
const hookMap = new Map(hooks.map(h => [h.id, h]));
|
|
146
|
+
|
|
147
|
+
// Create tasks for worker pool
|
|
148
|
+
const tasks = layer.map(hookId => ({
|
|
149
|
+
taskFn: async (hook, store, delta, opts) => {
|
|
150
|
+
try {
|
|
151
|
+
const event = {
|
|
152
|
+
store,
|
|
153
|
+
delta,
|
|
154
|
+
...opts,
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
const result = await hook.run(event, opts);
|
|
158
|
+
|
|
159
|
+
return {
|
|
160
|
+
hookId: hook.id,
|
|
161
|
+
success: true,
|
|
162
|
+
result,
|
|
163
|
+
};
|
|
164
|
+
} catch (error) {
|
|
165
|
+
return {
|
|
166
|
+
hookId: hook.id,
|
|
167
|
+
success: false,
|
|
168
|
+
error: error.message,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
args: [hookMap.get(hookId), store, delta, options],
|
|
173
|
+
}));
|
|
174
|
+
|
|
175
|
+
// Execute layer in parallel using worker pool
|
|
176
|
+
const poolResults = await this.workerPool.executeBatch(tasks);
|
|
177
|
+
|
|
178
|
+
// Unwrap worker pool results (handle both worker and direct execution formats)
|
|
179
|
+
const layerResults = poolResults.map(poolResult => {
|
|
180
|
+
if (poolResult.workerId === 'direct') {
|
|
181
|
+
// Direct execution fallback - result is already unwrapped
|
|
182
|
+
return poolResult.result;
|
|
183
|
+
} else {
|
|
184
|
+
// Worker execution - unwrap from { success, result } format
|
|
185
|
+
return poolResult.result;
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
return layerResults;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Execute hooks sequentially (fallback for cycles).
|
|
194
|
+
*
|
|
195
|
+
* @private
|
|
196
|
+
* @param {Array} hooks - Hooks to execute
|
|
197
|
+
* @param {any} store - N3 Store instance
|
|
198
|
+
* @param {{adds?: any[], deletes?: any[]}} delta - Proposed changes
|
|
199
|
+
* @param {Object} options - Execution options
|
|
200
|
+
* @returns {Promise<Array<{hookId: string, success: boolean, result?: any, error?: string}>>}
|
|
201
|
+
*/
|
|
202
|
+
async _executeSequential(hooks, store, delta, options) {
|
|
203
|
+
/** @type {Array<{hookId: string, success: boolean, result?: any, error?: string}>} */
|
|
204
|
+
const results = [];
|
|
205
|
+
|
|
206
|
+
for (const hook of hooks) {
|
|
207
|
+
try {
|
|
208
|
+
const event = {
|
|
209
|
+
store,
|
|
210
|
+
delta,
|
|
211
|
+
...options,
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
const result = await hook.run(event, options);
|
|
215
|
+
|
|
216
|
+
results.push({
|
|
217
|
+
hookId: hook.id,
|
|
218
|
+
success: true,
|
|
219
|
+
result,
|
|
220
|
+
});
|
|
221
|
+
} catch (error) {
|
|
222
|
+
results.push({
|
|
223
|
+
hookId: hook.id,
|
|
224
|
+
success: false,
|
|
225
|
+
error: error.message,
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
// Stop on first error in sequential mode
|
|
229
|
+
break;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return results;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Get execution statistics.
|
|
238
|
+
*
|
|
239
|
+
* @returns {{graphStats: object, poolStats: object}} - Statistics from graph and worker pool
|
|
240
|
+
*/
|
|
241
|
+
getStats() {
|
|
242
|
+
return {
|
|
243
|
+
graphStats: this.dependencyGraph.getStats(),
|
|
244
|
+
poolStats: this.workerPool.getStats(),
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Shutdown the executor and clean up resources.
|
|
250
|
+
*
|
|
251
|
+
* @returns {Promise<void>}
|
|
252
|
+
*/
|
|
253
|
+
async shutdown() {
|
|
254
|
+
await this.workerPool.shutdown();
|
|
255
|
+
this.dependencyGraph.clear();
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/* ========================================================================= */
|
|
260
|
+
/* Factory Functions */
|
|
261
|
+
/* ========================================================================= */
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Create a parallel hook executor.
|
|
265
|
+
*
|
|
266
|
+
* @param {Object} [config={}] - Executor configuration
|
|
267
|
+
* @returns {ParallelHookExecutor} - New executor instance
|
|
268
|
+
*/
|
|
269
|
+
export function createParallelExecutor(config = {}) {
|
|
270
|
+
return new ParallelHookExecutor(config);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/* ========================================================================= */
|
|
274
|
+
/* Convenience Functions */
|
|
275
|
+
/* ========================================================================= */
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Execute hooks in parallel (convenience function).
|
|
279
|
+
*
|
|
280
|
+
* @param {Array<{id: string, name?: string, run: Function, condition?: any, dependsOn?: string[]}>} hooks
|
|
281
|
+
* - Hooks to execute
|
|
282
|
+
* @param {any} store - N3 Store instance
|
|
283
|
+
* @param {{adds?: any[], deletes?: any[]}} delta - Proposed changes
|
|
284
|
+
* @param {Object} [options={}] - Execution options
|
|
285
|
+
* @param {Object} [executorConfig={}] - Executor configuration
|
|
286
|
+
* @returns {Promise<Array<{hookId: string, success: boolean, result?: any, error?: string}>>}
|
|
287
|
+
*/
|
|
288
|
+
export async function executeHooksParallel(hooks, store, delta, options = {}, executorConfig = {}) {
|
|
289
|
+
const executor = createParallelExecutor(executorConfig);
|
|
290
|
+
try {
|
|
291
|
+
return await executor.execute(hooks, store, delta, options);
|
|
292
|
+
} finally {
|
|
293
|
+
await executor.shutdown();
|
|
294
|
+
}
|
|
295
|
+
}
|
|
@@ -284,7 +284,7 @@ export class PolicyPack {
|
|
|
284
284
|
// Check version compatibility
|
|
285
285
|
if (this.manifest.config.conditions?.version) {
|
|
286
286
|
const requiredVersion = this.manifest.config.conditions.version;
|
|
287
|
-
const currentVersion = environment.version || '
|
|
287
|
+
const currentVersion = environment.version || '[VERSION]';
|
|
288
288
|
|
|
289
289
|
if (!this._isVersionCompatible(currentVersion, requiredVersion)) {
|
|
290
290
|
result.compatible = false;
|
|
@@ -547,7 +547,7 @@ export function createPolicyPackManifest(name, hooks, options = {}) {
|
|
|
547
547
|
id: randomUUID(),
|
|
548
548
|
meta: {
|
|
549
549
|
name: name,
|
|
550
|
-
version: options.version || '
|
|
550
|
+
version: options.version || '[VERSION]',
|
|
551
551
|
description: options.description,
|
|
552
552
|
author: options.author,
|
|
553
553
|
license: options.license || 'MIT',
|
package/src/hooks/schemas.mjs
CHANGED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { executeSemanticQuery } from '@unrdf/core/utils/semantic-bridge';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Bridges wasm4pm process conformance results to Open Ontologies.
|
|
5
|
+
*
|
|
6
|
+
* @param {Object} conformanceResult - Output from pictl/wasm4pm conformance check
|
|
7
|
+
* @returns {Object} Semantic event metadata
|
|
8
|
+
*/
|
|
9
|
+
export async function bridgeConformanceEvent(conformanceResult) {
|
|
10
|
+
// Translate wasm4pm deviation report to N-Triples for the Reasoner
|
|
11
|
+
const deviation = conformanceResult.deviations[0];
|
|
12
|
+
const rdfEvent = `
|
|
13
|
+
@prefix ex: <http://example.org/process#> .
|
|
14
|
+
ex:Event_${Date.now()} a ex:ConformanceViolation ;
|
|
15
|
+
ex:source "wasm4pm" ;
|
|
16
|
+
ex:activity "${deviation.activity}" ;
|
|
17
|
+
ex:reason "${deviation.reason}" .
|
|
18
|
+
`;
|
|
19
|
+
|
|
20
|
+
// Inject into OO engine for immediate autonomic evaluation
|
|
21
|
+
return await executeSemanticQuery(
|
|
22
|
+
'SELECT ?event WHERE { ?event a <http://example.org/process#ConformanceViolation> }',
|
|
23
|
+
{ rawTriples: [rdfEvent] }
|
|
24
|
+
);
|
|
25
|
+
}
|