@cnstra/core 2.2.0 → 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/CNS.d.ts.map +1 -1
- package/dist/CNSNetwork.d.ts +8 -15
- package/dist/CNSNetwork.d.ts.map +1 -1
- package/dist/CNSStimulation.d.ts +41 -11
- package/dist/CNSStimulation.d.ts.map +1 -1
- package/dist/CNSStimulationContextStore.d.ts +1 -1
- package/dist/CNSStimulationContextStore.d.ts.map +1 -1
- package/dist/factory/index.d.ts.map +1 -1
- package/dist/index.cjs +380 -295
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +380 -295
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/CNSNeuronActivationPump.d.ts +0 -27
- package/dist/CNSNeuronActivationPump.d.ts.map +0 -1
- package/dist/CNSTaskQueue.d.ts +0 -12
- package/dist/CNSTaskQueue.d.ts.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,79 +1,154 @@
|
|
|
1
1
|
// src/CNSStimulationContextStore.ts
|
|
2
2
|
var CNSStimulationContextStore = class {
|
|
3
|
-
constructor(ctx
|
|
3
|
+
constructor(ctx) {
|
|
4
4
|
this.ctx = ctx;
|
|
5
5
|
}
|
|
6
6
|
get(key) {
|
|
7
|
-
return this.ctx
|
|
7
|
+
return this.ctx?.get(key);
|
|
8
8
|
}
|
|
9
9
|
set(key, value) {
|
|
10
|
-
this.ctx.set(key, value);
|
|
10
|
+
(this.ctx ?? (this.ctx = /* @__PURE__ */ new Map())).set(key, value);
|
|
11
11
|
}
|
|
12
12
|
getAll() {
|
|
13
|
-
return new Map(this.ctx);
|
|
13
|
+
return this.ctx ? new Map(this.ctx) : /* @__PURE__ */ new Map();
|
|
14
14
|
}
|
|
15
15
|
setAll(values) {
|
|
16
|
-
this.ctx.
|
|
17
|
-
|
|
16
|
+
const ctx = this.ctx ?? (this.ctx = /* @__PURE__ */ new Map());
|
|
17
|
+
ctx.clear();
|
|
18
|
+
for (const [key, value] of values) ctx.set(key, value);
|
|
18
19
|
}
|
|
19
20
|
delete(key) {
|
|
20
|
-
this.ctx
|
|
21
|
+
this.ctx?.delete(key);
|
|
21
22
|
}
|
|
22
23
|
};
|
|
23
24
|
|
|
24
|
-
// src/
|
|
25
|
-
var
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
this.
|
|
25
|
+
// src/CNSStimulation.ts
|
|
26
|
+
var TASK_DENDRITE = /* @__PURE__ */ Symbol("cnstra.taskDendrite");
|
|
27
|
+
var CNSDendriteContext = class {
|
|
28
|
+
constructor(stimulation, neuron2, abortSignal, cns) {
|
|
29
|
+
this.stimulation = stimulation;
|
|
30
|
+
this.neuron = neuron2;
|
|
29
31
|
this.abortSignal = abortSignal;
|
|
30
|
-
this.
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
this.
|
|
34
|
-
|
|
32
|
+
this.cns = cns;
|
|
33
|
+
}
|
|
34
|
+
get() {
|
|
35
|
+
return this.stimulation.getContext().get(this.neuron);
|
|
36
|
+
}
|
|
37
|
+
set(value) {
|
|
38
|
+
this.stimulation.getContext().set(this.neuron, value);
|
|
39
|
+
}
|
|
40
|
+
delete() {
|
|
41
|
+
this.stimulation.getContext().delete(this.neuron);
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
var CNSStimulationResponseImpl = class {
|
|
45
|
+
constructor(stimulation, store, inputSignal, outputSignal, queueLength, hops, error) {
|
|
46
|
+
this.stimulation = stimulation;
|
|
47
|
+
this.store = store;
|
|
48
|
+
this.inputSignal = inputSignal;
|
|
49
|
+
this.outputSignal = outputSignal;
|
|
50
|
+
this.queueLength = queueLength;
|
|
51
|
+
this.hops = hops;
|
|
52
|
+
this.error = error;
|
|
53
|
+
}
|
|
54
|
+
get contextValue() {
|
|
55
|
+
return this.store ? this.store.getAll() : /* @__PURE__ */ new Map();
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
var CNSStimulation = class {
|
|
59
|
+
constructor(cns, instanceNeuronQueue, options, onResponse) {
|
|
60
|
+
this.cns = cns;
|
|
61
|
+
this.options = options;
|
|
62
|
+
this.qHead = 0;
|
|
63
|
+
this.qTail = 0;
|
|
64
|
+
this.qSize = 0;
|
|
65
|
+
this.qCapacity = 4;
|
|
35
66
|
this.activeOperations = 0;
|
|
36
|
-
|
|
37
|
-
// NEW: prevent re-entrant pump; defer extra runs
|
|
67
|
+
// Re-entrancy guard for the pump loop.
|
|
38
68
|
this.pumping = false;
|
|
39
69
|
this.needsPump = false;
|
|
40
|
-
|
|
41
|
-
|
|
70
|
+
this.scheduledCount = 0;
|
|
71
|
+
this.isCompleted = false;
|
|
72
|
+
this.completionRejected = false;
|
|
73
|
+
this.ctxStore = options?.ctx;
|
|
74
|
+
this.instanceNeuronQueue = instanceNeuronQueue;
|
|
75
|
+
this.onResponse = onResponse;
|
|
76
|
+
if (this.options?.maxNeuronHops) {
|
|
77
|
+
this.neuronVisitMap = /* @__PURE__ */ new Map();
|
|
78
|
+
}
|
|
79
|
+
if (this.autoCleanupContextsEnabled) {
|
|
80
|
+
this.activeSccCounts = /* @__PURE__ */ new Map();
|
|
42
81
|
}
|
|
43
|
-
this.items = new Array(this.capacity);
|
|
44
82
|
}
|
|
83
|
+
pushFailedTask(failure) {
|
|
84
|
+
(this.failedTasks ?? (this.failedTasks = [])).push(failure);
|
|
85
|
+
}
|
|
86
|
+
finalizeResolve() {
|
|
87
|
+
this.completionRejected = false;
|
|
88
|
+
this.resolveCompleted?.();
|
|
89
|
+
}
|
|
90
|
+
finalizeReject(error) {
|
|
91
|
+
this.completionRejected = true;
|
|
92
|
+
this.completionError = error;
|
|
93
|
+
this.rejectCompleted?.(error);
|
|
94
|
+
}
|
|
95
|
+
/** Lazily-created neuron context store (see {@link ctxStore}). */
|
|
96
|
+
get ctx() {
|
|
97
|
+
return this.ctxStore ?? (this.ctxStore = new CNSStimulationContextStore());
|
|
98
|
+
}
|
|
99
|
+
// ----- Inlined activation queue / pump -----
|
|
45
100
|
get canStartOperation() {
|
|
46
|
-
const limit = this.concurrency ?? Infinity;
|
|
47
|
-
return this.activeOperations < limit && !this.abortSignal?.aborted;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const oldCapacity = this.
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
101
|
+
const limit = this.options?.concurrency ?? Infinity;
|
|
102
|
+
return this.activeOperations < limit && !this.options?.abortSignal?.aborted;
|
|
103
|
+
}
|
|
104
|
+
qResize() {
|
|
105
|
+
const oldCapacity = this.qCapacity;
|
|
106
|
+
const oldItems = this.qItems;
|
|
107
|
+
this.qCapacity = oldCapacity * 2;
|
|
108
|
+
const newItems = new Array(this.qCapacity);
|
|
109
|
+
let oldIndex = this.qHead;
|
|
110
|
+
for (let i = 0; i < this.qSize; i++) {
|
|
111
|
+
newItems[i] = oldItems[oldIndex];
|
|
56
112
|
oldIndex = (oldIndex + 1) % oldCapacity;
|
|
57
113
|
}
|
|
58
|
-
this.
|
|
59
|
-
this.
|
|
60
|
-
this.
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
if (this.
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
this.
|
|
67
|
-
this.
|
|
68
|
-
|
|
114
|
+
this.qItems = newItems;
|
|
115
|
+
this.qHead = 0;
|
|
116
|
+
this.qTail = this.qSize;
|
|
117
|
+
}
|
|
118
|
+
qDequeue() {
|
|
119
|
+
if (this.qSize === 0) return void 0;
|
|
120
|
+
const items = this.qItems;
|
|
121
|
+
const task = items[this.qHead];
|
|
122
|
+
items[this.qHead] = void 0;
|
|
123
|
+
this.qHead = (this.qHead + 1) % this.qCapacity;
|
|
124
|
+
this.qSize--;
|
|
125
|
+
return task;
|
|
126
|
+
}
|
|
127
|
+
qEnqueueItem(task) {
|
|
128
|
+
if (!this.qItems) {
|
|
129
|
+
this.qItems = new Array(this.qCapacity);
|
|
130
|
+
} else if (this.qSize === this.qCapacity) {
|
|
131
|
+
this.qResize();
|
|
132
|
+
}
|
|
133
|
+
this.qItems[this.qTail] = task;
|
|
134
|
+
this.qTail = (this.qTail + 1) % this.qCapacity;
|
|
135
|
+
this.qSize++;
|
|
136
|
+
}
|
|
137
|
+
/** Enqueue a task and drive the pump unless one is already running. */
|
|
138
|
+
qEnqueue(task) {
|
|
139
|
+
this.qEnqueueItem(task);
|
|
140
|
+
if (!this.pumping) this.pump();
|
|
141
|
+
else this.needsPump = true;
|
|
69
142
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
143
|
+
qGetQueuedTasks() {
|
|
144
|
+
const result = [];
|
|
145
|
+
if (!this.qItems) return result;
|
|
146
|
+
let index = this.qHead;
|
|
147
|
+
for (let i = 0; i < this.qSize; i++) {
|
|
148
|
+
result.push(this.qItems[index]);
|
|
149
|
+
index = (index + 1) % this.qCapacity;
|
|
73
150
|
}
|
|
74
|
-
|
|
75
|
-
this.tail = (this.tail + 1) % this.capacity;
|
|
76
|
-
this.size++;
|
|
151
|
+
return result;
|
|
77
152
|
}
|
|
78
153
|
pump() {
|
|
79
154
|
if (this.pumping) {
|
|
@@ -81,26 +156,26 @@ var CNSNeuronActivationPump = class {
|
|
|
81
156
|
return;
|
|
82
157
|
}
|
|
83
158
|
this.pumping = true;
|
|
84
|
-
while (this.canStartOperation && this.
|
|
85
|
-
const
|
|
159
|
+
while (this.canStartOperation && this.qSize > 0) {
|
|
160
|
+
const task = this.qDequeue();
|
|
86
161
|
this.activeOperations++;
|
|
87
|
-
this.
|
|
88
|
-
const ret = this.processor(neuronActivationTask);
|
|
162
|
+
const ret = this.executeActivationTask(task);
|
|
89
163
|
if (ret && typeof ret.then === "function") {
|
|
164
|
+
(this.activeTasks ?? (this.activeTasks = /* @__PURE__ */ new Set())).add(task);
|
|
90
165
|
ret.then(
|
|
91
166
|
(cb) => {
|
|
92
167
|
this.activeOperations--;
|
|
93
|
-
this.activeTasks.delete(
|
|
168
|
+
this.activeTasks.delete(task);
|
|
94
169
|
if (typeof cb === "function") cb();
|
|
95
|
-
if (this.
|
|
170
|
+
if (this.qSize > 0 && this.canStartOperation) {
|
|
96
171
|
if (this.pumping) this.needsPump = true;
|
|
97
172
|
else this.pump();
|
|
98
173
|
}
|
|
99
174
|
},
|
|
100
175
|
(err) => {
|
|
101
176
|
this.activeOperations--;
|
|
102
|
-
this.activeTasks.delete(
|
|
103
|
-
if (this.
|
|
177
|
+
this.activeTasks.delete(task);
|
|
178
|
+
if (this.qSize > 0 && this.canStartOperation) {
|
|
104
179
|
if (this.pumping) this.needsPump = true;
|
|
105
180
|
else this.pump();
|
|
106
181
|
}
|
|
@@ -110,7 +185,6 @@ var CNSNeuronActivationPump = class {
|
|
|
110
185
|
continue;
|
|
111
186
|
}
|
|
112
187
|
this.activeOperations--;
|
|
113
|
-
this.activeTasks.delete(neuronActivationTask);
|
|
114
188
|
if (typeof ret === "function") ret();
|
|
115
189
|
}
|
|
116
190
|
this.pumping = false;
|
|
@@ -119,73 +193,20 @@ var CNSNeuronActivationPump = class {
|
|
|
119
193
|
this.pump();
|
|
120
194
|
}
|
|
121
195
|
}
|
|
122
|
-
enqueue(neuronActivationTask) {
|
|
123
|
-
this.enqueueItem(neuronActivationTask);
|
|
124
|
-
if (!this.pumping) this.pump();
|
|
125
|
-
else this.needsPump = true;
|
|
126
|
-
}
|
|
127
|
-
get length() {
|
|
128
|
-
return this.size;
|
|
129
|
-
}
|
|
130
|
-
getActiveOperationsCount() {
|
|
131
|
-
return this.activeOperations;
|
|
132
|
-
}
|
|
133
|
-
getQueuedTasks() {
|
|
134
|
-
const result = [];
|
|
135
|
-
let index = this.head;
|
|
136
|
-
for (let i = 0; i < this.size; i++) {
|
|
137
|
-
result.push(this.items[index]);
|
|
138
|
-
index = (index + 1) % this.capacity;
|
|
139
|
-
}
|
|
140
|
-
return result;
|
|
141
|
-
}
|
|
142
|
-
getActiveTasks() {
|
|
143
|
-
return Array.from(this.activeTasks);
|
|
144
|
-
}
|
|
145
|
-
};
|
|
146
|
-
|
|
147
|
-
// src/CNSStimulation.ts
|
|
148
|
-
var CNSStimulation = class {
|
|
149
|
-
constructor(cns, instanceNeuronQueue, options) {
|
|
150
|
-
this.cns = cns;
|
|
151
|
-
this.options = options;
|
|
152
|
-
this.scheduledCount = 0;
|
|
153
|
-
this.pendingTasks = /* @__PURE__ */ new Set();
|
|
154
|
-
this.failedTasks = [];
|
|
155
|
-
this.completed = new Promise((resolve, reject) => {
|
|
156
|
-
this.resolveCompleted = resolve;
|
|
157
|
-
this.rejectCompleted = reject;
|
|
158
|
-
});
|
|
159
|
-
this.isCompleted = false;
|
|
160
|
-
/**
|
|
161
|
-
* Track active SCCs: SCC index -> count of active neurons in that SCC
|
|
162
|
-
*/
|
|
163
|
-
this.activeSccCounts = /* @__PURE__ */ new Map();
|
|
164
|
-
this.ctx = options?.ctx ?? new CNSStimulationContextStore();
|
|
165
|
-
this.queue = new CNSNeuronActivationPump(
|
|
166
|
-
(neuronActivationTask) => this.executeActivationTask(neuronActivationTask),
|
|
167
|
-
options?.concurrency,
|
|
168
|
-
options?.abortSignal
|
|
169
|
-
);
|
|
170
|
-
this.instanceNeuronQueue = instanceNeuronQueue;
|
|
171
|
-
if (this.options?.maxNeuronHops) {
|
|
172
|
-
this.nueronVisitMap = /* @__PURE__ */ new Map();
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
196
|
tryResolveCompleted() {
|
|
176
197
|
if (this.isCompleted) return;
|
|
177
|
-
const noActive = this.
|
|
178
|
-
const noPending = this.
|
|
198
|
+
const noActive = this.activeOperations === 0;
|
|
199
|
+
const noPending = this.qSize + this.scheduledCount === 0;
|
|
179
200
|
const aborted = !!this.options?.abortSignal?.aborted;
|
|
180
201
|
if (noPending && noActive || aborted && noActive && !noPending) {
|
|
181
202
|
if (aborted) {
|
|
182
|
-
const queuedTasks = this.
|
|
203
|
+
const queuedTasks = this.qGetQueuedTasks();
|
|
183
204
|
for (const task of [...queuedTasks]) {
|
|
184
|
-
const alreadyTracked = this.failedTasks
|
|
205
|
+
const alreadyTracked = this.failedTasks?.some(
|
|
185
206
|
(ft) => ft.task === task
|
|
186
207
|
);
|
|
187
208
|
if (!alreadyTracked) {
|
|
188
|
-
this.
|
|
209
|
+
this.pushFailedTask({
|
|
189
210
|
task,
|
|
190
211
|
error: new Error(
|
|
191
212
|
"Task aborted - not started due to abort signal"
|
|
@@ -195,37 +216,47 @@ var CNSStimulation = class {
|
|
|
195
216
|
}
|
|
196
217
|
}
|
|
197
218
|
this.isCompleted = true;
|
|
198
|
-
this.
|
|
219
|
+
this.finalizeReject(new Error("Stimulation aborted"));
|
|
199
220
|
return;
|
|
200
221
|
}
|
|
201
222
|
this.isCompleted = true;
|
|
202
|
-
|
|
223
|
+
const failedCount = this.failedTasks?.length ?? 0;
|
|
224
|
+
if (failedCount > 0 || this.onResponseError) {
|
|
203
225
|
const error = this.onResponseError || new Error(
|
|
204
|
-
`Stimulation completed with ${
|
|
226
|
+
`Stimulation completed with ${failedCount} failed task(s)`
|
|
205
227
|
);
|
|
206
|
-
this.
|
|
228
|
+
this.finalizeReject(error);
|
|
207
229
|
} else {
|
|
208
|
-
this.
|
|
230
|
+
this.finalizeResolve();
|
|
209
231
|
}
|
|
210
232
|
}
|
|
211
233
|
}
|
|
212
234
|
waitUntilComplete() {
|
|
235
|
+
if (this.completed) return this.completed;
|
|
236
|
+
if (this.isCompleted) {
|
|
237
|
+
this.completed = this.completionRejected ? Promise.reject(this.completionError) : Promise.resolve();
|
|
238
|
+
return this.completed;
|
|
239
|
+
}
|
|
240
|
+
this.completed = new Promise((resolve, reject) => {
|
|
241
|
+
this.resolveCompleted = resolve;
|
|
242
|
+
this.rejectCompleted = reject;
|
|
243
|
+
});
|
|
213
244
|
return this.completed;
|
|
214
245
|
}
|
|
215
246
|
/**
|
|
216
247
|
* Returns all current activation tasks: queued, active, and pending (scheduled but not yet enqueued)
|
|
217
248
|
*/
|
|
218
249
|
getAllActivationTasks() {
|
|
219
|
-
const queuedTasks = this.
|
|
220
|
-
const activeTasks = this.
|
|
221
|
-
const pendingTasks = Array.from(this.pendingTasks);
|
|
250
|
+
const queuedTasks = this.qGetQueuedTasks();
|
|
251
|
+
const activeTasks = this.activeTasks ? Array.from(this.activeTasks) : [];
|
|
252
|
+
const pendingTasks = this.pendingTasks ? Array.from(this.pendingTasks) : [];
|
|
222
253
|
return [...queuedTasks, ...activeTasks, ...pendingTasks];
|
|
223
254
|
}
|
|
224
255
|
/**
|
|
225
256
|
* Returns all tasks that failed or were aborted
|
|
226
257
|
*/
|
|
227
258
|
getFailedTasks() {
|
|
228
|
-
return [...this.failedTasks];
|
|
259
|
+
return this.failedTasks ? [...this.failedTasks] : [];
|
|
229
260
|
}
|
|
230
261
|
/**
|
|
231
262
|
* Get the context store for this stimulation
|
|
@@ -238,7 +269,7 @@ var CNSStimulation = class {
|
|
|
238
269
|
*/
|
|
239
270
|
enqueueTasks(tasks) {
|
|
240
271
|
for (const task of tasks) {
|
|
241
|
-
this.
|
|
272
|
+
this.qEnqueue(task);
|
|
242
273
|
}
|
|
243
274
|
this.tryResolveCompleted();
|
|
244
275
|
}
|
|
@@ -266,20 +297,24 @@ var CNSStimulation = class {
|
|
|
266
297
|
* Increment the active count for the SCC containing this neuron
|
|
267
298
|
*/
|
|
268
299
|
incrementSccCount(neuron2) {
|
|
300
|
+
const counts = this.activeSccCounts;
|
|
301
|
+
if (!counts) return;
|
|
269
302
|
const sccIndex = this.cns.network.getSccIndexByNeuron(neuron2);
|
|
270
303
|
if (sccIndex === void 0) return;
|
|
271
|
-
const currentCount =
|
|
272
|
-
|
|
304
|
+
const currentCount = counts.get(sccIndex) || 0;
|
|
305
|
+
counts.set(sccIndex, currentCount + 1);
|
|
273
306
|
}
|
|
274
307
|
/**
|
|
275
308
|
* Decrement the active count for the SCC containing this neuron
|
|
276
309
|
*/
|
|
277
310
|
decrementSccCount(neuron2) {
|
|
311
|
+
const counts = this.activeSccCounts;
|
|
312
|
+
if (!counts) return;
|
|
278
313
|
const sccIndex = this.cns.network.getSccIndexByNeuron(neuron2);
|
|
279
314
|
if (sccIndex === void 0) return;
|
|
280
|
-
const currentCount =
|
|
315
|
+
const currentCount = counts.get(sccIndex) || 0;
|
|
281
316
|
const nextCount = Math.max(0, currentCount - 1);
|
|
282
|
-
|
|
317
|
+
counts.set(sccIndex, nextCount);
|
|
283
318
|
if (currentCount === 0) {
|
|
284
319
|
console.warn(
|
|
285
320
|
`[CNSStimulation] Attempting to decrement SCC count below 0 for neuron`
|
|
@@ -290,7 +325,8 @@ var CNSStimulation = class {
|
|
|
290
325
|
* Check if a neuron can be guaranteed not to be visited again
|
|
291
326
|
*/
|
|
292
327
|
canNeuronBeGuaranteedDone(neuron2) {
|
|
293
|
-
if (!this.autoCleanupContextsEnabled
|
|
328
|
+
if (!this.autoCleanupContextsEnabled || !this.activeSccCounts)
|
|
329
|
+
return false;
|
|
294
330
|
return this.cns.network.canNeuronBeGuaranteedDone(
|
|
295
331
|
neuron2,
|
|
296
332
|
this.activeSccCounts
|
|
@@ -298,19 +334,19 @@ var CNSStimulation = class {
|
|
|
298
334
|
}
|
|
299
335
|
cleanupCtxIfNeeded(neuron2) {
|
|
300
336
|
if (this.autoCleanupContextsEnabled && this.canNeuronBeGuaranteedDone(neuron2)) {
|
|
301
|
-
this.
|
|
337
|
+
this.ctxStore?.delete(neuron2);
|
|
302
338
|
}
|
|
303
339
|
}
|
|
304
340
|
createSubscriberQueueItem(subscriber, inputSignal) {
|
|
305
341
|
if (this.options?.maxNeuronHops) {
|
|
306
|
-
if ((this.
|
|
342
|
+
if ((this.neuronVisitMap?.get(subscriber.neuron) ?? 0) >= this.options?.maxNeuronHops) {
|
|
307
343
|
throw new Error(
|
|
308
344
|
`Max neuron hops reached when trying to enqueue subscriber`
|
|
309
345
|
);
|
|
310
346
|
} else {
|
|
311
|
-
this.
|
|
347
|
+
this.neuronVisitMap?.set(
|
|
312
348
|
subscriber.neuron,
|
|
313
|
-
(this.
|
|
349
|
+
(this.neuronVisitMap?.get(subscriber.neuron) ?? 0) + 1
|
|
314
350
|
);
|
|
315
351
|
}
|
|
316
352
|
}
|
|
@@ -319,17 +355,24 @@ var CNSStimulation = class {
|
|
|
319
355
|
dendriteCollateral: subscriber.dendrite.collateral,
|
|
320
356
|
input: inputSignal
|
|
321
357
|
};
|
|
358
|
+
neuronActivationTask[TASK_DENDRITE] = subscriber;
|
|
322
359
|
return neuronActivationTask;
|
|
323
360
|
}
|
|
324
361
|
executeActivationTask(neuronActivationTask) {
|
|
325
|
-
|
|
326
|
-
neuronActivationTask.dendriteCollateral
|
|
327
|
-
);
|
|
328
|
-
const subscriber = subscribers.find(
|
|
329
|
-
(s) => s.neuron === neuronActivationTask.neuron
|
|
330
|
-
);
|
|
362
|
+
let subscriber = neuronActivationTask[TASK_DENDRITE];
|
|
331
363
|
if (!subscriber) {
|
|
332
|
-
this.
|
|
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
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
if (!subscriber) {
|
|
375
|
+
this.pushFailedTask({
|
|
333
376
|
task: neuronActivationTask,
|
|
334
377
|
error: new Error(
|
|
335
378
|
`Subscriber not found for activation task`
|
|
@@ -342,40 +385,58 @@ var CNSStimulation = class {
|
|
|
342
385
|
const neuron2 = subscriber.neuron;
|
|
343
386
|
const dendrite = subscriber.dendrite;
|
|
344
387
|
const inputSignal = neuronActivationTask.input;
|
|
345
|
-
const
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
388
|
+
const concurrency = neuron2.concurrency;
|
|
389
|
+
if (concurrency === void 0 || concurrency <= 0) {
|
|
390
|
+
return this.runStarter(
|
|
391
|
+
neuron2,
|
|
392
|
+
dendrite,
|
|
393
|
+
inputSignal,
|
|
394
|
+
neuronActivationTask
|
|
395
|
+
);
|
|
396
|
+
}
|
|
397
|
+
return this.instanceNeuronQueue.run(
|
|
398
|
+
neuron2,
|
|
399
|
+
() => this.runStarter(
|
|
400
|
+
neuron2,
|
|
401
|
+
dendrite,
|
|
402
|
+
inputSignal,
|
|
403
|
+
neuronActivationTask
|
|
404
|
+
)
|
|
405
|
+
);
|
|
406
|
+
}
|
|
407
|
+
runStarter(neuron2, dendrite, inputSignal, neuronActivationTask) {
|
|
408
|
+
this.markNeuronActive(neuron2);
|
|
409
|
+
let response;
|
|
410
|
+
try {
|
|
411
|
+
response = dendrite.response(
|
|
412
|
+
inputSignal?.payload,
|
|
413
|
+
neuron2.axon,
|
|
414
|
+
new CNSDendriteContext(
|
|
415
|
+
this,
|
|
370
416
|
neuron2,
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
417
|
+
this.options?.abortSignal,
|
|
418
|
+
this.cns
|
|
419
|
+
)
|
|
420
|
+
);
|
|
421
|
+
} catch (error) {
|
|
422
|
+
this.markNeuronInactive(neuron2);
|
|
423
|
+
const isAborted = this.options?.abortSignal?.aborted ?? false;
|
|
424
|
+
this.pushFailedTask({
|
|
425
|
+
task: neuronActivationTask,
|
|
426
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
427
|
+
aborted: isAborted
|
|
428
|
+
});
|
|
429
|
+
this.processResponseOrResponses(
|
|
430
|
+
neuron2,
|
|
431
|
+
inputSignal,
|
|
432
|
+
void 0,
|
|
433
|
+
error
|
|
434
|
+
);
|
|
435
|
+
return () => {
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
const maxDuration = neuron2.maxDuration;
|
|
439
|
+
if (response instanceof Promise || maxDuration) {
|
|
379
440
|
const asPromise = response instanceof Promise ? response : Promise.resolve(response);
|
|
380
441
|
const timedPromise = maxDuration && maxDuration > 0 ? new Promise((resolve, reject) => {
|
|
381
442
|
const t = setTimeout(() => {
|
|
@@ -395,48 +456,45 @@ var CNSStimulation = class {
|
|
|
395
456
|
}
|
|
396
457
|
);
|
|
397
458
|
}) : asPromise;
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
(
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
(
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
459
|
+
return timedPromise.then(
|
|
460
|
+
(signal) => {
|
|
461
|
+
return () => {
|
|
462
|
+
this.markNeuronInactive(neuron2);
|
|
463
|
+
return this.processResponseOrResponses(
|
|
464
|
+
neuron2,
|
|
465
|
+
inputSignal,
|
|
466
|
+
signal
|
|
467
|
+
);
|
|
468
|
+
};
|
|
469
|
+
},
|
|
470
|
+
(error) => {
|
|
471
|
+
return () => {
|
|
472
|
+
this.markNeuronInactive(neuron2);
|
|
473
|
+
const isAborted = this.options?.abortSignal?.aborted ?? false;
|
|
474
|
+
this.pushFailedTask({
|
|
475
|
+
task: neuronActivationTask,
|
|
476
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
477
|
+
aborted: isAborted
|
|
478
|
+
});
|
|
479
|
+
return this.processResponseOrResponses(
|
|
480
|
+
neuron2,
|
|
481
|
+
inputSignal,
|
|
482
|
+
void 0,
|
|
483
|
+
error
|
|
484
|
+
);
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
);
|
|
488
|
+
} else {
|
|
489
|
+
return () => {
|
|
490
|
+
this.markNeuronInactive(neuron2);
|
|
491
|
+
return this.processResponseOrResponses(
|
|
492
|
+
neuron2,
|
|
493
|
+
inputSignal,
|
|
494
|
+
response
|
|
427
495
|
);
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
this.markNeuronInactive(neuron2);
|
|
431
|
-
return this.processResponseOrResponses(
|
|
432
|
-
neuron2,
|
|
433
|
-
inputSignal,
|
|
434
|
-
response
|
|
435
|
-
);
|
|
436
|
-
};
|
|
437
|
-
}
|
|
438
|
-
};
|
|
439
|
-
return this.instanceNeuronQueue.run(neuron2, starter);
|
|
496
|
+
};
|
|
497
|
+
}
|
|
440
498
|
}
|
|
441
499
|
processResponseOrResponses(emitter, inputSignal, outputSignalOrSignals, error) {
|
|
442
500
|
if (Array.isArray(outputSignalOrSignals)) {
|
|
@@ -454,36 +512,62 @@ var CNSStimulation = class {
|
|
|
454
512
|
processResponse(emitter, inputSignal, outputSignal, error) {
|
|
455
513
|
const collateral2 = outputSignal?.collateral;
|
|
456
514
|
const subscribers = collateral2 ? this.cns.network.getSubscribers(collateral2) : [];
|
|
515
|
+
const onResponse = this.onResponse;
|
|
516
|
+
if (!onResponse) {
|
|
517
|
+
if (collateral2 && !error) {
|
|
518
|
+
if (emitter) this.cleanupCtxIfNeeded(emitter);
|
|
519
|
+
const n = subscribers.length;
|
|
520
|
+
if (n > 0) {
|
|
521
|
+
const tasks = new Array(n);
|
|
522
|
+
for (let i = 0; i < n; i++) {
|
|
523
|
+
tasks[i] = this.createSubscriberQueueItem(
|
|
524
|
+
subscribers[i],
|
|
525
|
+
outputSignal
|
|
526
|
+
);
|
|
527
|
+
}
|
|
528
|
+
this.scheduledCount += n;
|
|
529
|
+
for (let i = 0; i < n; i++) {
|
|
530
|
+
this.scheduledCount--;
|
|
531
|
+
this.qEnqueue(tasks[i]);
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
this.tryResolveCompleted();
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
457
538
|
const subscriberActivationTasks = [];
|
|
458
539
|
if (collateral2 && !error) {
|
|
459
540
|
if (emitter) this.cleanupCtxIfNeeded(emitter);
|
|
541
|
+
const pending = this.pendingTasks ?? (this.pendingTasks = /* @__PURE__ */ new Set());
|
|
460
542
|
for (let i = 0; i < subscribers.length; i++) {
|
|
461
543
|
const task = this.createSubscriberQueueItem(
|
|
462
544
|
subscribers[i],
|
|
463
545
|
outputSignal
|
|
464
546
|
);
|
|
465
547
|
subscriberActivationTasks.push(task);
|
|
466
|
-
|
|
548
|
+
pending.add(task);
|
|
467
549
|
}
|
|
468
550
|
}
|
|
469
551
|
this.scheduledCount += subscriberActivationTasks.length;
|
|
470
552
|
let maybePromise;
|
|
471
553
|
try {
|
|
472
|
-
maybePromise =
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
554
|
+
maybePromise = onResponse(
|
|
555
|
+
new CNSStimulationResponseImpl(
|
|
556
|
+
this,
|
|
557
|
+
this.ctxStore,
|
|
558
|
+
inputSignal,
|
|
559
|
+
outputSignal,
|
|
560
|
+
this.qSize + this.scheduledCount,
|
|
561
|
+
this.options?.maxNeuronHops && emitter ? this.neuronVisitMap?.get(emitter) ?? 0 : void 0,
|
|
562
|
+
error
|
|
563
|
+
)
|
|
564
|
+
);
|
|
481
565
|
} catch (e) {
|
|
482
566
|
this.onResponseError = e instanceof Error ? e : new Error(String(e));
|
|
483
567
|
for (let i = 0; i < subscriberActivationTasks.length; i++) {
|
|
484
568
|
this.scheduledCount--;
|
|
485
|
-
this.pendingTasks
|
|
486
|
-
this.
|
|
569
|
+
this.pendingTasks?.delete(subscriberActivationTasks[i]);
|
|
570
|
+
this.qEnqueue(subscriberActivationTasks[i]);
|
|
487
571
|
}
|
|
488
572
|
this.tryResolveCompleted();
|
|
489
573
|
return;
|
|
@@ -493,8 +577,8 @@ var CNSStimulation = class {
|
|
|
493
577
|
() => {
|
|
494
578
|
for (let i = 0; i < subscriberActivationTasks.length; i++) {
|
|
495
579
|
this.scheduledCount--;
|
|
496
|
-
this.pendingTasks
|
|
497
|
-
this.
|
|
580
|
+
this.pendingTasks?.delete(subscriberActivationTasks[i]);
|
|
581
|
+
this.qEnqueue(subscriberActivationTasks[i]);
|
|
498
582
|
}
|
|
499
583
|
this.tryResolveCompleted();
|
|
500
584
|
},
|
|
@@ -502,8 +586,8 @@ var CNSStimulation = class {
|
|
|
502
586
|
this.onResponseError = error2 instanceof Error ? error2 : new Error(String(error2));
|
|
503
587
|
for (let i = 0; i < subscriberActivationTasks.length; i++) {
|
|
504
588
|
this.scheduledCount--;
|
|
505
|
-
this.pendingTasks
|
|
506
|
-
this.
|
|
589
|
+
this.pendingTasks?.delete(subscriberActivationTasks[i]);
|
|
590
|
+
this.qEnqueue(subscriberActivationTasks[i]);
|
|
507
591
|
}
|
|
508
592
|
this.tryResolveCompleted();
|
|
509
593
|
}
|
|
@@ -512,8 +596,8 @@ var CNSStimulation = class {
|
|
|
512
596
|
}
|
|
513
597
|
for (let i = 0; i < subscriberActivationTasks.length; i++) {
|
|
514
598
|
this.scheduledCount--;
|
|
515
|
-
this.pendingTasks
|
|
516
|
-
this.
|
|
599
|
+
this.pendingTasks?.delete(subscriberActivationTasks[i]);
|
|
600
|
+
this.qEnqueue(subscriberActivationTasks[i]);
|
|
517
601
|
}
|
|
518
602
|
this.tryResolveCompleted();
|
|
519
603
|
}
|
|
@@ -589,8 +673,12 @@ var CNSNetwork = class {
|
|
|
589
673
|
/**
|
|
590
674
|
* Strongly Connected Components of the neuron graph.
|
|
591
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.
|
|
592
679
|
*/
|
|
593
|
-
this.
|
|
680
|
+
this._stronglyConnectedComponents = [];
|
|
681
|
+
this.sccBuilt = false;
|
|
594
682
|
/**
|
|
595
683
|
* Quick lookup: neuron -> SCC index for fast reachability checks.
|
|
596
684
|
*/
|
|
@@ -608,21 +696,18 @@ var CNSNetwork = class {
|
|
|
608
696
|
* Built once at construction time.
|
|
609
697
|
*/
|
|
610
698
|
this.subIndex = /* @__PURE__ */ new Map();
|
|
611
|
-
this.dendriteIndex = /* @__PURE__ */ new Map();
|
|
612
|
-
this.collateralIndex = /* @__PURE__ */ new Set();
|
|
613
699
|
this.parentNeuronByCollateral = /* @__PURE__ */ new Map();
|
|
614
|
-
this.validateUniqueIdentifiers();
|
|
615
700
|
this.buildIndexes();
|
|
616
|
-
this.buildSCC();
|
|
617
701
|
}
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
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();
|
|
626
711
|
}
|
|
627
712
|
/**
|
|
628
713
|
* Build the neuron graph where neurons are connected if one can stimulate the other.
|
|
@@ -697,7 +782,7 @@ var CNSNetwork = class {
|
|
|
697
782
|
strongConnect(neuron2);
|
|
698
783
|
}
|
|
699
784
|
}
|
|
700
|
-
this.
|
|
785
|
+
this._stronglyConnectedComponents = components;
|
|
701
786
|
for (let i = 0; i < components.length; i++) {
|
|
702
787
|
for (const neuronId of Array.from(components[i])) {
|
|
703
788
|
this.neuronToSCC.set(neuronId, i);
|
|
@@ -711,11 +796,11 @@ var CNSNetwork = class {
|
|
|
711
796
|
*/
|
|
712
797
|
buildSCCDAG(neuronGraph) {
|
|
713
798
|
this.sccDag.clear();
|
|
714
|
-
for (let i = 0; i < this.
|
|
799
|
+
for (let i = 0; i < this._stronglyConnectedComponents.length; i++) {
|
|
715
800
|
this.sccDag.set(i, /* @__PURE__ */ new Set());
|
|
716
801
|
}
|
|
717
|
-
for (let i = 0; i < this.
|
|
718
|
-
const scc = this.
|
|
802
|
+
for (let i = 0; i < this._stronglyConnectedComponents.length; i++) {
|
|
803
|
+
const scc = this._stronglyConnectedComponents[i];
|
|
719
804
|
for (const neuron2 of Array.from(scc)) {
|
|
720
805
|
const neighbors = neuronGraph.get(neuron2) || /* @__PURE__ */ new Set();
|
|
721
806
|
for (const neighbor of Array.from(neighbors)) {
|
|
@@ -733,25 +818,27 @@ var CNSNetwork = class {
|
|
|
733
818
|
*/
|
|
734
819
|
buildSCCAncestors() {
|
|
735
820
|
this.sccAncestors.clear();
|
|
736
|
-
for (let i = 0; i < this.
|
|
821
|
+
for (let i = 0; i < this._stronglyConnectedComponents.length; i++) {
|
|
737
822
|
this.sccAncestors.set(i, /* @__PURE__ */ new Set());
|
|
738
823
|
}
|
|
824
|
+
const n = this._stronglyConnectedComponents.length;
|
|
825
|
+
const forward = Array.from({ length: n }, () => []);
|
|
739
826
|
const inDegree = /* @__PURE__ */ new Map();
|
|
740
827
|
const queue = [];
|
|
741
|
-
for (let
|
|
742
|
-
const
|
|
743
|
-
inDegree.set(
|
|
744
|
-
if (
|
|
745
|
-
|
|
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);
|
|
746
834
|
}
|
|
747
835
|
}
|
|
748
836
|
while (queue.length > 0) {
|
|
749
837
|
const current = queue.shift();
|
|
750
|
-
const
|
|
751
|
-
for (const neighbor of
|
|
838
|
+
const currentAncestors = this.sccAncestors.get(current);
|
|
839
|
+
for (const neighbor of forward[current]) {
|
|
752
840
|
const neighborAncestors = this.sccAncestors.get(neighbor);
|
|
753
841
|
neighborAncestors.add(current);
|
|
754
|
-
const currentAncestors = this.sccAncestors.get(current);
|
|
755
842
|
for (const ancestor of Array.from(currentAncestors)) {
|
|
756
843
|
neighborAncestors.add(ancestor);
|
|
757
844
|
}
|
|
@@ -763,32 +850,22 @@ var CNSNetwork = class {
|
|
|
763
850
|
}
|
|
764
851
|
}
|
|
765
852
|
}
|
|
766
|
-
/**
|
|
767
|
-
* Get outgoing edges for a given SCC index
|
|
768
|
-
*/
|
|
769
|
-
getOutgoingEdges(sccIndex) {
|
|
770
|
-
const outgoing = /* @__PURE__ */ new Set();
|
|
771
|
-
for (const [targetScc, incomingEdges] of Array.from(this.sccDag)) {
|
|
772
|
-
if (incomingEdges.has(sccIndex)) {
|
|
773
|
-
outgoing.add(targetScc);
|
|
774
|
-
}
|
|
775
|
-
}
|
|
776
|
-
return outgoing;
|
|
777
|
-
}
|
|
778
853
|
/**
|
|
779
854
|
* Check if a neuron can be reached again (including self-calling).
|
|
780
855
|
* Returns true if the neuron is in a strongly connected component with more than one member,
|
|
781
856
|
* or if it's in a single-member SCC that can reach itself.
|
|
782
857
|
*/
|
|
783
858
|
getSCCSetByNeuron(neuron2) {
|
|
859
|
+
this.ensureSCC();
|
|
784
860
|
const sccIndex = this.neuronToSCC.get(neuron2);
|
|
785
861
|
if (sccIndex === void 0) return;
|
|
786
|
-
return this.
|
|
862
|
+
return this._stronglyConnectedComponents[sccIndex];
|
|
787
863
|
}
|
|
788
864
|
/**
|
|
789
865
|
* Get the SCC index for a given neuron
|
|
790
866
|
*/
|
|
791
867
|
getSccIndexByNeuron(neuron2) {
|
|
868
|
+
this.ensureSCC();
|
|
792
869
|
return this.neuronToSCC.get(neuron2);
|
|
793
870
|
}
|
|
794
871
|
/**
|
|
@@ -796,6 +873,7 @@ var CNSNetwork = class {
|
|
|
796
873
|
* This is the core logic for safe context cleanup.
|
|
797
874
|
*/
|
|
798
875
|
canNeuronBeGuaranteedDone(neuron2, activeSccCounts) {
|
|
876
|
+
this.ensureSCC();
|
|
799
877
|
const sccIndex = this.neuronToSCC.get(neuron2);
|
|
800
878
|
if (sccIndex === void 0) return true;
|
|
801
879
|
if (activeSccCounts.get(sccIndex) && activeSccCounts.get(sccIndex) > 0) {
|
|
@@ -813,8 +891,6 @@ var CNSNetwork = class {
|
|
|
813
891
|
buildIndexes() {
|
|
814
892
|
this.subIndex.clear();
|
|
815
893
|
this.parentNeuronByCollateral.clear();
|
|
816
|
-
this.dendriteIndex.clear();
|
|
817
|
-
this.collateralIndex.clear();
|
|
818
894
|
for (const neuron2 of this.neurons) {
|
|
819
895
|
for (const dendrite of neuron2.dendrites) {
|
|
820
896
|
const key = dendrite.collateral;
|
|
@@ -824,25 +900,40 @@ var CNSNetwork = class {
|
|
|
824
900
|
key,
|
|
825
901
|
arr
|
|
826
902
|
);
|
|
827
|
-
this.dendriteIndex.set(key, dendrite);
|
|
828
903
|
}
|
|
829
904
|
Object.values(neuron2.axon).forEach((collateral2) => {
|
|
830
905
|
this.parentNeuronByCollateral.set(
|
|
831
906
|
collateral2,
|
|
832
907
|
neuron2
|
|
833
908
|
);
|
|
834
|
-
this.collateralIndex.add(collateral2);
|
|
835
909
|
});
|
|
836
910
|
}
|
|
837
911
|
}
|
|
838
912
|
getParentNeuronByCollateral(collateral2) {
|
|
839
913
|
return this.parentNeuronByCollateral.get(collateral2);
|
|
840
914
|
}
|
|
915
|
+
// Computed on demand (rarely used): one dendrite per collateral, last wins —
|
|
916
|
+
// preserving the previous index semantics without building it every construct.
|
|
841
917
|
getDendrites() {
|
|
842
|
-
|
|
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());
|
|
843
928
|
}
|
|
844
929
|
getCollaterals() {
|
|
845
|
-
|
|
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);
|
|
846
937
|
}
|
|
847
938
|
getSubscribers(collateral2) {
|
|
848
939
|
return this.subIndex.get(collateral2) ?? [];
|
|
@@ -877,8 +968,7 @@ var CNS = class {
|
|
|
877
968
|
}
|
|
878
969
|
wrapOnResponse(local) {
|
|
879
970
|
if (this.globalResponseListeners.length === 0 && !local) {
|
|
880
|
-
return
|
|
881
|
-
};
|
|
971
|
+
return void 0;
|
|
882
972
|
}
|
|
883
973
|
return (r) => {
|
|
884
974
|
let anyPromise = false;
|
|
@@ -922,10 +1012,8 @@ var CNS = class {
|
|
|
922
1012
|
const stimulation = new CNSStimulation(
|
|
923
1013
|
this,
|
|
924
1014
|
this.instanceNeuronQueue,
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
onResponse: wrapped
|
|
928
|
-
}
|
|
1015
|
+
options,
|
|
1016
|
+
wrapped
|
|
929
1017
|
);
|
|
930
1018
|
stimulation.responseToSignal(signalOrSignals);
|
|
931
1019
|
return stimulation;
|
|
@@ -938,10 +1026,8 @@ var CNS = class {
|
|
|
938
1026
|
const stimulation = new CNSStimulation(
|
|
939
1027
|
this,
|
|
940
1028
|
this.instanceNeuronQueue,
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
onResponse: wrapped
|
|
944
|
-
}
|
|
1029
|
+
options,
|
|
1030
|
+
wrapped
|
|
945
1031
|
);
|
|
946
1032
|
stimulation.enqueueTasks(tasks);
|
|
947
1033
|
return stimulation;
|
|
@@ -1121,27 +1207,26 @@ var neuron = (axon) => {
|
|
|
1121
1207
|
modality: singleModality,
|
|
1122
1208
|
modalities: multipleModalities,
|
|
1123
1209
|
afferentPaths: singleAfferentPaths,
|
|
1124
|
-
default:
|
|
1125
|
-
default: globalDefaultHandler,
|
|
1210
|
+
default: defaultHandler,
|
|
1126
1211
|
output
|
|
1127
1212
|
} = s;
|
|
1128
1213
|
const modalityConfigs = multipleModalities ? multipleModalities : singleModality ? [
|
|
1129
1214
|
{
|
|
1130
1215
|
modality: singleModality,
|
|
1131
1216
|
afferentPaths: singleAfferentPaths,
|
|
1132
|
-
default:
|
|
1217
|
+
default: defaultHandler
|
|
1133
1218
|
}
|
|
1134
1219
|
] : [];
|
|
1135
1220
|
const response = (payload, axon2, ctx) => {
|
|
1136
1221
|
const stimulation = ctx.stimulation;
|
|
1137
1222
|
const stimOptions = stimulation?.options;
|
|
1138
1223
|
const runGlobalDefault = () => {
|
|
1139
|
-
if (!
|
|
1224
|
+
if (!defaultHandler) {
|
|
1140
1225
|
throw new Error(
|
|
1141
1226
|
`modalityDendrite: No handler found for modality and no default handler provided`
|
|
1142
1227
|
);
|
|
1143
1228
|
}
|
|
1144
|
-
const res =
|
|
1229
|
+
const res = defaultHandler(payload, axon2, ctx);
|
|
1145
1230
|
if (res && typeof res.then === "function") {
|
|
1146
1231
|
return res.then(
|
|
1147
1232
|
(result) => output(result, axon2, ctx)
|
|
@@ -1160,7 +1245,7 @@ var neuron = (axon) => {
|
|
|
1160
1245
|
}
|
|
1161
1246
|
const stimAfferentPath = stimOptions.afferentPath;
|
|
1162
1247
|
const handler = stimAfferentPath && matchingConfig.afferentPaths ? matchingConfig.afferentPaths.get(stimAfferentPath) : void 0;
|
|
1163
|
-
const effectiveHandler = handler ?? matchingConfig.default ??
|
|
1248
|
+
const effectiveHandler = handler ?? matchingConfig.default ?? defaultHandler;
|
|
1164
1249
|
if (!effectiveHandler) {
|
|
1165
1250
|
throw new Error(
|
|
1166
1251
|
`modalityDendrite: No handler found for afferent path in modality and no default handler provided`
|