@unrdf/hooks 26.4.2 → 26.4.4

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 (60) hide show
  1. package/LICENSE +24 -0
  2. package/README.md +562 -53
  3. package/examples/atomvm-fibo-hooks-demo.mjs +323 -0
  4. package/examples/delta-monitoring-example.mjs +213 -0
  5. package/examples/fibo-jtbd-governance.mjs +388 -0
  6. package/examples/hook-chains/node_modules/.bin/jiti +21 -0
  7. package/examples/hook-chains/node_modules/.bin/msw +21 -0
  8. package/examples/hook-chains/node_modules/.bin/terser +21 -0
  9. package/examples/hook-chains/node_modules/.bin/tsc +21 -0
  10. package/examples/hook-chains/node_modules/.bin/tsserver +21 -0
  11. package/examples/hook-chains/node_modules/.bin/tsx +21 -0
  12. package/examples/hook-chains/node_modules/.bin/vite +21 -0
  13. package/examples/hook-chains/node_modules/.bin/vitest +2 -2
  14. package/examples/hook-chains/node_modules/.bin/yaml +21 -0
  15. package/examples/hook-chains/package.json +2 -2
  16. package/examples/hook-chains/unrdf-hooks-example-chains-5.0.0.tgz +0 -0
  17. package/examples/hooks-marketplace.mjs +261 -0
  18. package/examples/n3-reasoning-example.mjs +279 -0
  19. package/examples/policy-hooks/node_modules/.bin/jiti +21 -0
  20. package/examples/policy-hooks/node_modules/.bin/msw +21 -0
  21. package/examples/policy-hooks/node_modules/.bin/terser +21 -0
  22. package/examples/policy-hooks/node_modules/.bin/tsc +21 -0
  23. package/examples/policy-hooks/node_modules/.bin/tsserver +21 -0
  24. package/examples/policy-hooks/node_modules/.bin/tsx +21 -0
  25. package/examples/policy-hooks/node_modules/.bin/vite +21 -0
  26. package/examples/policy-hooks/node_modules/.bin/vitest +2 -2
  27. package/examples/policy-hooks/node_modules/.bin/yaml +21 -0
  28. package/examples/policy-hooks/package.json +2 -2
  29. package/examples/policy-hooks/unrdf-hooks-example-policy-5.0.0.tgz +0 -0
  30. package/examples/shacl-repair-example.mjs +191 -0
  31. package/examples/window-condition-example.mjs +285 -0
  32. package/package.json +6 -3
  33. package/src/atomvm.mjs +9 -0
  34. package/src/define.mjs +114 -0
  35. package/src/executor.mjs +23 -0
  36. package/src/hooks/atomvm-bridge.mjs +332 -0
  37. package/src/hooks/builtin-hooks.mjs +13 -7
  38. package/src/hooks/condition-evaluator.mjs +684 -77
  39. package/src/hooks/define-hook.mjs +23 -21
  40. package/src/hooks/effect-executor.mjs +630 -0
  41. package/src/hooks/effect-sandbox.mjs +19 -9
  42. package/src/hooks/file-resolver.mjs +155 -1
  43. package/src/hooks/hook-chain-compiler.mjs +11 -1
  44. package/src/hooks/hook-executor.mjs +98 -73
  45. package/src/hooks/knowledge-hook-engine.mjs +133 -7
  46. package/src/hooks/ontology-learner.mjs +190 -0
  47. package/src/hooks/policy-pack.mjs +7 -1
  48. package/src/hooks/query-optimizer.mjs +1 -5
  49. package/src/hooks/query.mjs +3 -3
  50. package/src/hooks/schemas.mjs +55 -5
  51. package/src/hooks/security/error-sanitizer.mjs +46 -24
  52. package/src/hooks/self-play-autonomics.mjs +423 -0
  53. package/src/hooks/telemetry.mjs +32 -9
  54. package/src/hooks/validate.mjs +100 -33
  55. package/src/index.mjs +2 -0
  56. package/src/lib/admit-hook.mjs +615 -0
  57. package/src/policy-compiler.mjs +23 -13
  58. package/dist/index.d.mts +0 -1738
  59. package/dist/index.d.ts +0 -1738
  60. package/dist/index.mjs +0 -1738
