@cnstra/core 1.3.0 → 1.4.0

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