@unrdf/hooks 5.0.1 → 26.4.2

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 (40) hide show
  1. package/dist/index.d.mts +1738 -0
  2. package/dist/index.d.ts +1738 -0
  3. package/dist/index.mjs +1738 -0
  4. package/examples/basic.mjs +113 -0
  5. package/examples/hook-chains/README.md +263 -0
  6. package/examples/hook-chains/node_modules/.bin/validate-hooks +21 -0
  7. package/examples/hook-chains/node_modules/.bin/vitest +21 -0
  8. package/examples/hook-chains/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
  9. package/examples/hook-chains/package.json +25 -0
  10. package/examples/hook-chains/src/index.mjs +348 -0
  11. package/examples/hook-chains/test/example.test.mjs +252 -0
  12. package/examples/hook-chains/vitest.config.mjs +14 -0
  13. package/examples/knowledge-hook-manager-usage.mjs +65 -0
  14. package/examples/policy-hooks/README.md +193 -0
  15. package/examples/policy-hooks/node_modules/.bin/validate-hooks +21 -0
  16. package/examples/policy-hooks/node_modules/.bin/vitest +21 -0
  17. package/examples/policy-hooks/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
  18. package/examples/policy-hooks/package.json +25 -0
  19. package/examples/policy-hooks/src/index.mjs +275 -0
  20. package/examples/policy-hooks/test/example.test.mjs +204 -0
  21. package/examples/policy-hooks/vitest.config.mjs +14 -0
  22. package/examples/validate-hooks.mjs +154 -0
  23. package/package.json +29 -24
  24. package/src/hooks/builtin-hooks.mjs +72 -48
  25. package/src/hooks/condition-evaluator.mjs +1 -1
  26. package/src/hooks/define-hook.mjs +25 -9
  27. package/src/hooks/effect-sandbox-worker.mjs +1 -1
  28. package/src/hooks/effect-sandbox.mjs +5 -2
  29. package/src/hooks/file-resolver.mjs +2 -2
  30. package/src/hooks/hook-executor.mjs +12 -19
  31. package/src/hooks/policy-pack.mjs +3 -3
  32. package/src/hooks/query-optimizer.mjs +196 -0
  33. package/src/hooks/query.mjs +150 -0
  34. package/src/hooks/schemas.mjs +158 -0
  35. package/src/hooks/security/path-validator.mjs +1 -1
  36. package/src/hooks/security/sandbox-restrictions.mjs +2 -2
  37. package/src/hooks/store-cache.mjs +189 -0
  38. package/src/hooks/validate.mjs +133 -0
  39. package/src/index.mjs +62 -0
  40. package/src/policy-compiler.mjs +503 -0