@@ -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/tsx@4.21.0/node_modules/tsx/dist/node_modules:/Users/sac/unrdf/node_modules/.pnpm/tsx@4.21.0/node_modules/tsx/node_modules:/Users/sac/unrdf/node_modules/.pnpm/tsx@4.21.0/node_modules:/Users/sac/unrdf/node_modules/.pnpm/node_modules"
14
+ else
15
+ export NODE_PATH="/Users/sac/unrdf/node_modules/.pnpm/tsx@4.21.0/node_modules/tsx/dist/node_modules:/Users/sac/unrdf/node_modules/.pnpm/tsx@4.21.0/node_modules/tsx/node_modules:/Users/sac/unrdf/node_modules/.pnpm/tsx@4.21.0/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/../../../../../../node_modules/.pnpm/tsx@4.21.0/node_modules/tsx/dist/cli.mjs" "$@"
19
+ else
20
+ exec node "$basedir/../../../../../../node_modules/.pnpm/tsx@4.21.0/node_modules/tsx/dist/cli.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/vite@7.3.1_@types+node@25.5.0_jiti@2.6.1_lightningcss@1.32.0_terser@5.46.1_tsx@4.21.0_yaml@2.8.3/node_modules/vite/bin/node_modules:/Users/sac/unrdf/node_modules/.pnpm/vite@7.3.1_@types+node@25.5.0_jiti@2.6.1_lightningcss@1.32.0_terser@5.46.1_tsx@4.21.0_yaml@2.8.3/node_modules/vite/node_modules:/Users/sac/unrdf/node_modules/.pnpm/vite@7.3.1_@types+node@25.5.0_jiti@2.6.1_lightningcss@1.32.0_terser@5.46.1_tsx@4.21.0_yaml@2.8.3/node_modules:/Users/sac/unrdf/node_modules/.pnpm/node_modules"
14
+ else
15
+ export NODE_PATH="/Users/sac/unrdf/node_modules/.pnpm/vite@7.3.1_@types+node@25.5.0_jiti@2.6.1_lightningcss@1.32.0_terser@5.46.1_tsx@4.21.0_yaml@2.8.3/node_modules/vite/bin/node_modules:/Users/sac/unrdf/node_modules/.pnpm/vite@7.3.1_@types+node@25.5.0_jiti@2.6.1_lightningcss@1.32.0_terser@5.46.1_tsx@4.21.0_yaml@2.8.3/node_modules/vite/node_modules:/Users/sac/unrdf/node_modules/.pnpm/vite@7.3.1_@types+node@25.5.0_jiti@2.6.1_lightningcss@1.32.0_terser@5.46.1_tsx@4.21.0_yaml@2.8.3/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/../../../../../../node_modules/.pnpm/vite@7.3.1_@types+node@25.5.0_jiti@2.6.1_lightningcss@1.32.0_terser@5.46.1_tsx@4.21.0_yaml@2.8.3/node_modules/vite/bin/vite.js" "$@"
19
+ else
20
+ exec node "$basedir/../../../../../../node_modules/.pnpm/vite@7.3.1_@types+node@25.5.0_jiti@2.6.1_lightningcss@1.32.0_terser@5.46.1_tsx@4.21.0_yaml@2.8.3/node_modules/vite/bin/vite.js" "$@"
21
+ fi
@@ -10,9 +10,9 @@ case `uname` in
10
10
  esac
11
11
 
