@cnstra/core 1.4.0 → 1.6.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.
Files changed (37) hide show
  1. package/README.md +112 -36
  2. package/dist/CNS.d.ts +18 -8
  3. package/dist/CNS.d.ts.map +1 -1
  4. package/dist/CNSCollateral.d.ts +5 -5
  5. package/dist/CNSFunctionalQueue.d.ts +4 -4
  6. package/dist/CNSFunctionalQueue.d.ts.map +1 -1
  7. package/dist/CNSStimulation.d.ts +14 -10
  8. package/dist/CNSStimulation.d.ts.map +1 -1
  9. package/dist/factory/index.d.ts +14 -10
  10. package/dist/factory/index.d.ts.map +1 -1
  11. package/dist/index.cjs +214 -76
  12. package/dist/index.cjs.map +1 -1
  13. package/dist/index.d.ts +4 -0
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +214 -77
  16. package/dist/index.js.map +1 -1
  17. package/dist/interfaces/ICNS.d.ts +11 -3
  18. package/dist/interfaces/ICNS.d.ts.map +1 -1
  19. package/dist/interfaces/ICNSCollateral.d.ts +3 -3
  20. package/dist/types/TCNSAxon.d.ts +2 -2
  21. package/dist/types/TCNSDendrite.d.ts +6 -4
  22. package/dist/types/TCNSDendrite.d.ts.map +1 -1
  23. package/dist/types/TCNSLocalContextValueStore.d.ts +1 -0
  24. package/dist/types/TCNSLocalContextValueStore.d.ts.map +1 -1
  25. package/dist/types/TCNSNeuron.d.ts +4 -2
  26. package/dist/types/TCNSNeuron.d.ts.map +1 -1
  27. package/dist/types/TCNSNeuronResponseReturn.d.ts +1 -1
  28. package/dist/types/TCNSNeuronResponseReturn.d.ts.map +1 -1
  29. package/dist/types/TCNSSerializedSignal.d.ts +10 -0
  30. package/dist/types/TCNSSerializedSignal.d.ts.map +1 -0
  31. package/dist/types/TCNSSignal.d.ts +2 -3
  32. package/dist/types/TCNSSignal.d.ts.map +1 -1
  33. package/dist/types/TCNSStimulationOptions.d.ts +3 -3
  34. package/dist/types/TCNSStimulationOptions.d.ts.map +1 -1
  35. package/dist/types/TCNSStimulationQueueItem.d.ts +2 -2
  36. package/dist/types/TCNSStimulationResponse.d.ts +3 -3
  37. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -106,7 +106,7 @@ var CNSFunctionalQueue = class {
106
106
  throw err;
107
107
  }
108
108
  );
109
- break;
109
+ continue;
110
110
  }
111
111
  this.activeOperations--;
112
112
  if (typeof ret === "function") ret();
