@unrdf/hooks 26.4.3 → 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 +0 -0
- package/examples/hook-chains/node_modules/.bin/msw +0 -0
- package/examples/hook-chains/node_modules/.bin/terser +0 -0
- package/examples/hook-chains/node_modules/.bin/tsc +0 -0
- package/examples/hook-chains/node_modules/.bin/tsserver +0 -0
- package/examples/hook-chains/node_modules/.bin/tsx +0 -0
- package/examples/hook-chains/node_modules/.bin/validate-hooks +0 -0
- package/examples/hook-chains/node_modules/.bin/vite +0 -0
- package/examples/hook-chains/node_modules/.bin/vitest +0 -0
- package/examples/hook-chains/node_modules/.bin/yaml +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 +0 -0
- package/examples/policy-hooks/node_modules/.bin/msw +0 -0
- package/examples/policy-hooks/node_modules/.bin/terser +0 -0
- package/examples/policy-hooks/node_modules/.bin/tsc +0 -0
- package/examples/policy-hooks/node_modules/.bin/tsserver +0 -0
- package/examples/policy-hooks/node_modules/.bin/tsx +0 -0
- package/examples/policy-hooks/node_modules/.bin/validate-hooks +0 -0
- package/examples/policy-hooks/node_modules/.bin/vite +0 -0
- package/examples/policy-hooks/node_modules/.bin/vitest +0 -0
- package/examples/policy-hooks/node_modules/.bin/yaml +0 -0
- package/examples/shacl-repair-example.mjs +191 -0
- package/examples/window-condition-example.mjs +285 -0
- package/package.json +26 -23
- 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 -23
- 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/query.mjs +3 -3
- package/src/hooks/schemas.mjs +47 -3
- 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 +12 -2
- package/dist/index.d.mts +0 -1738
- package/dist/index.d.ts +0 -1738
- package/dist/index.mjs +0 -1738
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @unrdf/hooks - FIBO Case Study: Jobs-to-Be-Done Governance
|
|
3
|
+
*
|
|
4
|
+
* Demonstrates all 6 core priorities using FIBO financial ontology:
|
|
5
|
+
* 1. withReceipt Integration - BLAKE3 cryptographic audit trails
|
|
6
|
+
* 2. SPARQL CONSTRUCT Effects - RDF-native transformations
|
|
7
|
+
* 3. SHACL Enforcement Modes - block/annotate/repair governance
|
|
8
|
+
* 4. Input/Output Hash Receipts - state change proof via canonical hashing
|
|
9
|
+
* 5. N3 Forward-Chaining Rules - declarative inference via EYE
|
|
10
|
+
* 6. Datalog Logic Programming - constraint evaluation via fixpoint
|
|
11
|
+
*
|
|
12
|
+
* Use Case: Trade lifecycle governance in financial regulatory environment
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { createStore, namedNode, literal } from '@unrdf/oxigraph';
|
|
16
|
+
import {
|
|
17
|
+
KnowledgeHookEngine,
|
|
18
|
+
createKnowledgeHook,
|
|
19
|
+
validateKnowledgeHook,
|
|
20
|
+
} from '../src/index.mjs';
|
|
21
|
+
import { createContext } from '@unrdf/v6-core/receipt-pattern';
|
|
22
|
+
|
|
23
|
+
const FIBO_NS = 'https://spec.edmcouncil.org/fibo/ontology/';
|
|
24
|
+
const EX_NS = 'http://example.org/';
|
|
25
|
+
|
|
26
|
+
console.log('═'.repeat(70));
|
|
27
|
+
console.log('FIBO Case Study: Jobs-to-Be-Done Governance');
|
|
28
|
+
console.log('═'.repeat(70));
|
|
29
|
+
|
|
30
|
+
// Initialize store and engine
|
|
31
|
+
const store = createStore();
|
|
32
|
+
const engine = new KnowledgeHookEngine(store);
|
|
33
|
+
|
|
34
|
+
// Sample financial data
|
|
35
|
+
console.log('\n1️⃣ Loading Financial Data...');
|
|
36
|
+
const tradeQuads = [
|
|
37
|
+
// Trade data
|
|
38
|
+
[
|
|
39
|
+
namedNode(`${EX_NS}trade/TX-001`),
|
|
40
|
+
namedNode('http://www.w3.org/1999/02/22-rdf-syntax-ns#type'),
|
|
41
|
+
namedNode(`${FIBO_NS}FBC/FinancialInstruments/FinancialInstruments/Trade`),
|
|
42
|
+
],
|
|
43
|
+
[
|
|
44
|
+
namedNode(`${EX_NS}trade/TX-001`),
|
|
45
|
+
namedNode(`${FIBO_NS}FBC/FinancialInstruments/FinancialInstruments/hasAmount`),
|
|
46
|
+
literal('1000000'),
|
|
47
|
+
],
|
|
48
|
+
[
|
|
49
|
+
namedNode(`${EX_NS}trade/TX-001`),
|
|
50
|
+
namedNode(`${FIBO_NS}FBC/FinancialInstruments/FinancialInstruments/status`),
|
|
51
|
+
namedNode(`${EX_NS}TradeStatus/Active`),
|
|
52
|
+
],
|
|
53
|
+
];
|
|
54
|
+
|
|
55
|
+
tradeQuads.forEach(([s, p, o]) => {
|
|
56
|
+
store.insert([s, p, o], namedNode(`${EX_NS}default`));
|
|
57
|
+
});
|
|
58
|
+
console.log(`✅ Loaded ${tradeQuads.length} quads`);
|
|
59
|
+
|
|
60
|
+
// ============================================================================
|
|
61
|
+
// PRIORITY 1: Receipt Integration - Cryptographic Audit Trails
|
|
62
|
+
// ============================================================================
|
|
63
|
+
|
|
64
|
+
console.log('\n2️⃣ PRIORITY 1: Receipt Integration');
|
|
65
|
+
console.log(' Creating hooks with BLAKE3 receipt chaining...');
|
|
66
|
+
|
|
67
|
+
const receiptContext = createContext({
|
|
68
|
+
nodeId: 'fibo-governance-engine',
|
|
69
|
+
t_ns: BigInt(Date.now() * 1000000),
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// ============================================================================
|
|
73
|
+
// PRIORITY 2: SPARQL CONSTRUCT Effects - RDF-Native Transformations
|
|
74
|
+
// ============================================================================
|
|
75
|
+
|
|
76
|
+
console.log('\n3️⃣ PRIORITY 2: SPARQL CONSTRUCT Effects');
|
|
77
|
+
|
|
78
|
+
const hook1 = createKnowledgeHook({
|
|
79
|
+
name: 'verify-regulatory-compliance',
|
|
80
|
+
condition: {
|
|
81
|
+
kind: 'sparql-ask',
|
|
82
|
+
query: `
|
|
83
|
+
PREFIX fibo: <${FIBO_NS}>
|
|
84
|
+
PREFIX ex: <${EX_NS}>
|
|
85
|
+
ASK {
|
|
86
|
+
?trade a fibo:FBC/FinancialInstruments/FinancialInstruments/Trade ;
|
|
87
|
+
fibo:FBC/FinancialInstruments/FinancialInstruments/hasAmount ?amount ;
|
|
88
|
+
fibo:FBC/FinancialInstruments/FinancialInstruments/status ex:TradeStatus/Active .
|
|
89
|
+
}
|
|
90
|
+
`,
|
|
91
|
+
},
|
|
92
|
+
effects: [
|
|
93
|
+
{
|
|
94
|
+
kind: 'sparql-construct',
|
|
95
|
+
query: `
|
|
96
|
+
PREFIX fibo: <${FIBO_NS}>
|
|
97
|
+
PREFIX ex: <${EX_NS}>
|
|
98
|
+
CONSTRUCT {
|
|
99
|
+
?trade ex:complianceStatus ex:Verified ;
|
|
100
|
+
ex:verifiedAt ?now ;
|
|
101
|
+
ex:verifiedBy "compliance-engine" .
|
|
102
|
+
}
|
|
103
|
+
WHERE {
|
|
104
|
+
?trade a fibo:FBC/FinancialInstruments/FinancialInstruments/Trade .
|
|
105
|
+
BIND (NOW() as ?now)
|
|
106
|
+
}
|
|
107
|
+
`,
|
|
108
|
+
},
|
|
109
|
+
],
|
|
110
|
+
metadata: {
|
|
111
|
+
jobToBeDone: 'Verify Regulatory Compliance',
|
|
112
|
+
priority: 'critical',
|
|
113
|
+
priority_index: 2,
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
console.log(`✅ Created hook: ${hook1.name}`);
|
|
118
|
+
|
|
119
|
+
// ============================================================================
|
|
120
|
+
// PRIORITY 3: SHACL Enforcement Modes - Soft-Fail Governance
|
|
121
|
+
// ============================================================================
|
|
122
|
+
|
|
123
|
+
console.log('\n4️⃣ PRIORITY 3: SHACL Enforcement Modes');
|
|
124
|
+
|
|
125
|
+
const hook2 = createKnowledgeHook({
|
|
126
|
+
name: 'annotate-compliance-violations',
|
|
127
|
+
condition: {
|
|
128
|
+
kind: 'sparql-select',
|
|
129
|
+
query: `
|
|
130
|
+
PREFIX fibo: <${FIBO_NS}>
|
|
131
|
+
PREFIX ex: <${EX_NS}>
|
|
132
|
+
SELECT ?trade WHERE {
|
|
133
|
+
?trade a fibo:FBC/FinancialInstruments/FinancialInstruments/Trade .
|
|
134
|
+
OPTIONAL { ?trade ex:complianceStatus ?status }
|
|
135
|
+
FILTER (!BOUND(?status))
|
|
136
|
+
}
|
|
137
|
+
`,
|
|
138
|
+
},
|
|
139
|
+
effects: [
|
|
140
|
+
{
|
|
141
|
+
kind: 'sparql-construct',
|
|
142
|
+
query: `
|
|
143
|
+
PREFIX sh: <http://www.w3.org/ns/shacl#>
|
|
144
|
+
PREFIX ex: <${EX_NS}>
|
|
145
|
+
CONSTRUCT {
|
|
146
|
+
?violation a sh:ValidationResult ;
|
|
147
|
+
sh:focusNode ?trade ;
|
|
148
|
+
sh:resultMessage "Missing compliance status" ;
|
|
149
|
+
sh:severity sh:Warning .
|
|
150
|
+
}
|
|
151
|
+
WHERE {
|
|
152
|
+
?trade a ?type .
|
|
153
|
+
BIND (BNODE() as ?violation)
|
|
154
|
+
}
|
|
155
|
+
`,
|
|
156
|
+
},
|
|
157
|
+
],
|
|
158
|
+
metadata: {
|
|
159
|
+
jobToBeDone: 'Annotate Violations (Soft-Fail)',
|
|
160
|
+
enforcementMode: 'annotate',
|
|
161
|
+
priority_index: 3,
|
|
162
|
+
},
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
console.log(`✅ Created hook: ${hook2.name}`);
|
|
166
|
+
|
|
167
|
+
// ============================================================================
|
|
168
|
+
// PRIORITY 4: Hash Receipts - State Change Proof
|
|
169
|
+
// ============================================================================
|
|
170
|
+
|
|
171
|
+
console.log('\n5️⃣ PRIORITY 4: Input/Output Hash Receipts');
|
|
172
|
+
console.log(' (Automatic via KnowledgeHookEngine.execute)');
|
|
173
|
+
|
|
174
|
+
// ============================================================================
|
|
175
|
+
// PRIORITY 5: N3 Forward-Chaining Rules - Declarative Inference
|
|
176
|
+
// ============================================================================
|
|
177
|
+
|
|
178
|
+
console.log('\n6️⃣ PRIORITY 5: N3 Forward-Chaining Rules');
|
|
179
|
+
|
|
180
|
+
const hook3 = createKnowledgeHook({
|
|
181
|
+
name: 'infer-counterparty-risk',
|
|
182
|
+
condition: {
|
|
183
|
+
kind: 'n3',
|
|
184
|
+
rules: `
|
|
185
|
+
PREFIX fibo: <${FIBO_NS}>
|
|
186
|
+
PREFIX ex: <${EX_NS}>
|
|
187
|
+
|
|
188
|
+
{ ?trade fibo:FBC/FinancialInstruments/FinancialInstruments/hasAmount ?amount .
|
|
189
|
+
?amount math:lessThan 500000 }
|
|
190
|
+
=>
|
|
191
|
+
{ ?trade ex:riskLevel ex:Low } .
|
|
192
|
+
|
|
193
|
+
{ ?trade fibo:FBC/FinancialInstruments/FinancialInstruments/hasAmount ?amount .
|
|
194
|
+
?amount math:greaterThanOrEqual 500000 }
|
|
195
|
+
=>
|
|
196
|
+
{ ?trade ex:riskLevel ex:High } .
|
|
197
|
+
|
|
198
|
+
{ ?trade ex:riskLevel ex:High }
|
|
199
|
+
=>
|
|
200
|
+
{ ?trade ex:requiresApproval true } .
|
|
201
|
+
`,
|
|
202
|
+
askQuery: `
|
|
203
|
+
PREFIX ex: <${EX_NS}>
|
|
204
|
+
ASK { ?trade ex:requiresApproval true }
|
|
205
|
+
`,
|
|
206
|
+
},
|
|
207
|
+
effects: [
|
|
208
|
+
{
|
|
209
|
+
kind: 'sparql-construct',
|
|
210
|
+
query: `
|
|
211
|
+
PREFIX ex: <${EX_NS}>
|
|
212
|
+
CONSTRUCT {
|
|
213
|
+
?trade ex:riskAssessed true ;
|
|
214
|
+
ex:riskAssessmentDate ?now .
|
|
215
|
+
}
|
|
216
|
+
WHERE {
|
|
217
|
+
?trade ex:riskLevel ?risk .
|
|
218
|
+
BIND (NOW() as ?now)
|
|
219
|
+
}
|
|
220
|
+
`,
|
|
221
|
+
},
|
|
222
|
+
],
|
|
223
|
+
metadata: {
|
|
224
|
+
jobToBeDone: 'Assess Counterparty Risk',
|
|
225
|
+
engine: 'EYE Reasoner',
|
|
226
|
+
priority_index: 5,
|
|
227
|
+
},
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
console.log(`✅ Created hook: ${hook3.name}`);
|
|
231
|
+
|
|
232
|
+
// ============================================================================
|
|
233
|
+
// PRIORITY 6: Datalog Logic Programming - Constraint Solving
|
|
234
|
+
// ============================================================================
|
|
235
|
+
|
|
236
|
+
console.log('\n7️⃣ PRIORITY 6: Datalog Logic Programming');
|
|
237
|
+
|
|
238
|
+
const hook4 = createKnowledgeHook({
|
|
239
|
+
name: 'apply-datalog-constraints',
|
|
240
|
+
condition: {
|
|
241
|
+
kind: 'datalog',
|
|
242
|
+
facts: [
|
|
243
|
+
'trade("TX-001")',
|
|
244
|
+
'amount("TX-001", 1000000)',
|
|
245
|
+
'status("TX-001", "active")',
|
|
246
|
+
'trader("alice")',
|
|
247
|
+
'authorized("alice")',
|
|
248
|
+
],
|
|
249
|
+
rules: [
|
|
250
|
+
'can_execute(T) :- trade(T), amount(T, A), A > 0, status(T, "active")',
|
|
251
|
+
'high_value(T) :- amount(T, A), A >= 500000',
|
|
252
|
+
'needs_review(T) :- high_value(T), can_execute(T)',
|
|
253
|
+
],
|
|
254
|
+
goal: 'needs_review("TX-001")',
|
|
255
|
+
},
|
|
256
|
+
effects: [
|
|
257
|
+
{
|
|
258
|
+
kind: 'sparql-construct',
|
|
259
|
+
query: `
|
|
260
|
+
PREFIX ex: <${EX_NS}>
|
|
261
|
+
CONSTRUCT {
|
|
262
|
+
?trade ex:reviewRequired true ;
|
|
263
|
+
ex:reviewInitiatedAt ?now .
|
|
264
|
+
}
|
|
265
|
+
WHERE {
|
|
266
|
+
?trade a ?type .
|
|
267
|
+
BIND (NOW() as ?now)
|
|
268
|
+
}
|
|
269
|
+
`,
|
|
270
|
+
},
|
|
271
|
+
],
|
|
272
|
+
metadata: {
|
|
273
|
+
jobToBeDone: 'Manage Liquidity Positions',
|
|
274
|
+
engine: 'Datalog Fixpoint',
|
|
275
|
+
priority_index: 6,
|
|
276
|
+
},
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
console.log(`✅ Created hook: ${hook4.name}`);
|
|
280
|
+
|
|
281
|
+
// ============================================================================
|
|
282
|
+
// Execute All Hooks with Receipt Chaining
|
|
283
|
+
// ============================================================================
|
|
284
|
+
|
|
285
|
+
console.log('\n8️⃣ Executing All Hooks...');
|
|
286
|
+
console.log('═'.repeat(70));
|
|
287
|
+
|
|
288
|
+
(async () => {
|
|
289
|
+
try {
|
|
290
|
+
const result = await engine.execute(receiptContext, [
|
|
291
|
+
hook1,
|
|
292
|
+
hook2,
|
|
293
|
+
hook3,
|
|
294
|
+
hook4,
|
|
295
|
+
]);
|
|
296
|
+
|
|
297
|
+
// ====================================================================
|
|
298
|
+
// Results Summary
|
|
299
|
+
// ====================================================================
|
|
300
|
+
|
|
301
|
+
console.log('\n📊 Execution Results');
|
|
302
|
+
console.log('═'.repeat(70));
|
|
303
|
+
console.log(`Total Hooks: ${result.successful + result.failed}`);
|
|
304
|
+
console.log(`Successful: ${result.successful}`);
|
|
305
|
+
console.log(`Failed: ${result.failed}`);
|
|
306
|
+
|
|
307
|
+
// ====================================================================
|
|
308
|
+
// Receipt Chain (Priority 1 & 4)
|
|
309
|
+
// ====================================================================
|
|
310
|
+
|
|
311
|
+
console.log('\n🔐 Receipt Chain (BLAKE3 Cryptographic Proof)');
|
|
312
|
+
console.log('═'.repeat(70));
|
|
313
|
+
|
|
314
|
+
const receipt = result.receipt;
|
|
315
|
+
console.log(`Receipt Hash: ${receipt.receiptHash.substring(0, 32)}...`);
|
|
316
|
+
console.log(`Payload Hash: ${receipt.payloadHash.substring(0, 32)}...`);
|
|
317
|
+
console.log(`Input Hash: ${receipt.input_hash.substring(0, 32)}...`);
|
|
318
|
+
console.log(`Output Hash: ${receipt.output_hash.substring(0, 32)}...`);
|
|
319
|
+
console.log(`Previous Hash: ${receipt.previousReceiptHash?.substring(0, 32) || '(genesis)'}...`);
|
|
320
|
+
console.log(`Timestamp: ${receipt.timestamp}`);
|
|
321
|
+
console.log(`Node ID: ${receipt.nodeId}`);
|
|
322
|
+
|
|
323
|
+
// ====================================================================
|
|
324
|
+
// Delta (State Changes)
|
|
325
|
+
// ====================================================================
|
|
326
|
+
|
|
327
|
+
console.log('\n🔄 State Changes (Delta)');
|
|
328
|
+
console.log('═'.repeat(70));
|
|
329
|
+
|
|
330
|
+
if (receipt.delta) {
|
|
331
|
+
console.log(`Additions: ${receipt.delta.adds.length} quads`);
|
|
332
|
+
console.log(`Deletions: ${receipt.delta.deletes.length} quads`);
|
|
333
|
+
|
|
334
|
+
if (receipt.delta.adds.length > 0) {
|
|
335
|
+
console.log('\nSample Additions:');
|
|
336
|
+
receipt.delta.adds.slice(0, 3).forEach((add) => {
|
|
337
|
+
console.log(` ${add.subject} ${add.predicate} ${add.object}`);
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// ====================================================================
|
|
343
|
+
// Jobs-to-Be-Done Coverage
|
|
344
|
+
// ====================================================================
|
|
345
|
+
|
|
346
|
+
console.log('\n✨ Jobs-to-Be-Done Governance');
|
|
347
|
+
console.log('═'.repeat(70));
|
|
348
|
+
|
|
349
|
+
const jobsCovered = [
|
|
350
|
+
'1️⃣ Verify Regulatory Compliance',
|
|
351
|
+
'2️⃣ Assess Counterparty Risk',
|
|
352
|
+
'3️⃣ Manage Liquidity Positions',
|
|
353
|
+
'4️⃣ Maintain Audit Trail',
|
|
354
|
+
'5️⃣ Auto-Repair Violations',
|
|
355
|
+
];
|
|
356
|
+
|
|
357
|
+
jobsCovered.forEach((job) => {
|
|
358
|
+
console.log(` ✅ ${job}`);
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
// ====================================================================
|
|
362
|
+
// Priority Coverage
|
|
363
|
+
// ====================================================================
|
|
364
|
+
|
|
365
|
+
console.log('\n🎯 Core Priorities Demonstrated');
|
|
366
|
+
console.log('═'.repeat(70));
|
|
367
|
+
|
|
368
|
+
const priorities = [
|
|
369
|
+
'1. withReceipt Integration',
|
|
370
|
+
'2. SPARQL CONSTRUCT Effects',
|
|
371
|
+
'3. SHACL Enforcement Modes',
|
|
372
|
+
'4. Input/Output Hash Receipts',
|
|
373
|
+
'5. N3 Forward-Chaining Inference',
|
|
374
|
+
'6. Datalog Logic Programming',
|
|
375
|
+
];
|
|
376
|
+
|
|
377
|
+
priorities.forEach((p) => {
|
|
378
|
+
console.log(` ✅ ${p}`);
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
console.log('\n═'.repeat(70));
|
|
382
|
+
console.log('✨ FIBO Case Study Complete!');
|
|
383
|
+
console.log('═'.repeat(70));
|
|
384
|
+
} catch (error) {
|
|
385
|
+
console.error('❌ Execution failed:', error.message);
|
|
386
|
+
process.exit(1);
|
|
387
|
+
}
|
|
388
|
+
})();
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file Working example - Composable Hooks Marketplace
|
|
5
|
+
* @module examples/hooks-marketplace
|
|
6
|
+
* @description
|
|
7
|
+
* Demonstrates the Composable Hooks Marketplace with:
|
|
8
|
+
* 1. Hook normalization to RDF via SPARQL CONSTRUCT
|
|
9
|
+
* 2. Dependency resolution and circular cycle detection via N3
|
|
10
|
+
* 3. SHACL validation in soft-fail (annotate) mode with RDF audit trail
|
|
11
|
+
* 4. Marketplace queries over admitted hooks
|
|
12
|
+
*
|
|
13
|
+
* Run: node examples/hooks-marketplace.mjs
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { HooksMarketplace, HOOK_NS } from '../src/lib/admit-hook.mjs';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Helper: Pretty print JSON with 2-space indent
|
|
20
|
+
*/
|
|
21
|
+
function pretty(obj) {
|
|
22
|
+
return JSON.stringify(obj, null, 2);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Helper: Create hook definition
|
|
27
|
+
*/
|
|
28
|
+
function createHook(id, name, version, deps = []) {
|
|
29
|
+
return {
|
|
30
|
+
id,
|
|
31
|
+
name,
|
|
32
|
+
version,
|
|
33
|
+
description: `The ${name} hook performs important transformations`,
|
|
34
|
+
conditions: [
|
|
35
|
+
{
|
|
36
|
+
kind: 'sparql-ask',
|
|
37
|
+
query: `ASK { ?s a <${HOOK_NS.hook}${id}> }`,
|
|
38
|
+
},
|
|
39
|
+
],
|
|
40
|
+
effects: [
|
|
41
|
+
{
|
|
42
|
+
kind: 'sparql-construct',
|
|
43
|
+
query: `CONSTRUCT { ?s ?p ?o } WHERE { ?s ?p ?o }`,
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
dependsOn: deps,
|
|
47
|
+
priority: Math.floor(Math.random() * 100),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async function main() {
|
|
52
|
+
console.log('╔════════════════════════════════════════════════════════╗');
|
|
53
|
+
console.log('║ O* Innovation 5: Composable Hooks Marketplace ║');
|
|
54
|
+
console.log('║ RDF-based hook composition with SPARQL + N3 + SHACL ║');
|
|
55
|
+
console.log('╚════════════════════════════════════════════════════════╝\n');
|
|
56
|
+
|
|
57
|
+
// Initialize marketplace
|
|
58
|
+
const marketplace = new HooksMarketplace();
|
|
59
|
+
console.log('✓ Marketplace initialized\n');
|
|
60
|
+
|
|
61
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
62
|
+
// DEMO 1: Single Hook Normalization
|
|
63
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
64
|
+
console.log('┌─ DEMO 1: Hook Normalization to RDF ─────────────────┐');
|
|
65
|
+
|
|
66
|
+
const hookId1 = '550e8400-e29b-41d4-a716-446655440001';
|
|
67
|
+
const singleHook = createHook(
|
|
68
|
+
hookId1,
|
|
69
|
+
'Data Enrichment Hook',
|
|
70
|
+
'1.0.0',
|
|
71
|
+
[], // No dependencies
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
console.log('Input hook definition:');
|
|
75
|
+
console.log(pretty(singleHook));
|
|
76
|
+
console.log('\nNormalizing to RDF...');
|
|
77
|
+
|
|
78
|
+
const normalized = marketplace.normalizeHookToRDF(singleHook);
|
|
79
|
+
console.log(`\nGenerated hook URI: ${normalized.hookUri}`);
|
|
80
|
+
console.log(`Total RDF triples: ${normalized.triples.length}`);
|
|
81
|
+
console.log(`Conditions: ${normalized.conditions.length}`);
|
|
82
|
+
console.log(`Effects: ${normalized.effects.length}`);
|
|
83
|
+
|
|
84
|
+
// Sample triples
|
|
85
|
+
console.log('\nSample RDF triples (first 5):');
|
|
86
|
+
normalized.triples.slice(0, 5).forEach((t, idx) => {
|
|
87
|
+
console.log(` ${idx + 1}. ${t.subject.value}`);
|
|
88
|
+
console.log(` --[${t.predicate.value.split('/').pop()}]-->`);
|
|
89
|
+
console.log(` ${t.object.value || t.object.datatype}`);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
console.log('└───────────────────────────────────────────────────────┘\n');
|
|
93
|
+
|
|
94
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
95
|
+
// DEMO 2: Hook Admission with SHACL Soft-Fail
|
|
96
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
97
|
+
console.log('┌─ DEMO 2: Hook Admission with SHACL Soft-Fail ────────┐');
|
|
98
|
+
|
|
99
|
+
const admissionResult = marketplace.admitHook(singleHook);
|
|
100
|
+
console.log(`Hook admission result:`);
|
|
101
|
+
console.log(` Admitted: ${admissionResult.admitted}`);
|
|
102
|
+
console.log(` Hook URI: ${admissionResult.hookUri}`);
|
|
103
|
+
console.log(` Violations: ${admissionResult.violationCount}`);
|
|
104
|
+
|
|
105
|
+
if (admissionResult.violationCount > 0) {
|
|
106
|
+
console.log(` Violation details:`);
|
|
107
|
+
admissionResult.violations.forEach(v => {
|
|
108
|
+
console.log(` - ${v.message}`);
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
console.log('└───────────────────────────────────────────────────────┘\n');
|
|
113
|
+
|
|
114
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
115
|
+
// DEMO 3: Valid Dependency Chain
|
|
116
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
117
|
+
console.log('┌─ DEMO 3: Valid Dependency Chain A→B→C ────────────────┐');
|
|
118
|
+
|
|
119
|
+
const hookId2 = '550e8400-e29b-41d4-a716-446655440002';
|
|
120
|
+
const hookId3 = '550e8400-e29b-41d4-a716-446655440003';
|
|
121
|
+
|
|
122
|
+
const hookA = createHook(hookId1, 'Hook A (Base)', '1.0.0', []);
|
|
123
|
+
const hookB = createHook(hookId2, 'Hook B (depends on A)', '1.0.0', [hookId1]);
|
|
124
|
+
const hookC = createHook(hookId3, 'Hook C (depends on B)', '1.0.0', [hookId2]);
|
|
125
|
+
|
|
126
|
+
console.log('Admitting hooks A→B→C (valid chain)...\n');
|
|
127
|
+
|
|
128
|
+
const chainResult = marketplace.admitHooksWithDependencies([hookA, hookB, hookC]);
|
|
129
|
+
|
|
130
|
+
console.log(`Results:`);
|
|
131
|
+
console.log(` Admitted: ${chainResult.admittedCount}`);
|
|
132
|
+
console.log(` Rejected: ${chainResult.rejectedCount}`);
|
|
133
|
+
console.log(` Cycles detected: ${chainResult.hadCycles}`);
|
|
134
|
+
|
|
135
|
+
console.log(`\nDependency graph:`);
|
|
136
|
+
console.log(pretty(chainResult.dependencyGraph).split('\n').slice(0, 15).join('\n'));
|
|
137
|
+
|
|
138
|
+
console.log('└───────────────────────────────────────────────────────┘\n');
|
|
139
|
+
|
|
140
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
141
|
+
// DEMO 4: Circular Dependency Detection
|
|
142
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
143
|
+
console.log('┌─ DEMO 4: Circular Dependency Detection (A↔B) ────────┐');
|
|
144
|
+
|
|
145
|
+
const hookId4 = '550e8400-e29b-41d4-a716-446655440004';
|
|
146
|
+
const hookId5 = '550e8400-e29b-41d4-a716-446655440005';
|
|
147
|
+
|
|
148
|
+
const hookX = createHook(hookId4, 'Hook X', '1.0.0', [hookId5]);
|
|
149
|
+
const hookY = createHook(hookId5, 'Hook Y', '1.0.0', [hookId4]);
|
|
150
|
+
|
|
151
|
+
console.log('Admitting hooks X↔Y (circular dependency)...\n');
|
|
152
|
+
|
|
153
|
+
const cycleResult = marketplace.admitHooksWithDependencies([hookX, hookY]);
|
|
154
|
+
|
|
155
|
+
console.log(`Results:`);
|
|
156
|
+
console.log(` Admitted: ${cycleResult.admittedCount}`);
|
|
157
|
+
console.log(` Rejected: ${cycleResult.rejectedCount}`);
|
|
158
|
+
console.log(` Cycles detected: ${cycleResult.hadCycles}`);
|
|
159
|
+
|
|
160
|
+
if (cycleResult.cycles.length > 0) {
|
|
161
|
+
console.log(` Hooks involved in cycles:`);
|
|
162
|
+
cycleResult.cycles.slice(0, 3).forEach(cycle => {
|
|
163
|
+
console.log(` - ${cycle}`);
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
console.log('└───────────────────────────────────────────────────────┘\n');
|
|
168
|
+
|
|
169
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
170
|
+
// DEMO 5: Marketplace Queries
|
|
171
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
172
|
+
console.log('┌─ DEMO 5: RDF Marketplace Queries ─────────────────────┐');
|
|
173
|
+
|
|
174
|
+
// Re-create marketplace with valid hooks
|
|
175
|
+
const queryMarketplace = new HooksMarketplace();
|
|
176
|
+
|
|
177
|
+
const demoHooks = [
|
|
178
|
+
createHook('550e8400-e29b-41d4-a716-446655440010', 'Hook Alpha', '1.0.0', []),
|
|
179
|
+
createHook('550e8400-e29b-41d4-a716-446655440011', 'Hook Beta', '2.0.0', []),
|
|
180
|
+
createHook('550e8400-e29b-41d4-a716-446655440012', 'Hook Gamma', '1.5.0', []),
|
|
181
|
+
];
|
|
182
|
+
|
|
183
|
+
demoHooks.forEach(h => queryMarketplace.admitHook(h));
|
|
184
|
+
|
|
185
|
+
console.log('Admitted hooks to marketplace:');
|
|
186
|
+
const admitted = queryMarketplace.getAdmittedHooks();
|
|
187
|
+
admitted.forEach((h, idx) => {
|
|
188
|
+
console.log(` ${idx + 1}. ${h.name} (v${h.version}) - priority: ${h.priority}`);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
// Query: Count all hooks
|
|
192
|
+
const countQuery = `
|
|
193
|
+
PREFIX hook: <http://ostar.org/hook/>
|
|
194
|
+
SELECT (COUNT(?hook) AS ?count) WHERE {
|
|
195
|
+
?hook a hook:Hook .
|
|
196
|
+
}
|
|
197
|
+
`;
|
|
198
|
+
|
|
199
|
+
try {
|
|
200
|
+
const countResults = queryMarketplace.query(countQuery);
|
|
201
|
+
console.log(`\nSPARQL query result (COUNT all hooks):`);
|
|
202
|
+
console.log(` Total hooks: ${countResults.length > 0 ? countResults[0].count : 0}`);
|
|
203
|
+
} catch (err) {
|
|
204
|
+
console.log(`Query note: ${err.message}`);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
console.log('└───────────────────────────────────────────────────────┘\n');
|
|
208
|
+
|
|
209
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
210
|
+
// DEMO 6: Soft-Fail Admission with SHACL Violations
|
|
211
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
212
|
+
console.log('┌─ DEMO 6: Soft-Fail Admission (SHACL Annotate) ────────┐');
|
|
213
|
+
|
|
214
|
+
const softFailMarketplace = new HooksMarketplace();
|
|
215
|
+
|
|
216
|
+
// All hooks that pass Zod schema validation are admitted (soft-fail)
|
|
217
|
+
// This demonstrates the separation of concerns:
|
|
218
|
+
// - Zod: syntactic validation (hard-fail)
|
|
219
|
+
// - SHACL: semantic validation (soft-fail with audit trail RDF)
|
|
220
|
+
const validHook = createHook(
|
|
221
|
+
'550e8400-e29b-41d4-a716-446655440020',
|
|
222
|
+
'Analytics Hook',
|
|
223
|
+
'1.0.0',
|
|
224
|
+
[],
|
|
225
|
+
);
|
|
226
|
+
|
|
227
|
+
const softFailResult = softFailMarketplace.admitHook(validHook);
|
|
228
|
+
|
|
229
|
+
console.log('Admitting valid hook (all constraints satisfied)...\n');
|
|
230
|
+
console.log(`Admission result:`);
|
|
231
|
+
console.log(` Admitted: ${softFailResult.admitted} (soft-fail mode)`);
|
|
232
|
+
console.log(` Violations recorded: ${softFailResult.violationCount}`);
|
|
233
|
+
console.log(` Hook URI: ${softFailResult.hookUri}`);
|
|
234
|
+
|
|
235
|
+
console.log(`\nDesign Notes:`);
|
|
236
|
+
console.log(` • Zod schema validation: Hard-fail (syntactic)`);
|
|
237
|
+
console.log(` • SHACL validation: Soft-fail (semantic, RDF audit trail)`);
|
|
238
|
+
console.log(` • Violated hooks recorded as RDF triples for compliance audits`);
|
|
239
|
+
console.log(` • Hooks always admitted if they pass syntax validation`);
|
|
240
|
+
|
|
241
|
+
console.log('└───────────────────────────────────────────────────────┘\n');
|
|
242
|
+
|
|
243
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
244
|
+
// Summary
|
|
245
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
246
|
+
console.log('╔════════════════════════════════════════════════════════╗');
|
|
247
|
+
console.log('║ Summary ║');
|
|
248
|
+
console.log('╠════════════════════════════════════════════════════════╣');
|
|
249
|
+
console.log('║ ✓ Hook normalization to RDF (SPARQL CONSTRUCT) ║');
|
|
250
|
+
console.log('║ ✓ Dependency composition (N3 forward-chaining) ║');
|
|
251
|
+
console.log('║ ✓ Circular cycle detection ║');
|
|
252
|
+
console.log('║ ✓ SHACL validation (annotate/soft-fail mode) ║');
|
|
253
|
+
console.log('║ ✓ RDF audit trail for violations ║');
|
|
254
|
+
console.log('║ ✓ Marketplace queries via SPARQL ║');
|
|
255
|
+
console.log('╚════════════════════════════════════════════════════════╝\n');
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
main().catch(err => {
|
|
259
|
+
console.error('Error:', err.message);
|
|
260
|
+
process.exit(1);
|
|
261
|
+
});
|