12
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"
13
+ export NODE_PATH="/Users/sac/unrdf/node_modules/.pnpm/vitest@4.1.2_@opentelemetry+api@1.9.1_@types+node@25.5.0_@vitest+ui@4.1.2_happy-dom@20._43a84fb45b8a6c418aa4e6591a004ea7/node_modules/vitest/node_modules:/Users/sac/unrdf/node_modules/.pnpm/vitest@4.1.2_@opentelemetry+api@1.9.1_@types+node@25.5.0_@vitest+ui@4.1.2_happy-dom@20._43a84fb45b8a6c418aa4e6591a004ea7/node_modules:/Users/sac/unrdf/node_modules/.pnpm/node_modules"
14
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"
15
+ export NODE_PATH="/Users/sac/unrdf/node_modules/.pnpm/vitest@4.1.2_@opentelemetry+api@1.9.1_@types+node@25.5.0_@vitest+ui@4.1.2_happy-dom@20._43a84fb45b8a6c418aa4e6591a004ea7/node_modules/vitest/node_modules:/Users/sac/unrdf/node_modules/.pnpm/vitest@4.1.2_@opentelemetry+api@1.9.1_@types+node@25.5.0_@vitest+ui@4.1.2_happy-dom@20._43a84fb45b8a6c418aa4e6591a004ea7/node_modules:/Users/sac/unrdf/node_modules/.pnpm/node_modules:$NODE_PATH"
16
16
  fi
17
17
  if [ -x "$basedir/node" ]; then
18
18
  exec "$basedir/node" "$basedir/../vitest/vitest.mjs" "$@"
@@ -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/yaml@2.8.3/node_modules/yaml/node_modules:/Users/sac/unrdf/node_modules/.pnpm/yaml@2.8.3/node_modules:/Users/sac/unrdf/node_modules/.pnpm/node_modules"
14
+ else
15
+ export NODE_PATH="/Users/sac/unrdf/node_modules/.pnpm/yaml@2.8.3/node_modules/yaml/node_modules:/Users/sac/unrdf/node_modules/.pnpm/yaml@2.8.3/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/../../../../../../node_modules/.pnpm/yaml@2.8.3/node_modules/yaml/bin.mjs" "$@"
19
+ else
20
+ exec node "$basedir/../../../../../../node_modules/.pnpm/yaml@2.8.3/node_modules/yaml/bin.mjs" "$@"
21
+ fi
@@ -11,8 +11,8 @@
11
11
  "test:coverage": "vitest run --coverage"
12
12
  },
13
13
  "dependencies": {
14
- "@unrdf/core": "workspace:*",
15
- "@unrdf/hooks": "workspace:*",
14
+ "@unrdf/core": "26.4.3",
15
+ "@unrdf/hooks": "26.4.3",
16
16
  "n3": "^1.26.0",
17
17
  "zod": "^4.1.13"
18
18
  },
