@cnstra/core 2.6.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.
package/dist/index.cjs CHANGED
@@ -1,41 +1,16 @@
1
1
  'use strict';
2
2
 
3
- // src/CNSStimulationContextStore.ts
4
- var CNSStimulationContextStore = class {
5
- constructor(ctx) {
6
- this.ctx = ctx;
7
- }
8
- get(key) {
9
- return this.ctx?.get(key);
10
- }
11
- set(key, value) {
12
- (this.ctx ?? (this.ctx = /* @__PURE__ */ new Map())).set(key, value);
13
- }
14
- getAll() {
15
- return this.ctx ? new Map(this.ctx) : /* @__PURE__ */ new Map();
16
- }
17
- setAll(values) {
18
- const ctx = this.ctx ?? (this.ctx = /* @__PURE__ */ new Map());
19
- ctx.clear();
20
- for (const [key, value] of values) ctx.set(key, value);
21
- }
22
- delete(key) {
23
- this.ctx?.delete(key);
24
- }
25
- };
26
-
27
3
  // src/CNSStimulation.ts
28
4
  var TASK_DENDRITE = /* @__PURE__ */ Symbol("cnstra.taskDendrite");
29
5
  var CNSDendriteContext = class {
30
- constructor(stimulation, neuron2, abortSignal, cns) {
6
+ constructor(stimulation, abortSignal, cns) {
31
7
  this.stimulation = stimulation;
32
- this.neuron = neuron2;
33
8
  this.abortSignal = abortSignal;
34
9
  this.cns = cns;
35
10
  }
36
- // Prototype getter, not a constructor field: the per-activation allocation is
37
- // unchanged (still 4 fields) and you only pay a single `cns.global` read on the
38
- // dendrites that actually reach for it. Value getter, so `{ global }` destructuring is safe.
11
+ // Prototype getter, not a constructor field: you only pay a single `cns.global`
12
+ // read on the dendrites that actually reach for it. Value getter, so
13
+ // `{ global }` destructuring is safe.
39
14
  get global() {
40
15
  return this.cns.global;
41
16
  }
@@ -46,20 +21,10 @@ var CNSDendriteContext = class {
46
21
  get stimulationContext() {
47
22
  return this.stimulation.options?.stimulationContext;
48
23
  }
49
- get() {
50
- return this.stimulation.getContext().get(this.neuron);
51
- }
52
- set(value) {
53
- this.stimulation.getContext().set(this.neuron, value);
54
- }
55
- delete() {
56
- this.stimulation.getContext().delete(this.neuron);
57
- }
58
24
  };
59
25
  var CNSStimulationResponseImpl = class {
60
- constructor(stimulation, store, inputSignal, outputSignal, queueLength, pendingActivations, activeActivations, hops, error, neuron2) {
26
+ constructor(stimulation, inputSignal, outputSignal, queueLength, pendingActivations, activeActivations, hops, error, neuron2) {
61
27
  this.stimulation = stimulation;
62
- this.store = store;
63
28
  this.inputSignal = inputSignal;
64
29
  this.outputSignal = outputSignal;
65
30
  this.queueLength = queueLength;
@@ -69,9 +34,6 @@ var CNSStimulationResponseImpl = class {
69
34
  this.error = error;
70
35
  this.neuron = neuron2;
71
36
  }
72
- get contextValue() {
73
- return this.store ? this.store.getAll() : /* @__PURE__ */ new Map();
74
- }
75
37
  };
76
38
  var CNSStimulation = class {
77
39
  constructor(cns, instanceNeuronQueue, options, onResponse, onDrain) {
@@ -99,15 +61,11 @@ var CNSStimulation = class {
99
61
  this.isCompleted = false;
100
62
  this.completionRejected = false;
101
63
  this.onDrain = onDrain;
102
- this.ctxStore = options?.ctx;
103
64
  this.instanceNeuronQueue = instanceNeuronQueue;
104
65
  this.onResponse = onResponse;
105
66
  if (this.options?.maxNeuronHops) {
106
67
  this.neuronVisitMap = /* @__PURE__ */ new Map();
107
68
  }
108
- if (this.autoCleanupContextsEnabled) {
109
- this.activeSccCounts = /* @__PURE__ */ new Map();
110
- }
111
69
  }
112
70
  pushFailedTask(failure) {
113
71
  (this.failedTasks ?? (this.failedTasks = [])).push(failure);
@@ -121,10 +79,6 @@ var CNSStimulation = class {
121
79
  this.completionError = error;
122
80
  this.rejectCompleted?.(error);
123
81
  }
124
- /** Lazily-created neuron context store (see {@link ctxStore}). */
125
- get ctx() {
126
- return this.ctxStore ?? (this.ctxStore = new CNSStimulationContextStore());
127
- }
128
82
  // ----- Inlined activation queue / pump -----
129
83
  get canStartOperation() {
130
84
  const limit = this.options?.concurrency ?? Infinity;
@@ -354,12 +308,6 @@ var CNSStimulation = class {
354
308
  ...this.getFailedTasks().map((f) => f.task)
355
309
  ];
356
310
  }
357
- /**
358
- * Get the context store for this stimulation
359
- */
360
- getContext() {
361
- return this.ctx;
362
- }
363
311
  /**
364
312
  * Enqueue activation tasks directly into the stimulation queue
365
313
  */
@@ -377,67 +325,6 @@ var CNSStimulation = class {
377
325
  get concurrencyEnabled() {
378
326
  return this.options?.concurrency !== void 0 && this.options?.concurrency > 0;
379
327
  }
380
- get autoCleanupContextsEnabled() {
381
- return this.cns?.options?.autoCleanupContexts ?? false;
382
- }
383
- /**
384
- * Mark a neuron as active (being processed)
385
- */
386
- markNeuronActive(neuron2) {
387
- if (!this.autoCleanupContextsEnabled) return;
388
- this.incrementSccCount(neuron2);
389
- }
390
- /**
391
- * Mark a neuron as inactive (finished processing)
392
- */
393
- markNeuronInactive(neuron2) {
394
- if (!this.autoCleanupContextsEnabled) return;
395
- this.decrementSccCount(neuron2);
396
- }
397
- /**
398
- * Increment the active count for the SCC containing this neuron
399
- */
400
- incrementSccCount(neuron2) {
401
- const counts = this.activeSccCounts;
402
- if (!counts) return;
403
- const sccIndex = this.cns.network.getSccIndexByNeuron(neuron2);
404
- if (sccIndex === void 0) return;
405
- const currentCount = counts.get(sccIndex) || 0;
406
- counts.set(sccIndex, currentCount + 1);
407
- }
408
- /**
409
- * Decrement the active count for the SCC containing this neuron
410
- */
411
- decrementSccCount(neuron2) {
412
- const counts = this.activeSccCounts;
413
- if (!counts) return;
414
- const sccIndex = this.cns.network.getSccIndexByNeuron(neuron2);
415
- if (sccIndex === void 0) return;
416
- const currentCount = counts.get(sccIndex) || 0;
417
- const nextCount = Math.max(0, currentCount - 1);
418
- counts.set(sccIndex, nextCount);
419
- if (currentCount === 0) {
420
- console.warn(
421
- `[CNSStimulation] Attempting to decrement SCC count below 0 for neuron`
422
- );
423
- }
424
- }
425
- /**
426
- * Check if a neuron can be guaranteed not to be visited again
427
- */
428
- canNeuronBeGuaranteedDone(neuron2) {
429
- if (!this.autoCleanupContextsEnabled || !this.activeSccCounts)
430
- return false;
431
- return this.cns.network.canNeuronBeGuaranteedDone(
432
- neuron2,
433
- this.activeSccCounts
434
- );
435
- }
436
- cleanupCtxIfNeeded(neuron2) {
437
- if (this.autoCleanupContextsEnabled && this.canNeuronBeGuaranteedDone(neuron2)) {
438
- this.ctxStore?.delete(neuron2);
439
- }
440
- }
441
328
  createSubscriberQueueItem(subscriber, inputSignal) {
442
329
  if (this.options?.maxNeuronHops) {
443
330
  if ((this.neuronVisitMap?.get(subscriber.neuron) ?? 0) >= this.options?.maxNeuronHops) {
@@ -511,7 +398,6 @@ var CNSStimulation = class {
511
398
  );
512
399
  }
513
400
  runStarter(neuron2, dendrite, inputSignal, neuronActivationTask) {
514
- this.markNeuronActive(neuron2);
515
401
  let response;
516
402
  try {
517
403
  response = dendrite.response(
@@ -519,13 +405,11 @@ var CNSStimulation = class {
519
405
  neuron2.axon,
520
406
  new CNSDendriteContext(
521
407
  this,
522
- neuron2,
523
408
  this.options?.abortSignal,
524
409
  this.cns
525
410
  )
526
411
  );
527
412
  } catch (error) {
528
- this.markNeuronInactive(neuron2);
529
413
  const isAborted = this.options?.abortSignal?.aborted ?? false;
530
414
  this.pushFailedTask({
531
415
  task: neuronActivationTask,
@@ -565,7 +449,6 @@ var CNSStimulation = class {
565
449
  return timedPromise.then(
566
450
  (signal) => {
567
451
  return () => {
568
- this.markNeuronInactive(neuron2);
569
452
  return this.processResponseOrResponses(
570
453
  neuron2,
571
454
  inputSignal,
@@ -575,7 +458,6 @@ var CNSStimulation = class {
575
458
  },
576
459
  (error) => {
577
460
  return () => {
578
- this.markNeuronInactive(neuron2);
579
461
  const isAborted = this.options?.abortSignal?.aborted ?? false;
580
462
  this.pushFailedTask({
581
463
  task: neuronActivationTask,
@@ -593,7 +475,6 @@ var CNSStimulation = class {
593
475
  );
594
476
  } else {
595
477
  return () => {
596
- this.markNeuronInactive(neuron2);
597
478
  return this.processResponseOrResponses(
598
479
  neuron2,
599
480
  inputSignal,
@@ -639,7 +520,6 @@ var CNSStimulation = class {
639
520
  const onResponse = this.onResponse;
640
521
  if (!onResponse) {
641
522
  if (collateral2 && !error) {
642
- if (emitter) this.cleanupCtxIfNeeded(emitter);
643
523
  const n = subscribers.length;
644
524
  if (n > 0) {
645
525
  const tasks = new Array(n);
@@ -661,7 +541,6 @@ var CNSStimulation = class {
661
541
  }
662
542
  const subscriberActivationTasks = [];
663
543
  if (collateral2 && !error) {
664
- if (emitter) this.cleanupCtxIfNeeded(emitter);
665
544
  const pending = this.pendingTasks ?? (this.pendingTasks = /* @__PURE__ */ new Set());
666
545
  for (let i = 0; i < subscribers.length; i++) {
667
546
  const task = this.createSubscriberQueueItem(
@@ -678,7 +557,6 @@ var CNSStimulation = class {
678
557
  maybePromise = onResponse(
679
558
  new CNSStimulationResponseImpl(
680
559
  this,
681
- this.ctxStore,
682
560
  inputSignal,
683
561
  outputSignal,
684
562
  this.pendingActivations + this.activeOperations,
@@ -824,8 +702,9 @@ var CNSNetwork = class {
824
702
  * Strongly Connected Components of the neuron graph.
825
703
  * Each component is a set of neuron objects that can reach each other.
826
704
  *
827
- * Computed lazily on first access: the SCC machinery is only needed for
828
- * `autoCleanupContexts` (off by default), so most networks never pay for it.
705
+ * Computed lazily on first access: the SCC machinery only drives graph
706
+ * introspection (`getSCCSetByNeuron`, `canNeuronBeGuaranteedDone`), so a
707
+ * network that never inspects reachability never pays for it.
829
708
  */
830
709
  this._stronglyConnectedComponents = [];
831
710
  this.sccBuilt = false;
@@ -1111,16 +990,20 @@ var CNS = class {
1111
990
  this.network = new CNSNetwork(this.neurons);
1112
991
  this.global = global;
1113
992
  }
1114
- addResponseListener(listener) {
1115
- this.globalResponseListeners.push(listener);
993
+ /** Push a listener onto a list; return an idempotent unsubscribe. */
994
+ addListener(list, listener) {
995
+ list.push(listener);
1116
996
  let active = true;
1117
997
  return () => {
1118
998
  if (!active) return;
1119
999
  active = false;
1120
- const idx = this.globalResponseListeners.indexOf(listener);
1121
- if (idx >= 0) this.globalResponseListeners.splice(idx, 1);
1000
+ const idx = list.indexOf(listener);
1001
+ if (idx >= 0) list.splice(idx, 1);
1122
1002
  };
1123
1003
  }
1004
+ addResponseListener(listener) {
1005
+ return this.addListener(this.globalResponseListeners, listener);
1006
+ }
1124
1007
  /**
1125
1008
  * Register a listener called once at the end of every synchronous turn of
1126
1009
  * every stimulation - see {@link TCNSStimulationDrain}. This is how an
@@ -1130,14 +1013,7 @@ var CNS = class {
1130
1013
  * Returns an unsubscribe function.
1131
1014
  */
1132
1015
  addDrainListener(listener) {
1133
- this.globalDrainListeners.push(listener);
1134
- let active = true;
1135
- return () => {
1136
- if (!active) return;
1137
- active = false;
1138
- const idx = this.globalDrainListeners.indexOf(listener);
1139
- if (idx >= 0) this.globalDrainListeners.splice(idx, 1);
1140
- };
1016
+ return this.addListener(this.globalDrainListeners, listener);
1141
1017
  }
1142
1018
  /**
1143
1019
  * Fan a drain out to the local and global listeners, or return undefined when
@@ -1206,15 +1082,18 @@ var CNS = class {
1206
1082
  }
1207
1083
  };
1208
1084
  }
1209
- stimulate(signalOrSignals, options) {
1210
- const wrapped = this.wrapOnResponse(options?.onResponse);
1211
- const stimulation = new CNSStimulation(
1085
+ /** Build a stimulation (wired listeners, unseeded). Shared by stimulate/activate. */
1086
+ createStimulation(options) {
1087
+ return new CNSStimulation(
1212
1088
  this,
1213
1089
  this.instanceNeuronQueue,
1214
1090
  options,
1215
- wrapped,
1091
+ this.wrapOnResponse(options?.onResponse),
1216
1092
  this.wrapOnDrain(options?.onDrain)
1217
1093
  );
1094
+ }
1095
+ stimulate(signalOrSignals, options) {
1096
+ const stimulation = this.createStimulation(options);
1218
1097
  stimulation.responseToSignal(signalOrSignals);
1219
1098
  return stimulation;
1220
1099
  }
@@ -1222,14 +1101,7 @@ var CNS = class {
1222
1101
  * Start a stimulation with activation tasks directly
1223
1102
  */
1224
1103
  activate(tasks, options) {
1225
- const wrapped = this.wrapOnResponse(options?.onResponse);
1226
- const stimulation = new CNSStimulation(
1227
- this,
1228
- this.instanceNeuronQueue,
1229
- options,
1230
- wrapped,
1231
- this.wrapOnDrain(options?.onDrain)
1232
- );
1104
+ const stimulation = this.createStimulation(options);
1233
1105
  stimulation.enqueueTasks(tasks);
1234
1106
  return stimulation;
1235
1107
  }
@@ -1417,13 +1289,11 @@ var neuron = (axon) => {
1417
1289
  };
1418
1290
  var neuronFactory = () => {
1419
1291
  const factory = {
1420
- withCtx: () => factory,
1421
1292
  withGlobal: () => factory,
1422
1293
  neuron: (axon) => neuron(axon)
1423
1294
  };
1424
1295
  return factory;
1425
1296
  };
1426
- var withCtx = () => neuronFactory();
1427
1297
  var withGlobal = () => neuronFactory();
1428
1298
  var afferentPath = (parentAfferentPath) => {
1429
1299
  return {
@@ -1440,14 +1310,12 @@ var modality = (afferentPaths) => {
1440
1310
 
1441
1311
  exports.CNS = CNS;
1442
1312
  exports.CNSCollateral = CNSCollateral;
1443
- exports.CNSStimulationContextStore = CNSStimulationContextStore;
1444
1313
  exports.CNSStimulationGate = CNSStimulationGate;
1445
1314
  exports.afferentPath = afferentPath;
1446
1315
  exports.collateral = collateral;
1447
1316
  exports.modality = modality;
1448
1317
  exports.neuron = neuron;
1449
1318
  exports.neuronFactory = neuronFactory;
1450
- exports.withCtx = withCtx;
1451
1319
  exports.withGlobal = withGlobal;
1452
1320
  //# sourceMappingURL=index.cjs.map
1453
1321
  //# sourceMappingURL=index.cjs.map