@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.
- package/LICENSE +21 -0
- package/README.md +86 -0
- package/package.json +70 -0
- package/src/hooks/builtin-hooks.mjs +296 -0
- package/src/hooks/condition-cache.mjs +109 -0
- package/src/hooks/condition-evaluator.mjs +722 -0
- package/src/hooks/define-hook.mjs +211 -0
- package/src/hooks/effect-sandbox-worker.mjs +170 -0
- package/src/hooks/effect-sandbox.mjs +517 -0
- package/src/hooks/file-resolver.mjs +387 -0
- package/src/hooks/hook-chain-compiler.mjs +236 -0
- package/src/hooks/hook-executor-batching.mjs +277 -0
- package/src/hooks/hook-executor.mjs +465 -0
- package/src/hooks/hook-management.mjs +202 -0
- package/src/hooks/hook-scheduler.mjs +413 -0
- package/src/hooks/knowledge-hook-engine.mjs +358 -0
- package/src/hooks/knowledge-hook-manager.mjs +269 -0
- package/src/hooks/observability.mjs +531 -0
- package/src/hooks/policy-pack.mjs +572 -0
- package/src/hooks/quad-pool.mjs +249 -0
- package/src/hooks/quality-metrics.mjs +544 -0
- package/src/hooks/security/error-sanitizer.mjs +257 -0
- package/src/hooks/security/path-validator.mjs +194 -0
- package/src/hooks/security/sandbox-restrictions.mjs +331 -0
- package/src/hooks/telemetry.mjs +167 -0
- package/src/index.mjs +101 -0
- package/src/security/sandbox/browser-executor.mjs +220 -0
- package/src/security/sandbox/detector.mjs +342 -0
- package/src/security/sandbox/isolated-vm-executor.mjs +373 -0
- package/src/security/sandbox/vm2-executor.mjs +217 -0
- package/src/security/sandbox/worker-executor-runtime.mjs +74 -0
- package/src/security/sandbox/worker-executor.mjs +212 -0
- 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
|
+
}
|