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