@cnstra/core 2.1.0 → 2.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/dist/index.js CHANGED
@@ -1,79 +1,153 @@
1
1
  // src/CNSStimulationContextStore.ts
2
2
  var CNSStimulationContextStore = class {
3
- constructor(ctx = /* @__PURE__ */ new Map()) {
3
+ constructor(ctx) {
4
4
  this.ctx = ctx;
5
5
  }
6
6
  get(key) {
7
- return this.ctx.get(key);
7
+ return this.ctx?.get(key);
8
8
  }
9
9
  set(key, value) {
10
- this.ctx.set(key, value);
10
+ (this.ctx ?? (this.ctx = /* @__PURE__ */ new Map())).set(key, value);
11
11
  }
12
12
  getAll() {
13
- return new Map(this.ctx);
13
+ return this.ctx ? new Map(this.ctx) : /* @__PURE__ */ new Map();
14
14
  }
15
15
  setAll(values) {
16
- this.ctx.clear();
17
- for (const [key, value] of values) this.ctx.set(key, value);
16
+ const ctx = this.ctx ?? (this.ctx = /* @__PURE__ */ new Map());
17
+ ctx.clear();
18
+ for (const [key, value] of values) ctx.set(key, value);
18
19
  }
19
20
  delete(key) {
20
- this.ctx.delete(key);
21
+ this.ctx?.delete(key);
21
22
  }
22
23
  };
23
24
 
24
- // src/CNSNeuronActivationPump.ts
25
- var CNSNeuronActivationPump = class {
26
- constructor(processor, concurrency, abortSignal, initialCapacity) {
27
- this.processor = processor;
28
- this.concurrency = concurrency;
25
+ // src/CNSStimulation.ts
26
+ var CNSDendriteContext = class {
27
+ constructor(stimulation, neuron2, abortSignal, cns) {
28
+ this.stimulation = stimulation;
29
+ this.neuron = neuron2;
29
30
  this.abortSignal = abortSignal;
30
- this.items = [];
31
- this.head = 0;
32
- this.tail = 0;
33
- this.size = 0;
34
- this.capacity = 16;
31
+ this.cns = cns;
32
+ }
33
+ get() {
34
+ return this.stimulation.getContext().get(this.neuron);
35
+ }
36
+ set(value) {
37
+ this.stimulation.getContext().set(this.neuron, value);
38
+ }
39
+ delete() {
40
+ this.stimulation.getContext().delete(this.neuron);
41
+ }
42
+ };
43
+ var CNSStimulationResponseImpl = class {
44
+ constructor(stimulation, store, inputSignal, outputSignal, queueLength, hops, error) {
45
+ this.stimulation = stimulation;
46
+ this.store = store;
47
+ this.inputSignal = inputSignal;
48
+ this.outputSignal = outputSignal;
49
+ this.queueLength = queueLength;
50
+ this.hops = hops;
51
+ this.error = error;
52
+ }
53
+ get contextValue() {
54
+ return this.store ? this.store.getAll() : /* @__PURE__ */ new Map();
55
+ }
56
+ };
57
+ var CNSStimulation = class {
58
+ constructor(cns, instanceNeuronQueue, options, onResponse) {
59
+ this.cns = cns;
60
+ this.options = options;
61
+ this.qHead = 0;
62
+ this.qTail = 0;
63
+ this.qSize = 0;
64
+ this.qCapacity = 4;
35
65
  this.activeOperations = 0;
36
- this.activeTasks = /* @__PURE__ */ new Set();
37
- // NEW: prevent re-entrant pump; defer extra runs
66
+ // Re-entrancy guard for the pump loop.
38
67
  this.pumping = false;
39
68
  this.needsPump = false;
40
- if (initialCapacity && initialCapacity > 0) {
41
- this.capacity = initialCapacity;
69
+ this.scheduledCount = 0;
70
+ this.isCompleted = false;
71
+ this.completionRejected = false;
72
+ this.ctxStore = options?.ctx;
73
+ this.instanceNeuronQueue = instanceNeuronQueue;
74
+ this.onResponse = onResponse;
75
+ if (this.options?.maxNeuronHops) {
76
+ this.nueronVisitMap = /* @__PURE__ */ new Map();
77
+ }
78
+ if (this.autoCleanupContextsEnabled) {
79
+ this.activeSccCounts = /* @__PURE__ */ new Map();
42
80
  }
43
- this.items = new Array(this.capacity);
44
81
  }
82
+ pushFailedTask(failure) {
83
+ (this.failedTasks ?? (this.failedTasks = [])).push(failure);
84
+ }
85
+ finalizeResolve() {
86
+ this.completionRejected = false;
87
+ this.resolveCompleted?.();
88
+ }
89
+ finalizeReject(error) {
90
+ this.completionRejected = true;
91
+ this.completionError = error;
92
+ this.rejectCompleted?.(error);
93
+ }
94
+ /** Lazily-created neuron context store (see {@link ctxStore}). */
95
+ get ctx() {
96
+ return this.ctxStore ?? (this.ctxStore = new CNSStimulationContextStore());
97
+ }
98
+ // ----- Inlined activation queue / pump -----
45
99
  get canStartOperation() {
46
- const limit = this.concurrency ?? Infinity;
47
- return this.activeOperations < limit && !this.abortSignal?.aborted;
48
- }
49
- resize() {
50
- const oldCapacity = this.capacity;
51
- this.capacity = oldCapacity * 2;
52
- const newItems = new Array(this.capacity);
53
- let oldIndex = this.head;
54
- for (let i = 0; i < this.size; i++) {
55
- newItems[i] = this.items[oldIndex];
100
+ const limit = this.options?.concurrency ?? Infinity;
101
+ return this.activeOperations < limit && !this.options?.abortSignal?.aborted;
102
+ }
103
+ qResize() {
104
+ const oldCapacity = this.qCapacity;
105
+ const oldItems = this.qItems;
106
+ this.qCapacity = oldCapacity * 2;
107
+ const newItems = new Array(this.qCapacity);
108
+ let oldIndex = this.qHead;
109
+ for (let i = 0; i < this.qSize; i++) {
110
+ newItems[i] = oldItems[oldIndex];
56
111
  oldIndex = (oldIndex + 1) % oldCapacity;
57
112
  }
58
- this.items = newItems;
59
- this.head = 0;
60
- this.tail = this.size;
61
- }
62
- dequeue() {
63
- if (this.size === 0) return void 0;
64
- const neuronActivationTask = this.items[this.head];
65
- this.items[this.head] = void 0;
66
- this.head = (this.head + 1) % this.capacity;
67
- this.size--;
68
- return neuronActivationTask;
113
+ this.qItems = newItems;
114
+ this.qHead = 0;
115
+ this.qTail = this.qSize;
116
+ }
117
+ qDequeue() {
118
+ if (this.qSize === 0) return void 0;
119
+ const items = this.qItems;
120
+ const task = items[this.qHead];
121
+ items[this.qHead] = void 0;
122
+ this.qHead = (this.qHead + 1) % this.qCapacity;
123
+ this.qSize--;
124
+ return task;
125
+ }
126
+ qEnqueueItem(task) {
127
+ if (!this.qItems) {
128
+ this.qItems = new Array(this.qCapacity);
129
+ } else if (this.qSize === this.qCapacity) {
130
+ this.qResize();
131
+ }
132
+ this.qItems[this.qTail] = task;
133
+ this.qTail = (this.qTail + 1) % this.qCapacity;
134
+ this.qSize++;
135
+ }
136
+ /** Enqueue a task and drive the pump unless one is already running. */
137
+ qEnqueue(task) {
138
+ this.qEnqueueItem(task);
139
+ if (!this.pumping) this.pump();
140
+ else this.needsPump = true;
69
141
  }
70
- enqueueItem(neuronActivationTask) {
71
- if (this.size === this.capacity) {
72
- this.resize();
142
+ qGetQueuedTasks() {
143
+ const result = [];
144
+ if (!this.qItems) return result;
145
+ let index = this.qHead;
146
+ for (let i = 0; i < this.qSize; i++) {
147
+ result.push(this.qItems[index]);
148
+ index = (index + 1) % this.qCapacity;
73
149
  }
74
- this.items[this.tail] = neuronActivationTask;
75
- this.tail = (this.tail + 1) % this.capacity;
76
- this.size++;
150
+ return result;
77
151
  }
78
152
  pump() {
79
153
  if (this.pumping) {
@@ -81,26 +155,26 @@ var CNSNeuronActivationPump = class {
81
155
  return;
82
156
  }
83
157
  this.pumping = true;
84
- while (this.canStartOperation && this.size > 0) {
85
- const neuronActivationTask = this.dequeue();
158
+ while (this.canStartOperation && this.qSize > 0) {
159
+ const task = this.qDequeue();
86
160
  this.activeOperations++;
87
- this.activeTasks.add(neuronActivationTask);
88
- const ret = this.processor(neuronActivationTask);
161
+ const ret = this.executeActivationTask(task);
89
162
  if (ret && typeof ret.then === "function") {
163
+ (this.activeTasks ?? (this.activeTasks = /* @__PURE__ */ new Set())).add(task);
90
164
  ret.then(
91
165
  (cb) => {
92
166
  this.activeOperations--;
93
- this.activeTasks.delete(neuronActivationTask);
167
+ this.activeTasks.delete(task);
94
168
  if (typeof cb === "function") cb();
95
- if (this.size > 0 && this.canStartOperation) {
169
+ if (this.qSize > 0 && this.canStartOperation) {
96
170
  if (this.pumping) this.needsPump = true;
97
171
  else this.pump();
98
172
  }
99
173
  },
100
174
  (err) => {
101
175
  this.activeOperations--;
102
- this.activeTasks.delete(neuronActivationTask);
103
- if (this.size > 0 && this.canStartOperation) {
176
+ this.activeTasks.delete(task);
177
+ if (this.qSize > 0 && this.canStartOperation) {
104
178
  if (this.pumping) this.needsPump = true;
105
179
  else this.pump();
106
180
  }
@@ -110,7 +184,6 @@ var CNSNeuronActivationPump = class {
110
184
  continue;
111
185
  }
112
186
  this.activeOperations--;
113
- this.activeTasks.delete(neuronActivationTask);
114
187
  if (typeof ret === "function") ret();
115
188
  }
116
189
  this.pumping = false;
@@ -119,73 +192,20 @@ var CNSNeuronActivationPump = class {
119
192
  this.pump();
120
193
  }
121
194
  }
122
- enqueue(neuronActivationTask) {
123
- this.enqueueItem(neuronActivationTask);
124
- if (!this.pumping) this.pump();
125
- else this.needsPump = true;
126
- }
127
- get length() {
128
- return this.size;
129
- }
130
- getActiveOperationsCount() {
131
- return this.activeOperations;
132
- }
133
- getQueuedTasks() {
134
- const result = [];
135
- let index = this.head;
136
- for (let i = 0; i < this.size; i++) {
137
- result.push(this.items[index]);
138
- index = (index + 1) % this.capacity;
139
- }
140
- return result;
141
- }
142
- getActiveTasks() {
143
- return Array.from(this.activeTasks);
144
- }
145
- };
146
-
147
- // src/CNSStimulation.ts
148
- var CNSStimulation = class {
149
- constructor(cns, instanceNeuronQueue, options) {
150
- this.cns = cns;
151
- this.options = options;
152
- this.scheduledCount = 0;
153
- this.pendingTasks = /* @__PURE__ */ new Set();
154
- this.failedTasks = [];
155
- this.completed = new Promise((resolve, reject) => {
156
- this.resolveCompleted = resolve;
157
- this.rejectCompleted = reject;
158
- });
159
- this.isCompleted = false;
160
- /**
161
- * Track active SCCs: SCC index -> count of active neurons in that SCC
162
- */
163
- this.activeSccCounts = /* @__PURE__ */ new Map();
164
- this.ctx = options?.ctx ?? new CNSStimulationContextStore();
165
- this.queue = new CNSNeuronActivationPump(
166
- (neuronActivationTask) => this.executeActivationTask(neuronActivationTask),
167
- options?.concurrency,
168
- options?.abortSignal
169
- );
170
- this.instanceNeuronQueue = instanceNeuronQueue;
171
- if (this.options?.maxNeuronHops) {
172
- this.nueronVisitMap = /* @__PURE__ */ new Map();
173
- }
174
- }
175
195
  tryResolveCompleted() {
176
196
  if (this.isCompleted) return;
177
- const noActive = this.queue.getActiveOperationsCount() === 0;
178
- const noPending = this.queue.length + this.scheduledCount === 0;
197
+ const noActive = this.activeOperations === 0;
198
+ const noPending = this.qSize + this.scheduledCount === 0;
179
199
  const aborted = !!this.options?.abortSignal?.aborted;
180
200
  if (noPending && noActive || aborted && noActive && !noPending) {
181
201
  if (aborted) {
182
- const queuedTasks = this.queue.getQueuedTasks();
202
+ const queuedTasks = this.qGetQueuedTasks();
183
203
  for (const task of [...queuedTasks]) {
184
- const alreadyTracked = this.failedTasks.some(
204
+ const alreadyTracked = this.failedTasks?.some(
185
205
  (ft) => ft.task === task
186
206
  );
187
207
  if (!alreadyTracked) {
188
- this.failedTasks.push({
208
+ this.pushFailedTask({
189
209
  task,
190
210
  error: new Error(
191
211
  "Task aborted - not started due to abort signal"
@@ -195,37 +215,47 @@ var CNSStimulation = class {
195
215
  }
196
216
  }
197
217
  this.isCompleted = true;
198
- this.rejectCompleted(new Error("Stimulation aborted"));
218
+ this.finalizeReject(new Error("Stimulation aborted"));
199
219
  return;
200
220
  }
201
221
  this.isCompleted = true;
202
- if (this.failedTasks.length > 0 || this.onResponseError) {
222
+ const failedCount = this.failedTasks?.length ?? 0;
223
+ if (failedCount > 0 || this.onResponseError) {
203
224
  const error = this.onResponseError || new Error(
204
- `Stimulation completed with ${this.failedTasks.length} failed task(s)`
225
+ `Stimulation completed with ${failedCount} failed task(s)`
205
226
  );
206
- this.rejectCompleted(error);
227
+ this.finalizeReject(error);
207
228
  } else {
208
- this.resolveCompleted();
229
+ this.finalizeResolve();
209
230
  }
210
231
  }
211
232
  }
212
233
  waitUntilComplete() {
234
+ if (this.completed) return this.completed;
235
+ if (this.isCompleted) {
236
+ this.completed = this.completionRejected ? Promise.reject(this.completionError) : Promise.resolve();
237
+ return this.completed;
238
+ }
239
+ this.completed = new Promise((resolve, reject) => {
240
+ this.resolveCompleted = resolve;
241
+ this.rejectCompleted = reject;
242
+ });
213
243
  return this.completed;
214
244
  }
215
245
  /**
216
246
  * Returns all current activation tasks: queued, active, and pending (scheduled but not yet enqueued)
217
247
  */
218
248
  getAllActivationTasks() {
219
- const queuedTasks = this.queue.getQueuedTasks();
220
- const activeTasks = this.queue.getActiveTasks();
221
- const pendingTasks = Array.from(this.pendingTasks);
249
+ const queuedTasks = this.qGetQueuedTasks();
250
+ const activeTasks = this.activeTasks ? Array.from(this.activeTasks) : [];
251
+ const pendingTasks = this.pendingTasks ? Array.from(this.pendingTasks) : [];
222
252
  return [...queuedTasks, ...activeTasks, ...pendingTasks];
223
253
  }
224
254
  /**
225
255
  * Returns all tasks that failed or were aborted
226
256
  */
227
257
  getFailedTasks() {
228
- return [...this.failedTasks];
258
+ return this.failedTasks ? [...this.failedTasks] : [];
229
259
  }
230
260
  /**
231
261
  * Get the context store for this stimulation
@@ -238,7 +268,7 @@ var CNSStimulation = class {
238
268
  */
239
269
  enqueueTasks(tasks) {
240
270
  for (const task of tasks) {
241
- this.queue.enqueue(task);
271
+ this.qEnqueue(task);
242
272
  }
243
273
  this.tryResolveCompleted();
244
274
  }
@@ -266,20 +296,24 @@ var CNSStimulation = class {
266
296
  * Increment the active count for the SCC containing this neuron
267
297
  */
268
298
  incrementSccCount(neuron2) {
299
+ const counts = this.activeSccCounts;
300
+ if (!counts) return;
269
301
  const sccIndex = this.cns.network.getSccIndexByNeuron(neuron2);
270
302
  if (sccIndex === void 0) return;
271
- const currentCount = this.activeSccCounts.get(sccIndex) || 0;
272
- this.activeSccCounts.set(sccIndex, currentCount + 1);
303
+ const currentCount = counts.get(sccIndex) || 0;
304
+ counts.set(sccIndex, currentCount + 1);
273
305
  }
274
306
  /**
275
307
  * Decrement the active count for the SCC containing this neuron
276
308
  */
277
309
  decrementSccCount(neuron2) {
310
+ const counts = this.activeSccCounts;
311
+ if (!counts) return;
278
312
  const sccIndex = this.cns.network.getSccIndexByNeuron(neuron2);
279
313
  if (sccIndex === void 0) return;
280
- const currentCount = this.activeSccCounts.get(sccIndex) || 0;
314
+ const currentCount = counts.get(sccIndex) || 0;
281
315
  const nextCount = Math.max(0, currentCount - 1);
282
- this.activeSccCounts.set(sccIndex, nextCount);
316
+ counts.set(sccIndex, nextCount);
283
317
  if (currentCount === 0) {
284
318
  console.warn(
285
319
  `[CNSStimulation] Attempting to decrement SCC count below 0 for neuron`
@@ -290,7 +324,8 @@ var CNSStimulation = class {
290
324
  * Check if a neuron can be guaranteed not to be visited again
291
325
  */
292
326
  canNeuronBeGuaranteedDone(neuron2) {
293
- if (!this.autoCleanupContextsEnabled) return false;
327
+ if (!this.autoCleanupContextsEnabled || !this.activeSccCounts)
328
+ return false;
294
329
  return this.cns.network.canNeuronBeGuaranteedDone(
295
330
  neuron2,
296
331
  this.activeSccCounts
@@ -298,7 +333,7 @@ var CNSStimulation = class {
298
333
  }
299
334
  cleanupCtxIfNeeded(neuron2) {
300
335
  if (this.autoCleanupContextsEnabled && this.canNeuronBeGuaranteedDone(neuron2)) {
301
- this.ctx.delete(neuron2);
336
+ this.ctxStore?.delete(neuron2);
302
337
  }
303
338
  }
304
339
  createSubscriberQueueItem(subscriber, inputSignal) {
@@ -325,11 +360,15 @@ var CNSStimulation = class {
325
360
  const subscribers = this.cns.network.getSubscribers(
326
361
  neuronActivationTask.dendriteCollateral
327
362
  );
328
- const subscriber = subscribers.find(
329
- (s) => s.neuron === neuronActivationTask.neuron
330
- );
363
+ let subscriber;
364
+ for (let i = 0; i < subscribers.length; i++) {
365
+ if (subscribers[i].neuron === neuronActivationTask.neuron) {
366
+ subscriber = subscribers[i];
367
+ break;
368
+ }
369
+ }
331
370
  if (!subscriber) {
332
- this.failedTasks.push({
371
+ this.pushFailedTask({
333
372
  task: neuronActivationTask,
334
373
  error: new Error(
335
374
  `Subscriber not found for activation task`
@@ -342,40 +381,58 @@ var CNSStimulation = class {
342
381
  const neuron2 = subscriber.neuron;
343
382
  const dendrite = subscriber.dendrite;
344
383
  const inputSignal = neuronActivationTask.input;
345
- const starter = () => {
346
- this.markNeuronActive(neuron2);
347
- let response;
348
- try {
349
- response = dendrite.response(
350
- inputSignal?.payload,
351
- neuron2.axon,
352
- {
353
- get: () => this.ctx.get(neuron2),
354
- set: (value) => this.ctx.set(neuron2, value),
355
- delete: () => this.ctx.delete(neuron2),
356
- abortSignal: this.options?.abortSignal,
357
- cns: this.cns,
358
- stimulation: this
359
- }
360
- );
361
- } catch (error) {
362
- this.markNeuronInactive(neuron2);
363
- const isAborted = this.options?.abortSignal?.aborted ?? false;
364
- this.failedTasks.push({
365
- task: neuronActivationTask,
366
- error: error instanceof Error ? error : new Error(String(error)),
367
- aborted: isAborted
368
- });
369
- this.processResponseOrResponses(
384
+ const concurrency = neuron2.concurrency;
385
+ if (concurrency === void 0 || concurrency <= 0) {
386
+ return this.runStarter(
387
+ neuron2,
388
+ dendrite,
389
+ inputSignal,
390
+ neuronActivationTask
391
+ );
392
+ }
393
+ return this.instanceNeuronQueue.run(
394
+ neuron2,
395
+ () => this.runStarter(
396
+ neuron2,
397
+ dendrite,
398
+ inputSignal,
399
+ neuronActivationTask
400
+ )
401
+ );
402
+ }
403
+ runStarter(neuron2, dendrite, inputSignal, neuronActivationTask) {
404
+ this.markNeuronActive(neuron2);
405
+ let response;
406
+ try {
407
+ response = dendrite.response(
408
+ inputSignal?.payload,
409
+ neuron2.axon,
410
+ new CNSDendriteContext(
411
+ this,
370
412
  neuron2,
371
- inputSignal,
372
- void 0,
373
- error
374
- );
375
- return () => {
376
- };
377
- }
378
- const maxDuration = neuron2.maxDuration;
413
+ this.options?.abortSignal,
414
+ this.cns
415
+ )
416
+ );
417
+ } catch (error) {
418
+ this.markNeuronInactive(neuron2);
419
+ const isAborted = this.options?.abortSignal?.aborted ?? false;
420
+ this.pushFailedTask({
421
+ task: neuronActivationTask,
422
+ error: error instanceof Error ? error : new Error(String(error)),
423
+ aborted: isAborted
424
+ });
425
+ this.processResponseOrResponses(
426
+ neuron2,
427
+ inputSignal,
428
+ void 0,
429
+ error
430
+ );
431
+ return () => {
432
+ };
433
+ }
434
+ const maxDuration = neuron2.maxDuration;
435
+ if (response instanceof Promise || maxDuration) {
379
436
  const asPromise = response instanceof Promise ? response : Promise.resolve(response);
380
437
  const timedPromise = maxDuration && maxDuration > 0 ? new Promise((resolve, reject) => {
381
438
  const t = setTimeout(() => {
@@ -395,48 +452,45 @@ var CNSStimulation = class {
395
452
  }
396
453
  );
397
454
  }) : asPromise;
398
- if (response instanceof Promise || maxDuration) {
399
- return timedPromise.then(
400
- (signal) => {
401
- return () => {
402
- this.markNeuronInactive(neuron2);
403
- return this.processResponseOrResponses(
404
- neuron2,
405
- inputSignal,
406
- signal
407
- );
408
- };
409
- },
410
- (error) => {
411
- return () => {
412
- this.markNeuronInactive(neuron2);
413
- const isAborted = this.options?.abortSignal?.aborted ?? false;
414
- this.failedTasks.push({
415
- task: neuronActivationTask,
416
- error: error instanceof Error ? error : new Error(String(error)),
417
- aborted: isAborted
418
- });
419
- return this.processResponseOrResponses(
420
- neuron2,
421
- inputSignal,
422
- void 0,
423
- error
424
- );
425
- };
426
- }
455
+ return timedPromise.then(
456
+ (signal) => {
457
+ return () => {
458
+ this.markNeuronInactive(neuron2);
459
+ return this.processResponseOrResponses(
460
+ neuron2,
461
+ inputSignal,
462
+ signal
463
+ );
464
+ };
465
+ },
466
+ (error) => {
467
+ return () => {
468
+ this.markNeuronInactive(neuron2);
469
+ const isAborted = this.options?.abortSignal?.aborted ?? false;
470
+ this.pushFailedTask({
471
+ task: neuronActivationTask,
472
+ error: error instanceof Error ? error : new Error(String(error)),
473
+ aborted: isAborted
474
+ });
475
+ return this.processResponseOrResponses(
476
+ neuron2,
477
+ inputSignal,
478
+ void 0,
479
+ error
480
+ );
481
+ };
482
+ }
483
+ );
484
+ } else {
485
+ return () => {
486
+ this.markNeuronInactive(neuron2);
487
+ return this.processResponseOrResponses(
488
+ neuron2,
489
+ inputSignal,
490
+ response
427
491
  );
428
- } else {
429
- return () => {
430
- this.markNeuronInactive(neuron2);
431
- return this.processResponseOrResponses(
432
- neuron2,
433
- inputSignal,
434
- response
435
- );
436
- };
437
- }
438
- };
439
- return this.instanceNeuronQueue.run(neuron2, starter);
492
+ };
493
+ }
440
494
  }
441
495
  processResponseOrResponses(emitter, inputSignal, outputSignalOrSignals, error) {
442
496
  if (Array.isArray(outputSignalOrSignals)) {
@@ -454,36 +508,62 @@ var CNSStimulation = class {
454
508
  processResponse(emitter, inputSignal, outputSignal, error) {
455
509
  const collateral2 = outputSignal?.collateral;
456
510
  const subscribers = collateral2 ? this.cns.network.getSubscribers(collateral2) : [];
511
+ const onResponse = this.onResponse;
512
+ if (!onResponse) {
513
+ if (collateral2 && !error) {
514
+ if (emitter) this.cleanupCtxIfNeeded(emitter);
515
+ const n = subscribers.length;
516
+ if (n > 0) {
517
+ const tasks = new Array(n);
518
+ for (let i = 0; i < n; i++) {
519
+ tasks[i] = this.createSubscriberQueueItem(
520
+ subscribers[i],
521
+ outputSignal
522
+ );
523
+ }
524
+ this.scheduledCount += n;
525
+ for (let i = 0; i < n; i++) {
526
+ this.scheduledCount--;
527
+ this.qEnqueue(tasks[i]);
528
+ }
529
+ }
530
+ }
531
+ this.tryResolveCompleted();
532
+ return;
533
+ }
457
534
  const subscriberActivationTasks = [];
458
535
  if (collateral2 && !error) {
459
536
  if (emitter) this.cleanupCtxIfNeeded(emitter);
537
+ const pending = this.pendingTasks ?? (this.pendingTasks = /* @__PURE__ */ new Set());
460
538
  for (let i = 0; i < subscribers.length; i++) {
461
539
  const task = this.createSubscriberQueueItem(
462
540
  subscribers[i],
463
541
  outputSignal
464
542
  );
465
543
  subscriberActivationTasks.push(task);
466
- this.pendingTasks.add(task);
544
+ pending.add(task);
467
545
  }
468
546
  }
469
547
  this.scheduledCount += subscriberActivationTasks.length;
470
548
  let maybePromise;
471
549
  try {
472
- maybePromise = this.options?.onResponse?.({
473
- inputSignal,
474
- outputSignal,
475
- contextValue: this.ctx.getAll(),
476
- queueLength: this.queue.length + this.scheduledCount,
477
- stimulation: this,
478
- hops: this.options?.maxNeuronHops && emitter ? this.nueronVisitMap?.get(emitter) ?? 0 : void 0,
479
- error
480
- });
550
+ maybePromise = onResponse(
551
+ new CNSStimulationResponseImpl(
552
+ this,
553
+ this.ctxStore,
554
+ inputSignal,
555
+ outputSignal,
556
+ this.qSize + this.scheduledCount,
557
+ this.options?.maxNeuronHops && emitter ? this.nueronVisitMap?.get(emitter) ?? 0 : void 0,
558
+ error
559
+ )
560
+ );
481
561
  } catch (e) {
482
562
  this.onResponseError = e instanceof Error ? e : new Error(String(e));
483
563
  for (let i = 0; i < subscriberActivationTasks.length; i++) {
484
564
  this.scheduledCount--;
485
- this.pendingTasks.delete(subscriberActivationTasks[i]);
486
- this.queue.enqueue(subscriberActivationTasks[i]);
565
+ this.pendingTasks?.delete(subscriberActivationTasks[i]);
566
+ this.qEnqueue(subscriberActivationTasks[i]);
487
567
  }
488
568
  this.tryResolveCompleted();
489
569
  return;
@@ -493,8 +573,8 @@ var CNSStimulation = class {
493
573
  () => {
494
574
  for (let i = 0; i < subscriberActivationTasks.length; i++) {
495
575
  this.scheduledCount--;
496
- this.pendingTasks.delete(subscriberActivationTasks[i]);
497
- this.queue.enqueue(subscriberActivationTasks[i]);
576
+ this.pendingTasks?.delete(subscriberActivationTasks[i]);
577
+ this.qEnqueue(subscriberActivationTasks[i]);
498
578
  }
499
579
  this.tryResolveCompleted();
500
580
  },
@@ -502,8 +582,8 @@ var CNSStimulation = class {
502
582
  this.onResponseError = error2 instanceof Error ? error2 : new Error(String(error2));
503
583
  for (let i = 0; i < subscriberActivationTasks.length; i++) {
504
584
  this.scheduledCount--;
505
- this.pendingTasks.delete(subscriberActivationTasks[i]);
506
- this.queue.enqueue(subscriberActivationTasks[i]);
585
+ this.pendingTasks?.delete(subscriberActivationTasks[i]);
586
+ this.qEnqueue(subscriberActivationTasks[i]);
507
587
  }
508
588
  this.tryResolveCompleted();
509
589
  }
@@ -512,8 +592,8 @@ var CNSStimulation = class {
512
592
  }
513
593
  for (let i = 0; i < subscriberActivationTasks.length; i++) {
514
594
  this.scheduledCount--;
515
- this.pendingTasks.delete(subscriberActivationTasks[i]);
516
- this.queue.enqueue(subscriberActivationTasks[i]);
595
+ this.pendingTasks?.delete(subscriberActivationTasks[i]);
596
+ this.qEnqueue(subscriberActivationTasks[i]);
517
597
  }
518
598
  this.tryResolveCompleted();
519
599
  }
@@ -877,8 +957,7 @@ var CNS = class {
877
957
  }
878
958
  wrapOnResponse(local) {
879
959
  if (this.globalResponseListeners.length === 0 && !local) {
880
- return () => {
881
- };
960
+ return void 0;
882
961
  }
883
962
  return (r) => {
884
963
  let anyPromise = false;
@@ -922,10 +1001,8 @@ var CNS = class {
922
1001
  const stimulation = new CNSStimulation(
923
1002
  this,
924
1003
  this.instanceNeuronQueue,
925
- {
926
- ...options,
927
- onResponse: wrapped
928
- }
1004
+ options,
1005
+ wrapped
929
1006
  );
930
1007
  stimulation.responseToSignal(signalOrSignals);
931
1008
  return stimulation;
@@ -938,10 +1015,8 @@ var CNS = class {
938
1015
  const stimulation = new CNSStimulation(
939
1016
  this,
940
1017
  this.instanceNeuronQueue,
941
- {
942
- ...options,
943
- onResponse: wrapped
944
- }
1018
+ options,
1019
+ wrapped
945
1020
  );
946
1021
  stimulation.enqueueTasks(tasks);
947
1022
  return stimulation;
@@ -998,6 +1073,20 @@ var CNSPersistOptionsRegistry = class {
998
1073
  removeStimulation(stimulationId) {
999
1074
  this.stimulations.delete(stimulationId);
1000
1075
  }
1076
+ register(name, neuron2, collateralNames) {
1077
+ this.addNeuron(neuron2, { name, neuron: neuron2 });
1078
+ for (const [key, col] of Object.entries(neuron2.axon)) {
1079
+ this.addCollateral(col, {
1080
+ name: collateralNames?.[key] ?? key,
1081
+ collateral: col
1082
+ });
1083
+ }
1084
+ return this;
1085
+ }
1086
+ registerCollateral(name, collateral2) {
1087
+ this.addCollateral(collateral2, { name, collateral: collateral2 });
1088
+ return this;
1089
+ }
1001
1090
  };
1002
1091
 
1003
1092
  // src/CNSCollateral.ts
@@ -1200,11 +1289,14 @@ var modality = (afferentPaths) => {
1200
1289
  };
1201
1290
  var createPersistRegistry = (namedNeurons) => {
1202
1291
  const registry = new CNSPersistOptionsRegistry();
1203
- for (const [name, neuron2] of Object.entries(namedNeurons)) {
1292
+ for (const [name, entry] of Object.entries(namedNeurons)) {
1293
+ const isEntry = "neuron" in entry && !("axon" in entry);
1294
+ const neuron2 = isEntry ? entry.neuron : entry;
1295
+ const colNames = isEntry ? entry.collaterals ?? {} : {};
1204
1296
  registry.addNeuron(neuron2, { name, neuron: neuron2 });
1205
1297
  for (const [key, col] of Object.entries(neuron2.axon)) {
1206
1298
  registry.addCollateral(col, {
1207
- name: key,
1299
+ name: colNames[key] ?? key,
1208
1300
  collateral: col
1209
1301
  });
1210
1302
  }