@unrdf/hooks 5.0.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.
Files changed (33) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +86 -0
  3. package/package.json +70 -0
  4. package/src/hooks/builtin-hooks.mjs +296 -0
  5. package/src/hooks/condition-cache.mjs +109 -0
  6. package/src/hooks/condition-evaluator.mjs +722 -0
  7. package/src/hooks/define-hook.mjs +211 -0
  8. package/src/hooks/effect-sandbox-worker.mjs +170 -0
  9. package/src/hooks/effect-sandbox.mjs +517 -0
  10. package/src/hooks/file-resolver.mjs +387 -0
  11. package/src/hooks/hook-chain-compiler.mjs +236 -0
  12. package/src/hooks/hook-executor-batching.mjs +277 -0
  13. package/src/hooks/hook-executor.mjs +465 -0
  14. package/src/hooks/hook-management.mjs +202 -0
  15. package/src/hooks/hook-scheduler.mjs +413 -0
  16. package/src/hooks/knowledge-hook-engine.mjs +358 -0
  17. package/src/hooks/knowledge-hook-manager.mjs +269 -0
  18. package/src/hooks/observability.mjs +531 -0
  19. package/src/hooks/policy-pack.mjs +572 -0
  20. package/src/hooks/quad-pool.mjs +249 -0
  21. package/src/hooks/quality-metrics.mjs +544 -0
  22. package/src/hooks/security/error-sanitizer.mjs +257 -0
  23. package/src/hooks/security/path-validator.mjs +194 -0
  24. package/src/hooks/security/sandbox-restrictions.mjs +331 -0
  25. package/src/hooks/telemetry.mjs +167 -0
  26. package/src/index.mjs +101 -0
  27. package/src/security/sandbox/browser-executor.mjs +220 -0
  28. package/src/security/sandbox/detector.mjs +342 -0
  29. package/src/security/sandbox/isolated-vm-executor.mjs +373 -0
  30. package/src/security/sandbox/vm2-executor.mjs +217 -0
  31. package/src/security/sandbox/worker-executor-runtime.mjs +74 -0
  32. package/src/security/sandbox/worker-executor.mjs +212 -0
  33. package/src/security/sandbox-adapter.mjs +141 -0
