@cnstra/core 1.3.1 → 1.5.2

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.
Files changed (44) hide show
  1. package/README.md +80 -7
  2. package/dist/CNS.d.ts +43 -9
  3. package/dist/CNS.d.ts.map +1 -1
  4. package/dist/CNSCollateral.d.ts +5 -5
  5. package/dist/CNSCollateral.d.ts.map +1 -1
  6. package/dist/CNSFunctionalQueue.d.ts +3 -3
  7. package/dist/CNSFunctionalQueue.d.ts.map +1 -1
  8. package/dist/CNSInstanceNeuronQueue.d.ts +8 -0
  9. package/dist/CNSInstanceNeuronQueue.d.ts.map +1 -0
  10. package/dist/CNSStimulation.d.ts +13 -11
  11. package/dist/CNSStimulation.d.ts.map +1 -1
  12. package/dist/CNSTaskQueue.d.ts +12 -0
  13. package/dist/CNSTaskQueue.d.ts.map +1 -0
  14. package/dist/factory/index.d.ts +14 -7
  15. package/dist/factory/index.d.ts.map +1 -1
  16. package/dist/index.cjs +391 -84
  17. package/dist/index.cjs.map +1 -1
  18. package/dist/index.d.ts +3 -0
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +391 -85
  21. package/dist/index.js.map +1 -1
  22. package/dist/interfaces/ICNS.d.ts +20 -5
  23. package/dist/interfaces/ICNS.d.ts.map +1 -1
  24. package/dist/interfaces/ICNSCollateral.d.ts +3 -3
  25. package/dist/interfaces/ICNSCollateral.d.ts.map +1 -1
  26. package/dist/types/TCNSAxon.d.ts +3 -1
  27. package/dist/types/TCNSAxon.d.ts.map +1 -1
  28. package/dist/types/TCNSDendrite.d.ts +7 -3
  29. package/dist/types/TCNSDendrite.d.ts.map +1 -1
  30. package/dist/types/TCNSLocalContextValueStore.d.ts +1 -0
  31. package/dist/types/TCNSLocalContextValueStore.d.ts.map +1 -1
  32. package/dist/types/TCNSNeuron.d.ts +5 -3
  33. package/dist/types/TCNSNeuron.d.ts.map +1 -1
  34. package/dist/types/TCNSNeuronResponseReturn.d.ts +1 -1
  35. package/dist/types/TCNSNeuronResponseReturn.d.ts.map +1 -1
  36. package/dist/types/TCNSSignal.d.ts +2 -2
  37. package/dist/types/TCNSSignal.d.ts.map +1 -1
  38. package/dist/types/TCNSStimulationOptions.d.ts +3 -3
  39. package/dist/types/TCNSStimulationOptions.d.ts.map +1 -1
  40. package/dist/types/TCNSStimulationQueueItem.d.ts +3 -3
  41. package/dist/types/TCNSStimulationQueueItem.d.ts.map +1 -1
  42. package/dist/types/TCNSStimulationResponse.d.ts +3 -3
  43. package/dist/types/TCNSStimulationResponse.d.ts.map +1 -1
  44. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -132,7 +132,7 @@ var CNSFunctionalQueue = class {
132
132
 
133
133
  // src/CNSStimulation.ts
134
134
  var CNSStimulation = class {
135
- constructor(cns, options) {
135
+ constructor(cns, instanceNeuronQueue, options) {
136
136
  this.cns = cns;
137
137
  this.options = options;
138
138
  this.scheduledCount = 0;
@@ -147,6 +147,7 @@ var CNSStimulation = class {
147
147
  options?.concurrency,
148
148
  options?.abortSignal
149
149
  );
150
+ this.instanceNeuronQueue = instanceNeuronQueue;
150
151
  if (this.options?.maxNeuronHops) {
151
152
  this.nueronVisitMap = /* @__PURE__ */ new Map();
152
153
  }
@@ -175,7 +176,7 @@ var CNSStimulation = class {
175
176
  * Increment the active count for the SCC containing this neuron
176
177
  */
177
178
  incrementSccCount(neuronId) {
178
- const sccIndex = this.cns.getSccIndexByNeuronId(neuronId);
179
+ const sccIndex = this.cns.getSccIndexByNeuronName(neuronId);
179
180
  if (sccIndex === void 0) return;
180
181
  const currentCount = this.activeSccCounts.get(sccIndex) || 0;
181
182
  this.activeSccCounts.set(sccIndex, currentCount + 1);
@@ -184,7 +185,7 @@ var CNSStimulation = class {
184
185
  * Decrement the active count for the SCC containing this neuron
185
186
  */
186
187
  decrementSccCount(neuronId) {
187
- const sccIndex = this.cns.getSccIndexByNeuronId(neuronId);
188
+ const sccIndex = this.cns.getSccIndexByNeuronName(neuronId);
188
189
  if (sccIndex === void 0) return;
189
190
  const currentCount = this.activeSccCounts.get(sccIndex) || 0;
190
191
  const nextCount = Math.max(0, currentCount - 1);
@@ -206,25 +207,25 @@ var CNSStimulation = class {
206
207
  );
207
208
  }
208
209
  cleanupCtxIfNeeded(neuron2) {
209
- if (this.autoCleanupContextsEnabled && this.canNeuronBeGuaranteedDone(neuron2.id)) {
210
- this.ctx.delete(neuron2.id);
210
+ if (this.autoCleanupContextsEnabled && this.canNeuronBeGuaranteedDone(neuron2.name)) {
211
+ this.ctx.delete(neuron2.name);
211
212
  }
212
213
  }
213
214
  createSubscriberQueueItem(subscriber, inputSignal) {
214
215
  if (this.options?.maxNeuronHops) {
215
- if (this.nueronVisitMap?.get(subscriber.neuron.id) ?? 0 >= this.options?.maxNeuronHops) {
216
+ if (this.nueronVisitMap?.get(subscriber.neuron.name) ?? 0 >= this.options?.maxNeuronHops) {
216
217
  throw new Error(
217
- `Max neuron hops reached for neuron "${subscriber.neuron.id}" when trying to enqueue subscriber "${subscriber.dendrite.collateral.id}" in stimulation "${this.id}"`
218
+ `Max neuron hops reached for neuron "${subscriber.neuron.name}" when trying to enqueue subscriber "${subscriber.dendrite.collateral.name}" in stimulation "${this.id}"`
218
219
  );
219
220
  } else {
220
221
  this.nueronVisitMap?.set(
221
- subscriber.neuron.id,
222
- (this.nueronVisitMap?.get(subscriber.neuron.id) ?? 0) + 1
222
+ subscriber.neuron.name,
223
+ (this.nueronVisitMap?.get(subscriber.neuron.name) ?? 0) + 1
223
224
  );
224
225
  }
225
226
  }
226
227
  return {
227
- neuronId: subscriber.neuron.id,
228
+ neuronId: subscriber.neuron.name,
228
229
  subscriber,
229
230
  inputSignal
230
231
  };
@@ -232,52 +233,57 @@ var CNSStimulation = class {
232
233
  processQueueItem(item) {
233
234
  const { subscriber, inputSignal } = item;
234
235
  const { neuron: neuron2, dendrite } = subscriber;
235
- this.markNeuronActive(neuron2.id);
236
- const response = dendrite.response(
237
- inputSignal?.payload,
238
- neuron2.axon,
239
- {
240
- get: () => this.ctx.get(neuron2.id),
241
- set: (value) => this.ctx.set(neuron2.id, value)
242
- },
243
- this.options?.abortSignal
244
- );
245
- if (response instanceof Promise) {
246
- return response.then(
247
- (signal) => {
248
- return () => {
249
- this.markNeuronInactive(neuron2.id);
250
- return this.processResponse(
251
- inputSignal,
252
- signal
253
- );
254
- };
255
- },
256
- (error) => {
257
- return () => {
258
- this.markNeuronInactive(neuron2.id);
259
- return this.processResponse(
260
- inputSignal,
261
- void 0,
262
- error
263
- );
264
- };
236
+ const starter = () => {
237
+ this.markNeuronActive(neuron2.name);
238
+ const response = dendrite.response(
239
+ inputSignal?.payload,
240
+ neuron2.axon,
241
+ {
242
+ get: () => this.ctx.get(neuron2.name),
243
+ set: (value) => this.ctx.set(neuron2.name, value),
244
+ delete: () => this.ctx.delete(neuron2.name),
245
+ abortSignal: this.options?.abortSignal,
246
+ cns: this.cns
265
247
  }
266
248
  );
267
- } else {
268
- return () => {
269
- this.markNeuronInactive(neuron2.id);
270
- return this.processResponse(
271
- inputSignal,
272
- response
249
+ if (response instanceof Promise) {
250
+ return response.then(
251
+ (signal) => {
252
+ return () => {
253
+ this.markNeuronInactive(neuron2.name);
254
+ return this.processResponse(
255
+ inputSignal,
256
+ signal
257
+ );
258
+ };
259
+ },
260
+ (error) => {
261
+ return () => {
262
+ this.markNeuronInactive(neuron2.name);
263
+ return this.processResponse(
264
+ inputSignal,
265
+ void 0,
266
+ error
267
+ );
268
+ };
269
+ }
273
270
  );
274
- };
275
- }
271
+ } else {
272
+ return () => {
273
+ this.markNeuronInactive(neuron2.name);
274
+ return this.processResponse(
275
+ inputSignal,
276
+ response
277
+ );
278
+ };
279
+ }
280
+ };
281
+ return this.instanceNeuronQueue.run(neuron2, starter);
276
282
  }
277
283
  processResponse(inputSignal, outputSignal, error) {
278
284
  const collateral2 = outputSignal?.collateral;
279
- const ownerNeuron = collateral2 ? this.cns.getParentNeuronByCollateralId(collateral2.id) : void 0;
280
- const subscribers = collateral2 ? this.cns.getSubscribers(collateral2.id) : [];
285
+ const ownerNeuron = collateral2 ? this.cns.getParentNeuronByCollateralName(collateral2.name) : void 0;
286
+ const subscribers = collateral2 ? this.cns.getSubscribers(collateral2.name) : [];
281
287
  const subscriberQueueItems = [];
282
288
  if (collateral2 && !error) {
283
289
  if (ownerNeuron) {
@@ -295,7 +301,8 @@ var CNSStimulation = class {
295
301
  outputSignal,
296
302
  ctx: this.ctx,
297
303
  queueLength: this.queue.length + this.scheduledCount,
298
- hops: this.options?.maxNeuronHops && ownerNeuron ? this.nueronVisitMap?.get(ownerNeuron.id) ?? 0 : void 0,
304
+ stimulationId: this.id,
305
+ hops: this.options?.maxNeuronHops && ownerNeuron ? this.nueronVisitMap?.get(ownerNeuron.name) ?? 0 : void 0,
299
306
  error
300
307
  });
301
308
  for (let i = 0; i < subscriberQueueItems.length; i++) {
@@ -304,27 +311,167 @@ var CNSStimulation = class {
304
311
  }
305
312
  }
306
313
  responseToSignal(signal) {
307
- const ownerNeuron = this.cns.getParentNeuronByCollateralId(
308
- signal.collateral.id
314
+ const ownerNeuron = this.cns.getParentNeuronByCollateralName(
315
+ signal.collateral.name
309
316
  );
310
317
  if (ownerNeuron) {
311
- this.markNeuronActive(ownerNeuron.id);
318
+ this.markNeuronActive(ownerNeuron.name);
312
319
  }
313
320
  this.processResponse(void 0, signal);
314
321
  }
315
322
  };
316
323
 
324
+ // src/CNSTaskQueue.ts
325
+ var CNSTaskQueue = class {
326
+ constructor(concurrency) {
327
+ this.concurrency = concurrency;
328
+ this.pumping = false;
329
+ this.needsPump = false;
330
+ this.activeOperations = 0;
331
+ this.items = [];
332
+ }
333
+ get canStartOperation() {
334
+ const limit = this.concurrency ?? Infinity;
335
+ return this.activeOperations < limit;
336
+ }
337
+ enqueue(task) {
338
+ return new Promise((resolve, reject) => {
339
+ this.items.push({ task, resolve, reject });
340
+ if (!this.pumping) this.pump();
341
+ else this.needsPump = true;
342
+ });
343
+ }
344
+ pump() {
345
+ if (this.pumping) {
346
+ this.needsPump = true;
347
+ return;
348
+ }
349
+ this.pumping = true;
350
+ while (this.canStartOperation && this.items.length > 0) {
351
+ const { task, resolve, reject } = this.items.shift();
352
+ this.activeOperations++;
353
+ try {
354
+ const ret = task();
355
+ if (ret && typeof ret.then === "function") {
356
+ ret.then(
357
+ (cb) => {
358
+ try {
359
+ if (typeof cb === "function") cb();
360
+ resolve();
361
+ } finally {
362
+ this.activeOperations--;
363
+ if (this.items.length > 0 && this.canStartOperation) {
364
+ if (this.pumping) this.needsPump = true;
365
+ else this.pump();
366
+ }
367
+ }
368
+ },
369
+ (err) => {
370
+ try {
371
+ reject(err);
372
+ } finally {
373
+ this.activeOperations--;
374
+ if (this.items.length > 0 && this.canStartOperation) {
375
+ if (this.pumping) this.needsPump = true;
376
+ else this.pump();
377
+ }
378
+ }
379
+ }
380
+ );
381
+ break;
382
+ } else {
383
+ try {
384
+ if (typeof ret === "function") ret();
385
+ resolve();
386
+ } finally {
387
+ this.activeOperations--;
388
+ }
389
+ }
390
+ } catch (e) {
391
+ try {
392
+ reject(e);
393
+ } finally {
394
+ this.activeOperations--;
395
+ }
396
+ }
397
+ }
398
+ this.pumping = false;
399
+ if (this.needsPump) {
400
+ this.needsPump = false;
401
+ this.pump();
402
+ }
403
+ }
404
+ };
405
+
406
+ // src/CNSInstanceNeuronQueue.ts
407
+ var CNSInstanceNeuronQueue = class {
408
+ constructor() {
409
+ this.gates = /* @__PURE__ */ new Map();
410
+ }
411
+ run(neuron2, fn) {
412
+ const limit = neuron2?.concurrency;
413
+ if (limit === void 0 || limit <= 0) {
414
+ return fn();
415
+ }
416
+ let gate = this.gates.get(neuron2.name);
417
+ if (!gate) {
418
+ gate = { limit, active: 0, waiters: [] };
419
+ this.gates.set(neuron2.name, gate);
420
+ } else {
421
+ gate.limit = limit;
422
+ }
423
+ const wrap = (cb) => {
424
+ return () => {
425
+ try {
426
+ cb();
427
+ } finally {
428
+ const g = this.gates.get(neuron2.name);
429
+ g.active = Math.max(0, g.active - 1);
430
+ if (g.waiters.length > 0) {
431
+ const next = g.waiters.shift();
432
+ next();
433
+ }
434
+ }
435
+ };
436
+ };
437
+ if (gate.active < gate.limit) {
438
+ gate.active++;
439
+ const ret = fn();
440
+ if (ret && typeof ret.then === "function") {
441
+ return ret.then((cb) => wrap(cb));
442
+ }
443
+ return wrap(ret);
444
+ }
445
+ return new Promise((resolve) => {
446
+ const starter = () => {
447
+ const g = this.gates.get(neuron2.name);
448
+ g.active++;
449
+ const ret = fn();
450
+ if (ret && typeof ret.then === "function") {
451
+ ret.then((cb) => resolve(wrap(cb)));
452
+ } else {
453
+ resolve(wrap(ret));
454
+ }
455
+ };
456
+ gate.waiters.push(starter);
457
+ });
458
+ }
459
+ };
460
+
317
461
  // src/CNS.ts
318
462
  var CNS = class {
319
463
  constructor(neurons, options) {
320
464
  this.neurons = neurons;
321
465
  this.options = options;
322
466
  /**
323
- * Fast lookup: collateralId -> list of (neuron, dendrite) subscribers.
467
+ * Fast lookup: collateralName -> list of (neuron, dendrite) subscribers.
324
468
  * Built once at construction time.
325
469
  */
326
470
  this.subIndex = /* @__PURE__ */ new Map();
327
- this.parentNeuronByCollateralId = /* @__PURE__ */ new Map();
471
+ this.neuronIndex = /* @__PURE__ */ new Map();
472
+ this.dendriteIndex = /* @__PURE__ */ new Map();
473
+ this.collateralIndex = /* @__PURE__ */ new Map();
474
+ this.parentNeuronByCollateralName = /* @__PURE__ */ new Map();
328
475
  /**
329
476
  * Strongly Connected Components of the neuron graph.
330
477
  * Each component is a set of neuron IDs that can reach each other.
@@ -342,29 +489,157 @@ var CNS = class {
342
489
  * Precomputed ancestor sets for each SCC for fast reachability checks
343
490
  */
344
491
  this.sccAncestors = /* @__PURE__ */ new Map();
492
+ /**
493
+ * Global per-neuron concurrency gates shared across all stimulations.
494
+ */
495
+ this.neuronGates = /* @__PURE__ */ new Map();
496
+ /**
497
+ * Global task queue used by stimulation to schedule per-neuron gated work.
498
+ * Does not replace per-stimulation concurrency; only coordinates global per-neuron limits.
499
+ */
500
+ this.globalTaskQueue = new CNSTaskQueue();
501
+ this.instanceNeuronQueue = new CNSInstanceNeuronQueue();
502
+ /**
503
+ * Global response listeners applied to every stimulation.
504
+ */
505
+ this.globalResponseListeners = [];
506
+ this.validateUniqueIdentifiers();
345
507
  this.buildIndexes();
346
508
  if (this.options?.autoCleanupContexts) {
347
509
  this.buildSCC();
348
510
  }
349
511
  }
512
+ /**
513
+ * Validate uniqueness constraints:
514
+ * - neuron names are unique and non-empty
515
+ * - axon collateral types are globally unique (single owner)
516
+ * - the same collateral instance cannot be owned by multiple neurons
517
+ * Throws aggregated error if violations are found.
518
+ */
519
+ validateUniqueIdentifiers() {
520
+ const errors = [];
521
+ const seenNeuronNames = /* @__PURE__ */ new Set();
522
+ for (const neuron2 of this.neurons) {
523
+ const name = neuron2?.name ?? "";
524
+ if (!name || typeof name !== "string" || name.trim().length === 0) {
525
+ errors.push(`Neuron has empty or invalid name: "${name}"`);
526
+ } else if (seenNeuronNames.has(name)) {
527
+ errors.push(`Duplicate neuron name: "${name}"`);
528
+ } else {
529
+ seenNeuronNames.add(name);
530
+ }
531
+ }
532
+ if (errors.length > 0) {
533
+ throw new Error(
534
+ `[CNS] Topology uniqueness validation failed:
535
+ - ` + errors.join(`
536
+ - `)
537
+ );
538
+ }
539
+ }
540
+ addResponseListener(listener) {
541
+ this.globalResponseListeners.push(listener);
542
+ let active = true;
543
+ return () => {
544
+ if (!active) return;
545
+ active = false;
546
+ const idx = this.globalResponseListeners.indexOf(listener);
547
+ if (idx >= 0) this.globalResponseListeners.splice(idx, 1);
548
+ };
549
+ }
550
+ wrapOnResponse(local) {
551
+ if (this.globalResponseListeners.length === 0 && !local) {
552
+ return () => {
553
+ };
554
+ }
555
+ return (r) => {
556
+ try {
557
+ if (local) local(r);
558
+ } finally {
559
+ for (let i = 0; i < this.globalResponseListeners.length; i++) {
560
+ try {
561
+ this.globalResponseListeners[i](r);
562
+ } catch (e) {
563
+ }
564
+ }
565
+ }
566
+ };
567
+ }
568
+ runWithNeuronConcurrency(neuron2, fn) {
569
+ const limit = neuron2.concurrency;
570
+ if (limit === void 0 || limit <= 0) {
571
+ return this.globalTaskQueue.enqueue(fn);
572
+ }
573
+ let gate = this.neuronGates.get(neuron2.name);
574
+ if (!gate) {
575
+ gate = { limit, active: 0, waiters: [] };
576
+ this.neuronGates.set(neuron2.name, gate);
577
+ } else {
578
+ gate.limit = limit;
579
+ }
580
+ const wrap = (cb) => {
581
+ return () => {
582
+ try {
583
+ cb();
584
+ } finally {
585
+ gate.active = Math.max(0, gate.active - 1);
586
+ if (gate.waiters.length > 0) {
587
+ const next = gate.waiters.shift();
588
+ next();
589
+ }
590
+ }
591
+ };
592
+ };
593
+ if (gate.active < gate.limit) {
594
+ gate.active++;
595
+ return this.globalTaskQueue.enqueue(() => {
596
+ const ret = fn();
597
+ if (ret && typeof ret.then === "function") {
598
+ return ret.then((cb) => wrap(cb));
599
+ }
600
+ return wrap(ret);
601
+ });
602
+ }
603
+ return new Promise((resolve) => {
604
+ const starter = () => {
605
+ gate.active++;
606
+ this.globalTaskQueue.enqueue(() => {
607
+ const ret = fn();
608
+ if (ret && typeof ret.then === "function") {
609
+ return ret.then(
610
+ (cb) => wrap(cb)
611
+ );
612
+ }
613
+ return wrap(ret);
614
+ }).then(resolve);
615
+ };
616
+ gate.waiters.push(starter);
617
+ });
618
+ }
350
619
  buildIndexes() {
351
620
  this.subIndex.clear();
352
- this.parentNeuronByCollateralId.clear();
621
+ this.parentNeuronByCollateralName.clear();
353
622
  for (const neuron2 of this.neurons) {
623
+ this.neuronIndex.set(neuron2.name, neuron2);
354
624
  for (const dendrite of neuron2.dendrites) {
355
- const key = dendrite.collateral.id;
625
+ const key = dendrite.collateral.name;
356
626
  const arr = this.subIndex.get(key) ?? [];
357
627
  arr.push({ neuron: neuron2, dendrite });
358
628
  this.subIndex.set(
359
629
  key,
360
630
  arr
361
631
  );
632
+ this.dendriteIndex.set(key, dendrite);
362
633
  }
363
634
  Object.values(neuron2.axon).forEach((collateral2) => {
364
- this.parentNeuronByCollateralId.set(
365
- collateral2.id,
635
+ this.parentNeuronByCollateralName.set(
636
+ collateral2.name,
366
637
  neuron2
367
638
  );
639
+ this.collateralIndex.set(
640
+ collateral2.name,
641
+ collateral2
642
+ );
368
643
  });
369
644
  }
370
645
  }
@@ -373,7 +648,7 @@ var CNS = class {
373
648
  */
374
649
  buildNeuronGraph() {
375
650
  const graph = /* @__PURE__ */ new Map();
376
- const neuronIds = this.neurons.map((n) => n.id);
651
+ const neuronIds = this.neurons.map((n) => n.name);
377
652
  for (const neuronId of neuronIds) {
378
653
  graph.set(neuronId, /* @__PURE__ */ new Set());
379
654
  }
@@ -381,15 +656,15 @@ var CNS = class {
381
656
  const reachableNeurons = /* @__PURE__ */ new Set();
382
657
  for (const axonKey in neuron2.axon) {
383
658
  const collateral2 = neuron2.axon[axonKey];
384
- const collateralId = collateral2.id;
385
- const subscribers = this.subIndex.get(collateralId);
659
+ const collateralName = collateral2.name;
660
+ const subscribers = this.subIndex.get(collateralName);
386
661
  if (subscribers) {
387
662
  for (const { neuron: targetNeuron } of subscribers) {
388
- reachableNeurons.add(targetNeuron.id);
663
+ reachableNeurons.add(targetNeuron.name);
389
664
  }
390
665
  }
391
666
  }
392
- graph.set(neuron2.id, reachableNeurons);
667
+ graph.set(neuron2.name, reachableNeurons);
393
668
  }
394
669
  return graph;
395
670
  }
@@ -398,7 +673,7 @@ var CNS = class {
398
673
  */
399
674
  buildSCC() {
400
675
  const graph = this.buildNeuronGraph();
401
- const neuronIds = this.neurons.map((n) => n.id);
676
+ const neuronIds = this.neurons.map((n) => n.name);
402
677
  const index = /* @__PURE__ */ new Map();
403
678
  const lowlink = /* @__PURE__ */ new Map();
404
679
  const onStack = /* @__PURE__ */ new Set();
@@ -525,23 +800,23 @@ var CNS = class {
525
800
  * Returns true if the neuron is in a strongly connected component with more than one member,
526
801
  * or if it's in a single-member SCC that can reach itself.
527
802
  */
528
- getSCCSetByNeuronId(neuronId) {
529
- const sccIndex = this.neuronToSCC.get(neuronId);
803
+ getSCCSetByNeuronName(neuronName) {
804
+ const sccIndex = this.neuronToSCC.get(neuronName);
530
805
  if (sccIndex === void 0) return;
531
806
  return this.stronglyConnectedComponents[sccIndex];
532
807
  }
533
808
  /**
534
809
  * Get the SCC index for a given neuron ID
535
810
  */
536
- getSccIndexByNeuronId(neuronId) {
537
- return this.neuronToSCC.get(neuronId);
811
+ getSccIndexByNeuronName(neuronName) {
812
+ return this.neuronToSCC.get(neuronName);
538
813
  }
539
814
  /**
540
815
  * Check if a neuron can be guaranteed not to be visited again during the current propagation.
541
816
  * This is the core logic for safe context cleanup.
542
817
  */
543
- canNeuronBeGuaranteedDone(neuronId, activeSccCounts) {
544
- const sccIndex = this.neuronToSCC.get(neuronId);
818
+ canNeuronBeGuaranteedDone(neuronName, activeSccCounts) {
819
+ const sccIndex = this.neuronToSCC.get(neuronName);
545
820
  if (sccIndex === void 0) return true;
546
821
  if (activeSccCounts.get(sccIndex) && activeSccCounts.get(sccIndex) > 0) {
547
822
  return false;
@@ -555,22 +830,35 @@ var CNS = class {
555
830
  }
556
831
  return true;
557
832
  }
558
- getParentNeuronByCollateralId(collateralId) {
559
- return this.parentNeuronByCollateralId.get(collateralId);
833
+ getParentNeuronByCollateralName(collateralName) {
834
+ return this.parentNeuronByCollateralName.get(collateralName);
835
+ }
836
+ getDendrites() {
837
+ return Array.from(this.dendriteIndex.values());
560
838
  }
561
- getSubscribers(collateralId) {
562
- return this.subIndex.get(collateralId) ?? [];
839
+ getCollaterals() {
840
+ return Array.from(this.collateralIndex.values());
841
+ }
842
+ getNeurons() {
843
+ return Array.from(this.neuronIndex.values());
844
+ }
845
+ getSubscribers(collateralName) {
846
+ return this.subIndex.get(collateralName) ?? [];
563
847
  }
564
848
  stimulate(signal, options) {
565
- const stimulation = new CNSStimulation(this, options);
849
+ const wrapped = this.wrapOnResponse(options?.onResponse);
850
+ const stimulation = new CNSStimulation(this, this.instanceNeuronQueue, {
851
+ ...options,
852
+ onResponse: wrapped
853
+ });
566
854
  stimulation.responseToSignal(signal);
567
855
  }
568
856
  };
569
857
 
570
858
  // src/CNSCollateral.ts
571
859
  var CNSCollateral = class {
572
- constructor(id) {
573
- this.id = id;
860
+ constructor(name) {
861
+ this.name = name;
574
862
  }
575
863
  createSignal(payload) {
576
864
  return { collateral: this, payload };
@@ -578,12 +866,30 @@ var CNSCollateral = class {
578
866
  };
579
867
 
580
868
  // src/factory/index.ts
581
- var collateral = (id) => new CNSCollateral(id);
582
- var neuron = (id, axon) => {
869
+ var collateral = (name) => new CNSCollateral(name);
870
+ var neuron = (name, axon) => {
583
871
  const dendrites = [];
584
872
  const api = {
873
+ setConcurrency: (n) => {
874
+ api.concurrency = n;
875
+ return api;
876
+ },
877
+ bind: ((axon2, newDendrites) => {
878
+ for (const key in newDendrites) {
879
+ const value = newDendrites[key];
880
+ const dendrite = typeof value === "function" ? {
881
+ collateral: axon2[key],
882
+ response: value
883
+ } : value;
884
+ dendrites.push(
885
+ dendrite
886
+ );
887
+ }
888
+ return api;
889
+ }),
585
890
  axon,
586
- id,
891
+ name,
892
+ concurrency: void 0,
587
893
  dendrites,
588
894
  dendrite(s) {
589
895
  dendrites.push(
@@ -595,13 +901,14 @@ var neuron = (id, axon) => {
595
901
  return api;
596
902
  };
597
903
  var withCtx = () => ({
598
- neuron: (id, axon) => {
599
- return neuron(id, axon);
904
+ neuron: (name, axon) => {
905
+ return neuron(name, axon);
600
906
  }
601
907
  });
602
908
 
603
909
  exports.CNS = CNS;
604
910
  exports.CNSCollateral = CNSCollateral;
911
+ exports.CNSStimulationContextStore = CNSStimulationContextStore;
605
912
  exports.collateral = collateral;
606
913
  exports.neuron = neuron;
607
914
  exports.withCtx = withCtx;