@cnstra/core 2.6.0 → 3.2.1
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/README.md +51 -35
- package/dist/CNS.d.ts +10 -3
- package/dist/CNS.d.ts.map +1 -1
- package/dist/CNSCollateral.d.ts +1 -0
- package/dist/CNSCollateral.d.ts.map +1 -1
- package/dist/CNSInstanceNeuronQueue.d.ts +2 -1
- package/dist/CNSInstanceNeuronQueue.d.ts.map +1 -1
- package/dist/CNSNetwork.d.ts +14 -6
- package/dist/CNSNetwork.d.ts.map +1 -1
- package/dist/CNSStimulation.d.ts +41 -41
- package/dist/CNSStimulation.d.ts.map +1 -1
- package/dist/createCNS.d.ts +4 -0
- package/dist/createCNS.d.ts.map +1 -0
- package/dist/factory/index.d.ts +37 -40
- package/dist/factory/index.d.ts.map +1 -1
- package/dist/index.cjs +534 -408
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +528 -401
- package/dist/index.js.map +1 -1
- package/dist/types/TCNSModalitySelection.d.ts +13 -0
- package/dist/types/TCNSModalitySelection.d.ts.map +1 -0
- package/dist/utils/CNSStimulationGate.d.ts +2 -2
- package/dist/utils/CNSStimulationGate.d.ts.map +1 -1
- package/dist/utils/cnsComposeModalityResponse.d.ts +2 -0
- package/dist/utils/cnsComposeModalityResponse.d.ts.map +1 -0
- package/dist/utils/cnsResolveModalityHandler.d.ts +4 -0
- package/dist/utils/cnsResolveModalityHandler.d.ts.map +1 -0
- package/package.json +5 -4
- package/dist/CNSStimulationContextStore.d.ts +0 -11
- package/dist/CNSStimulationContextStore.d.ts.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,39 +1,17 @@
|
|
|
1
|
-
// src/CNSStimulationContextStore.ts
|
|
2
|
-
var CNSStimulationContextStore = class {
|
|
3
|
-
constructor(ctx) {
|
|
4
|
-
this.ctx = ctx;
|
|
5
|
-
}
|
|
6
|
-
get(key) {
|
|
7
|
-
return this.ctx?.get(key);
|
|
8
|
-
}
|
|
9
|
-
set(key, value) {
|
|
10
|
-
(this.ctx ?? (this.ctx = /* @__PURE__ */ new Map())).set(key, value);
|
|
11
|
-
}
|
|
12
|
-
getAll() {
|
|
13
|
-
return this.ctx ? new Map(this.ctx) : /* @__PURE__ */ new Map();
|
|
14
|
-
}
|
|
15
|
-
setAll(values) {
|
|
16
|
-
const ctx = this.ctx ?? (this.ctx = /* @__PURE__ */ new Map());
|
|
17
|
-
ctx.clear();
|
|
18
|
-
for (const [key, value] of values) ctx.set(key, value);
|
|
19
|
-
}
|
|
20
|
-
delete(key) {
|
|
21
|
-
this.ctx?.delete(key);
|
|
22
|
-
}
|
|
23
|
-
};
|
|
24
|
-
|
|
25
1
|
// src/CNSStimulation.ts
|
|
26
2
|
var TASK_DENDRITE = /* @__PURE__ */ Symbol("cnstra.taskDendrite");
|
|
3
|
+
var TASK_ACTIVE_POSITION = /* @__PURE__ */ Symbol("cnstra.taskActivePosition");
|
|
4
|
+
var TASK_PENDING_POSITION = /* @__PURE__ */ Symbol("cnstra.taskPendingPosition");
|
|
27
5
|
var CNSDendriteContext = class {
|
|
28
|
-
constructor(stimulation,
|
|
6
|
+
constructor(stimulation, abortSignal, cns, task) {
|
|
29
7
|
this.stimulation = stimulation;
|
|
30
|
-
this.neuron = neuron2;
|
|
31
8
|
this.abortSignal = abortSignal;
|
|
32
9
|
this.cns = cns;
|
|
10
|
+
this.task = task;
|
|
33
11
|
}
|
|
34
|
-
// Prototype getter, not a constructor field:
|
|
35
|
-
//
|
|
36
|
-
//
|
|
12
|
+
// Prototype getter, not a constructor field: you only pay a single `cns.global`
|
|
13
|
+
// read on the dendrites that actually reach for it. Value getter, so
|
|
14
|
+
// `{ global }` destructuring is safe.
|
|
37
15
|
get global() {
|
|
38
16
|
return this.cns.global;
|
|
39
17
|
}
|
|
@@ -44,20 +22,16 @@ var CNSDendriteContext = class {
|
|
|
44
22
|
get stimulationContext() {
|
|
45
23
|
return this.stimulation.options?.stimulationContext;
|
|
46
24
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
this.
|
|
52
|
-
}
|
|
53
|
-
delete() {
|
|
54
|
-
this.stimulation.getContext().delete(this.neuron);
|
|
25
|
+
// This activation's own store - written by a response listener before the
|
|
26
|
+
// activation ran. Prototype getter like the two above: nothing is allocated
|
|
27
|
+
// for a handler that never reaches for it.
|
|
28
|
+
get activationContext() {
|
|
29
|
+
return this.task?.activationContext;
|
|
55
30
|
}
|
|
56
31
|
};
|
|
57
32
|
var CNSStimulationResponseImpl = class {
|
|
58
|
-
constructor(stimulation,
|
|
33
|
+
constructor(stimulation, inputSignal, outputSignal, queueLength, pendingActivations, activeActivations, hops, error, neuron, startedAt, duration, scheduledActivationTasks = EMPTY_TASKS, activationContext = void 0) {
|
|
59
34
|
this.stimulation = stimulation;
|
|
60
|
-
this.store = store;
|
|
61
35
|
this.inputSignal = inputSignal;
|
|
62
36
|
this.outputSignal = outputSignal;
|
|
63
37
|
this.queueLength = queueLength;
|
|
@@ -65,12 +39,15 @@ var CNSStimulationResponseImpl = class {
|
|
|
65
39
|
this.activeActivations = activeActivations;
|
|
66
40
|
this.hops = hops;
|
|
67
41
|
this.error = error;
|
|
68
|
-
this.neuron =
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
42
|
+
this.neuron = neuron;
|
|
43
|
+
this.startedAt = startedAt;
|
|
44
|
+
this.duration = duration;
|
|
45
|
+
this.scheduledActivationTasks = scheduledActivationTasks;
|
|
46
|
+
this.activationContext = activationContext;
|
|
72
47
|
}
|
|
73
48
|
};
|
|
49
|
+
var EMPTY_TASKS = [];
|
|
50
|
+
var EMPTY_SUBSCRIBERS = [];
|
|
74
51
|
var CNSStimulation = class {
|
|
75
52
|
constructor(cns, instanceNeuronQueue, options, onResponse, onDrain) {
|
|
76
53
|
this.cns = cns;
|
|
@@ -97,14 +74,16 @@ var CNSStimulation = class {
|
|
|
97
74
|
this.isCompleted = false;
|
|
98
75
|
this.completionRejected = false;
|
|
99
76
|
this.onDrain = onDrain;
|
|
100
|
-
this.ctxStore = options?.ctx;
|
|
101
77
|
this.instanceNeuronQueue = instanceNeuronQueue;
|
|
102
78
|
this.onResponse = onResponse;
|
|
79
|
+
this.concurrencyLimit = this.options?.concurrency ?? Infinity;
|
|
80
|
+
this.abortSignal = this.options?.abortSignal;
|
|
81
|
+
this.subscribersByCollateralIndex = cns.network.getSubscriberTable(
|
|
82
|
+
this.options?.modality,
|
|
83
|
+
this.options?.afferentPath
|
|
84
|
+
);
|
|
103
85
|
if (this.options?.maxNeuronHops) {
|
|
104
|
-
this.
|
|
105
|
-
}
|
|
106
|
-
if (this.autoCleanupContextsEnabled) {
|
|
107
|
-
this.activeSccCounts = /* @__PURE__ */ new Map();
|
|
86
|
+
this.neuronVisitCounts = [];
|
|
108
87
|
}
|
|
109
88
|
}
|
|
110
89
|
pushFailedTask(failure) {
|
|
@@ -119,14 +98,9 @@ var CNSStimulation = class {
|
|
|
119
98
|
this.completionError = error;
|
|
120
99
|
this.rejectCompleted?.(error);
|
|
121
100
|
}
|
|
122
|
-
/** Lazily-created neuron context store (see {@link ctxStore}). */
|
|
123
|
-
get ctx() {
|
|
124
|
-
return this.ctxStore ?? (this.ctxStore = new CNSStimulationContextStore());
|
|
125
|
-
}
|
|
126
101
|
// ----- Inlined activation queue / pump -----
|
|
127
102
|
get canStartOperation() {
|
|
128
|
-
|
|
129
|
-
return this.activeOperations < limit && !this.options?.abortSignal?.aborted;
|
|
103
|
+
return this.activeOperations < this.concurrencyLimit && !this.abortSignal?.aborted;
|
|
130
104
|
}
|
|
131
105
|
qResize() {
|
|
132
106
|
const oldCapacity = this.qCapacity;
|
|
@@ -183,48 +157,56 @@ var CNSStimulation = class {
|
|
|
183
157
|
return;
|
|
184
158
|
}
|
|
185
159
|
this.pumping = true;
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
(
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
if (this.
|
|
201
|
-
|
|
160
|
+
try {
|
|
161
|
+
while (this.canStartOperation && this.qSize > 0) {
|
|
162
|
+
const task = this.qDequeue();
|
|
163
|
+
this.activeOperations++;
|
|
164
|
+
const ret = this.executeActivationTask(task);
|
|
165
|
+
if (ret && typeof ret.then === "function") {
|
|
166
|
+
this.addPositionedTask(this.activeTasks ?? (this.activeTasks = []), task, TASK_ACTIVE_POSITION);
|
|
167
|
+
ret.then(
|
|
168
|
+
(cb) => {
|
|
169
|
+
this.activeOperations--;
|
|
170
|
+
this.removeActiveTask(task);
|
|
171
|
+
this.beginTurn();
|
|
172
|
+
try {
|
|
173
|
+
if (typeof cb === "function") cb();
|
|
174
|
+
if (this.qSize > 0 && this.canStartOperation) {
|
|
175
|
+
if (this.pumping) this.needsPump = true;
|
|
176
|
+
else this.pump();
|
|
177
|
+
}
|
|
178
|
+
} catch (error) {
|
|
179
|
+
this.endRunWithError(error);
|
|
180
|
+
} finally {
|
|
181
|
+
this.endTurn();
|
|
202
182
|
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
183
|
+
},
|
|
184
|
+
(err) => {
|
|
185
|
+
this.activeOperations--;
|
|
186
|
+
this.removeActiveTask(task);
|
|
187
|
+
this.beginTurn();
|
|
188
|
+
try {
|
|
189
|
+
if (this.qSize > 0 && this.canStartOperation) {
|
|
190
|
+
if (this.pumping) this.needsPump = true;
|
|
191
|
+
else this.pump();
|
|
192
|
+
}
|
|
193
|
+
} finally {
|
|
194
|
+
this.endTurn();
|
|
215
195
|
}
|
|
216
|
-
|
|
217
|
-
this.endTurn();
|
|
196
|
+
throw err;
|
|
218
197
|
}
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
198
|
+
);
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
this.activeOperations--;
|
|
202
|
+
if (typeof ret === "function") ret();
|
|
223
203
|
}
|
|
224
|
-
|
|
225
|
-
|
|
204
|
+
} catch (error) {
|
|
205
|
+
this.endRunWithError(error);
|
|
206
|
+
throw error;
|
|
207
|
+
} finally {
|
|
208
|
+
this.pumping = false;
|
|
226
209
|
}
|
|
227
|
-
this.pumping = false;
|
|
228
210
|
if (this.needsPump) {
|
|
229
211
|
this.needsPump = false;
|
|
230
212
|
this.pump();
|
|
@@ -266,14 +248,30 @@ var CNSStimulation = class {
|
|
|
266
248
|
}
|
|
267
249
|
}
|
|
268
250
|
/**
|
|
269
|
-
* Activations that exist (or are reserved) but whose dendrite
|
|
270
|
-
* been invoked yet: sitting in the ring buffer, created and held in
|
|
251
|
+
* Activations that exist (or are reserved) but whose dendrite response has
|
|
252
|
+
* not been invoked yet: sitting in the ring buffer, created and held in
|
|
271
253
|
* pendingTasks, or reserved by an array fan-out mid-dispatch. Subscribers
|
|
272
254
|
* parked behind an async onResponse land here too - their body has not run.
|
|
273
255
|
*/
|
|
274
256
|
get pendingActivations() {
|
|
275
257
|
return this.qSize + this.scheduledCount + this.reservedActivations;
|
|
276
258
|
}
|
|
259
|
+
/**
|
|
260
|
+
* End the run because the machinery itself threw. Dendrite bodies never get
|
|
261
|
+
* here - runStarter catches those and records them as failed tasks; what
|
|
262
|
+
* lands here is a throw from dispatch, today the hop-limit guard in
|
|
263
|
+
* createSubscriberQueueItem. Nothing settles the counters after such a
|
|
264
|
+
* throw, so completion has to be forced: without it the stimulation neither
|
|
265
|
+
* resolves nor rejects, and a worker holds a job that never finishes and
|
|
266
|
+
* never fails.
|
|
267
|
+
*/
|
|
268
|
+
endRunWithError(error) {
|
|
269
|
+
if (this.isCompleted) return;
|
|
270
|
+
this.isCompleted = true;
|
|
271
|
+
this.finalizeReject(
|
|
272
|
+
error instanceof Error ? error : new Error(String(error))
|
|
273
|
+
);
|
|
274
|
+
}
|
|
277
275
|
tryResolveCompleted() {
|
|
278
276
|
if (this.isCompleted) return;
|
|
279
277
|
const noActive = this.activeOperations === 0;
|
|
@@ -297,6 +295,7 @@ var CNSStimulation = class {
|
|
|
297
295
|
}
|
|
298
296
|
}
|
|
299
297
|
this.isCompleted = true;
|
|
298
|
+
if (!noPending) this.emitAbortedResponse();
|
|
300
299
|
this.finalizeReject(new Error("Stimulation aborted"));
|
|
301
300
|
return;
|
|
302
301
|
}
|
|
@@ -329,10 +328,34 @@ var CNSStimulation = class {
|
|
|
329
328
|
*/
|
|
330
329
|
getAllActivationTasks() {
|
|
331
330
|
const queuedTasks = this.qGetQueuedTasks();
|
|
332
|
-
const activeTasks = this.activeTasks
|
|
333
|
-
const pendingTasks = this.pendingTasks
|
|
331
|
+
const activeTasks = this.activeTasks ?? EMPTY_TASKS;
|
|
332
|
+
const pendingTasks = this.pendingTasks ?? EMPTY_TASKS;
|
|
334
333
|
return [...queuedTasks, ...activeTasks, ...pendingTasks];
|
|
335
334
|
}
|
|
335
|
+
getSubscribers(collateral) {
|
|
336
|
+
return this.subscribersByCollateralIndex[collateral.index] ?? EMPTY_SUBSCRIBERS;
|
|
337
|
+
}
|
|
338
|
+
addPositionedTask(tasks, task, positionKey) {
|
|
339
|
+
task[positionKey] = tasks.length;
|
|
340
|
+
tasks.push(task);
|
|
341
|
+
}
|
|
342
|
+
removePositionedTask(tasks, task, positionKey) {
|
|
343
|
+
if (tasks === void 0) return;
|
|
344
|
+
const position = task[positionKey];
|
|
345
|
+
if (position === void 0 || tasks[position] !== task) return;
|
|
346
|
+
const lastTask = tasks.pop();
|
|
347
|
+
if (lastTask !== task) {
|
|
348
|
+
tasks[position] = lastTask;
|
|
349
|
+
lastTask[positionKey] = position;
|
|
350
|
+
}
|
|
351
|
+
task[positionKey] = void 0;
|
|
352
|
+
}
|
|
353
|
+
removeActiveTask(task) {
|
|
354
|
+
this.removePositionedTask(this.activeTasks, task, TASK_ACTIVE_POSITION);
|
|
355
|
+
}
|
|
356
|
+
removePendingTask(task) {
|
|
357
|
+
this.removePositionedTask(this.pendingTasks, task, TASK_PENDING_POSITION);
|
|
358
|
+
}
|
|
336
359
|
/**
|
|
337
360
|
* Returns all tasks that failed or were aborted
|
|
338
361
|
*/
|
|
@@ -345,18 +368,25 @@ var CNSStimulation = class {
|
|
|
345
368
|
* failed/aborted tasks. This is the set to persist for resume: re-activating
|
|
346
369
|
* it re-runs exactly the not-yet-done work. Safe to call at any point during
|
|
347
370
|
* a run (for progress) or after it fails.
|
|
371
|
+
*
|
|
372
|
+
* Each task appears exactly once. The two sources overlap: aborting marks
|
|
373
|
+
* every still-queued task as failed without dequeuing it, so a task sits in
|
|
374
|
+
* the ring buffer AND in failedTasks. Concatenating them blindly handed the
|
|
375
|
+
* broker a checkpoint with the whole queued frontier listed twice, and the
|
|
376
|
+
* resumed run re-executed every one of those tasks a second time - duplicate
|
|
377
|
+
* charges, emails and POSTs on every graceful shutdown.
|
|
348
378
|
*/
|
|
349
379
|
getOutstandingTasks() {
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
return
|
|
380
|
+
const seen = /* @__PURE__ */ new Set();
|
|
381
|
+
const outstanding = [];
|
|
382
|
+
const add = (task) => {
|
|
383
|
+
if (seen.has(task)) return;
|
|
384
|
+
seen.add(task);
|
|
385
|
+
outstanding.push(task);
|
|
386
|
+
};
|
|
387
|
+
for (const task of this.getAllActivationTasks()) add(task);
|
|
388
|
+
for (const failure of this.getFailedTasks()) add(failure.task);
|
|
389
|
+
return outstanding;
|
|
360
390
|
}
|
|
361
391
|
/**
|
|
362
392
|
* Enqueue activation tasks directly into the stimulation queue
|
|
@@ -375,79 +405,17 @@ var CNSStimulation = class {
|
|
|
375
405
|
get concurrencyEnabled() {
|
|
376
406
|
return this.options?.concurrency !== void 0 && this.options?.concurrency > 0;
|
|
377
407
|
}
|
|
378
|
-
get autoCleanupContextsEnabled() {
|
|
379
|
-
return this.cns?.options?.autoCleanupContexts ?? false;
|
|
380
|
-
}
|
|
381
|
-
/**
|
|
382
|
-
* Mark a neuron as active (being processed)
|
|
383
|
-
*/
|
|
384
|
-
markNeuronActive(neuron2) {
|
|
385
|
-
if (!this.autoCleanupContextsEnabled) return;
|
|
386
|
-
this.incrementSccCount(neuron2);
|
|
387
|
-
}
|
|
388
|
-
/**
|
|
389
|
-
* Mark a neuron as inactive (finished processing)
|
|
390
|
-
*/
|
|
391
|
-
markNeuronInactive(neuron2) {
|
|
392
|
-
if (!this.autoCleanupContextsEnabled) return;
|
|
393
|
-
this.decrementSccCount(neuron2);
|
|
394
|
-
}
|
|
395
|
-
/**
|
|
396
|
-
* Increment the active count for the SCC containing this neuron
|
|
397
|
-
*/
|
|
398
|
-
incrementSccCount(neuron2) {
|
|
399
|
-
const counts = this.activeSccCounts;
|
|
400
|
-
if (!counts) return;
|
|
401
|
-
const sccIndex = this.cns.network.getSccIndexByNeuron(neuron2);
|
|
402
|
-
if (sccIndex === void 0) return;
|
|
403
|
-
const currentCount = counts.get(sccIndex) || 0;
|
|
404
|
-
counts.set(sccIndex, currentCount + 1);
|
|
405
|
-
}
|
|
406
|
-
/**
|
|
407
|
-
* Decrement the active count for the SCC containing this neuron
|
|
408
|
-
*/
|
|
409
|
-
decrementSccCount(neuron2) {
|
|
410
|
-
const counts = this.activeSccCounts;
|
|
411
|
-
if (!counts) return;
|
|
412
|
-
const sccIndex = this.cns.network.getSccIndexByNeuron(neuron2);
|
|
413
|
-
if (sccIndex === void 0) return;
|
|
414
|
-
const currentCount = counts.get(sccIndex) || 0;
|
|
415
|
-
const nextCount = Math.max(0, currentCount - 1);
|
|
416
|
-
counts.set(sccIndex, nextCount);
|
|
417
|
-
if (currentCount === 0) {
|
|
418
|
-
console.warn(
|
|
419
|
-
`[CNSStimulation] Attempting to decrement SCC count below 0 for neuron`
|
|
420
|
-
);
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
/**
|
|
424
|
-
* Check if a neuron can be guaranteed not to be visited again
|
|
425
|
-
*/
|
|
426
|
-
canNeuronBeGuaranteedDone(neuron2) {
|
|
427
|
-
if (!this.autoCleanupContextsEnabled || !this.activeSccCounts)
|
|
428
|
-
return false;
|
|
429
|
-
return this.cns.network.canNeuronBeGuaranteedDone(
|
|
430
|
-
neuron2,
|
|
431
|
-
this.activeSccCounts
|
|
432
|
-
);
|
|
433
|
-
}
|
|
434
|
-
cleanupCtxIfNeeded(neuron2) {
|
|
435
|
-
if (this.autoCleanupContextsEnabled && this.canNeuronBeGuaranteedDone(neuron2)) {
|
|
436
|
-
this.ctxStore?.delete(neuron2);
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
408
|
createSubscriberQueueItem(subscriber, inputSignal) {
|
|
440
|
-
|
|
441
|
-
|
|
409
|
+
const neuronVisitCounts = this.neuronVisitCounts;
|
|
410
|
+
if (neuronVisitCounts !== void 0) {
|
|
411
|
+
const neuronIndex = subscriber.neuron.index;
|
|
412
|
+
const neuronVisitCount = neuronVisitCounts[neuronIndex] ?? 0;
|
|
413
|
+
if (neuronVisitCount >= this.options.maxNeuronHops) {
|
|
442
414
|
throw new Error(
|
|
443
415
|
`Max neuron hops reached when trying to enqueue subscriber`
|
|
444
416
|
);
|
|
445
|
-
} else {
|
|
446
|
-
this.neuronVisitMap?.set(
|
|
447
|
-
subscriber.neuron,
|
|
448
|
-
(this.neuronVisitMap?.get(subscriber.neuron) ?? 0) + 1
|
|
449
|
-
);
|
|
450
417
|
}
|
|
418
|
+
neuronVisitCounts[neuronIndex] = neuronVisitCount + 1;
|
|
451
419
|
}
|
|
452
420
|
const neuronActivationTask = {
|
|
453
421
|
neuron: subscriber.neuron,
|
|
@@ -455,12 +423,14 @@ var CNSStimulation = class {
|
|
|
455
423
|
input: inputSignal
|
|
456
424
|
};
|
|
457
425
|
neuronActivationTask[TASK_DENDRITE] = subscriber;
|
|
426
|
+
neuronActivationTask[TASK_ACTIVE_POSITION] = void 0;
|
|
427
|
+
neuronActivationTask[TASK_PENDING_POSITION] = void 0;
|
|
458
428
|
return neuronActivationTask;
|
|
459
429
|
}
|
|
460
430
|
executeActivationTask(neuronActivationTask) {
|
|
461
431
|
let subscriber = neuronActivationTask[TASK_DENDRITE];
|
|
462
432
|
if (!subscriber) {
|
|
463
|
-
const subscribers = this.
|
|
433
|
+
const subscribers = this.getSubscribers(
|
|
464
434
|
neuronActivationTask.dendriteCollateral
|
|
465
435
|
);
|
|
466
436
|
for (let i = 0; i < subscribers.length; i++) {
|
|
@@ -482,48 +452,49 @@ var CNSStimulation = class {
|
|
|
482
452
|
void 0,
|
|
483
453
|
neuronActivationTask.input,
|
|
484
454
|
void 0,
|
|
485
|
-
error
|
|
455
|
+
error,
|
|
456
|
+
void 0,
|
|
457
|
+
neuronActivationTask.activationContext
|
|
486
458
|
);
|
|
487
459
|
};
|
|
488
460
|
}
|
|
489
|
-
const
|
|
461
|
+
const neuron = subscriber.neuron;
|
|
490
462
|
const dendrite = subscriber.dendrite;
|
|
491
463
|
const inputSignal = neuronActivationTask.input;
|
|
492
|
-
const concurrency =
|
|
464
|
+
const concurrency = neuron.concurrency;
|
|
493
465
|
if (concurrency === void 0 || concurrency <= 0) {
|
|
494
466
|
return this.runStarter(
|
|
495
|
-
|
|
467
|
+
neuron,
|
|
496
468
|
dendrite,
|
|
497
469
|
inputSignal,
|
|
498
470
|
neuronActivationTask
|
|
499
471
|
);
|
|
500
472
|
}
|
|
501
473
|
return this.instanceNeuronQueue.run(
|
|
502
|
-
|
|
474
|
+
neuron,
|
|
503
475
|
() => this.runStarter(
|
|
504
|
-
|
|
476
|
+
neuron,
|
|
505
477
|
dendrite,
|
|
506
478
|
inputSignal,
|
|
507
479
|
neuronActivationTask
|
|
508
480
|
)
|
|
509
481
|
);
|
|
510
482
|
}
|
|
511
|
-
runStarter(
|
|
512
|
-
this.markNeuronActive(neuron2);
|
|
483
|
+
runStarter(neuron, dendrite, inputSignal, neuronActivationTask) {
|
|
513
484
|
let response;
|
|
485
|
+
const startedAt = this.onResponse === void 0 ? void 0 : Date.now();
|
|
514
486
|
try {
|
|
515
487
|
response = dendrite.response(
|
|
516
488
|
inputSignal?.payload,
|
|
517
|
-
|
|
489
|
+
neuron.axon,
|
|
518
490
|
new CNSDendriteContext(
|
|
519
491
|
this,
|
|
520
|
-
neuron2,
|
|
521
492
|
this.options?.abortSignal,
|
|
522
|
-
this.cns
|
|
493
|
+
this.cns,
|
|
494
|
+
neuronActivationTask
|
|
523
495
|
)
|
|
524
496
|
);
|
|
525
497
|
} catch (error) {
|
|
526
|
-
this.markNeuronInactive(neuron2);
|
|
527
498
|
const isAborted = this.options?.abortSignal?.aborted ?? false;
|
|
528
499
|
this.pushFailedTask({
|
|
529
500
|
task: neuronActivationTask,
|
|
@@ -532,14 +503,16 @@ var CNSStimulation = class {
|
|
|
532
503
|
});
|
|
533
504
|
return () => {
|
|
534
505
|
this.processResponseOrResponses(
|
|
535
|
-
|
|
506
|
+
neuron,
|
|
536
507
|
inputSignal,
|
|
537
508
|
void 0,
|
|
538
|
-
error
|
|
509
|
+
error,
|
|
510
|
+
startedAt,
|
|
511
|
+
neuronActivationTask.activationContext
|
|
539
512
|
);
|
|
540
513
|
};
|
|
541
514
|
}
|
|
542
|
-
const maxDuration =
|
|
515
|
+
const maxDuration = neuron.maxDuration;
|
|
543
516
|
if (response instanceof Promise || maxDuration) {
|
|
544
517
|
const asPromise = response instanceof Promise ? response : Promise.resolve(response);
|
|
545
518
|
const timedPromise = maxDuration && maxDuration > 0 ? new Promise((resolve, reject) => {
|
|
@@ -563,17 +536,18 @@ var CNSStimulation = class {
|
|
|
563
536
|
return timedPromise.then(
|
|
564
537
|
(signal) => {
|
|
565
538
|
return () => {
|
|
566
|
-
this.markNeuronInactive(neuron2);
|
|
567
539
|
return this.processResponseOrResponses(
|
|
568
|
-
|
|
540
|
+
neuron,
|
|
569
541
|
inputSignal,
|
|
570
|
-
signal
|
|
542
|
+
signal,
|
|
543
|
+
void 0,
|
|
544
|
+
startedAt,
|
|
545
|
+
neuronActivationTask.activationContext
|
|
571
546
|
);
|
|
572
547
|
};
|
|
573
548
|
},
|
|
574
549
|
(error) => {
|
|
575
550
|
return () => {
|
|
576
|
-
this.markNeuronInactive(neuron2);
|
|
577
551
|
const isAborted = this.options?.abortSignal?.aborted ?? false;
|
|
578
552
|
this.pushFailedTask({
|
|
579
553
|
task: neuronActivationTask,
|
|
@@ -581,29 +555,42 @@ var CNSStimulation = class {
|
|
|
581
555
|
aborted: isAborted
|
|
582
556
|
});
|
|
583
557
|
return this.processResponseOrResponses(
|
|
584
|
-
|
|
558
|
+
neuron,
|
|
585
559
|
inputSignal,
|
|
586
560
|
void 0,
|
|
587
|
-
error
|
|
561
|
+
error,
|
|
562
|
+
startedAt,
|
|
563
|
+
neuronActivationTask.activationContext
|
|
588
564
|
);
|
|
589
565
|
};
|
|
590
566
|
}
|
|
591
567
|
);
|
|
592
568
|
} else {
|
|
593
569
|
return () => {
|
|
594
|
-
this.markNeuronInactive(neuron2);
|
|
595
570
|
return this.processResponseOrResponses(
|
|
596
|
-
|
|
571
|
+
neuron,
|
|
597
572
|
inputSignal,
|
|
598
|
-
response
|
|
573
|
+
response,
|
|
574
|
+
void 0,
|
|
575
|
+
startedAt,
|
|
576
|
+
neuronActivationTask.activationContext
|
|
599
577
|
);
|
|
600
578
|
};
|
|
601
579
|
}
|
|
602
580
|
}
|
|
603
|
-
processResponseOrResponses(emitter, inputSignal, outputSignalOrSignals, error) {
|
|
581
|
+
processResponseOrResponses(emitter, inputSignal, outputSignalOrSignals, error, startedAt, activationContext) {
|
|
582
|
+
const duration = startedAt === void 0 ? void 0 : Date.now() - startedAt;
|
|
604
583
|
if (Array.isArray(outputSignalOrSignals)) {
|
|
605
584
|
if (outputSignalOrSignals.length === 0) {
|
|
606
|
-
this.processResponse(
|
|
585
|
+
this.processResponse(
|
|
586
|
+
emitter,
|
|
587
|
+
inputSignal,
|
|
588
|
+
void 0,
|
|
589
|
+
error,
|
|
590
|
+
startedAt,
|
|
591
|
+
duration,
|
|
592
|
+
activationContext
|
|
593
|
+
);
|
|
607
594
|
return;
|
|
608
595
|
}
|
|
609
596
|
let perSignal;
|
|
@@ -612,32 +599,82 @@ var CNSStimulation = class {
|
|
|
612
599
|
let reserved = 0;
|
|
613
600
|
for (let i = 0; i < outputSignalOrSignals.length; i++) {
|
|
614
601
|
const c = outputSignalOrSignals[i]?.collateral;
|
|
615
|
-
const n = c ? this.
|
|
602
|
+
const n = c ? this.getSubscribers(c).length : 0;
|
|
616
603
|
perSignal[i] = n;
|
|
617
604
|
reserved += n;
|
|
618
605
|
}
|
|
619
606
|
this.reservedActivations += reserved;
|
|
620
607
|
}
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
608
|
+
let dispatchIndex = 0;
|
|
609
|
+
try {
|
|
610
|
+
for (; dispatchIndex < outputSignalOrSignals.length; dispatchIndex++) {
|
|
611
|
+
if (perSignal)
|
|
612
|
+
this.reservedActivations -= perSignal[dispatchIndex];
|
|
613
|
+
this.processResponse(
|
|
614
|
+
emitter,
|
|
615
|
+
inputSignal,
|
|
616
|
+
outputSignalOrSignals[dispatchIndex],
|
|
617
|
+
error,
|
|
618
|
+
startedAt,
|
|
619
|
+
duration,
|
|
620
|
+
activationContext
|
|
621
|
+
);
|
|
622
|
+
}
|
|
623
|
+
} finally {
|
|
624
|
+
if (perSignal) {
|
|
625
|
+
for (let j = dispatchIndex + 1; j < perSignal.length; j++) {
|
|
626
|
+
this.reservedActivations -= perSignal[j];
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
629
|
}
|
|
630
630
|
return;
|
|
631
631
|
}
|
|
632
|
-
this.processResponse(
|
|
632
|
+
this.processResponse(
|
|
633
|
+
emitter,
|
|
634
|
+
inputSignal,
|
|
635
|
+
outputSignalOrSignals,
|
|
636
|
+
error,
|
|
637
|
+
startedAt,
|
|
638
|
+
duration,
|
|
639
|
+
activationContext
|
|
640
|
+
);
|
|
633
641
|
}
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
642
|
+
/**
|
|
643
|
+
* The one response an aborted run emits: no neuron, no signals, an empty
|
|
644
|
+
* queue. It exists so `queueLength === 0` — the completion condition every
|
|
645
|
+
* listener uses — is reached on the abort path too. Listener errors are
|
|
646
|
+
* swallowed: the run is already over and rejecting, and there is nothing
|
|
647
|
+
* left to fail.
|
|
648
|
+
*/
|
|
649
|
+
emitAbortedResponse() {
|
|
650
|
+
const onResponse = this.onResponse;
|
|
651
|
+
if (!onResponse) return;
|
|
652
|
+
try {
|
|
653
|
+
void onResponse(
|
|
654
|
+
new CNSStimulationResponseImpl(
|
|
655
|
+
this,
|
|
656
|
+
void 0,
|
|
657
|
+
void 0,
|
|
658
|
+
0,
|
|
659
|
+
0,
|
|
660
|
+
0,
|
|
661
|
+
void 0,
|
|
662
|
+
new Error("Stimulation aborted"),
|
|
663
|
+
void 0,
|
|
664
|
+
void 0,
|
|
665
|
+
void 0
|
|
666
|
+
)
|
|
667
|
+
);
|
|
668
|
+
} catch (e) {
|
|
669
|
+
this.onResponseError = e instanceof Error ? e : new Error(String(e));
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
processResponse(emitter, inputSignal, outputSignal, error, startedAt, duration, activationContext) {
|
|
673
|
+
const collateral = outputSignal?.collateral;
|
|
674
|
+
const subscribers = collateral ? this.getSubscribers(collateral) : EMPTY_SUBSCRIBERS;
|
|
637
675
|
const onResponse = this.onResponse;
|
|
638
676
|
if (!onResponse) {
|
|
639
|
-
if (
|
|
640
|
-
if (emitter) this.cleanupCtxIfNeeded(emitter);
|
|
677
|
+
if (collateral && !error) {
|
|
641
678
|
const n = subscribers.length;
|
|
642
679
|
if (n > 0) {
|
|
643
680
|
const tasks = new Array(n);
|
|
@@ -657,17 +694,17 @@ var CNSStimulation = class {
|
|
|
657
694
|
this.tryResolveCompleted();
|
|
658
695
|
return;
|
|
659
696
|
}
|
|
660
|
-
|
|
661
|
-
if (
|
|
662
|
-
|
|
663
|
-
|
|
697
|
+
let subscriberActivationTasks = EMPTY_TASKS;
|
|
698
|
+
if (collateral && !error && subscribers.length > 0) {
|
|
699
|
+
const pending = this.pendingTasks ?? (this.pendingTasks = []);
|
|
700
|
+
subscriberActivationTasks = new Array(subscribers.length);
|
|
664
701
|
for (let i = 0; i < subscribers.length; i++) {
|
|
665
702
|
const task = this.createSubscriberQueueItem(
|
|
666
703
|
subscribers[i],
|
|
667
704
|
outputSignal
|
|
668
705
|
);
|
|
669
|
-
subscriberActivationTasks
|
|
670
|
-
|
|
706
|
+
subscriberActivationTasks[i] = task;
|
|
707
|
+
this.addPositionedTask(pending, task, TASK_PENDING_POSITION);
|
|
671
708
|
}
|
|
672
709
|
}
|
|
673
710
|
this.scheduledCount += subscriberActivationTasks.length;
|
|
@@ -676,22 +713,25 @@ var CNSStimulation = class {
|
|
|
676
713
|
maybePromise = onResponse(
|
|
677
714
|
new CNSStimulationResponseImpl(
|
|
678
715
|
this,
|
|
679
|
-
this.ctxStore,
|
|
680
716
|
inputSignal,
|
|
681
717
|
outputSignal,
|
|
682
718
|
this.pendingActivations + this.activeOperations,
|
|
683
719
|
this.pendingActivations,
|
|
684
720
|
this.activeOperations,
|
|
685
|
-
this.options?.maxNeuronHops && emitter ? this.
|
|
721
|
+
this.options?.maxNeuronHops && emitter ? this.neuronVisitCounts?.[emitter.index] ?? 0 : void 0,
|
|
686
722
|
error,
|
|
687
|
-
emitter
|
|
723
|
+
emitter,
|
|
724
|
+
startedAt,
|
|
725
|
+
duration,
|
|
726
|
+
subscriberActivationTasks,
|
|
727
|
+
activationContext
|
|
688
728
|
)
|
|
689
729
|
);
|
|
690
730
|
} catch (e) {
|
|
691
731
|
this.onResponseError = e instanceof Error ? e : new Error(String(e));
|
|
692
732
|
for (let i = 0; i < subscriberActivationTasks.length; i++) {
|
|
693
733
|
this.scheduledCount--;
|
|
694
|
-
this.
|
|
734
|
+
this.removePendingTask(subscriberActivationTasks[i]);
|
|
695
735
|
this.qEnqueue(subscriberActivationTasks[i]);
|
|
696
736
|
}
|
|
697
737
|
this.tryResolveCompleted();
|
|
@@ -704,7 +744,7 @@ var CNSStimulation = class {
|
|
|
704
744
|
try {
|
|
705
745
|
for (let i = 0; i < subscriberActivationTasks.length; i++) {
|
|
706
746
|
this.scheduledCount--;
|
|
707
|
-
this.
|
|
747
|
+
this.removePendingTask(
|
|
708
748
|
subscriberActivationTasks[i]
|
|
709
749
|
);
|
|
710
750
|
this.qEnqueue(subscriberActivationTasks[i]);
|
|
@@ -720,7 +760,7 @@ var CNSStimulation = class {
|
|
|
720
760
|
try {
|
|
721
761
|
for (let i = 0; i < subscriberActivationTasks.length; i++) {
|
|
722
762
|
this.scheduledCount--;
|
|
723
|
-
this.
|
|
763
|
+
this.removePendingTask(
|
|
724
764
|
subscriberActivationTasks[i]
|
|
725
765
|
);
|
|
726
766
|
this.qEnqueue(subscriberActivationTasks[i]);
|
|
@@ -735,7 +775,7 @@ var CNSStimulation = class {
|
|
|
735
775
|
}
|
|
736
776
|
for (let i = 0; i < subscriberActivationTasks.length; i++) {
|
|
737
777
|
this.scheduledCount--;
|
|
738
|
-
this.
|
|
778
|
+
this.removePendingTask(subscriberActivationTasks[i]);
|
|
739
779
|
this.qEnqueue(subscriberActivationTasks[i]);
|
|
740
780
|
}
|
|
741
781
|
this.tryResolveCompleted();
|
|
@@ -762,36 +802,36 @@ var CNSStimulation = class {
|
|
|
762
802
|
// src/CNSInstanceNeuronQueue.ts
|
|
763
803
|
var CNSInstanceNeuronQueue = class {
|
|
764
804
|
constructor() {
|
|
765
|
-
this.
|
|
805
|
+
this.gatesByNeuronIndex = [];
|
|
766
806
|
}
|
|
767
|
-
run(
|
|
768
|
-
const limit =
|
|
807
|
+
run(neuron, fn) {
|
|
808
|
+
const limit = neuron?.concurrency;
|
|
769
809
|
if (limit === void 0 || limit <= 0) {
|
|
770
810
|
return fn();
|
|
771
811
|
}
|
|
772
|
-
let gate = this.
|
|
812
|
+
let gate = this.gatesByNeuronIndex[neuron.index];
|
|
773
813
|
if (!gate) {
|
|
774
814
|
gate = { limit, active: 0, waiters: [] };
|
|
775
|
-
this.
|
|
815
|
+
this.gatesByNeuronIndex[neuron.index] = gate;
|
|
776
816
|
} else {
|
|
777
817
|
gate.limit = limit;
|
|
778
818
|
}
|
|
819
|
+
const ownGate = gate;
|
|
779
820
|
const wrap = (cb) => {
|
|
780
821
|
return () => {
|
|
781
822
|
try {
|
|
782
823
|
cb();
|
|
783
824
|
} finally {
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
const next = g.waiters.shift();
|
|
825
|
+
ownGate.active = Math.max(0, ownGate.active - 1);
|
|
826
|
+
if (ownGate.waiters.length > 0) {
|
|
827
|
+
const next = ownGate.waiters.shift();
|
|
788
828
|
next();
|
|
789
829
|
}
|
|
790
830
|
}
|
|
791
831
|
};
|
|
792
832
|
};
|
|
793
|
-
if (
|
|
794
|
-
|
|
833
|
+
if (ownGate.active < ownGate.limit) {
|
|
834
|
+
ownGate.active++;
|
|
795
835
|
const ret = fn();
|
|
796
836
|
if (ret && typeof ret.then === "function") {
|
|
797
837
|
return ret.then((cb) => wrap(cb));
|
|
@@ -800,8 +840,7 @@ var CNSInstanceNeuronQueue = class {
|
|
|
800
840
|
}
|
|
801
841
|
return new Promise((resolve) => {
|
|
802
842
|
const starter = () => {
|
|
803
|
-
|
|
804
|
-
g.active++;
|
|
843
|
+
ownGate.active++;
|
|
805
844
|
const ret = fn();
|
|
806
845
|
if (ret && typeof ret.then === "function") {
|
|
807
846
|
ret.then((cb) => resolve(wrap(cb)));
|
|
@@ -809,12 +848,52 @@ var CNSInstanceNeuronQueue = class {
|
|
|
809
848
|
resolve(wrap(ret));
|
|
810
849
|
}
|
|
811
850
|
};
|
|
812
|
-
|
|
851
|
+
ownGate.waiters.push(starter);
|
|
813
852
|
});
|
|
814
853
|
}
|
|
815
854
|
};
|
|
816
855
|
|
|
856
|
+
// src/utils/cnsResolveModalityHandler.ts
|
|
857
|
+
function cnsResolveModalityHandler(modalitySelection, modality, afferentPath) {
|
|
858
|
+
if (modality !== void 0) {
|
|
859
|
+
const modalityConfigs = modalitySelection.modalityConfigs;
|
|
860
|
+
for (let i = 0; i < modalityConfigs.length; i++) {
|
|
861
|
+
const modalityConfig = modalityConfigs[i];
|
|
862
|
+
if (modalityConfig.modality !== modality) continue;
|
|
863
|
+
for (let path = afferentPath; path !== void 0; path = path.parentAfferentPath) {
|
|
864
|
+
const handler = modalityConfig.afferentPaths?.get(path);
|
|
865
|
+
if (handler !== void 0) return handler;
|
|
866
|
+
}
|
|
867
|
+
if (modalityConfig.default !== void 0) return modalityConfig.default;
|
|
868
|
+
break;
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
return modalitySelection.defaultHandler;
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
// src/utils/cnsComposeModalityResponse.ts
|
|
875
|
+
function cnsComposeModalityResponse(handler, output) {
|
|
876
|
+
return (payload, axon, ctx) => {
|
|
877
|
+
const maybeResult = handler(payload, axon, ctx);
|
|
878
|
+
if (maybeResult && typeof maybeResult.then === "function") {
|
|
879
|
+
return maybeResult.then((result) => output(result, axon, ctx));
|
|
880
|
+
}
|
|
881
|
+
try {
|
|
882
|
+
return output(maybeResult, axon, ctx);
|
|
883
|
+
} catch (error) {
|
|
884
|
+
if (error instanceof Error && error.message.includes("Cannot read properties of undefined")) {
|
|
885
|
+
const axonKeys = axon && typeof axon === "object" ? Object.keys(axon) : "not an object";
|
|
886
|
+
throw new Error(
|
|
887
|
+
`modalityDendrite: axon.output is undefined. Axon type: ${typeof axon}, Axon keys: ${Array.isArray(axonKeys) ? axonKeys.join(", ") : axonKeys}. Make sure the neuron was created with the correct axon structure. Original error: ${error.message}`
|
|
888
|
+
);
|
|
889
|
+
}
|
|
890
|
+
throw error;
|
|
891
|
+
}
|
|
892
|
+
};
|
|
893
|
+
}
|
|
894
|
+
|
|
817
895
|
// src/CNSNetwork.ts
|
|
896
|
+
var NO_SUBSCRIBERS = [];
|
|
818
897
|
var CNSNetwork = class {
|
|
819
898
|
constructor(neurons) {
|
|
820
899
|
this.neurons = neurons;
|
|
@@ -822,8 +901,9 @@ var CNSNetwork = class {
|
|
|
822
901
|
* Strongly Connected Components of the neuron graph.
|
|
823
902
|
* Each component is a set of neuron objects that can reach each other.
|
|
824
903
|
*
|
|
825
|
-
* Computed lazily on first access: the SCC machinery
|
|
826
|
-
*
|
|
904
|
+
* Computed lazily on first access: the SCC machinery only drives graph
|
|
905
|
+
* introspection (`getSCCSetByNeuron`, `canNeuronBeGuaranteedDone`), so a
|
|
906
|
+
* network that never inspects reachability never pays for it.
|
|
827
907
|
*/
|
|
828
908
|
this._stronglyConnectedComponents = [];
|
|
829
909
|
this.sccBuilt = false;
|
|
@@ -843,16 +923,19 @@ var CNSNetwork = class {
|
|
|
843
923
|
* Fast lookup: collateral -> list of (neuron, dendrite) subscribers.
|
|
844
924
|
* Built once at construction time.
|
|
845
925
|
*/
|
|
846
|
-
this.
|
|
926
|
+
this.subscribersByCollateralIndex = [];
|
|
927
|
+
this.allSubscribersByCollateralIndex = [];
|
|
928
|
+
this.modalityCollateralIndices = [];
|
|
929
|
+
this.subscriberTablesByModalityIndex = [];
|
|
847
930
|
this.parentNeuronByCollateral = /* @__PURE__ */ new Map();
|
|
848
931
|
this.buildIndexes();
|
|
849
932
|
}
|
|
850
933
|
get stronglyConnectedComponents() {
|
|
851
|
-
this.
|
|
934
|
+
this.buildSccIndex();
|
|
852
935
|
return this._stronglyConnectedComponents;
|
|
853
936
|
}
|
|
854
937
|
/** Build the SCC analysis the first time anything needs it. */
|
|
855
|
-
|
|
938
|
+
buildSccIndex() {
|
|
856
939
|
if (this.sccBuilt) return;
|
|
857
940
|
this.sccBuilt = true;
|
|
858
941
|
this.buildSCC();
|
|
@@ -862,23 +945,21 @@ var CNSNetwork = class {
|
|
|
862
945
|
*/
|
|
863
946
|
buildNeuronGraph() {
|
|
864
947
|
const graph = /* @__PURE__ */ new Map();
|
|
865
|
-
for (const
|
|
866
|
-
graph.set(
|
|
948
|
+
for (const neuron of this.neurons) {
|
|
949
|
+
graph.set(neuron, /* @__PURE__ */ new Set());
|
|
867
950
|
}
|
|
868
|
-
for (const
|
|
951
|
+
for (const neuron of this.neurons) {
|
|
869
952
|
const reachableNeurons = /* @__PURE__ */ new Set();
|
|
870
|
-
for (const axonKey in
|
|
871
|
-
const
|
|
872
|
-
const subscribers = this.
|
|
873
|
-
collateral2
|
|
874
|
-
);
|
|
953
|
+
for (const axonKey in neuron.axon) {
|
|
954
|
+
const collateral = neuron.axon[axonKey];
|
|
955
|
+
const subscribers = this.allSubscribersByCollateralIndex[collateral.index];
|
|
875
956
|
if (subscribers) {
|
|
876
957
|
for (const { neuron: targetNeuron } of subscribers) {
|
|
877
958
|
reachableNeurons.add(targetNeuron);
|
|
878
959
|
}
|
|
879
960
|
}
|
|
880
961
|
}
|
|
881
|
-
graph.set(
|
|
962
|
+
graph.set(neuron, reachableNeurons);
|
|
882
963
|
}
|
|
883
964
|
return graph;
|
|
884
965
|
}
|
|
@@ -893,41 +974,41 @@ var CNSNetwork = class {
|
|
|
893
974
|
const stack = [];
|
|
894
975
|
const components = [];
|
|
895
976
|
let currentIndex = 0;
|
|
896
|
-
const strongConnect = (
|
|
897
|
-
index.set(
|
|
898
|
-
lowlink.set(
|
|
977
|
+
const strongConnect = (neuron) => {
|
|
978
|
+
index.set(neuron, currentIndex);
|
|
979
|
+
lowlink.set(neuron, currentIndex);
|
|
899
980
|
currentIndex++;
|
|
900
|
-
stack.push(
|
|
901
|
-
onStack.add(
|
|
902
|
-
const neighbors = graph.get(
|
|
981
|
+
stack.push(neuron);
|
|
982
|
+
onStack.add(neuron);
|
|
983
|
+
const neighbors = graph.get(neuron) || /* @__PURE__ */ new Set();
|
|
903
984
|
for (const neighbor of Array.from(neighbors)) {
|
|
904
985
|
if (!index.has(neighbor)) {
|
|
905
986
|
strongConnect(neighbor);
|
|
906
987
|
lowlink.set(
|
|
907
|
-
|
|
908
|
-
Math.min(lowlink.get(
|
|
988
|
+
neuron,
|
|
989
|
+
Math.min(lowlink.get(neuron), lowlink.get(neighbor))
|
|
909
990
|
);
|
|
910
991
|
} else if (onStack.has(neighbor)) {
|
|
911
992
|
lowlink.set(
|
|
912
|
-
|
|
913
|
-
Math.min(lowlink.get(
|
|
993
|
+
neuron,
|
|
994
|
+
Math.min(lowlink.get(neuron), index.get(neighbor))
|
|
914
995
|
);
|
|
915
996
|
}
|
|
916
997
|
}
|
|
917
|
-
if (lowlink.get(
|
|
998
|
+
if (lowlink.get(neuron) === index.get(neuron)) {
|
|
918
999
|
const component = /* @__PURE__ */ new Set();
|
|
919
1000
|
let w;
|
|
920
1001
|
do {
|
|
921
1002
|
w = stack.pop();
|
|
922
1003
|
onStack.delete(w);
|
|
923
1004
|
component.add(w);
|
|
924
|
-
} while (w !==
|
|
1005
|
+
} while (w !== neuron);
|
|
925
1006
|
components.push(component);
|
|
926
1007
|
}
|
|
927
1008
|
};
|
|
928
|
-
for (const
|
|
929
|
-
if (!index.has(
|
|
930
|
-
strongConnect(
|
|
1009
|
+
for (const neuron of this.neurons) {
|
|
1010
|
+
if (!index.has(neuron)) {
|
|
1011
|
+
strongConnect(neuron);
|
|
931
1012
|
}
|
|
932
1013
|
}
|
|
933
1014
|
this._stronglyConnectedComponents = components;
|
|
@@ -949,8 +1030,8 @@ var CNSNetwork = class {
|
|
|
949
1030
|
}
|
|
950
1031
|
for (let i = 0; i < this._stronglyConnectedComponents.length; i++) {
|
|
951
1032
|
const scc = this._stronglyConnectedComponents[i];
|
|
952
|
-
for (const
|
|
953
|
-
const neighbors = neuronGraph.get(
|
|
1033
|
+
for (const neuron of Array.from(scc)) {
|
|
1034
|
+
const neighbors = neuronGraph.get(neuron) || /* @__PURE__ */ new Set();
|
|
954
1035
|
for (const neighbor of Array.from(neighbors)) {
|
|
955
1036
|
const neighborSccIndex = this.neuronToSCC.get(neighbor);
|
|
956
1037
|
if (neighborSccIndex !== void 0 && neighborSccIndex !== i) {
|
|
@@ -1003,26 +1084,26 @@ var CNSNetwork = class {
|
|
|
1003
1084
|
* Returns true if the neuron is in a strongly connected component with more than one member,
|
|
1004
1085
|
* or if it's in a single-member SCC that can reach itself.
|
|
1005
1086
|
*/
|
|
1006
|
-
getSCCSetByNeuron(
|
|
1007
|
-
this.
|
|
1008
|
-
const sccIndex = this.neuronToSCC.get(
|
|
1087
|
+
getSCCSetByNeuron(neuron) {
|
|
1088
|
+
this.buildSccIndex();
|
|
1089
|
+
const sccIndex = this.neuronToSCC.get(neuron);
|
|
1009
1090
|
if (sccIndex === void 0) return;
|
|
1010
1091
|
return this._stronglyConnectedComponents[sccIndex];
|
|
1011
1092
|
}
|
|
1012
1093
|
/**
|
|
1013
1094
|
* Get the SCC index for a given neuron
|
|
1014
1095
|
*/
|
|
1015
|
-
getSccIndexByNeuron(
|
|
1016
|
-
this.
|
|
1017
|
-
return this.neuronToSCC.get(
|
|
1096
|
+
getSccIndexByNeuron(neuron) {
|
|
1097
|
+
this.buildSccIndex();
|
|
1098
|
+
return this.neuronToSCC.get(neuron);
|
|
1018
1099
|
}
|
|
1019
1100
|
/**
|
|
1020
1101
|
* Check if a neuron can be guaranteed not to be visited again during the current propagation.
|
|
1021
1102
|
* This is the core logic for safe context cleanup.
|
|
1022
1103
|
*/
|
|
1023
|
-
canNeuronBeGuaranteedDone(
|
|
1024
|
-
this.
|
|
1025
|
-
const sccIndex = this.neuronToSCC.get(
|
|
1104
|
+
canNeuronBeGuaranteedDone(neuron, activeSccCounts) {
|
|
1105
|
+
this.buildSccIndex();
|
|
1106
|
+
const sccIndex = this.neuronToSCC.get(neuron);
|
|
1026
1107
|
if (sccIndex === void 0) return true;
|
|
1027
1108
|
if (activeSccCounts.get(sccIndex) && activeSccCounts.get(sccIndex) > 0) {
|
|
1028
1109
|
return false;
|
|
@@ -1036,36 +1117,93 @@ var CNSNetwork = class {
|
|
|
1036
1117
|
}
|
|
1037
1118
|
return true;
|
|
1038
1119
|
}
|
|
1120
|
+
describeNeuron(neuron) {
|
|
1121
|
+
const axonKeys = Object.keys(neuron.axon);
|
|
1122
|
+
return `neuron #${neuron.index} (${axonKeys.length === 0 ? "no axon" : `axon: ${axonKeys.join(", ")}`})`;
|
|
1123
|
+
}
|
|
1124
|
+
describeCollateral(neuron, collateral) {
|
|
1125
|
+
const axonKey = Object.keys(neuron.axon).find((key) => neuron.axon[key] === collateral);
|
|
1126
|
+
return `"${axonKey}" #${collateral.index}`;
|
|
1127
|
+
}
|
|
1039
1128
|
buildIndexes() {
|
|
1040
|
-
|
|
1129
|
+
var _a;
|
|
1130
|
+
this.allSubscribersByCollateralIndex = [];
|
|
1131
|
+
this.modalityCollateralIndices = [];
|
|
1132
|
+
this.subscriberTablesByModalityIndex = [];
|
|
1041
1133
|
this.parentNeuronByCollateral.clear();
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
arr.push({ neuron: neuron2, dendrite });
|
|
1047
|
-
this.subIndex.set(
|
|
1048
|
-
key,
|
|
1049
|
-
arr
|
|
1050
|
-
);
|
|
1134
|
+
const listedNeurons = /* @__PURE__ */ new Set();
|
|
1135
|
+
for (const neuron of this.neurons) {
|
|
1136
|
+
if (listedNeurons.has(neuron)) {
|
|
1137
|
+
throw new Error(`${this.describeNeuron(neuron)} is listed twice; every dendrite of it would run twice`);
|
|
1051
1138
|
}
|
|
1052
|
-
|
|
1139
|
+
listedNeurons.add(neuron);
|
|
1140
|
+
for (const dendrite of neuron.dendrites) {
|
|
1141
|
+
const index = dendrite.collateral.index;
|
|
1142
|
+
const subscribers = (_a = this.allSubscribersByCollateralIndex)[index] ?? (_a[index] = []);
|
|
1143
|
+
subscribers.push({ neuron, dendrite });
|
|
1144
|
+
if (dendrite.modalitySelection !== void 0 && this.modalityCollateralIndices.indexOf(index) < 0) {
|
|
1145
|
+
this.modalityCollateralIndices.push(index);
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
Object.values(neuron.axon).forEach((collateral) => {
|
|
1149
|
+
const owner = this.parentNeuronByCollateral.get(collateral);
|
|
1150
|
+
if (owner !== void 0 && owner !== neuron) {
|
|
1151
|
+
throw new Error(
|
|
1152
|
+
`collateral ${this.describeCollateral(neuron, collateral)} is in the axons of ${this.describeNeuron(owner)} and ${this.describeNeuron(neuron)}; a collateral is owned by one neuron`
|
|
1153
|
+
);
|
|
1154
|
+
}
|
|
1053
1155
|
this.parentNeuronByCollateral.set(
|
|
1054
|
-
|
|
1055
|
-
|
|
1156
|
+
collateral,
|
|
1157
|
+
neuron
|
|
1056
1158
|
);
|
|
1057
1159
|
});
|
|
1058
1160
|
}
|
|
1161
|
+
this.subscribersByCollateralIndex = this.buildSubscriberTable(void 0, void 0);
|
|
1162
|
+
}
|
|
1163
|
+
getSubscriberTable(modality, afferentPath) {
|
|
1164
|
+
var _a, _b;
|
|
1165
|
+
if (modality === void 0) return this.subscribersByCollateralIndex;
|
|
1166
|
+
const subscriberTablesByAfferentPathIndex = (_a = this.subscriberTablesByModalityIndex)[_b = modality.index] ?? (_a[_b] = []);
|
|
1167
|
+
const afferentPathSlot = afferentPath === void 0 ? 0 : afferentPath.index + 1;
|
|
1168
|
+
return subscriberTablesByAfferentPathIndex[afferentPathSlot] ?? (subscriberTablesByAfferentPathIndex[afferentPathSlot] = this.buildSubscriberTable(modality, afferentPath));
|
|
1169
|
+
}
|
|
1170
|
+
buildSubscriberTable(modality, afferentPath) {
|
|
1171
|
+
const subscriberTable = this.allSubscribersByCollateralIndex.slice();
|
|
1172
|
+
const modalityCollateralIndices = this.modalityCollateralIndices;
|
|
1173
|
+
for (let i = 0; i < modalityCollateralIndices.length; i++) {
|
|
1174
|
+
const index = modalityCollateralIndices[i];
|
|
1175
|
+
const resolvedSubscribers = [];
|
|
1176
|
+
const subscribers = this.allSubscribersByCollateralIndex[index];
|
|
1177
|
+
for (let j = 0; j < subscribers.length; j++) {
|
|
1178
|
+
const subscriber = subscribers[j];
|
|
1179
|
+
const modalitySelection = subscriber.dendrite.modalitySelection;
|
|
1180
|
+
if (modalitySelection === void 0) {
|
|
1181
|
+
resolvedSubscribers.push(subscriber);
|
|
1182
|
+
continue;
|
|
1183
|
+
}
|
|
1184
|
+
const handler = cnsResolveModalityHandler(modalitySelection, modality, afferentPath);
|
|
1185
|
+
if (handler === void 0) continue;
|
|
1186
|
+
resolvedSubscribers.push({
|
|
1187
|
+
neuron: subscriber.neuron,
|
|
1188
|
+
dendrite: {
|
|
1189
|
+
collateral: subscriber.dendrite.collateral,
|
|
1190
|
+
response: cnsComposeModalityResponse(handler, modalitySelection.output)
|
|
1191
|
+
}
|
|
1192
|
+
});
|
|
1193
|
+
}
|
|
1194
|
+
subscriberTable[index] = resolvedSubscribers;
|
|
1195
|
+
}
|
|
1196
|
+
return subscriberTable;
|
|
1059
1197
|
}
|
|
1060
|
-
getParentNeuronByCollateral(
|
|
1061
|
-
return this.parentNeuronByCollateral.get(
|
|
1198
|
+
getParentNeuronByCollateral(collateral) {
|
|
1199
|
+
return this.parentNeuronByCollateral.get(collateral);
|
|
1062
1200
|
}
|
|
1063
1201
|
// Computed on demand (rarely used): one dendrite per collateral, last wins —
|
|
1064
1202
|
// preserving the previous index semantics without building it every construct.
|
|
1065
1203
|
getDendrites() {
|
|
1066
1204
|
const byCollateral = /* @__PURE__ */ new Map();
|
|
1067
|
-
for (const
|
|
1068
|
-
for (const dendrite of
|
|
1205
|
+
for (const neuron of this.neurons) {
|
|
1206
|
+
for (const dendrite of neuron.dendrites) {
|
|
1069
1207
|
byCollateral.set(
|
|
1070
1208
|
dendrite.collateral,
|
|
1071
1209
|
dendrite
|
|
@@ -1076,15 +1214,15 @@ var CNSNetwork = class {
|
|
|
1076
1214
|
}
|
|
1077
1215
|
getCollaterals() {
|
|
1078
1216
|
const collaterals = /* @__PURE__ */ new Set();
|
|
1079
|
-
for (const
|
|
1080
|
-
for (const
|
|
1081
|
-
collaterals.add(
|
|
1217
|
+
for (const neuron of this.neurons) {
|
|
1218
|
+
for (const collateral of Object.values(neuron.axon)) {
|
|
1219
|
+
collaterals.add(collateral);
|
|
1082
1220
|
}
|
|
1083
1221
|
}
|
|
1084
1222
|
return Array.from(collaterals);
|
|
1085
1223
|
}
|
|
1086
|
-
getSubscribers(
|
|
1087
|
-
return this.
|
|
1224
|
+
getSubscribers(collateral) {
|
|
1225
|
+
return this.subscribersByCollateralIndex[collateral.index] ?? NO_SUBSCRIBERS;
|
|
1088
1226
|
}
|
|
1089
1227
|
};
|
|
1090
1228
|
|
|
@@ -1106,36 +1244,47 @@ var CNS = class {
|
|
|
1106
1244
|
* Global batch-boundary listeners applied to every stimulation.
|
|
1107
1245
|
*/
|
|
1108
1246
|
this.globalDrainListeners = [];
|
|
1247
|
+
this.batchDepth = 0;
|
|
1248
|
+
this.batchedDrains = [];
|
|
1109
1249
|
this.network = new CNSNetwork(this.neurons);
|
|
1110
1250
|
this.global = global;
|
|
1111
1251
|
}
|
|
1112
|
-
|
|
1113
|
-
|
|
1252
|
+
/** Push a listener onto a list; return an idempotent unsubscribe. */
|
|
1253
|
+
addListener(list, listener) {
|
|
1254
|
+
list.push(listener);
|
|
1114
1255
|
let active = true;
|
|
1115
1256
|
return () => {
|
|
1116
1257
|
if (!active) return;
|
|
1117
1258
|
active = false;
|
|
1118
|
-
const idx =
|
|
1119
|
-
if (idx >= 0)
|
|
1259
|
+
const idx = list.indexOf(listener);
|
|
1260
|
+
if (idx >= 0) list.splice(idx, 1);
|
|
1120
1261
|
};
|
|
1121
1262
|
}
|
|
1263
|
+
addResponseListener(listener) {
|
|
1264
|
+
return this.addListener(this.globalResponseListeners, listener);
|
|
1265
|
+
}
|
|
1122
1266
|
/**
|
|
1123
1267
|
* Register a listener called once at the end of every synchronous turn of
|
|
1124
|
-
* every stimulation - see {@link TCNSStimulationDrain}. This is how an
|
|
1268
|
+
* every stimulation (inside `batch`, when the batch ends) - see {@link TCNSStimulationDrain}. This is how an
|
|
1125
1269
|
* integration (a state manager, an in-memory database) installs a single
|
|
1126
1270
|
* commit point for the whole organism instead of passing `onDrain` per call.
|
|
1127
1271
|
*
|
|
1128
1272
|
* Returns an unsubscribe function.
|
|
1129
1273
|
*/
|
|
1130
1274
|
addDrainListener(listener) {
|
|
1131
|
-
this.globalDrainListeners
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1275
|
+
return this.addListener(this.globalDrainListeners, listener);
|
|
1276
|
+
}
|
|
1277
|
+
batch(run) {
|
|
1278
|
+
this.batchDepth++;
|
|
1279
|
+
try {
|
|
1280
|
+
return run();
|
|
1281
|
+
} finally {
|
|
1282
|
+
if (--this.batchDepth === 0) {
|
|
1283
|
+
const batchedDrains = this.batchedDrains;
|
|
1284
|
+
this.batchedDrains = [];
|
|
1285
|
+
for (const [announce, drain] of batchedDrains) announce(drain);
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1139
1288
|
}
|
|
1140
1289
|
/**
|
|
1141
1290
|
* Fan a drain out to the local and global listeners, or return undefined when
|
|
@@ -1146,7 +1295,7 @@ var CNS = class {
|
|
|
1146
1295
|
*/
|
|
1147
1296
|
wrapOnDrain(local) {
|
|
1148
1297
|
if (this.globalDrainListeners.length === 0 && !local) return void 0;
|
|
1149
|
-
|
|
1298
|
+
const announce = (d) => {
|
|
1150
1299
|
if (local) {
|
|
1151
1300
|
try {
|
|
1152
1301
|
local(d);
|
|
@@ -1162,39 +1311,38 @@ var CNS = class {
|
|
|
1162
1311
|
}
|
|
1163
1312
|
}
|
|
1164
1313
|
};
|
|
1314
|
+
return (d) => {
|
|
1315
|
+
if (this.batchDepth > 0) this.batchedDrains.push([announce, d]);
|
|
1316
|
+
else announce(d);
|
|
1317
|
+
};
|
|
1165
1318
|
}
|
|
1166
1319
|
wrapOnResponse(local) {
|
|
1167
1320
|
if (this.globalResponseListeners.length === 0 && !local) {
|
|
1168
1321
|
return void 0;
|
|
1169
1322
|
}
|
|
1170
1323
|
return (r) => {
|
|
1171
|
-
let
|
|
1172
|
-
const promises = [];
|
|
1324
|
+
let promises;
|
|
1173
1325
|
if (local) {
|
|
1174
1326
|
try {
|
|
1175
1327
|
const res = local(r);
|
|
1176
1328
|
if (res && typeof res.then === "function") {
|
|
1177
|
-
|
|
1178
|
-
promises.push(res);
|
|
1329
|
+
(promises ?? (promises = [])).push(res);
|
|
1179
1330
|
}
|
|
1180
1331
|
} catch (error) {
|
|
1181
|
-
|
|
1182
|
-
promises.push(Promise.reject(error));
|
|
1332
|
+
(promises ?? (promises = [])).push(Promise.reject(error));
|
|
1183
1333
|
}
|
|
1184
1334
|
}
|
|
1185
1335
|
for (let i = 0; i < this.globalResponseListeners.length; i++) {
|
|
1186
1336
|
try {
|
|
1187
1337
|
const res = this.globalResponseListeners[i](r);
|
|
1188
1338
|
if (res && typeof res.then === "function") {
|
|
1189
|
-
|
|
1190
|
-
promises.push(res);
|
|
1339
|
+
(promises ?? (promises = [])).push(res);
|
|
1191
1340
|
}
|
|
1192
1341
|
} catch (error) {
|
|
1193
|
-
|
|
1194
|
-
promises.push(Promise.reject(error));
|
|
1342
|
+
(promises ?? (promises = [])).push(Promise.reject(error));
|
|
1195
1343
|
}
|
|
1196
1344
|
}
|
|
1197
|
-
if (
|
|
1345
|
+
if (promises) {
|
|
1198
1346
|
return Promise.allSettled(promises).then((results) => {
|
|
1199
1347
|
const rejected = results.find(
|
|
1200
1348
|
(r2) => r2.status === "rejected"
|
|
@@ -1204,15 +1352,18 @@ var CNS = class {
|
|
|
1204
1352
|
}
|
|
1205
1353
|
};
|
|
1206
1354
|
}
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1355
|
+
/** Build a stimulation (wired listeners, unseeded). Shared by stimulate/activate. */
|
|
1356
|
+
createStimulation(options) {
|
|
1357
|
+
return new CNSStimulation(
|
|
1210
1358
|
this,
|
|
1211
1359
|
this.instanceNeuronQueue,
|
|
1212
1360
|
options,
|
|
1213
|
-
|
|
1361
|
+
this.wrapOnResponse(options?.onResponse),
|
|
1214
1362
|
this.wrapOnDrain(options?.onDrain)
|
|
1215
1363
|
);
|
|
1364
|
+
}
|
|
1365
|
+
stimulate(signalOrSignals, options) {
|
|
1366
|
+
const stimulation = this.createStimulation(options);
|
|
1216
1367
|
stimulation.responseToSignal(signalOrSignals);
|
|
1217
1368
|
return stimulation;
|
|
1218
1369
|
}
|
|
@@ -1220,21 +1371,26 @@ var CNS = class {
|
|
|
1220
1371
|
* Start a stimulation with activation tasks directly
|
|
1221
1372
|
*/
|
|
1222
1373
|
activate(tasks, options) {
|
|
1223
|
-
const
|
|
1224
|
-
const stimulation = new CNSStimulation(
|
|
1225
|
-
this,
|
|
1226
|
-
this.instanceNeuronQueue,
|
|
1227
|
-
options,
|
|
1228
|
-
wrapped,
|
|
1229
|
-
this.wrapOnDrain(options?.onDrain)
|
|
1230
|
-
);
|
|
1374
|
+
const stimulation = this.createStimulation(options);
|
|
1231
1375
|
stimulation.enqueueTasks(tasks);
|
|
1232
1376
|
return stimulation;
|
|
1233
1377
|
}
|
|
1234
1378
|
};
|
|
1235
1379
|
|
|
1380
|
+
// src/createCNS.ts
|
|
1381
|
+
var createCNS = (neuronFactories, dependencies, options) => {
|
|
1382
|
+
if (new Set(neuronFactories).size !== neuronFactories.length) {
|
|
1383
|
+
throw new Error("createCNS: a neuron factory is listed twice; every dendrite of its neuron would run twice");
|
|
1384
|
+
}
|
|
1385
|
+
return new CNS(neuronFactories.map((createNeuron) => createNeuron(dependencies)), options);
|
|
1386
|
+
};
|
|
1387
|
+
|
|
1236
1388
|
// src/CNSCollateral.ts
|
|
1389
|
+
var nextCollateralIndex = 0;
|
|
1237
1390
|
var CNSCollateral = class {
|
|
1391
|
+
constructor() {
|
|
1392
|
+
this.index = nextCollateralIndex++;
|
|
1393
|
+
}
|
|
1238
1394
|
createSignal(payload) {
|
|
1239
1395
|
return { collateral: this, payload };
|
|
1240
1396
|
}
|
|
@@ -1290,8 +1446,11 @@ var CNSStimulationGate = class {
|
|
|
1290
1446
|
};
|
|
1291
1447
|
|
|
1292
1448
|
// src/factory/index.ts
|
|
1293
|
-
var
|
|
1294
|
-
var
|
|
1449
|
+
var nextNeuronIndex = 0;
|
|
1450
|
+
var nextAfferentPathIndex = 0;
|
|
1451
|
+
var nextModalityIndex = 0;
|
|
1452
|
+
var cnsCollateral = () => new CNSCollateral();
|
|
1453
|
+
var cnsNeuron = (axon) => {
|
|
1295
1454
|
const dendrites = [];
|
|
1296
1455
|
const api = {
|
|
1297
1456
|
setConcurrency: (n) => {
|
|
@@ -1315,15 +1474,16 @@ var neuron = (axon) => {
|
|
|
1315
1474
|
}
|
|
1316
1475
|
return api;
|
|
1317
1476
|
}),
|
|
1477
|
+
index: nextNeuronIndex++,
|
|
1318
1478
|
axon,
|
|
1319
1479
|
concurrency: void 0,
|
|
1320
1480
|
maxDuration: void 0,
|
|
1321
1481
|
dendrites,
|
|
1322
1482
|
dendrite(s) {
|
|
1323
1483
|
if ("collateral" in s && "response" in s && Array.isArray(s.collateral)) {
|
|
1324
|
-
for (const
|
|
1484
|
+
for (const collateral of s.collateral) {
|
|
1325
1485
|
dendrites.push({
|
|
1326
|
-
collateral
|
|
1486
|
+
collateral,
|
|
1327
1487
|
response: s.response
|
|
1328
1488
|
});
|
|
1329
1489
|
}
|
|
@@ -1336,7 +1496,7 @@ var neuron = (axon) => {
|
|
|
1336
1496
|
},
|
|
1337
1497
|
modalityDendrite(s) {
|
|
1338
1498
|
const {
|
|
1339
|
-
collateral
|
|
1499
|
+
collateral,
|
|
1340
1500
|
modality: singleModality,
|
|
1341
1501
|
modalities: multipleModalities,
|
|
1342
1502
|
afferentPaths: singleAfferentPaths,
|
|
@@ -1353,89 +1513,56 @@ var neuron = (axon) => {
|
|
|
1353
1513
|
const response = (payload, axon2, ctx) => {
|
|
1354
1514
|
const stimulation = ctx.stimulation;
|
|
1355
1515
|
const stimOptions = stimulation?.options;
|
|
1356
|
-
const
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
);
|
|
1361
|
-
}
|
|
1362
|
-
const res = defaultHandler(payload, axon2, ctx);
|
|
1363
|
-
if (res && typeof res.then === "function") {
|
|
1364
|
-
return res.then(
|
|
1365
|
-
(result) => output(result, axon2, ctx)
|
|
1366
|
-
);
|
|
1367
|
-
}
|
|
1368
|
-
return output(res, axon2, ctx);
|
|
1369
|
-
};
|
|
1370
|
-
if (!stimOptions || !stimOptions.modality) {
|
|
1371
|
-
return runGlobalDefault();
|
|
1372
|
-
}
|
|
1373
|
-
const matchingConfig = modalityConfigs.find(
|
|
1374
|
-
(config) => config.modality === stimOptions.modality
|
|
1516
|
+
const handler = cnsResolveModalityHandler(
|
|
1517
|
+
modalitySelection,
|
|
1518
|
+
stimOptions?.modality,
|
|
1519
|
+
stimOptions?.afferentPath
|
|
1375
1520
|
);
|
|
1376
|
-
if (
|
|
1377
|
-
return runGlobalDefault();
|
|
1378
|
-
}
|
|
1379
|
-
const stimAfferentPath = stimOptions.afferentPath;
|
|
1380
|
-
const handler = stimAfferentPath && matchingConfig.afferentPaths ? matchingConfig.afferentPaths.get(stimAfferentPath) : void 0;
|
|
1381
|
-
const effectiveHandler = handler ?? matchingConfig.default ?? defaultHandler;
|
|
1382
|
-
if (!effectiveHandler) {
|
|
1521
|
+
if (handler === void 0) {
|
|
1383
1522
|
throw new Error(
|
|
1384
1523
|
`modalityDendrite: No handler found for afferent path in modality and no default handler provided`
|
|
1385
1524
|
);
|
|
1386
1525
|
}
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
try {
|
|
1394
|
-
return output(maybeResult, axon2, ctx);
|
|
1395
|
-
} catch (error) {
|
|
1396
|
-
if (error instanceof Error && error.message.includes(
|
|
1397
|
-
"Cannot read properties of undefined"
|
|
1398
|
-
)) {
|
|
1399
|
-
const axonKeys = axon2 && typeof axon2 === "object" ? Object.keys(axon2) : "not an object";
|
|
1400
|
-
throw new Error(
|
|
1401
|
-
`modalityDendrite: axon.output is undefined. Axon type: ${typeof axon2}, Axon keys: ${Array.isArray(axonKeys) ? axonKeys.join(", ") : axonKeys}. Make sure the neuron was created with the correct axon structure. Original error: ${error.message}`
|
|
1402
|
-
);
|
|
1403
|
-
}
|
|
1404
|
-
throw error;
|
|
1405
|
-
}
|
|
1526
|
+
return cnsComposeModalityResponse(handler, output)(payload, axon2, ctx);
|
|
1527
|
+
};
|
|
1528
|
+
const modalitySelection = {
|
|
1529
|
+
modalityConfigs,
|
|
1530
|
+
defaultHandler,
|
|
1531
|
+
output
|
|
1406
1532
|
};
|
|
1407
1533
|
dendrites.push({
|
|
1408
|
-
collateral
|
|
1409
|
-
response
|
|
1534
|
+
collateral,
|
|
1535
|
+
response,
|
|
1536
|
+
modalitySelection
|
|
1410
1537
|
});
|
|
1411
1538
|
return api;
|
|
1412
1539
|
}
|
|
1413
1540
|
};
|
|
1414
1541
|
return api;
|
|
1415
1542
|
};
|
|
1416
|
-
var
|
|
1543
|
+
var cnsNeuronFactory = () => {
|
|
1417
1544
|
const factory = {
|
|
1418
|
-
withCtx: () => factory,
|
|
1419
1545
|
withGlobal: () => factory,
|
|
1420
|
-
neuron: (axon) =>
|
|
1546
|
+
neuron: (axon) => cnsNeuron(axon)
|
|
1421
1547
|
};
|
|
1422
1548
|
return factory;
|
|
1423
1549
|
};
|
|
1424
|
-
var
|
|
1425
|
-
var
|
|
1426
|
-
var afferentPath = (parentAfferentPath) => {
|
|
1550
|
+
var withCNSGlobal = () => cnsNeuronFactory();
|
|
1551
|
+
var cnsAfferentPath = (parentAfferentPath) => {
|
|
1427
1552
|
return {
|
|
1553
|
+
index: nextAfferentPathIndex++,
|
|
1428
1554
|
...parentAfferentPath !== void 0 && {
|
|
1429
1555
|
parentAfferentPath
|
|
1430
1556
|
}
|
|
1431
1557
|
};
|
|
1432
1558
|
};
|
|
1433
|
-
var
|
|
1559
|
+
var cnsModality = (afferentPaths) => {
|
|
1434
1560
|
return {
|
|
1561
|
+
index: nextModalityIndex++,
|
|
1435
1562
|
afferentPaths
|
|
1436
1563
|
};
|
|
1437
1564
|
};
|
|
1438
1565
|
|
|
1439
|
-
export { CNS, CNSCollateral,
|
|
1566
|
+
export { CNS, CNSCollateral, CNSStimulationGate, cnsAfferentPath, cnsCollateral, cnsModality, cnsNeuron, cnsNeuronFactory, createCNS, withCNSGlobal };
|
|
1440
1567
|
//# sourceMappingURL=index.js.map
|
|
1441
1568
|
//# sourceMappingURL=index.js.map
|