@cnstra/core 1.3.0 → 1.4.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/README.md +73 -2
- package/dist/CNS.d.ts +33 -7
- package/dist/CNS.d.ts.map +1 -1
- package/dist/CNSCollateral.d.ts +5 -5
- package/dist/CNSCollateral.d.ts.map +1 -1
- package/dist/CNSFunctionalQueue.d.ts +3 -3
- package/dist/CNSFunctionalQueue.d.ts.map +1 -1
- package/dist/CNSInstanceNeuronQueue.d.ts +8 -0
- package/dist/CNSInstanceNeuronQueue.d.ts.map +1 -0
- package/dist/CNSStimulation.d.ts +13 -11
- package/dist/CNSStimulation.d.ts.map +1 -1
- package/dist/CNSTaskQueue.d.ts +12 -0
- package/dist/CNSTaskQueue.d.ts.map +1 -0
- package/dist/factory/index.d.ts +12 -7
- package/dist/factory/index.d.ts.map +1 -1
- package/dist/index.cjs +386 -79
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +386 -79
- package/dist/index.js.map +1 -1
- package/dist/interfaces/ICNS.d.ts +14 -5
- package/dist/interfaces/ICNS.d.ts.map +1 -1
- package/dist/interfaces/ICNSCollateral.d.ts +3 -3
- package/dist/interfaces/ICNSCollateral.d.ts.map +1 -1
- package/dist/types/TCNSAxon.d.ts +3 -1
- package/dist/types/TCNSAxon.d.ts.map +1 -1
- package/dist/types/TCNSDendrite.d.ts +6 -3
- package/dist/types/TCNSDendrite.d.ts.map +1 -1
- package/dist/types/TCNSNeuron.d.ts +5 -3
- package/dist/types/TCNSNeuron.d.ts.map +1 -1
- package/dist/types/TCNSNeuronResponseReturn.d.ts +1 -1
- package/dist/types/TCNSNeuronResponseReturn.d.ts.map +1 -1
- package/dist/types/TCNSSignal.d.ts +2 -2
- package/dist/types/TCNSSignal.d.ts.map +1 -1
- package/dist/types/TCNSStimulationOptions.d.ts +2 -2
- package/dist/types/TCNSStimulationOptions.d.ts.map +1 -1
- package/dist/types/TCNSStimulationQueueItem.d.ts +3 -3
- package/dist/types/TCNSStimulationQueueItem.d.ts.map +1 -1
- package/dist/types/TCNSStimulationResponse.d.ts +3 -3
- package/dist/types/TCNSStimulationResponse.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -132,7 +132,7 @@ var CNSFunctionalQueue = class {
|
|
|
132
132
|
|
|
133
133
|
// src/CNSStimulation.ts
|
|
134
134
|
var CNSStimulation = class {
|
|
135
|
-
constructor(cns, options) {
|
|
135
|
+
constructor(cns, instanceNeuronQueue, options) {
|
|
136
136
|
this.cns = cns;
|
|
137
137
|
this.options = options;
|
|
138
138
|
this.scheduledCount = 0;
|
|
@@ -147,6 +147,7 @@ var CNSStimulation = class {
|
|
|
147
147
|
options?.concurrency,
|
|
148
148
|
options?.abortSignal
|
|
149
149
|
);
|
|
150
|
+
this.instanceNeuronQueue = instanceNeuronQueue;
|
|
150
151
|
if (this.options?.maxNeuronHops) {
|
|
151
152
|
this.nueronVisitMap = /* @__PURE__ */ new Map();
|
|
152
153
|
}
|
|
@@ -175,7 +176,7 @@ var CNSStimulation = class {
|
|
|
175
176
|
* Increment the active count for the SCC containing this neuron
|
|
176
177
|
*/
|
|
177
178
|
incrementSccCount(neuronId) {
|
|
178
|
-
const sccIndex = this.cns.
|
|
179
|
+
const sccIndex = this.cns.getSccIndexByNeuronName(neuronId);
|
|
179
180
|
if (sccIndex === void 0) return;
|
|
180
181
|
const currentCount = this.activeSccCounts.get(sccIndex) || 0;
|
|
181
182
|
this.activeSccCounts.set(sccIndex, currentCount + 1);
|
|
@@ -184,7 +185,7 @@ var CNSStimulation = class {
|
|
|
184
185
|
* Decrement the active count for the SCC containing this neuron
|
|
185
186
|
*/
|
|
186
187
|
decrementSccCount(neuronId) {
|
|
187
|
-
const sccIndex = this.cns.
|
|
188
|
+
const sccIndex = this.cns.getSccIndexByNeuronName(neuronId);
|
|
188
189
|
if (sccIndex === void 0) return;
|
|
189
190
|
const currentCount = this.activeSccCounts.get(sccIndex) || 0;
|
|
190
191
|
const nextCount = Math.max(0, currentCount - 1);
|
|
@@ -206,25 +207,25 @@ var CNSStimulation = class {
|
|
|
206
207
|
);
|
|
207
208
|
}
|
|
208
209
|
cleanupCtxIfNeeded(neuron2) {
|
|
209
|
-
if (this.autoCleanupContextsEnabled && this.canNeuronBeGuaranteedDone(neuron2.
|
|
210
|
-
this.ctx.delete(neuron2.
|
|
210
|
+
if (this.autoCleanupContextsEnabled && this.canNeuronBeGuaranteedDone(neuron2.name)) {
|
|
211
|
+
this.ctx.delete(neuron2.name);
|
|
211
212
|
}
|
|
212
213
|
}
|
|
213
214
|
createSubscriberQueueItem(subscriber, inputSignal) {
|
|
214
215
|
if (this.options?.maxNeuronHops) {
|
|
215
|
-
if (this.nueronVisitMap?.get(subscriber.neuron.
|
|
216
|
+
if (this.nueronVisitMap?.get(subscriber.neuron.name) ?? 0 >= this.options?.maxNeuronHops) {
|
|
216
217
|
throw new Error(
|
|
217
|
-
`Max neuron hops reached for neuron "${subscriber.neuron.
|
|
218
|
+
`Max neuron hops reached for neuron "${subscriber.neuron.name}" when trying to enqueue subscriber "${subscriber.dendrite.collateral.type}" in stimulation "${this.id}"`
|
|
218
219
|
);
|
|
219
220
|
} else {
|
|
220
221
|
this.nueronVisitMap?.set(
|
|
221
|
-
subscriber.neuron.
|
|
222
|
-
(this.nueronVisitMap?.get(subscriber.neuron.
|
|
222
|
+
subscriber.neuron.name,
|
|
223
|
+
(this.nueronVisitMap?.get(subscriber.neuron.name) ?? 0) + 1
|
|
223
224
|
);
|
|
224
225
|
}
|
|
225
226
|
}
|
|
226
227
|
return {
|
|
227
|
-
neuronId: subscriber.neuron.
|
|
228
|
+
neuronId: subscriber.neuron.name,
|
|
228
229
|
subscriber,
|
|
229
230
|
inputSignal
|
|
230
231
|
};
|
|
@@ -232,52 +233,56 @@ var CNSStimulation = class {
|
|
|
232
233
|
processQueueItem(item) {
|
|
233
234
|
const { subscriber, inputSignal } = item;
|
|
234
235
|
const { neuron: neuron2, dendrite } = subscriber;
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
if (response instanceof Promise) {
|
|
246
|
-
return response.then(
|
|
247
|
-
(signal) => {
|
|
248
|
-
return () => {
|
|
249
|
-
this.markNeuronInactive(neuron2.id);
|
|
250
|
-
return this.processResponse(
|
|
251
|
-
inputSignal,
|
|
252
|
-
signal
|
|
253
|
-
);
|
|
254
|
-
};
|
|
255
|
-
},
|
|
256
|
-
(error) => {
|
|
257
|
-
return () => {
|
|
258
|
-
this.markNeuronInactive(neuron2.id);
|
|
259
|
-
return this.processResponse(
|
|
260
|
-
inputSignal,
|
|
261
|
-
void 0,
|
|
262
|
-
error
|
|
263
|
-
);
|
|
264
|
-
};
|
|
236
|
+
const starter = () => {
|
|
237
|
+
this.markNeuronActive(neuron2.name);
|
|
238
|
+
const response = dendrite.response(
|
|
239
|
+
inputSignal?.payload,
|
|
240
|
+
neuron2.axon,
|
|
241
|
+
{
|
|
242
|
+
get: () => this.ctx.get(neuron2.name),
|
|
243
|
+
set: (value) => this.ctx.set(neuron2.name, value),
|
|
244
|
+
abortSignal: this.options?.abortSignal,
|
|
245
|
+
cns: this.cns
|
|
265
246
|
}
|
|
266
247
|
);
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
248
|
+
if (response instanceof Promise) {
|
|
249
|
+
return response.then(
|
|
250
|
+
(signal) => {
|
|
251
|
+
return () => {
|
|
252
|
+
this.markNeuronInactive(neuron2.name);
|
|
253
|
+
return this.processResponse(
|
|
254
|
+
inputSignal,
|
|
255
|
+
signal
|
|
256
|
+
);
|
|
257
|
+
};
|
|
258
|
+
},
|
|
259
|
+
(error) => {
|
|
260
|
+
return () => {
|
|
261
|
+
this.markNeuronInactive(neuron2.name);
|
|
262
|
+
return this.processResponse(
|
|
263
|
+
inputSignal,
|
|
264
|
+
void 0,
|
|
265
|
+
error
|
|
266
|
+
);
|
|
267
|
+
};
|
|
268
|
+
}
|
|
273
269
|
);
|
|
274
|
-
}
|
|
275
|
-
|
|
270
|
+
} else {
|
|
271
|
+
return () => {
|
|
272
|
+
this.markNeuronInactive(neuron2.name);
|
|
273
|
+
return this.processResponse(
|
|
274
|
+
inputSignal,
|
|
275
|
+
response
|
|
276
|
+
);
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
return this.instanceNeuronQueue.run(neuron2, starter);
|
|
276
281
|
}
|
|
277
282
|
processResponse(inputSignal, outputSignal, error) {
|
|
278
283
|
const collateral2 = outputSignal?.collateral;
|
|
279
|
-
const ownerNeuron = collateral2 ? this.cns.getParentNeuronByCollateralId(collateral2.
|
|
280
|
-
const subscribers = collateral2 ? this.cns.getSubscribers(collateral2.
|
|
284
|
+
const ownerNeuron = collateral2 ? this.cns.getParentNeuronByCollateralId(collateral2.type) : void 0;
|
|
285
|
+
const subscribers = collateral2 ? this.cns.getSubscribers(collateral2.type) : [];
|
|
281
286
|
const subscriberQueueItems = [];
|
|
282
287
|
if (collateral2 && !error) {
|
|
283
288
|
if (ownerNeuron) {
|
|
@@ -295,7 +300,7 @@ var CNSStimulation = class {
|
|
|
295
300
|
outputSignal,
|
|
296
301
|
ctx: this.ctx,
|
|
297
302
|
queueLength: this.queue.length + this.scheduledCount,
|
|
298
|
-
hops: this.options?.maxNeuronHops && ownerNeuron ? this.nueronVisitMap?.get(ownerNeuron.
|
|
303
|
+
hops: this.options?.maxNeuronHops && ownerNeuron ? this.nueronVisitMap?.get(ownerNeuron.name) ?? 0 : void 0,
|
|
299
304
|
error
|
|
300
305
|
});
|
|
301
306
|
for (let i = 0; i < subscriberQueueItems.length; i++) {
|
|
@@ -305,15 +310,152 @@ var CNSStimulation = class {
|
|
|
305
310
|
}
|
|
306
311
|
responseToSignal(signal) {
|
|
307
312
|
const ownerNeuron = this.cns.getParentNeuronByCollateralId(
|
|
308
|
-
signal.collateral.
|
|
313
|
+
signal.collateral.type
|
|
309
314
|
);
|
|
310
315
|
if (ownerNeuron) {
|
|
311
|
-
this.markNeuronActive(ownerNeuron.
|
|
316
|
+
this.markNeuronActive(ownerNeuron.name);
|
|
312
317
|
}
|
|
313
318
|
this.processResponse(void 0, signal);
|
|
314
319
|
}
|
|
315
320
|
};
|
|
316
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
|
+
|
|
317
459
|
// src/CNS.ts
|
|
318
460
|
var CNS = class {
|
|
319
461
|
constructor(neurons, options) {
|
|
@@ -342,17 +484,139 @@ var CNS = class {
|
|
|
342
484
|
* Precomputed ancestor sets for each SCC for fast reachability checks
|
|
343
485
|
*/
|
|
344
486
|
this.sccAncestors = /* @__PURE__ */ new Map();
|
|
487
|
+
/**
|
|
488
|
+
* Global per-neuron concurrency gates shared across all stimulations.
|
|
489
|
+
*/
|
|
490
|
+
this.neuronGates = /* @__PURE__ */ new Map();
|
|
491
|
+
/**
|
|
492
|
+
* Global task queue used by stimulation to schedule per-neuron gated work.
|
|
493
|
+
* Does not replace per-stimulation concurrency; only coordinates global per-neuron limits.
|
|
494
|
+
*/
|
|
495
|
+
this.globalTaskQueue = new CNSTaskQueue();
|
|
496
|
+
this.instanceNeuronQueue = new CNSInstanceNeuronQueue();
|
|
497
|
+
/**
|
|
498
|
+
* Global response listeners applied to every stimulation.
|
|
499
|
+
*/
|
|
500
|
+
this.globalResponseListeners = [];
|
|
501
|
+
this.validateUniqueIdentifiers();
|
|
345
502
|
this.buildIndexes();
|
|
346
503
|
if (this.options?.autoCleanupContexts) {
|
|
347
504
|
this.buildSCC();
|
|
348
505
|
}
|
|
349
506
|
}
|
|
507
|
+
/**
|
|
508
|
+
* Validate uniqueness constraints:
|
|
509
|
+
* - neuron names are unique and non-empty
|
|
510
|
+
* - axon collateral types are globally unique (single owner)
|
|
511
|
+
* - the same collateral instance cannot be owned by multiple neurons
|
|
512
|
+
* Throws aggregated error if violations are found.
|
|
513
|
+
*/
|
|
514
|
+
validateUniqueIdentifiers() {
|
|
515
|
+
const errors = [];
|
|
516
|
+
const seenNeuronNames = /* @__PURE__ */ new Set();
|
|
517
|
+
for (const neuron2 of this.neurons) {
|
|
518
|
+
const name = neuron2?.name ?? "";
|
|
519
|
+
if (!name || typeof name !== "string" || name.trim().length === 0) {
|
|
520
|
+
errors.push(`Neuron has empty or invalid name: "${name}"`);
|
|
521
|
+
} else if (seenNeuronNames.has(name)) {
|
|
522
|
+
errors.push(`Duplicate neuron name: "${name}"`);
|
|
523
|
+
} else {
|
|
524
|
+
seenNeuronNames.add(name);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
if (errors.length > 0) {
|
|
528
|
+
throw new Error(
|
|
529
|
+
`[CNS] Topology uniqueness validation failed:
|
|
530
|
+
- ` + errors.join(`
|
|
531
|
+
- `)
|
|
532
|
+
);
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
addResponseListener(listener) {
|
|
536
|
+
this.globalResponseListeners.push(listener);
|
|
537
|
+
let active = true;
|
|
538
|
+
return () => {
|
|
539
|
+
if (!active) return;
|
|
540
|
+
active = false;
|
|
541
|
+
const idx = this.globalResponseListeners.indexOf(listener);
|
|
542
|
+
if (idx >= 0) this.globalResponseListeners.splice(idx, 1);
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
wrapOnResponse(local) {
|
|
546
|
+
if (this.globalResponseListeners.length === 0 && !local) {
|
|
547
|
+
return () => {
|
|
548
|
+
};
|
|
549
|
+
}
|
|
550
|
+
return (r) => {
|
|
551
|
+
try {
|
|
552
|
+
if (local) local(r);
|
|
553
|
+
} finally {
|
|
554
|
+
for (let i = 0; i < this.globalResponseListeners.length; i++) {
|
|
555
|
+
try {
|
|
556
|
+
this.globalResponseListeners[i](r);
|
|
557
|
+
} catch (e) {
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
};
|
|
562
|
+
}
|
|
563
|
+
runWithNeuronConcurrency(neuron2, fn) {
|
|
564
|
+
const limit = neuron2.concurrency;
|
|
565
|
+
if (limit === void 0 || limit <= 0) {
|
|
566
|
+
return this.globalTaskQueue.enqueue(fn);
|
|
567
|
+
}
|
|
568
|
+
let gate = this.neuronGates.get(neuron2.name);
|
|
569
|
+
if (!gate) {
|
|
570
|
+
gate = { limit, active: 0, waiters: [] };
|
|
571
|
+
this.neuronGates.set(neuron2.name, gate);
|
|
572
|
+
} else {
|
|
573
|
+
gate.limit = limit;
|
|
574
|
+
}
|
|
575
|
+
const wrap = (cb) => {
|
|
576
|
+
return () => {
|
|
577
|
+
try {
|
|
578
|
+
cb();
|
|
579
|
+
} finally {
|
|
580
|
+
gate.active = Math.max(0, gate.active - 1);
|
|
581
|
+
if (gate.waiters.length > 0) {
|
|
582
|
+
const next = gate.waiters.shift();
|
|
583
|
+
next();
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
};
|
|
587
|
+
};
|
|
588
|
+
if (gate.active < gate.limit) {
|
|
589
|
+
gate.active++;
|
|
590
|
+
return this.globalTaskQueue.enqueue(() => {
|
|
591
|
+
const ret = fn();
|
|
592
|
+
if (ret && typeof ret.then === "function") {
|
|
593
|
+
return ret.then((cb) => wrap(cb));
|
|
594
|
+
}
|
|
595
|
+
return wrap(ret);
|
|
596
|
+
});
|
|
597
|
+
}
|
|
598
|
+
return new Promise((resolve) => {
|
|
599
|
+
const starter = () => {
|
|
600
|
+
gate.active++;
|
|
601
|
+
this.globalTaskQueue.enqueue(() => {
|
|
602
|
+
const ret = fn();
|
|
603
|
+
if (ret && typeof ret.then === "function") {
|
|
604
|
+
return ret.then(
|
|
605
|
+
(cb) => wrap(cb)
|
|
606
|
+
);
|
|
607
|
+
}
|
|
608
|
+
return wrap(ret);
|
|
609
|
+
}).then(resolve);
|
|
610
|
+
};
|
|
611
|
+
gate.waiters.push(starter);
|
|
612
|
+
});
|
|
613
|
+
}
|
|
350
614
|
buildIndexes() {
|
|
351
615
|
this.subIndex.clear();
|
|
352
616
|
this.parentNeuronByCollateralId.clear();
|
|
353
617
|
for (const neuron2 of this.neurons) {
|
|
354
618
|
for (const dendrite of neuron2.dendrites) {
|
|
355
|
-
const key = dendrite.collateral.
|
|
619
|
+
const key = dendrite.collateral.type;
|
|
356
620
|
const arr = this.subIndex.get(key) ?? [];
|
|
357
621
|
arr.push({ neuron: neuron2, dendrite });
|
|
358
622
|
this.subIndex.set(
|
|
@@ -362,7 +626,7 @@ var CNS = class {
|
|
|
362
626
|
}
|
|
363
627
|
Object.values(neuron2.axon).forEach((collateral2) => {
|
|
364
628
|
this.parentNeuronByCollateralId.set(
|
|
365
|
-
collateral2.
|
|
629
|
+
collateral2.type,
|
|
366
630
|
neuron2
|
|
367
631
|
);
|
|
368
632
|
});
|
|
@@ -373,7 +637,7 @@ var CNS = class {
|
|
|
373
637
|
*/
|
|
374
638
|
buildNeuronGraph() {
|
|
375
639
|
const graph = /* @__PURE__ */ new Map();
|
|
376
|
-
const neuronIds = this.neurons.map((n) => n.
|
|
640
|
+
const neuronIds = this.neurons.map((n) => n.name);
|
|
377
641
|
for (const neuronId of neuronIds) {
|
|
378
642
|
graph.set(neuronId, /* @__PURE__ */ new Set());
|
|
379
643
|
}
|
|
@@ -381,15 +645,15 @@ var CNS = class {
|
|
|
381
645
|
const reachableNeurons = /* @__PURE__ */ new Set();
|
|
382
646
|
for (const axonKey in neuron2.axon) {
|
|
383
647
|
const collateral2 = neuron2.axon[axonKey];
|
|
384
|
-
const
|
|
385
|
-
const subscribers = this.subIndex.get(
|
|
648
|
+
const collateralType = collateral2.type;
|
|
649
|
+
const subscribers = this.subIndex.get(collateralType);
|
|
386
650
|
if (subscribers) {
|
|
387
651
|
for (const { neuron: targetNeuron } of subscribers) {
|
|
388
|
-
reachableNeurons.add(targetNeuron.
|
|
652
|
+
reachableNeurons.add(targetNeuron.name);
|
|
389
653
|
}
|
|
390
654
|
}
|
|
391
655
|
}
|
|
392
|
-
graph.set(neuron2.
|
|
656
|
+
graph.set(neuron2.name, reachableNeurons);
|
|
393
657
|
}
|
|
394
658
|
return graph;
|
|
395
659
|
}
|
|
@@ -398,7 +662,7 @@ var CNS = class {
|
|
|
398
662
|
*/
|
|
399
663
|
buildSCC() {
|
|
400
664
|
const graph = this.buildNeuronGraph();
|
|
401
|
-
const neuronIds = this.neurons.map((n) => n.
|
|
665
|
+
const neuronIds = this.neurons.map((n) => n.name);
|
|
402
666
|
const index = /* @__PURE__ */ new Map();
|
|
403
667
|
const lowlink = /* @__PURE__ */ new Map();
|
|
404
668
|
const onStack = /* @__PURE__ */ new Set();
|
|
@@ -525,23 +789,23 @@ var CNS = class {
|
|
|
525
789
|
* Returns true if the neuron is in a strongly connected component with more than one member,
|
|
526
790
|
* or if it's in a single-member SCC that can reach itself.
|
|
527
791
|
*/
|
|
528
|
-
|
|
529
|
-
const sccIndex = this.neuronToSCC.get(
|
|
792
|
+
getSCCSetByNeuronName(neuronName) {
|
|
793
|
+
const sccIndex = this.neuronToSCC.get(neuronName);
|
|
530
794
|
if (sccIndex === void 0) return;
|
|
531
795
|
return this.stronglyConnectedComponents[sccIndex];
|
|
532
796
|
}
|
|
533
797
|
/**
|
|
534
798
|
* Get the SCC index for a given neuron ID
|
|
535
799
|
*/
|
|
536
|
-
|
|
537
|
-
return this.neuronToSCC.get(
|
|
800
|
+
getSccIndexByNeuronName(neuronName) {
|
|
801
|
+
return this.neuronToSCC.get(neuronName);
|
|
538
802
|
}
|
|
539
803
|
/**
|
|
540
804
|
* Check if a neuron can be guaranteed not to be visited again during the current propagation.
|
|
541
805
|
* This is the core logic for safe context cleanup.
|
|
542
806
|
*/
|
|
543
|
-
canNeuronBeGuaranteedDone(
|
|
544
|
-
const sccIndex = this.neuronToSCC.get(
|
|
807
|
+
canNeuronBeGuaranteedDone(neuronName, activeSccCounts) {
|
|
808
|
+
const sccIndex = this.neuronToSCC.get(neuronName);
|
|
545
809
|
if (sccIndex === void 0) return true;
|
|
546
810
|
if (activeSccCounts.get(sccIndex) && activeSccCounts.get(sccIndex) > 0) {
|
|
547
811
|
return false;
|
|
@@ -555,22 +819,26 @@ var CNS = class {
|
|
|
555
819
|
}
|
|
556
820
|
return true;
|
|
557
821
|
}
|
|
558
|
-
getParentNeuronByCollateralId(
|
|
559
|
-
return this.parentNeuronByCollateralId.get(
|
|
822
|
+
getParentNeuronByCollateralId(collateralType) {
|
|
823
|
+
return this.parentNeuronByCollateralId.get(collateralType);
|
|
560
824
|
}
|
|
561
|
-
getSubscribers(
|
|
562
|
-
return this.subIndex.get(
|
|
825
|
+
getSubscribers(collateralType) {
|
|
826
|
+
return this.subIndex.get(collateralType) ?? [];
|
|
563
827
|
}
|
|
564
828
|
stimulate(signal, options) {
|
|
565
|
-
const
|
|
829
|
+
const wrapped = this.wrapOnResponse(options?.onResponse);
|
|
830
|
+
const stimulation = new CNSStimulation(this, this.instanceNeuronQueue, {
|
|
831
|
+
...options,
|
|
832
|
+
onResponse: wrapped
|
|
833
|
+
});
|
|
566
834
|
stimulation.responseToSignal(signal);
|
|
567
835
|
}
|
|
568
836
|
};
|
|
569
837
|
|
|
570
838
|
// src/CNSCollateral.ts
|
|
571
839
|
var CNSCollateral = class {
|
|
572
|
-
constructor(
|
|
573
|
-
this.
|
|
840
|
+
constructor(type) {
|
|
841
|
+
this.type = type;
|
|
574
842
|
}
|
|
575
843
|
createSignal(payload) {
|
|
576
844
|
return { collateral: this, payload };
|
|
@@ -578,12 +846,30 @@ var CNSCollateral = class {
|
|
|
578
846
|
};
|
|
579
847
|
|
|
580
848
|
// src/factory/index.ts
|
|
581
|
-
var collateral = (
|
|
582
|
-
var neuron = (
|
|
849
|
+
var collateral = (type) => new CNSCollateral(type);
|
|
850
|
+
var neuron = (name, axon) => {
|
|
583
851
|
const dendrites = [];
|
|
584
852
|
const api = {
|
|
853
|
+
setConcurrency: (n) => {
|
|
854
|
+
api.concurrency = n;
|
|
855
|
+
return api;
|
|
856
|
+
},
|
|
857
|
+
bind: ((axon2, newDendrites) => {
|
|
858
|
+
for (const key in newDendrites) {
|
|
859
|
+
const value = newDendrites[key];
|
|
860
|
+
const dendrite = typeof value === "function" ? {
|
|
861
|
+
collateral: axon2[key],
|
|
862
|
+
response: value
|
|
863
|
+
} : value;
|
|
864
|
+
dendrites.push(
|
|
865
|
+
dendrite
|
|
866
|
+
);
|
|
867
|
+
}
|
|
868
|
+
return api;
|
|
869
|
+
}),
|
|
585
870
|
axon,
|
|
586
|
-
|
|
871
|
+
name,
|
|
872
|
+
concurrency: void 0,
|
|
587
873
|
dendrites,
|
|
588
874
|
dendrite(s) {
|
|
589
875
|
dendrites.push(
|
|
@@ -595,8 +881,29 @@ var neuron = (id, axon) => {
|
|
|
595
881
|
return api;
|
|
596
882
|
};
|
|
597
883
|
var withCtx = () => ({
|
|
598
|
-
neuron: (
|
|
599
|
-
return neuron(
|
|
884
|
+
neuron: (name, axon) => {
|
|
885
|
+
return neuron(name, axon);
|
|
886
|
+
}
|
|
887
|
+
});
|
|
888
|
+
var fa = {
|
|
889
|
+
test1: new CNSCollateral("test1"),
|
|
890
|
+
test2: new CNSCollateral("test2")
|
|
891
|
+
};
|
|
892
|
+
var fb = {
|
|
893
|
+
test4: collateral("test4"),
|
|
894
|
+
test5: collateral("test5")
|
|
895
|
+
};
|
|
896
|
+
withCtx().neuron("test", {
|
|
897
|
+
test1: fa.test1,
|
|
898
|
+
test2: fa.test2
|
|
899
|
+
}).bind(fb, {
|
|
900
|
+
test4: (payload, axon) => {
|
|
901
|
+
const ok = payload.test44;
|
|
902
|
+
return axon.test1.createSignal(ok);
|
|
903
|
+
},
|
|
904
|
+
test5: (payload, axon) => {
|
|
905
|
+
const ok2 = payload.test44;
|
|
906
|
+
return axon.test2.createSignal(ok2);
|
|
600
907
|
}
|
|
601
908
|
});
|
|
602
909
|
|