CNStra API Reference - Neurons, Signals, Collaterals, Context
collateral<T>(id: string)
Create a typed output channel.
const userEvent = collateral<{ userId: string }>('user:event');
const simpleEvent = collateral('simple:event');
collateral.createSignal(payload?)
Create a signal from a collateral. Payload is optional for collaterals without payload type.
const signal = userEvent.createSignal({ userId: '123' });
const emptySignal = simpleEvent.createSignal(); // no payload
neuron(id: string, axon: Axon)
Create a neuron with the given axon.
const myNeuron = neuron('my-neuron', { output: myCollateral });
Signal ownership
A neuron may emit only collaterals declared in its own axon. It must not emit another neuron's collaterals. Cross-neuron orchestration is done by having a controller own request collaterals and letting each domain neuron emit its own responses.
Incorrect (emits someone else's collateral):
// DON'T: myNeuron emits otherAxon.some
return otherAxon.some.createSignal(result);
Correct (controller-owned request, domain emits its own):
const controller = neuron('controller', { requestA });
const serviceA = neuron('serviceA', { doneA })
.dendrite({ collateral: requestA, response: (_, axon) => axon.doneA.createSignal(...) });
// controller emits requestA; serviceA emits doneA
neuron.dendrite({...})
Add a dendrite bound to a collateral.
myNeuron.dendrite({
collateral: inputCollateral,
response: async (payload, axon, ctx) => {
// ctx: per-neuron per-stimulation context (metadata storage)
if (ctx.abortSignal?.aborted) return;
// Business data flows through payloads, not context
return axon.output.createSignal(result);
}
});
neuron.bind(axon, map)
Exhaustive bind to every collateral of another neuron's axon (compile-time safety).
// Context is for per-neuron per-stimulation metadata, not business data
withCtx<{ attempt: number }>().neuron('order-mailer', {})
.bind(order, {
created: (payload, axon, ctx) => {
// Context stores per-neuron per-stimulation metadata
const attempt = (ctx.get()?.attempt || 0) + 1;
ctx.set({ attempt });
// Business data flows through payloads
},
updated: (payload) => { /* ... */ },
cancelled: (payload) => { /* ... */ },
});
neuron.setConcurrency(n: number | undefined)
Set per-neuron global concurrency limit (shared across all parallel stimulations).
const worker = neuron('worker', { out })
.setConcurrency(2) // max 2 parallel executions across all runs
.dendrite({ collateral: task, response: async (p, axon) => { /* ... */ } });
This limits how many concurrent executions of this neuron's dendrites can run at the same time, even across different stimulate() calls. Useful for rate-limiting external APIs or heavy I/O operations.
neuron.setMaxDuration(ms: number | undefined)
Set maximum execution duration for this neuron's dendrites. If exceeded, the dendrite execution will be aborted.
const worker = neuron('worker', { out })
.setMaxDuration(5000) // 5 seconds max
.dendrite({ collateral: task, response: async (p, axon) => { /* ... */ } });
CNS
Main orchestrator. new CNS(neurons, options?)
Constructor options:
const cns = new CNS(neurons, {
autoCleanupContexts: false // Auto-cleanup unused contexts (performance warning: O(V²) cost)
});
Properties:
cns.network— Access to network graph analysis (SCC, subscribers, etc.)
const unsubscribe = cns.addResponseListener(r => { /* ... */ });
cns.stimulate(signal, options?)
Run a stimulation. Returns a CNSStimulation instance. Use stimulation.waitUntilComplete() to await completion.
const stimulation = cns.stimulate(userCreated.createSignal({ id: '123', name: 'John' }));
await stimulation.waitUntilComplete();
cns.activate(tasks, options?)
Start a stimulation with activation tasks directly. Useful for advanced scenarios where you need fine-grained control over task execution.
const tasks: TCNSNeuronActivationTask[] = [
{ stimulationId: 'run-1', neuronId: 'worker', dendriteCollateralName: 'task', input: signal }
];
const stimulation = cns.activate(tasks, { concurrency: 4 });
await stimulation.waitUntilComplete();
Single entry point
stimulate(...) and activate(...) are the entry points that begin execution. Nothing runs until you explicitly stimulate a signal or activate tasks. This is the "inverted" part of CNS: you start the run and each dendrite returns the explicit continuation.
Stimulation options
const stimulation = cns.stimulate(signal, {
onResponse: (r) => { /* per-stimulation hook */ },
abortSignal, // Abort the whole run cooperatively
concurrency: 4, // Per-stimulation parallelism
maxNeuronHops: undefined, // Disabled by default; set to cap traversal length
allowName: (neuronName) => true, // Filter allowed neurons by name
stimulationId: 'run-123', // Optional id for tracing
ctx, // Pre-supplied context store
contextValues: { 'neuron-id': { attempt: 0 } }, // Pre-populate context values
});
await stimulation.waitUntilComplete();
Response shape (for listeners)
Both onResponse and global listeners receive the same object:
{
inputSignal?: TCNSSignal; // when a signal is ingested
outputSignal?: TCNSSignal; // when a dendrite returns a continuation
contextValue: TCNSStimulationSerializedContextValue; // per-neuron per-stimulation metadata (not business data)
queueLength: number; // current work queue size
stimulation: CNSStimulation; // reference to the stimulation instance
error?: Error; // when a dendrite throws
hops?: number; // present if maxNeuronHops is set
stimulationId?: string; // unique identifier for this stimulation
}
Global response listeners (middleware‑style)
Use addResponseListener to attach cross‑cutting concerns (logging, metrics, tracing) that run for every stimulation.
const off = cns.addResponseListener((r) => {
if (r.error) {
metrics.count('error', 1);
return;
}
if (r.outputSignal) {
trace.log('out', r.outputSignal.collateralName);
} else if (r.inputSignal) {
trace.log('in', r.inputSignal.collateralName);
}
});
// later
off();
CNSStimulation methods
stimulation.waitUntilComplete()
Wait for the stimulation to complete. Returns a Promise that resolves when all tasks are done or rejects on error.
await stimulation.waitUntilComplete();
stimulation.getContext()
Get the context store for this stimulation. Useful for saving/restoring stimulation state.
const ctx = stimulation.getContext();
// Save for retry
const savedCtx = ctx;
stimulation.getFailedTasks()
Get all tasks that failed or were aborted.
const failures = stimulation.getFailedTasks();
failures.forEach(f => console.error(f.error));
cns.network — Network Graph Analysis
Access network analysis utilities:
// Get subscribers for a collateral
const subscribers = cns.network.getSubscribers('user:created');
// Get neuron by name
const neuron = cns.network.getNeuronByName('user-service');
// Get collateral by name
const collateral = cns.network.getCollateralByName('user:created');
// Get all neurons
const neurons = cns.network.getNeurons();
// Get all collaterals
const collaterals = cns.network.getCollaterals();
// Get strongly connected components
const sccs = cns.network.stronglyConnectedComponents;
Notes
- Local
onResponse(per stimulation) runs as well as global listeners; both can beasync. - All listeners run in parallel per response; errors from any listener reject the
stimulation.waitUntilComplete()Promise. - If all listeners are synchronous, no extra async deferrals are introduced.
- Use
allowName/maxNeuronHopsto constrain traversal if needed.