@@ -0,0 +1,191 @@
1
+ /**
2
+ * @unrdf/hooks - SHACL Repair Mode Example
3
+ *
4
+ * Demonstrates SHACL validation with automatic repair.
5
+ * When validation fails, repair queries fix data, then re-validate.
6
+ *
7
+ * Use cases:
8
+ * - Auto-filling missing required properties with defaults
9
+ * - Normalizing invalid references
10
+ * - Fixing data quality issues automatically
11
+ */
12
+
13
+ import { createStore } from '@unrdf/oxigraph';
14
+ import { evaluateCondition } from '../src/hooks/condition-evaluator.mjs';
15
+ import { UnrdfDataFactory as DataFactory } from '@unrdf/core/rdf/n3-justified-only';
16
+
17
+ const { namedNode, literal, quad } = DataFactory;
18
+
19
+ console.log('=== SHACL Repair Mode Example ===\n');
20
+
21
+ // Create a test store
22
+ const store = createStore();
23
+
24
+ // Add test data (some valid, some missing required properties)
25
+ console.log('1. Loading test data...');
26
+ store.add(
27
+ quad(
28
+ namedNode('http://example.org/trade/t1'),
29
+ namedNode('http://www.w3.org/1999/02/22-rdf-syntax-ns#type'),
30
+ namedNode('http://example.org/Trade')
31
+ )
32
+ );
33
+ store.add(
34
+ quad(
35
+ namedNode('http://example.org/trade/t1'),
36
+ namedNode('http://example.org/amount'),
37
+ literal('150000')
38
+ )
39
+ );
40
+ // Note: missing riskScore
41
+
42
+ store.add(
43
+ quad(
44
+ namedNode('http://example.org/trade/t2'),
45
+ namedNode('http://www.w3.org/1999/02/22-rdf-syntax-ns#type'),
46
+ namedNode('http://example.org/Trade')
47
+ )
48
+ );
49
+ // Note: missing both amount and riskScore
50
+
51
+ console.log(` Loaded ${store.size} quads\n`);
52
+
53
+ // Define SHACL condition with repair query
54
+ console.log('2. Defining SHACL repair condition...\n');
55
+
56
+ const shaclRepairCondition = {
57
+ kind: 'shacl',
58
+ ref: {
59
+ uri: 'file:///shapes/trade-shape.ttl',
60
+ // Note: in production, provide actual sha256 hash
61
+ // sha256: 'abc123def456...'
62
+ },
63
+ enforcementMode: 'repair',
64
+ repairConstruct: `
65
+ # Fill in missing required properties with defaults
66
+ CONSTRUCT {
67
+ ?trade ex:riskScore ?defaultRisk ;
68
+ ex:amount ?defaultAmount ;
69
+ ex:status ex:Pending .
70
+ }
71
+ WHERE {
72
+ ?trade a ex:Trade .
73
+ OPTIONAL { ?trade ex:riskScore ?existing . }
74
+ OPTIONAL { ?trade ex:amount ?existingAmount . }
75
+ FILTER (!BOUND(?existing) || !BOUND(?existingAmount))
76
+ BIND (50 as ?defaultRisk)
77
+ BIND (0 as ?defaultAmount)
78
+ }
79
+ `
80
+ };
81
+
82
+ console.log('Repair CONSTRUCT query:');
83
+ console.log(shaclRepairCondition.repairConstruct);
84
+ console.log('');
85
+
86
+ // Example 1: Block mode (strictest - no repairs)
87
+ console.log('3. Example: Block Mode (no repairs)\n');
88
+ const blockCondition = {
89
+ ...shaclRepairCondition,
90
+ enforcementMode: 'block'
91
+ };
92
+
93
+ console.log('With block mode:');
94
+ console.log('- Validation failure = hook fails (false)');
95
+ console.log('- No automatic repair attempted');
96
+ console.log('- Application must handle fix separately\n');
97
+
98
+ // Example 2: Annotate mode (audit trail)
99
+ console.log('4. Example: Annotate Mode (logging)\n');
100
+ const annotateCondition = {
101
+ ...shaclRepairCondition,
102
+ enforcementMode: 'annotate'
103
+ };
104
+
105
+ console.log('With annotate mode:');
106
+ console.log('- Validation failure = add sh:ValidationResult triples');
107
+ console.log('- Returns true (permits operation)');
108
+ console.log('- Violations recorded in RDF for audit trail');
109
+ console.log('- Good for compliance tracking\n');
110
+
111
+ // Example 3: Repair mode (automatic fixing)
112
+ console.log('5. Example: Repair Mode (automatic fix)\n');
113
+ const repairCondition = {
114
+ ...shaclRepairCondition,
115
+ enforcementMode: 'repair'
116
+ };
117
+
118
+ console.log('With repair mode:');
119
+ console.log('- Validation failure = execute repairConstruct');
120
+ console.log('- Repair adds missing properties with defaults');
121
+ console.log('- Re-validates after repair');
122
+ console.log('- Returns success/failure of final validation\n');
123
+
124
+ // Example 4: Multi-phase repair strategy
125
+ console.log('6. Complex Example: Multi-Phase Repair\n');
126
+
127
+ const complexRepairCondition = {
128
+ kind: 'shacl',
129
+ ref: { uri: 'file:///shapes/comprehensive.ttl' },
130
+ enforcementMode: 'repair',
131
+ repairConstruct: `
132
+ PREFIX ex: <http://example.org/>
133
+ PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
134
+
135
+ CONSTRUCT {
136
+ ?entity ex:status ex:Reviewed ;
137
+ ex:lastChecked ?now ;
138
+ ex:quality ?quality .
139
+ }
140
+ WHERE {
141
+ ?entity a ex:Entity .
142
+ BIND (NOW() as ?now)
143
+
144
+ # Quality assessment: calculate based on completeness
145
+ OPTIONAL { ?entity ex:description ?desc . }
146
+ OPTIONAL { ?entity ex:classification ?class . }
147
+ OPTIONAL { ?entity ex:owner ?owner . }
148
+
149
+ BIND (
150
+ IF(BOUND(?desc) && BOUND(?class) && BOUND(?owner),
151
+ "high",
152
+ "medium")
153
+ as ?quality
154
+ )
155
+ }
156
+ `
157
+ };
158
+
159
+ console.log('Multi-phase repair includes:');
160
+ console.log('1. Add status field if missing');
161
+ console.log('2. Record timestamp of check');
162
+ console.log('3. Calculate quality score based on available properties\n');
163
+
164
+ // Practical workflow
165
+ console.log('7. Practical Workflow\n');
166
+ console.log('Step 1: Data arrives (potentially with gaps)');
167
+ console.log('Step 2: evaluateCondition() with shacl/repair');
168
+ console.log('Step 3: If validation fails:');
169
+ console.log(' - Auto repair executes');
170
+ console.log(' - Missing properties filled with defaults');
171
+ console.log(' - Re-validation checks if repair succeeded');
172
+ console.log('Step 4: Result indicates success/failure');
173
+ console.log('Step 5: Application continues based on result\n');
174
+
175
+ // Best practices
176
+ console.log('8. Best Practices for Repair Mode\n');
177
+ console.log('✓ Use repair for:');
178
+ console.log(' - Filling optional properties with sensible defaults');
179
+ console.log(' - Normalizing data format (URI standardization)');
180
+ console.log(' - Adding audit/lifecycle properties');
181
+ console.log('');
182
+ console.log('✗ Avoid repair for:');
183
+ console.log(' - Changing core business data semantics');
184
+ console.log(' - Complex transformations (use effects instead)');
185
+ console.log(' - Expensive computations (will run on every violation)');
186
+ console.log('');
187
+ console.log('💡 Combine with effects:');
188
+ console.log(' 1. Condition: shacl/repair → auto-fix violations');
189
+ console.log(' 2. Effect: sparql-construct → apply business logic\n');
190
+
191
+ console.log('=== Example Complete ===');
@@ -0,0 +1,285 @@
1
+ /**
2
+ * @unrdf/hooks - Window Condition Example
3
+ *
4
+ * Demonstrates time-windowed aggregation evaluation.
5
+ * Triggers rules when aggregated metrics within time windows meet thresholds.
6
+ *
7
+ * Use cases:
8
+ * - Rate limiting: "more than 100 requests/minute"
9
+ * - Anomaly detection: "average temperature exceeds threshold"
10
+ * - Traffic analysis: "peak traffic detection"
11
+ * - Quota enforcement: "daily transaction limit"
12
+ */
13
+
14
+ import { createStore } from '@unrdf/oxigraph';
15
+ import { evaluateCondition } from '../src/hooks/condition-evaluator.mjs';
16
+ import { UnrdfDataFactory as DataFactory } from '@unrdf/core/rdf/n3-justified-only';
17
+
18
+ const { namedNode, literal, quad } = DataFactory;
19
+
20
+ console.log('=== Window Condition Example ===\n');
21
+
22
+ // Utility: create window condition
23
+ function createWindowCondition(aggregate, windowMs, query) {
24
+ return {
25
+ kind: 'window',
26
+ spec: {
27
+ size: windowMs,
28
+ aggregate: aggregate,
29
+ query: query
30
+ }
31
+ };
32
+ }
33
+
34
+ // Example 1: Count-based window
35
+ console.log('1. Count Window (Rate Limiting)\n');
36
+
37
+ const countWindowCondition = createWindowCondition(
38
+ 'count',
39
+ 60000, // 1 minute
40
+ `SELECT ?s WHERE {
41
+ ?s a ex:Request ;
42
+ ex:timestamp ?t .
43
+ FILTER (?t > NOW() - PT1M)
44
+ }`
45
+ );
46
+
47
+ console.log('Condition:');
48
+ console.log(JSON.stringify(countWindowCondition, null, 2));
49
+ console.log('');
50
+ console.log('Purpose: Count matches in last 60 seconds');
51
+ console.log('Triggers when: count >= configured threshold');
52
+ console.log('Use for: Rate limiting, quota enforcement');
53
+ console.log('');
54
+
55
+ console.log('Example usage in hook:');
56
+ console.log(`
57
+ {
58
+ condition: ${JSON.stringify(countWindowCondition, null, 4)},
59
+ effects: [{
60
+ kind: 'sparql-construct',
61
+ query: \`CONSTRUCT { ?s ex:rateLimited true } WHERE { ?s a ex:Request }\`
62
+ }]
63
+ }
64
+
65
+ Flow:
66
+ 1. Query finds all requests in last 60 seconds
67
+ 2. Count results
68
+ 3. If count > 100, hook triggers
69
+ 4. Affected requests marked as rate-limited
70
+ `);
71
+
72
+ // Example 2: Sum aggregation
73
+ console.log('\n2. Sum Window (Traffic Volume)\n');
74
+
75
+ const sumWindowCondition = createWindowCondition(
76
+ 'sum',
77
+ 300000, // 5 minutes
78
+ `SELECT ?bytes WHERE {
79
+ ?transfer ex:transferredBytes ?bytes ;
80
+ ex:timestamp ?t .
81
+ FILTER (?t > NOW() - PT5M)
82
+ }`
83
+ );
84
+
85
+ console.log('Condition:');
86
+ console.log(JSON.stringify(sumWindowCondition, null, 2));
87
+ console.log('');
88
+ console.log('Purpose: Sum bytes transferred in last 5 minutes');
89
+ console.log('Triggers when: sum > threshold (e.g., 1GB)');
90
+ console.log('Use for: Bandwidth management, peak detection\n');
91
+
92
+ // Example 3: Average aggregation
93
+ console.log('3. Average Window (Anomaly Detection)\n');
94
+
95
+ const avgWindowCondition = createWindowCondition(
96
+ 'avg',
97
+ 3600000, // 1 hour
98
+ `SELECT ?temp WHERE {
99
+ ?sensor ex:reading ?temp ;
100
+ ex:timestamp ?t .
101
+ FILTER (?t > NOW() - PT1H)
102
+ }`
103
+ );
104
+
105
+ console.log('Condition:');
106
+ console.log(JSON.stringify(avgWindowCondition, null, 2));
107
+ console.log('');
108
+ console.log('Purpose: Average temperature over last hour');
109
+ console.log('Triggers when: avg > 85 degrees (anomaly)');
110
+ console.log('Use for: Anomaly detection, SLA monitoring\n');
111
+
112
+ // Example 4: Min/Max windows
113
+ console.log('4. Min/Max Windows (Range Monitoring)\n');
114
+
115
+ const minWindowCondition = createWindowCondition(
116
+ 'min',
117
+ 86400000, // 24 hours
118
+ `SELECT ?value WHERE {
119
+ ?metric ex:dailyValue ?value ;
120
+ ex:timestamp ?t .
121
+ FILTER (?t > NOW() - P1D)
122
+ }`
123
+ );
124
+
125
+ const maxWindowCondition = createWindowCondition(
126
+ 'max',
127
+ 86400000, // 24 hours
128
+ `SELECT ?value WHERE {
129
+ ?metric ex:dailyValue ?value ;
130
+ ex:timestamp ?t .
131
+ FILTER (?t > NOW() - P1D)
132
+ }`
133
+ );
134
+
135
+ console.log('Min window (find lowest value in window):');
136
+ console.log(JSON.stringify(minWindowCondition, null, 2));
137
+ console.log('');
138
+ console.log('Max window (find highest value in window):');
139
+ console.log(JSON.stringify(maxWindowCondition, null, 2));
140
+ console.log('');
141
+ console.log('Use for: Performance tracking, threshold detection\n');
142
+
143
+ // Real-world scenarios
144
+ console.log('5. Real-World Scenarios\n');
145
+
146
+ console.log('Scenario A: API Rate Limiting');
147
+ console.log('-'.repeat(40));
148
+ console.log(`
149
+ Condition: Window count in 1 minute > 1000
150
+ {
151
+ kind: 'window',
152
+ spec: {
153
+ size: 60000,
154
+ aggregate: 'count',
155
+ query: 'SELECT ?req WHERE { ?req a ex:APICall ; ex:timestamp ?t }'
156
+ }
157
+ }
158
+
159
+ Hook flow:
160
+ 1. Client makes API requests
161
+ 2. Each request added as RDF triple with timestamp
162
+ 3. Window counts requests in last minute
163
+ 4. If > 1000, trigger rate limiting effect
164
+ 5. Mark client as throttled, return 429 Too Many Requests
165
+ `);
166
+
167
+ console.log('\nScenario B: Data Quality Monitoring');
168
+ console.log('-'.repeat(40));
169
+ console.log(`
170
+ Condition: Window average error rate in 1 hour > 5%
171
+ {
172
+ kind: 'window',
173
+ spec: {
174
+ size: 3600000,
175
+ aggregate: 'avg',
176
+ query: 'SELECT ?rate WHERE { ?batch ex:errorRate ?rate }'
177
+ }
178
+ }
179
+
180
+ Hook flow:
181
+ 1. Data processing batches complete
182
+ 2. Each batch records error rate
183
+ 3. Window averages error rates from last hour
184
+ 4. If average > 5%, alert operations
185
+ 5. Trigger investigation and mitigation effect
186
+ `);
187
+
188
+ console.log('\nScenario C: Quota Enforcement');
189
+ console.log('-'.repeat(40));
190
+ console.log(`
191
+ Condition: Window sum of usage in 24 hours > quota
192
+ {
193
+ kind: 'window',
194
+ spec: {
195
+ size: 86400000,
196
+ aggregate: 'sum',
197
+ query: 'SELECT ?usage WHERE { ?tx ex:usage ?usage }'
198
+ }
199
+ }
200
+
201
+ Hook flow:
202
+ 1. Transactions recorded with usage amount
203
+ 2. Window sums usage over rolling 24 hours
204
+ 3. If sum > daily quota, enforce limit
205
+ 4. Mark user as over-quota
206
+ 5. Require billing upgrade or wait for next period
207
+ `);
208
+
209
+ // Aggregate functions explained
210
+ console.log('\n6. Aggregate Functions Explained\n');
211
+
212
+ console.log('count - Number of matches in window');
213
+ console.log(' Example: How many API calls in last minute?');
214
+ console.log('');
215
+ console.log('sum - Total of numeric values in window');
216
+ console.log(' Example: Total bytes transferred in 5 minutes?');
217
+ console.log('');
218
+ console.log('avg - Average of numeric values in window');
219
+ console.log(' Example: Average response time over 1 hour?');
220
+ console.log('');
221
+ console.log('min - Minimum value in window');
222
+ console.log(' Example: Lowest temperature recorded today?');
223
+ console.log('');
224
+ console.log('max - Maximum value in window');
225
+ console.log(' Example: Peak memory usage in last hour?\n');
226
+
227
+ // Window sizing
228
+ console.log('7. Window Size Best Practices\n');
229
+
230
+ console.log('Use Case Window Size Frequency Aggregate');
231
+ console.log('-'.repeat(60));
232
+ console.log('API rate limiting 1 minute Per request count');
233
+ console.log('Traffic analysis 5 minutes Per measurement sum/avg');
234
+ console.log('SLA monitoring 1 hour Per measurement avg/max');
235
+ console.log('Daily quota 24 hours Per transaction sum');
236
+ console.log('Anomaly detection 1 hour Per event avg/stddev\n');
237
+
238
+ // Combining with effects
239
+ console.log('8. Full Hook Example: Rate Limiting\n');
240
+
241
+ console.log(`
242
+ const rateLimitHook = {
243
+ name: 'enforce-api-rate-limit',
244
+
245
+ condition: {
246
+ kind: 'window',
247
+ spec: {
248
+ size: 60000, // 1 minute window
249
+ aggregate: 'count',
250
+ query: \`
251
+ SELECT ?request WHERE {
252
+ ?request a ex:APIRequest ;
253
+ ex:clientId ?client ;
254
+ ex:timestamp ?t .
255
+ FILTER (?t > NOW() - PT1M)
256
+ }
257
+ \`
258
+ }
259
+ },
260
+
261
+ effects: [{
262
+ kind: 'sparql-construct',
263
+ query: \`
264
+ CONSTRUCT {
265
+ ?client ex:rateLimited true ;
266
+ ex:retryAfter "60"^^xsd:integer ;
267
+ ex:error "Rate limit exceeded" .
268
+ }
269
+ WHERE {
270
+ ?request a ex:APIRequest ;
271
+ ex:clientId ?client .
272
+ }
273
+ \`
274
+ }]
275
+ };
276
+ `);
277
+
278
+ console.log('\nExecution:');
279
+ console.log('1. Request arrives, added to store with timestamp');
280
+ console.log('2. Window condition counts requests from same client in 1min');
281
+ console.log('3. If count > 1000, condition triggers');
282
+ console.log('4. Effect adds rate limit response triples');
283
+ console.log('5. API returns 429 with retry-after header\n');
284
+
285
+ console.log('=== Example Complete ===');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unrdf/hooks",
3
- "version": "26.4.2",
3
+ "version": "26.4.4",
4
4
  "description": "UNRDF Knowledge Hooks - Policy Definition and Execution Framework",
5
5
  "type": "module",
6
6
  "bin": {
@@ -47,9 +47,12 @@
47
47
  "validation"
48
48
  ],
49
49
  "dependencies": {
50
- "@unrdf/core": "workspace:*",
51
- "@unrdf/oxigraph": "workspace:*",
50
+ "@noble/hashes": "^1.4.0",
51
+ "@unrdf/core": "26.4.4",
52
+ "@unrdf/oxigraph": "26.4.4",
52
53
  "citty": "^0.1.6",
54
+ "eyereasoner": "^10",
55
+ "sparqljs": "^3.7.3",
53
56
  "zod": "^4.1.13"
54
57
  },
55
58
  "devDependencies": {
package/src/atomvm.mjs ADDED
@@ -0,0 +1,9 @@
1
+ /**
2
+ * @unrdf/hooks/atomvm
3
+ *
4
+ * AtomVM/Erlang integration for Knowledge Hooks
5
+ *
6
+ * @module @unrdf/hooks/atomvm
7
+ */
8
+
9
+ export { HooksBridge, createHooksBridge } from './hooks/atomvm-bridge.mjs';