@@ -0,0 +1,113 @@
1
+ /**
2
+ * @unrdf/hooks - Basic Example
3
+ *
4
+ * Demonstrates basic usage of the Knowledge Hooks package.
5
+ */
6
+
7
+ import { createStore, dataFactory } from '@unrdf/oxigraph';
8
+ import {
9
+ defineHook,
10
+ createHookRegistry,
11
+ registerHook,
12
+ executeHooksByTrigger,
13
+ builtinHooks,
14
+ } from '../src/index.mjs';
15
+
16
+ const { namedNode, literal, quad } = dataFactory;
17
+
18
+ console.log('=== @unrdf/hooks Basic Example ===\n');
19
+
20
+ // 1. Define a custom validation hook
21
+ const validateEmailProperty = defineHook({
22
+ name: 'validate-email-property',
23
+ trigger: 'before-add',
24
+ validate: (quad) => {
25
+ if (quad.predicate.value === 'http://schema.org/email') {
26
+ if (quad.object.termType !== 'Literal') {
27
+ return false;
28
+ }
29
+ const email = quad.object.value;
30
+ return email.includes('@') && email.includes('.');
31
+ }
32
+ return true;
33
+ },
34
+ metadata: {
35
+ description: 'Validates email format for schema:email properties',
36
+ },
37
+ });
38
+
39
+ console.log('1. Defined custom email validation hook');
40
+
41
+ // 2. Create registry and register hooks
42
+ const registry = createHookRegistry();
43
+ registerHook(registry, validateEmailProperty);
44
+ registerHook(registry, builtinHooks.validatePredicateIRI);
45
+
46
+ console.log('2. Registered hooks in registry\n');
47
+
48
+ // 3. Create test quads
49
+ const validQuad = quad(
50
+ namedNode('http://example.org/person1'),
51
+ namedNode('http://schema.org/email'),
52
+ literal('user@example.com')
53
+ );
54
+
55
+ const invalidQuad = quad(
56
+ namedNode('http://example.org/person2'),
57
+ namedNode('http://schema.org/email'),
58
+ literal('invalid-email')
59
+ );
60
+
61
+ // 4. Execute hooks on quads
62
+ console.log('3. Executing hooks on valid email quad:');
63
+ const result1 = executeHooksByTrigger(
64
+ [validateEmailProperty, builtinHooks.validatePredicateIRI],
65
+ 'before-add',
66
+ validQuad
67
+ );
68
+ console.log(` Valid: ${result1.valid}`);
69
+ console.log(` Hooks executed: ${result1.results.length}\n`);
70
+
71
+ console.log('4. Executing hooks on invalid email quad:');
72
+ const result2 = executeHooksByTrigger(
73
+ [validateEmailProperty, builtinHooks.validatePredicateIRI],
74
+ 'before-add',
75
+ invalidQuad
76
+ );
77
+ console.log(` Valid: ${result2.valid}`);
78
+ console.log(` Error: ${result2.error}\n`);
79
+
80
+ // 5. Demonstrate transformation hook
81
+ const normalizeEmail = defineHook({
82
+ name: 'normalize-email',
83
+ trigger: 'before-add',
84
+ transform: (quad) => {
85
+ if (quad.predicate.value === 'http://schema.org/email' &&
86
+ quad.object.termType === 'Literal') {
87
+ return dataFactory.quad(
88
+ quad.subject,
89
+ quad.predicate,
90
+ literal(quad.object.value.toLowerCase()),
91
+ quad.graph
92
+ );
93
+ }
94
+ return quad;
95
+ },
96
+ });
97
+
98
+ console.log('5. Applying transformation hook:');
99
+ const upperEmail = quad(
100
+ namedNode('http://example.org/person3'),
101
+ namedNode('http://schema.org/email'),
102
+ literal('USER@EXAMPLE.COM')
103
+ );
104
+
105
+ const result3 = executeHooksByTrigger(
106
+ [normalizeEmail],
107
+ 'before-add',
108
+ upperEmail
109
+ );
110
+ console.log(` Original: ${upperEmail.object.value}`);
111
+ console.log(` Transformed: ${result3.quad.object.value}\n`);
112
+
113
+ console.log('=== Example Complete ===');
@@ -0,0 +1,263 @@
1
+ # Hook Chains Example
2
+
3
+ This example demonstrates how to use **@unrdf/hooks** for creating sequential hook chains that validate and transform RDF quads through multiple stages.
4
+
5
+ ## Features
6
+
7
+ - **Sequential Execution**: Hooks execute in defined order
8
+ - **Progressive Transformation**: Quads transform through pipeline stages
9
+ - **Early Termination**: Chain stops on first validation failure
10
+ - **Result Aggregation**: Complete execution history and results
11
+ - **Composable Patterns**: Mix and match hooks for different pipelines
12
+
13
+ ## Installation
14
+
15
+ ```bash
16
+ pnpm install
17
+ ```
18
+
19
+ ## Usage
20
+
21
+ Run the example:
22
+
23
+ ```bash
24
+ pnpm start
25
+ ```
26
+
27
+ Run tests:
28
+
29
+ ```bash
30
+ pnpm test
31
+ ```
32
+
33
+ ## Example Output
34
+
35
+ ```
36
+ 🔗 Hook Chains Example
37
+
38
+ ============================================================
39
+
40
+ 🧹 Data Cleaning Chain
41
+ ────────────────────────────────────────────────────────────
42
+ Input: Alice Smith
43
+ " Alice Smith "
44
+ Status: ✅ SUCCESS
45
+ Steps: 3/3 passed
46
+ Output: Alice Smith
47
+ "Alice Smith"
48
+
49
+ Chain execution:
50
+ 1. ✅ 🔄 trim-literals
51
+ 2. ✅ 🔄 normalize-whitespace
52
+ 3. ✅ validate-literal-length
53
+
54
+ 🔍 Quality Assurance Chain
55
+ ────────────────────────────────────────────────────────────
56
+ Input: Literal with 1500 characters
57
+ Status: ❌ FAILED
58
+ Steps: 3/4 passed
59
+ Failed at: validate-literal-length
60
+ Reason: validation failed
61
+
62
+ Chain execution (early termination):
63
+ 1. ✅ standard-validation
64
+ 2. ✅ validate-iris
65
+ 3. ✅ validate-iri-format
66
+ 4. ❌ validate-literal-length
67
+
68
+ ⚙️ Complete Processing Chain
69
+ ────────────────────────────────────────────────────────────
70
+ Input:
71
+ Object: " Chuck "
72
+ Graph: DefaultGraph
73
+
74
+ Status: ✅ SUCCESS
75
+ Steps: 5/5 passed
76
+
77
+ Output:
78
+ Object: "Chuck"
79
+ Graph: NamedNode
80
+ Graph IRI: http://example.org/provenance/1733288888888
81
+
82
+ Chain execution:
83
+ 1. ✅ validate-iris
84
+ 2. ✅ 🔄 normalize-whitespace
85
+ └─ Transformation applied
86
+ 3. ✅ validate-literal-length
87
+ 4. ✅ 🔄 add-provenance
88
+ └─ Transformation applied
89
+ 5. ✅ final-validation
90
+
91
+ ============================================================
92
+ ✨ Hook Chains Example Complete
93
+ ```
94
+
95
+ ## Hook Chain Patterns
96
+
97
+ ### 1. Data Cleaning Chain
98
+
99
+ Clean and normalize messy data:
100
+
101
+ ```javascript
102
+ const dataCleaningChain = [
103
+ builtinHooks.trimLiterals, // Remove leading/trailing whitespace
104
+ normalizeWhitespace, // Collapse multiple spaces
105
+ validateLiteralLength, // Ensure valid length
106
+ ];
107
+
108
+ const result = executeHookChain(dataCleaningChain, store, quad);
109
+ ```
110
+
111
+ ### 2. Quality Assurance Chain
112
+
113
+ Comprehensive validation pipeline:
114
+
115
+ ```javascript
116
+ const qualityAssuranceChain = [
117
+ builtinHooks.standardValidation, // RDF structure
118
+ validateIRIs, // IRI well-formedness
119
+ builtinHooks.validateIRIFormat, // URL validation
120
+ validateLiteralLength, // Length constraints
121
+ ];
122
+
123
+ const result = executeHookChain(qualityAssuranceChain, store, quad);
124
+ ```
125
+
126
+ ### 3. Complete Processing Chain
127
+
128
+ Full pipeline with validation and transformation:
129
+
130
+ ```javascript
131
+ const completeProcessingChain = [
132
+ validateIRIs, // 1. Validate structure
133
+ normalizeWhitespace, // 2. Clean data
134
+ validateLiteralLength, // 3. Check constraints
135
+ addProvenance, // 4. Add metadata
136
+ finalValidation, // 5. Final checks
137
+ ];
138
+
139
+ const result = executeHookChain(completeProcessingChain, store, quad);
140
+ ```
141
+
142
+ ## Custom Hooks
143
+
144
+ ### Validation Hook
145
+
146
+ ```javascript
147
+ const validateIRIs = defineHook({
148
+ name: 'validate-iris',
149
+ trigger: 'before-add',
150
+ validate: quad => {
151
+ const validateIRI = term => {
152
+ if (term.termType !== 'NamedNode') return true;
153
+ try {
154
+ new URL(term.value);
155
+ return true;
156
+ } catch {
157
+ return false;
158
+ }
159
+ };
160
+
161
+ return validateIRI(quad.subject) &&
162
+ validateIRI(quad.predicate) &&
163
+ validateIRI(quad.object);
164
+ },
165
+ });
166
+ ```
167
+
168
+ ### Transformation Hook
169
+
170
+ ```javascript
171
+ const normalizeWhitespace = defineHook({
172
+ name: 'normalize-whitespace',
173
+ trigger: 'before-add',
174
+ transform: quad => {
175
+ if (quad.object.termType !== 'Literal') {
176
+ return quad;
177
+ }
178
+
179
+ const normalized = quad.object.value
180
+ .trim()
181
+ .replace(/\s+/g, ' ');
182
+
183
+ return DataFactory.quad(
184
+ quad.subject,
185
+ quad.predicate,
186
+ DataFactory.literal(
187
+ normalized,
188
+ quad.object.language || quad.object.datatype
189
+ ),
190
+ quad.graph
191
+ );
192
+ },
193
+ });
194
+ ```
195
+
196
+ ## Chain Execution
197
+
198
+ ### Success Case
199
+
200
+ ```javascript
201
+ const result = executeHookChain(chain, store, quad);
202
+
203
+ if (result.success) {
204
+ console.log('All hooks passed!');
205
+ console.log('Transformed quad:', result.quad);
206
+ console.log('Steps executed:', result.results.length);
207
+ }
208
+ ```
209
+
210
+ ### Failure Case (Early Termination)
211
+
212
+ ```javascript
213
+ const result = executeHookChain(chain, store, invalidQuad);
214
+
215
+ if (!result.success) {
216
+ const failedHook = result.results.find(r => !r.passed);
217
+ console.log('Failed at:', failedHook.hookName);
218
+ console.log('Reason:', failedHook.reason);
219
+ console.log('Steps before failure:', result.results.length);
220
+ }
221
+ ```
222
+
223
+ ### Tracking Transformations
224
+
225
+ ```javascript
226
+ const result = executeHookChain(chain, store, quad);
227
+
228
+ result.results.forEach((r, i) => {
229
+ console.log(`Step ${i + 1}: ${r.hookName}`);
230
+ if (r.transformed) {
231
+ console.log(' → Transformation applied');
232
+ }
233
+ if (!r.passed) {
234
+ console.log(' → Validation failed:', r.reason);
235
+ }
236
+ });
237
+ ```
238
+
239
+ ## Testing
240
+
241
+ The example includes comprehensive tests:
242
+
243
+ - Individual hook behavior
244
+ - Chain execution order
245
+ - Early termination on failures
246
+ - Progressive transformations
247
+ - Result aggregation
248
+
249
+ Run tests with:
250
+
251
+ ```bash
252
+ pnpm test
253
+ ```
254
+
255
+ ## Learn More
256
+
257
+ - [@unrdf/hooks Documentation](../../README.md)
258
+ - [Policy Hooks Example](../policy-hooks/)
259
+ - [UNRDF Core](../../../core/)
260
+
261
+ ## License
262
+
263
+ MIT
@@ -0,0 +1,21 @@
1
+ #!/bin/sh
2
+ basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
3
+
4
+ case `uname` in
5
+ *CYGWIN*|*MINGW*|*MSYS*)
6
+ if command -v cygpath > /dev/null 2>&1; then
7
+ basedir=`cygpath -w "$basedir"`
8
+ fi
9
+ ;;
10
+ esac
11
+
12
+ if [ -z "$NODE_PATH" ]; then
13
+ export NODE_PATH="/Users/sac/unrdf/packages/hooks/examples/node_modules:/Users/sac/unrdf/packages/hooks/node_modules:/Users/sac/unrdf/packages/node_modules:/Users/sac/unrdf/node_modules:/Users/sac/node_modules:/Users/node_modules:/node_modules:/Users/sac/unrdf/node_modules/.pnpm/node_modules"
14
+ else
15
+ export NODE_PATH="/Users/sac/unrdf/packages/hooks/examples/node_modules:/Users/sac/unrdf/packages/hooks/node_modules:/Users/sac/unrdf/packages/node_modules:/Users/sac/unrdf/node_modules:/Users/sac/node_modules:/Users/node_modules:/node_modules:/Users/sac/unrdf/node_modules/.pnpm/node_modules:$NODE_PATH"
16
+ fi
17
+ if [ -x "$basedir/node" ]; then
18
+ exec "$basedir/node" "$basedir/../@unrdf/hooks/examples/validate-hooks.mjs" "$@"
19
+ else
20
+ exec node "$basedir/../@unrdf/hooks/examples/validate-hooks.mjs" "$@"
21
+ fi
@@ -0,0 +1,21 @@
1
+ #!/bin/sh
2
+ basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
3
+
4
+ case `uname` in
5
+ *CYGWIN*|*MINGW*|*MSYS*)
6
+ if command -v cygpath > /dev/null 2>&1; then
7
+ basedir=`cygpath -w "$basedir"`
8
+ fi
9
+ ;;
10
+ esac
11
+
12
+ if [ -z "$NODE_PATH" ]; then
13
+ export NODE_PATH="/Users/sac/unrdf/node_modules/.pnpm/vitest@4.0.15_@opentelemetry+api@1.9.0_@types+node@24.10.1_@vitest+ui@4.0.15_happy-dom@_4020ef6a5cc83a28d6954792bad1f77a/node_modules/vitest/node_modules:/Users/sac/unrdf/node_modules/.pnpm/vitest@4.0.15_@opentelemetry+api@1.9.0_@types+node@24.10.1_@vitest+ui@4.0.15_happy-dom@_4020ef6a5cc83a28d6954792bad1f77a/node_modules:/Users/sac/unrdf/node_modules/.pnpm/node_modules"
14
+ else
15
+ export NODE_PATH="/Users/sac/unrdf/node_modules/.pnpm/vitest@4.0.15_@opentelemetry+api@1.9.0_@types+node@24.10.1_@vitest+ui@4.0.15_happy-dom@_4020ef6a5cc83a28d6954792bad1f77a/node_modules/vitest/node_modules:/Users/sac/unrdf/node_modules/.pnpm/vitest@4.0.15_@opentelemetry+api@1.9.0_@types+node@24.10.1_@vitest+ui@4.0.15_happy-dom@_4020ef6a5cc83a28d6954792bad1f77a/node_modules:/Users/sac/unrdf/node_modules/.pnpm/node_modules:$NODE_PATH"
16
+ fi
17
+ if [ -x "$basedir/node" ]; then
18
+ exec "$basedir/node" "$basedir/../vitest/vitest.mjs" "$@"
19
+ else
20
+ exec node "$basedir/../vitest/vitest.mjs" "$@"
21
+ fi
@@ -0,0 +1 @@
1
+ {"version":"4.0.15","results":[[":test/example.test.mjs",{"duration":7.120207999999991,"failed":false}]]}
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "@unrdf/hooks-example-chains",
3
+ "version": "5.0.0",
4
+ "type": "module",
5
+ "description": "Hook Chains Example - Sequential hook execution and composition",
6
+ "private": true,
7
+ "scripts": {
8
+ "start": "node src/index.mjs",
9
+ "test": "vitest run",
10
+ "test:watch": "vitest",
11
+ "test:coverage": "vitest run --coverage"
12
+ },
13
+ "dependencies": {
14
+ "@unrdf/core": "workspace:*",
15
+ "@unrdf/hooks": "workspace:*",
16
+ "n3": "^1.26.0",
17
+ "zod": "^4.1.13"
18
+ },
19
+ "devDependencies": {
20
+ "vitest": "^4.0.15"
21
+ },
22
+ "engines": {
23
+ "node": ">=18.0.0"
24
+ }
25
+ }