@cnstra/core 2.5.0 → 3.0.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 (68) hide show
  1. package/README.md +3 -4
  2. package/dist/CNS.d.ts +16 -12
  3. package/dist/CNS.d.ts.map +1 -1
  4. package/dist/CNSCollateral.d.ts +2 -2
  5. package/dist/CNSCollateral.d.ts.map +1 -1
  6. package/dist/CNSNetwork.d.ts +11 -10
  7. package/dist/CNSNetwork.d.ts.map +1 -1
  8. package/dist/CNSStimulation.d.ts +21 -48
  9. package/dist/CNSStimulation.d.ts.map +1 -1
  10. package/dist/factory/index.d.ts +45 -105
  11. package/dist/factory/index.d.ts.map +1 -1
  12. package/dist/index.cjs +44 -247
  13. package/dist/index.cjs.map +1 -1
  14. package/dist/index.d.ts +2 -18
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +45 -243
  17. package/dist/index.js.map +1 -1
  18. package/dist/utils/CNSStimulationGate.d.ts +9 -9
  19. package/dist/utils/CNSStimulationGate.d.ts.map +1 -1
  20. package/package.json +5 -2
  21. package/dist/CNSPersistOptionsRegistry.d.ts +0 -28
  22. package/dist/CNSPersistOptionsRegistry.d.ts.map +0 -1
  23. package/dist/CNSStimulationContextStore.d.ts +0 -11
  24. package/dist/CNSStimulationContextStore.d.ts.map +0 -1
  25. package/dist/interfaces/ICNS.d.ts +0 -27
  26. package/dist/interfaces/ICNS.d.ts.map +0 -1
  27. package/dist/interfaces/ICNSCollateral.d.ts +0 -6
  28. package/dist/interfaces/ICNSCollateral.d.ts.map +0 -1
  29. package/dist/interfaces/ICNSStimulationContextStore.d.ts +0 -8
  30. package/dist/interfaces/ICNSStimulationContextStore.d.ts.map +0 -1
  31. package/dist/types/TCNSAfferentPath.d.ts +0 -4
  32. package/dist/types/TCNSAfferentPath.d.ts.map +0 -1
  33. package/dist/types/TCNSAxon.d.ts +0 -26
  34. package/dist/types/TCNSAxon.d.ts.map +0 -1
  35. package/dist/types/TCNSDendrite.d.ts +0 -9
  36. package/dist/types/TCNSDendrite.d.ts.map +0 -1
  37. package/dist/types/TCNSHandlerContext.d.ts +0 -24
  38. package/dist/types/TCNSHandlerContext.d.ts.map +0 -1
  39. package/dist/types/TCNSLocalContextValueStore.d.ts +0 -6
  40. package/dist/types/TCNSLocalContextValueStore.d.ts.map +0 -1
  41. package/dist/types/TCNSModality.d.ts +0 -5
  42. package/dist/types/TCNSModality.d.ts.map +0 -1
  43. package/dist/types/TCNSNeuron.d.ts +0 -12
  44. package/dist/types/TCNSNeuron.d.ts.map +0 -1
  45. package/dist/types/TCNSNeuronActivationTask.d.ts +0 -8
  46. package/dist/types/TCNSNeuronActivationTask.d.ts.map +0 -1
  47. package/dist/types/TCNSNeuronActivationTaskFailure.d.ts +0 -7
  48. package/dist/types/TCNSNeuronActivationTaskFailure.d.ts.map +0 -1
  49. package/dist/types/TCNSNeuronResponseReturn.d.ts +0 -9
  50. package/dist/types/TCNSNeuronResponseReturn.d.ts.map +0 -1
  51. package/dist/types/TCNSOptions.d.ts +0 -11
  52. package/dist/types/TCNSOptions.d.ts.map +0 -1
  53. package/dist/types/TCNSPersist.d.ts +0 -16
  54. package/dist/types/TCNSPersist.d.ts.map +0 -1
  55. package/dist/types/TCNSSignal.d.ts +0 -6
  56. package/dist/types/TCNSSignal.d.ts.map +0 -1
  57. package/dist/types/TCNSStimulationDrain.d.ts +0 -37
  58. package/dist/types/TCNSStimulationDrain.d.ts.map +0 -1
  59. package/dist/types/TCNSStimulationOptions.d.ts +0 -32
  60. package/dist/types/TCNSStimulationOptions.d.ts.map +0 -1
  61. package/dist/types/TCNSStimulationQueueItem.d.ts +0 -11
  62. package/dist/types/TCNSStimulationQueueItem.d.ts.map +0 -1
  63. package/dist/types/TCNSStimulationResponse.d.ts +0 -57
  64. package/dist/types/TCNSStimulationResponse.d.ts.map +0 -1
  65. package/dist/types/TCNSStimulationSerializedContextValue.d.ts +0 -4
  66. package/dist/types/TCNSStimulationSerializedContextValue.d.ts.map +0 -1
  67. package/dist/types/TCNSSubscriber.d.ts +0 -7
  68. package/dist/types/TCNSSubscriber.d.ts.map +0 -1
