@unrdf/knowledge-engine 5.0.1
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 +21 -0
- package/README.md +84 -0
- package/package.json +64 -0
- package/src/browser-shims.mjs +343 -0
- package/src/browser.mjs +910 -0
- package/src/canonicalize.mjs +414 -0
- package/src/condition-cache.mjs +109 -0
- package/src/condition-evaluator.mjs +722 -0
- package/src/dark-matter-core.mjs +742 -0
- package/src/define-hook.mjs +213 -0
- package/src/effect-sandbox-browser.mjs +283 -0
- package/src/effect-sandbox-worker.mjs +170 -0
- package/src/effect-sandbox.mjs +517 -0
- package/src/engines/index.mjs +11 -0
- package/src/engines/rdf-engine.mjs +299 -0
- package/src/file-resolver.mjs +387 -0
- package/src/hook-executor-batching.mjs +277 -0
- package/src/hook-executor.mjs +870 -0
- package/src/hook-management.mjs +150 -0
- package/src/index.mjs +93 -0
- package/src/ken-parliment.mjs +119 -0
- package/src/ken.mjs +149 -0
- package/src/knowledge-engine/builtin-rules.mjs +190 -0
- package/src/knowledge-engine/inference-engine.mjs +418 -0
- package/src/knowledge-engine/knowledge-engine.mjs +317 -0
- package/src/knowledge-engine/pattern-dsl.mjs +142 -0
- package/src/knowledge-engine/pattern-matcher.mjs +215 -0
- package/src/knowledge-engine/rules.mjs +184 -0
- package/src/knowledge-engine.mjs +319 -0
- package/src/knowledge-hook-engine.mjs +360 -0
- package/src/knowledge-hook-manager.mjs +469 -0
- package/src/knowledge-substrate-core.mjs +927 -0
- package/src/lite.mjs +222 -0
- package/src/lockchain-writer-browser.mjs +414 -0
- package/src/lockchain-writer.mjs +602 -0
- package/src/monitoring/andon-signals.mjs +775 -0
- package/src/observability.mjs +531 -0
- package/src/parse.mjs +290 -0
- package/src/performance-optimizer.mjs +678 -0
- package/src/policy-pack.mjs +572 -0
- package/src/query-cache.mjs +116 -0
- package/src/query-optimizer.mjs +1051 -0
- package/src/query.mjs +306 -0
- package/src/reason.mjs +350 -0
- package/src/resolution-layer.mjs +506 -0
- package/src/schemas.mjs +1063 -0
- package/src/security/error-sanitizer.mjs +257 -0
- package/src/security/path-validator.mjs +194 -0
- package/src/security/sandbox-restrictions.mjs +331 -0
- package/src/security-validator.mjs +389 -0
- package/src/store-cache.mjs +137 -0
- package/src/telemetry.mjs +167 -0
- package/src/transaction.mjs +810 -0
- package/src/utils/adaptive-monitor.mjs +746 -0
- package/src/utils/circuit-breaker.mjs +513 -0
- package/src/utils/edge-case-handler.mjs +503 -0
- package/src/utils/memory-manager.mjs +498 -0
- package/src/utils/ring-buffer.mjs +282 -0
- package/src/validate.mjs +319 -0
- package/src/validators/index.mjs +338 -0
package/src/browser.mjs
ADDED
|
@@ -0,0 +1,910 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Browser-Compatible Knowledge Engine
|
|
3
|
+
* @module knowledge-engine-browser
|
|
4
|
+
*
|
|
5
|
+
* @description
|
|
6
|
+
* Browser-compatible version of the knowledge engine with Node.js APIs
|
|
7
|
+
* replaced by browser-compatible alternatives.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { _isBrowser } from './browser-shims.mjs';
|
|
11
|
+
import { randomUUID, path, _fs } from './browser-shims.mjs';
|
|
12
|
+
import { process as _browserProcess } from './browser-shims.mjs';
|
|
13
|
+
|
|
14
|
+
// Browser-compatible versions
|
|
15
|
+
import { EffectSandbox, createEffectSandbox } from './effect-sandbox-browser.mjs';
|
|
16
|
+
import {
|
|
17
|
+
BrowserLockchainWriter,
|
|
18
|
+
createBrowserLockchainWriter,
|
|
19
|
+
} from './lockchain-writer-browser.mjs';
|
|
20
|
+
|
|
21
|
+
// Re-export schemas (these are already browser-compatible)
|
|
22
|
+
export * from './schemas.mjs';
|
|
23
|
+
|
|
24
|
+
// Core Engine Components (Browser-compatible)
|
|
25
|
+
import { _KnowledgeHookManager } from './knowledge-hook-manager.mjs';
|
|
26
|
+
import { _QueryOptimizer } from './query-optimizer.mjs';
|
|
27
|
+
|
|
28
|
+
// Browser-compatible Transaction Manager base class
|
|
29
|
+
class BrowserTransactionManager {
|
|
30
|
+
constructor(options = {}) {
|
|
31
|
+
this.options = {
|
|
32
|
+
basePath: options.basePath || '/',
|
|
33
|
+
strictMode: options.strictMode || false,
|
|
34
|
+
enableConditionEvaluation: options.enableConditionEvaluation ?? true,
|
|
35
|
+
maxHooks: options.maxHooks || 100,
|
|
36
|
+
timeout: options.timeout || 30000,
|
|
37
|
+
enableCache: options.enableCache ?? true,
|
|
38
|
+
cacheMaxAge: options.cacheMaxAge || 300000,
|
|
39
|
+
enableMetrics: options.enableMetrics ?? true,
|
|
40
|
+
logLevel: options.logLevel || 'info',
|
|
41
|
+
...options,
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
this.hooks = [];
|
|
45
|
+
this.executionCount = 0;
|
|
46
|
+
this.totalDuration = 0;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
getStats() {
|
|
50
|
+
return {
|
|
51
|
+
hooks: this.hooks.length,
|
|
52
|
+
executions: this.executionCount,
|
|
53
|
+
averageDuration: this.executionCount > 0 ? this.totalDuration / this.executionCount : 0,
|
|
54
|
+
config: this.options,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Browser-compatible hook executor
|
|
60
|
+
/**
|
|
61
|
+
*
|
|
62
|
+
*/
|
|
63
|
+
export class BrowserHookExecutor {
|
|
64
|
+
/**
|
|
65
|
+
*
|
|
66
|
+
*/
|
|
67
|
+
constructor(config = {}) {
|
|
68
|
+
this.config = {
|
|
69
|
+
basePath: config.basePath || '/',
|
|
70
|
+
strictMode: config.strictMode || false,
|
|
71
|
+
enableConditionEvaluation: config.enableConditionEvaluation ?? true,
|
|
72
|
+
enableMetrics: config.enableMetrics ?? true,
|
|
73
|
+
timeout: config.timeout || 30000,
|
|
74
|
+
...config,
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
this.metrics = {
|
|
78
|
+
executions: 0,
|
|
79
|
+
errors: 0,
|
|
80
|
+
totalDuration: 0,
|
|
81
|
+
cacheHits: 0,
|
|
82
|
+
cacheMisses: 0,
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
this.cache = new Map();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
*
|
|
90
|
+
*/
|
|
91
|
+
async execute(hook, event, _options = {}) {
|
|
92
|
+
const startTime = Date.now();
|
|
93
|
+
this.metrics.executions++;
|
|
94
|
+
|
|
95
|
+
try {
|
|
96
|
+
let result;
|
|
97
|
+
|
|
98
|
+
if (hook.run) {
|
|
99
|
+
result = await hook.run(event);
|
|
100
|
+
} else {
|
|
101
|
+
result = { success: true };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const duration = Date.now() - startTime;
|
|
105
|
+
this.metrics.totalDuration += duration;
|
|
106
|
+
|
|
107
|
+
return {
|
|
108
|
+
success: true,
|
|
109
|
+
result,
|
|
110
|
+
duration,
|
|
111
|
+
timestamp: Date.now(),
|
|
112
|
+
};
|
|
113
|
+
} catch (error) {
|
|
114
|
+
this.metrics.errors++;
|
|
115
|
+
const duration = Date.now() - startTime;
|
|
116
|
+
this.metrics.totalDuration += duration;
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
success: false,
|
|
120
|
+
error: error.message,
|
|
121
|
+
duration,
|
|
122
|
+
timestamp: Date.now(),
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
*
|
|
129
|
+
*/
|
|
130
|
+
async executeAll(hooks, event, options = {}) {
|
|
131
|
+
const results = [];
|
|
132
|
+
|
|
133
|
+
for (const hook of hooks) {
|
|
134
|
+
try {
|
|
135
|
+
const result = await this.execute(hook, event, options);
|
|
136
|
+
results.push(result);
|
|
137
|
+
} catch (error) {
|
|
138
|
+
results.push({
|
|
139
|
+
success: false,
|
|
140
|
+
error: error.message,
|
|
141
|
+
hook: hook.meta?.name || 'unknown',
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return results;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
*
|
|
151
|
+
*/
|
|
152
|
+
getMetrics() {
|
|
153
|
+
return { ...this.metrics };
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
*
|
|
158
|
+
*/
|
|
159
|
+
clearCache() {
|
|
160
|
+
this.cache.clear();
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Browser-compatible condition evaluator
|
|
165
|
+
/**
|
|
166
|
+
*
|
|
167
|
+
*/
|
|
168
|
+
export class BrowserConditionEvaluator {
|
|
169
|
+
/**
|
|
170
|
+
*
|
|
171
|
+
*/
|
|
172
|
+
constructor(config = {}) {
|
|
173
|
+
this.config = {
|
|
174
|
+
basePath: config.basePath || '/',
|
|
175
|
+
enableCache: config.enableCache ?? true,
|
|
176
|
+
cacheMaxAge: config.cacheMaxAge || 300000,
|
|
177
|
+
timeout: config.timeout || 10000,
|
|
178
|
+
strict: config.strict || false,
|
|
179
|
+
...config,
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
this.cache = new Map();
|
|
183
|
+
this.cacheStats = {
|
|
184
|
+
hits: 0,
|
|
185
|
+
misses: 0,
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
*
|
|
191
|
+
*/
|
|
192
|
+
async evaluateCondition(condition, graph, _options = {}) {
|
|
193
|
+
const cacheKey = `${JSON.stringify(condition)}_${graph.size || 0}`;
|
|
194
|
+
|
|
195
|
+
if (this.cache.has(cacheKey)) {
|
|
196
|
+
this.cacheStats.hits++;
|
|
197
|
+
return this.cache.get(cacheKey);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
this.cacheStats.misses++;
|
|
201
|
+
|
|
202
|
+
try {
|
|
203
|
+
let result = false;
|
|
204
|
+
|
|
205
|
+
// Browser-compatible condition evaluation using pattern matching
|
|
206
|
+
if (condition.kind === 'sparql-ask') {
|
|
207
|
+
result = await this._evaluateSparqlAsk(condition, graph);
|
|
208
|
+
} else if (condition.kind === 'sparql-select') {
|
|
209
|
+
result = await this._evaluateSparqlSelect(condition, graph);
|
|
210
|
+
} else if (condition.kind === 'shacl') {
|
|
211
|
+
result = await this._evaluateShacl(condition, graph);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const evaluationResult = {
|
|
215
|
+
satisfied: result,
|
|
216
|
+
evaluationTime: Date.now(),
|
|
217
|
+
condition,
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
this.cache.set(cacheKey, evaluationResult);
|
|
221
|
+
return evaluationResult;
|
|
222
|
+
} catch (error) {
|
|
223
|
+
return {
|
|
224
|
+
satisfied: false,
|
|
225
|
+
error: error.message,
|
|
226
|
+
evaluationTime: Date.now(),
|
|
227
|
+
condition,
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
*
|
|
234
|
+
*/
|
|
235
|
+
getCacheStats() {
|
|
236
|
+
return { ...this.cacheStats };
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
*
|
|
241
|
+
*/
|
|
242
|
+
clearCache() {
|
|
243
|
+
this.cache.clear();
|
|
244
|
+
this.cacheStats = { hits: 0, misses: 0 };
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Evaluate SPARQL ASK query using pattern matching
|
|
249
|
+
* @param {Object} condition - Condition with query property
|
|
250
|
+
* @param {Store} graph - RDF graph
|
|
251
|
+
* @returns {Promise<boolean>} Query result
|
|
252
|
+
* @private
|
|
253
|
+
*/
|
|
254
|
+
async _evaluateSparqlAsk(condition, graph) {
|
|
255
|
+
const query = condition.query || condition.ref?.query || '';
|
|
256
|
+
if (!query) {
|
|
257
|
+
return graph.size > 0; // Fallback
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Parse basic SPARQL ASK patterns
|
|
261
|
+
const whereMatch = query.match(/WHERE\s*\{([^}]+)\}/is);
|
|
262
|
+
if (!whereMatch) {
|
|
263
|
+
return graph.size > 0; // Fallback if can't parse
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const patterns = whereMatch[1]
|
|
267
|
+
.split(/\.\s*/)
|
|
268
|
+
.map(p => p.trim())
|
|
269
|
+
.filter(p => p && !p.startsWith('#'));
|
|
270
|
+
|
|
271
|
+
// For each pattern, check if it matches in the graph
|
|
272
|
+
for (const pattern of patterns) {
|
|
273
|
+
const match = pattern.match(/(\S+)\s+(\S+)\s+(\S+)/);
|
|
274
|
+
if (!match) continue;
|
|
275
|
+
|
|
276
|
+
const [, s, p, o] = match;
|
|
277
|
+
const subject = this._normalizeTerm(s);
|
|
278
|
+
const predicate = this._normalizeTerm(p);
|
|
279
|
+
const object = this._normalizeTerm(o);
|
|
280
|
+
|
|
281
|
+
// Check if any quad matches this pattern
|
|
282
|
+
const quads = graph.getQuads(subject, predicate, object, null);
|
|
283
|
+
if (quads.length === 0) {
|
|
284
|
+
return false; // Pattern doesn't match
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
return true; // All patterns matched
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Evaluate SPARQL SELECT query using pattern matching
|
|
293
|
+
* @param {Object} condition - Condition with query property
|
|
294
|
+
* @param {Store} graph - RDF graph
|
|
295
|
+
* @returns {Promise<Array>} Query results
|
|
296
|
+
* @private
|
|
297
|
+
*/
|
|
298
|
+
async _evaluateSparqlSelect(condition, graph) {
|
|
299
|
+
const query = condition.query || condition.ref?.query || '';
|
|
300
|
+
if (!query) {
|
|
301
|
+
return [];
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Extract SELECT variables
|
|
305
|
+
const selectMatch = query.match(/SELECT\s+(.+?)\s+WHERE/is);
|
|
306
|
+
if (!selectMatch) {
|
|
307
|
+
return [];
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
const _variables = selectMatch[1]
|
|
311
|
+
.split(/\s+/)
|
|
312
|
+
.map(v => v.replace(/^\?/, '').trim())
|
|
313
|
+
.filter(v => v);
|
|
314
|
+
|
|
315
|
+
// Extract WHERE patterns
|
|
316
|
+
const whereMatch = query.match(/WHERE\s*\{([^}]+)\}/is);
|
|
317
|
+
if (!whereMatch) {
|
|
318
|
+
return [];
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
const patterns = whereMatch[1]
|
|
322
|
+
.split(/\.\s*/)
|
|
323
|
+
.map(p => p.trim())
|
|
324
|
+
.filter(p => p && !p.startsWith('#'));
|
|
325
|
+
|
|
326
|
+
// Simple pattern matching - find matching quads
|
|
327
|
+
const results = [];
|
|
328
|
+
|
|
329
|
+
for (const pattern of patterns) {
|
|
330
|
+
const match = pattern.match(/(\S+)\s+(\S+)\s+(\S+)/);
|
|
331
|
+
if (!match) continue;
|
|
332
|
+
|
|
333
|
+
const [, s, p, o] = match;
|
|
334
|
+
const subject = this._normalizeTerm(s);
|
|
335
|
+
const predicate = this._normalizeTerm(p);
|
|
336
|
+
const object = this._normalizeTerm(o);
|
|
337
|
+
|
|
338
|
+
const quads = graph.getQuads(subject, predicate, object, null);
|
|
339
|
+
for (const quad of quads) {
|
|
340
|
+
const binding = {};
|
|
341
|
+
if (s.startsWith('?')) {
|
|
342
|
+
binding[s.substring(1)] = quad.subject.value;
|
|
343
|
+
}
|
|
344
|
+
if (p.startsWith('?')) {
|
|
345
|
+
binding[p.substring(1)] = quad.predicate.value;
|
|
346
|
+
}
|
|
347
|
+
if (o.startsWith('?')) {
|
|
348
|
+
binding[o.substring(1)] = quad.object.value;
|
|
349
|
+
}
|
|
350
|
+
if (Object.keys(binding).length > 0) {
|
|
351
|
+
results.push(binding);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
return results;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Evaluate SHACL validation using basic pattern matching
|
|
361
|
+
* @param {Object} condition - Condition with shapes
|
|
362
|
+
* @param {Store} graph - RDF graph
|
|
363
|
+
* @returns {Promise<boolean>} Validation result
|
|
364
|
+
* @private
|
|
365
|
+
*/
|
|
366
|
+
async _evaluateShacl(condition, graph) {
|
|
367
|
+
// Basic SHACL validation - check for required properties
|
|
368
|
+
// Full SHACL validation would require a proper SHACL engine
|
|
369
|
+
const shapes = condition.shapes || condition.ref?.shapes;
|
|
370
|
+
if (!shapes) {
|
|
371
|
+
return graph.size > 0; // No shapes to validate against
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// For now, return true if graph has data
|
|
375
|
+
// Full implementation would parse SHACL shapes and validate
|
|
376
|
+
return graph.size > 0;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* Normalize SPARQL term (variable, IRI, literal) to match format
|
|
381
|
+
* @param {string} term - SPARQL term
|
|
382
|
+
* @returns {string|null} Normalized term or null for variables
|
|
383
|
+
* @private
|
|
384
|
+
*/
|
|
385
|
+
_normalizeTerm(term) {
|
|
386
|
+
if (!term) return null;
|
|
387
|
+
term = term.trim();
|
|
388
|
+
|
|
389
|
+
// Variable - return null to match any
|
|
390
|
+
if (term.startsWith('?')) {
|
|
391
|
+
return null;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// IRI with angle brackets
|
|
395
|
+
if (term.startsWith('<') && term.endsWith('>')) {
|
|
396
|
+
return term.slice(1, -1);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// Literal with quotes
|
|
400
|
+
if (
|
|
401
|
+
(term.startsWith('"') && term.endsWith('"')) ||
|
|
402
|
+
(term.startsWith("'") && term.endsWith("'"))
|
|
403
|
+
) {
|
|
404
|
+
return term.slice(1, -1);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// Plain IRI or term
|
|
408
|
+
return term;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// Browser-compatible policy pack manager
|
|
413
|
+
/**
|
|
414
|
+
*
|
|
415
|
+
*/
|
|
416
|
+
export class BrowserPolicyPackManager {
|
|
417
|
+
/**
|
|
418
|
+
*
|
|
419
|
+
*/
|
|
420
|
+
constructor(basePath = '/') {
|
|
421
|
+
this.basePath = basePath;
|
|
422
|
+
this.policyPacks = new Map();
|
|
423
|
+
this.activePacks = new Set();
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
*
|
|
428
|
+
*/
|
|
429
|
+
async loadPolicyPack(packName, manifest) {
|
|
430
|
+
this.policyPacks.set(packName, {
|
|
431
|
+
name: packName,
|
|
432
|
+
manifest,
|
|
433
|
+
hooks: manifest.hooks || [],
|
|
434
|
+
rules: manifest.rules || [],
|
|
435
|
+
loaded: true,
|
|
436
|
+
activated: false,
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
*
|
|
442
|
+
*/
|
|
443
|
+
activatePolicyPack(packName) {
|
|
444
|
+
const pack = this.policyPacks.get(packName);
|
|
445
|
+
if (pack) {
|
|
446
|
+
pack.activated = true;
|
|
447
|
+
this.activePacks.add(packName);
|
|
448
|
+
return true;
|
|
449
|
+
}
|
|
450
|
+
return false;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
*
|
|
455
|
+
*/
|
|
456
|
+
deactivatePolicyPack(packName) {
|
|
457
|
+
const pack = this.policyPacks.get(packName);
|
|
458
|
+
if (pack) {
|
|
459
|
+
pack.activated = false;
|
|
460
|
+
this.activePacks.delete(packName);
|
|
461
|
+
return true;
|
|
462
|
+
}
|
|
463
|
+
return false;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
/**
|
|
467
|
+
*
|
|
468
|
+
*/
|
|
469
|
+
getPolicyPack(packName) {
|
|
470
|
+
return this.policyPacks.get(packName);
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
*
|
|
475
|
+
*/
|
|
476
|
+
getActivePolicyPacks() {
|
|
477
|
+
return Array.from(this.activePacks)
|
|
478
|
+
.map(name => this.policyPacks.get(name))
|
|
479
|
+
.filter(Boolean);
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
*
|
|
484
|
+
*/
|
|
485
|
+
getActiveHooks() {
|
|
486
|
+
return this.getActivePolicyPacks().flatMap(pack => pack.hooks);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// Browser-compatible file resolver
|
|
491
|
+
/**
|
|
492
|
+
*
|
|
493
|
+
*/
|
|
494
|
+
export class BrowserFileResolver {
|
|
495
|
+
/**
|
|
496
|
+
*
|
|
497
|
+
*/
|
|
498
|
+
constructor(config = {}) {
|
|
499
|
+
this.config = {
|
|
500
|
+
basePath: config.basePath || '/',
|
|
501
|
+
enableCache: config.enableCache ?? true,
|
|
502
|
+
cacheMaxAge: config.cacheMaxAge || 300000,
|
|
503
|
+
maxFileSize: config.maxFileSize || 1024 * 1024,
|
|
504
|
+
allowedMediaTypes: config.allowedMediaTypes || [
|
|
505
|
+
'application/sparql-query',
|
|
506
|
+
'text/turtle',
|
|
507
|
+
'application/rdf+xml',
|
|
508
|
+
'application/ld+json',
|
|
509
|
+
],
|
|
510
|
+
timeout: config.timeout || 5000,
|
|
511
|
+
...config,
|
|
512
|
+
};
|
|
513
|
+
|
|
514
|
+
this.cache = new Map();
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
/**
|
|
518
|
+
*
|
|
519
|
+
*/
|
|
520
|
+
async resolveFileUri(uri, basePath = this.config.basePath) {
|
|
521
|
+
if (uri.startsWith('file://')) {
|
|
522
|
+
return uri.replace('file://', '');
|
|
523
|
+
} else if (uri.startsWith('http://') || uri.startsWith('https://')) {
|
|
524
|
+
return uri;
|
|
525
|
+
} else {
|
|
526
|
+
return path.join(basePath, uri);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
/**
|
|
531
|
+
*
|
|
532
|
+
*/
|
|
533
|
+
async calculateFileHash(filePath) {
|
|
534
|
+
// Use Web Crypto API for SHA-256 hash calculation
|
|
535
|
+
if (
|
|
536
|
+
typeof globalThis?.window === 'undefined' ||
|
|
537
|
+
!globalThis?.window?.crypto ||
|
|
538
|
+
!globalThis?.window?.crypto?.subtle
|
|
539
|
+
) {
|
|
540
|
+
// Fallback for environments without Web Crypto API
|
|
541
|
+
return 'fallback-hash-' + Date.now().toString(36);
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
try {
|
|
545
|
+
// Read file content (in browser, this would typically be from File/Blob)
|
|
546
|
+
// For now, we'll hash the file path and any available content
|
|
547
|
+
let content = filePath;
|
|
548
|
+
|
|
549
|
+
// If filePath is a File or Blob, read it
|
|
550
|
+
if (filePath instanceof File || filePath instanceof Blob) {
|
|
551
|
+
const arrayBuffer = await filePath.arrayBuffer();
|
|
552
|
+
content = new Uint8Array(arrayBuffer);
|
|
553
|
+
} else if (typeof filePath === 'string') {
|
|
554
|
+
// Convert string to Uint8Array
|
|
555
|
+
const encoder = new TextEncoder();
|
|
556
|
+
content = encoder.encode(filePath);
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
// Calculate SHA-256 hash
|
|
560
|
+
const hashBuffer = await globalThis.window.crypto.subtle.digest('SHA-256', content);
|
|
561
|
+
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
562
|
+
const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
|
|
563
|
+
|
|
564
|
+
return hashHex;
|
|
565
|
+
} catch (error) {
|
|
566
|
+
// Fallback on error
|
|
567
|
+
console.warn('Failed to calculate file hash with Web Crypto API:', error);
|
|
568
|
+
return 'error-hash-' + Date.now().toString(36);
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
/**
|
|
573
|
+
*
|
|
574
|
+
*/
|
|
575
|
+
async loadFileWithHash(uri, expectedHash, basePath = this.config.basePath) {
|
|
576
|
+
const filePath = await this.resolveFileUri(uri, basePath);
|
|
577
|
+
const cacheKey = `${filePath}:${expectedHash}`;
|
|
578
|
+
|
|
579
|
+
if (this.cache.has(cacheKey)) {
|
|
580
|
+
return this.cache.get(cacheKey);
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
// Mock file loading for browser
|
|
584
|
+
const mockContent = `-- Mock content from ${filePath} --`;
|
|
585
|
+
const content = {
|
|
586
|
+
content: mockContent,
|
|
587
|
+
size: mockContent.length,
|
|
588
|
+
lastModified: Date.now(),
|
|
589
|
+
hash: await this.calculateFileHash(filePath),
|
|
590
|
+
};
|
|
591
|
+
|
|
592
|
+
this.cache.set(cacheKey, content);
|
|
593
|
+
return content;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
/**
|
|
597
|
+
*
|
|
598
|
+
*/
|
|
599
|
+
async loadSparqlFile(uri, expectedHash, basePath = this.config.basePath) {
|
|
600
|
+
const file = await this.loadFileWithHash(uri, expectedHash, basePath);
|
|
601
|
+
return file.content;
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
// Browser-compatible resolution layer
|
|
606
|
+
/**
|
|
607
|
+
*
|
|
608
|
+
*/
|
|
609
|
+
export class BrowserResolutionLayer {
|
|
610
|
+
/**
|
|
611
|
+
*
|
|
612
|
+
*/
|
|
613
|
+
constructor(config = {}) {
|
|
614
|
+
this.config = {
|
|
615
|
+
defaultStrategy: config.defaultStrategy || 'voting',
|
|
616
|
+
maxProposals: config.maxProposals || 100,
|
|
617
|
+
enableConflictDetection: config.enableConflictDetection ?? true,
|
|
618
|
+
enableConsensus: config.enableConsensus ?? true,
|
|
619
|
+
timeout: config.timeout || 30000,
|
|
620
|
+
...config,
|
|
621
|
+
};
|
|
622
|
+
|
|
623
|
+
this.proposals = new Map();
|
|
624
|
+
this.resolutionHistory = [];
|
|
625
|
+
this.agents = new Map();
|
|
626
|
+
this.strategies = new Map();
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
/**
|
|
630
|
+
*
|
|
631
|
+
*/
|
|
632
|
+
registerAgent(agentId, metadata = {}) {
|
|
633
|
+
this.agents.set(agentId, {
|
|
634
|
+
id: agentId,
|
|
635
|
+
...metadata,
|
|
636
|
+
registeredAt: Date.now(),
|
|
637
|
+
});
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
/**
|
|
641
|
+
*
|
|
642
|
+
*/
|
|
643
|
+
submitProposal(proposal) {
|
|
644
|
+
this.proposals.set(proposal.id, proposal);
|
|
645
|
+
return proposal.id;
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
/**
|
|
649
|
+
*
|
|
650
|
+
*/
|
|
651
|
+
async resolveProposals(strategy = this.config.defaultStrategy) {
|
|
652
|
+
const pendingProposals = Array.from(this.proposals.values());
|
|
653
|
+
if (pendingProposals.length === 0) {
|
|
654
|
+
return null;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
const resolution = {
|
|
658
|
+
id: randomUUID(),
|
|
659
|
+
strategy,
|
|
660
|
+
proposals: pendingProposals,
|
|
661
|
+
resolvedDelta: this._mergeProposals(pendingProposals),
|
|
662
|
+
confidence: this._calculateConsensus(pendingProposals),
|
|
663
|
+
consensus: pendingProposals.length > 1,
|
|
664
|
+
conflicts: this._detectConflicts(pendingProposals),
|
|
665
|
+
timestamp: Date.now(),
|
|
666
|
+
duration: 0,
|
|
667
|
+
};
|
|
668
|
+
|
|
669
|
+
this.resolutionHistory.push(resolution);
|
|
670
|
+
|
|
671
|
+
// Clear processed proposals
|
|
672
|
+
this.proposals.clear();
|
|
673
|
+
|
|
674
|
+
return resolution;
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
/**
|
|
678
|
+
*
|
|
679
|
+
*/
|
|
680
|
+
_mergeProposals(proposals) {
|
|
681
|
+
const additions = [];
|
|
682
|
+
const removals = [];
|
|
683
|
+
|
|
684
|
+
for (const proposal of proposals) {
|
|
685
|
+
additions.push(...proposal.delta.additions);
|
|
686
|
+
removals.push(...proposal.delta.removals);
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
return {
|
|
690
|
+
additions,
|
|
691
|
+
removals,
|
|
692
|
+
metadata: { mergedFrom: proposals.length },
|
|
693
|
+
};
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
/**
|
|
697
|
+
*
|
|
698
|
+
*/
|
|
699
|
+
_calculateConsensus(proposals) {
|
|
700
|
+
return proposals.length > 0 ? Math.min(0.8, proposals.length * 0.2) : 0;
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
/**
|
|
704
|
+
*
|
|
705
|
+
*/
|
|
706
|
+
_detectConflicts(_proposals) {
|
|
707
|
+
// Simple conflict detection
|
|
708
|
+
return [];
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
/**
|
|
712
|
+
*
|
|
713
|
+
*/
|
|
714
|
+
getStats() {
|
|
715
|
+
return {
|
|
716
|
+
agents: this.agents.size,
|
|
717
|
+
pendingProposals: this.proposals.size,
|
|
718
|
+
totalResolutions: this.resolutionHistory.length,
|
|
719
|
+
config: this.config,
|
|
720
|
+
};
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
// Browser-compatible knowledge hook manager
|
|
725
|
+
/**
|
|
726
|
+
*
|
|
727
|
+
*/
|
|
728
|
+
export class BrowserKnowledgeHookManager extends BrowserTransactionManager {
|
|
729
|
+
/**
|
|
730
|
+
*
|
|
731
|
+
*/
|
|
732
|
+
constructor(options = {}) {
|
|
733
|
+
super(options);
|
|
734
|
+
|
|
735
|
+
this.basePath = options.basePath || '/';
|
|
736
|
+
this.enableKnowledgeHooks = options.enableKnowledgeHooks ?? true;
|
|
737
|
+
this.strictMode = options.strictMode || false;
|
|
738
|
+
|
|
739
|
+
// Browser-compatible components
|
|
740
|
+
this.hookExecutor = new BrowserHookExecutor({
|
|
741
|
+
basePath: this.basePath,
|
|
742
|
+
strictMode: this.strictMode,
|
|
743
|
+
enableMetrics: true,
|
|
744
|
+
});
|
|
745
|
+
|
|
746
|
+
this.conditionEvaluator = new BrowserConditionEvaluator({
|
|
747
|
+
basePath: this.basePath,
|
|
748
|
+
strictMode: this.strictMode,
|
|
749
|
+
enableCache: true,
|
|
750
|
+
});
|
|
751
|
+
|
|
752
|
+
this.policyPackManager = new BrowserPolicyPackManager(this.basePath);
|
|
753
|
+
this.knowledgeHooks = new Map();
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
/**
|
|
757
|
+
*
|
|
758
|
+
*/
|
|
759
|
+
async addKnowledgeHook(hook) {
|
|
760
|
+
if (!this.enableKnowledgeHooks) {
|
|
761
|
+
throw new Error('Knowledge hooks are disabled');
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
if (!hook || typeof hook !== 'object') {
|
|
765
|
+
throw new TypeError('Hook must be an object');
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
if (!hook.meta || !hook.meta.name) {
|
|
769
|
+
throw new TypeError('Hook must have meta.name');
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
if (!hook.run || typeof hook.run !== 'function') {
|
|
773
|
+
throw new TypeError('Hook must have a run function');
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
if (this.knowledgeHooks.has(hook.meta.name)) {
|
|
777
|
+
throw new Error(`Knowledge hook "${hook.meta.name}" already exists`);
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
this.knowledgeHooks.set(hook.meta.name, hook);
|
|
781
|
+
return hook;
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
/**
|
|
785
|
+
*
|
|
786
|
+
*/
|
|
787
|
+
removeKnowledgeHook(hookName) {
|
|
788
|
+
const existed = this.knowledgeHooks.has(hookName);
|
|
789
|
+
if (existed) {
|
|
790
|
+
this.knowledgeHooks.delete(hookName);
|
|
791
|
+
}
|
|
792
|
+
return existed;
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
/**
|
|
796
|
+
*
|
|
797
|
+
*/
|
|
798
|
+
getKnowledgeHooks() {
|
|
799
|
+
return Array.from(this.knowledgeHooks.values());
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
/**
|
|
803
|
+
*
|
|
804
|
+
*/
|
|
805
|
+
async executeKnowledgeHook(hookName, event, options = {}) {
|
|
806
|
+
const hook = this.knowledgeHooks.get(hookName);
|
|
807
|
+
if (!hook) {
|
|
808
|
+
throw new Error(`Knowledge hook "${hookName}" not found`);
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
return this.hookExecutor.execute(hook, event, options);
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
/**
|
|
815
|
+
*
|
|
816
|
+
*/
|
|
817
|
+
async executeAllKnowledgeHooks(event, options = {}) {
|
|
818
|
+
const hooks = this.getKnowledgeHooks();
|
|
819
|
+
return this.hookExecutor.executeAll(hooks, event, options);
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
/**
|
|
823
|
+
*
|
|
824
|
+
*/
|
|
825
|
+
getStats() {
|
|
826
|
+
const baseStats = super.getStats();
|
|
827
|
+
const hookExecutorStats = this.hookExecutor.getMetrics();
|
|
828
|
+
const conditionEvaluatorStats = this.conditionEvaluator.getCacheStats();
|
|
829
|
+
|
|
830
|
+
return {
|
|
831
|
+
...baseStats,
|
|
832
|
+
knowledgeHooks: {
|
|
833
|
+
total: this.knowledgeHooks.size,
|
|
834
|
+
enabled: this.enableKnowledgeHooks,
|
|
835
|
+
strictMode: this.strictMode,
|
|
836
|
+
},
|
|
837
|
+
hookExecutor: hookExecutorStats,
|
|
838
|
+
conditionEvaluator: conditionEvaluatorStats,
|
|
839
|
+
};
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
// Export factory functions for browser compatibility
|
|
844
|
+
/**
|
|
845
|
+
*
|
|
846
|
+
*/
|
|
847
|
+
export function createBrowserHookExecutor(config = {}) {
|
|
848
|
+
return new BrowserHookExecutor(config);
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
/**
|
|
852
|
+
*
|
|
853
|
+
*/
|
|
854
|
+
export function createBrowserConditionEvaluator(config = {}) {
|
|
855
|
+
return new BrowserConditionEvaluator(config);
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
/**
|
|
859
|
+
*
|
|
860
|
+
*/
|
|
861
|
+
export function createBrowserPolicyPackManager(basePath = '/') {
|
|
862
|
+
return new BrowserPolicyPackManager(basePath);
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
/**
|
|
866
|
+
*
|
|
867
|
+
*/
|
|
868
|
+
export function createBrowserFileResolver(config = {}) {
|
|
869
|
+
return new BrowserFileResolver(config);
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
/**
|
|
873
|
+
*
|
|
874
|
+
*/
|
|
875
|
+
export function createBrowserResolutionLayer(config = {}) {
|
|
876
|
+
return new BrowserResolutionLayer(config);
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
/**
|
|
880
|
+
*
|
|
881
|
+
*/
|
|
882
|
+
export function createBrowserKnowledgeHookManager(options = {}) {
|
|
883
|
+
return new BrowserKnowledgeHookManager(options);
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
// Export core components for browser
|
|
887
|
+
export {
|
|
888
|
+
EffectSandbox,
|
|
889
|
+
createEffectSandbox,
|
|
890
|
+
BrowserLockchainWriter,
|
|
891
|
+
createBrowserLockchainWriter,
|
|
892
|
+
BrowserLockchainWriter as LockchainWriter,
|
|
893
|
+
createBrowserLockchainWriter as createLockchainWriter,
|
|
894
|
+
BrowserKnowledgeHookManager as KnowledgeHookManager,
|
|
895
|
+
createBrowserKnowledgeHookManager as createKnowledgeHookManager,
|
|
896
|
+
};
|
|
897
|
+
|
|
898
|
+
export default {
|
|
899
|
+
EffectSandbox,
|
|
900
|
+
createEffectSandbox,
|
|
901
|
+
LockchainWriter: BrowserLockchainWriter,
|
|
902
|
+
createLockchainWriter: createBrowserLockchainWriter,
|
|
903
|
+
KnowledgeHookManager: BrowserKnowledgeHookManager,
|
|
904
|
+
createKnowledgeHookManager: createBrowserKnowledgeHookManager,
|
|
905
|
+
BrowserHookExecutor,
|
|
906
|
+
BrowserConditionEvaluator,
|
|
907
|
+
BrowserPolicyPackManager,
|
|
908
|
+
BrowserFileResolver,
|
|
909
|
+
BrowserResolutionLayer,
|
|
910
|
+
};
|