@cnstra/core 1.1.0 → 1.2.1
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 +567 -10
- package/dist/index.cjs +233 -85
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +233 -85
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -11,16 +11,57 @@ var CNSStimulationContextStore = class {
|
|
|
11
11
|
set(key, value) {
|
|
12
12
|
this.ctx.set(key, value);
|
|
13
13
|
}
|
|
14
|
+
getAll() {
|
|
15
|
+
return Object.fromEntries(this.ctx);
|
|
16
|
+
}
|
|
17
|
+
setAll(values) {
|
|
18
|
+
for (const [key, value] of Object.entries(values)) {
|
|
19
|
+
this.ctx.set(key, value);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
14
22
|
};
|
|
15
23
|
|
|
16
24
|
// src/CNS.ts
|
|
17
25
|
var asArray = (x) => Array.isArray(x) ? x : [x];
|
|
26
|
+
var isPromise = (v) => !!v && typeof v.then === "function";
|
|
27
|
+
var Deque = class {
|
|
28
|
+
constructor() {
|
|
29
|
+
this.a = [];
|
|
30
|
+
this.head = 0;
|
|
31
|
+
}
|
|
32
|
+
push(x) {
|
|
33
|
+
this.a.push(x);
|
|
34
|
+
}
|
|
35
|
+
shift() {
|
|
36
|
+
if (this.head >= this.a.length) return void 0;
|
|
37
|
+
const v = this.a[this.head++];
|
|
38
|
+
if (this.head > 1024 && this.head * 2 > this.a.length) {
|
|
39
|
+
this.a = this.a.slice(this.head);
|
|
40
|
+
this.head = 0;
|
|
41
|
+
}
|
|
42
|
+
return v;
|
|
43
|
+
}
|
|
44
|
+
get length() {
|
|
45
|
+
return this.a.length - this.head;
|
|
46
|
+
}
|
|
47
|
+
// Optional convenience if you need to inspect without removing:
|
|
48
|
+
// peek(): T | undefined { return this.a[this.head]; }
|
|
49
|
+
};
|
|
18
50
|
var CNS = class {
|
|
19
51
|
constructor(neurons) {
|
|
20
52
|
this.neurons = neurons;
|
|
21
|
-
|
|
53
|
+
/**
|
|
54
|
+
* Fast lookup: collateralId -> list of (neuron, dendrite) subscribers.
|
|
55
|
+
* Built once at construction time.
|
|
56
|
+
*/
|
|
22
57
|
this.subIndex = /* @__PURE__ */ new Map();
|
|
58
|
+
/**
|
|
59
|
+
* Cached hashes for stable IDs to optimize dedup key generation.
|
|
60
|
+
*/
|
|
61
|
+
this.neuronHashes = /* @__PURE__ */ new Map();
|
|
62
|
+
this.collateralHashes = /* @__PURE__ */ new Map();
|
|
23
63
|
this.buildIndex();
|
|
64
|
+
this.precomputeHashes();
|
|
24
65
|
}
|
|
25
66
|
buildIndex() {
|
|
26
67
|
this.subIndex.clear();
|
|
@@ -36,61 +77,216 @@ var CNS = class {
|
|
|
36
77
|
}
|
|
37
78
|
}
|
|
38
79
|
}
|
|
39
|
-
|
|
80
|
+
/**
|
|
81
|
+
* Precompute hashes for stable neuron and collateral IDs to optimize
|
|
82
|
+
* dedup key generation during stimulation.
|
|
83
|
+
*/
|
|
84
|
+
precomputeHashes() {
|
|
85
|
+
for (const neuron2 of this.neurons) {
|
|
86
|
+
this.getHash(neuron2.id, this.neuronHashes);
|
|
87
|
+
for (const dendrite of neuron2.dendrites) {
|
|
88
|
+
this.getHash(dendrite.collateral.id, this.collateralHashes);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Simple but effective hash function for string keys.
|
|
94
|
+
*/
|
|
95
|
+
simpleHash(str) {
|
|
96
|
+
let hash = 0;
|
|
97
|
+
for (let i = 0; i < str.length; i++) {
|
|
98
|
+
const char = str.charCodeAt(i);
|
|
99
|
+
hash = (hash << 5) - hash + char;
|
|
100
|
+
hash = hash & hash;
|
|
101
|
+
}
|
|
102
|
+
return hash;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Get or compute hash for a string, using cache for stable IDs.
|
|
106
|
+
*/
|
|
107
|
+
getHash(str, cache) {
|
|
108
|
+
let hash = cache.get(str);
|
|
109
|
+
if (hash === void 0) {
|
|
110
|
+
hash = this.simpleHash(str);
|
|
111
|
+
if (cache.size > 1e3) {
|
|
112
|
+
const oldestKey = cache.keys().next().value;
|
|
113
|
+
if (oldestKey !== void 0) {
|
|
114
|
+
cache.delete(oldestKey);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
cache.set(str, hash);
|
|
118
|
+
} else {
|
|
119
|
+
cache.delete(str);
|
|
120
|
+
cache.set(str, hash);
|
|
121
|
+
}
|
|
122
|
+
return hash;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Create optimized dedup key using composite numeric hash.
|
|
126
|
+
* This replaces string concatenation with faster numeric operations.
|
|
127
|
+
* Includes hops to allow signals to pass through the same neuron at different levels.
|
|
128
|
+
*/
|
|
129
|
+
createDedupKey(neuronId, collateralId, spikeId, hops) {
|
|
130
|
+
const nHash = this.getHash(neuronId, this.neuronHashes);
|
|
131
|
+
const cHash = this.getHash(collateralId, this.collateralHashes);
|
|
132
|
+
const sHash = this.simpleHash(spikeId);
|
|
133
|
+
const hHash = this.simpleHash(hops.toString());
|
|
134
|
+
return `${nHash}:${cHash}:${sHash}:${hHash}`;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Fire-and-forget fan-out for a single queue item.
|
|
138
|
+
*
|
|
139
|
+
* Behavior:
|
|
140
|
+
* - Synchronous dendrite responses enqueue their outgoing signals immediately.
|
|
141
|
+
* - Asynchronous responses:
|
|
142
|
+
* • We DO NOT await them (no per-item barrier).
|
|
143
|
+
* • We attach `.then` to enqueue outgoing signals when ready.
|
|
144
|
+
* • We notify the scheduler when the first async for this item starts,
|
|
145
|
+
* and when the last async for this item settles.
|
|
146
|
+
* - No error handling: we do not try/catch. Rejections bubble via wireRejectionToRun().
|
|
147
|
+
*
|
|
148
|
+
* Returns:
|
|
149
|
+
* boolean — whether this item launched any async work (>= 1 promises).
|
|
150
|
+
*/
|
|
151
|
+
fanOutFireAndForget(incoming, allowType, seen, queue, ctx, enableDedup, onItemFirstAsyncStart, onItemAllAsyncSettled, onAnyAsyncSettled, onTrace) {
|
|
40
152
|
const subscribers = this.subIndex.get(incoming.collateralId);
|
|
41
|
-
if (!subscribers || subscribers.length === 0) return;
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
const out = await dendrite.response(incoming.payload, neuron2.axon, {
|
|
47
|
-
get: () => ctx.get(neuron2.id),
|
|
48
|
-
set: (value) => ctx.set(neuron2.id, value)
|
|
49
|
-
});
|
|
50
|
-
if (out === void 0) continue;
|
|
153
|
+
if (!subscribers || subscribers.length === 0) return false;
|
|
154
|
+
const nextHop = incoming.hops + 1;
|
|
155
|
+
const spikeId = incoming.spikeId;
|
|
156
|
+
const enqueueOut = (out) => {
|
|
157
|
+
if (out == null) return;
|
|
51
158
|
for (const spike of asArray(out)) {
|
|
52
|
-
|
|
53
|
-
|
|
159
|
+
const t = spike.type;
|
|
160
|
+
if (allowType && !allowType(t)) continue;
|
|
54
161
|
queue.push({
|
|
55
|
-
collateralId:
|
|
162
|
+
collateralId: t,
|
|
56
163
|
payload: spike.payload,
|
|
57
|
-
hops:
|
|
58
|
-
spikeId
|
|
59
|
-
// keep the same spike id across the cascade
|
|
164
|
+
hops: nextHop,
|
|
165
|
+
spikeId
|
|
60
166
|
});
|
|
61
167
|
}
|
|
168
|
+
};
|
|
169
|
+
let openAsync = 0;
|
|
170
|
+
let itemMarkedAsync = false;
|
|
171
|
+
for (const { neuron: neuron2, dendrite } of subscribers) {
|
|
172
|
+
const dedupKey = enableDedup ? this.createDedupKey(
|
|
173
|
+
neuron2.id,
|
|
174
|
+
incoming.collateralId,
|
|
175
|
+
incoming.spikeId,
|
|
176
|
+
incoming.hops
|
|
177
|
+
) : void 0;
|
|
178
|
+
if (dedupKey && seen.has(dedupKey)) continue;
|
|
179
|
+
if (dedupKey) seen.add(dedupKey);
|
|
180
|
+
const res = dendrite.response(incoming.payload, neuron2.axon, {
|
|
181
|
+
get: () => ctx.get(neuron2.id),
|
|
182
|
+
set: (value) => ctx.set(neuron2.id, value)
|
|
183
|
+
});
|
|
184
|
+
if (isPromise(res)) {
|
|
185
|
+
if (!itemMarkedAsync) {
|
|
186
|
+
itemMarkedAsync = true;
|
|
187
|
+
onItemFirstAsyncStart();
|
|
188
|
+
}
|
|
189
|
+
openAsync++;
|
|
190
|
+
res.then(
|
|
191
|
+
(out) => {
|
|
192
|
+
enqueueOut(out);
|
|
193
|
+
openAsync--;
|
|
194
|
+
onAnyAsyncSettled();
|
|
195
|
+
if (openAsync === 0) {
|
|
196
|
+
onItemAllAsyncSettled();
|
|
197
|
+
}
|
|
198
|
+
},
|
|
199
|
+
(err) => {
|
|
200
|
+
onTrace?.({
|
|
201
|
+
collateralId: incoming.collateralId,
|
|
202
|
+
hops: incoming.hops,
|
|
203
|
+
payload: incoming.payload,
|
|
204
|
+
queueLength: queue.length,
|
|
205
|
+
error: err,
|
|
206
|
+
contextStore: ctx
|
|
207
|
+
// Pass the context store interface
|
|
208
|
+
});
|
|
209
|
+
openAsync--;
|
|
210
|
+
onAnyAsyncSettled();
|
|
211
|
+
if (openAsync === 0) {
|
|
212
|
+
onItemAllAsyncSettled();
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
);
|
|
216
|
+
} else {
|
|
217
|
+
enqueueOut(res);
|
|
218
|
+
}
|
|
62
219
|
}
|
|
220
|
+
return itemMarkedAsync;
|
|
63
221
|
}
|
|
64
|
-
|
|
65
|
-
const ctx = opts?.ctx ?? new CNSStimulationContextStore();
|
|
222
|
+
stimulate(collateral2, payload, opts) {
|
|
223
|
+
const ctx = opts?.ctx ?? opts?.createContextStore?.() ?? new CNSStimulationContextStore();
|
|
66
224
|
const spikeId = opts?.spikeId || Math.random().toString(36).slice(2);
|
|
67
|
-
const maxHops = opts?.maxHops
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
225
|
+
const maxHops = opts?.maxHops;
|
|
226
|
+
const limit = (opts?.concurrency ?? 0) > 0 ? opts.concurrency : Infinity;
|
|
227
|
+
const queue = new Deque();
|
|
228
|
+
queue.push({
|
|
229
|
+
collateralId: collateral2.id,
|
|
230
|
+
payload,
|
|
231
|
+
hops: 0,
|
|
232
|
+
spikeId
|
|
233
|
+
});
|
|
76
234
|
const seen = /* @__PURE__ */ new Set();
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
235
|
+
let activeOperations = 0;
|
|
236
|
+
const startOperation = () => {
|
|
237
|
+
activeOperations++;
|
|
238
|
+
};
|
|
239
|
+
const finishOperation = () => {
|
|
240
|
+
activeOperations--;
|
|
241
|
+
tryProcessMore();
|
|
242
|
+
};
|
|
243
|
+
const canStartOperation = () => {
|
|
244
|
+
return activeOperations < limit && !opts?.abortSignal?.aborted;
|
|
245
|
+
};
|
|
246
|
+
const processItem = (item) => {
|
|
80
247
|
opts?.onTrace?.({
|
|
81
248
|
collateralId: item.collateralId,
|
|
82
249
|
hops: item.hops,
|
|
83
|
-
payload: item.payload
|
|
250
|
+
payload: item.payload,
|
|
251
|
+
queueLength: queue.length,
|
|
252
|
+
contextStore: ctx
|
|
253
|
+
// Pass the context store
|
|
84
254
|
});
|
|
85
|
-
if (item.hops >= maxHops)
|
|
86
|
-
|
|
255
|
+
if (maxHops && item.hops >= maxHops) {
|
|
256
|
+
finishOperation();
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
const onOperationComplete = () => {
|
|
260
|
+
finishOperation();
|
|
261
|
+
};
|
|
262
|
+
const onAsyncStart = () => {
|
|
263
|
+
};
|
|
264
|
+
const launchedAsync = this.fanOutFireAndForget(
|
|
87
265
|
item,
|
|
88
266
|
opts?.allowType,
|
|
89
267
|
seen,
|
|
90
268
|
queue,
|
|
91
|
-
ctx
|
|
269
|
+
ctx,
|
|
270
|
+
maxHops !== void 0,
|
|
271
|
+
// Enable dedup only if maxHops is set
|
|
272
|
+
onAsyncStart,
|
|
273
|
+
onOperationComplete,
|
|
274
|
+
tryProcessMore,
|
|
275
|
+
// Continue streaming on any completion
|
|
276
|
+
opts?.onTrace
|
|
92
277
|
);
|
|
93
|
-
|
|
278
|
+
if (!launchedAsync) {
|
|
279
|
+
finishOperation();
|
|
280
|
+
}
|
|
281
|
+
};
|
|
282
|
+
const tryProcessMore = () => {
|
|
283
|
+
while (queue.length && canStartOperation()) {
|
|
284
|
+
const item = queue.shift();
|
|
285
|
+
startOperation();
|
|
286
|
+
processItem(item);
|
|
287
|
+
}
|
|
288
|
+
};
|
|
289
|
+
tryProcessMore();
|
|
94
290
|
}
|
|
95
291
|
};
|
|
96
292
|
|
|
@@ -127,54 +323,6 @@ var withCtx = () => ({
|
|
|
127
323
|
return neuron(id, axon);
|
|
128
324
|
}
|
|
129
325
|
});
|
|
130
|
-
var request = collateral("request");
|
|
131
|
-
var success = collateral("success");
|
|
132
|
-
var error = collateral("error");
|
|
133
|
-
neuron("router", { success, error }).dendrite({
|
|
134
|
-
collateral: request,
|
|
135
|
-
response: async (payload, axon) => {
|
|
136
|
-
const value = payload.value;
|
|
137
|
-
if (value > 0) {
|
|
138
|
-
return axon.success.createSignal({
|
|
139
|
-
result: `Success: ${value}`
|
|
140
|
-
});
|
|
141
|
-
} else {
|
|
142
|
-
return axon.error.createSignal({
|
|
143
|
-
error: `Error: ${value} is not positive`
|
|
144
|
-
});
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
});
|
|
148
|
-
var a1 = {
|
|
149
|
-
test: collateral("test")
|
|
150
|
-
};
|
|
151
|
-
var a2 = {
|
|
152
|
-
test2: collateral("test2")
|
|
153
|
-
};
|
|
154
|
-
var a3 = {
|
|
155
|
-
test3: collateral("test3")
|
|
156
|
-
};
|
|
157
|
-
var n1 = withCtx().neuron("n1", a1).dendrite({
|
|
158
|
-
collateral: a2.test2,
|
|
159
|
-
response: async (payload, axon, ctx) => {
|
|
160
|
-
ctx.set({
|
|
161
|
-
testCtx: payload.test2
|
|
162
|
-
});
|
|
163
|
-
ctx.get()?.testCtx;
|
|
164
|
-
return axon.test.createSignal({
|
|
165
|
-
test: payload.test2
|
|
166
|
-
});
|
|
167
|
-
}
|
|
168
|
-
});
|
|
169
|
-
var n2 = neuron("n2", a2).dendrite({
|
|
170
|
-
collateral: a3.test3,
|
|
171
|
-
response: async (payload, axon) => {
|
|
172
|
-
return axon.test2.createSignal({
|
|
173
|
-
test2: payload.test3
|
|
174
|
-
});
|
|
175
|
-
}
|
|
176
|
-
});
|
|
177
|
-
new CNS([n1, n2]);
|
|
178
326
|
|
|
179
327
|
exports.CNS = CNS;
|
|
180
328
|
exports.CNSCollateral = CNSCollateral;
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/CNSStimulationContextStore.ts","../src/CNS.ts","../src/CNSCollateral.ts","../src/factory/index.ts"],"names":["neuron","collateral"],"mappings":"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","file":"index.cjs","sourcesContent":["import { ICNSStimulationContextStore } from './interfaces/ICNSStimulationContextStore';\n\nexport class CNSStimulationContextStore implements ICNSStimulationContextStore {\n constructor(private readonly ctx: Map<string, unknown> = new Map()) {}\n\n get(key: string): unknown {\n return this.ctx.get(key);\n }\n\n set(key: string, value: unknown): void {\n this.ctx.set(key, value);\n }\n}\n","import { TCNSNeuron } from './types/TCNSNeuron';\nimport { TCNSDendrite } from './types/TCNSDendrite';\nimport { CNSCollateral } from './CNSCollateral';\nimport { ICNSStimulationContextStore } from './interfaces/ICNSStimulationContextStore';\nimport { CNSStimulationContextStore } from './CNSStimulationContextStore';\n\nconst asArray = <T>(x: T | T[]) => (Array.isArray(x) ? x : [x]);\n\ntype TSubscriber<\n TNeuron extends TCNSNeuron<any, any, any, any, any, any, any>,\n TDendrite extends TCNSDendrite<any, any, any, any, any, any>\n> = {\n neuron: TNeuron;\n dendrite: TDendrite;\n};\n\ntype TQueueItem<TCollateralId extends string> = {\n collateralId: TCollateralId;\n payload: unknown;\n hops: number;\n spikeId: string;\n};\n\nexport class CNS<\n TCollateralId extends string,\n TNeuron extends TCNSNeuron<any, any, any, any, any, any, any>,\n TDendrite extends TCNSDendrite<any, any, any, any, any, any>\n> {\n // index by collateral *type* => list of subscribers\n private subIndex = new Map<\n TCollateralId,\n TSubscriber<TNeuron, TDendrite>[]\n >();\n\n constructor(protected readonly neurons: TNeuron[]) {\n this.buildIndex();\n }\n\n private buildIndex() {\n this.subIndex.clear();\n for (const neuron of this.neurons) {\n for (const dendrite of neuron.dendrites) {\n const key = dendrite.collateral.id;\n const arr = this.subIndex.get(key) ?? [];\n arr.push({ neuron, dendrite: dendrite as TDendrite });\n this.subIndex.set(\n key,\n arr as TSubscriber<TNeuron, TDendrite>[]\n );\n }\n }\n }\n\n private async fanOut(\n incoming: TQueueItem<TCollateralId>,\n allowType: ((t: TCollateralId) => boolean) | undefined,\n seen: Set<string>,\n queue: TQueueItem<TCollateralId>[],\n ctx: ICNSStimulationContextStore\n ) {\n const subscribers = this.subIndex.get(incoming.collateralId);\n if (!subscribers || subscribers.length === 0) return;\n\n for (const { neuron, dendrite } of subscribers) {\n const k = `${neuron.id}::${incoming.collateralId}::${incoming.spikeId}`;\n if (seen.has(k)) continue;\n seen.add(k);\n\n const out = await dendrite.response(incoming.payload, neuron.axon, {\n get: () => ctx.get(neuron.id),\n set: value => ctx.set(neuron.id, value),\n });\n if (out === undefined) continue;\n\n for (const spike of asArray(out)) {\n if (allowType && !allowType(spike.type as TCollateralId))\n continue; // TODO: add allowType\n\n queue.push({\n collateralId: spike.type as TCollateralId,\n payload: spike.payload,\n hops: incoming.hops + 1,\n spikeId: incoming.spikeId, // keep the same spike id across the cascade\n });\n }\n }\n }\n\n async stimulate<\n TAfferentCollateralId extends TCollateralId,\n TAfferentCollateralPayload\n >(\n collateral: CNSCollateral<\n TAfferentCollateralId,\n TAfferentCollateralPayload\n >,\n payload: TAfferentCollateralPayload,\n opts?: {\n maxHops?: number;\n allowType?: (t: TCollateralId) => boolean;\n onTrace?: (e: {\n collateralId: TCollateralId;\n hops: number;\n payload: unknown;\n }) => void;\n abortSignal?: AbortSignal;\n spikeId?: string;\n ctx?: ICNSStimulationContextStore;\n }\n ): Promise<void> {\n const ctx = opts?.ctx ?? new CNSStimulationContextStore();\n const spikeId = opts?.spikeId || Math.random().toString(36).slice(2);\n const maxHops = opts?.maxHops ?? 1000;\n\n const queue: TQueueItem<TCollateralId>[] = [\n {\n collateralId: collateral.id,\n payload,\n hops: 0,\n spikeId,\n },\n ];\n const seen = new Set<string>();\n\n while (queue.length) {\n if (opts?.abortSignal?.aborted) break;\n\n const item = queue.shift()!;\n opts?.onTrace?.({\n collateralId: item.collateralId,\n hops: item.hops,\n payload: item.payload,\n });\n if (item.hops >= maxHops) continue;\n\n await this.fanOut(\n item as TQueueItem<TCollateralId>,\n opts?.allowType,\n seen,\n queue as TQueueItem<TCollateralId>[],\n ctx\n );\n }\n }\n}\n","import { TCNSSignal } from './types/TCNSSignal';\n\nexport class CNSCollateral<TId extends string, TPayload> {\n constructor(public readonly id: TId) {}\n\n createSignal(\n ...args: [TPayload] extends [undefined] ? [] : [payload: TPayload]\n ): TCNSSignal<TId, TPayload> {\n const payload = (args as any)[0] as TPayload;\n return { type: this.id, payload };\n }\n}\n","import { CNS } from '../CNS';\nimport { CNSCollateral } from '../CNSCollateral';\nimport { TCNSAxon } from '../types/TCNSAxon';\nimport { TCNSDendrite } from '../types/TCNSDendrite';\n\nexport const collateral = <TPayload = undefined, TId extends string = string>(\n id: TId\n) => new CNSCollateral<TId, TPayload>(id);\n\ntype InterNeuronAPI<\n TContextValue,\n TIdType extends string,\n TReceiverCollateralIdType extends string,\n TReceiverAxonCollateralPayload,\n TAxonType extends TCNSAxon<\n TReceiverCollateralIdType,\n TReceiverAxonCollateralPayload\n >\n> = {\n id: TIdType;\n axon: TAxonType;\n dendrites: TCNSDendrite<\n TContextValue,\n string,\n unknown,\n TReceiverCollateralIdType,\n TReceiverAxonCollateralPayload,\n TAxonType\n >[];\n dendrite: <\n TSenderExactCollateralIdType extends string,\n TSenderExactAxonCollateralPayload\n >(\n s: TCNSDendrite<\n TContextValue,\n TSenderExactCollateralIdType,\n TSenderExactAxonCollateralPayload,\n TReceiverCollateralIdType,\n TReceiverAxonCollateralPayload,\n TAxonType\n >\n ) => InterNeuronAPI<\n TContextValue,\n TIdType,\n TReceiverCollateralIdType,\n TReceiverAxonCollateralPayload,\n TAxonType\n >;\n};\n\n// Concrete builder\nexport const neuron = <\n TContextValue,\n TIdType extends string,\n TReceiverCollateralIdType extends string,\n TReceiverAxonCollateralPayload,\n TAxonType extends TCNSAxon<\n TReceiverCollateralIdType,\n TReceiverAxonCollateralPayload\n >\n>(\n id: TIdType,\n axon: TAxonType\n): InterNeuronAPI<\n TContextValue,\n TIdType,\n TReceiverCollateralIdType,\n TReceiverAxonCollateralPayload,\n TAxonType\n> => {\n const dendrites: TCNSDendrite<\n TContextValue,\n string,\n unknown,\n TReceiverCollateralIdType,\n TReceiverAxonCollateralPayload,\n TAxonType\n >[] = [];\n\n const api: InterNeuronAPI<\n TContextValue,\n TIdType,\n TReceiverCollateralIdType,\n TReceiverAxonCollateralPayload,\n TAxonType\n > = {\n axon,\n id,\n dendrites,\n dendrite<\n TSenderExactCollateralIdType extends string,\n TSenderExactAxonCollateralPayload\n >(\n s: TCNSDendrite<\n TContextValue,\n TSenderExactCollateralIdType,\n TSenderExactAxonCollateralPayload,\n TReceiverCollateralIdType,\n TReceiverAxonCollateralPayload,\n TAxonType\n >\n ) {\n dendrites.push(\n s as TCNSDendrite<\n TContextValue,\n string,\n unknown,\n TReceiverCollateralIdType,\n TReceiverAxonCollateralPayload,\n TAxonType\n >\n );\n return api; // keep full API for chaining\n },\n };\n\n return api;\n};\n\nexport const withCtx = <TContextValue>() => ({\n neuron: <\n TIdType extends string,\n TReceiverCollateralIdType extends string,\n TReceiverAxonCollateralPayload,\n TAxonType extends TCNSAxon<\n TReceiverCollateralIdType,\n TReceiverAxonCollateralPayload\n >\n >(\n id: TIdType,\n axon: TAxonType\n ) => {\n return neuron<\n TContextValue,\n TIdType,\n TReceiverCollateralIdType,\n TReceiverAxonCollateralPayload,\n TAxonType\n >(id, axon);\n },\n});\n\nconst request = collateral<{ value: number }>('request');\nconst success = collateral<{ result: string }>('success');\nconst error = collateral<{ error: string }>('error');\n\nconst router = neuron('router', { success, error }).dendrite({\n collateral: request,\n response: async (payload, axon) => {\n const value = (payload as { value: number }).value;\n if (value > 0) {\n return axon.success.createSignal({\n result: `Success: ${value}`,\n });\n } else {\n return axon.error.createSignal({\n error: `Error: ${value} is not positive`,\n });\n }\n },\n});\n\nconst a1 = {\n test: collateral<{ test: string }>('test'),\n};\n\nconst a2 = {\n test2: collateral<{ test2: string }>('test2'),\n};\n\nconst a3 = {\n test3: collateral<{ test3: string }>('test3'),\n};\n\nconst n1 = withCtx<{ testCtx: string }>()\n .neuron('n1', a1)\n .dendrite({\n collateral: a2.test2,\n response: async (payload, axon, ctx) => {\n ctx.set({\n testCtx: payload.test2,\n });\n ctx.get()?.testCtx;\n return axon.test.createSignal({\n test: payload.test2,\n });\n },\n });\n\nconst n2 = neuron('n2', a2).dendrite({\n collateral: a3.test3,\n response: async (payload, axon) => {\n return axon.test2.createSignal({\n test2: payload.test3,\n });\n },\n});\n\nconst cns = new CNS([n1, n2]);\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/CNSStimulationContextStore.ts","../src/CNS.ts","../src/CNSCollateral.ts","../src/factory/index.ts"],"names":["neuron","collateral"],"mappings":"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","file":"index.cjs","sourcesContent":["import { ICNSStimulationContextStore } from './interfaces/ICNSStimulationContextStore';\n\nexport class CNSStimulationContextStore implements ICNSStimulationContextStore {\n constructor(private readonly ctx: Map<string, unknown> = new Map()) {}\n\n get(key: string): unknown {\n return this.ctx.get(key);\n }\n\n set(key: string, value: unknown): void {\n this.ctx.set(key, value);\n }\n\n getAll(): Record<string, unknown> {\n return Object.fromEntries(this.ctx);\n }\n\n setAll(values: Record<string, unknown>): void {\n for (const [key, value] of Object.entries(values)) {\n this.ctx.set(key, value);\n }\n }\n}\n","import { TCNSNeuron } from './types/TCNSNeuron';\nimport { TCNSDendrite } from './types/TCNSDendrite';\nimport { CNSCollateral } from './CNSCollateral';\nimport { ICNSStimulationContextStore } from './interfaces/ICNSStimulationContextStore';\nimport { CNSStimulationContextStore } from './CNSStimulationContextStore';\n\n/**\n * Small helpers\n */\nconst asArray = <T>(x: T | T[]) => (Array.isArray(x) ? x : [x]);\nconst isPromise = (v: unknown): v is Promise<unknown> =>\n !!v && typeof (v as any).then === 'function';\n\n/**\n * Minimal, GC-friendly deque (ring-buffer-ish) with O(1) push/shift.\n * Avoids Array.shift() O(n) tail copies on large queues.\n */\nclass Deque<T> {\n private a: T[] = [];\n private head = 0;\n\n push(x: T) {\n this.a.push(x);\n }\n\n shift(): T | undefined {\n if (this.head >= this.a.length) return undefined;\n const v = this.a[this.head++];\n // Periodic compaction to keep memory bounded:\n // when head grows big and passes half of array, slice the live tail.\n if (this.head > 1024 && this.head * 2 > this.a.length) {\n this.a = this.a.slice(this.head);\n this.head = 0;\n }\n return v;\n }\n\n get length() {\n return this.a.length - this.head;\n }\n\n // Optional convenience if you need to inspect without removing:\n // peek(): T | undefined { return this.a[this.head]; }\n}\n\ntype TSubscriber<\n TNeuron extends TCNSNeuron<any, any, any, any, any, any, any>,\n TDendrite extends TCNSDendrite<any, any, any, any, any, any>\n> = {\n neuron: TNeuron;\n dendrite: TDendrite;\n};\n\ntype TQueueItem<TCollateralId extends string> = {\n collateralId: TCollateralId;\n payload: unknown;\n hops: number;\n spikeId: string;\n};\n\nexport class CNS<\n TCollateralId extends string,\n TNeuron extends TCNSNeuron<any, any, any, any, any, any, any>,\n TDendrite extends TCNSDendrite<any, any, any, any, any, any>\n> {\n /**\n * Fast lookup: collateralId -> list of (neuron, dendrite) subscribers.\n * Built once at construction time.\n */\n private subIndex = new Map<\n TCollateralId,\n TSubscriber<TNeuron, TDendrite>[]\n >();\n\n /**\n * Cached hashes for stable IDs to optimize dedup key generation.\n */\n private neuronHashes = new Map<string, number>();\n private collateralHashes = new Map<string, number>();\n\n constructor(protected readonly neurons: TNeuron[]) {\n this.buildIndex();\n this.precomputeHashes();\n }\n\n private buildIndex() {\n this.subIndex.clear();\n for (const neuron of this.neurons) {\n for (const dendrite of neuron.dendrites) {\n const key = dendrite.collateral.id as TCollateralId;\n const arr = this.subIndex.get(key) ?? [];\n arr.push({ neuron, dendrite: dendrite as TDendrite });\n this.subIndex.set(\n key,\n arr as TSubscriber<TNeuron, TDendrite>[]\n );\n }\n }\n }\n\n /**\n * Precompute hashes for stable neuron and collateral IDs to optimize\n * dedup key generation during stimulation.\n */\n private precomputeHashes() {\n for (const neuron of this.neurons) {\n this.getHash(neuron.id, this.neuronHashes);\n for (const dendrite of neuron.dendrites) {\n this.getHash(dendrite.collateral.id, this.collateralHashes);\n }\n }\n }\n\n /**\n * Simple but effective hash function for string keys.\n */\n private simpleHash(str: string): number {\n let hash = 0;\n for (let i = 0; i < str.length; i++) {\n const char = str.charCodeAt(i);\n hash = (hash << 5) - hash + char;\n hash = hash & hash; // Convert to 32-bit integer\n }\n return hash;\n }\n\n /**\n * Get or compute hash for a string, using cache for stable IDs.\n */\n private getHash(str: string, cache: Map<string, number>): number {\n let hash = cache.get(str);\n if (hash === undefined) {\n hash = this.simpleHash(str);\n\n // Simple cache management - remove oldest entry if cache gets too large\n if (cache.size > 1000) {\n // Remove oldest entry (first key in Map maintains insertion order)\n const oldestKey = cache.keys().next().value;\n if (oldestKey !== undefined) {\n cache.delete(oldestKey);\n }\n }\n\n cache.set(str, hash);\n } else {\n // Move to end for LRU (delete + re-add updates position)\n cache.delete(str);\n cache.set(str, hash);\n }\n return hash;\n }\n\n /**\n * Create optimized dedup key using composite numeric hash.\n * This replaces string concatenation with faster numeric operations.\n * Includes hops to allow signals to pass through the same neuron at different levels.\n */\n private createDedupKey(\n neuronId: string,\n collateralId: string,\n spikeId: string,\n hops: number\n ): string {\n const nHash = this.getHash(neuronId, this.neuronHashes);\n const cHash = this.getHash(collateralId, this.collateralHashes);\n const sHash = this.simpleHash(spikeId); // Don't cache spike IDs (too many unique values)\n const hHash = this.simpleHash(hops.toString()); // Hash the hops level\n\n return `${nHash}:${cHash}:${sHash}:${hHash}`;\n }\n\n /**\n * Fire-and-forget fan-out for a single queue item.\n *\n * Behavior:\n * - Synchronous dendrite responses enqueue their outgoing signals immediately.\n * - Asynchronous responses:\n * • We DO NOT await them (no per-item barrier).\n * • We attach `.then` to enqueue outgoing signals when ready.\n * • We notify the scheduler when the first async for this item starts,\n * and when the last async for this item settles.\n * - No error handling: we do not try/catch. Rejections bubble via wireRejectionToRun().\n *\n * Returns:\n * boolean — whether this item launched any async work (>= 1 promises).\n */\n private fanOutFireAndForget(\n incoming: TQueueItem<TCollateralId>,\n allowType: ((t: TCollateralId) => boolean) | undefined,\n seen: Set<string>,\n queue: Deque<TQueueItem<TCollateralId>>,\n ctx: ICNSStimulationContextStore,\n enableDedup: boolean, // New parameter\n onItemFirstAsyncStart: () => void, // called exactly once per item (on first async)\n onItemAllAsyncSettled: () => void, // called when this item's last async settles\n onAnyAsyncSettled: () => void, // called after every async settles (to pump again)\n onTrace?: (trace: {\n collateralId: TCollateralId;\n hops: number;\n payload: unknown;\n queueLength: number;\n error?: Error;\n contextStore: ICNSStimulationContextStore; // Pass the actual context store interface\n }) => void\n ): boolean {\n const subscribers = this.subIndex.get(incoming.collateralId);\n if (!subscribers || subscribers.length === 0) return false;\n\n // Do not capture the entire `incoming` object in closures — copy only what we need.\n const nextHop = incoming.hops + 1;\n const spikeId = incoming.spikeId;\n\n const enqueueOut = (out: any) => {\n if (out == null) return;\n for (const spike of asArray(out)) {\n const t = spike.type as TCollateralId;\n if (allowType && !allowType(t)) continue;\n queue.push({\n collateralId: t,\n payload: spike.payload,\n hops: nextHop,\n spikeId,\n });\n }\n };\n\n // Track active async subscribers for THIS item.\n let openAsync = 0;\n let itemMarkedAsync = false;\n\n for (const { neuron, dendrite } of subscribers) {\n // Dedup to prevent double-processing within this spike cascade\n const dedupKey = enableDedup\n ? this.createDedupKey(\n neuron.id,\n incoming.collateralId,\n incoming.spikeId,\n incoming.hops\n )\n : undefined; // Only dedup if enabled\n\n if (dedupKey && seen.has(dedupKey)) continue;\n if (dedupKey) seen.add(dedupKey);\n\n const res = dendrite.response(incoming.payload, neuron.axon, {\n get: () => ctx.get(neuron.id),\n set: (value: any) => ctx.set(neuron.id, value),\n });\n\n if (isPromise(res)) {\n if (!itemMarkedAsync) {\n itemMarkedAsync = true;\n onItemFirstAsyncStart();\n }\n openAsync++;\n\n // We do NOT catch here; rejections are wired to the run-level promise.\n (res as Promise<any>).then(\n out => {\n enqueueOut(out);\n // After successful async settles:\n openAsync--;\n onAnyAsyncSettled();\n if (openAsync === 0) {\n onItemAllAsyncSettled();\n }\n },\n err => {\n // Trace the error\n onTrace?.({\n collateralId: incoming.collateralId,\n hops: incoming.hops,\n payload: incoming.payload,\n queueLength: queue.length,\n error: err,\n contextStore: ctx, // Pass the context store interface\n });\n\n // After failed async settles:\n openAsync--;\n onAnyAsyncSettled();\n if (openAsync === 0) {\n onItemAllAsyncSettled();\n }\n // Error is already traced, just continue\n }\n );\n\n // Fire-and-forget: no need to wire rejections\n } else {\n // Synchronous branch: enqueue immediately.\n enqueueOut(res);\n }\n }\n\n return itemMarkedAsync;\n }\n\n stimulate<\n TAfferentCollateralId extends TCollateralId,\n TAfferentCollateralPayload\n >(\n collateral: CNSCollateral<\n TAfferentCollateralId,\n TAfferentCollateralPayload\n >,\n payload: TAfferentCollateralPayload,\n opts?: {\n maxHops?: number;\n allowType?: (t: TCollateralId) => boolean;\n onTrace?: (e: {\n collateralId: TCollateralId;\n hops: number;\n payload: unknown;\n queueLength: number;\n error?: Error;\n contextStore: ICNSStimulationContextStore; // Pass the actual context store interface\n }) => void;\n abortSignal?: AbortSignal;\n spikeId?: string;\n ctx?: ICNSStimulationContextStore;\n /**\n * Factory method to create a new context store.\n * Useful for recovery scenarios where you want to restore state.\n */\n createContextStore?: () => ICNSStimulationContextStore;\n /**\n * Maximum number of signal processing operations active at once.\n * - Sync operations count for their brief execution time\n * - Async operations count until completion\n * - undefined / <= 0 => Infinity (no limit)\n * Maintains streaming flow - no artificial barriers or level synchronization.\n */\n concurrency?: number;\n }\n ): void {\n const ctx =\n opts?.ctx ??\n opts?.createContextStore?.() ??\n new CNSStimulationContextStore();\n const spikeId = opts?.spikeId || Math.random().toString(36).slice(2);\n const maxHops = opts?.maxHops;\n const limit =\n (opts?.concurrency ?? 0) > 0 ? opts!.concurrency! : Infinity;\n\n // BFS queue backed by a Deque to avoid O(n) cost of Array.shift()\n const queue = new Deque<TQueueItem<TCollateralId>>();\n queue.push({\n collateralId: collateral.id,\n payload,\n hops: 0,\n spikeId,\n });\n\n // Used to avoid double-processing (neuronId + collateralId + spikeId)\n const seen = new Set<string>();\n\n /**\n * Number of operations currently being processed.\n * Both sync and async operations count toward this limit.\n * Streaming concurrency: no artificial barriers, just resource limiting.\n */\n let activeOperations = 0;\n\n /**\n * Fire-and-forget: we don't return promises, just trace errors.\n */\n\n /**\n * Streaming processor:\n * - Processes items from queue up to concurrency limit\n * - Each operation (sync/async) counts as one slot while active\n * - No level barriers - operations start as soon as slots are available\n * - Maintains fire-and-forget flow with proper backpressure\n *\n * Abort:\n * - If AbortSignal is aborted, stops pulling new work\n * - Already-launched operations continue to completion\n */\n const startOperation = () => {\n activeOperations++;\n };\n\n const finishOperation = () => {\n activeOperations--;\n // Stream processing: immediately try to start more work\n tryProcessMore();\n };\n\n const canStartOperation = (): boolean => {\n return activeOperations < limit && !opts?.abortSignal?.aborted;\n };\n\n const processItem = (item: TQueueItem<TCollateralId>) => {\n // Trace BEFORE processing to reflect actual order\n opts?.onTrace?.({\n collateralId: item.collateralId,\n hops: item.hops,\n payload: item.payload,\n queueLength: queue.length,\n contextStore: ctx, // Pass the context store\n });\n\n if (maxHops && item.hops >= maxHops) {\n // Skip due to hop guard, but still finish the operation\n finishOperation();\n return;\n }\n\n // Simplified callbacks for streaming concurrency\n const onOperationComplete = () => {\n finishOperation();\n };\n\n const onAsyncStart = () => {\n // Already counted in startOperation(), nothing extra needed\n };\n\n // Fan out (fire-and-forget)\n const launchedAsync = this.fanOutFireAndForget(\n item,\n opts?.allowType,\n seen,\n queue,\n ctx,\n maxHops !== undefined, // Enable dedup only if maxHops is set\n onAsyncStart,\n onOperationComplete,\n tryProcessMore, // Continue streaming on any completion\n opts?.onTrace\n );\n\n // If sync operation, complete immediately\n if (!launchedAsync) {\n finishOperation();\n }\n };\n\n const tryProcessMore = () => {\n // Stream processing: start as many operations as concurrency allows\n while (queue.length && canStartOperation()) {\n const item = queue.shift()!;\n startOperation();\n processItem(item);\n }\n };\n\n // Start streaming processing\n tryProcessMore();\n\n // Fire-and-forget: always return void\n }\n}\n","import { TCNSSignal } from './types/TCNSSignal';\n\nexport class CNSCollateral<TId extends string, TPayload> {\n constructor(public readonly id: TId) {}\n\n createSignal(\n ...args: [TPayload] extends [undefined] ? [] : [payload: TPayload]\n ): TCNSSignal<TId, TPayload> {\n const payload = (args as any)[0] as TPayload;\n return { type: this.id, payload };\n }\n}\n","import { CNS } from '../CNS';\nimport { CNSCollateral } from '../CNSCollateral';\nimport { TCNSAxon } from '../types/TCNSAxon';\nimport { TCNSDendrite } from '../types/TCNSDendrite';\n\nexport const collateral = <TPayload = undefined, TId extends string = string>(\n id: TId\n) => new CNSCollateral<TId, TPayload>(id);\n\ntype InterNeuronAPI<\n TContextValue,\n TIdType extends string,\n TReceiverCollateralIdType extends string,\n TReceiverAxonCollateralPayload,\n TAxonType extends TCNSAxon<\n TReceiverCollateralIdType,\n TReceiverAxonCollateralPayload\n >\n> = {\n id: TIdType;\n axon: TAxonType;\n dendrites: TCNSDendrite<\n TContextValue,\n string,\n unknown,\n TReceiverCollateralIdType,\n TReceiverAxonCollateralPayload,\n TAxonType\n >[];\n dendrite: <\n TSenderExactCollateralIdType extends string,\n TSenderExactAxonCollateralPayload\n >(\n s: TCNSDendrite<\n TContextValue,\n TSenderExactCollateralIdType,\n TSenderExactAxonCollateralPayload,\n TReceiverCollateralIdType,\n TReceiverAxonCollateralPayload,\n TAxonType\n >\n ) => InterNeuronAPI<\n TContextValue,\n TIdType,\n TReceiverCollateralIdType,\n TReceiverAxonCollateralPayload,\n TAxonType\n >;\n};\n\n// Concrete builder\nexport const neuron = <\n TContextValue,\n TIdType extends string,\n TReceiverCollateralIdType extends string,\n TReceiverAxonCollateralPayload,\n TAxonType extends TCNSAxon<\n TReceiverCollateralIdType,\n TReceiverAxonCollateralPayload\n >\n>(\n id: TIdType,\n axon: TAxonType\n): InterNeuronAPI<\n TContextValue,\n TIdType,\n TReceiverCollateralIdType,\n TReceiverAxonCollateralPayload,\n TAxonType\n> => {\n const dendrites: TCNSDendrite<\n TContextValue,\n string,\n unknown,\n TReceiverCollateralIdType,\n TReceiverAxonCollateralPayload,\n TAxonType\n >[] = [];\n\n const api: InterNeuronAPI<\n TContextValue,\n TIdType,\n TReceiverCollateralIdType,\n TReceiverAxonCollateralPayload,\n TAxonType\n > = {\n axon,\n id,\n dendrites,\n dendrite<\n TSenderExactCollateralIdType extends string,\n TSenderExactAxonCollateralPayload\n >(\n s: TCNSDendrite<\n TContextValue,\n TSenderExactCollateralIdType,\n TSenderExactAxonCollateralPayload,\n TReceiverCollateralIdType,\n TReceiverAxonCollateralPayload,\n TAxonType\n >\n ) {\n dendrites.push(\n s as TCNSDendrite<\n TContextValue,\n string,\n unknown,\n TReceiverCollateralIdType,\n TReceiverAxonCollateralPayload,\n TAxonType\n >\n );\n return api; // keep full API for chaining\n },\n };\n\n return api;\n};\n\nexport const withCtx = <TContextValue>() => ({\n neuron: <\n TIdType extends string,\n TReceiverCollateralIdType extends string,\n TReceiverAxonCollateralPayload,\n TAxonType extends TCNSAxon<\n TReceiverCollateralIdType,\n TReceiverAxonCollateralPayload\n >\n >(\n id: TIdType,\n axon: TAxonType\n ) => {\n return neuron<\n TContextValue,\n TIdType,\n TReceiverCollateralIdType,\n TReceiverAxonCollateralPayload,\n TAxonType\n >(id, axon);\n },\n});\n"]}
|