@cnstra/core 2.3.0 → 2.5.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.
package/dist/index.d.ts CHANGED
@@ -7,13 +7,17 @@ export type { ICNS } from './interfaces/ICNS';
7
7
  export type { TCNSNeuron } from './types/TCNSNeuron';
8
8
  export type { TCNSAxon } from './types/TCNSAxon';
9
9
  export type { TCNSDendrite } from './types/TCNSDendrite';
10
+ export type { TCNSHandlerContext } from './types/TCNSHandlerContext';
10
11
  export type { TCNSSignal } from './types/TCNSSignal';
11
12
  export type { TCNSNeuronActivationTask } from './types/TCNSNeuronActivationTask';
12
13
  export type { TCNSStimulationOptions } from './types/TCNSStimulationOptions';
14
+ export type { TCNSStimulationResponse } from './types/TCNSStimulationResponse';
15
+ export type { TCNSStimulationDrain } from './types/TCNSStimulationDrain';
13
16
  export type { TCNSModality } from './types/TCNSModality';
14
17
  export type { TCNSAfferentPath } from './types/TCNSAfferentPath';
15
18
  export type { TCNSNeuronActivationTaskFailure } from './types/TCNSNeuronActivationTaskFailure';
16
19
  export type { TCNSNeuronPersistOptions, TCNSCollateralPersistOptions, TCNSStimulationPersistOptions } from './types/TCNSPersist';
17
20
  export type { TCNSStimulationGateOptions, TCNSStimulationGateSignal } from './utils/CNSStimulationGate';
18
- export { collateral, neuron, withCtx, afferentPath, modality, createPersistRegistry, } from './factory/index';
21
+ export { collateral, neuron, neuronFactory, withCtx, withGlobal, afferentPath, modality, createPersistRegistry, createCNS, } from './factory/index';
22
+ export type { TNeuronFactory } from './factory/index';
19
23
  //# 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,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACrD,YAAY,EAAE,wBAAwB,EAAE,MAAM,kCAAkC,CAAC;AACjF,YAAY,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AAC7E,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,OAAO,EACP,YAAY,EACZ,QAAQ,EACR,qBAAqB,GACxB,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;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"}
package/dist/index.js CHANGED
@@ -31,6 +31,12 @@ var CNSDendriteContext = class {
31
31
  this.abortSignal = abortSignal;
32
32
  this.cns = cns;
33
33
  }
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.
37
+ get global() {
38
+ return this.cns.global;
39
+ }
34
40
  get() {
35
41
  return this.stimulation.getContext().get(this.neuron);
36
42
  }
@@ -42,12 +48,14 @@ var CNSDendriteContext = class {
42
48
  }
43
49
  };
