@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.
- package/LICENSE +24 -0
- package/README.md +562 -53
- package/examples/atomvm-fibo-hooks-demo.mjs +323 -0
- package/examples/delta-monitoring-example.mjs +213 -0
- package/examples/fibo-jtbd-governance.mjs +388 -0
- package/examples/hook-chains/node_modules/.bin/jiti +21 -0
- package/examples/hook-chains/node_modules/.bin/msw +21 -0
- package/examples/hook-chains/node_modules/.bin/terser +21 -0
- package/examples/hook-chains/node_modules/.bin/tsc +21 -0
- package/examples/hook-chains/node_modules/.bin/tsserver +21 -0
- package/examples/hook-chains/node_modules/.bin/tsx +21 -0
- package/examples/hook-chains/node_modules/.bin/vite +21 -0
- package/examples/hook-chains/node_modules/.bin/vitest +2 -2
- package/examples/hook-chains/node_modules/.bin/yaml +21 -0
- package/examples/hook-chains/package.json +2 -2
- package/examples/hook-chains/unrdf-hooks-example-chains-5.0.0.tgz +0 -0
- package/examples/hooks-marketplace.mjs +261 -0
- package/examples/n3-reasoning-example.mjs +279 -0
- package/examples/policy-hooks/node_modules/.bin/jiti +21 -0
- package/examples/policy-hooks/node_modules/.bin/msw +21 -0
- package/examples/policy-hooks/node_modules/.bin/terser +21 -0
- package/examples/policy-hooks/node_modules/.bin/tsc +21 -0
- package/examples/policy-hooks/node_modules/.bin/tsserver +21 -0
- package/examples/policy-hooks/node_modules/.bin/tsx +21 -0
- package/examples/policy-hooks/node_modules/.bin/vite +21 -0
- package/examples/policy-hooks/node_modules/.bin/vitest +2 -2
- package/examples/policy-hooks/node_modules/.bin/yaml +21 -0
- package/examples/policy-hooks/package.json +2 -2
- package/examples/policy-hooks/unrdf-hooks-example-policy-5.0.0.tgz +0 -0
- package/examples/shacl-repair-example.mjs +191 -0
- package/examples/window-condition-example.mjs +285 -0
- package/package.json +6 -3
- package/src/atomvm.mjs +9 -0
- package/src/define.mjs +114 -0
- package/src/executor.mjs +23 -0
- package/src/hooks/atomvm-bridge.mjs +332 -0
- package/src/hooks/builtin-hooks.mjs +13 -7
- package/src/hooks/condition-evaluator.mjs +684 -77
- package/src/hooks/define-hook.mjs +23 -21
- package/src/hooks/effect-executor.mjs +630 -0
- package/src/hooks/effect-sandbox.mjs +19 -9
- package/src/hooks/file-resolver.mjs +155 -1
- package/src/hooks/hook-chain-compiler.mjs +11 -1
- package/src/hooks/hook-executor.mjs +98 -73
- package/src/hooks/knowledge-hook-engine.mjs +133 -7
- package/src/hooks/ontology-learner.mjs +190 -0
- package/src/hooks/policy-pack.mjs +7 -1
- package/src/hooks/query-optimizer.mjs +1 -5
- package/src/hooks/query.mjs +3 -3
- package/src/hooks/schemas.mjs +55 -5
- package/src/hooks/security/error-sanitizer.mjs +46 -24
- package/src/hooks/self-play-autonomics.mjs +423 -0
- package/src/hooks/telemetry.mjs +32 -9
- package/src/hooks/validate.mjs +100 -33
- package/src/index.mjs +2 -0
- package/src/lib/admit-hook.mjs +615 -0
- package/src/policy-compiler.mjs +23 -13
- package/dist/index.d.mts +0 -1738
- package/dist/index.d.ts +0 -1738
- 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.
|
|
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.
|
|
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
|
|
Binary file
|
|
@@ -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.
|
|
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
|
-
"@
|
|
51
|
-
"@unrdf/
|
|
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": {
|