@cnstra/core 1.3.1 → 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.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.type}" 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,56 @@ 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
+ abortSignal: this.options?.abortSignal,
245
+ cns: this.cns
265
246
  }
266
247
  );
267
- } else {
268
- return () => {
269
- this.markNeuronInactive(neuron2.id);
270
- return this.processResponse(
271
- inputSignal,
272
- response
248
+ if (response instanceof Promise) {
249
+ return response.then(
250
+ (signal) => {
251
+ return () => {
252
+ this.markNeuronInactive(neuron2.name);
253
+ return this.processResponse(
254
+ inputSignal,
255
+ signal
256
+ );
257
+ };
258
+ },
259
+ (error) => {
260
+ return () => {
261
+ this.markNeuronInactive(neuron2.name);
262
+ return this.processResponse(
263
+ inputSignal,
264
+ void 0,
265
+ error
266
+ );
267
+ };
268
+ }
273
269
  );
274
- };
275
- }
270
+ } else {
271
+ return () => {
272
+ this.markNeuronInactive(neuron2.name);
273
+ return this.processResponse(
274
+ inputSignal,
275
+ response
276
+ );
277
+ };
278
+ }
279
+ };
280
+ return this.instanceNeuronQueue.run(neuron2, starter);
276
281
  }
277
282
  processResponse(inputSignal, outputSignal, error) {
278
283
  const collateral2 = outputSignal?.collateral;
279
- const ownerNeuron = collateral2 ? this.cns.getParentNeuronByCollateralId(collateral2.id) : void 0;
280
- const subscribers = collateral2 ? this.cns.getSubscribers(collateral2.id) : [];
284
+ const ownerNeuron = collateral2 ? this.cns.getParentNeuronByCollateralId(collateral2.type) : void 0;
285
+ const subscribers = collateral2 ? this.cns.getSubscribers(collateral2.type) : [];
281
286
  const subscriberQueueItems = [];
282
287
  if (collateral2 && !error) {
283
288
  if (ownerNeuron) {
@@ -295,7 +300,7 @@ var CNSStimulation = class {
295
300
  outputSignal,
296
301
  ctx: this.ctx,
297
302
  queueLength: this.queue.length + this.scheduledCount,
298
- hops: this.options?.maxNeuronHops && ownerNeuron ? this.nueronVisitMap?.get(ownerNeuron.id) ?? 0 : void 0,
303
+ hops: this.options?.maxNeuronHops && ownerNeuron ? this.nueronVisitMap?.get(ownerNeuron.name) ?? 0 : void 0,
299
304
  error
300
305
  });
301
306
  for (let i = 0; i < subscriberQueueItems.length; i++) {
@@ -305,15 +310,152 @@ var CNSStimulation = class {
305
310
  }
306
311
  responseToSignal(signal) {
307
312
  const ownerNeuron = this.cns.getParentNeuronByCollateralId(
308
- signal.collateral.id
313
+ signal.collateral.type
309
314
  );
310
315
  if (ownerNeuron) {
311
- this.markNeuronActive(ownerNeuron.id);
316
+ this.markNeuronActive(ownerNeuron.name);
312
317
  }
313
318
  this.processResponse(void 0, signal);
314
319
  }
315
320
  };
316
321
 
322
+ // src/CNSTaskQueue.ts
323
+ var CNSTaskQueue = class {
324
+ constructor(concurrency) {
325
+ this.concurrency = concurrency;
326
+ this.pumping = false;
327
+ this.needsPump = false;
328
+ this.activeOperations = 0;
329
+ this.items = [];
330
+ }
331
+ get canStartOperation() {
332
+ const limit = this.concurrency ?? Infinity;
333
+ return this.activeOperations < limit;
334
+ }
335
+ enqueue(task) {
336
+ return new Promise((resolve, reject) => {
337
+ this.items.push({ task, resolve, reject });
338
+ if (!this.pumping) this.pump();
339
+ else this.needsPump = true;
340
+ });
341
+ }
342
+ pump() {
343
+ if (this.pumping) {
344
+ this.needsPump = true;
345
+ return;
346
+ }
347
+ this.pumping = true;
348
+ while (this.canStartOperation && this.items.length > 0) {
349
+ const { task, resolve, reject } = this.items.shift();
350
+ this.activeOperations++;
351
+ try {
352
+ const ret = task();
353
+ if (ret && typeof ret.then === "function") {
354
+ ret.then(
355
+ (cb) => {
356
+ try {
357
+ if (typeof cb === "function") cb();
358
+ resolve();
359
+ } finally {
360
+ this.activeOperations--;
361
+ if (this.items.length > 0 && this.canStartOperation) {
362
+ if (this.pumping) this.needsPump = true;
363
+ else this.pump();
364
+ }
365
+ }
366
+ },
367
+ (err) => {
368
+ try {
369
+ reject(err);
370
+ } finally {
371
+ this.activeOperations--;
372
+ if (this.items.length > 0 && this.canStartOperation) {
373
+ if (this.pumping) this.needsPump = true;
374
+ else this.pump();
375
+ }
376
+ }
377
+ }
378
+ );
379
+ break;
380
+ } else {
381
+ try {
382
+ if (typeof ret === "function") ret();
383
+ resolve();
384
+ } finally {
385
+ this.activeOperations--;
386
+ }
387
+ }
388
+ } catch (e) {
389
+ try {
390
+ reject(e);
391
+ } finally {
392
+ this.activeOperations--;
393
+ }
394
+ }
395
+ }
396
+ this.pumping = false;
397
+ if (this.needsPump) {
398
+ this.needsPump = false;
399
+ this.pump();
400
+ }
401
+ }
402
+ };
403
+
404
+ // src/CNSInstanceNeuronQueue.ts
405
+ var CNSInstanceNeuronQueue = class {
406
+ constructor() {
407
+ this.gates = /* @__PURE__ */ new Map();
408
+ }
409
+ run(neuron2, fn) {
410
+ const limit = neuron2?.concurrency;
411
+ if (limit === void 0 || limit <= 0) {
412
+ return fn();
413
+ }
414
+ let gate = this.gates.get(neuron2.name);
415
+ if (!gate) {
416
+ gate = { limit, active: 0, waiters: [] };
417
+ this.gates.set(neuron2.name, gate);
418
+ } else {
419
+ gate.limit = limit;
420
+ }
421
+ const wrap = (cb) => {
422
+ return () => {
423
+ try {
424
+ cb();
425
+ } finally {
426
+ const g = this.gates.get(neuron2.name);
427
+ g.active = Math.max(0, g.active - 1);
428
+ if (g.waiters.length > 0) {
429
+ const next = g.waiters.shift();
430
+ next();
431
+ }
432
+ }
433
+ };
434
+ };
435
+ if (gate.active < gate.limit) {
436
+ gate.active++;
437
+ const ret = fn();
438
+ if (ret && typeof ret.then === "function") {
439
+ return ret.then((cb) => wrap(cb));
440
+ }
441
+ return wrap(ret);
442
+ }
443
+ return new Promise((resolve) => {
444
+ const starter = () => {
445
+ const g = this.gates.get(neuron2.name);
446
+ g.active++;
447
+ const ret = fn();
448
+ if (ret && typeof ret.then === "function") {
449
+ ret.then((cb) => resolve(wrap(cb)));
450
+ } else {
451
+ resolve(wrap(ret));
452
+ }
453
+ };
454
+ gate.waiters.push(starter);
455
+ });
456
+ }
457
+ };
458
+
317
459
  // src/CNS.ts
318
460
  var CNS = class {
319
461
  constructor(neurons, options) {
@@ -342,17 +484,139 @@ var CNS = class {
342
484
  * Precomputed ancestor sets for each SCC for fast reachability checks
343
485
  */
344
486
  this.sccAncestors = /* @__PURE__ */ new Map();
487
+ /**
488
+ * Global per-neuron concurrency gates shared across all stimulations.
489
+ */
490
+ this.neuronGates = /* @__PURE__ */ new Map();
491
+ /**
492
+ * Global task queue used by stimulation to schedule per-neuron gated work.
493
+ * Does not replace per-stimulation concurrency; only coordinates global per-neuron limits.
494
+ */
495
+ this.globalTaskQueue = new CNSTaskQueue();
496
+ this.instanceNeuronQueue = new CNSInstanceNeuronQueue();
497
+ /**
498
+ * Global response listeners applied to every stimulation.
499
+ */
500
+ this.globalResponseListeners = [];
501
+ this.validateUniqueIdentifiers();
345
502
  this.buildIndexes();
346
503
  if (this.options?.autoCleanupContexts) {
347
504
  this.buildSCC();
348
505
  }
349
506
  }
507
+ /**
508
+ * Validate uniqueness constraints:
509
+ * - neuron names are unique and non-empty
510
+ * - axon collateral types are globally unique (single owner)
511
+ * - the same collateral instance cannot be owned by multiple neurons
512
+ * Throws aggregated error if violations are found.
513
+ */
514
+ validateUniqueIdentifiers() {
515
+ const errors = [];
516
+ const seenNeuronNames = /* @__PURE__ */ new Set();
517
+ for (const neuron2 of this.neurons) {
518
+ const name = neuron2?.name ?? "";
519
+ if (!name || typeof name !== "string" || name.trim().length === 0) {
520
+ errors.push(`Neuron has empty or invalid name: "${name}"`);
521
+ } else if (seenNeuronNames.has(name)) {
522
+ errors.push(`Duplicate neuron name: "${name}"`);
523
+ } else {
524
+ seenNeuronNames.add(name);
525
+ }
526
+ }
527
+ if (errors.length > 0) {
528
+ throw new Error(
529
+ `[CNS] Topology uniqueness validation failed:
530
+ - ` + errors.join(`
531
+ - `)
532
+ );
533
+ }
534
+ }
535
+ addResponseListener(listener) {
536
+ this.globalResponseListeners.push(listener);
537
+ let active = true;
538
+ return () => {
539
+ if (!active) return;
540
+ active = false;
541
+ const idx = this.globalResponseListeners.indexOf(listener);
542
+ if (idx >= 0) this.globalResponseListeners.splice(idx, 1);
543
+ };
544
+ }
545
+ wrapOnResponse(local) {
546
+ if (this.globalResponseListeners.length === 0 && !local) {
547
+ return () => {
548
+ };
549
+ }
550
+ return (r) => {
551
+ try {
552
+ if (local) local(r);
553
+ } finally {
554
+ for (let i = 0; i < this.globalResponseListeners.length; i++) {
555
+ try {
556
+ this.globalResponseListeners[i](r);
557
+ } catch (e) {
558
+ }
559
+ }
560
+ }
561
+ };
562
+ }
563
+ runWithNeuronConcurrency(neuron2, fn) {
564
+ const limit = neuron2.concurrency;
565
+ if (limit === void 0 || limit <= 0) {
566
+ return this.globalTaskQueue.enqueue(fn);
567
+ }
568
+ let gate = this.neuronGates.get(neuron2.name);
569
+ if (!gate) {
570
+ gate = { limit, active: 0, waiters: [] };
571
+ this.neuronGates.set(neuron2.name, gate);
572
+ } else {
573
+ gate.limit = limit;
574
+ }
575
+ const wrap = (cb) => {
576
+ return () => {
577
+ try {
578
+ cb();
579
+ } finally {
580
+ gate.active = Math.max(0, gate.active - 1);
581
+ if (gate.waiters.length > 0) {
582
+ const next = gate.waiters.shift();
583
+ next();
584
+ }
585
+ }
586
+ };
587
+ };
588
+ if (gate.active < gate.limit) {
589
+ gate.active++;
590
+ return this.globalTaskQueue.enqueue(() => {
591
+ const ret = fn();
592
+ if (ret && typeof ret.then === "function") {
593
+ return ret.then((cb) => wrap(cb));
594
+ }
595
+ return wrap(ret);
596
+ });
597
+ }
598
+ return new Promise((resolve) => {
599
+ const starter = () => {
600
+ gate.active++;
601
+ this.globalTaskQueue.enqueue(() => {
602
+ const ret = fn();
603
+ if (ret && typeof ret.then === "function") {
604
+ return ret.then(
605
+ (cb) => wrap(cb)
606
+ );
607
+ }
608
+ return wrap(ret);
609
+ }).then(resolve);
610
+ };
611
+ gate.waiters.push(starter);
612
+ });
613
+ }
350
614
  buildIndexes() {
351
615
  this.subIndex.clear();
352
616
  this.parentNeuronByCollateralId.clear();
353
617
  for (const neuron2 of this.neurons) {
354
618
  for (const dendrite of neuron2.dendrites) {
355
- const key = dendrite.collateral.id;
619
+ const key = dendrite.collateral.type;
356
620
  const arr = this.subIndex.get(key) ?? [];
357
621
  arr.push({ neuron: neuron2, dendrite });
358
622
  this.subIndex.set(
@@ -362,7 +626,7 @@ var CNS = class {
362
626
  }
363
627
  Object.values(neuron2.axon).forEach((collateral2) => {
364
628
  this.parentNeuronByCollateralId.set(
365
- collateral2.id,
629
+ collateral2.type,
366
630
  neuron2
367
631
  );
368
632
  });
@@ -373,7 +637,7 @@ var CNS = class {
373
637
  */
374
638
  buildNeuronGraph() {
375
639
  const graph = /* @__PURE__ */ new Map();
376
- const neuronIds = this.neurons.map((n) => n.id);
640
+ const neuronIds = this.neurons.map((n) => n.name);
377
641
  for (const neuronId of neuronIds) {
378
642
  graph.set(neuronId, /* @__PURE__ */ new Set());
379
643
  }
@@ -381,15 +645,15 @@ var CNS = class {
381
645
  const reachableNeurons = /* @__PURE__ */ new Set();
382
646
  for (const axonKey in neuron2.axon) {
383
647
  const collateral2 = neuron2.axon[axonKey];
384
- const collateralId = collateral2.id;
385
- const subscribers = this.subIndex.get(collateralId);
648
+ const collateralType = collateral2.type;
649
+ const subscribers = this.subIndex.get(collateralType);
386
650
  if (subscribers) {
387
651
  for (const { neuron: targetNeuron } of subscribers) {
388
- reachableNeurons.add(targetNeuron.id);
652
+ reachableNeurons.add(targetNeuron.name);
389
653
  }
390
654
  }
391
655
  }
392
- graph.set(neuron2.id, reachableNeurons);
656
+ graph.set(neuron2.name, reachableNeurons);
393
657
  }
394
658
  return graph;
395
659
  }
@@ -398,7 +662,7 @@ var CNS = class {
398
662
  */
399
663
  buildSCC() {
400
664
  const graph = this.buildNeuronGraph();
401
- const neuronIds = this.neurons.map((n) => n.id);
665
+ const neuronIds = this.neurons.map((n) => n.name);
402
666
  const index = /* @__PURE__ */ new Map();
403
667
  const lowlink = /* @__PURE__ */ new Map();
404
668
  const onStack = /* @__PURE__ */ new Set();
@@ -525,23 +789,23 @@ var CNS = class {
525
789
  * Returns true if the neuron is in a strongly connected component with more than one member,
526
790
  * or if it's in a single-member SCC that can reach itself.
527
791
  */
528
- getSCCSetByNeuronId(neuronId) {
529
- const sccIndex = this.neuronToSCC.get(neuronId);
792
+ getSCCSetByNeuronName(neuronName) {
793
+ const sccIndex = this.neuronToSCC.get(neuronName);
530
794
  if (sccIndex === void 0) return;
531
795
  return this.stronglyConnectedComponents[sccIndex];
532
796
  }
533
797
  /**
534
798
  * Get the SCC index for a given neuron ID
535
799
  */
536
- getSccIndexByNeuronId(neuronId) {
537
- return this.neuronToSCC.get(neuronId);
800
+ getSccIndexByNeuronName(neuronName) {
801
+ return this.neuronToSCC.get(neuronName);
538
802
  }
539
803
  /**
540
804
  * Check if a neuron can be guaranteed not to be visited again during the current propagation.
541
805
  * This is the core logic for safe context cleanup.
542
806
  */
543
- canNeuronBeGuaranteedDone(neuronId, activeSccCounts) {
544
- const sccIndex = this.neuronToSCC.get(neuronId);
807
+ canNeuronBeGuaranteedDone(neuronName, activeSccCounts) {
808
+ const sccIndex = this.neuronToSCC.get(neuronName);
545
809
  if (sccIndex === void 0) return true;
546
810
  if (activeSccCounts.get(sccIndex) && activeSccCounts.get(sccIndex) > 0) {
547
811
  return false;
@@ -555,22 +819,26 @@ var CNS = class {
555
819
  }
556
820
  return true;
557
821
  }
558
- getParentNeuronByCollateralId(collateralId) {
559
- return this.parentNeuronByCollateralId.get(collateralId);
822
+ getParentNeuronByCollateralId(collateralType) {
823
+ return this.parentNeuronByCollateralId.get(collateralType);
560
824
  }
561
- getSubscribers(collateralId) {
562
- return this.subIndex.get(collateralId) ?? [];
825
+ getSubscribers(collateralType) {
826
+ return this.subIndex.get(collateralType) ?? [];
563
827
  }
564
828
  stimulate(signal, options) {
565
- const stimulation = new CNSStimulation(this, options);
829
+ const wrapped = this.wrapOnResponse(options?.onResponse);
830
+ const stimulation = new CNSStimulation(this, this.instanceNeuronQueue, {
831
+ ...options,
832
+ onResponse: wrapped
833
+ });
566
834
  stimulation.responseToSignal(signal);
567
835
  }
568
836
  };
569
837
 
570
838
  // src/CNSCollateral.ts
571
839
  var CNSCollateral = class {
572
- constructor(id) {
573
- this.id = id;
840
+ constructor(type) {
841
+ this.type = type;
574
842
  }
575
843
  createSignal(payload) {
576
844
  return { collateral: this, payload };
@@ -578,12 +846,30 @@ var CNSCollateral = class {
578
846
  };
579
847
 
580
848
  // src/factory/index.ts
581
- var collateral = (id) => new CNSCollateral(id);
582
- var neuron = (id, axon) => {
849
+ var collateral = (type) => new CNSCollateral(type);
850
+ var neuron = (name, axon) => {
583
851
  const dendrites = [];
584
852
  const api = {
853
+ setConcurrency: (n) => {
854
+ api.concurrency = n;
855
+ return api;
856
+ },
857
+ bind: ((axon2, newDendrites) => {
858
+ for (const key in newDendrites) {
859
+ const value = newDendrites[key];
860
+ const dendrite = typeof value === "function" ? {
861
+ collateral: axon2[key],
862
+ response: value
863
+ } : value;
864
+ dendrites.push(
865
+ dendrite
866
+ );
867
+ }
868
+ return api;
869
+ }),
585
870
  axon,
586
- id,
871
+ name,
872
+ concurrency: void 0,
587
873
  dendrites,
588
874
  dendrite(s) {
589
875
  dendrites.push(
@@ -595,8 +881,29 @@ var neuron = (id, axon) => {
595
881
  return api;
596
882
  };
597
883
  var withCtx = () => ({
598
- neuron: (id, axon) => {
599
- return neuron(id, axon);
884
+ neuron: (name, axon) => {
885
+ return neuron(name, axon);
886
+ }
887
+ });
888
+ var fa = {
889
+ test1: new CNSCollateral("test1"),
890
+ test2: new CNSCollateral("test2")
891
+ };
892
+ var fb = {
893
+ test4: collateral("test4"),
894
+ test5: collateral("test5")
895
+ };
896
+ withCtx().neuron("test", {
897
+ test1: fa.test1,
898
+ test2: fa.test2
899
+ }).bind(fb, {
900
+ test4: (payload, axon) => {
901
+ const ok = payload.test44;
902
+ return axon.test1.createSignal(ok);
903
+ },
904
+ test5: (payload, axon) => {
905
+ const ok2 = payload.test44;
906
+ return axon.test2.createSignal(ok2);
600
907
  }
601
908
  });
602
909