@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.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.name}" 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,57 @@ 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
+ delete: () => this.ctx.delete(neuron2.name),
243
+ abortSignal: this.options?.abortSignal,
244
+ cns: this.cns
263
245
  }
264
246
  );
265
- } else {
266
- return () => {
267
- this.markNeuronInactive(neuron2.id);
268
- return this.processResponse(
269
- inputSignal,
270
- response
247
+ if (response instanceof Promise) {
248
+ return response.then(
249
+ (signal) => {
250
+ return () => {
251
+ this.markNeuronInactive(neuron2.name);
252
+ return this.processResponse(
253
+ inputSignal,
254
+ signal
255
+ );
256
+ };
257
+ },
258
+ (error) => {
259
+ return () => {
260
+ this.markNeuronInactive(neuron2.name);
261
+ return this.processResponse(
262
+ inputSignal,
263
+ void 0,
264
+ error
265
+ );
266
+ };
267
+ }
271
268
  );
272
- };
273
- }
269
+ } else {
270
+ return () => {
271
+ this.markNeuronInactive(neuron2.name);
272
+ return this.processResponse(
273
+ inputSignal,
274
+ response
275
+ );
276
+ };
277
+ }
278
+ };
279
+ return this.instanceNeuronQueue.run(neuron2, starter);
274
280
  }
