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.
Files changed (213) hide show
  1. package/CONTRIBUTING.md +22 -0
  2. package/LICENSE +661 -0
  3. package/README.md +245 -0
  4. package/bin/sg +20 -0
  5. package/concepts/README.md +23 -0
  6. package/concepts/_substrate/Claim/Confidence/Trusted.json +6 -0
  7. package/concepts/_substrate/Claim/Confidence.json +10 -0
  8. package/concepts/_substrate/Claim/Freshness/Stale.json +7 -0
  9. package/concepts/_substrate/Claim/Freshness.json +5 -0
  10. package/concepts/_substrate/Claim/Verification/Refuted.json +6 -0
  11. package/concepts/_substrate/Claim/Verification.json +6 -0
  12. package/concepts/_substrate/Claim.json +4 -0
  13. package/concepts/_substrate/Frontier/InBeam.json +6 -0
  14. package/concepts/_substrate/Frontier/Stuck.json +6 -0
  15. package/concepts/_substrate/Frontier.json +5 -0
  16. package/concepts/_substrate/Intake/ToTask.json +22 -0
  17. package/concepts/_substrate/Intake/Typed.json +10 -0
  18. package/concepts/_substrate/Intake.json +27 -0
  19. package/concepts/_substrate/Task/Answer.json +12 -0
  20. package/concepts/_substrate/Task/Complexity/Atomic.json +5 -0
  21. package/concepts/_substrate/Task/Complexity/Compound/Expansion.json +7 -0
  22. package/concepts/_substrate/Task/Complexity/Compound.json +6 -0
  23. package/concepts/_substrate/Task/Complexity.json +6 -0
  24. package/concepts/_substrate/Task/EvalComplexity.json +13 -0
  25. package/concepts/_substrate/Task/ReportUp.json +6 -0
  26. package/concepts/_substrate/Task/Rollup.json +10 -0
  27. package/concepts/_substrate/Task.json +5 -0
  28. package/concepts/clinical/Diagnosis/Medication.json +6 -0
  29. package/concepts/clinical/Diagnosis.json +15 -0
  30. package/concepts/clinical/Observation/LabValue/OutOfRange.json +6 -0
  31. package/concepts/clinical/Observation/LabValue.json +5 -0
  32. package/concepts/clinical/Observation.json +6 -0
  33. package/concepts/common/Document/pathBasket.json +6 -0
  34. package/concepts/common/Document.json +3 -0
  35. package/concepts/common/Edge/Distance.json +13 -0
  36. package/concepts/common/Edge/Stay/LongStay.json +8 -0
  37. package/concepts/common/Edge/Stay.json +8 -0
  38. package/concepts/common/Edge/Travel/LongTravel.json +16 -0
  39. package/concepts/common/Edge/Travel/ShortTravel.json +11 -0
  40. package/concepts/common/Edge/Travel/targetNode.json +7 -0
  41. package/concepts/common/Edge/Travel.json +7 -0
  42. package/concepts/common/Edge.json +3 -0
  43. package/concepts/common/Vertice.json +3 -0
  44. package/concepts/supply/Fulfillment.json +10 -0
  45. package/concepts/supply/Inventory/Reorder.json +7 -0
  46. package/concepts/supply/Inventory.json +5 -0
  47. package/concepts/supply/Procurement/SupplierConfirm.json +6 -0
  48. package/concepts/supply/Procurement.json +8 -0
  49. package/concepts/supply/Transport.json +5 -0
  50. package/doc/API.md +556 -0
  51. package/doc/CAPABILITIES.md +373 -0
  52. package/doc/MODELISATION.md +511 -0
  53. package/doc/architecture.md +267 -0
  54. package/doc/concept-as-graph.md +379 -0
  55. package/doc/concept-learning.md +168 -0
  56. package/doc/creative-loop-map.md +202 -0
  57. package/doc/original-2016-doc.md +148 -0
  58. package/doc/usage.md +501 -0
  59. package/lib/authoring/abstract.js +401 -0
  60. package/lib/authoring/abstraction.js +190 -0
  61. package/lib/authoring/adapt.js +393 -0
  62. package/lib/authoring/ancestry.js +216 -0
  63. package/lib/authoring/author.js +147 -0
  64. package/lib/authoring/bounded-merge.js +57 -0
  65. package/lib/authoring/canon.js +158 -0
  66. package/lib/authoring/clock.js +123 -0
  67. package/lib/authoring/combinator.js +92 -0
  68. package/lib/authoring/compete.js +76 -0
  69. package/lib/authoring/compose-hotspot.js +314 -0
  70. package/lib/authoring/compress.js +279 -0
  71. package/lib/authoring/concept-net.js +254 -0
  72. package/lib/authoring/concepts.js +66 -0
  73. package/lib/authoring/context-project.js +277 -0
  74. package/lib/authoring/contract.js +476 -0
  75. package/lib/authoring/corpus-pack.js +125 -0
  76. package/lib/authoring/cost-probe.js +86 -0
  77. package/lib/authoring/crystallize.js +555 -0
  78. package/lib/authoring/dag-decompose.js +256 -0
  79. package/lib/authoring/dataset-adapter.js +253 -0
  80. package/lib/authoring/debug-provider.js +92 -0
  81. package/lib/authoring/decompose.js +343 -0
  82. package/lib/authoring/emittability.js +236 -0
  83. package/lib/authoring/equilibrium.js +160 -0
  84. package/lib/authoring/extract.js +153 -0
  85. package/lib/authoring/forest.js +55 -0
  86. package/lib/authoring/forge-fallback.js +116 -0
  87. package/lib/authoring/givens.js +121 -0
  88. package/lib/authoring/glossary.js +177 -0
  89. package/lib/authoring/grammar-graph.js +191 -0
  90. package/lib/authoring/granularity.js +80 -0
  91. package/lib/authoring/graph-net.js +108 -0
  92. package/lib/authoring/ground.js +148 -0
  93. package/lib/authoring/higher-order.js +85 -0
  94. package/lib/authoring/hotspot.js +132 -0
  95. package/lib/authoring/hysteresis.js +85 -0
  96. package/lib/authoring/index.js +106 -0
  97. package/lib/authoring/lattice-morphism.js +61 -0
  98. package/lib/authoring/lattice-pack.js +148 -0
  99. package/lib/authoring/leaf-io.js +77 -0
  100. package/lib/authoring/library.js +156 -0
  101. package/lib/authoring/lifecycle.js +92 -0
  102. package/lib/authoring/loop.js +193 -0
  103. package/lib/authoring/master-loop.js +132 -0
  104. package/lib/authoring/mdl.js +209 -0
  105. package/lib/authoring/memo-stability.js +122 -0
  106. package/lib/authoring/method-explorer.js +138 -0
  107. package/lib/authoring/method-pack.js +181 -0
  108. package/lib/authoring/method.js +202 -0
  109. package/lib/authoring/mine.js +510 -0
  110. package/lib/authoring/mount.js +119 -0
  111. package/lib/authoring/negotiate.js +100 -0
  112. package/lib/authoring/parametric.js +139 -0
  113. package/lib/authoring/reaggregate.js +75 -0
  114. package/lib/authoring/rebalance.js +157 -0
  115. package/lib/authoring/recall.js +135 -0
  116. package/lib/authoring/registry.js +350 -0
  117. package/lib/authoring/relearn.js +147 -0
  118. package/lib/authoring/retention.js +89 -0
  119. package/lib/authoring/segment-proxy.js +114 -0
  120. package/lib/authoring/serve-leaf.js +64 -0
  121. package/lib/authoring/slot-aware-serve.js +77 -0
  122. package/lib/authoring/sound-invoke.js +59 -0
  123. package/lib/authoring/split-serve.js +74 -0
  124. package/lib/authoring/ste.js +239 -0
  125. package/lib/authoring/stock.js +101 -0
  126. package/lib/authoring/store.js +94 -0
  127. package/lib/authoring/supervise.js +138 -0
  128. package/lib/authoring/support.js +131 -0
  129. package/lib/authoring/task-mirror.js +51 -0
  130. package/lib/authoring/typed-loop.js +160 -0
  131. package/lib/authoring/validate.js +569 -0
  132. package/lib/authoring/widen.js +88 -0
  133. package/lib/combos/appliance.js +189 -0
  134. package/lib/combos/critique.js +377 -0
  135. package/lib/combos/defaults.js +86 -0
  136. package/lib/combos/durable-runner.js +115 -0
  137. package/lib/combos/forge.js +209 -0
  138. package/lib/combos/index.js +86 -0
  139. package/lib/combos/learning-library.js +253 -0
  140. package/lib/combos/mixture-serve.js +193 -0
  141. package/lib/combos/plan-loop.js +192 -0
  142. package/lib/combos/proxy-cache.js +272 -0
  143. package/lib/combos/self-mod.js +94 -0
  144. package/lib/durable/audit.js +102 -0
  145. package/lib/durable/checkpoint-store.js +550 -0
  146. package/lib/durable/fold.js +62 -0
  147. package/lib/durable/interpreter.js +242 -0
  148. package/lib/durable/xlate.js +185 -0
  149. package/lib/graph/Graph.js +2486 -0
  150. package/lib/graph/expr.js +307 -0
  151. package/lib/graph/index.js +26 -0
  152. package/lib/graph/log.js +134 -0
  153. package/lib/graph/objects/Concept.js +382 -0
  154. package/lib/graph/objects/Entity.js +503 -0
  155. package/lib/graph/objects/Node.js +57 -0
  156. package/lib/graph/objects/PathMap.js +557 -0
  157. package/lib/graph/objects/Segment.js +100 -0
  158. package/lib/graph/tasks/stabilize.js +59 -0
  159. package/lib/graph/tasks/taskflow.js +247 -0
  160. package/lib/index.js +122 -0
  161. package/lib/load.js +132 -0
  162. package/lib/providers/backends.js +117 -0
  163. package/lib/providers/borderline.js +129 -0
  164. package/lib/providers/cache.js +226 -0
  165. package/lib/providers/canonicalize.js +198 -0
  166. package/lib/providers/constat.js +107 -0
  167. package/lib/providers/geo.js +53 -0
  168. package/lib/providers/index.js +116 -0
  169. package/lib/providers/intake.js +263 -0
  170. package/lib/providers/llm-local.js +88 -0
  171. package/lib/providers/llm.js +236 -0
  172. package/lib/providers/local-host.js +197 -0
  173. package/lib/providers/merge-consistency.js +162 -0
  174. package/lib/providers/nogood.js +149 -0
  175. package/lib/providers/reason-loop.js +223 -0
  176. package/lib/providers/semiring.js +285 -0
  177. package/lib/providers/solver-fork.js +163 -0
  178. package/lib/providers/stats.js +228 -0
  179. package/lib/providers/verify.js +120 -0
  180. package/lib/runtime/index.js +135 -0
  181. package/lib/runtime/invoke-pool.js +92 -0
  182. package/lib/runtime/protocol.js +210 -0
  183. package/lib/runtime/transport-socket.js +77 -0
  184. package/lib/runtime/worker-entry.js +31 -0
  185. package/lib/sg/cli.js +601 -0
  186. package/lib/sg/log-sinks.js +214 -0
  187. package/lib/sg/mcp.js +395 -0
  188. package/lib/sg/proxy-run.js +66 -0
  189. package/lib/sg/serve-demo.js +185 -0
  190. package/lib/sg/serve.js +205 -0
  191. package/lib/sg/trace.js +118 -0
  192. package/lib/studio/protocol.js +45 -0
  193. package/lib/studio/public/app.js +310 -0
  194. package/lib/studio/public/components/ConceptEditor.js +43 -0
  195. package/lib/studio/public/components/ConceptTree.js +43 -0
  196. package/lib/studio/public/components/CorpusPanel.js +68 -0
  197. package/lib/studio/public/components/ForkTree.js +36 -0
  198. package/lib/studio/public/components/GrammarGraph.js +99 -0
  199. package/lib/studio/public/components/GraphCanvas.js +156 -0
  200. package/lib/studio/public/components/Inspector.js +48 -0
  201. package/lib/studio/public/components/LearningPanel.js +85 -0
  202. package/lib/studio/public/components/PromptConsole.js +46 -0
  203. package/lib/studio/public/components/ProviderTrace.js +39 -0
  204. package/lib/studio/public/components/SessionSplit.js +47 -0
  205. package/lib/studio/public/components/TilingOverlay.js +40 -0
  206. package/lib/studio/public/components/Timeline.js +57 -0
  207. package/lib/studio/public/index.html +26 -0
  208. package/lib/studio/public/styles.css +185 -0
  209. package/lib/studio/public/ws.js +58 -0
  210. package/lib/studio/server.js +130 -0
  211. package/lib/studio/session.js +397 -0
  212. package/lib/studio/studio.js +149 -0
  213. 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;