@@ -0,0 +1,211 @@
1
+ /**
2
+ * @file Hook definition utilities for UNRDF Knowledge Hooks.
3
+ * @module hooks/define-hook
4
+ */
5
+
6
+ import { z } from 'zod';
7
+
8
+ /**
9
+ * @typedef {import('n3').Quad} Quad
10
+ */
11
+
12
+ /**
13
+ * Hook trigger types.
14
+ * @typedef {'before-add' | 'after-add' | 'before-query' | 'after-query' | 'before-remove' | 'after-remove'} HookTrigger
15
+ */
16
+
17
+ /**
18
+ * Validation function for hooks.
19
+ * @callback ValidateFn
20
+ * @param {Quad} quad - The quad to validate
21
+ * @returns {boolean} - True if validation passes, false otherwise
22
+ */
23
+
24
+ /**
25
+ * Transformation function for hooks.
26
+ * @callback TransformFn
27
+ * @param {Quad} quad - The quad to transform
28
+ * @returns {Quad} - The transformed quad
29
+ */
30
+
31
+ /**
32
+ * Hook definition configuration.
33
+ * @typedef {Object} HookConfig
34
+ * @property {string} name - Hook identifier
35
+ * @property {HookTrigger} trigger - When to execute the hook
36
+ * @property {ValidateFn} [validate] - Optional validation function
37
+ * @property {TransformFn} [transform] - Optional transformation function
38
+ * @property {Record<string, any>} [metadata] - Optional metadata
39
+ */
40
+
41
+ /**
42
+ * Defined hook with runtime state.
43
+ * @typedef {Object} Hook
44
+ * @property {string} name - Hook identifier
45
+ * @property {HookTrigger} trigger - When to execute the hook
46
+ * @property {ValidateFn} [validate] - Optional validation function
47
+ * @property {TransformFn} [transform] - Optional transformation function
48
+ * @property {Record<string, any>} [metadata] - Optional metadata
49
+ */
50
+
51
+ /* ========================================================================= */
52
+ /* Zod Schemas */
53
+ /* ========================================================================= */
54
+
55
+ export const HookTriggerSchema = z.enum([
56
+ // Core CRUD (6)
57
+ 'before-add',
58
+ 'after-add',
59
+ 'before-query',
60
+ 'after-query',
61
+ 'before-remove',
62
+ 'after-remove',
63
+ // Transaction Hooks (4)
64
+ 'before-commit',
65
+ 'after-commit',
66
+ 'before-rollback',
67
+ 'after-rollback',
68
+ // Error/Event Hooks (5)
69
+ 'on-error',
70
+ 'on-validation-fail',
71
+ 'on-transform',
72
+ 'on-timeout',
73
+ 'on-circuit-open',
74
+ // Async/IO Hooks (6)
75
+ 'before-fetch',
76
+ 'after-fetch',
77
+ 'before-sync',
78
+ 'after-sync',
79
+ 'before-import',
80
+ 'after-import',
81
+ // Cron/Time Hooks (4)
82
+ 'on-schedule',
83
+ 'on-interval',
84
+ 'on-idle',
85
+ 'on-startup',
86
+ // Lean Six Sigma Quality Hooks (8)
87
+ 'quality-gate',
88
+ 'defect-detection',
89
+ 'continuous-improvement',
90
+ 'spc-control',
91
+ 'capability-analysis',
92
+ 'root-cause',
93
+ 'kaizen-event',
94
+ 'audit-trail',
95
+ ]);
96
+
97
+ export const HookConfigSchema = z.object({
98
+ name: z.string().min(1, 'Hook name is required'),
99
+ trigger: HookTriggerSchema,
100
+ // Note: No return type enforcement - runtime POKA-YOKE guard handles non-boolean returns
101
+ validate: z.function().optional(),
102
+ transform: z.function().optional(),
103
+ metadata: z.record(z.string(), z.any()).optional(),
104
+ });
105
+
106
+ export const HookSchema = z.object({
107
+ name: z.string(),
108
+ trigger: HookTriggerSchema,
109
+ validate: z.function().optional(),
110
+ transform: z.function().optional(),
111
+ metadata: z.record(z.string(), z.any()).optional(),
112
+ });
113
+
114
+ /* ========================================================================= */
115
+ /* Public API */
116
+ /* ========================================================================= */
117
+
118
+ /**
119
+ * Define a validation or transformation hook.
120
+ *
121
+ * @param {HookConfig} config - Hook configuration
122
+ * @returns {Hook} - The defined hook
123
+ * @throws {z.ZodError} - If configuration is invalid
124
+ *
125
+ * @example
126
+ * const iriValidator = defineHook({
127
+ * name: 'validate-iri',
128
+ * trigger: 'before-add',
129
+ * validate: (quad) => {
130
+ * return quad.subject.termType === 'NamedNode';
131
+ * }
132
+ * });
133
+ */
134
+ export function defineHook(config) {
135
+ const validated = HookConfigSchema.parse(config);
136
+
137
+ if (!validated.validate && !validated.transform) {
138
+ throw new Error('Hook must define either validate or transform function');
139
+ }
140
+
141
+ return {
142
+ name: validated.name,
143
+ trigger: validated.trigger,
144
+ validate: validated.validate,
145
+ transform: validated.transform,
146
+ metadata: validated.metadata || {},
147
+ // Pre-computed flags for sub-1μs execution (skip Zod in hot path)
148
+ _hasValidation: typeof validated.validate === 'function',
149
+ _hasTransformation: typeof validated.transform === 'function',
150
+ _validated: true,
151
+ };
152
+ }
153
+
154
+ /**
155
+ * Validate a hook object.
156
+ *
157
+ * @param {any} hook - Hook to validate
158
+ * @returns {boolean} - True if valid, false otherwise
159
+ */
160
+ export function isValidHook(hook) {
161
+ try {
162
+ HookSchema.parse(hook);
163
+ return hook.validate !== undefined || hook.transform !== undefined;
164
+ } catch {
165
+ return false;
166
+ }
167
+ }
168
+
169
+ /**
170
+ * Get hook metadata.
171
+ *
172
+ * @param {Hook} hook - Hook instance
173
+ * @param {string} key - Metadata key
174
+ * @returns {any} - Metadata value or undefined
175
+ */
176
+ export function getHookMetadata(hook, key) {
177
+ const validated = HookSchema.parse(hook);
178
+ return validated.metadata?.[key];
179
+ }
180
+
181
+ /**
182
+ * Check if hook has validation function.
183
+ * Uses pre-computed flag for sub-1μs execution (no Zod overhead).
184
+ *
185
+ * @param {Hook} hook - Hook instance
186
+ * @returns {boolean} - True if hook has validate function
187
+ */
188
+ export function hasValidation(hook) {
189
+ // Fast path: use pre-computed flag (set by defineHook)
190
+ if (hook._validated) {
191
+ return hook._hasValidation;
192
+ }
193
+ // Fallback for non-defineHook'd objects
194
+ return typeof hook.validate === 'function';
195
+ }
196
+
197
+ /**
198
+ * Check if hook has transformation function.
199
+ * Uses pre-computed flag for sub-1μs execution (no Zod overhead).
200
+ *
201
+ * @param {Hook} hook - Hook instance
202
+ * @returns {boolean} - True if hook has transform function
203
+ */
204
+ export function hasTransformation(hook) {
205
+ // Fast path: use pre-computed flag (set by defineHook)
206
+ if (hook._validated) {
207
+ return hook._hasTransformation;
208
+ }
209
+ // Fallback for non-defineHook'd objects
210
+ return typeof hook.transform === 'function';
211
+ }
@@ -0,0 +1,170 @@
1
+ /**
2
+ * @file Worker thread implementation for effect sandbox
3
+ * @module effect-sandbox-worker
4
+ *
5
+ * @description
6
+ * Worker thread implementation for secure hook effect execution.
7
+ * This file runs in a separate worker thread to isolate hook execution.
8
+ */
9
+
10
+ import { parentPort, workerData, isMainThread } from 'worker_threads';
11
+
12
+ /**
13
+ * Worker thread entry point
14
+ */
15
+ if (!isMainThread) {
16
+ const { effect, context, executionId, config, _options } = workerData;
17
+
18
+ try {
19
+ // Create safe execution environment
20
+ const safeGlobals = createSafeGlobals(context, config);
21
+
22
+ // Create safe effect function
23
+ const safeEffect = createSafeEffect(effect, safeGlobals);
24
+
25
+ // Execute effect with timeout
26
+ const result = await executeWithTimeout(safeEffect, context, config.timeout);
27
+
28
+ // Send result back to main thread
29
+ parentPort.postMessage({
30
+ success: true,
31
+ result: result.result,
32
+ assertions: result.assertions || [],
33
+ events: result.events || [],
34
+ executionId,
35
+ });
36
+ } catch (error) {
37
+ // Send error back to main thread
38
+ parentPort.postMessage({
39
+ success: false,
40
+ error: error.message,
41
+ executionId,
42
+ });
43
+ }
44
+ }
45
+
46
+ /**
47
+ * Create safe globals for worker execution
48
+ * @param {Object} context - Execution context
49
+ * @param {Object} config - Sandbox configuration
50
+ * @returns {Object} Safe globals
51
+ */
52
+ function createSafeGlobals(context, config) {
53
+ const globals = {};
54
+
55
+ // Add context data
56
+ globals.event = context.event;
57
+ globals.store = context.store;
58
+ globals.delta = context.delta;
59
+ globals.metadata = context.metadata || {};
60
+
61
+ // Add safe console
62
+ globals.console = {
63
+ log: message => console.log(`[Worker] ${message}`),
64
+ warn: message => console.warn(`[Worker] ${message}`),
65
+ error: message => console.error(`[Worker] ${message}`),
66
+ info: message => console.info(`[Worker] ${message}`),
67
+ };
68
+
69
+ // Add safe functions
70
+ globals.emitEvent = eventData => {
71
+ if (!context.events) context.events = [];
72
+ context.events.push(eventData);
73
+ };
74
+
75
+ globals.log = (message, level = 'info') => {
76
+ console.log(`[Sandbox ${level.toUpperCase()}] ${message}`);
77
+ };
78
+
79
+ globals.assert = (subject, predicate, object, graph) => {
80
+ if (!context.assertions) context.assertions = [];
81
+ context.assertions.push({ subject, predicate, object, graph });
82
+ };
83
+
84
+ // Add allowed globals
85
+ for (const globalName of config.allowedGlobals || []) {
86
+ if (globalName === 'Date') globals.Date = Date;
87
+ if (globalName === 'Math') globals.Math = Math;
88
+ if (globalName === 'JSON') globals.JSON = JSON;
89
+ }
90
+
91
+ return globals;
92
+ }
93
+
94
+ /**
95
+ * Create safe effect function
96
+ * @param {string} effectCode - Effect function code
97
+ * @param {Object} safeGlobals - Safe globals
98
+ * @returns {Function} Safe effect function
99
+ */
100
+ function createSafeEffect(effectCode, safeGlobals) {
101
+ // Create function with safe globals
102
+ const safeFunction = new Function(
103
+ ...Object.keys(safeGlobals),
104
+ `
105
+ "use strict";
106
+
107
+ // Override dangerous globals
108
+ const process = undefined;
109
+ const require = undefined;
110
+ const module = undefined;
111
+ const exports = undefined;
112
+ const __dirname = undefined;
113
+ const __filename = undefined;
114
+
115
+ // Create effect function
116
+ const effect = ${effectCode};
117
+
118
+ // Return wrapper that captures assertions and events
119
+ return function(context) {
120
+ const result = effect(context);
121
+
122
+ return {
123
+ result,
124
+ assertions: context.assertions || [],
125
+ events: context.events || []
126
+ };
127
+ };
128
+ `
129
+ );
130
+
131
+ return safeFunction(...Object.values(safeGlobals));
132
+ }
133
+
134
+ /**
135
+ * Execute function with timeout
136
+ * @param {Function} fn - Function to execute
137
+ * @param {Object} context - Execution context
138
+ * @param {number} timeout - Timeout in milliseconds
139
+ * @returns {Promise<Object>} Execution result
140
+ */
141
+ async function executeWithTimeout(fn, context, timeout) {
142
+ return new Promise((resolve, reject) => {
143
+ const timeoutId = setTimeout(() => {
144
+ reject(new Error(`Execution timeout after ${timeout}ms`));
145
+ }, timeout);
146
+
147
+ try {
148
+ const result = fn(context);
149
+
150
+ // Handle both sync and async results
151
+ if (result && typeof result.then === 'function') {
152
+ result
153
+ .then(res => {
154
+ clearTimeout(timeoutId);
155
+ resolve(res);
156
+ })
157
+ .catch(err => {
158
+ clearTimeout(timeoutId);
159
+ reject(err);
160
+ });
161
+ } else {
162
+ clearTimeout(timeoutId);
163
+ resolve(result);
164
+ }
165
+ } catch (error) {
166
+ clearTimeout(timeoutId);
167
+ reject(error);
168
+ }
169
+ });
170
+ }