275
281
  processResponse(inputSignal, outputSignal, error) {
276
282
  const collateral2 = outputSignal?.collateral;
277
- const ownerNeuron = collateral2 ? this.cns.getParentNeuronByCollateralId(collateral2.id) : void 0;
278
- const subscribers = collateral2 ? this.cns.getSubscribers(collateral2.id) : [];
283
+ const ownerNeuron = collateral2 ? this.cns.getParentNeuronByCollateralName(collateral2.name) : void 0;
284
+ const subscribers = collateral2 ? this.cns.getSubscribers(collateral2.name) : [];
279
285
  const subscriberQueueItems = [];
280
286
  if (collateral2 && !error) {
281
287
  if (ownerNeuron) {
@@ -293,7 +299,8 @@ var CNSStimulation = class {
293
299
  outputSignal,
294
300
  ctx: this.ctx,
295
301
  queueLength: this.queue.length + this.scheduledCount,
296
- hops: this.options?.maxNeuronHops && ownerNeuron ? this.nueronVisitMap?.get(ownerNeuron.id) ?? 0 : void 0,
302
+ stimulationId: this.id,
303
+ hops: this.options?.maxNeuronHops && ownerNeuron ? this.nueronVisitMap?.get(ownerNeuron.name) ?? 0 : void 0,
297
304
  error
298
305
  });
299
306
  for (let i = 0; i < subscriberQueueItems.length; i++) {
@@ -302,27 +309,167 @@ var CNSStimulation = class {
302
309
  }
303
310
  }
304
311
  responseToSignal(signal) {
305
- const ownerNeuron = this.cns.getParentNeuronByCollateralId(
306
- signal.collateral.id
312
+ const ownerNeuron = this.cns.getParentNeuronByCollateralName(
313
+ signal.collateral.name
307
314
  );
308
315
  if (ownerNeuron) {
309
- this.markNeuronActive(ownerNeuron.id);
316
+ this.markNeuronActive(ownerNeuron.name);
310
317
  }
311
318
  this.processResponse(void 0, signal);
312
319
  }
313
320
  };
314
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
+
315
459
  // src/CNS.ts
316
460
  var CNS = class {
317
461
  constructor(neurons, options) {
318
462
  this.neurons = neurons;
319
463
  this.options = options;
320
464
  /**
321
- * Fast lookup: collateralId -> list of (neuron, dendrite) subscribers.
465
+ * Fast lookup: collateralName -> list of (neuron, dendrite) subscribers.
322
466
  * Built once at construction time.
323
467
  */
324
468
  this.subIndex = /* @__PURE__ */ new Map();
325
- this.parentNeuronByCollateralId = /* @__PURE__ */ new Map();
469
+ this.neuronIndex = /* @__PURE__ */ new Map();
470
+ this.dendriteIndex = /* @__PURE__ */ new Map();
471
+ this.collateralIndex = /* @__PURE__ */ new Map();
472
+ this.parentNeuronByCollateralName = /* @__PURE__ */ new Map();
326
473
  /**
327
474
  * Strongly Connected Components of the neuron graph.
328
475
  * Each component is a set of neuron IDs that can reach each other.
@@ -340,29 +487,157 @@ var CNS = class {
340
487
  * Precomputed ancestor sets for each SCC for fast reachability checks
341
488
  */
342
489
  this.sccAncestors = /* @__PURE__ */ new Map();
490
+ /**
491
+ * Global per-neuron concurrency gates shared across all stimulations.
492
+ */
493
+ this.neuronGates = /* @__PURE__ */ new Map();
494
+ /**
495
+ * Global task queue used by stimulation to schedule per-neuron gated work.
496
+ * Does not replace per-stimulation concurrency; only coordinates global per-neuron limits.
497
+ */
498
+ this.globalTaskQueue = new CNSTaskQueue();
499
+ this.instanceNeuronQueue = new CNSInstanceNeuronQueue();
500
+ /**
501
+ * Global response listeners applied to every stimulation.
502
+ */
503
+ this.globalResponseListeners = [];
504
+ this.validateUniqueIdentifiers();
343
505
  this.buildIndexes();
344
506
  if (this.options?.autoCleanupContexts) {
345
507
  this.buildSCC();
346
508
  }
347
509
  }
510
+ /**
511
+ * Validate uniqueness constraints:
512
+ * - neuron names are unique and non-empty
513
+ * - axon collateral types are globally unique (single owner)
514
+ * - the same collateral instance cannot be owned by multiple neurons
515
+ * Throws aggregated error if violations are found.
516
+ */
517
+ validateUniqueIdentifiers() {
518
+ const errors = [];
519
+ const seenNeuronNames = /* @__PURE__ */ new Set();
520
+ for (const neuron2 of this.neurons) {
521
+ const name = neuron2?.name ?? "";
522
+ if (!name || typeof name !== "string" || name.trim().length === 0) {
523
+ errors.push(`Neuron has empty or invalid name: "${name}"`);
524
+ } else if (seenNeuronNames.has(name)) {
525
+ errors.push(`Duplicate neuron name: "${name}"`);
526
+ } else {
527
+ seenNeuronNames.add(name);
528
+ }
529
+ }
530
+ if (errors.length > 0) {
531
+ throw new Error(
532
+ `[CNS] Topology uniqueness validation failed:
533
+ - ` + errors.join(`
534
+ - `)
535
+ );
536
+ }
537
+ }
538
+ addResponseListener(listener) {
539
+ this.globalResponseListeners.push(listener);
540
+ let active = true;
541
+ return () => {
542
+ if (!active) return;
543
+ active = false;
544
+ const idx = this.globalResponseListeners.indexOf(listener);
545
+ if (idx >= 0) this.globalResponseListeners.splice(idx, 1);
546
+ };
547
+ }
548
+ wrapOnResponse(local) {
549
+ if (this.globalResponseListeners.length === 0 && !local) {
550
+ return () => {
551
+ };
552
+ }
553
+ return (r) => {
554
+ try {
555
+ if (local) local(r);
556
+ } finally {
557
+ for (let i = 0; i < this.globalResponseListeners.length; i++) {
558
+ try {
559
+ this.globalResponseListeners[i](r);
560
+ } catch (e) {
561
+ }
562
+ }
563
+ }
564
+ };
565
+ }
566
+ runWithNeuronConcurrency(neuron2, fn) {
567
+ const limit = neuron2.concurrency;
568
+ if (limit === void 0 || limit <= 0) {
569
+ return this.globalTaskQueue.enqueue(fn);
570
+ }
571
+ let gate = this.neuronGates.get(neuron2.name);
572
+ if (!gate) {
573
+ gate = { limit, active: 0, waiters: [] };
574
+ this.neuronGates.set(neuron2.name, gate);
575
+ } else {
576
+ gate.limit = limit;
577
+ }
578
+ const wrap = (cb) => {
579
+ return () => {
580
+ try {
581
+ cb();
582
+ } finally {
583
+ gate.active = Math.max(0, gate.active - 1);
584
+ if (gate.waiters.length > 0) {
585
+ const next = gate.waiters.shift();
586
+ next();
587
+ }
588
+ }
589
+ };
590
+ };
591
+ if (gate.active < gate.limit) {
592
+ gate.active++;
593
+ return this.globalTaskQueue.enqueue(() => {
594
+ const ret = fn();
595
+ if (ret && typeof ret.then === "function") {
596
+ return ret.then((cb) => wrap(cb));
597
+ }
598
+ return wrap(ret);
599
+ });
600
+ }
601
+ return new Promise((resolve) => {
602
+ const starter = () => {
603
+ gate.active++;
604
+ this.globalTaskQueue.enqueue(() => {
605
+ const ret = fn();
606
+ if (ret && typeof ret.then === "function") {
607
+ return ret.then(
608
+ (cb) => wrap(cb)
609
+ );
610
+ }
611
+ return wrap(ret);
612
+ }).then(resolve);
613
+ };
614
+ gate.waiters.push(starter);
615
+ });
616
+ }
348
617
  buildIndexes() {
349
618
  this.subIndex.clear();
350
- this.parentNeuronByCollateralId.clear();
619
+ this.parentNeuronByCollateralName.clear();
351
620
  for (const neuron2 of this.neurons) {
621
+ this.neuronIndex.set(neuron2.name, neuron2);
352
622
  for (const dendrite of neuron2.dendrites) {
353
- const key = dendrite.collateral.id;
623
+ const key = dendrite.collateral.name;
354
624
  const arr = this.subIndex.get(key) ?? [];
355
625
  arr.push({ neuron: neuron2, dendrite });
356
626
  this.subIndex.set(
357
627
  key,
358
628
  arr
359
629
  );
630
+ this.dendriteIndex.set(key, dendrite);
360
631
  }
361
632
  Object.values(neuron2.axon).forEach((collateral2) => {
362
- this.parentNeuronByCollateralId.set(
363
- collateral2.id,
633
+ this.parentNeuronByCollateralName.set(
634
+ collateral2.name,
364
635
  neuron2
365
636
  );
637
+ this.collateralIndex.set(
638
+ collateral2.name,
639
+ collateral2
640
+ );
366
641
  });
367
642
  }
368
643
  }
@@ -371,7 +646,7 @@ var CNS = class {
371
646
  */
372
647
  buildNeuronGraph() {
373
648
  const graph = /* @__PURE__ */ new Map();
374
- const neuronIds = this.neurons.map((n) => n.id);
649
+ const neuronIds = this.neurons.map((n) => n.name);
375
650
  for (const neuronId of neuronIds) {
376
651
  graph.set(neuronId, /* @__PURE__ */ new Set());
377
652
  }
@@ -379,15 +654,15 @@ var CNS = class {
379
654
  const reachableNeurons = /* @__PURE__ */ new Set();
380
655
  for (const axonKey in neuron2.axon) {
381
656
  const collateral2 = neuron2.axon[axonKey];
382
- const collateralId = collateral2.id;
383
- const subscribers = this.subIndex.get(collateralId);
657
+ const collateralName = collateral2.name;
658
+ const subscribers = this.subIndex.get(collateralName);
384
659
  if (subscribers) {
385
660
  for (const { neuron: targetNeuron } of subscribers) {
386
- reachableNeurons.add(targetNeuron.id);
661
+ reachableNeurons.add(targetNeuron.name);
387
662
  }
388
663
  }
389
664
  }
390
- graph.set(neuron2.id, reachableNeurons);
665
+ graph.set(neuron2.name, reachableNeurons);
391
666
  }
392
667
  return graph;
393
668
  }
@@ -396,7 +671,7 @@ var CNS = class {
396
671
  */
397
672
  buildSCC() {
398
673
  const graph = this.buildNeuronGraph();
399
- const neuronIds = this.neurons.map((n) => n.id);
674
+ const neuronIds = this.neurons.map((n) => n.name);
400
675
  const index = /* @__PURE__ */ new Map();
401
676
  const lowlink = /* @__PURE__ */ new Map();
402
677
  const onStack = /* @__PURE__ */ new Set();
@@ -523,23 +798,23 @@ var CNS = class {
523
798
  * Returns true if the neuron is in a strongly connected component with more than one member,
524
799
  * or if it's in a single-member SCC that can reach itself.
525
800
  */
526
- getSCCSetByNeuronId(neuronId) {
527
- const sccIndex = this.neuronToSCC.get(neuronId);
801
+ getSCCSetByNeuronName(neuronName) {
802
+ const sccIndex = this.neuronToSCC.get(neuronName);
528
803
  if (sccIndex === void 0) return;
529
804
  return this.stronglyConnectedComponents[sccIndex];
530
805
  }
531
806
  /**
532
807
  * Get the SCC index for a given neuron ID
533
808
  */
534
- getSccIndexByNeuronId(neuronId) {
535
- return this.neuronToSCC.get(neuronId);
809
+ getSccIndexByNeuronName(neuronName) {
810
+ return this.neuronToSCC.get(neuronName);
536
811
  }
537
812
  /**
538
813
  * Check if a neuron can be guaranteed not to be visited again during the current propagation.
539
814
  * This is the core logic for safe context cleanup.
540
815
  */
541
- canNeuronBeGuaranteedDone(neuronId, activeSccCounts) {
542
- const sccIndex = this.neuronToSCC.get(neuronId);
816
+ canNeuronBeGuaranteedDone(neuronName, activeSccCounts) {
817
+ const sccIndex = this.neuronToSCC.get(neuronName);
543
818
  if (sccIndex === void 0) return true;
544
819
  if (activeSccCounts.get(sccIndex) && activeSccCounts.get(sccIndex) > 0) {
545
820
  return false;
@@ -553,22 +828,35 @@ var CNS = class {
553
828
  }
554
829
  return true;
555
830
  }
556
- getParentNeuronByCollateralId(collateralId) {
557
- return this.parentNeuronByCollateralId.get(collateralId);
831
+ getParentNeuronByCollateralName(collateralName) {
832
+ return this.parentNeuronByCollateralName.get(collateralName);
833
+ }
834
+ getDendrites() {
835
+ return Array.from(this.dendriteIndex.values());
558
836
  }
559
- getSubscribers(collateralId) {
560
- return this.subIndex.get(collateralId) ?? [];
837
+ getCollaterals() {
838
+ return Array.from(this.collateralIndex.values());
839
+ }
840
+ getNeurons() {
841
+ return Array.from(this.neuronIndex.values());
842
+ }
843
+ getSubscribers(collateralName) {
844
+ return this.subIndex.get(collateralName) ?? [];
561
845
  }
562
846
  stimulate(signal, options) {
563
- const stimulation = new CNSStimulation(this, options);
847
+ const wrapped = this.wrapOnResponse(options?.onResponse);
848
+ const stimulation = new CNSStimulation(this, this.instanceNeuronQueue, {
849
+ ...options,
850
+ onResponse: wrapped
851
+ });
564
852
  stimulation.responseToSignal(signal);
565
853
  }
566
854
  };
567
855
 
568
856
  // src/CNSCollateral.ts
569
857
  var CNSCollateral = class {
570
- constructor(id) {
571
- this.id = id;
858
+ constructor(name) {
859
+ this.name = name;
572
860
  }
573
861
  createSignal(payload) {
574
862
  return { collateral: this, payload };
@@ -576,12 +864,30 @@ var CNSCollateral = class {
576
864
  };
577
865
 
578
866
  // src/factory/index.ts
579
- var collateral = (id) => new CNSCollateral(id);
580
- var neuron = (id, axon) => {
867
+ var collateral = (name) => new CNSCollateral(name);
868
+ var neuron = (name, axon) => {
581
869
  const dendrites = [];
582
870
  const api = {
871
+ setConcurrency: (n) => {
872
+ api.concurrency = n;
873
+ return api;
874
+ },
875
+ bind: ((axon2, newDendrites) => {
876
+ for (const key in newDendrites) {
877
+ const value = newDendrites[key];
878
+ const dendrite = typeof value === "function" ? {
879
+ collateral: axon2[key],
880
+ response: value
881
+ } : value;
882
+ dendrites.push(
883
+ dendrite
884
+ );
885
+ }
886
+ return api;
887
+ }),
583
888
  axon,
584
- id,
889
+ name,
890
+ concurrency: void 0,
585
891
  dendrites,
586
892
  dendrite(s) {
587
893
  dendrites.push(
@@ -593,11 +899,11 @@ var neuron = (id, axon) => {
593
899
  return api;
594
900
  };
595
901
  var withCtx = () => ({
596
- neuron: (id, axon) => {
597
- return neuron(id, axon);
902
+ neuron: (name, axon) => {
903
+ return neuron(name, axon);
598
904
  }
599
905
  });
600
906
 
601
- export { CNS, CNSCollateral, collateral, neuron, withCtx };
907
+ export { CNS, CNSCollateral, CNSStimulationContextStore, collateral, neuron, withCtx };
602
908
  //# sourceMappingURL=index.js.map
603
909
  //# sourceMappingURL=index.js.map