package/dist/index.d.ts CHANGED
@@ -1,23 +1,7 @@
1
1
  export { CNS } from './CNS';
2
- export { CNSPersistOptionsRegistry } from './CNSPersistOptionsRegistry';
3
2
  export { CNSCollateral } from './CNSCollateral';
4
- export { CNSStimulationContextStore } from './CNSStimulationContextStore';
5
3
  export { CNSStimulationGate } from './utils/CNSStimulationGate';
6
- export type { ICNS } from './interfaces/ICNS';
7
- export type { TCNSNeuron } from './types/TCNSNeuron';
8
- export type { TCNSAxon } from './types/TCNSAxon';
9
- export type { TCNSDendrite } from './types/TCNSDendrite';
10
- export type { TCNSHandlerContext } from './types/TCNSHandlerContext';
11
- export type { TCNSSignal } from './types/TCNSSignal';
12
- export type { TCNSNeuronActivationTask } from './types/TCNSNeuronActivationTask';
13
- export type { TCNSStimulationOptions } from './types/TCNSStimulationOptions';
14
- export type { TCNSStimulationResponse } from './types/TCNSStimulationResponse';
15
- export type { TCNSStimulationDrain } from './types/TCNSStimulationDrain';
16
- export type { TCNSModality } from './types/TCNSModality';
17
- export type { TCNSAfferentPath } from './types/TCNSAfferentPath';
18
- export type { TCNSNeuronActivationTaskFailure } from './types/TCNSNeuronActivationTaskFailure';
19
- export type { TCNSNeuronPersistOptions, TCNSCollateralPersistOptions, TCNSStimulationPersistOptions } from './types/TCNSPersist';
20
- export type { TCNSStimulationGateOptions, TCNSStimulationGateSignal } from './utils/CNSStimulationGate';
21
- export { collateral, neuron, neuronFactory, withCtx, withGlobal, afferentPath, modality, createPersistRegistry, createCNS, } from './factory/index';
4
+ export type { TCNSStimulationGateOptions } from './utils/CNSStimulationGate';
5
+ export { collateral, neuron, neuronFactory, withGlobal, afferentPath, modality, } from './factory/index';
22
6
  export type { TNeuronFactory } from './factory/index';
