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,503 @@
|
|
|
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
|
+
var debug = require('../log').defaultLogger;// module fallback; methods rebind to this._graph._log
|
|
20
|
+
|
|
21
|
+
var isArray = require('is').array;
|
|
22
|
+
var isFunction = require('is').fn;
|
|
23
|
+
var { compileExpression } = require('../expr');
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
let evalReplaceRE = /\$(\$?[a-zA-Z\_][\w\.\:\$]+)/ig;// 30mn;
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
function static_ensure( scope, c, cName ) {
|
|
30
|
+
return function () {
|
|
31
|
+
var __R;
|
|
32
|
+
__R = scope._graph._conceptLib[c].isApplicableTo(scope);
|
|
33
|
+
if ( __R ) !scope._mappedConcepts[cName] && scope._graph.castConcept(scope._._id, c);
|
|
34
|
+
else scope._mappedConcepts[cName] && scope.unCast(cName, null);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function Entity( _, graph ) {
|
|
39
|
+
this.init(_, graph);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
Entity.prototype = {
|
|
43
|
+
/**
|
|
44
|
+
*
|
|
45
|
+
* @param _
|
|
46
|
+
* @param graph
|
|
47
|
+
*/
|
|
48
|
+
init: function ( _, graph ) {
|
|
49
|
+
this._ = _;
|
|
50
|
+
this._graph = graph;
|
|
51
|
+
// `|| {}`: a root concept set with no childConcepts has no _openConcepts —
|
|
52
|
+
// a graph with zero capabilities is degenerate but valid (mounts, stabilizes).
|
|
53
|
+
this._mapOpenConcepts = Object.keys(graph._rootConcept._openConcepts || {});
|
|
54
|
+
this._mappedConcepts = {};
|
|
55
|
+
this._extOpenConcepts = [];
|
|
56
|
+
|
|
57
|
+
this._followersByConceptName = {};
|
|
58
|
+
this._watcherByConceptName = {};
|
|
59
|
+
this._watchers = {};
|
|
60
|
+
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* A child logger bound to THIS object's context {target,type}, memoized on the
|
|
65
|
+
* Entity instance — stored on `this._ctxLog`, NEVER on `this._`, so it is not
|
|
66
|
+
* serialized (logs stay out of the graph). A provider reaches it as `scope.log`:
|
|
67
|
+
* scope.log.warn('positions missing on %s', scope._._id)
|
|
68
|
+
* For apply-correlated context (concept + applyId) use `concept.log(scope)` instead.
|
|
69
|
+
*/
|
|
70
|
+
get log () {
|
|
71
|
+
if ( !this._ctxLog ) {
|
|
72
|
+
var t = this._ && (this._.Node ? 'node' : this._.Segment ? 'segment' : 'object');
|
|
73
|
+
this._ctxLog = this._graph._log.child({ target: this._ && this._._id, type: t });
|
|
74
|
+
}
|
|
75
|
+
return this._ctxLog;
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
// ------------------------------------------------- concepts cast
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Walk this object's OPEN concepts (those reachable given what is already cast)
|
|
82
|
+
* and return the ones that are newly applicable — the discovery step that drives
|
|
83
|
+
* casting. Called at mount and whenever the object is destabilized.
|
|
84
|
+
*
|
|
85
|
+
* It is a tree walk over a stack (`ocStack`, seeded from `_mapOpenConcepts`):
|
|
86
|
+
* - For an open concept with `follow`/`ensure`, install watchers ONCE so the
|
|
87
|
+
* object is re-tested (or auto de/re-cast) when a watched ref changes.
|
|
88
|
+
* - If the concept's fact is already present (`me._[cname]`), it is CAST: mark it
|
|
89
|
+
* in `_mappedConcepts` and push its children onto the stack (descend), so child
|
|
90
|
+
* capabilities only open under a cast parent (enum pushes the chosen branches).
|
|
91
|
+
* - Else it stays OPEN (kept in `cStack`); `autoCast:false` ones are parked in
|
|
92
|
+
* `_extOpenConcepts` (dormant — only retested when a follow-watcher fires).
|
|
93
|
+
* `_mapOpenConcepts` is then replaced by the still-open set, and the applicable
|
|
94
|
+
* (open + isApplicableTo) concepts are returned for specialize() to cast.
|
|
95
|
+
*
|
|
96
|
+
* @returns {Concept[]} concepts to cast now
|
|
97
|
+
*/
|
|
98
|
+
updateApplicableConcepts: function () {
|
|
99
|
+
if ( this._dead ) return [];
|
|
100
|
+
|
|
101
|
+
var me = this,
|
|
102
|
+
execTm = Date.now(),
|
|
103
|
+
push = Array.prototype.push,
|
|
104
|
+
cStack = [],
|
|
105
|
+
ocStack = this._mapOpenConcepts,
|
|
106
|
+
c, i, cname,
|
|
107
|
+
cSchema,
|
|
108
|
+
follow,
|
|
109
|
+
followMap, ensure,
|
|
110
|
+
graph = this._graph;
|
|
111
|
+
|
|
112
|
+
// if (!me._._id)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
while ( ocStack.length ) {
|
|
117
|
+
c = ocStack.pop();
|
|
118
|
+
cname = graph._conceptLib[c]._name;
|
|
119
|
+
cSchema = graph._conceptLib[c]._schema;
|
|
120
|
+
|
|
121
|
+
// if an "open" concept have follow || ensure it must watch some refs
|
|
122
|
+
if ( !me._watchers[cname] && (cSchema.follow || cSchema.ensure) ) {
|
|
123
|
+
followMap = {};
|
|
124
|
+
follow = [];
|
|
125
|
+
follow.map(( v ) => followMap[v] = true);
|
|
126
|
+
ensure = null;
|
|
127
|
+
|
|
128
|
+
me._watchers[cname] = [];
|
|
129
|
+
if ( cSchema.ensure ) {// ensure: auto-cast when the condition holds, auto-uncast when it falls
|
|
130
|
+
ensure = static_ensure(me, c, cname);
|
|
131
|
+
|
|
132
|
+
cSchema.ensure.forEach(
|
|
133
|
+
( exp ) => {
|
|
134
|
+
var e = exp.match(/\$(\$?[a-zA-Z\_][\w\.\:\$]+)/ig);
|
|
135
|
+
// e && e.shift();
|
|
136
|
+
e && e.length
|
|
137
|
+
&& e.forEach(( v ) => {
|
|
138
|
+
followMap[v.substr(1)] = ensure
|
|
139
|
+
})
|
|
140
|
+
}
|
|
141
|
+
);
|
|
142
|
+
follow = Object.keys(followMap);
|
|
143
|
+
|
|
144
|
+
follow.forEach(function ( ref ) {
|
|
145
|
+
// CAPTURE per-concept: `ensure` is a function-scoped `var` REASSIGNED on every
|
|
146
|
+
// loop iteration. Without this capture, every ensure-watcher closure shares the
|
|
147
|
+
// SAME binding, so after the loop ALL of an object's ensure-watchers fire the
|
|
148
|
+
// LAST-processed concept's ensure — earlier defeasible concepts then never
|
|
149
|
+
// re-evaluate on their own fact's change (silent JTMS-retraction cross-wiring).
|
|
150
|
+
var boundEnsure = ensure;
|
|
151
|
+
var wfn = function () { boundEnsure.apply(this, arguments); };
|
|
152
|
+
me._watchers[cname].push(ref, wfn);
|
|
153
|
+
return !graph.getRef(ref, me, wfn) && true || null;
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
cSchema.follow && cSchema.follow.reduce(function ( v, ref ) {// follow: re-test applicability when ref changes
|
|
157
|
+
var cc = c;// capture per-concept (same closure-over-var fix as the ensure path above)
|
|
158
|
+
var wfn = function () {
|
|
159
|
+
graph._conceptLib[cc].isApplicableTo(me, graph) && graph.castConcept(me._._id, cc);
|
|
160
|
+
};
|
|
161
|
+
me._watchers[cname].push(ref, wfn);
|
|
162
|
+
return graph.getRef(ref, me, wfn) && v;
|
|
163
|
+
}, true);
|
|
164
|
+
}
|
|
165
|
+
if ( me._[cname] || me._[cname] === false ) {
|
|
166
|
+
this._mappedConcepts[cname] = graph._conceptLib[c];
|
|
167
|
+
|
|
168
|
+
i = this._extOpenConcepts.indexOf(c);
|
|
169
|
+
if ( i !== -1 ) {
|
|
170
|
+
this._extOpenConcepts.splice(i, 1);
|
|
171
|
+
}
|
|
172
|
+
if ( graph._conceptLib[c]._schema.autoReCast ) {
|
|
173
|
+
graph.castConcept(this._._id, c);
|
|
174
|
+
}
|
|
175
|
+
if ( (!graph._conceptLib[c].isApplicableTo(me, graph) &&
|
|
176
|
+
graph._conceptLib[c]._schema.autoCast !== false) || graph._conceptLib[c].isLeaf ) continue;
|
|
177
|
+
if ( cSchema.type == "enum" ) {
|
|
178
|
+
push.apply(
|
|
179
|
+
ocStack,
|
|
180
|
+
isArray(me._[cname]) ?
|
|
181
|
+
me._[cname]
|
|
182
|
+
:
|
|
183
|
+
me._[cname] && Object.keys(graph._conceptLib[c]._openConcepts) || []
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
// if not leaf check childs
|
|
188
|
+
|
|
189
|
+
push.apply(ocStack, Object.keys(graph._conceptLib[c]._openConcepts));
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
else {
|
|
193
|
+
if ( cSchema.autoCast === false ) {
|
|
194
|
+
i = this._extOpenConcepts.indexOf(c);
|
|
195
|
+
(i == -1) && this._extOpenConcepts.push(c);
|
|
196
|
+
}
|
|
197
|
+
cStack.push(c);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
this._mapOpenConcepts = cStack;
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
let ret = cStack.filter(function ( c ) {// test if c is applicable
|
|
204
|
+
return !!graph._conceptLib[c].isApplicableTo(me, graph);
|
|
205
|
+
}).map(function ( c ) {
|
|
206
|
+
return graph._conceptLib[c];
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
// P1 (#frontierComparator) — OPT-IN per-concept frontier ordering. By default `ret`
|
|
210
|
+
// keeps its discovery order (no behaviour change). When the host supplies
|
|
211
|
+
// `cfg.frontierComparator(a, b, etty, graph)` it orders which applicable concepts
|
|
212
|
+
// specialize() applies FIRST on this object — the only way a learned SOFT-preference
|
|
213
|
+
// policy reduces work (sound-skip is fixpoint-preserving; this is a scheduling hint, so
|
|
214
|
+
// it never changes the fixpoint, only the order cheap-likely-useful experts run). The
|
|
215
|
+
// accumulation order-invariance (E1 monoid) is untouched — it folds the same set.
|
|
216
|
+
var frontierCmp = graph.cfg && graph.cfg.frontierComparator;
|
|
217
|
+
if ( frontierCmp && ret.length > 1 ) ret.sort(function ( a, b ) { return frontierCmp(a, b, me, graph); });
|
|
218
|
+
|
|
219
|
+
// stats
|
|
220
|
+
graph._statsByProvider["updateApplicableConcepts"] = graph._statsByProvider["updateApplicableConcepts"] || 0;
|
|
221
|
+
graph._statsByProvider["updateApplicableConcepts"] += (Date.now() - execTm);
|
|
222
|
+
|
|
223
|
+
return ret;
|
|
224
|
+
},
|
|
225
|
+
/**
|
|
226
|
+
* Retract a cast concept (JTMS-style defeasance): delete its fact, tear down its
|
|
227
|
+
* watchers, drop it from the by-concept index, and CASCADE — uncast every child
|
|
228
|
+
* concept too (they were only reachable under this parent). Unless `unReachable`,
|
|
229
|
+
* the parent is re-armed as OPEN (`_mapOpenConcepts`) so it can re-cast later. If
|
|
230
|
+
* the concept declares a `cleaner` provider, it runs (optional teardown mutation).
|
|
231
|
+
*
|
|
232
|
+
* @param {string} cid concept name/id to retract
|
|
233
|
+
* @param {string} [unReachable] if set, do NOT re-open (the concept is gone for good)
|
|
234
|
+
*/
|
|
235
|
+
unCast : function ( cid, unReachable ) {
|
|
236
|
+
var me = this, graph = me._graph, debug = graph._log;
|
|
237
|
+
|
|
238
|
+
// if ( cid == 'SelectingTarget' )
|
|
239
|
+
|
|
240
|
+
while ( this._watchers[cid] && this._watchers[cid].length )
|
|
241
|
+
this._graph.getRef(this._watchers[cid].shift(), this, this._watchers[cid].shift(), true);
|
|
242
|
+
|
|
243
|
+
if ( this._mappedConcepts[cid] ) {
|
|
244
|
+
|
|
245
|
+
delete this._[cid];
|
|
246
|
+
var c = this._mappedConcepts[cid],
|
|
247
|
+
i = this._graph._mapsByConcept[c._name] && this._graph._mapsByConcept[c._name].indexOf(this._._id);
|
|
248
|
+
this._graph._mapsByConcept[c._name] && (i != -1) && this._graph._mapsByConcept[c._name].splice(i, 1);
|
|
249
|
+
if ( !c ) return debug.warn('cant uncast', cid);
|
|
250
|
+
|
|
251
|
+
//i = .indexOf()
|
|
252
|
+
this._mapOpenConcepts = this._mapOpenConcepts.filter(( v ) => (v.substr(0, c._id.length) != c._id));
|
|
253
|
+
this._extOpenConcepts = this._extOpenConcepts.filter(( v ) => (v.substr(0, c._id.length) != c._id));
|
|
254
|
+
|
|
255
|
+
if ( !unReachable ) {
|
|
256
|
+
this._mapOpenConcepts.push(c._id);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
c._openConcepts
|
|
260
|
+
&& Object.keys(c._openConcepts)
|
|
261
|
+
.forEach(( v ) => (this.unCast(c._openConcepts[v]._name, c._openConcepts[v]._id)), this);
|
|
262
|
+
|
|
263
|
+
// uncaster
|
|
264
|
+
if ( c._schema.cleaner ) {
|
|
265
|
+
var p = isArray(c._schema.cleaner) ? c._schema.cleaner[0] : c._schema.cleaner,
|
|
266
|
+
argz = isArray(c._schema.cleaner) && c._schema.cleaner.slice(1),
|
|
267
|
+
providers = graph.static._providers;
|
|
268
|
+
p = p.split("::");
|
|
269
|
+
if ( providers[p[0]] && providers[p[0]][p[1]] ) {
|
|
270
|
+
graph._taskFlow.wait();
|
|
271
|
+
providers[p[0]][p[1]](
|
|
272
|
+
graph, c, me, argz,
|
|
273
|
+
function ( e, r ) {
|
|
274
|
+
r && graph.pushMutation(r, me._._id);
|
|
275
|
+
e && debug.log("Hum cleaner ", p, " has failed : \n", e, e.stack);
|
|
276
|
+
graph._taskFlow.release();
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
else {
|
|
282
|
+
unReachable && (this._mapOpenConcepts = this._mapOpenConcepts.filter(
|
|
283
|
+
( v ) => (v.substr(0, unReachable.length) != unReachable)));
|
|
284
|
+
}
|
|
285
|
+
this._watchers[cid] = this._mappedConcepts[cid] = undefined;
|
|
286
|
+
},
|
|
287
|
+
/**
|
|
288
|
+
* return an async specialisation task that will apply open-concepts
|
|
289
|
+
* (this will be called as long as the box is unstable)
|
|
290
|
+
*/
|
|
291
|
+
specialize : function () {
|
|
292
|
+
var me = this;
|
|
293
|
+
return function doSpecialize( graph, flow ) {
|
|
294
|
+
if ( me._dead ) return graph.removeObj(me._._id, true);
|
|
295
|
+
|
|
296
|
+
var concepts = me.updateApplicableConcepts(graph),
|
|
297
|
+
todo = [];
|
|
298
|
+
|
|
299
|
+
if ( !concepts.length ) {// if there is no applicable concept; this scope is stable
|
|
300
|
+
return graph.toggleGraphObjectState(me._._id, "stable");
|
|
301
|
+
}
|
|
302
|
+
// if mutations come the node will go in pending state
|
|
303
|
+
//
|
|
304
|
+
concepts.forEach(
|
|
305
|
+
function ( c ) {
|
|
306
|
+
todo.push(c.applyTo(me, graph))
|
|
307
|
+
}
|
|
308
|
+
);
|
|
309
|
+
return todo;
|
|
310
|
+
};
|
|
311
|
+
},
|
|
312
|
+
/**
|
|
313
|
+
* Merge '_' keys-values in the cbox, call watchers if needed
|
|
314
|
+
* @param _
|
|
315
|
+
* @param graph
|
|
316
|
+
*/
|
|
317
|
+
update : function ( _, graph ) {
|
|
318
|
+
var me = this;
|
|
319
|
+
|
|
320
|
+
Object.keys(_).forEach(
|
|
321
|
+
function ( c ) {
|
|
322
|
+
me.set(c, _[c], graph);
|
|
323
|
+
}
|
|
324
|
+
)
|
|
325
|
+
},
|
|
326
|
+
/**
|
|
327
|
+
* Set one fact and fire its listeners. Two listener kinds react here:
|
|
328
|
+
* - watchers (_watcherByConceptName): on-change callbacks (e.g. Segment relink,
|
|
329
|
+
* ensure tests) called with (newValue, oldValue).
|
|
330
|
+
* - followers (_followersByConceptName): dependents to DESTABILIZE so they get
|
|
331
|
+
* re-tested (this is how a `require`/ref consumer wakes when its input appears).
|
|
332
|
+
* Setting a cast concept to null cascades an unCast. `{__push:x}` appends (see below).
|
|
333
|
+
*
|
|
334
|
+
* @param {string} key fact / concept name
|
|
335
|
+
* @param {*} content value (or {__push:x} to append)
|
|
336
|
+
* @param {Graph} graph
|
|
337
|
+
*/
|
|
338
|
+
set : function ( key, content, graph ) {
|
|
339
|
+
var old = this._[key], tmp;
|
|
340
|
+
if ( this._dead ) return;
|
|
341
|
+
|
|
342
|
+
// array-append primitive: `{__push:x}` appends x to the existing array instead
|
|
343
|
+
// of replacing. Append happens here, at apply-time — and mutations are serialized
|
|
344
|
+
// (one mutation thread) — so concurrent fan-in (many writers -> one array) is
|
|
345
|
+
// race-free, unlike a provider-side read-modify-write.
|
|
346
|
+
if ( content && typeof content === 'object' && content.__push !== undefined ) {
|
|
347
|
+
content = (isArray(this._[key]) ? this._[key].slice() : []).concat([content.__push]);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
this._[key] = content;
|
|
351
|
+
this._graph._mapsByConcept[key] = this._graph._mapsByConcept[key] || [];
|
|
352
|
+
(old === undefined) && this._graph._mapsByConcept[key].push(this._._id);
|
|
353
|
+
|
|
354
|
+
if ( content === null && old !== null && this._mappedConcepts[key] ) {// unref concept
|
|
355
|
+
this.unCast(key);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
if ( this._watcherByConceptName[key] ) {
|
|
359
|
+
var i = 0;
|
|
360
|
+
tmp = this._watcherByConceptName[key].slice();// snapshot: a watcher may re-enter set()
|
|
361
|
+
while ( i < tmp.length ) {
|
|
362
|
+
tmp[i].call(
|
|
363
|
+
tmp[i + 1],
|
|
364
|
+
content,
|
|
365
|
+
old
|
|
366
|
+
);
|
|
367
|
+
if ( this._dead ) return;
|
|
368
|
+
i += 2;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
if ( this._followersByConceptName[key] ) {
|
|
372
|
+
var i = -1;
|
|
373
|
+
while ( ++i < this._followersByConceptName[key].length ) {
|
|
374
|
+
// "destabilize ", this._followersByConceptName[key][i],
|
|
375
|
+
// "due to set ", this._._id, key
|
|
376
|
+
// );
|
|
377
|
+
this._graph.toggleGraphObjectState(this._followersByConceptName[key][i], "unstable");
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
},
|
|
381
|
+
/**
|
|
382
|
+
* get an element by concept name
|
|
383
|
+
* if followerId is set, followerId will be destabilized
|
|
384
|
+
*
|
|
385
|
+
* @param key
|
|
386
|
+
* @param followerId
|
|
387
|
+
*/
|
|
388
|
+
get : function ( key, followerId, doUnref ) {
|
|
389
|
+
if ( typeof followerId == "function" ) {
|
|
390
|
+
doUnref ?
|
|
391
|
+
this.unFollow(key, followerId)
|
|
392
|
+
:
|
|
393
|
+
this.follow(key, followerId);
|
|
394
|
+
}
|
|
395
|
+
else if ( followerId ) {
|
|
396
|
+
if ( followerId === this._._id ) return this._[key];
|
|
397
|
+
|
|
398
|
+
this._followersByConceptName[key] = this._followersByConceptName[key] || [];
|
|
399
|
+
var i = this._followersByConceptName[key].indexOf(followerId);
|
|
400
|
+
if ( i == -1 && !doUnref )
|
|
401
|
+
this._followersByConceptName[key].push(followerId);
|
|
402
|
+
if ( i !== -1 && doUnref )
|
|
403
|
+
this._followersByConceptName[key].splice(i, 1);
|
|
404
|
+
|
|
405
|
+
}
|
|
406
|
+
return this._[key];
|
|
407
|
+
|
|
408
|
+
},
|
|
409
|
+
// -------------------------------------------------- refs & events
|
|
410
|
+
doEval : function ( asserts = "", refMap ) {
|
|
411
|
+
var me = this,
|
|
412
|
+
_fn = compileExpression(asserts, { empty: true });
|
|
413
|
+
try {
|
|
414
|
+
// `refMap` resolves bare identifiers (the old `with(refMap)`).
|
|
415
|
+
return _fn(function ( ref ) { return me.getRef(ref); }, refMap || {});
|
|
416
|
+
} catch ( e ) {
|
|
417
|
+
me._graph._log.error("expression eval failed: %s", asserts, e);
|
|
418
|
+
return undefined;
|
|
419
|
+
}
|
|
420
|
+
},
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* evaluate 'exp' from this cbox, and add 'follow' as watcher if the targeted value is updated
|
|
424
|
+
*
|
|
425
|
+
* @param exp
|
|
426
|
+
* @param follow
|
|
427
|
+
* @param unref bool do unwatch 'follow' instead of watch
|
|
428
|
+
* @returns {*}
|
|
429
|
+
*/
|
|
430
|
+
getRef : function ( exp, follow, unref ) {
|
|
431
|
+
return this._graph.getRef(exp, this, follow, unref);
|
|
432
|
+
},
|
|
433
|
+
/**
|
|
434
|
+
* call 'fn' on any change to 'key'
|
|
435
|
+
* @param key
|
|
436
|
+
* @param fn
|
|
437
|
+
* @param scope
|
|
438
|
+
*/
|
|
439
|
+
follow : function ( key, fn, scope ) {
|
|
440
|
+
this._watcherByConceptName[key] = this._watcherByConceptName[key] || [];
|
|
441
|
+
this._watcherByConceptName[key].push(fn, scope);
|
|
442
|
+
},
|
|
443
|
+
/**
|
|
444
|
+
* Stop calling 'fn' on 'key' change
|
|
445
|
+
* @param key
|
|
446
|
+
* @param fn
|
|
447
|
+
* @param scope
|
|
448
|
+
*/
|
|
449
|
+
unFollow: function ( key, fn, scope ) {
|
|
450
|
+
|
|
451
|
+
this._watcherByConceptName[key] = this._watcherByConceptName[key] || [];
|
|
452
|
+
var i = this._watcherByConceptName[key].indexOf(fn);
|
|
453
|
+
(i != -1) && this._watcherByConceptName[key].splice(i, 2);
|
|
454
|
+
},
|
|
455
|
+
|
|
456
|
+
/**
|
|
457
|
+
* dirty reset/clean of the box
|
|
458
|
+
*/
|
|
459
|
+
reset : function () {
|
|
460
|
+
this._mapOpenConcepts = Object.keys(this._graph._rootConcept._openConcepts || {});
|
|
461
|
+
this._mappedConcepts = {};
|
|
462
|
+
this._followersByConceptName = {};
|
|
463
|
+
this._watcherByConceptName = {};
|
|
464
|
+
},
|
|
465
|
+
/**
|
|
466
|
+
* Do un ref watchers boris watchvosky
|
|
467
|
+
*/
|
|
468
|
+
unRefAll: function () {
|
|
469
|
+
var me = this;
|
|
470
|
+
Object.keys(this._watchers)
|
|
471
|
+
.forEach(( k ) => {
|
|
472
|
+
while ( me._watchers[k].length ) {
|
|
473
|
+
me._graph.getRef(me._watchers[k].shift(), me, me._watchers[k].shift(), true)
|
|
474
|
+
}
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
this._watchers = {};
|
|
478
|
+
Object.keys(this._)
|
|
479
|
+
.forEach(( k ) => {
|
|
480
|
+
var i = me._graph._mapsByConcept[k].indexOf(me._._id);
|
|
481
|
+
(i != -1) && me._graph._mapsByConcept[k].splice(i, 1);
|
|
482
|
+
me._graph._conceptLib[k] && me._graph._conceptLib[k].unRefRequires(me, me._graph);
|
|
483
|
+
});
|
|
484
|
+
},
|
|
485
|
+
|
|
486
|
+
test: function ( query ) {// only for ui !
|
|
487
|
+
|
|
488
|
+
var me = this,
|
|
489
|
+
_q = isFunction(query) ? null : compileExpression(query, { empty: false }),
|
|
490
|
+
fn = isFunction(query) ? query : function ( scope ) { return _q(function ( ref ) { return scope.getRef(ref); }); };
|
|
491
|
+
return fn(this);
|
|
492
|
+
|
|
493
|
+
},
|
|
494
|
+
|
|
495
|
+
destroy: function ( unrefAll ) {
|
|
496
|
+
var me = this;
|
|
497
|
+
this._dead = true;
|
|
498
|
+
this._mapOpenConcepts = this._mappedConcepts = this._followersByConceptName = this._watcherByConceptName = null;
|
|
499
|
+
this._ = {};
|
|
500
|
+
}
|
|
501
|
+
};
|
|
502
|
+
|
|
503
|
+
module.exports = Entity;
|
|
@@ -0,0 +1,57 @@
|
|
|
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
|
+
var Entity = require('./Entity');
|
|
21
|
+
|
|
22
|
+
// A graph vertex: a thin wrapper over an Entity (which carries its facts + concepts)
|
|
23
|
+
// that additionally tracks adjacency — _incoming / _outgoing segment ids, kept in
|
|
24
|
+
// sync by Segment._onTargetChange / _onOriginChange.
|
|
25
|
+
function Node( _, graph, parentMutation ) {
|
|
26
|
+
this.init(_, graph);
|
|
27
|
+
};
|
|
28
|
+
Node.prototype = {
|
|
29
|
+
init : function ( _, graph, parentMutation ) {
|
|
30
|
+
this._outgoing = [];
|
|
31
|
+
this._incoming = [];
|
|
32
|
+
this._id = _._id;
|
|
33
|
+
|
|
34
|
+
this._etty = new Entity(
|
|
35
|
+
{
|
|
36
|
+
_id : _._id,
|
|
37
|
+
Node : true
|
|
38
|
+
},
|
|
39
|
+
graph
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
graph._mapsByConcept["Node"]=graph._mapsByConcept["Node"]||[];
|
|
43
|
+
graph._mapsByConcept["Node"].push(_._id);
|
|
44
|
+
|
|
45
|
+
!__SERVER__ && this._etty.follow("_autokill", this._killMe, this);
|
|
46
|
+
|
|
47
|
+
this._etty.update(_,graph);
|
|
48
|
+
},
|
|
49
|
+
_killMe: function ( n, o ) {
|
|
50
|
+
this._etty.unRefAll();
|
|
51
|
+
},
|
|
52
|
+
specialize : function () {
|
|
53
|
+
return this._etty.specialize();
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
module.exports = Node;
|