@@ -136,6 +136,11 @@ var CNSStimulation = class {
136
136
  this.cns = cns;
137
137
  this.options = options;
138
138
  this.scheduledCount = 0;
139
+ this.completed = new Promise((resolve, reject) => {
140
+ this.resolveCompleted = resolve;
141
+ this.rejectCompleted = reject;
142
+ });
143
+ this.isCompleted = false;
139
144
  /**
140
145
  * Track active SCCs: SCC index -> count of active neurons in that SCC
141
146
  */
@@ -151,6 +156,24 @@ var CNSStimulation = class {
151
156
  if (this.options?.maxNeuronHops) {
152
157
  this.nueronVisitMap = /* @__PURE__ */ new Map();
153
158
  }
159
+ if (this.options?.abortSignal) {
160
+ this.options.abortSignal.addEventListener("abort", () => {
161
+ this.tryResolveCompleted();
162
+ });
163
+ }
164
+ }
165
+ tryResolveCompleted() {
166
+ if (this.isCompleted) return;
167
+ const noActive = this.queue.getActiveOperationsCount() === 0;
168
+ const noPending = this.queue.length + this.scheduledCount === 0;
169
+ const aborted = !!this.options?.abortSignal?.aborted;
170
+ if (noPending && noActive || aborted && noActive) {
171
+ this.isCompleted = true;
172
+ this.resolveCompleted();
173
+ }
174
+ }
175
+ waitUntilComplete() {
176
+ return this.completed;
154
177
  }
155
178
  get concurrencyEnabled() {
156
179
  return this.options?.concurrency !== void 0 && this.options?.concurrency > 0;
@@ -215,7 +238,7 @@ var CNSStimulation = class {
215
238
  if (this.options?.maxNeuronHops) {
216
239
  if (this.nueronVisitMap?.get(subscriber.neuron.name) ?? 0 >= this.options?.maxNeuronHops) {
217
240
  throw new Error(
218
- `Max neuron hops reached for neuron "${subscriber.neuron.name}" when trying to enqueue subscriber "${subscriber.dendrite.collateral.type}" in stimulation "${this.id}"`
241
+ `Max neuron hops reached for neuron "${subscriber.neuron.name}" when trying to enqueue subscriber "${subscriber.dendrite.collateral.name}" in stimulation "${this.id}"`
219
242
  );
220
243
  } else {
221
244
  this.nueronVisitMap?.set(
@@ -224,15 +247,31 @@ var CNSStimulation = class {
224
247
  );
225
248
  }
226
249
  }
227
- return {
250
+ const serialized = {
228
251
  neuronId: subscriber.neuron.name,
229
- subscriber,
230
- inputSignal
252
+ dendriteCollateralName: subscriber.dendrite.collateral.name,
253
+ input: inputSignal ? {
254
+ collateralName: inputSignal.collateralName,
255
+ payload: inputSignal.payload
256
+ } : void 0
231
257
  };
258
+ return serialized;
232
259
  }
233
260
  processQueueItem(item) {
234
- const { subscriber, inputSignal } = item;
235
- const { neuron: neuron2, dendrite } = subscriber;
261
+ const subscribers = this.cns.getSubscribers(
262
+ item.dendriteCollateralName
263
+ );
264
+ const subscriber = subscribers.find(
265
+ (s) => s.neuron.name === item.neuronId
266
+ );
267
+ if (!subscriber) return () => {
268
+ };
269
+ const neuron2 = subscriber.neuron;
270
+ const dendrite = subscriber.dendrite;
271
+ const inputSignal = item.input ? {
272
+ collateralName: item.input.collateralName,
273
+ payload: item.input.payload
274
+ } : void 0;
236
275
  const starter = () => {
237
276
  this.markNeuronActive(neuron2.name);
238
277
  const response = dendrite.response(
@@ -241,16 +280,40 @@ var CNSStimulation = class {
241
280
  {
242
281
  get: () => this.ctx.get(neuron2.name),
243
282
  set: (value) => this.ctx.set(neuron2.name, value),
283
+ delete: () => this.ctx.delete(neuron2.name),
244
284
  abortSignal: this.options?.abortSignal,
245
- cns: this.cns
285
+ cns: this.cns,
286
+ stimulationId: this.id
246
287
  }
247
288
  );
248
- if (response instanceof Promise) {
249
- return response.then(
289
+ const maxDuration = neuron2.maxDuration;
290
+ const asPromise = response instanceof Promise ? response : Promise.resolve(response);
291
+ const timedPromise = maxDuration && maxDuration > 0 ? new Promise((resolve, reject) => {
292
+ const t = setTimeout(() => {
293
+ const err = new Error(
294
+ `Neuron "${String(
295
+ neuron2.name
296
+ )}" exceeded maxDuration ${maxDuration}ms`
297
+ );
298
+ reject(err);
299
+ }, maxDuration);
300
+ asPromise.then(
301
+ (v) => {
302
+ clearTimeout(t);
303
+ resolve(v);
304
+ },
305
+ (e) => {
306
+ clearTimeout(t);
307
+ reject(e);
308
+ }
309
+ );
310
+ }) : asPromise;
311
+ if (response instanceof Promise || maxDuration) {
312
+ return timedPromise.then(
250
313
  (signal) => {
251
314
  return () => {
252
315
  this.markNeuronInactive(neuron2.name);
253
- return this.processResponse(
316
+ return this.processResponseOrResponses(
254
317
  inputSignal,
255
318
  signal
256
319
  );
@@ -259,7 +322,7 @@ var CNSStimulation = class {
259
322
  (error) => {
260
323
  return () => {
261
324
  this.markNeuronInactive(neuron2.name);
262
- return this.processResponse(
325
+ return this.processResponseOrResponses(
263
326
  inputSignal,
264
327
  void 0,
265
328
  error
@@ -270,7 +333,7 @@ var CNSStimulation = class {
270
333
  } else {
271
334
  return () => {
272
335
  this.markNeuronInactive(neuron2.name);
273
- return this.processResponse(
336
+ return this.processResponseOrResponses(
274
337
  inputSignal,
275
338
  response
276
339
  );
@@ -279,12 +342,25 @@ var CNSStimulation = class {
279
342
  };
280
343
  return this.instanceNeuronQueue.run(neuron2, starter);
281
344
  }
345
+ processResponseOrResponses(inputSignal, outputSignalOrSignals, error) {
346
+ if (Array.isArray(outputSignalOrSignals)) {
347
+ if (outputSignalOrSignals.length === 0) {
348
+ this.processResponse(inputSignal, void 0, error);
349
+ return;
350
+ }
351
+ for (const signal of outputSignalOrSignals) {
352
+ this.processResponse(inputSignal, signal, error);
353
+ }
354
+ return;
355
+ }
356
+ this.processResponse(inputSignal, outputSignalOrSignals, error);
357
+ }
282
358
  processResponse(inputSignal, outputSignal, error) {
283
- const collateral2 = outputSignal?.collateral;
284
- const ownerNeuron = collateral2 ? this.cns.getParentNeuronByCollateralId(collateral2.type) : void 0;
285
- const subscribers = collateral2 ? this.cns.getSubscribers(collateral2.type) : [];
359
+ const collateralName = outputSignal?.collateralName;
360
+ const ownerNeuron = collateralName ? this.cns.getParentNeuronByCollateralName(collateralName) : void 0;
361
+ const subscribers = collateralName ? this.cns.getSubscribers(collateralName) : [];
286
362
  const subscriberQueueItems = [];
287
- if (collateral2 && !error) {
363
+ if (collateralName && !error) {
288
364
  if (ownerNeuron) {
289
365
  this.cleanupCtxIfNeeded(ownerNeuron);
290
366
  }
@@ -295,27 +371,63 @@ var CNSStimulation = class {
295
371
  }
296
372
  }
297
373
  this.scheduledCount += subscriberQueueItems.length;
298
- this.options?.onResponse?.({
299
- inputSignal,
300
- outputSignal,
301
- ctx: this.ctx,
302
- queueLength: this.queue.length + this.scheduledCount,
303
- hops: this.options?.maxNeuronHops && ownerNeuron ? this.nueronVisitMap?.get(ownerNeuron.name) ?? 0 : void 0,
304
- error
305
- });
374
+ let maybePromise;
375
+ try {
376
+ maybePromise = this.options?.onResponse?.({
377
+ inputSignal,
378
+ outputSignal,
379
+ ctx: this.ctx,
380
+ queueLength: this.queue.length + this.scheduledCount,
381
+ stimulationId: this.id,
382
+ hops: this.options?.maxNeuronHops && ownerNeuron ? this.nueronVisitMap?.get(ownerNeuron.name) ?? 0 : void 0,
383
+ error
384
+ });
385
+ } catch (e) {
386
+ this.rejectCompleted(e);
387
+ return;
388
+ }
389
+ if (maybePromise && typeof maybePromise.then === "function") {
390
+ maybePromise.then(
391
+ () => {
392
+ for (let i = 0; i < subscriberQueueItems.length; i++) {
393
+ this.scheduledCount--;
394
+ this.queue.enqueue(subscriberQueueItems[i]);
395
+ }
396
+ this.tryResolveCompleted();
397
+ },
398
+ (error2) => {
399
+ this.rejectCompleted(error2);
400
+ }
401
+ );
402
+ return;
403
+ }
306
404
  for (let i = 0; i < subscriberQueueItems.length; i++) {
307
405
  this.scheduledCount--;
308
406
  this.queue.enqueue(subscriberQueueItems[i]);
309
407
  }
408
+ this.tryResolveCompleted();
310
409
  }
311
- responseToSignal(signal) {
312
- const ownerNeuron = this.cns.getParentNeuronByCollateralId(
313
- signal.collateral.type
410
+ responseToSignal(signalOrSignals) {
411
+ if (Array.isArray(signalOrSignals)) {
412
+ for (const signal of signalOrSignals) {
413
+ const ownerNeuron2 = this.cns.getParentNeuronByCollateralName(
414
+ signal.collateralName
415
+ );
416
+ if (ownerNeuron2) {
417
+ this.markNeuronActive(ownerNeuron2.name);
418
+ }
419
+ }
420
+ this.processResponseOrResponses(void 0, signalOrSignals);
421
+ return;
422
+ }
423
+ const ownerNeuron = this.cns.getParentNeuronByCollateralName(
424
+ signalOrSignals.collateralName
314
425
  );
315
426
  if (ownerNeuron) {
316
427
  this.markNeuronActive(ownerNeuron.name);
317
428
  }
318
- this.processResponse(void 0, signal);
429
+ this.processResponse(void 0, signalOrSignals);
430
+ this.tryResolveCompleted();
319
431
  }
320
432
  };
321
433
 
@@ -462,11 +574,14 @@ var CNS = class {
462
574
  this.neurons = neurons;
463
575
  this.options = options;
464
576
  /**
465
- * Fast lookup: collateralId -> list of (neuron, dendrite) subscribers.
577
+ * Fast lookup: collateralName -> list of (neuron, dendrite) subscribers.
466
578
  * Built once at construction time.
467
579
  */
468
580
  this.subIndex = /* @__PURE__ */ new Map();
469
- this.parentNeuronByCollateralId = /* @__PURE__ */ new Map();
581
+ this.neuronIndex = /* @__PURE__ */ new Map();
582
+ this.dendriteIndex = /* @__PURE__ */ new Map();
583
+ this.collateralIndex = /* @__PURE__ */ new Map();
584
+ this.parentNeuronByCollateralName = /* @__PURE__ */ new Map();
470
585
  /**
471
586
  * Strongly Connected Components of the neuron graph.
472
587
  * Each component is a set of neuron IDs that can reach each other.
@@ -548,16 +663,30 @@ var CNS = class {
548
663
  };
549
664
  }
550
665
  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
- }
666
+ let anyPromise = false;
667
+ const promises = [];
668
+ if (local) {
669
+ const res = local(r);
670
+ if (res && typeof res.then === "function") {
671
+ anyPromise = true;
672
+ promises.push(res);
673
+ }
674
+ }
675
+ for (let i = 0; i < this.globalResponseListeners.length; i++) {
676
+ const res = this.globalResponseListeners[i](r);
677
+ if (res && typeof res.then === "function") {
678
+ anyPromise = true;
679
+ promises.push(res);
559
680
  }
560
681
  }
682
+ if (anyPromise) {
683
+ return Promise.allSettled(promises).then((results) => {
684
+ const rejected = results.find(
685
+ (r2) => r2.status === "rejected"
686
+ );
687
+ if (rejected) throw rejected.reason;
688
+ });
689
+ }
561
690
  };
562
691
  }
563
692
  runWithNeuronConcurrency(neuron2, fn) {
@@ -613,22 +742,28 @@ var CNS = class {
613
742
  }
614
743
  buildIndexes() {
615
744
  this.subIndex.clear();
616
- this.parentNeuronByCollateralId.clear();
745
+ this.parentNeuronByCollateralName.clear();
617
746
  for (const neuron2 of this.neurons) {
747
+ this.neuronIndex.set(neuron2.name, neuron2);
618
748
  for (const dendrite of neuron2.dendrites) {
619
- const key = dendrite.collateral.type;
749
+ const key = dendrite.collateral.name;
620
750
  const arr = this.subIndex.get(key) ?? [];
621
751
  arr.push({ neuron: neuron2, dendrite });
622
752
  this.subIndex.set(
623
753
  key,
624
754
  arr
625
755
  );
756
+ this.dendriteIndex.set(key, dendrite);
626
757
  }
627
758
  Object.values(neuron2.axon).forEach((collateral2) => {
628
- this.parentNeuronByCollateralId.set(
629
- collateral2.type,
759
+ this.parentNeuronByCollateralName.set(
760
+ collateral2.name,
630
761
  neuron2
631
762
  );
763
+ this.collateralIndex.set(
764
+ collateral2.name,
765
+ collateral2
766
+ );
632
767
  });
633
768
  }
634
769
  }
@@ -645,8 +780,8 @@ var CNS = class {
645
780
  const reachableNeurons = /* @__PURE__ */ new Set();
646
781
  for (const axonKey in neuron2.axon) {
647
782
  const collateral2 = neuron2.axon[axonKey];
648
- const collateralType = collateral2.type;
649
- const subscribers = this.subIndex.get(collateralType);
783
+ const collateralName = collateral2.name;
784
+ const subscribers = this.subIndex.get(collateralName);
650
785
  if (subscribers) {
651
786
  for (const { neuron: targetNeuron } of subscribers) {
652
787
  reachableNeurons.add(targetNeuron.name);
@@ -819,34 +954,52 @@ var CNS = class {
819
954
  }
820
955
  return true;
821
956
  }
822
- getParentNeuronByCollateralId(collateralType) {
823
- return this.parentNeuronByCollateralId.get(collateralType);
957
+ getParentNeuronByCollateralName(collateralName) {
958
+ return this.parentNeuronByCollateralName.get(collateralName);
959
+ }
960
+ getDendrites() {
961
+ return Array.from(this.dendriteIndex.values());
962
+ }
963
+ getCollaterals() {
964
+ return Array.from(this.collateralIndex.values());
965
+ }
966
+ getNeurons() {
967
+ return Array.from(this.neuronIndex.values());
968
+ }
969
+ getCollateralByName(collateralName) {
970
+ return this.collateralIndex.get(
971
+ collateralName
972
+ );
973
+ }
974
+ getNeuronByName(neuronName) {
975
+ return this.neuronIndex.get(neuronName);
824
976
  }
825
- getSubscribers(collateralType) {
826
- return this.subIndex.get(collateralType) ?? [];
977
+ getSubscribers(collateralName) {
978
+ return this.subIndex.get(collateralName) ?? [];
827
979
  }
828
- stimulate(signal, options) {
980
+ stimulate(signalOrSignals, options) {
829
981
  const wrapped = this.wrapOnResponse(options?.onResponse);
830
982
  const stimulation = new CNSStimulation(this, this.instanceNeuronQueue, {
831
983
  ...options,
832
984
  onResponse: wrapped
833
985
  });
834
- stimulation.responseToSignal(signal);
986
+ stimulation.responseToSignal(signalOrSignals);
987
+ return stimulation.waitUntilComplete();
835
988
  }
836
989
  };
837
990
 
838
991
  // src/CNSCollateral.ts
839
992
  var CNSCollateral = class {
840
- constructor(type) {
841
- this.type = type;
993
+ constructor(name) {
994
+ this.name = name;
842
995
  }
843
996
  createSignal(payload) {
844
- return { collateral: this, payload };
997
+ return { collateralName: this.name, payload };
845
998
  }
846
999
  };
847
1000
 
848
1001
  // src/factory/index.ts
849
- var collateral = (type) => new CNSCollateral(type);
1002
+ var collateral = (name) => new CNSCollateral(name);
850
1003
  var neuron = (name, axon) => {
851
1004
  const dendrites = [];
852
1005
  const api = {
@@ -854,6 +1007,10 @@ var neuron = (name, axon) => {
854
1007
  api.concurrency = n;
855
1008
  return api;
856
1009
  },
1010
+ setMaxDuration: (ms) => {
1011
+ api.maxDuration = ms;
1012
+ return api;
1013
+ },
857
1014
  bind: ((axon2, newDendrites) => {
858
1015
  for (const key in newDendrites) {
859
1016
  const value = newDendrites[key];
@@ -870,6 +1027,7 @@ var neuron = (name, axon) => {
870
1027
  axon,
871
1028
  name,
872
1029
  concurrency: void 0,
1030
+ maxDuration: void 0,
873
1031
  dendrites,
874
1032
  dendrite(s) {
875
1033
  dendrites.push(
@@ -885,30 +1043,10 @@ var withCtx = () => ({
885
1043
  return neuron(name, axon);
886
1044
  }
887
1045
  });
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);
907
- }
908
- });
909
1046
 
910
1047
  exports.CNS = CNS;
911
1048
  exports.CNSCollateral = CNSCollateral;
1049
+ exports.CNSStimulationContextStore = CNSStimulationContextStore;
912
1050
  exports.collateral = collateral;
913
1051
  exports.neuron = neuron;
914
1052
  exports.withCtx = withCtx;