@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.
Files changed (60) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +84 -0
  3. package/package.json +64 -0
  4. package/src/browser-shims.mjs +343 -0
  5. package/src/browser.mjs +910 -0
  6. package/src/canonicalize.mjs +414 -0
  7. package/src/condition-cache.mjs +109 -0
  8. package/src/condition-evaluator.mjs +722 -0
  9. package/src/dark-matter-core.mjs +742 -0
  10. package/src/define-hook.mjs +213 -0
  11. package/src/effect-sandbox-browser.mjs +283 -0
  12. package/src/effect-sandbox-worker.mjs +170 -0
  13. package/src/effect-sandbox.mjs +517 -0
  14. package/src/engines/index.mjs +11 -0
  15. package/src/engines/rdf-engine.mjs +299 -0
  16. package/src/file-resolver.mjs +387 -0
  17. package/src/hook-executor-batching.mjs +277 -0
  18. package/src/hook-executor.mjs +870 -0
  19. package/src/hook-management.mjs +150 -0
  20. package/src/index.mjs +93 -0
  21. package/src/ken-parliment.mjs +119 -0
  22. package/src/ken.mjs +149 -0
  23. package/src/knowledge-engine/builtin-rules.mjs +190 -0
  24. package/src/knowledge-engine/inference-engine.mjs +418 -0
  25. package/src/knowledge-engine/knowledge-engine.mjs +317 -0
  26. package/src/knowledge-engine/pattern-dsl.mjs +142 -0
  27. package/src/knowledge-engine/pattern-matcher.mjs +215 -0
  28. package/src/knowledge-engine/rules.mjs +184 -0
  29. package/src/knowledge-engine.mjs +319 -0
  30. package/src/knowledge-hook-engine.mjs +360 -0
  31. package/src/knowledge-hook-manager.mjs +469 -0
  32. package/src/knowledge-substrate-core.mjs +927 -0
  33. package/src/lite.mjs +222 -0
  34. package/src/lockchain-writer-browser.mjs +414 -0
  35. package/src/lockchain-writer.mjs +602 -0
  36. package/src/monitoring/andon-signals.mjs +775 -0
  37. package/src/observability.mjs +531 -0
  38. package/src/parse.mjs +290 -0
  39. package/src/performance-optimizer.mjs +678 -0
  40. package/src/policy-pack.mjs +572 -0
  41. package/src/query-cache.mjs +116 -0
  42. package/src/query-optimizer.mjs +1051 -0
  43. package/src/query.mjs +306 -0
  44. package/src/reason.mjs +350 -0
  45. package/src/resolution-layer.mjs +506 -0
  46. package/src/schemas.mjs +1063 -0
  47. package/src/security/error-sanitizer.mjs +257 -0
  48. package/src/security/path-validator.mjs +194 -0
  49. package/src/security/sandbox-restrictions.mjs +331 -0
  50. package/src/security-validator.mjs +389 -0
  51. package/src/store-cache.mjs +137 -0
  52. package/src/telemetry.mjs +167 -0
  53. package/src/transaction.mjs +810 -0
  54. package/src/utils/adaptive-monitor.mjs +746 -0
  55. package/src/utils/circuit-breaker.mjs +513 -0
  56. package/src/utils/edge-case-handler.mjs +503 -0
  57. package/src/utils/memory-manager.mjs +498 -0
  58. package/src/utils/ring-buffer.mjs +282 -0
  59. package/src/validate.mjs +319 -0
  60. package/src/validators/index.mjs +338 -0
