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