23
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AAC5B,OAAO,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAC;AACxE,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,0BAA0B,EAAE,MAAM,8BAA8B,CAAC;AAC1E,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAEhE,YAAY,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAE9C,YAAY,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACrD,YAAY,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACjD,YAAY,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACzD,YAAY,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AACrE,YAAY,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACrD,YAAY,EAAE,wBAAwB,EAAE,MAAM,kCAAkC,CAAC;AACjF,YAAY,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AAC7E,YAAY,EAAE,uBAAuB,EAAE,MAAM,iCAAiC,CAAC;AAC/E,YAAY,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AACzE,YAAY,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACzD,YAAY,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACjE,YAAY,EAAE,+BAA+B,EAAE,MAAM,yCAAyC,CAAC;AAC/F,YAAY,EAAE,wBAAwB,EAAE,4BAA4B,EAAE,6BAA6B,EAAE,MAAM,qBAAqB,CAAC;AACjI,YAAY,EAAE,0BAA0B,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AAGxG,OAAO,EACH,UAAU,EACV,MAAM,EACN,aAAa,EACb,OAAO,EACP,UAAU,EACV,YAAY,EACZ,QAAQ,EACR,qBAAqB,EACrB,SAAS,GACZ,MAAM,iBAAiB,CAAC;AACzB,YAAY,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AAG5B,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAKhE,YAAY,EAAE,0BAA0B,EAAE,MAAM,4BAA4B,CAAC;AAG7E,OAAO,EACH,UAAU,EACV,MAAM,EACN,aAAa,EACb,UAAU,EACV,YAAY,EACZ,QAAQ,GACX,MAAM,iBAAiB,CAAC;AACzB,YAAY,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC"}
package/dist/index.js CHANGED
@@ -1,56 +1,28 @@
1
- // src/CNSStimulationContextStore.ts
2
- var CNSStimulationContextStore = class {
3
- constructor(ctx) {
4
- this.ctx = ctx;
5
- }
6
- get(key) {
7
- return this.ctx?.get(key);
8
- }
9
- set(key, value) {
10
- (this.ctx ?? (this.ctx = /* @__PURE__ */ new Map())).set(key, value);
11
- }
12
- getAll() {
13
- return this.ctx ? new Map(this.ctx) : /* @__PURE__ */ new Map();
14
- }
15
- setAll(values) {
16
- const ctx = this.ctx ?? (this.ctx = /* @__PURE__ */ new Map());
17
- ctx.clear();
18
- for (const [key, value] of values) ctx.set(key, value);
19
- }
20
- delete(key) {
21
- this.ctx?.delete(key);
22
- }
23
- };
24
-
25
1
  // src/CNSStimulation.ts
26
2
  var TASK_DENDRITE = /* @__PURE__ */ Symbol("cnstra.taskDendrite");
