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