@@ -0,0 +1,418 @@
1
+ /**
2
+ * @file Inference Engine - Forward-chaining rule execution
3
+ * @module @unrdf/knowledge-engine/inference-engine
4
+ */
5
+
6
+ import { addQuad, quad, namedNode, literal, countQuads, getQuads } from '@unrdf/core';
7
+ import { compileRule } from './rules.mjs';
8
+
9
+ /**
10
+ * @typedef {import('n3').Store} Store
11
+ * @typedef {import('n3').Quad} Quad
12
+ */
13
+
14
+ /**
15
+ * Create an inference engine
16
+ *
17
+ * @param {Store} store - RDF store for inference
18
+ * @returns {Object} Inference engine instance
19
+ *
20
+ * @example
21
+ * const engine = createInferenceEngine(store);
22
+ * addRules(engine, [rdfsSubClassRule, rdfsSubPropertyRule]);
23
+ * runInference(engine);
24
+ */
25
+ export function createInferenceEngine(store) {
26
+ return {
27
+ store,
28
+ rules: [],
29
+ inferredQuads: [],
30
+ iterations: 0,
31
+ };
32
+ }
33
+
34
+ /**
35
+ * Add rules to the inference engine
36
+ *
37
+ * @param {Object} engine - Inference engine
38
+ * @param {Object[]} rules - Array of rules to add
39
+ * @returns {void}
40
+ *
41
+ * @example
42
+ * addRules(engine, [rule1, rule2, rule3]);
43
+ */
44
+ export function addRules(engine, rules) {
45
+ for (const rule of rules) {
46
+ const compiled = compileRule(rule);
47
+ engine.rules.push(compiled);
48
+ }
49
+
50
+ engine.rules.sort((a, b) => b.salience - a.salience);
51
+ }
52
+
53
+ /**
54
+ * Run forward-chaining inference until fixpoint
55
+ *
56
+ * @param {Object} engine - Inference engine
57
+ * @param {number} [maxIterations=100] - Maximum iterations to prevent infinite loops
58
+ * @returns {Object} Inference results
59
+ *
60
+ * @example
61
+ * const results = runInference(engine, 50);
62
+ * console.log(`Inferred ${results.inferredCount} new facts in ${results.iterations} iterations`);
63
+ */
64
+ export function runInference(engine, maxIterations = 100) {
65
+ engine.iterations = 0;
66
+ engine.inferredQuads = [];
67
+
68
+ let previousCount = countQuads(engine.store);
69
+ let iteration = 0;
70
+
71
+ while (iteration < maxIterations) {
72
+ iteration++;
73
+ engine.iterations = iteration;
74
+
75
+ for (const rule of engine.rules) {
76
+ applyRule(engine, rule);
77
+ }
78
+
79
+ const currentCount = countQuads(engine.store);
80
+
81
+ if (currentCount === previousCount) {
82
+ break;
83
+ }
84
+
85
+ previousCount = currentCount;
86
+ }
87
+
88
+ return {
89
+ iterations: engine.iterations,
90
+ inferredCount: engine.inferredQuads.length,
91
+ fixpointReached: engine.iterations < maxIterations,
92
+ };
93
+ }
94
+
95
+ /**
96
+ * Apply a single rule to the store
97
+ *
98
+ * @param {Object} engine - Inference engine
99
+ * @param {Object} rule - Compiled rule
100
+ * @returns {number} Number of new facts inferred
101
+ */
102
+ function applyRule(engine, rule) {
103
+ const bindings = matchRulePatterns(engine.store, rule.pattern);
104
+ let newFactsCount = 0;
105
+
106
+ for (const binding of bindings) {
107
+ for (const consequentPattern of rule.consequent) {
108
+ const newQuad = instantiateConsequent(consequentPattern, binding);
109
+
110
+ if (newQuad && !quadExists(engine.store, newQuad)) {
111
+ try {
112
+ addQuad(engine.store, newQuad);
113
+ engine.inferredQuads.push(newQuad);
114
+ newFactsCount++;
115
+ } catch (err) {
116
+ // Ignore duplicate quads
117
+ }
118
+ }
119
+ }
120
+ }
121
+
122
+ return newFactsCount;
123
+ }
124
+
125
+ /**
126
+ * Match rule patterns (handles compiled patterns with type/value objects)
127
+ *
128
+ * @param {Store} store - RDF store
129
+ * @param {Object[]} patterns - Array of compiled patterns
130
+ * @returns {Object[]} Array of binding sets
131
+ */
132
+ function matchRulePatterns(store, patterns) {
133
+ if (patterns.length === 0) {
134
+ return [];
135
+ }
136
+
137
+ if (patterns.length === 1) {
138
+ return matchSinglePattern(store, patterns[0]);
139
+ }
140
+
141
+ let currentBindings = matchSinglePattern(store, patterns[0]);
142
+
143
+ for (let i = 1; i < patterns.length; i++) {
144
+ const pattern = patterns[i];
145
+ const newBindings = [];
146
+
147
+ for (const binding of currentBindings) {
148
+ const instantiatedPattern = instantiatePatternForMatching(pattern, binding);
149
+ const matches = matchSinglePattern(store, instantiatedPattern);
150
+
151
+ for (const match of matches) {
152
+ const merged = { ...binding, ...match };
153
+ newBindings.push(merged);
154
+ }
155
+ }
156
+
157
+ currentBindings = newBindings;
158
+
159
+ if (currentBindings.length === 0) {
160
+ break;
161
+ }
162
+ }
163
+
164
+ return currentBindings;
165
+ }
166
+
167
+ /**
168
+ * Match a single pattern against the store
169
+ *
170
+ * @param {Store} store - RDF store
171
+ * @param {Object} pattern - Pattern with type/value objects
172
+ * @returns {Object[]} Array of bindings
173
+ */
174
+ function matchSinglePattern(store, pattern) {
175
+ const subject = patternElementToTerm(pattern.subject);
176
+ const predicate = patternElementToTerm(pattern.predicate);
177
+ const object = patternElementToTerm(pattern.object);
178
+ const graph = pattern.graph ? patternElementToTerm(pattern.graph) : null;
179
+
180
+ const quads = getQuads(store, subject, predicate, object, graph);
181
+ const bindings = [];
182
+
183
+ for (const quad of quads) {
184
+ const binding = {};
185
+
186
+ if (pattern.subject && pattern.subject.type === 'variable') {
187
+ const varName = pattern.subject.value.replace(/^\?/, '');
188
+ binding[varName] = quad.subject;
189
+ }
190
+
191
+ if (pattern.predicate && pattern.predicate.type === 'variable') {
192
+ const varName = pattern.predicate.value.replace(/^\?/, '');
193
+ binding[varName] = quad.predicate;
194
+ }
195
+
196
+ if (pattern.object && pattern.object.type === 'variable') {
197
+ const varName = pattern.object.value.replace(/^\?/, '');
198
+ binding[varName] = quad.object;
199
+ }
200
+
201
+ if (pattern.graph && pattern.graph.type === 'variable') {
202
+ const varName = pattern.graph.value.replace(/^\?/, '');
203
+ binding[varName] = quad.graph;
204
+ }
205
+
206
+ bindings.push(binding);
207
+ }
208
+
209
+ return bindings;
210
+ }
211
+
212
+ /**
213
+ * Convert a pattern element to an N3 term (null for variables)
214
+ *
215
+ * @param {Object} element - Pattern element with type and value
216
+ * @returns {Object|null} N3 term or null (for variables)
217
+ */
218
+ function patternElementToTerm(element) {
219
+ if (!element) return null;
220
+
221
+ if (element.type === 'variable') {
222
+ return null;
223
+ }
224
+
225
+ if (element.type === 'namedNode') {
226
+ return namedNode(element.value);
227
+ }
228
+
229
+ if (element.type === 'prefixed') {
230
+ return namedNode(expandPrefix(element.value));
231
+ }
232
+
233
+ if (element.type === 'literal') {
234
+ return literal(element.value);
235
+ }
236
+
237
+ if (typeof element === 'object' && element.value) {
238
+ return element;
239
+ }
240
+
241
+ return null;
242
+ }
243
+
244
+ /**
245
+ * Instantiate a pattern with bindings for matching
246
+ *
247
+ * @param {Object} pattern - Pattern template
248
+ * @param {Object} bindings - Variable bindings
249
+ * @returns {Object} Pattern with variables replaced
250
+ */
251
+ function instantiatePatternForMatching(pattern, bindings) {
252
+ return {
253
+ subject: instantiatePatternElement(pattern.subject, bindings),
254
+ predicate: instantiatePatternElement(pattern.predicate, bindings),
255
+ object: instantiatePatternElement(pattern.object, bindings),
256
+ graph: pattern.graph ? instantiatePatternElement(pattern.graph, bindings) : null,
257
+ };
258
+ }
259
+
260
+ /**
261
+ * Instantiate a pattern element
262
+ *
263
+ * @param {Object} element - Pattern element
264
+ * @param {Object} bindings - Variable bindings
265
+ * @returns {Object} Instantiated element
266
+ */
267
+ function instantiatePatternElement(element, bindings) {
268
+ if (!element) return null;
269
+
270
+ if (element.type === 'variable') {
271
+ const varName = element.value.replace(/^\?/, '');
272
+ const boundValue = bindings[varName];
273
+ if (boundValue) {
274
+ return boundValue;
275
+ }
276
+ return element;
277
+ }
278
+
279
+ return element;
280
+ }
281
+
282
+ /**
283
+ * Instantiate a consequent pattern with variable bindings
284
+ *
285
+ * @param {Object} pattern - Consequent pattern
286
+ * @param {Object} bindings - Variable bindings
287
+ * @returns {Quad|null} Instantiated quad or null
288
+ */
289
+ function instantiateConsequent(pattern, bindings) {
290
+ const subject = instantiateTerm(pattern.subject, bindings);
291
+ const predicate = instantiateTerm(pattern.predicate, bindings);
292
+ const object = instantiateTerm(pattern.object, bindings);
293
+
294
+ if (!subject || !predicate || !object) {
295
+ return null;
296
+ }
297
+
298
+ return quad(subject, predicate, object);
299
+ }
300
+
301
+ /**
302
+ * Instantiate a term with variable bindings
303
+ *
304
+ * @param {string|Object} term - Term or variable
305
+ * @param {Object} bindings - Variable bindings
306
+ * @returns {Object|null} Instantiated term or null
307
+ */
308
+ function instantiateTerm(term, bindings) {
309
+ if (!term) return null;
310
+
311
+ if (typeof term === 'object' && term.type === 'variable') {
312
+ const varName = term.value.replace(/^\?/, '');
313
+ const boundValue = bindings[varName];
314
+ if (boundValue && typeof boundValue === 'object' && boundValue.value) {
315
+ return boundValue;
316
+ }
317
+ return null;
318
+ }
319
+
320
+ if (typeof term === 'string' && term.startsWith('?')) {
321
+ const varName = term.replace(/^\?/, '');
322
+ const boundValue = bindings[varName];
323
+ if (boundValue && typeof boundValue === 'object' && boundValue.value) {
324
+ return boundValue;
325
+ }
326
+ return null;
327
+ }
328
+
329
+ if (typeof term === 'object' && term.termType) {
330
+ return term;
331
+ }
332
+
333
+ if (typeof term === 'object' && term.type === 'namedNode' && term.value) {
334
+ return namedNode(term.value);
335
+ }
336
+
337
+ if (typeof term === 'object' && term.type === 'literal' && term.value) {
338
+ return literal(term.value);
339
+ }
340
+
341
+ if (typeof term === 'object' && term.type === 'prefixed' && term.value) {
342
+ return namedNode(expandPrefix(term.value));
343
+ }
344
+
345
+ return null;
346
+ }
347
+
348
+ /**
349
+ * Check if a quad already exists in the store
350
+ *
351
+ * @param {Store} store - RDF store
352
+ * @param {Quad} quadToCheck - Quad to check
353
+ * @returns {boolean} True if quad exists
354
+ */
355
+ function quadExists(store, quadToCheck) {
356
+ if (!quadToCheck || !quadToCheck.subject || !quadToCheck.predicate || !quadToCheck.object) {
357
+ return false;
358
+ }
359
+
360
+ const matches = store.getQuads(
361
+ quadToCheck.subject,
362
+ quadToCheck.predicate,
363
+ quadToCheck.object,
364
+ quadToCheck.graph || null
365
+ );
366
+ return matches.length > 0;
367
+ }
368
+
369
+ /**
370
+ * Expand a prefixed URI (basic implementation)
371
+ *
372
+ * @param {string} prefixed - Prefixed URI (e.g., 'rdf:type')
373
+ * @returns {string} Expanded URI
374
+ */
375
+ function expandPrefix(prefixed) {
376
+ const prefixMap = {
377
+ rdf: 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
378
+ rdfs: 'http://www.w3.org/2000/01/rdf-schema#',
379
+ owl: 'http://www.w3.org/2002/07/owl#',
380
+ xsd: 'http://www.w3.org/2001/XMLSchema#',
381
+ foaf: 'http://xmlns.com/foaf/0.1/',
382
+ skos: 'http://www.w3.org/2004/02/skos/core#',
383
+ };
384
+
385
+ const [prefix, local] = prefixed.split(':');
386
+ const baseUri = prefixMap[prefix];
387
+
388
+ if (!baseUri) {
389
+ return prefixed;
390
+ }
391
+
392
+ return baseUri + local;
393
+ }
394
+
395
+ /**
396
+ * Get all inferred quads from the engine
397
+ *
398
+ * @param {Object} engine - Inference engine
399
+ * @returns {Quad[]} Array of inferred quads
400
+ *
401
+ * @example
402
+ * const inferred = getInferredQuads(engine);
403
+ * console.log(`${inferred.length} facts were inferred`);
404
+ */
405
+ export function getInferredQuads(engine) {
406
+ return engine.inferredQuads;
407
+ }
408
+
409
+ /**
410
+ * Reset the inference engine (clear inferred quads)
411
+ *
412
+ * @param {Object} engine - Inference engine
413
+ * @returns {void}
414
+ */
415
+ export function resetEngine(engine) {
416
+ engine.inferredQuads = [];
417
+ engine.iterations = 0;
418
+ }