27
3
  var CNSDendriteContext = class {
28
- constructor(stimulation, neuron2, abortSignal, cns) {
4
+ constructor(stimulation, abortSignal, cns) {
29
5
  this.stimulation = stimulation;
30
- this.neuron = neuron2;
31
6
  this.abortSignal = abortSignal;
32
7
  this.cns = cns;
33
8
  }
34
- // Prototype getter, not a constructor field: the per-activation allocation is
35
- // unchanged (still 4 fields) and you only pay a single `cns.global` read on the
36
- // dendrites that actually reach for it. Value getter, so `{ global }` destructuring is safe.
9
+ // Prototype getter, not a constructor field: you only pay a single `cns.global`
10
+ // read on the dendrites that actually reach for it. Value getter, so
11
+ // `{ global }` destructuring is safe.
37
12
  get global() {
38
13
  return this.cns.global;
39
14
  }
40
- get() {
41
- return this.stimulation.getContext().get(this.neuron);
42
- }
43
- set(value) {
44
- this.stimulation.getContext().set(this.neuron, value);
45
- }
46
- delete() {
47
- this.stimulation.getContext().delete(this.neuron);
15
+ // The caller-supplied per-stimulation context (`stimulate(sig, {stimulationContext})`),
16
+ // the same value on every hop — the environment-agnostic way to correlate logs to a
17
+ // run (pass `{ stimulationId, attemptNumber }`). Prototype getter like `global`:
18
+ // zero extra allocation, read only where a handler reaches for it.
19
+ get stimulationContext() {
20
+ return this.stimulation.options?.stimulationContext;
48
21
  }
49
22
  };
50
23
  var CNSStimulationResponseImpl = class {
51
- constructor(stimulation, store, inputSignal, outputSignal, queueLength, pendingActivations, activeActivations, hops, error) {
24
+ constructor(stimulation, inputSignal, outputSignal, queueLength, pendingActivations, activeActivations, hops, error, neuron2) {
52
25
  this.stimulation = stimulation;
53
- this.store = store;
54
26
  this.inputSignal = inputSignal;
55
27
  this.outputSignal = outputSignal;
56
28
  this.queueLength = queueLength;
@@ -58,9 +30,7 @@ var CNSStimulationResponseImpl = class {
58
30
  this.activeActivations = activeActivations;
59
31
  this.hops = hops;
60
32
  this.error = error;
61
- }
62
- get contextValue() {
63
- return this.store ? this.store.getAll() : /* @__PURE__ */ new Map();
33
+ this.neuron = neuron2;
64
34
  }
65
35
  };
66
36
  var CNSStimulation = class {
@@ -89,15 +59,11 @@ var CNSStimulation = class {
89
59
  this.isCompleted = false;
90
60
  this.completionRejected = false;
91
61
  this.onDrain = onDrain;
92
- this.ctxStore = options?.ctx;
93
62
  this.instanceNeuronQueue = instanceNeuronQueue;
94
63
  this.onResponse = onResponse;
95
64
  if (this.options?.maxNeuronHops) {
96
65
  this.neuronVisitMap = /* @__PURE__ */ new Map();
97
66
  }
98
- if (this.autoCleanupContextsEnabled) {
99
- this.activeSccCounts = /* @__PURE__ */ new Map();
100
- }
101
67
  }
102
68
  pushFailedTask(failure) {
103
69
  (this.failedTasks ?? (this.failedTasks = [])).push(failure);
@@ -111,10 +77,6 @@ var CNSStimulation = class {
111
77
  this.completionError = error;
112
78
  this.rejectCompleted?.(error);
113
79
  }
114
- /** Lazily-created neuron context store (see {@link ctxStore}). */
115
- get ctx() {
116
- return this.ctxStore ?? (this.ctxStore = new CNSStimulationContextStore());
117
- }
118
80
  // ----- Inlined activation queue / pump -----
119
81
  get canStartOperation() {
120
82
  const limit = this.options?.concurrency ?? Infinity;
@@ -332,10 +294,17 @@ var CNSStimulation = class {
332
294
  return this.failedTasks ? [...this.failedTasks] : [];
333
295
  }
334
296
  /**
335
- * Get the context store for this stimulation
297
+ * The current outstanding frontier — every task that has not completed:
298
+ * queued, active, and pending (awaiting an async onResponse), plus any
299
+ * failed/aborted tasks. This is the set to persist for resume: re-activating
300
+ * it re-runs exactly the not-yet-done work. Safe to call at any point during
301
+ * a run (for progress) or after it fails.
336
302
  */
337
- getContext() {
338
- return this.ctx;
303
+ getOutstandingTasks() {
304
+ return [
305
+ ...this.getAllActivationTasks(),
306
+ ...this.getFailedTasks().map((f) => f.task)
307
+ ];
339
308
  }
340
309
  /**
341
310
  * Enqueue activation tasks directly into the stimulation queue
@@ -354,67 +323,6 @@ var CNSStimulation = class {
354
323
  get concurrencyEnabled() {
355
324
  return this.options?.concurrency !== void 0 && this.options?.concurrency > 0;
356
325
  }
357
- get autoCleanupContextsEnabled() {
358
- return this.cns?.options?.autoCleanupContexts ?? false;
359
- }
360
- /**
361
- * Mark a neuron as active (being processed)
362
- */
363
- markNeuronActive(neuron2) {
364
- if (!this.autoCleanupContextsEnabled) return;
365
- this.incrementSccCount(neuron2);
366
- }
367
- /**
368
- * Mark a neuron as inactive (finished processing)
369
- */
370
- markNeuronInactive(neuron2) {
371
- if (!this.autoCleanupContextsEnabled) return;
372
- this.decrementSccCount(neuron2);
373
- }
374
- /**
375
- * Increment the active count for the SCC containing this neuron
376
- */
377
- incrementSccCount(neuron2) {
378
- const counts = this.activeSccCounts;
379
- if (!counts) return;
380
- const sccIndex = this.cns.network.getSccIndexByNeuron(neuron2);
381
- if (sccIndex === void 0) return;
382
- const currentCount = counts.get(sccIndex) || 0;
383
- counts.set(sccIndex, currentCount + 1);
384
- }
385
- /**
386
- * Decrement the active count for the SCC containing this neuron
387
- */
388
- decrementSccCount(neuron2) {
389
- const counts = this.activeSccCounts;
390
- if (!counts) return;
391
- const sccIndex = this.cns.network.getSccIndexByNeuron(neuron2);
392
- if (sccIndex === void 0) return;
393
- const currentCount = counts.get(sccIndex) || 0;
394
- const nextCount = Math.max(0, currentCount - 1);
395
- counts.set(sccIndex, nextCount);
396
- if (currentCount === 0) {
397
- console.warn(
398
- `[CNSStimulation] Attempting to decrement SCC count below 0 for neuron`
399
- );
400
- }
401
- }
402
- /**
403
- * Check if a neuron can be guaranteed not to be visited again
404
- */
405
- canNeuronBeGuaranteedDone(neuron2) {
406
- if (!this.autoCleanupContextsEnabled || !this.activeSccCounts)
407
- return false;
408
- return this.cns.network.canNeuronBeGuaranteedDone(
409
- neuron2,
410
- this.activeSccCounts
411
- );
412
- }
413
- cleanupCtxIfNeeded(neuron2) {
414
- if (this.autoCleanupContextsEnabled && this.canNeuronBeGuaranteedDone(neuron2)) {
415
- this.ctxStore?.delete(neuron2);
416
- }
417
- }
418
326
  createSubscriberQueueItem(subscriber, inputSignal) {
419
327
  if (this.options?.maxNeuronHops) {
420
328
  if ((this.neuronVisitMap?.get(subscriber.neuron) ?? 0) >= this.options?.maxNeuronHops) {
@@ -488,7 +396,6 @@ var CNSStimulation = class {
488
396
  );
489
397
  }
490
398
  runStarter(neuron2, dendrite, inputSignal, neuronActivationTask) {
491
- this.markNeuronActive(neuron2);
492
399
  let response;
493
400
  try {
494
401
  response = dendrite.response(
@@ -496,13 +403,11 @@ var CNSStimulation = class {
496
403
  neuron2.axon,
497
404
  new CNSDendriteContext(
498
405
  this,
499
- neuron2,
500
406
  this.options?.abortSignal,
501
407
  this.cns
502
408
  )
503
409
  );
504
410
  } catch (error) {
505
- this.markNeuronInactive(neuron2);
506
411
  const isAborted = this.options?.abortSignal?.aborted ?? false;
507
412
  this.pushFailedTask({
508
413
  task: neuronActivationTask,
@@ -542,7 +447,6 @@ var CNSStimulation = class {
542
447
  return timedPromise.then(
543
448
  (signal) => {
544
449
  return () => {
545
- this.markNeuronInactive(neuron2);
546
450
  return this.processResponseOrResponses(
547
451
  neuron2,
548
452
  inputSignal,
@@ -552,7 +456,6 @@ var CNSStimulation = class {
552
456
  },
553
457
  (error) => {
554
458
  return () => {
555
- this.markNeuronInactive(neuron2);
556
459
  const isAborted = this.options?.abortSignal?.aborted ?? false;
557
460
  this.pushFailedTask({
558
461
  task: neuronActivationTask,
@@ -570,7 +473,6 @@ var CNSStimulation = class {
570
473
  );
571
474
  } else {
572
475
  return () => {
573
- this.markNeuronInactive(neuron2);
574
476
  return this.processResponseOrResponses(
575
477
  neuron2,
576
478
  inputSignal,
@@ -616,7 +518,6 @@ var CNSStimulation = class {
616
518
  const onResponse = this.onResponse;
617
519
  if (!onResponse) {
618
520
  if (collateral2 && !error) {
619
- if (emitter) this.cleanupCtxIfNeeded(emitter);
620
521
  const n = subscribers.length;
621
522
  if (n > 0) {
622
523
  const tasks = new Array(n);
@@ -638,7 +539,6 @@ var CNSStimulation = class {
638
539
  }
639
540
  const subscriberActivationTasks = [];
640
541
  if (collateral2 && !error) {
641
- if (emitter) this.cleanupCtxIfNeeded(emitter);
642
542
  const pending = this.pendingTasks ?? (this.pendingTasks = /* @__PURE__ */ new Set());
643
543
  for (let i = 0; i < subscribers.length; i++) {
644
544
  const task = this.createSubscriberQueueItem(
@@ -655,14 +555,14 @@ var CNSStimulation = class {
655
555
  maybePromise = onResponse(
656
556
  new CNSStimulationResponseImpl(
657
557
  this,
658
- this.ctxStore,
659
558
  inputSignal,
660
559
  outputSignal,
661
560
  this.pendingActivations + this.activeOperations,
662
561
  this.pendingActivations,
663
562
  this.activeOperations,
664
563
  this.options?.maxNeuronHops && emitter ? this.neuronVisitMap?.get(emitter) ?? 0 : void 0,
665
- error
564
+ error,
565
+ emitter
666
566
  )
667
567
  );
668
568
  } catch (e) {
@@ -800,8 +700,9 @@ var CNSNetwork = class {
800
700
  * Strongly Connected Components of the neuron graph.
801
701
  * Each component is a set of neuron objects that can reach each other.
802
702
  *
803
- * Computed lazily on first access: the SCC machinery is only needed for
804
- * `autoCleanupContexts` (off by default), so most networks never pay for it.
703
+ * Computed lazily on first access: the SCC machinery only drives graph
704
+ * introspection (`getSCCSetByNeuron`, `canNeuronBeGuaranteedDone`), so a
705
+ * network that never inspects reachability never pays for it.
805
706
  */
806
707
  this._stronglyConnectedComponents = [];
807
708
  this.sccBuilt = false;
@@ -1087,16 +988,20 @@ var CNS = class {
1087
988
  this.network = new CNSNetwork(this.neurons);
1088
989
  this.global = global;
1089
990
  }
1090
- addResponseListener(listener) {
1091
- this.globalResponseListeners.push(listener);
991
+ /** Push a listener onto a list; return an idempotent unsubscribe. */
992
+ addListener(list, listener) {
993
+ list.push(listener);
1092
994
  let active = true;
1093
995
  return () => {
1094
996
  if (!active) return;
1095
997
  active = false;
1096
- const idx = this.globalResponseListeners.indexOf(listener);
1097
- if (idx >= 0) this.globalResponseListeners.splice(idx, 1);
998
+ const idx = list.indexOf(listener);
999
+ if (idx >= 0) list.splice(idx, 1);
1098
1000
  };
1099
1001
  }
1002
+ addResponseListener(listener) {
1003
+ return this.addListener(this.globalResponseListeners, listener);
1004
+ }
1100
1005
  /**
1101
1006
  * Register a listener called once at the end of every synchronous turn of
1102
1007
  * every stimulation - see {@link TCNSStimulationDrain}. This is how an
@@ -1106,14 +1011,7 @@ var CNS = class {
1106
1011
  * Returns an unsubscribe function.
1107
1012
  */
1108
1013
  addDrainListener(listener) {
1109
- this.globalDrainListeners.push(listener);
1110
- let active = true;
1111
- return () => {
1112
- if (!active) return;
1113
- active = false;
1114
- const idx = this.globalDrainListeners.indexOf(listener);
1115
- if (idx >= 0) this.globalDrainListeners.splice(idx, 1);
1116
- };
1014
+ return this.addListener(this.globalDrainListeners, listener);
1117
1015
  }
1118
1016
  /**
1119
1017
  * Fan a drain out to the local and global listeners, or return undefined when
@@ -1182,15 +1080,18 @@ var CNS = class {
1182
1080
  }
1183
1081
  };
1184
1082
  }
1185
- stimulate(signalOrSignals, options) {
1186
- const wrapped = this.wrapOnResponse(options?.onResponse);
1187
- const stimulation = new CNSStimulation(
1083
+ /** Build a stimulation (wired listeners, unseeded). Shared by stimulate/activate. */
1084
+ createStimulation(options) {
1085
+ return new CNSStimulation(
1188
1086
  this,
1189
1087
  this.instanceNeuronQueue,
1190
1088
  options,
1191
- wrapped,
1089
+ this.wrapOnResponse(options?.onResponse),
1192
1090
  this.wrapOnDrain(options?.onDrain)
1193
1091
  );
1092
+ }
1093
+ stimulate(signalOrSignals, options) {
1094
+ const stimulation = this.createStimulation(options);
1194
1095
  stimulation.responseToSignal(signalOrSignals);
1195
1096
  return stimulation;
1196
1097
  }
@@ -1198,85 +1099,12 @@ var CNS = class {
1198
1099
  * Start a stimulation with activation tasks directly
1199
1100
  */
1200
1101
  activate(tasks, options) {
1201
- const wrapped = this.wrapOnResponse(options?.onResponse);
1202
- const stimulation = new CNSStimulation(
1203
- this,
1204
- this.instanceNeuronQueue,
1205
- options,
1206
- wrapped,
1207
- this.wrapOnDrain(options?.onDrain)
1208
- );
1102
+ const stimulation = this.createStimulation(options);
1209
1103
  stimulation.enqueueTasks(tasks);
1210
1104
  return stimulation;
1211
1105
  }
1212
1106
  };
1213
1107
 
1214
- // src/CNSPersistOptionsRegistry.ts
1215
- var CNSPersistOptionsRegistry = class {
1216
- constructor() {
1217
- this.neurons = /* @__PURE__ */ new Map();
1218
- this.neuronNames = /* @__PURE__ */ new WeakMap();
1219
- this.collaterals = /* @__PURE__ */ new Map();
1220
- this.stimulations = /* @__PURE__ */ new Map();
1221
- }
1222
- addNeuron(neuron2, options) {
1223
- this.neurons.set(options.name, neuron2);
1224
- this.neuronNames.set(neuron2, options.name);
1225
- }
1226
- getNeuron(name) {
1227
- return this.neurons.get(name);
1228
- }
1229
- getNeuronName(neuron2) {
1230
- return this.neuronNames.get(neuron2);
1231
- }
1232
- getNamedNeurons() {
1233
- return this.neurons;
1234
- }
1235
- getCollateralName(collateral2) {
1236
- for (const [name, col] of this.collaterals) {
1237
- if (col === collateral2) return name;
1238
- }
1239
- return void 0;
1240
- }
1241
- removeNeuron(name) {
1242
- const neuron2 = this.neurons.get(name);
1243
- if (neuron2) this.neuronNames.delete(neuron2);
1244
- this.neurons.delete(name);
1245
- }
1246
- addCollateral(collateral2, options) {
1247
- this.collaterals.set(options.name, collateral2);
1248
- }
1249
- getCollateral(name) {
1250
- return this.collaterals.get(name);
1251
- }
1252
- removeCollateral(name) {
1253
- this.collaterals.delete(name);
1254
- }
1255
- addStimulation(stimulation, options) {
1256
- this.stimulations.set(options.stimulationId, stimulation);
1257
- }
1258
- getStimulation(stimulationId) {
1259
- return this.stimulations.get(stimulationId);
1260
- }
1261
- removeStimulation(stimulationId) {
1262
- this.stimulations.delete(stimulationId);
1263
- }
1264
- register(name, neuron2, collateralNames) {
1265
- this.addNeuron(neuron2, { name, neuron: neuron2 });
1266
- for (const [key, col] of Object.entries(neuron2.axon)) {
1267
- this.addCollateral(col, {
1268
- name: collateralNames?.[key] ?? key,
1269
- collateral: col
1270
- });
1271
- }
1272
- return this;
1273
- }
1274
- registerCollateral(name, collateral2) {
1275
- this.addCollateral(collateral2, { name, collateral: collateral2 });
1276
- return this;
1277
- }
1278
- };
1279
-
1280
1108
  // src/CNSCollateral.ts
1281
1109
  var CNSCollateral = class {
1282
1110
  createSignal(payload) {
@@ -1459,13 +1287,11 @@ var neuron = (axon) => {
1459
1287
  };
1460
1288
  var neuronFactory = () => {
1461
1289
  const factory = {
1462
- withCtx: () => factory,
1463
1290
  withGlobal: () => factory,
1464
1291
  neuron: (axon) => neuron(axon)
1465
1292
  };
1466
1293
  return factory;
1467
1294
  };
1468
- var withCtx = () => neuronFactory();
1469
1295
  var withGlobal = () => neuronFactory();
1470
1296
  var afferentPath = (parentAfferentPath) => {
1471
1297
  return {
@@ -1479,31 +1305,7 @@ var modality = (afferentPaths) => {
1479
1305
  afferentPaths
1480
1306
  };
1481
1307
  };
1482
- var createPersistRegistry = (namedNeurons) => {
1483
- const registry = new CNSPersistOptionsRegistry();
1484
- for (const [name, entry] of Object.entries(namedNeurons)) {
1485
- const isEntry = "neuron" in entry && !("axon" in entry);
1486
- const neuron2 = isEntry ? entry.neuron : entry;
1487
- const colNames = isEntry ? entry.collaterals ?? {} : {};
1488
- registry.addNeuron(neuron2, { name, neuron: neuron2 });
1489
- for (const [key, col] of Object.entries(neuron2.axon)) {
1490
- registry.addCollateral(col, {
1491
- name: colNames[key] ?? key,
1492
- collateral: col
1493
- });
1494
- }
1495
- }
1496
- return registry;
1497
- };
1498
- var createCNS = (namedNeurons, options) => {
1499
- const neurons = Object.values(namedNeurons).map(
1500
- (entry) => entry && typeof entry === "object" && "neuron" in entry ? entry.neuron : entry
1501
- );
1502
- const cns = new CNS(neurons, options, options?.global);
1503
- const registry = createPersistRegistry(namedNeurons);
1504
- return { cns, registry };
1505
- };
1506
1308
 
1507
- export { CNS, CNSCollateral, CNSPersistOptionsRegistry, CNSStimulationContextStore, CNSStimulationGate, afferentPath, collateral, createCNS, createPersistRegistry, modality, neuron, neuronFactory, withCtx, withGlobal };
1309
+ export { CNS, CNSCollateral, CNSStimulationGate, afferentPath, collateral, modality, neuron, neuronFactory, withGlobal };
1508
1310
  //# sourceMappingURL=index.js.map
1509
1311
  //# sourceMappingURL=index.js.map