@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.
Files changed (42) hide show
  1. package/README.md +1 -1
  2. package/examples/hook-chains/node_modules/.bin/jiti +2 -2
  3. package/examples/hook-chains/node_modules/.bin/msw +4 -4
  4. package/examples/hook-chains/node_modules/.bin/terser +2 -2
  5. package/examples/hook-chains/node_modules/.bin/tsc +2 -2
  6. package/examples/hook-chains/node_modules/.bin/tsserver +2 -2
  7. package/examples/hook-chains/node_modules/.bin/tsx +2 -2
  8. package/examples/hook-chains/node_modules/.bin/validate-hooks +2 -2
  9. package/examples/hook-chains/node_modules/.bin/vite +4 -4
  10. package/examples/hook-chains/node_modules/.bin/vitest +2 -2
  11. package/examples/hook-chains/node_modules/.bin/yaml +4 -4
  12. package/examples/hook-chains/package.json +3 -3
  13. package/examples/hooks-marketplace.mjs +10 -10
  14. package/examples/knowledge-hook-manager-usage.mjs +1 -1
  15. package/examples/policy-hooks/node_modules/.bin/jiti +2 -2
  16. package/examples/policy-hooks/node_modules/.bin/msw +4 -4
  17. package/examples/policy-hooks/node_modules/.bin/terser +2 -2
  18. package/examples/policy-hooks/node_modules/.bin/tsc +2 -2
  19. package/examples/policy-hooks/node_modules/.bin/tsserver +2 -2
  20. package/examples/policy-hooks/node_modules/.bin/tsx +2 -2
  21. package/examples/policy-hooks/node_modules/.bin/validate-hooks +2 -2
  22. package/examples/policy-hooks/node_modules/.bin/vite +4 -4
  23. package/examples/policy-hooks/node_modules/.bin/vitest +2 -2
  24. package/examples/policy-hooks/node_modules/.bin/yaml +4 -4
  25. package/examples/policy-hooks/package.json +3 -3
  26. package/examples/production-policy-chain.mjs +54 -0
  27. package/examples/semantic-inference-hook.mjs +66 -0
  28. package/examples/validate-hooks.mjs +1 -1
  29. package/package.json +10 -5
  30. package/src/define.mjs +3 -0
  31. package/src/hooks/condition-evaluator.mjs +2 -0
  32. package/src/hooks/dependency-graph.mjs +388 -0
  33. package/src/hooks/observability.mjs +67 -74
  34. package/src/hooks/parallel-executor.mjs +295 -0
  35. package/src/hooks/policy-pack.mjs +2 -2
  36. package/src/hooks/schemas.mjs +1 -0
  37. package/src/hooks/wasm4pm-conformance-bridge.mjs +25 -0
  38. package/src/hooks/worker-pool.mjs +478 -0
  39. package/examples/hook-chains/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
  40. package/examples/hook-chains/unrdf-hooks-example-chains-5.0.0.tgz +0 -0
  41. package/examples/policy-hooks/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
  42. 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 || '1.0.0';
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 || '1.0.0',
550
+ version: options.version || '[VERSION]',
551
551
  description: options.description,
552
552
  author: options.author,
553
553
  license: options.license || 'MIT',
@@ -61,6 +61,7 @@ export const HookConditionSchema = z.object({
61
61
  'window',
62
62
  'n3',
63
63
  'datalog',
64
+ 'semantic-inference'
64
65
  ]),
65
66
  ref: HookConditionRefSchema.optional(),
66
67
  query: z.string().optional(),
@@ -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
+ }