@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.
- package/dist/index.d.mts +1738 -0
- package/dist/index.d.ts +1738 -0
- package/dist/index.mjs +1738 -0
- package/examples/basic.mjs +113 -0
- package/examples/hook-chains/README.md +263 -0
- package/examples/hook-chains/node_modules/.bin/validate-hooks +21 -0
- package/examples/hook-chains/node_modules/.bin/vitest +21 -0
- package/examples/hook-chains/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
- package/examples/hook-chains/package.json +25 -0
- package/examples/hook-chains/src/index.mjs +348 -0
- package/examples/hook-chains/test/example.test.mjs +252 -0
- package/examples/hook-chains/vitest.config.mjs +14 -0
- package/examples/knowledge-hook-manager-usage.mjs +65 -0
- package/examples/policy-hooks/README.md +193 -0
- package/examples/policy-hooks/node_modules/.bin/validate-hooks +21 -0
- package/examples/policy-hooks/node_modules/.bin/vitest +21 -0
- package/examples/policy-hooks/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
- package/examples/policy-hooks/package.json +25 -0
- package/examples/policy-hooks/src/index.mjs +275 -0
- package/examples/policy-hooks/test/example.test.mjs +204 -0
- package/examples/policy-hooks/vitest.config.mjs +14 -0
- package/examples/validate-hooks.mjs +154 -0
- package/package.json +29 -24
- package/src/hooks/builtin-hooks.mjs +72 -48
- package/src/hooks/condition-evaluator.mjs +1 -1
- package/src/hooks/define-hook.mjs +25 -9
- package/src/hooks/effect-sandbox-worker.mjs +1 -1
- package/src/hooks/effect-sandbox.mjs +5 -2
- package/src/hooks/file-resolver.mjs +2 -2
- package/src/hooks/hook-executor.mjs +12 -19
- package/src/hooks/policy-pack.mjs +3 -3
- package/src/hooks/query-optimizer.mjs +196 -0
- package/src/hooks/query.mjs +150 -0
- package/src/hooks/schemas.mjs +158 -0
- package/src/hooks/security/path-validator.mjs +1 -1
- package/src/hooks/security/sandbox-restrictions.mjs +2 -2
- package/src/hooks/store-cache.mjs +189 -0
- package/src/hooks/validate.mjs +133 -0
- package/src/index.mjs +62 -0
- package/src/policy-compiler.mjs +503 -0
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# Policy Hooks Example
|
|
2
|
+
|
|
3
|
+
This example demonstrates how to use **@unrdf/hooks** for implementing RDF data policies including access control, data validation, privacy, and provenance tracking.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **ACL Policy**: Access control lists for trusted namespaces
|
|
8
|
+
- **Data Type Policy**: Strict type validation for RDF literals
|
|
9
|
+
- **Privacy Policy**: Automatic data redaction and transformation
|
|
10
|
+
- **Provenance Policy**: Require metadata for audit trails
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
pnpm install
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Usage
|
|
19
|
+
|
|
20
|
+
Run the example:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
pnpm start
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Run tests:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pnpm test
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Example Output
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
š Policy Hooks Example
|
|
36
|
+
|
|
37
|
+
============================================================
|
|
38
|
+
|
|
39
|
+
ā
Test 1: Trusted namespace
|
|
40
|
+
Passed: 4/4
|
|
41
|
+
Failed: 0/4
|
|
42
|
+
|
|
43
|
+
ā Test 2: Untrusted namespace
|
|
44
|
+
Passed: 2/4
|
|
45
|
+
Failed: 2/4
|
|
46
|
+
ā acl-policy: validation failed
|
|
47
|
+
ā provenance-policy: validation failed
|
|
48
|
+
|
|
49
|
+
ā
Test 3: Valid age constraint
|
|
50
|
+
Passed: 4/4
|
|
51
|
+
Failed: 0/4
|
|
52
|
+
|
|
53
|
+
ā Test 4: Invalid age constraint
|
|
54
|
+
Passed: 3/4
|
|
55
|
+
Failed: 1/4
|
|
56
|
+
ā data-type-policy: validation failed
|
|
57
|
+
|
|
58
|
+
š Test 5: Privacy policy transformation
|
|
59
|
+
Status: PASSED
|
|
60
|
+
Original: alice@example.org
|
|
61
|
+
Transformed: [REDACTED]
|
|
62
|
+
|
|
63
|
+
ā Test 6: Missing provenance
|
|
64
|
+
Passed: 3/4
|
|
65
|
+
Failed: 1/4
|
|
66
|
+
ā provenance-policy: validation failed
|
|
67
|
+
|
|
68
|
+
============================================================
|
|
69
|
+
⨠Policy Hooks Example Complete
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Policy Hooks
|
|
73
|
+
|
|
74
|
+
### 1. ACL Policy
|
|
75
|
+
|
|
76
|
+
Validates that RDF quads come from trusted namespaces:
|
|
77
|
+
|
|
78
|
+
```javascript
|
|
79
|
+
const aclPolicy = defineHook({
|
|
80
|
+
name: 'acl-policy',
|
|
81
|
+
trigger: 'before-add',
|
|
82
|
+
validate: quad => {
|
|
83
|
+
const trustedNamespaces = [
|
|
84
|
+
'http://example.org/',
|
|
85
|
+
'http://xmlns.com/foaf/0.1/',
|
|
86
|
+
];
|
|
87
|
+
// Check subject and predicate IRIs
|
|
88
|
+
return trustedNamespaces.some(ns =>
|
|
89
|
+
quad.subject.value.startsWith(ns) ||
|
|
90
|
+
quad.predicate.value.startsWith(ns)
|
|
91
|
+
);
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### 2. Data Type Policy
|
|
97
|
+
|
|
98
|
+
Enforces type constraints on specific predicates:
|
|
99
|
+
|
|
100
|
+
```javascript
|
|
101
|
+
const dataTypePolicy = defineHook({
|
|
102
|
+
name: 'data-type-policy',
|
|
103
|
+
trigger: 'before-add',
|
|
104
|
+
validate: quad => {
|
|
105
|
+
if (quad.predicate.value === 'http://xmlns.com/foaf/0.1/age') {
|
|
106
|
+
const value = parseInt(quad.object.value, 10);
|
|
107
|
+
return !isNaN(value) && value >= 0 && value <= 150;
|
|
108
|
+
}
|
|
109
|
+
return true;
|
|
110
|
+
},
|
|
111
|
+
});
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### 3. Privacy Policy
|
|
115
|
+
|
|
116
|
+
Transforms sensitive data automatically:
|
|
117
|
+
|
|
118
|
+
```javascript
|
|
119
|
+
const privacyPolicy = defineHook({
|
|
120
|
+
name: 'privacy-policy',
|
|
121
|
+
trigger: 'before-add',
|
|
122
|
+
transform: quad => {
|
|
123
|
+
if (quad.predicate.value === 'http://xmlns.com/foaf/0.1/mbox') {
|
|
124
|
+
return DataFactory.quad(
|
|
125
|
+
quad.subject,
|
|
126
|
+
quad.predicate,
|
|
127
|
+
DataFactory.literal('[REDACTED]'),
|
|
128
|
+
quad.graph
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
return quad;
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### 4. Provenance Policy
|
|
137
|
+
|
|
138
|
+
Requires named graphs for audit trails:
|
|
139
|
+
|
|
140
|
+
```javascript
|
|
141
|
+
const provenancePolicy = defineHook({
|
|
142
|
+
name: 'provenance-policy',
|
|
143
|
+
trigger: 'before-add',
|
|
144
|
+
validate: quad => {
|
|
145
|
+
return quad.graph && quad.graph.termType === 'NamedNode';
|
|
146
|
+
},
|
|
147
|
+
});
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## Registry Usage
|
|
151
|
+
|
|
152
|
+
Create and configure a policy registry:
|
|
153
|
+
|
|
154
|
+
```javascript
|
|
155
|
+
import { createHookRegistry, registerHook } from '@unrdf/hooks';
|
|
156
|
+
|
|
157
|
+
const registry = createHookRegistry();
|
|
158
|
+
|
|
159
|
+
registerHook(registry, aclPolicy);
|
|
160
|
+
registerHook(registry, dataTypePolicy);
|
|
161
|
+
registerHook(registry, privacyPolicy);
|
|
162
|
+
registerHook(registry, provenancePolicy);
|
|
163
|
+
|
|
164
|
+
// Execute all policies
|
|
165
|
+
const results = executeHooksByTrigger(registry, store, 'before-add', quad);
|
|
166
|
+
console.log(`Passed: ${results.passed.length}/${results.total}`);
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## Testing
|
|
170
|
+
|
|
171
|
+
The example includes comprehensive tests:
|
|
172
|
+
|
|
173
|
+
- ACL policy validation
|
|
174
|
+
- Data type constraints
|
|
175
|
+
- Privacy transformations
|
|
176
|
+
- Provenance requirements
|
|
177
|
+
- Registry execution
|
|
178
|
+
|
|
179
|
+
Run tests with:
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
pnpm test
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## Learn More
|
|
186
|
+
|
|
187
|
+
- [@unrdf/hooks Documentation](../../README.md)
|
|
188
|
+
- [Hook Chains Example](../hook-chains/)
|
|
189
|
+
- [UNRDF Core](../../../core/)
|
|
190
|
+
|
|
191
|
+
## License
|
|
192
|
+
|
|
193
|
+
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":6.318749999999994,"failed":false}]]}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@unrdf/hooks-example-policy",
|
|
3
|
+
"version": "5.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Policy Hooks Example - RDF data validation and access control",
|
|
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
|
+
}
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Policy Hooks Example
|
|
3
|
+
*
|
|
4
|
+
* Demonstrates:
|
|
5
|
+
* - Defining custom policy hooks
|
|
6
|
+
* - RDF access control policies
|
|
7
|
+
* - Data validation constraints
|
|
8
|
+
* - Hook execution and results
|
|
9
|
+
*
|
|
10
|
+
* @module hooks-example-policy
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { namedNode, literal, quad, createStore, addQuad } from '@unrdf/core';
|
|
14
|
+
import { createStore, dataFactory } from '@unrdf/oxigraph';
|
|
15
|
+
import {
|
|
16
|
+
defineHook,
|
|
17
|
+
createHookRegistry,
|
|
18
|
+
registerHook,
|
|
19
|
+
executeHook,
|
|
20
|
+
executeHooksByTrigger,
|
|
21
|
+
} from '@unrdf/hooks';
|
|
22
|
+
/* ========================================================================= */
|
|
23
|
+
/* Policy Hook Definitions */
|
|
24
|
+
/* ========================================================================= */
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Access Control List (ACL) Policy Hook
|
|
28
|
+
*
|
|
29
|
+
* Only allows quads from trusted namespaces.
|
|
30
|
+
*/
|
|
31
|
+
const aclPolicy = defineHook({
|
|
32
|
+
name: 'acl-policy',
|
|
33
|
+
trigger: 'before-add',
|
|
34
|
+
validate: quad => {
|
|
35
|
+
const trustedNamespaces = [
|
|
36
|
+
'http://example.org/',
|
|
37
|
+
'http://xmlns.com/foaf/0.1/',
|
|
38
|
+
'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
const subjectIRI = quad.subject.termType === 'NamedNode' ? quad.subject.value : '';
|
|
42
|
+
const predicateIRI = quad.predicate.value;
|
|
43
|
+
|
|
44
|
+
return trustedNamespaces.some(ns => subjectIRI.startsWith(ns) || predicateIRI.startsWith(ns));
|
|
45
|
+
},
|
|
46
|
+
metadata: {
|
|
47
|
+
description: 'ACL policy - only allow quads from trusted namespaces',
|
|
48
|
+
policy: 'security',
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Data Type Policy Hook
|
|
54
|
+
*
|
|
55
|
+
* Enforces strict typing on foaf:age - must be integer.
|
|
56
|
+
*/
|
|
57
|
+
const dataTypePolicy = defineHook({
|
|
58
|
+
name: 'data-type-policy',
|
|
59
|
+
trigger: 'before-add',
|
|
60
|
+
validate: quad => {
|
|
61
|
+
if (quad.predicate.value === 'http://xmlns.com/foaf/0.1/age') {
|
|
62
|
+
if (quad.object.termType !== 'Literal') {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
const value = parseInt(quad.object.value, 10);
|
|
66
|
+
return !isNaN(value) && value >= 0 && value <= 150;
|
|
67
|
+
}
|
|
68
|
+
return true;
|
|
69
|
+
},
|
|
70
|
+
metadata: {
|
|
71
|
+
description: 'Data type policy - foaf:age must be valid integer 0-150',
|
|
72
|
+
policy: 'validation',
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Privacy Policy Hook
|
|
78
|
+
*
|
|
79
|
+
* Redacts email addresses unless explicitly allowed.
|
|
80
|
+
*/
|
|
81
|
+
const privacyPolicy = defineHook({
|
|
82
|
+
name: 'privacy-policy',
|
|
83
|
+
trigger: 'before-add',
|
|
84
|
+
transform: quad => {
|
|
85
|
+
if (quad.predicate.value === 'http://xmlns.com/foaf/0.1/mbox') {
|
|
86
|
+
if (quad.object.termType === 'Literal') {
|
|
87
|
+
// Redact email - replace with placeholder
|
|
88
|
+
return dataFactory.quad(
|
|
89
|
+
quad.subject,
|
|
90
|
+
quad.predicate,
|
|
91
|
+
dataFactory.literal('[REDACTED]'),
|
|
92
|
+
quad.graph
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return quad;
|
|
97
|
+
},
|
|
98
|
+
metadata: {
|
|
99
|
+
description: 'Privacy policy - redact email addresses',
|
|
100
|
+
policy: 'privacy',
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Provenance Policy Hook
|
|
106
|
+
*
|
|
107
|
+
* Requires all quads to have provenance metadata.
|
|
108
|
+
*/
|
|
109
|
+
const provenancePolicy = defineHook({
|
|
110
|
+
name: 'provenance-policy',
|
|
111
|
+
trigger: 'before-add',
|
|
112
|
+
validate: quad => {
|
|
113
|
+
// In a real implementation, this would check for provenance metadata
|
|
114
|
+
// For example, check that quad.graph is not the default graph
|
|
115
|
+
return quad.graph && quad.graph.termType === 'NamedNode';
|
|
116
|
+
},
|
|
117
|
+
metadata: {
|
|
118
|
+
description: 'Provenance policy - require provenance metadata',
|
|
119
|
+
policy: 'audit',
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
/* ========================================================================= */
|
|
124
|
+
/* Example Usage */
|
|
125
|
+
/* ========================================================================= */
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Create and configure hook registry with policies.
|
|
129
|
+
*/
|
|
130
|
+
function setupPolicyRegistry() {
|
|
131
|
+
const registry = createHookRegistry();
|
|
132
|
+
|
|
133
|
+
// Register all policy hooks
|
|
134
|
+
registerHook(registry, aclPolicy);
|
|
135
|
+
registerHook(registry, dataTypePolicy);
|
|
136
|
+
registerHook(registry, privacyPolicy);
|
|
137
|
+
registerHook(registry, provenancePolicy);
|
|
138
|
+
|
|
139
|
+
return registry;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Test quads against policy hooks.
|
|
144
|
+
*/
|
|
145
|
+
function testPolicies() {
|
|
146
|
+
const registry = setupPolicyRegistry();
|
|
147
|
+
const store = createStore();
|
|
148
|
+
|
|
149
|
+
console.log('š Policy Hooks Example\n');
|
|
150
|
+
console.log('='.repeat(60));
|
|
151
|
+
|
|
152
|
+
// Test 1: Trusted namespace (should pass ACL)
|
|
153
|
+
console.log('\nā
Test 1: Trusted namespace');
|
|
154
|
+
const q1 = quad(
|
|
155
|
+
namedNode('http://example.org/alice'),
|
|
156
|
+
namedNode('http://xmlns.com/foaf/0.1/name'),
|
|
157
|
+
literal('Alice'),
|
|
158
|
+
namedNode('http://example.org/graph1')
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
const hooks1 = [aclPolicy, dataTypePolicy, privacyPolicy, provenancePolicy];
|
|
162
|
+
const results1 = executeHooksByTrigger(hooks1, 'before-add', q1);
|
|
163
|
+
const passedCount1 = results1.results.filter(r => r.valid).length;
|
|
164
|
+
console.log(` Passed: ${passedCount1}/${results1.results.length}`);
|
|
165
|
+
console.log(` Failed: ${results1.results.length - passedCount1}/${results1.results.length}`);
|
|
166
|
+
if (!results1.valid) {
|
|
167
|
+
results1.results.filter(r => !r.valid).forEach(r => console.log(` ā ${r.hookName}: ${r.error}`));
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Test 2: Untrusted namespace (should fail ACL)
|
|
171
|
+
console.log('\nā Test 2: Untrusted namespace');
|
|
172
|
+
const q2 = quad(
|
|
173
|
+
namedNode('http://untrusted.org/bob'),
|
|
174
|
+
namedNode('http://untrusted.org/property'),
|
|
175
|
+
literal('Bob'),
|
|
176
|
+
namedNode('http://example.org/graph1')
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
const hooks2 = [aclPolicy, dataTypePolicy, privacyPolicy, provenancePolicy];
|
|
180
|
+
const results2 = executeHooksByTrigger(hooks2, 'before-add', q2);
|
|
181
|
+
const passedCount2 = results2.results.filter(r => r.valid).length;
|
|
182
|
+
console.log(` Passed: ${passedCount2}/${results2.results.length}`);
|
|
183
|
+
console.log(` Failed: ${results2.results.length - passedCount2}/${results2.results.length}`);
|
|
184
|
+
if (!results2.valid) {
|
|
185
|
+
results2.results.filter(r => !r.valid).forEach(r => console.log(` ā ${r.hookName}: ${r.error}`));
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Test 3: Valid age (should pass data type policy)
|
|
189
|
+
console.log('\nā
Test 3: Valid age constraint');
|
|
190
|
+
const q3 = quad(
|
|
191
|
+
namedNode('http://example.org/alice'),
|
|
192
|
+
namedNode('http://xmlns.com/foaf/0.1/age'),
|
|
193
|
+
literal('30'),
|
|
194
|
+
namedNode('http://example.org/graph1')
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
const hooks3 = [aclPolicy, dataTypePolicy, privacyPolicy, provenancePolicy];
|
|
198
|
+
const results3 = executeHooksByTrigger(hooks3, 'before-add', q3);
|
|
199
|
+
const passedCount3 = results3.results.filter(r => r.valid).length;
|
|
200
|
+
console.log(` Passed: ${passedCount3}/${results3.results.length}`);
|
|
201
|
+
console.log(` Failed: ${results3.results.length - passedCount3}/${results3.results.length}`);
|
|
202
|
+
|
|
203
|
+
// Test 4: Invalid age (should fail data type policy)
|
|
204
|
+
console.log('\nā Test 4: Invalid age constraint');
|
|
205
|
+
const q4 = quad(
|
|
206
|
+
namedNode('http://example.org/bob'),
|
|
207
|
+
namedNode('http://xmlns.com/foaf/0.1/age'),
|
|
208
|
+
literal('999'),
|
|
209
|
+
namedNode('http://example.org/graph1')
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
const hooks4 = [aclPolicy, dataTypePolicy, privacyPolicy, provenancePolicy];
|
|
213
|
+
const results4 = executeHooksByTrigger(hooks4, 'before-add', q4);
|
|
214
|
+
const passedCount4 = results4.results.filter(r => r.valid).length;
|
|
215
|
+
console.log(` Passed: ${passedCount4}/${results4.results.length}`);
|
|
216
|
+
console.log(` Failed: ${results4.results.length - passedCount4}/${results4.results.length}`);
|
|
217
|
+
if (!results4.valid) {
|
|
218
|
+
results4.results.filter(r => !r.valid).forEach(r => console.log(` ā ${r.hookName}: ${r.error}`));
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Test 5: Email privacy transformation
|
|
222
|
+
console.log('\nš Test 5: Privacy policy transformation');
|
|
223
|
+
const q5 = quad(
|
|
224
|
+
namedNode('http://example.org/alice'),
|
|
225
|
+
namedNode('http://xmlns.com/foaf/0.1/mbox'),
|
|
226
|
+
literal('alice@example.org'),
|
|
227
|
+
namedNode('http://example.org/graph1')
|
|
228
|
+
);
|
|
229
|
+
|
|
230
|
+
const privacyResult = executeHook(privacyPolicy, q5);
|
|
231
|
+
console.log(` Status: ${privacyResult.valid ? 'PASSED' : 'FAILED'}`);
|
|
232
|
+
if (privacyResult.quad && privacyResult.quad.object.value !== q5.object.value) {
|
|
233
|
+
console.log(` Original: ${q5.object.value}`);
|
|
234
|
+
console.log(` Transformed: ${privacyResult.quad.object.value}`);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Test 6: Missing provenance (should fail provenance policy)
|
|
238
|
+
console.log('\nā Test 6: Missing provenance');
|
|
239
|
+
const q6 = quad(
|
|
240
|
+
namedNode('http://example.org/charlie'),
|
|
241
|
+
namedNode('http://xmlns.com/foaf/0.1/name'),
|
|
242
|
+
literal('Charlie')
|
|
243
|
+
// No graph parameter - uses default graph
|
|
244
|
+
);
|
|
245
|
+
|
|
246
|
+
const hooks6 = [aclPolicy, dataTypePolicy, privacyPolicy, provenancePolicy];
|
|
247
|
+
const results6 = executeHooksByTrigger(hooks6, 'before-add', q6);
|
|
248
|
+
const passedCount6 = results6.results.filter(r => r.valid).length;
|
|
249
|
+
console.log(` Passed: ${passedCount6}/${results6.results.length}`);
|
|
250
|
+
console.log(` Failed: ${results6.results.length - passedCount6}/${results6.results.length}`);
|
|
251
|
+
if (!results6.valid) {
|
|
252
|
+
results6.results.filter(r => !r.valid).forEach(r => console.log(` ā ${r.hookName}: ${r.error}`));
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
console.log('\n' + '='.repeat(60));
|
|
256
|
+
console.log('⨠Policy Hooks Example Complete\n');
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/* ========================================================================= */
|
|
260
|
+
/* Export API */
|
|
261
|
+
/* ========================================================================= */
|
|
262
|
+
|
|
263
|
+
export {
|
|
264
|
+
aclPolicy,
|
|
265
|
+
dataTypePolicy,
|
|
266
|
+
privacyPolicy,
|
|
267
|
+
provenancePolicy,
|
|
268
|
+
setupPolicyRegistry,
|
|
269
|
+
testPolicies,
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
// Run example if executed directly
|
|
273
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
274
|
+
testPolicies();
|
|
275
|
+
}
|