@cnstra/core 2.2.1 → 2.3.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/dist/CNSNetwork.d.ts +8 -15
- package/dist/CNSNetwork.d.ts.map +1 -1
- package/dist/CNSStimulation.d.ts +1 -1
- package/dist/CNSStimulation.d.ts.map +1 -1
- package/dist/factory/index.d.ts.map +1 -1
- package/dist/index.cjs +74 -64
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +74 -64
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/CNSTaskQueue.d.ts +0 -12
- package/dist/CNSTaskQueue.d.ts.map +0 -1
package/dist/index.js
CHANGED
|
@@ -23,6 +23,7 @@ var CNSStimulationContextStore = class {
|
|
|
23
23
|
};
|
|
24
24
|
|
|
25
25
|
// src/CNSStimulation.ts
|
|
26
|
+
var TASK_DENDRITE = /* @__PURE__ */ Symbol("cnstra.taskDendrite");
|
|
26
27
|
var CNSDendriteContext = class {
|
|
27
28
|
constructor(stimulation, neuron2, abortSignal, cns) {
|
|
28
29
|
this.stimulation = stimulation;
|
|
@@ -73,7 +74,7 @@ var CNSStimulation = class {
|
|
|
73
74
|
this.instanceNeuronQueue = instanceNeuronQueue;
|
|
74
75
|
this.onResponse = onResponse;
|
|
75
76
|
if (this.options?.maxNeuronHops) {
|
|
76
|
-
this.
|
|
77
|
+
this.neuronVisitMap = /* @__PURE__ */ new Map();
|
|
77
78
|
}
|
|
78
79
|
if (this.autoCleanupContextsEnabled) {
|
|
79
80
|
this.activeSccCounts = /* @__PURE__ */ new Map();
|
|
@@ -338,14 +339,14 @@ var CNSStimulation = class {
|
|
|
338
339
|
}
|
|
339
340
|
createSubscriberQueueItem(subscriber, inputSignal) {
|
|
340
341
|
if (this.options?.maxNeuronHops) {
|
|
341
|
-
if ((this.
|
|
342
|
+
if ((this.neuronVisitMap?.get(subscriber.neuron) ?? 0) >= this.options?.maxNeuronHops) {
|
|
342
343
|
throw new Error(
|
|
343
344
|
`Max neuron hops reached when trying to enqueue subscriber`
|
|
344
345
|
);
|
|
345
346
|
} else {
|
|
346
|
-
this.
|
|
347
|
+
this.neuronVisitMap?.set(
|
|
347
348
|
subscriber.neuron,
|
|
348
|
-
(this.
|
|
349
|
+
(this.neuronVisitMap?.get(subscriber.neuron) ?? 0) + 1
|
|
349
350
|
);
|
|
350
351
|
}
|
|
351
352
|
}
|
|
@@ -354,17 +355,20 @@ var CNSStimulation = class {
|
|
|
354
355
|
dendriteCollateral: subscriber.dendrite.collateral,
|
|
355
356
|
input: inputSignal
|
|
356
357
|
};
|
|
358
|
+
neuronActivationTask[TASK_DENDRITE] = subscriber;
|
|
357
359
|
return neuronActivationTask;
|
|
358
360
|
}
|
|
359
361
|
executeActivationTask(neuronActivationTask) {
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
362
|
+
let subscriber = neuronActivationTask[TASK_DENDRITE];
|
|
363
|
+
if (!subscriber) {
|
|
364
|
+
const subscribers = this.cns.network.getSubscribers(
|
|
365
|
+
neuronActivationTask.dendriteCollateral
|
|
366
|
+
);
|
|
367
|
+
for (let i = 0; i < subscribers.length; i++) {
|
|
368
|
+
if (subscribers[i].neuron === neuronActivationTask.neuron) {
|
|
369
|
+
subscriber = subscribers[i];
|
|
370
|
+
break;
|
|
371
|
+
}
|
|
368
372
|
}
|
|
369
373
|
}
|
|
370
374
|
if (!subscriber) {
|
|
@@ -554,7 +558,7 @@ var CNSStimulation = class {
|
|
|
554
558
|
inputSignal,
|
|
555
559
|
outputSignal,
|
|
556
560
|
this.qSize + this.scheduledCount,
|
|
557
|
-
this.options?.maxNeuronHops && emitter ? this.
|
|
561
|
+
this.options?.maxNeuronHops && emitter ? this.neuronVisitMap?.get(emitter) ?? 0 : void 0,
|
|
558
562
|
error
|
|
559
563
|
)
|
|
560
564
|
);
|
|
@@ -669,8 +673,12 @@ var CNSNetwork = class {
|
|
|
669
673
|
/**
|
|
670
674
|
* Strongly Connected Components of the neuron graph.
|
|
671
675
|
* Each component is a set of neuron objects that can reach each other.
|
|
676
|
+
*
|
|
677
|
+
* Computed lazily on first access: the SCC machinery is only needed for
|
|
678
|
+
* `autoCleanupContexts` (off by default), so most networks never pay for it.
|
|
672
679
|
*/
|
|
673
|
-
this.
|
|
680
|
+
this._stronglyConnectedComponents = [];
|
|
681
|
+
this.sccBuilt = false;
|
|
674
682
|
/**
|
|
675
683
|
* Quick lookup: neuron -> SCC index for fast reachability checks.
|
|
676
684
|
*/
|
|
@@ -688,21 +696,18 @@ var CNSNetwork = class {
|
|
|
688
696
|
* Built once at construction time.
|
|
689
697
|
*/
|
|
690
698
|
this.subIndex = /* @__PURE__ */ new Map();
|
|
691
|
-
this.dendriteIndex = /* @__PURE__ */ new Map();
|
|
692
|
-
this.collateralIndex = /* @__PURE__ */ new Set();
|
|
693
699
|
this.parentNeuronByCollateral = /* @__PURE__ */ new Map();
|
|
694
|
-
this.validateUniqueIdentifiers();
|
|
695
700
|
this.buildIndexes();
|
|
696
|
-
this.buildSCC();
|
|
697
701
|
}
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
702
|
+
get stronglyConnectedComponents() {
|
|
703
|
+
this.ensureSCC();
|
|
704
|
+
return this._stronglyConnectedComponents;
|
|
705
|
+
}
|
|
706
|
+
/** Build the SCC analysis the first time anything needs it. */
|
|
707
|
+
ensureSCC() {
|
|
708
|
+
if (this.sccBuilt) return;
|
|
709
|
+
this.sccBuilt = true;
|
|
710
|
+
this.buildSCC();
|
|
706
711
|
}
|
|
707
712
|
/**
|
|
708
713
|
* Build the neuron graph where neurons are connected if one can stimulate the other.
|
|
@@ -777,7 +782,7 @@ var CNSNetwork = class {
|
|
|
777
782
|
strongConnect(neuron2);
|
|
778
783
|
}
|
|
779
784
|
}
|
|
780
|
-
this.
|
|
785
|
+
this._stronglyConnectedComponents = components;
|
|
781
786
|
for (let i = 0; i < components.length; i++) {
|
|
782
787
|
for (const neuronId of Array.from(components[i])) {
|
|
783
788
|
this.neuronToSCC.set(neuronId, i);
|
|
@@ -791,11 +796,11 @@ var CNSNetwork = class {
|
|
|
791
796
|
*/
|
|
792
797
|
buildSCCDAG(neuronGraph) {
|
|
793
798
|
this.sccDag.clear();
|
|
794
|
-
for (let i = 0; i < this.
|
|
799
|
+
for (let i = 0; i < this._stronglyConnectedComponents.length; i++) {
|
|
795
800
|
this.sccDag.set(i, /* @__PURE__ */ new Set());
|
|
796
801
|
}
|
|
797
|
-
for (let i = 0; i < this.
|
|
798
|
-
const scc = this.
|
|
802
|
+
for (let i = 0; i < this._stronglyConnectedComponents.length; i++) {
|
|
803
|
+
const scc = this._stronglyConnectedComponents[i];
|
|
799
804
|
for (const neuron2 of Array.from(scc)) {
|
|
800
805
|
const neighbors = neuronGraph.get(neuron2) || /* @__PURE__ */ new Set();
|
|
801
806
|
for (const neighbor of Array.from(neighbors)) {
|
|
@@ -813,25 +818,27 @@ var CNSNetwork = class {
|
|
|
813
818
|
*/
|
|
814
819
|
buildSCCAncestors() {
|
|
815
820
|
this.sccAncestors.clear();
|
|
816
|
-
for (let i = 0; i < this.
|
|
821
|
+
for (let i = 0; i < this._stronglyConnectedComponents.length; i++) {
|
|
817
822
|
this.sccAncestors.set(i, /* @__PURE__ */ new Set());
|
|
818
823
|
}
|
|
824
|
+
const n = this._stronglyConnectedComponents.length;
|
|
825
|
+
const forward = Array.from({ length: n }, () => []);
|
|
819
826
|
const inDegree = /* @__PURE__ */ new Map();
|
|
820
827
|
const queue = [];
|
|
821
|
-
for (let
|
|
822
|
-
const
|
|
823
|
-
inDegree.set(
|
|
824
|
-
if (
|
|
825
|
-
|
|
828
|
+
for (let target = 0; target < n; target++) {
|
|
829
|
+
const sources = this.sccDag.get(target) || /* @__PURE__ */ new Set();
|
|
830
|
+
inDegree.set(target, sources.size);
|
|
831
|
+
if (sources.size === 0) queue.push(target);
|
|
832
|
+
for (const source of Array.from(sources)) {
|
|
833
|
+
forward[source].push(target);
|
|
826
834
|
}
|
|
827
835
|
}
|
|
828
836
|
while (queue.length > 0) {
|
|
829
837
|
const current = queue.shift();
|
|
830
|
-
const
|
|
831
|
-
for (const neighbor of
|
|
838
|
+
const currentAncestors = this.sccAncestors.get(current);
|
|
839
|
+
for (const neighbor of forward[current]) {
|
|
832
840
|
const neighborAncestors = this.sccAncestors.get(neighbor);
|
|
833
841
|
neighborAncestors.add(current);
|
|
834
|
-
const currentAncestors = this.sccAncestors.get(current);
|
|
835
842
|
for (const ancestor of Array.from(currentAncestors)) {
|
|
836
843
|
neighborAncestors.add(ancestor);
|
|
837
844
|
}
|
|
@@ -843,32 +850,22 @@ var CNSNetwork = class {
|
|
|
843
850
|
}
|
|
844
851
|
}
|
|
845
852
|
}
|
|
846
|
-
/**
|
|
847
|
-
* Get outgoing edges for a given SCC index
|
|
848
|
-
*/
|
|
849
|
-
getOutgoingEdges(sccIndex) {
|
|
850
|
-
const outgoing = /* @__PURE__ */ new Set();
|
|
851
|
-
for (const [targetScc, incomingEdges] of Array.from(this.sccDag)) {
|
|
852
|
-
if (incomingEdges.has(sccIndex)) {
|
|
853
|
-
outgoing.add(targetScc);
|
|
854
|
-
}
|
|
855
|
-
}
|
|
856
|
-
return outgoing;
|
|
857
|
-
}
|
|
858
853
|
/**
|
|
859
854
|
* Check if a neuron can be reached again (including self-calling).
|
|
860
855
|
* Returns true if the neuron is in a strongly connected component with more than one member,
|
|
861
856
|
* or if it's in a single-member SCC that can reach itself.
|
|
862
857
|
*/
|
|
863
858
|
getSCCSetByNeuron(neuron2) {
|
|
859
|
+
this.ensureSCC();
|
|
864
860
|
const sccIndex = this.neuronToSCC.get(neuron2);
|
|
865
861
|
if (sccIndex === void 0) return;
|
|
866
|
-
return this.
|
|
862
|
+
return this._stronglyConnectedComponents[sccIndex];
|
|
867
863
|
}
|
|
868
864
|
/**
|
|
869
865
|
* Get the SCC index for a given neuron
|
|
870
866
|
*/
|
|
871
867
|
getSccIndexByNeuron(neuron2) {
|
|
868
|
+
this.ensureSCC();
|
|
872
869
|
return this.neuronToSCC.get(neuron2);
|
|
873
870
|
}
|
|
874
871
|
/**
|
|
@@ -876,6 +873,7 @@ var CNSNetwork = class {
|
|
|
876
873
|
* This is the core logic for safe context cleanup.
|
|
877
874
|
*/
|
|
878
875
|
canNeuronBeGuaranteedDone(neuron2, activeSccCounts) {
|
|
876
|
+
this.ensureSCC();
|
|
879
877
|
const sccIndex = this.neuronToSCC.get(neuron2);
|
|
880
878
|
if (sccIndex === void 0) return true;
|
|
881
879
|
if (activeSccCounts.get(sccIndex) && activeSccCounts.get(sccIndex) > 0) {
|
|
@@ -893,8 +891,6 @@ var CNSNetwork = class {
|
|
|
893
891
|
buildIndexes() {
|
|
894
892
|
this.subIndex.clear();
|
|
895
893
|
this.parentNeuronByCollateral.clear();
|
|
896
|
-
this.dendriteIndex.clear();
|
|
897
|
-
this.collateralIndex.clear();
|
|
898
894
|
for (const neuron2 of this.neurons) {
|
|
899
895
|
for (const dendrite of neuron2.dendrites) {
|
|
900
896
|
const key = dendrite.collateral;
|
|
@@ -904,25 +900,40 @@ var CNSNetwork = class {
|
|
|
904
900
|
key,
|
|
905
901
|
arr
|
|
906
902
|
);
|
|
907
|
-
this.dendriteIndex.set(key, dendrite);
|
|
908
903
|
}
|
|
909
904
|
Object.values(neuron2.axon).forEach((collateral2) => {
|
|
910
905
|
this.parentNeuronByCollateral.set(
|
|
911
906
|
collateral2,
|
|
912
907
|
neuron2
|
|
913
908
|
);
|
|
914
|
-
this.collateralIndex.add(collateral2);
|
|
915
909
|
});
|
|
916
910
|
}
|
|
917
911
|
}
|
|
918
912
|
getParentNeuronByCollateral(collateral2) {
|
|
919
913
|
return this.parentNeuronByCollateral.get(collateral2);
|
|
920
914
|
}
|
|
915
|
+
// Computed on demand (rarely used): one dendrite per collateral, last wins —
|
|
916
|
+
// preserving the previous index semantics without building it every construct.
|
|
921
917
|
getDendrites() {
|
|
922
|
-
|
|
918
|
+
const byCollateral = /* @__PURE__ */ new Map();
|
|
919
|
+
for (const neuron2 of this.neurons) {
|
|
920
|
+
for (const dendrite of neuron2.dendrites) {
|
|
921
|
+
byCollateral.set(
|
|
922
|
+
dendrite.collateral,
|
|
923
|
+
dendrite
|
|
924
|
+
);
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
return Array.from(byCollateral.values());
|
|
923
928
|
}
|
|
924
929
|
getCollaterals() {
|
|
925
|
-
|
|
930
|
+
const collaterals = /* @__PURE__ */ new Set();
|
|
931
|
+
for (const neuron2 of this.neurons) {
|
|
932
|
+
for (const collateral2 of Object.values(neuron2.axon)) {
|
|
933
|
+
collaterals.add(collateral2);
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
return Array.from(collaterals);
|
|
926
937
|
}
|
|
927
938
|
getSubscribers(collateral2) {
|
|
928
939
|
return this.subIndex.get(collateral2) ?? [];
|
|
@@ -1196,27 +1207,26 @@ var neuron = (axon) => {
|
|
|
1196
1207
|
modality: singleModality,
|
|
1197
1208
|
modalities: multipleModalities,
|
|
1198
1209
|
afferentPaths: singleAfferentPaths,
|
|
1199
|
-
default:
|
|
1200
|
-
default: globalDefaultHandler,
|
|
1210
|
+
default: defaultHandler,
|
|
1201
1211
|
output
|
|
1202
1212
|
} = s;
|
|
1203
1213
|
const modalityConfigs = multipleModalities ? multipleModalities : singleModality ? [
|
|
1204
1214
|
{
|
|
1205
1215
|
modality: singleModality,
|
|
1206
1216
|
afferentPaths: singleAfferentPaths,
|
|
1207
|
-
default:
|
|
1217
|
+
default: defaultHandler
|
|
1208
1218
|
}
|
|
1209
1219
|
] : [];
|
|
1210
1220
|
const response = (payload, axon2, ctx) => {
|
|
1211
1221
|
const stimulation = ctx.stimulation;
|
|
1212
1222
|
const stimOptions = stimulation?.options;
|
|
1213
1223
|
const runGlobalDefault = () => {
|
|
1214
|
-
if (!
|
|
1224
|
+
if (!defaultHandler) {
|
|
1215
1225
|
throw new Error(
|
|
1216
1226
|
`modalityDendrite: No handler found for modality and no default handler provided`
|
|
1217
1227
|
);
|
|
1218
1228
|
}
|
|
1219
|
-
const res =
|
|
1229
|
+
const res = defaultHandler(payload, axon2, ctx);
|
|
1220
1230
|
if (res && typeof res.then === "function") {
|
|
1221
1231
|
return res.then(
|
|
1222
1232
|
(result) => output(result, axon2, ctx)
|
|
@@ -1235,7 +1245,7 @@ var neuron = (axon) => {
|
|
|
1235
1245
|
}
|
|
1236
1246
|
const stimAfferentPath = stimOptions.afferentPath;
|
|
1237
1247
|
const handler = stimAfferentPath && matchingConfig.afferentPaths ? matchingConfig.afferentPaths.get(stimAfferentPath) : void 0;
|
|
1238
|
-
const effectiveHandler = handler ?? matchingConfig.default ??
|
|
1248
|
+
const effectiveHandler = handler ?? matchingConfig.default ?? defaultHandler;
|
|
1239
1249
|
if (!effectiveHandler) {
|
|
1240
1250
|
throw new Error(
|
|
1241
1251
|
`modalityDendrite: No handler found for afferent path in modality and no default handler provided`
|