skynet-graph 1.2.0
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/CONTRIBUTING.md +22 -0
- package/LICENSE +661 -0
- package/README.md +245 -0
- package/bin/sg +20 -0
- package/concepts/README.md +23 -0
- package/concepts/_substrate/Claim/Confidence/Trusted.json +6 -0
- package/concepts/_substrate/Claim/Confidence.json +10 -0
- package/concepts/_substrate/Claim/Freshness/Stale.json +7 -0
- package/concepts/_substrate/Claim/Freshness.json +5 -0
- package/concepts/_substrate/Claim/Verification/Refuted.json +6 -0
- package/concepts/_substrate/Claim/Verification.json +6 -0
- package/concepts/_substrate/Claim.json +4 -0
- package/concepts/_substrate/Frontier/InBeam.json +6 -0
- package/concepts/_substrate/Frontier/Stuck.json +6 -0
- package/concepts/_substrate/Frontier.json +5 -0
- package/concepts/_substrate/Intake/ToTask.json +22 -0
- package/concepts/_substrate/Intake/Typed.json +10 -0
- package/concepts/_substrate/Intake.json +27 -0
- package/concepts/_substrate/Task/Answer.json +12 -0
- package/concepts/_substrate/Task/Complexity/Atomic.json +5 -0
- package/concepts/_substrate/Task/Complexity/Compound/Expansion.json +7 -0
- package/concepts/_substrate/Task/Complexity/Compound.json +6 -0
- package/concepts/_substrate/Task/Complexity.json +6 -0
- package/concepts/_substrate/Task/EvalComplexity.json +13 -0
- package/concepts/_substrate/Task/ReportUp.json +6 -0
- package/concepts/_substrate/Task/Rollup.json +10 -0
- package/concepts/_substrate/Task.json +5 -0
- package/concepts/clinical/Diagnosis/Medication.json +6 -0
- package/concepts/clinical/Diagnosis.json +15 -0
- package/concepts/clinical/Observation/LabValue/OutOfRange.json +6 -0
- package/concepts/clinical/Observation/LabValue.json +5 -0
- package/concepts/clinical/Observation.json +6 -0
- package/concepts/common/Document/pathBasket.json +6 -0
- package/concepts/common/Document.json +3 -0
- package/concepts/common/Edge/Distance.json +13 -0
- package/concepts/common/Edge/Stay/LongStay.json +8 -0
- package/concepts/common/Edge/Stay.json +8 -0
- package/concepts/common/Edge/Travel/LongTravel.json +16 -0
- package/concepts/common/Edge/Travel/ShortTravel.json +11 -0
- package/concepts/common/Edge/Travel/targetNode.json +7 -0
- package/concepts/common/Edge/Travel.json +7 -0
- package/concepts/common/Edge.json +3 -0
- package/concepts/common/Vertice.json +3 -0
- package/concepts/supply/Fulfillment.json +10 -0
- package/concepts/supply/Inventory/Reorder.json +7 -0
- package/concepts/supply/Inventory.json +5 -0
- package/concepts/supply/Procurement/SupplierConfirm.json +6 -0
- package/concepts/supply/Procurement.json +8 -0
- package/concepts/supply/Transport.json +5 -0
- package/doc/API.md +556 -0
- package/doc/CAPABILITIES.md +373 -0
- package/doc/MODELISATION.md +511 -0
- package/doc/architecture.md +267 -0
- package/doc/concept-as-graph.md +379 -0
- package/doc/concept-learning.md +168 -0
- package/doc/creative-loop-map.md +202 -0
- package/doc/original-2016-doc.md +148 -0
- package/doc/usage.md +501 -0
- package/lib/authoring/abstract.js +401 -0
- package/lib/authoring/abstraction.js +190 -0
- package/lib/authoring/adapt.js +393 -0
- package/lib/authoring/ancestry.js +216 -0
- package/lib/authoring/author.js +147 -0
- package/lib/authoring/bounded-merge.js +57 -0
- package/lib/authoring/canon.js +158 -0
- package/lib/authoring/clock.js +123 -0
- package/lib/authoring/combinator.js +92 -0
- package/lib/authoring/compete.js +76 -0
- package/lib/authoring/compose-hotspot.js +314 -0
- package/lib/authoring/compress.js +279 -0
- package/lib/authoring/concept-net.js +254 -0
- package/lib/authoring/concepts.js +66 -0
- package/lib/authoring/context-project.js +277 -0
- package/lib/authoring/contract.js +476 -0
- package/lib/authoring/corpus-pack.js +125 -0
- package/lib/authoring/cost-probe.js +86 -0
- package/lib/authoring/crystallize.js +555 -0
- package/lib/authoring/dag-decompose.js +256 -0
- package/lib/authoring/dataset-adapter.js +253 -0
- package/lib/authoring/debug-provider.js +92 -0
- package/lib/authoring/decompose.js +343 -0
- package/lib/authoring/emittability.js +236 -0
- package/lib/authoring/equilibrium.js +160 -0
- package/lib/authoring/extract.js +153 -0
- package/lib/authoring/forest.js +55 -0
- package/lib/authoring/forge-fallback.js +116 -0
- package/lib/authoring/givens.js +121 -0
- package/lib/authoring/glossary.js +177 -0
- package/lib/authoring/grammar-graph.js +191 -0
- package/lib/authoring/granularity.js +80 -0
- package/lib/authoring/graph-net.js +108 -0
- package/lib/authoring/ground.js +148 -0
- package/lib/authoring/higher-order.js +85 -0
- package/lib/authoring/hotspot.js +132 -0
- package/lib/authoring/hysteresis.js +85 -0
- package/lib/authoring/index.js +106 -0
- package/lib/authoring/lattice-morphism.js +61 -0
- package/lib/authoring/lattice-pack.js +148 -0
- package/lib/authoring/leaf-io.js +77 -0
- package/lib/authoring/library.js +156 -0
- package/lib/authoring/lifecycle.js +92 -0
- package/lib/authoring/loop.js +193 -0
- package/lib/authoring/master-loop.js +132 -0
- package/lib/authoring/mdl.js +209 -0
- package/lib/authoring/memo-stability.js +122 -0
- package/lib/authoring/method-explorer.js +138 -0
- package/lib/authoring/method-pack.js +181 -0
- package/lib/authoring/method.js +202 -0
- package/lib/authoring/mine.js +510 -0
- package/lib/authoring/mount.js +119 -0
- package/lib/authoring/negotiate.js +100 -0
- package/lib/authoring/parametric.js +139 -0
- package/lib/authoring/reaggregate.js +75 -0
- package/lib/authoring/rebalance.js +157 -0
- package/lib/authoring/recall.js +135 -0
- package/lib/authoring/registry.js +350 -0
- package/lib/authoring/relearn.js +147 -0
- package/lib/authoring/retention.js +89 -0
- package/lib/authoring/segment-proxy.js +114 -0
- package/lib/authoring/serve-leaf.js +64 -0
- package/lib/authoring/slot-aware-serve.js +77 -0
- package/lib/authoring/sound-invoke.js +59 -0
- package/lib/authoring/split-serve.js +74 -0
- package/lib/authoring/ste.js +239 -0
- package/lib/authoring/stock.js +101 -0
- package/lib/authoring/store.js +94 -0
- package/lib/authoring/supervise.js +138 -0
- package/lib/authoring/support.js +131 -0
- package/lib/authoring/task-mirror.js +51 -0
- package/lib/authoring/typed-loop.js +160 -0
- package/lib/authoring/validate.js +569 -0
- package/lib/authoring/widen.js +88 -0
- package/lib/combos/appliance.js +189 -0
- package/lib/combos/critique.js +377 -0
- package/lib/combos/defaults.js +86 -0
- package/lib/combos/durable-runner.js +115 -0
- package/lib/combos/forge.js +209 -0
- package/lib/combos/index.js +86 -0
- package/lib/combos/learning-library.js +253 -0
- package/lib/combos/mixture-serve.js +193 -0
- package/lib/combos/plan-loop.js +192 -0
- package/lib/combos/proxy-cache.js +272 -0
- package/lib/combos/self-mod.js +94 -0
- package/lib/durable/audit.js +102 -0
- package/lib/durable/checkpoint-store.js +550 -0
- package/lib/durable/fold.js +62 -0
- package/lib/durable/interpreter.js +242 -0
- package/lib/durable/xlate.js +185 -0
- package/lib/graph/Graph.js +2486 -0
- package/lib/graph/expr.js +307 -0
- package/lib/graph/index.js +26 -0
- package/lib/graph/log.js +134 -0
- package/lib/graph/objects/Concept.js +382 -0
- package/lib/graph/objects/Entity.js +503 -0
- package/lib/graph/objects/Node.js +57 -0
- package/lib/graph/objects/PathMap.js +557 -0
- package/lib/graph/objects/Segment.js +100 -0
- package/lib/graph/tasks/stabilize.js +59 -0
- package/lib/graph/tasks/taskflow.js +247 -0
- package/lib/index.js +122 -0
- package/lib/load.js +132 -0
- package/lib/providers/backends.js +117 -0
- package/lib/providers/borderline.js +129 -0
- package/lib/providers/cache.js +226 -0
- package/lib/providers/canonicalize.js +198 -0
- package/lib/providers/constat.js +107 -0
- package/lib/providers/geo.js +53 -0
- package/lib/providers/index.js +116 -0
- package/lib/providers/intake.js +263 -0
- package/lib/providers/llm-local.js +88 -0
- package/lib/providers/llm.js +236 -0
- package/lib/providers/local-host.js +197 -0
- package/lib/providers/merge-consistency.js +162 -0
- package/lib/providers/nogood.js +149 -0
- package/lib/providers/reason-loop.js +223 -0
- package/lib/providers/semiring.js +285 -0
- package/lib/providers/solver-fork.js +163 -0
- package/lib/providers/stats.js +228 -0
- package/lib/providers/verify.js +120 -0
- package/lib/runtime/index.js +135 -0
- package/lib/runtime/invoke-pool.js +92 -0
- package/lib/runtime/protocol.js +210 -0
- package/lib/runtime/transport-socket.js +77 -0
- package/lib/runtime/worker-entry.js +31 -0
- package/lib/sg/cli.js +601 -0
- package/lib/sg/log-sinks.js +214 -0
- package/lib/sg/mcp.js +395 -0
- package/lib/sg/proxy-run.js +66 -0
- package/lib/sg/serve-demo.js +185 -0
- package/lib/sg/serve.js +205 -0
- package/lib/sg/trace.js +118 -0
- package/lib/studio/protocol.js +45 -0
- package/lib/studio/public/app.js +310 -0
- package/lib/studio/public/components/ConceptEditor.js +43 -0
- package/lib/studio/public/components/ConceptTree.js +43 -0
- package/lib/studio/public/components/CorpusPanel.js +68 -0
- package/lib/studio/public/components/ForkTree.js +36 -0
- package/lib/studio/public/components/GrammarGraph.js +99 -0
- package/lib/studio/public/components/GraphCanvas.js +156 -0
- package/lib/studio/public/components/Inspector.js +48 -0
- package/lib/studio/public/components/LearningPanel.js +85 -0
- package/lib/studio/public/components/PromptConsole.js +46 -0
- package/lib/studio/public/components/ProviderTrace.js +39 -0
- package/lib/studio/public/components/SessionSplit.js +47 -0
- package/lib/studio/public/components/TilingOverlay.js +40 -0
- package/lib/studio/public/components/Timeline.js +57 -0
- package/lib/studio/public/index.html +26 -0
- package/lib/studio/public/styles.css +185 -0
- package/lib/studio/public/ws.js +58 -0
- package/lib/studio/server.js +130 -0
- package/lib/studio/session.js +397 -0
- package/lib/studio/studio.js +149 -0
- package/package.json +42 -0
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2026 Nathanael Braun
|
|
3
|
+
* @author : Nathanael BRAUN <pp9ping@gmail.com>
|
|
4
|
+
*
|
|
5
|
+
* This program is free software: you can redistribute it and/or modify
|
|
6
|
+
* it under the terms of the GNU Affero General Public License as published by
|
|
7
|
+
* the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
* (at your option) any later version.
|
|
9
|
+
*
|
|
10
|
+
* This program is distributed in the hope that it will be useful,
|
|
11
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
* GNU Affero General Public License for more details.
|
|
14
|
+
*
|
|
15
|
+
* You should have received a copy of the GNU Affero General Public License
|
|
16
|
+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
+
*/
|
|
18
|
+
'use strict';
|
|
19
|
+
/**
|
|
20
|
+
* Safe expression evaluator for the concept mini-DSL — replaces the pervasive
|
|
21
|
+
* `new Function(...)` usage (Concept asserts/ensure, Graph.queryMaps /
|
|
22
|
+
* getChildMatching, Entity.doEval / test, PathMap queries).
|
|
23
|
+
*
|
|
24
|
+
* Why: `new Function` is an `eval` variant — RCE surface (LLM/host text flows
|
|
25
|
+
* into expressions), CSP-incompatible, and a hard blocker for WASM. This module
|
|
26
|
+
* parses the *full expression grammar* with `jsep` and interprets the AST with
|
|
27
|
+
* no code generation, so expressiveness is NOT capped: member access (./[]),
|
|
28
|
+
* method/function calls, ternary, array/object literals and every operator all
|
|
29
|
+
* work — only statement-level JS (loops/assignments) is out of reach, which
|
|
30
|
+
* `return (EXPR)` never meaningfully hosted anyway.
|
|
31
|
+
*
|
|
32
|
+
* Reference resolution preserves the engine's exact semantics: a `$ref` token
|
|
33
|
+
* (including cross-object walks like `$originNode:Position.lat` and the `$$id`
|
|
34
|
+
* bagRef/literal-id form) is captured *whole* by the original regex and handed
|
|
35
|
+
* verbatim to the injected resolver — i.e. to `getRef`, which walks the path.
|
|
36
|
+
* The regex rewrites `$ref` -> `__ref("ref")` (the same transformation the old
|
|
37
|
+
* code applied to `scope.getRef("ref")`), then jsep parses the rest.
|
|
38
|
+
*
|
|
39
|
+
* Security: because member access + calls are allowed, the classic
|
|
40
|
+
* `"".constructor.constructor("…")()` escape is blocked by refusing access to
|
|
41
|
+
* `constructor` / `__proto__` / `prototype`. No assert/query legitimately needs
|
|
42
|
+
* them, so this is not a cap on expressiveness — it is the whole point of
|
|
43
|
+
* dropping `eval`.
|
|
44
|
+
*
|
|
45
|
+
* Runtime errors yield `undefined`, faithful to the original
|
|
46
|
+
* `try{ … }catch{ return undefined }` wrapping.
|
|
47
|
+
*
|
|
48
|
+
* @author Skynet-Graph V1 (Neurosymbolic Reasoning Graph) — Phase 0
|
|
49
|
+
*/
|
|
50
|
+
|
|
51
|
+
var _jsep = require('jsep');
|
|
52
|
+
var jsep = (_jsep && _jsep.default) ? _jsep.default : _jsep;
|
|
53
|
+
|
|
54
|
+
var _ternary = require('@jsep-plugin/ternary');
|
|
55
|
+
var _object = require('@jsep-plugin/object');
|
|
56
|
+
jsep.plugins.register(
|
|
57
|
+
(_ternary && _ternary.default) ? _ternary.default : _ternary,
|
|
58
|
+
(_object && _object.default) ? _object.default : _object
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
// $ref / $$ref -> __ref("ref") (identical capture to the original engine regex)
|
|
62
|
+
var REF_RE = /\$(\$?[A-Za-z_][\w.:$]*)/g;
|
|
63
|
+
var REF_FN = '__ref';
|
|
64
|
+
|
|
65
|
+
// Properties that open a path back to the Function constructor / prototype chain.
|
|
66
|
+
var BLOCKED_PROPS = { constructor: true, __proto__: true, prototype: true };
|
|
67
|
+
|
|
68
|
+
// Stratified set-aggregation over a value array (roadmap #8) — closes the aggregation gap
|
|
69
|
+
// (getRef walks SINGLE refs; only `.length` worked) WITHOUT lambdas, so it stays inside the
|
|
70
|
+
// safe evaluator. `op`/`val` form a comparison predicate; omit them for a truthiness predicate.
|
|
71
|
+
// count($votes,'==','yes') >= 2 all($childScores,'>=',0.5) any($flags)
|
|
72
|
+
// "Stratified" = run it after the collection is complete (the existing cardinality gate,
|
|
73
|
+
// e.g. `$votes.length==$expected && count($votes,'==','yes')>=k`), exactly like `.length`.
|
|
74
|
+
function aggCompare( a, op, b ) {
|
|
75
|
+
switch ( op ) {
|
|
76
|
+
case '==': return a == b; case '!=': return a != b;
|
|
77
|
+
case '===': return a === b; case '!==': return a !== b;
|
|
78
|
+
case '<': return a < b; case '>': return a > b;
|
|
79
|
+
case '<=': return a <= b; case '>=': return a >= b;
|
|
80
|
+
}
|
|
81
|
+
return false; // unknown op -> matches nothing
|
|
82
|
+
}
|
|
83
|
+
function aggPredicate( op, val ) {
|
|
84
|
+
return op === undefined ? function ( e ) { return !!e; } : function ( e ) { return aggCompare(e, op, val); };
|
|
85
|
+
}
|
|
86
|
+
function aggCount( arr, op, val ) {
|
|
87
|
+
if ( !Array.isArray(arr) ) return 0;
|
|
88
|
+
var p = aggPredicate(op, val), n = 0;
|
|
89
|
+
for ( var i = 0; i < arr.length; i++ ) if ( p(arr[i]) ) n++;
|
|
90
|
+
return n;
|
|
91
|
+
}
|
|
92
|
+
function aggAll( arr, op, val ) { // vacuously true on []; false on a non-array (not-yet-present)
|
|
93
|
+
if ( !Array.isArray(arr) ) return false;
|
|
94
|
+
var p = aggPredicate(op, val);
|
|
95
|
+
for ( var i = 0; i < arr.length; i++ ) if ( !p(arr[i]) ) return false;
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
function aggAny( arr, op, val ) {
|
|
99
|
+
if ( !Array.isArray(arr) ) return false;
|
|
100
|
+
var p = aggPredicate(op, val);
|
|
101
|
+
for ( var i = 0; i < arr.length; i++ ) if ( p(arr[i]) ) return true;
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Curated, side-effect-free globals reachable by bare identifiers. Deliberately
|
|
106
|
+
// excludes Function/eval/require/process/globalThis etc. (absent -> undefined).
|
|
107
|
+
var GLOBALS = {
|
|
108
|
+
Math: Math, JSON: JSON,
|
|
109
|
+
Number: Number, String: String, Boolean: Boolean, Array: Array,
|
|
110
|
+
parseInt: parseInt, parseFloat: parseFloat,
|
|
111
|
+
isNaN: isNaN, isFinite: isFinite,
|
|
112
|
+
count: aggCount, all: aggAll, any: aggAny, // stratified set-aggregation (#8)
|
|
113
|
+
undefined: undefined, NaN: NaN, Infinity: Infinity
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
function hasOwn( o, k ) { return o != null && Object.prototype.hasOwnProperty.call(o, k); }
|
|
117
|
+
|
|
118
|
+
function safeProp( name ) {
|
|
119
|
+
if ( BLOCKED_PROPS[name] ) throw new Error('expr: access to "' + name + '" is blocked');
|
|
120
|
+
return name;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// ----------------------------------------------------------- interpreter
|
|
124
|
+
function evalNode( node, ctx ) {
|
|
125
|
+
switch ( node.type ) {
|
|
126
|
+
case 'Literal':
|
|
127
|
+
return node.value;
|
|
128
|
+
|
|
129
|
+
case 'Identifier': {
|
|
130
|
+
var nm = node.name;
|
|
131
|
+
if ( hasOwn(ctx.names, nm) ) return ctx.names[nm];
|
|
132
|
+
if ( hasOwn(GLOBALS, nm) ) return GLOBALS[nm];
|
|
133
|
+
return undefined;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
case 'MemberExpression': {
|
|
137
|
+
var obj = evalNode(node.object, ctx);
|
|
138
|
+
if ( obj == null ) return undefined;
|
|
139
|
+
var key = node.computed ? evalNode(node.property, ctx) : node.property.name;
|
|
140
|
+
return obj[safeProp(key)];
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
case 'CallExpression': {
|
|
144
|
+
var callee = node.callee;
|
|
145
|
+
|
|
146
|
+
// `$ref` marker: callee is the injected __ref(<string literal>)
|
|
147
|
+
if ( callee.type === 'Identifier' && callee.name === REF_FN ) {
|
|
148
|
+
return ctx.resolve(node.arguments[0].value);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
var thisObj, fn;
|
|
152
|
+
if ( callee.type === 'MemberExpression' ) {
|
|
153
|
+
thisObj = evalNode(callee.object, ctx);
|
|
154
|
+
if ( thisObj == null ) return undefined;
|
|
155
|
+
var mkey = callee.computed ? evalNode(callee.property, ctx) : callee.property.name;
|
|
156
|
+
fn = thisObj[safeProp(mkey)];
|
|
157
|
+
} else {
|
|
158
|
+
thisObj = undefined;
|
|
159
|
+
fn = evalNode(callee, ctx);
|
|
160
|
+
}
|
|
161
|
+
if ( typeof fn !== 'function' ) return undefined;
|
|
162
|
+
return fn.apply(thisObj, node.arguments.map(function ( a ) { return evalNode(a, ctx); }));
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
case 'BinaryExpression':
|
|
166
|
+
case 'LogicalExpression':
|
|
167
|
+
return evalBinary(node, ctx);
|
|
168
|
+
|
|
169
|
+
case 'UnaryExpression': {
|
|
170
|
+
var v = evalNode(node.argument, ctx);
|
|
171
|
+
switch ( node.operator ) {
|
|
172
|
+
case '!': return !v;
|
|
173
|
+
case '-': return -v;
|
|
174
|
+
case '+': return +v;
|
|
175
|
+
case '~': return ~v;
|
|
176
|
+
case 'typeof': return typeof v;
|
|
177
|
+
}
|
|
178
|
+
throw new Error('expr: unsupported unary "' + node.operator + '"');
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
case 'ConditionalExpression':
|
|
182
|
+
return evalNode(node.test, ctx)
|
|
183
|
+
? evalNode(node.consequent, ctx)
|
|
184
|
+
: evalNode(node.alternate, ctx);
|
|
185
|
+
|
|
186
|
+
case 'ArrayExpression':
|
|
187
|
+
return node.elements.map(function ( e ) { return evalNode(e, ctx); });
|
|
188
|
+
|
|
189
|
+
case 'ObjectExpression': {
|
|
190
|
+
var o = {};
|
|
191
|
+
node.properties.forEach(function ( pr ) {
|
|
192
|
+
var k = pr.computed ? evalNode(pr.key, ctx)
|
|
193
|
+
: (pr.key.name != null ? pr.key.name : pr.key.value);
|
|
194
|
+
o[safeProp(String(k))] = evalNode(pr.value, ctx);
|
|
195
|
+
});
|
|
196
|
+
return o;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
case 'Compound': {
|
|
200
|
+
var last;
|
|
201
|
+
node.body.forEach(function ( n ) { last = evalNode(n, ctx); });
|
|
202
|
+
return last;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
case 'ThisExpression':
|
|
206
|
+
return undefined;
|
|
207
|
+
}
|
|
208
|
+
throw new Error('expr: unsupported node "' + node.type + '"');
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function evalBinary( node, ctx ) {
|
|
212
|
+
var op = node.operator;
|
|
213
|
+
// logical operators short-circuit and return the operand (like JS / the
|
|
214
|
+
// original `return (expr)`)
|
|
215
|
+
if ( op === '&&' ) return evalNode(node.left, ctx) && evalNode(node.right, ctx);
|
|
216
|
+
if ( op === '||' ) return evalNode(node.left, ctx) || evalNode(node.right, ctx);
|
|
217
|
+
|
|
218
|
+
var l = evalNode(node.left, ctx), r = evalNode(node.right, ctx);
|
|
219
|
+
switch ( op ) {
|
|
220
|
+
case '==': return l == r; // loose, matching original DSL semantics
|
|
221
|
+
case '!=': return l != r;
|
|
222
|
+
case '===': return l === r;
|
|
223
|
+
case '!==': return l !== r;
|
|
224
|
+
case '<': return l < r;
|
|
225
|
+
case '>': return l > r;
|
|
226
|
+
case '<=': return l <= r;
|
|
227
|
+
case '>=': return l >= r;
|
|
228
|
+
case '+': return l + r;
|
|
229
|
+
case '-': return l - r;
|
|
230
|
+
case '*': return l * r;
|
|
231
|
+
case '/': return l / r;
|
|
232
|
+
case '%': return l % r;
|
|
233
|
+
case '**': return Math.pow(l, r);
|
|
234
|
+
case '&': return l & r;
|
|
235
|
+
case '|': return l | r;
|
|
236
|
+
case '^': return l ^ r;
|
|
237
|
+
case '<<': return l << r;
|
|
238
|
+
case '>>': return l >> r;
|
|
239
|
+
case '>>>': return l >>> r;
|
|
240
|
+
}
|
|
241
|
+
throw new Error('expr: unsupported binary "' + op + '"');
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// -------------------------------------------------------------- public API
|
|
245
|
+
function preprocessRefs( src ) {
|
|
246
|
+
return src.replace(REF_RE, function ( _m, ref ) {
|
|
247
|
+
return REF_FN + '(' + JSON.stringify(ref) + ')';
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Join an array of sub-expressions with AND, mirroring the original
|
|
252
|
+
// `query.join(") && (")` wrapped in `(...)`.
|
|
253
|
+
function normalize( source ) {
|
|
254
|
+
if ( Array.isArray(source) ) {
|
|
255
|
+
var parts = source.filter(function ( s ) { return s != null && String(s).trim() !== ''; });
|
|
256
|
+
if ( !parts.length ) return '';
|
|
257
|
+
return '(' + parts.join(') && (') + ')';
|
|
258
|
+
}
|
|
259
|
+
return source == null ? '' : String(source);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Compile a mini-DSL expression into a safe evaluator.
|
|
264
|
+
*
|
|
265
|
+
* @param {string|string[]} source expression, or array AND-joined together
|
|
266
|
+
* @param {{empty?: *}} [options] `empty` is returned for an empty source
|
|
267
|
+
* (default `true`, matching Concept/queryMaps;
|
|
268
|
+
* pass `false` for getChildMatching/test)
|
|
269
|
+
* @returns {function(resolve, names=): *} `resolve(refName)` resolves `$ref`s,
|
|
270
|
+
* `names` (optional) resolves bare identifiers
|
|
271
|
+
* (stands in for doEval's `with(refMap)`).
|
|
272
|
+
* Returns `undefined` on any runtime error.
|
|
273
|
+
* @throws {Error} on syntax errors (faithful: `new Function` also threw at compile)
|
|
274
|
+
*/
|
|
275
|
+
function compileExpression( source, options ) {
|
|
276
|
+
var fallback = ( options && 'empty' in options ) ? options.empty : true;
|
|
277
|
+
var src = normalize(source).trim();
|
|
278
|
+
|
|
279
|
+
if ( !src ) {
|
|
280
|
+
return function () { return fallback; };
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
var ast = jsep(preprocessRefs(src));
|
|
284
|
+
|
|
285
|
+
return function ( resolve, names ) {
|
|
286
|
+
try {
|
|
287
|
+
return evalNode(ast, { resolve: resolve, names: names });
|
|
288
|
+
} catch ( e ) {
|
|
289
|
+
return undefined;
|
|
290
|
+
}
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Parse a mini-DSL expression to its jsep AST (refs already rewritten to `__ref("ref")`
|
|
296
|
+
* CallExpression nodes — identify a ref node by `node.type==='CallExpression' &&
|
|
297
|
+
* node.callee.name===REF_FN`, its key is `node.arguments[0].value`). For author-time
|
|
298
|
+
* static analysis (validate.js); the evaluator path stays `compileExpression`.
|
|
299
|
+
* @returns the AST, or `null` for an empty source. Throws on a syntax error.
|
|
300
|
+
*/
|
|
301
|
+
function parseExpression( source ) {
|
|
302
|
+
var src = normalize(source).trim();
|
|
303
|
+
if ( !src ) return null;
|
|
304
|
+
return jsep(preprocessRefs(src));
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
module.exports = { compileExpression: compileExpression, parseExpression: parseExpression, REF_FN: REF_FN };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2026 Nathanael Braun
|
|
3
|
+
* @author : Nathanael BRAUN <pp9ping@gmail.com>
|
|
4
|
+
*
|
|
5
|
+
* This program is free software: you can redistribute it and/or modify
|
|
6
|
+
* it under the terms of the GNU Affero General Public License as published by
|
|
7
|
+
* the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
* (at your option) any later version.
|
|
9
|
+
*
|
|
10
|
+
* This program is distributed in the hope that it will be useful,
|
|
11
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
* GNU Affero General Public License for more details.
|
|
14
|
+
*
|
|
15
|
+
* You should have received a copy of the GNU Affero General Public License
|
|
16
|
+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
// `__SERVER__` is a host/build flag (true = node/server, false = browser/client) read as
|
|
21
|
+
// a bare global by the engine. Default it to server when undefined so the lib loads
|
|
22
|
+
// standalone without a ReferenceError; a browser host can set globalThis.__SERVER__ =
|
|
23
|
+
// false before requiring the engine. (Previously injected by webpack DefinePlugin / _boot.)
|
|
24
|
+
if ( typeof globalThis.__SERVER__ === 'undefined' ) globalThis.__SERVER__ = true;
|
|
25
|
+
|
|
26
|
+
module.exports = require('./Graph.js');
|
package/lib/graph/log.js
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2026 Nathanael Braun
|
|
3
|
+
* @author : Nathanael BRAUN <pp9ping@gmail.com>
|
|
4
|
+
*
|
|
5
|
+
* This program is free software: you can redistribute it and/or modify
|
|
6
|
+
* it under the terms of the GNU Affero General Public License as published by
|
|
7
|
+
* the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
* (at your option) any later version.
|
|
9
|
+
*
|
|
10
|
+
* This program is distributed in the hope that it will be useful,
|
|
11
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
* GNU Affero General Public License for more details.
|
|
14
|
+
*
|
|
15
|
+
* You should have received a copy of the GNU Affero General Public License
|
|
16
|
+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
+
*/
|
|
18
|
+
'use strict';
|
|
19
|
+
/**
|
|
20
|
+
* Skynet-Graph logger — fs-free core.
|
|
21
|
+
*
|
|
22
|
+
* Lives in the engine (lib/graph/) so every engine file can require it without
|
|
23
|
+
* pulling node:fs; all file/TTY sinks live in lib/sg/. This replaces the old
|
|
24
|
+
* `var debug = console` indirection: same console-compatible call shape, but now
|
|
25
|
+
* leveled, sinked, context-aware and per-graph.
|
|
26
|
+
*
|
|
27
|
+
* Levels (severity descending) — a record reaches sinks iff
|
|
28
|
+
* rank(record.level) <= rank(threshold):
|
|
29
|
+
* error(0) > warn(1) > log(2) > info(3) > verbose(4)
|
|
30
|
+
*
|
|
31
|
+
* A LogRecord { level, ts, label, ctx, msg, args } is JSON-serializable (Errors
|
|
32
|
+
* in args are reduced to {name,message,stack}), so it crosses a worker_threads
|
|
33
|
+
* boundary unchanged.
|
|
34
|
+
*
|
|
35
|
+
* child(ctx) returns a logger that shares the root's sinks / ring buffer / level
|
|
36
|
+
* but merges ctx into every record. A concept-apply tags its logs with
|
|
37
|
+
* { concept, target, applyId } this way, so they can be retrieved afterwards via
|
|
38
|
+
* tail(n,{concept|applyId}) WITHOUT ever being stored on the graph objects.
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
var LEVELS = { error: 0, warn: 1, log: 2, info: 3, verbose: 4 };
|
|
42
|
+
var LEVEL_NAMES = ['error', 'warn', 'log', 'info', 'verbose'];
|
|
43
|
+
|
|
44
|
+
function rankOf ( name ) { return LEVELS[name] == null ? LEVELS.info : LEVELS[name]; }
|
|
45
|
+
|
|
46
|
+
// Reduce args to something JSON-serializable (Errors -> plain object, functions
|
|
47
|
+
// -> tag), so a record survives structured-clone across a worker boundary.
|
|
48
|
+
function sanitizeArgs ( args ) {
|
|
49
|
+
return args.map(function ( a ) {
|
|
50
|
+
if ( a instanceof Error ) return { name: a.name, message: a.message, stack: a.stack };
|
|
51
|
+
if ( typeof a === 'function' ) return '[function ' + (a.name || 'anonymous') + ']';
|
|
52
|
+
return a;
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function recordMatches ( rec, f ) {
|
|
57
|
+
if ( !f ) return true;
|
|
58
|
+
var c = rec.ctx || {};
|
|
59
|
+
if ( f.level != null && rec.level !== f.level ) return false;
|
|
60
|
+
if ( f.concept != null && c.concept !== f.concept ) return false;
|
|
61
|
+
if ( f.target != null && c.target !== f.target ) return false;
|
|
62
|
+
if ( f.applyId != null && c.applyId !== f.applyId ) return false;
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Default sink: format the record back into a console.<level>(...) call so the
|
|
67
|
+
// out-of-the-box behavior matches the old `debug = console` — but level-gated.
|
|
68
|
+
function consoleSink ( rec ) {
|
|
69
|
+
var fn = console[rec.level] || console.log;
|
|
70
|
+
var head = '[' + rec.label + (rec.ctx && rec.ctx.concept ? '·' + rec.ctx.concept : '') + ']';
|
|
71
|
+
fn.apply(console, [head, rec.msg].concat(rec.args || []));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* @param {object} [opts]
|
|
76
|
+
* @param {string} [opts.label='graph']
|
|
77
|
+
* @param {string} [opts.level] threshold; default env SG_LOG_LEVEL or 'warn'
|
|
78
|
+
* @param {function} [opts.onRecord] convenience sink fn(record)
|
|
79
|
+
* @param {number} [opts.capacity=500] ring-buffer size for tail()
|
|
80
|
+
* @param {boolean} [opts.console=true] install the default console sink
|
|
81
|
+
* @returns {object} logger
|
|
82
|
+
*/
|
|
83
|
+
function createLogger ( opts ) {
|
|
84
|
+
opts = opts || {};
|
|
85
|
+
// shared mutable state — a child closes over the SAME state object as its root
|
|
86
|
+
var state = {
|
|
87
|
+
label: opts.label || 'graph',
|
|
88
|
+
level: opts.level || (typeof process !== 'undefined' && process.env.SG_LOG_LEVEL) || 'warn',
|
|
89
|
+
sinks: [],
|
|
90
|
+
buf : [],
|
|
91
|
+
cap : opts.capacity || 500
|
|
92
|
+
};
|
|
93
|
+
if ( opts.console !== false ) state.sinks.push(consoleSink);
|
|
94
|
+
if ( opts.onRecord ) state.sinks.push(opts.onRecord);
|
|
95
|
+
|
|
96
|
+
function make ( ctx ) {
|
|
97
|
+
var logger = {};
|
|
98
|
+
|
|
99
|
+
function emit ( level, msg, args ) {
|
|
100
|
+
if ( rankOf(level) > rankOf(state.level) ) return; // below threshold -> drop
|
|
101
|
+
var rec = { level: level, ts: Date.now(), label: state.label, ctx: ctx, msg: msg, args: sanitizeArgs(args) };
|
|
102
|
+
state.buf.push(rec);
|
|
103
|
+
if ( state.buf.length > state.cap ) state.buf.shift();
|
|
104
|
+
for ( var i = 0 ; i < state.sinks.length ; i++ ) {
|
|
105
|
+
try { state.sinks[i](rec); } catch ( e ) { /* a broken sink must never break the engine */ }
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
LEVEL_NAMES.forEach(function ( lvl ) {
|
|
110
|
+
logger[lvl] = function ( msg ) { emit(lvl, msg, Array.prototype.slice.call(arguments, 1)); };
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
logger.child = function ( extra ) { return make(Object.assign({}, ctx, extra)); };
|
|
114
|
+
logger.addSink = function ( fn ) { state.sinks.push(fn); return fn; };
|
|
115
|
+
logger.removeSink = function ( fn ) { var i = state.sinks.indexOf(fn); if ( i >= 0 ) state.sinks.splice(i, 1); };
|
|
116
|
+
logger.setLevel = function ( name ) { if ( LEVELS[name] != null ) state.level = name; return state.level; };
|
|
117
|
+
logger.tail = function ( n, filter ) {
|
|
118
|
+
var out = filter ? state.buf.filter(function ( r ) { return recordMatches(r, filter); }) : state.buf.slice();
|
|
119
|
+
return n ? out.slice(-n) : out;
|
|
120
|
+
};
|
|
121
|
+
Object.defineProperty(logger, 'level', { get: function () { return state.level; } });
|
|
122
|
+
Object.defineProperty(logger, 'records', { get: function () { return state.buf.slice(); } });
|
|
123
|
+
Object.defineProperty(logger, 'ctx', { get: function () { return ctx; } });
|
|
124
|
+
Object.defineProperty(logger, 'label', { get: function () { return state.label; } });
|
|
125
|
+
return logger;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return make(null);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// process-wide fallback for the rare engine spots with no graph in scope.
|
|
132
|
+
var defaultLogger = createLogger({ label: 'sg' });
|
|
133
|
+
|
|
134
|
+
module.exports = { createLogger: createLogger, defaultLogger: defaultLogger, LEVELS: LEVELS, LEVEL_NAMES: LEVEL_NAMES };
|