@cnstra/core 2.4.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/CNS.d.ts +22 -0
- package/dist/CNS.d.ts.map +1 -1
- package/dist/CNSStimulation.d.ts +33 -1
- package/dist/CNSStimulation.d.ts.map +1 -1
- package/dist/index.cjs +212 -42
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +212 -42
- package/dist/index.js.map +1 -1
- package/dist/interfaces/ICNS.d.ts +7 -0
- package/dist/interfaces/ICNS.d.ts.map +1 -1
- package/dist/types/TCNSStimulationDrain.d.ts +37 -0
- package/dist/types/TCNSStimulationDrain.d.ts.map +1 -0
- package/dist/types/TCNSStimulationOptions.d.ts +18 -0
- package/dist/types/TCNSStimulationOptions.d.ts.map +1 -1
- package/dist/types/TCNSStimulationResponse.d.ts +40 -0
- package/dist/types/TCNSStimulationResponse.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -11,6 +11,8 @@ export type { TCNSHandlerContext } from './types/TCNSHandlerContext';
|
|
|
11
11
|
export type { TCNSSignal } from './types/TCNSSignal';
|
|
12
12
|
export type { TCNSNeuronActivationTask } from './types/TCNSNeuronActivationTask';
|
|
13
13
|
export type { TCNSStimulationOptions } from './types/TCNSStimulationOptions';
|
|
14
|
+
export type { TCNSStimulationResponse } from './types/TCNSStimulationResponse';
|
|
15
|
+
export type { TCNSStimulationDrain } from './types/TCNSStimulationDrain';
|
|
14
16
|
export type { TCNSModality } from './types/TCNSModality';
|
|
15
17
|
export type { TCNSAfferentPath } from './types/TCNSAfferentPath';
|
|
16
18
|
export type { TCNSNeuronActivationTaskFailure } from './types/TCNSNeuronActivationTaskFailure';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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,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;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
|
@@ -48,12 +48,14 @@ var CNSDendriteContext = class {
|
|
|
48
48
|
}
|
|
49
49
|
};
|
|
50
50
|
var CNSStimulationResponseImpl = class {
|
|
51
|
-
constructor(stimulation, store, inputSignal, outputSignal, queueLength, hops, error) {
|
|
51
|
+
constructor(stimulation, store, inputSignal, outputSignal, queueLength, pendingActivations, activeActivations, hops, error) {
|
|
52
52
|
this.stimulation = stimulation;
|
|
53
53
|
this.store = store;
|
|
54
54
|
this.inputSignal = inputSignal;
|
|
55
55
|
this.outputSignal = outputSignal;
|
|
56
56
|
this.queueLength = queueLength;
|
|
57
|
+
this.pendingActivations = pendingActivations;
|
|
58
|
+
this.activeActivations = activeActivations;
|
|
57
59
|
this.hops = hops;
|
|
58
60
|
this.error = error;
|
|
59
61
|
}
|
|
@@ -62,7 +64,7 @@ var CNSStimulationResponseImpl = class {
|
|
|
62
64
|
}
|
|
63
65
|
};
|
|
64
66
|
var CNSStimulation = class {
|
|
65
|
-
constructor(cns, instanceNeuronQueue, options, onResponse) {
|
|
67
|
+
constructor(cns, instanceNeuronQueue, options, onResponse, onDrain) {
|
|
66
68
|
this.cns = cns;
|
|
67
69
|
this.options = options;
|
|
68
70
|
this.qHead = 0;
|
|
@@ -73,9 +75,20 @@ var CNSStimulation = class {
|
|
|
73
75
|
// Re-entrancy guard for the pump loop.
|
|
74
76
|
this.pumping = false;
|
|
75
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;
|
|
76
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;
|
|
77
89
|
this.isCompleted = false;
|
|
78
90
|
this.completionRejected = false;
|
|
91
|
+
this.onDrain = onDrain;
|
|
79
92
|
this.ctxStore = options?.ctx;
|
|
80
93
|
this.instanceNeuronQueue = instanceNeuronQueue;
|
|
81
94
|
this.onResponse = onResponse;
|
|
@@ -172,18 +185,28 @@ var CNSStimulation = class {
|
|
|
172
185
|
(cb) => {
|
|
173
186
|
this.activeOperations--;
|
|
174
187
|
this.activeTasks.delete(task);
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
if (
|
|
178
|
-
|
|
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();
|
|
179
197
|
}
|
|
180
198
|
},
|
|
181
199
|
(err) => {
|
|
182
200
|
this.activeOperations--;
|
|
183
201
|
this.activeTasks.delete(task);
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
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();
|
|
187
210
|
}
|
|
188
211
|
throw err;
|
|
189
212
|
}
|
|
@@ -199,10 +222,54 @@ var CNSStimulation = class {
|
|
|
199
222
|
this.pump();
|
|
200
223
|
}
|
|
201
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
|
+
}
|
|
202
269
|
tryResolveCompleted() {
|
|
203
270
|
if (this.isCompleted) return;
|
|
204
271
|
const noActive = this.activeOperations === 0;
|
|
205
|
-
const noPending = this.
|
|
272
|
+
const noPending = this.pendingActivations === 0;
|
|
206
273
|
const aborted = !!this.options?.abortSignal?.aborted;
|
|
207
274
|
if (noPending && noActive || aborted && noActive && !noPending) {
|
|
208
275
|
if (aborted) {
|
|
@@ -274,10 +341,15 @@ var CNSStimulation = class {
|
|
|
274
341
|
* Enqueue activation tasks directly into the stimulation queue
|
|
275
342
|
*/
|
|
276
343
|
enqueueTasks(tasks) {
|
|
277
|
-
|
|
278
|
-
|
|
344
|
+
this.beginTurn();
|
|
345
|
+
try {
|
|
346
|
+
for (const task of tasks) {
|
|
347
|
+
this.qEnqueue(task);
|
|
348
|
+
}
|
|
349
|
+
this.tryResolveCompleted();
|
|
350
|
+
} finally {
|
|
351
|
+
this.endTurn();
|
|
279
352
|
}
|
|
280
|
-
this.tryResolveCompleted();
|
|
281
353
|
}
|
|
282
354
|
get concurrencyEnabled() {
|
|
283
355
|
return this.options?.concurrency !== void 0 && this.options?.concurrency > 0;
|
|
@@ -378,14 +450,19 @@ var CNSStimulation = class {
|
|
|
378
450
|
}
|
|
379
451
|
}
|
|
380
452
|
if (!subscriber) {
|
|
453
|
+
const error = new Error(`Subscriber not found for activation task`);
|
|
381
454
|
this.pushFailedTask({
|
|
382
455
|
task: neuronActivationTask,
|
|
383
|
-
error
|
|
384
|
-
`Subscriber not found for activation task`
|
|
385
|
-
),
|
|
456
|
+
error,
|
|
386
457
|
aborted: false
|
|
387
458
|
});
|
|
388
459
|
return () => {
|
|
460
|
+
this.processResponseOrResponses(
|
|
461
|
+
void 0,
|
|
462
|
+
neuronActivationTask.input,
|
|
463
|
+
void 0,
|
|
464
|
+
error
|
|
465
|
+
);
|
|
389
466
|
};
|
|
390
467
|
}
|
|
391
468
|
const neuron2 = subscriber.neuron;
|
|
@@ -432,13 +509,13 @@ var CNSStimulation = class {
|
|
|
432
509
|
error: error instanceof Error ? error : new Error(String(error)),
|
|
433
510
|
aborted: isAborted
|
|
434
511
|
});
|
|
435
|
-
this.processResponseOrResponses(
|
|
436
|
-
neuron2,
|
|
437
|
-
inputSignal,
|
|
438
|
-
void 0,
|
|
439
|
-
error
|
|
440
|
-
);
|
|
441
512
|
return () => {
|
|
513
|
+
this.processResponseOrResponses(
|
|
514
|
+
neuron2,
|
|
515
|
+
inputSignal,
|
|
516
|
+
void 0,
|
|
517
|
+
error
|
|
518
|
+
);
|
|
442
519
|
};
|
|
443
520
|
}
|
|
444
521
|
const maxDuration = neuron2.maxDuration;
|
|
@@ -508,8 +585,26 @@ var CNSStimulation = class {
|
|
|
508
585
|
this.processResponse(emitter, inputSignal, void 0, error);
|
|
509
586
|
return;
|
|
510
587
|
}
|
|
511
|
-
|
|
512
|
-
|
|
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
|
+
);
|
|
513
608
|
}
|
|
514
609
|
return;
|
|
515
610
|
}
|
|
@@ -563,7 +658,9 @@ var CNSStimulation = class {
|
|
|
563
658
|
this.ctxStore,
|
|
564
659
|
inputSignal,
|
|
565
660
|
outputSignal,
|
|
566
|
-
this.
|
|
661
|
+
this.pendingActivations + this.activeOperations,
|
|
662
|
+
this.pendingActivations,
|
|
663
|
+
this.activeOperations,
|
|
567
664
|
this.options?.maxNeuronHops && emitter ? this.neuronVisitMap?.get(emitter) ?? 0 : void 0,
|
|
568
665
|
error
|
|
569
666
|
)
|
|
@@ -581,21 +678,35 @@ var CNSStimulation = class {
|
|
|
581
678
|
if (maybePromise && typeof maybePromise.then === "function") {
|
|
582
679
|
maybePromise.then(
|
|
583
680
|
() => {
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
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();
|
|
588
693
|
}
|
|
589
|
-
this.tryResolveCompleted();
|
|
590
694
|
},
|
|
591
695
|
(error2) => {
|
|
592
696
|
this.onResponseError = error2 instanceof Error ? error2 : new Error(String(error2));
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
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();
|
|
597
709
|
}
|
|
598
|
-
this.tryResolveCompleted();
|
|
599
710
|
}
|
|
600
711
|
);
|
|
601
712
|
return;
|
|
@@ -608,12 +719,21 @@ var CNSStimulation = class {
|
|
|
608
719
|
this.tryResolveCompleted();
|
|
609
720
|
}
|
|
610
721
|
responseToSignal(signalOrSignals) {
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
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();
|
|
614
736
|
}
|
|
615
|
-
this.processResponse(void 0, void 0, signalOrSignals);
|
|
616
|
-
this.tryResolveCompleted();
|
|
617
737
|
}
|
|
618
738
|
};
|
|
619
739
|
|
|
@@ -960,6 +1080,10 @@ var CNS = class {
|
|
|
960
1080
|
* Global response listeners applied to every stimulation.
|
|
961
1081
|
*/
|
|
962
1082
|
this.globalResponseListeners = [];
|
|
1083
|
+
/**
|
|
1084
|
+
* Global batch-boundary listeners applied to every stimulation.
|
|
1085
|
+
*/
|
|
1086
|
+
this.globalDrainListeners = [];
|
|
963
1087
|
this.network = new CNSNetwork(this.neurons);
|
|
964
1088
|
this.global = global;
|
|
965
1089
|
}
|
|
@@ -973,6 +1097,50 @@ var CNS = class {
|
|
|
973
1097
|
if (idx >= 0) this.globalResponseListeners.splice(idx, 1);
|
|
974
1098
|
};
|
|
975
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
|
+
}
|
|
976
1144
|
wrapOnResponse(local) {
|
|
977
1145
|
if (this.globalResponseListeners.length === 0 && !local) {
|
|
978
1146
|
return void 0;
|
|
@@ -1020,7 +1188,8 @@ var CNS = class {
|
|
|
1020
1188
|
this,
|
|
1021
1189
|
this.instanceNeuronQueue,
|
|
1022
1190
|
options,
|
|
1023
|
-
wrapped
|
|
1191
|
+
wrapped,
|
|
1192
|
+
this.wrapOnDrain(options?.onDrain)
|
|
1024
1193
|
);
|
|
1025
1194
|
stimulation.responseToSignal(signalOrSignals);
|
|
1026
1195
|
return stimulation;
|
|
@@ -1034,7 +1203,8 @@ var CNS = class {
|
|
|
1034
1203
|
this,
|
|
1035
1204
|
this.instanceNeuronQueue,
|
|
1036
1205
|
options,
|
|
1037
|
-
wrapped
|
|
1206
|
+
wrapped,
|
|
1207
|
+
this.wrapOnDrain(options?.onDrain)
|
|
1038
1208
|
);
|
|
1039
1209
|
stimulation.enqueueTasks(tasks);
|
|
1040
1210
|
return stimulation;
|