44
50
  var CNSStimulationResponseImpl = class {
45
- constructor(stimulation, store, inputSignal, outputSignal, queueLength, hops, error) {
51
+ constructor(stimulation, store, inputSignal, outputSignal, queueLength, pendingActivations, activeActivations, hops, error) {
46
52
  this.stimulation = stimulation;
47
53
  this.store = store;
48
54
  this.inputSignal = inputSignal;
49
55
  this.outputSignal = outputSignal;
50
56
  this.queueLength = queueLength;
57
+ this.pendingActivations = pendingActivations;
58
+ this.activeActivations = activeActivations;
51
59
  this.hops = hops;
52
60
  this.error = error;
53
61
  }
@@ -56,7 +64,7 @@ var CNSStimulationResponseImpl = class {
56
64
  }
57
65
  };
58
66
  var CNSStimulation = class {
59
- constructor(cns, instanceNeuronQueue, options, onResponse) {
67
+ constructor(cns, instanceNeuronQueue, options, onResponse, onDrain) {
60
68
  this.cns = cns;
61
69
  this.options = options;
62
70
  this.qHead = 0;
@@ -67,9 +75,20 @@ var CNSStimulation = class {
67
75
  // Re-entrancy guard for the pump loop.
68
76
  this.pumping = false;
69
77
  this.needsPump = false;
78
+ // Nesting depth of the current synchronous turn. Only the outermost close
79
+ // announces a drain - see beginTurn/endTurn.
80
+ this.turnDepth = 0;
70
81
  this.scheduledCount = 0;
82
+ // Activations belonging to signals of an in-progress array fan-out that have
83
+ // not been handed to processResponse yet, so the task objects do not exist.
84
+ // processResponse enqueues synchronously, so the first signal's whole subtree
85
+ // drains before the second signal is even looked at; without this counter
86
+ // that subtree's leaf sees an empty queue and reads as "all work done".
87
+ // Counted in activations, not signals, so every public counter shares a unit.
88
+ this.reservedActivations = 0;
71
89
  this.isCompleted = false;
72
90
  this.completionRejected = false;
91
+ this.onDrain = onDrain;
73
92
  this.ctxStore = options?.ctx;
74
93
  this.instanceNeuronQueue = instanceNeuronQueue;
75
94
  this.onResponse = onResponse;
@@ -166,18 +185,28 @@ var CNSStimulation = class {
166
185
  (cb) => {
167
186
  this.activeOperations--;
168
187
  this.activeTasks.delete(task);
169
- if (typeof cb === "function") cb();
170
- if (this.qSize > 0 && this.canStartOperation) {
171
- if (this.pumping) this.needsPump = true;
172
- else this.pump();
188
+ this.beginTurn();
189
+ try {
190
+ if (typeof cb === "function") cb();
191
+ if (this.qSize > 0 && this.canStartOperation) {
192
+ if (this.pumping) this.needsPump = true;
193
+ else this.pump();
194
+ }
195
+ } finally {
196
+ this.endTurn();
173
197
  }
174
198
  },
175
199
  (err) => {
176
200
  this.activeOperations--;
177
201
  this.activeTasks.delete(task);
178
- if (this.qSize > 0 && this.canStartOperation) {
179
- if (this.pumping) this.needsPump = true;
180
- else this.pump();
202
+ this.beginTurn();
203
+ try {
204
+ if (this.qSize > 0 && this.canStartOperation) {
205
+ if (this.pumping) this.needsPump = true;
206
+ else this.pump();
207
+ }
208
+ } finally {
209
+ this.endTurn();
181
210
  }
182
211
  throw err;
183
212
  }
@@ -193,10 +222,54 @@ var CNSStimulation = class {
193
222
  this.pump();
194
223
  }
195
224
  }
225
+ /**
226
+ * Open a synchronous turn. Turns nest: a drain is announced only when the
227
+ * outermost one closes, so an enqueue that happens while the pump is already
228
+ * running does not produce its own boundary.
229
+ */
230
+ beginTurn() {
231
+ this.turnDepth++;
232
+ }
233
+ /**
234
+ * Close a synchronous turn and, if it was the outermost and nothing more can
235
+ * run without yielding to the event loop, announce the drain.
236
+ *
237
+ * This is the only place the synchronous frontier is observable. A response
238
+ * cannot serve the purpose: responses are emitted when an activation
239
+ * *finishes*, but a turn just as often ends when one *starts* and returns a
240
+ * promise - at which point there is nothing left to emit a response from.
241
+ */
242
+ endTurn() {
243
+ if (--this.turnDepth > 0) return;
244
+ const onDrain = this.onDrain;
245
+ if (!onDrain) return;
246
+ if (this.pumping) return;
247
+ if (this.qSize > 0 && this.canStartOperation) return;
248
+ this.turnDepth++;
249
+ try {
250
+ onDrain({
251
+ stimulation: this,
252
+ queueLength: this.pendingActivations + this.activeOperations,
253
+ pendingActivations: this.pendingActivations,
254
+ activeActivations: this.activeOperations
255
+ });
256
+ } finally {
257
+ this.turnDepth--;
258
+ }
259
+ }
260
+ /**
261
+ * Activations that exist (or are reserved) but whose dendrite body has not
262
+ * been invoked yet: sitting in the ring buffer, created and held in
263
+ * pendingTasks, or reserved by an array fan-out mid-dispatch. Subscribers
264
+ * parked behind an async onResponse land here too - their body has not run.
265
+ */
266
+ get pendingActivations() {
267
+ return this.qSize + this.scheduledCount + this.reservedActivations;
268
+ }
196
269
  tryResolveCompleted() {
197
270
  if (this.isCompleted) return;
198
271
  const noActive = this.activeOperations === 0;
199
- const noPending = this.qSize + this.scheduledCount === 0;
272
+ const noPending = this.pendingActivations === 0;
200
273
  const aborted = !!this.options?.abortSignal?.aborted;
201
274
  if (noPending && noActive || aborted && noActive && !noPending) {
202
275
  if (aborted) {
@@ -268,10 +341,15 @@ var CNSStimulation = class {
268
341
  * Enqueue activation tasks directly into the stimulation queue
269
342
  */
270
343
  enqueueTasks(tasks) {
271
- for (const task of tasks) {
272
- this.qEnqueue(task);
344
+ this.beginTurn();
345
+ try {
346
+ for (const task of tasks) {
347
+ this.qEnqueue(task);
348
+ }
349
+ this.tryResolveCompleted();
350
+ } finally {
351
+ this.endTurn();
273
352
  }
274
- this.tryResolveCompleted();
275
353
  }
276
354
  get concurrencyEnabled() {
277
355
  return this.options?.concurrency !== void 0 && this.options?.concurrency > 0;
@@ -372,14 +450,19 @@ var CNSStimulation = class {
372
450
  }
373
451
  }
374
452
  if (!subscriber) {
453
+ const error = new Error(`Subscriber not found for activation task`);
375
454
  this.pushFailedTask({
376
455
  task: neuronActivationTask,
377
- error: new Error(
378
- `Subscriber not found for activation task`
379
- ),
456
+ error,
380
457
  aborted: false
381
458
  });
382
459
  return () => {
460
+ this.processResponseOrResponses(
461
+ void 0,
462
+ neuronActivationTask.input,
463
+ void 0,
464
+ error
465
+ );
383
466
  };
384
467
  }
385
468
  const neuron2 = subscriber.neuron;
@@ -426,13 +509,13 @@ var CNSStimulation = class {
426
509
  error: error instanceof Error ? error : new Error(String(error)),
427
510
  aborted: isAborted
428
511
  });
429
- this.processResponseOrResponses(
430
- neuron2,
431
- inputSignal,
432
- void 0,
433
- error
434
- );
435
512
  return () => {
513
+ this.processResponseOrResponses(
514
+ neuron2,
515
+ inputSignal,
516
+ void 0,
517
+ error
518
+ );
436
519
  };
437
520
  }
438
521
  const maxDuration = neuron2.maxDuration;
@@ -502,8 +585,26 @@ var CNSStimulation = class {
502
585
  this.processResponse(emitter, inputSignal, void 0, error);
503
586
  return;
504
587
  }
505
- for (const signal of outputSignalOrSignals) {
506
- this.processResponse(emitter, inputSignal, signal, error);
588
+ let perSignal;
589
+ if (!error) {
590
+ perSignal = new Array(outputSignalOrSignals.length);
591
+ let reserved = 0;
592
+ for (let i = 0; i < outputSignalOrSignals.length; i++) {
593
+ const c = outputSignalOrSignals[i]?.collateral;
594
+ const n = c ? this.cns.network.getSubscribers(c).length : 0;
595
+ perSignal[i] = n;
596
+ reserved += n;
597
+ }
598
+ this.reservedActivations += reserved;
599
+ }
600
+ for (let i = 0; i < outputSignalOrSignals.length; i++) {
601
+ if (perSignal) this.reservedActivations -= perSignal[i];
602
+ this.processResponse(
603
+ emitter,
604
+ inputSignal,
605
+ outputSignalOrSignals[i],
606
+ error
607
+ );
507
608
  }
508
609
  return;
509
610
  }
@@ -557,7 +658,9 @@ var CNSStimulation = class {
557
658
  this.ctxStore,
558
659
  inputSignal,
559
660
  outputSignal,
560
- this.qSize + this.scheduledCount,
661
+ this.pendingActivations + this.activeOperations,
662
+ this.pendingActivations,
663
+ this.activeOperations,
561
664
  this.options?.maxNeuronHops && emitter ? this.neuronVisitMap?.get(emitter) ?? 0 : void 0,
562
665
  error
563
666
  )
@@ -575,21 +678,35 @@ var CNSStimulation = class {
575
678
  if (maybePromise && typeof maybePromise.then === "function") {
576
679
  maybePromise.then(
577
680
  () => {
578
- for (let i = 0; i < subscriberActivationTasks.length; i++) {
579
- this.scheduledCount--;
580
- this.pendingTasks?.delete(subscriberActivationTasks[i]);
581
- this.qEnqueue(subscriberActivationTasks[i]);
681
+ this.beginTurn();
682
+ try {
683
+ for (let i = 0; i < subscriberActivationTasks.length; i++) {
684
+ this.scheduledCount--;
685
+ this.pendingTasks?.delete(
686
+ subscriberActivationTasks[i]
687
+ );
688
+ this.qEnqueue(subscriberActivationTasks[i]);
689
+ }
690
+ this.tryResolveCompleted();
691
+ } finally {
692
+ this.endTurn();
582
693
  }
583
- this.tryResolveCompleted();
584
694
  },
585
695
  (error2) => {
586
696
  this.onResponseError = error2 instanceof Error ? error2 : new Error(String(error2));
587
- for (let i = 0; i < subscriberActivationTasks.length; i++) {
588
- this.scheduledCount--;
589
- this.pendingTasks?.delete(subscriberActivationTasks[i]);
590
- this.qEnqueue(subscriberActivationTasks[i]);
697
+ this.beginTurn();
698
+ try {
699
+ for (let i = 0; i < subscriberActivationTasks.length; i++) {
700
+ this.scheduledCount--;
701
+ this.pendingTasks?.delete(
702
+ subscriberActivationTasks[i]
703
+ );
704
+ this.qEnqueue(subscriberActivationTasks[i]);
705
+ }
706
+ this.tryResolveCompleted();
707
+ } finally {
708
+ this.endTurn();
591
709
  }
592
- this.tryResolveCompleted();
593
710
  }
594
711
  );
595
712
  return;
@@ -602,12 +719,21 @@ var CNSStimulation = class {
602
719
  this.tryResolveCompleted();
603
720
  }
604
721
  responseToSignal(signalOrSignals) {
605
- if (Array.isArray(signalOrSignals)) {
606
- this.processResponseOrResponses(void 0, void 0, signalOrSignals);
607
- return;
722
+ this.beginTurn();
723
+ try {
724
+ if (Array.isArray(signalOrSignals)) {
725
+ this.processResponseOrResponses(
726
+ void 0,
727
+ void 0,
728
+ signalOrSignals
729
+ );
730
+ return;
731
+ }
732
+ this.processResponse(void 0, void 0, signalOrSignals);
733
+ this.tryResolveCompleted();
734
+ } finally {
735
+ this.endTurn();
608
736
  }
609
- this.processResponse(void 0, void 0, signalOrSignals);
610
- this.tryResolveCompleted();
611
737
  }
612
738
  };
613
739
 
@@ -942,7 +1068,7 @@ var CNSNetwork = class {
942
1068
 
943
1069
  // src/CNS.ts
944
1070
  var CNS = class {
945
- constructor(neurons, options) {
1071
+ constructor(neurons, options, global) {
946
1072
  this.neurons = neurons;
947
1073
  this.options = options;
948
1074
  /**
@@ -954,7 +1080,12 @@ var CNS = class {
954
1080
  * Global response listeners applied to every stimulation.
955
1081
  */
956
1082
  this.globalResponseListeners = [];
1083
+ /**
1084
+ * Global batch-boundary listeners applied to every stimulation.
1085
+ */
1086
+ this.globalDrainListeners = [];
957
1087
  this.network = new CNSNetwork(this.neurons);
1088
+ this.global = global;
958
1089
  }
959
1090
  addResponseListener(listener) {
960
1091
  this.globalResponseListeners.push(listener);
@@ -966,6 +1097,50 @@ var CNS = class {
966
1097
  if (idx >= 0) this.globalResponseListeners.splice(idx, 1);
967
1098
  };
968
1099
  }
1100
+ /**
1101
+ * Register a listener called once at the end of every synchronous turn of
1102
+ * every stimulation - see {@link TCNSStimulationDrain}. This is how an
1103
+ * integration (a state manager, an in-memory database) installs a single
1104
+ * commit point for the whole organism instead of passing `onDrain` per call.
1105
+ *
1106
+ * Returns an unsubscribe function.
1107
+ */
1108
+ 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
+ };
1117
+ }
1118
+ /**
1119
+ * Fan a drain out to the local and global listeners, or return undefined when
1120
+ * there are none so the stimulation can skip the boundary check entirely.
1121
+ *
1122
+ * Listener errors are isolated: a drain is a notification, and one broken
1123
+ * flush must not stop the others or derail the stimulation that triggered it.
1124
+ */
1125
+ wrapOnDrain(local) {
1126
+ if (this.globalDrainListeners.length === 0 && !local) return void 0;
1127
+ return (d) => {
1128
+ if (local) {
1129
+ try {
1130
+ local(d);
1131
+ } catch (error) {
1132
+ console.error("[CNS] onDrain listener threw", error);
1133
+ }
1134
+ }
1135
+ for (let i = 0; i < this.globalDrainListeners.length; i++) {
1136
+ try {
1137
+ this.globalDrainListeners[i](d);
1138
+ } catch (error) {
1139
+ console.error("[CNS] drain listener threw", error);
1140
+ }
1141
+ }
1142
+ };
1143
+ }
969
1144
  wrapOnResponse(local) {
970
1145
  if (this.globalResponseListeners.length === 0 && !local) {
971
1146
  return void 0;
@@ -1013,7 +1188,8 @@ var CNS = class {
1013
1188
  this,
1014
1189
  this.instanceNeuronQueue,
1015
1190
  options,
1016
- wrapped
1191
+ wrapped,
1192
+ this.wrapOnDrain(options?.onDrain)
1017
1193
  );
1018
1194
  stimulation.responseToSignal(signalOrSignals);
1019
1195
  return stimulation;
@@ -1027,7 +1203,8 @@ var CNS = class {
1027
1203
  this,
1028
1204
  this.instanceNeuronQueue,
1029
1205
  options,
1030
- wrapped
1206
+ wrapped,
1207
+ this.wrapOnDrain(options?.onDrain)
1031
1208
  );
1032
1209
  stimulation.enqueueTasks(tasks);
1033
1210
  return stimulation;
@@ -1280,11 +1457,16 @@ var neuron = (axon) => {
1280
1457
  };
1281
1458
  return api;
1282
1459
  };
1283
- var withCtx = () => ({
1284
- neuron: (axon) => {
1285
- return neuron(axon);
1286
- }
1287
- });
1460
+ var neuronFactory = () => {
1461
+ const factory = {
1462
+ withCtx: () => factory,
1463
+ withGlobal: () => factory,
1464
+ neuron: (axon) => neuron(axon)
1465
+ };
1466
+ return factory;
1467
+ };
1468
+ var withCtx = () => neuronFactory();
1469
+ var withGlobal = () => neuronFactory();
1288
1470
  var afferentPath = (parentAfferentPath) => {
1289
1471
  return {
1290
1472
  ...parentAfferentPath !== void 0 && {
@@ -1313,7 +1495,15 @@ var createPersistRegistry = (namedNeurons) => {
1313
1495
  }
1314
1496
  return registry;
1315
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
+ };
1316
1506
 
1317
- export { CNS, CNSCollateral, CNSPersistOptionsRegistry, CNSStimulationContextStore, CNSStimulationGate, afferentPath, collateral, createPersistRegistry, modality, neuron, withCtx };
1507
+ export { CNS, CNSCollateral, CNSPersistOptionsRegistry, CNSStimulationContextStore, CNSStimulationGate, afferentPath, collateral, createCNS, createPersistRegistry, modality, neuron, neuronFactory, withCtx, withGlobal };
1318
1508
  //# sourceMappingURL=index.js.map
1319
1509
  //# sourceMappingURL=index.js.map