@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,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
+ }