Package Exports
- @cnstra/core
Readme
@cnstra/core
Graph-routed, type-safe orchestration for reactive apps β no global event bus.
π§ What is CNStra?
CNStra (Central Nervous System Orchestrator) models your app as a typed neuron graph.
You explicitly start a run with cns.stimulate(...); CNStra then performs a deterministic, hop-bounded traversal from collateral β dendrite β returned signal, step by step.
Not pub/sub: there are no ambient listeners or global
emit. Only the signal you return from a dendrite continues the traversal; returningnull/undefinedends that branch.maxHopsguards against cycles.
ποΈ Core Model
Neurons
Units of logic with clear DI and sharp boundaries:
- ID β unique name
- Axon β the neuronβs output channels (its collaterals)
- Dendrites β input receptors (typed reactions bound to specific collaterals)
Collaterals
Typed output channels that mint signals:
- ID β string identifier (e.g.,
'user:created') - Payload β the shape carried by the signal
createSignal(payload)β{ type, payload }
Afferent axon: the object of collaterals you expose publicly. Its keys (e.g.,
userCreated) are what you pass tocns.stimulate(...), not the string IDs.
π Quick Start
npm install @cnstra/coreimport { CNS, collateral, neuron } from '@cnstra/core';
// Define collaterals (communication channels)
const userCreated = collateral<{ id: string; name: string }>('user:created');
const userRegistered = collateral<{ userId: string; status: string }>('user:registered');
// Create a neuron
const userService = neuron('user-service', {
userRegistered
})
.dendrite({
collateral: userCreated,
response: async (payload, axon) => {
const userData = payload as { id: string; name: string };
// Process the user creation
console.log(`Processing user: ${userData.name}`);
// Return the signal that will be processed by CNS
return axon.userRegistered.createSignal({
userId: userData.id,
status: 'completed'
});
}
});
// Create the CNS system
const cns = new CNS([userService]);
// Stimulate the system
await cns.stimulate(userCreated, {
id: '123',
name: 'John Doe'
});π API Reference
collateral<T>(id: string)
Creates a new collateral (communication channel).
const userEvent = collateral<{ userId: string }>('user:event');
const simpleEvent = collateral('simple:event'); // No payload typeneuron(id: string, axon: Axon)
Creates a new neuron with the specified axon (output channels).
const myNeuron = neuron('my-neuron', {
output: myCollateral
});neuron.dendrite(dendrite: Dendrite)
Adds a dendrite (input receptor) to a neuron. Returns the neuron for chaining.
myNeuron
.dendrite({
collateral: inputCollateral,
response: async (payload, axon, ctx) => {
// Process input and return output signal
// ctx parameter provides local context storage for this neuron
return axon.output.createSignal(result);
}
});CNS Class
The main orchestrator that manages signal flow between neurons.
Constructor
new CNS(neurons)Parameters:
neurons: Array of neurons that process signals
stimulate() Method
await cns.stimulate(collateral, payload, options?)Parameters:
collateral: The collateral instance to triggerpayload: Signal payload dataoptions: Optional configuration object
Options:
maxHops: Maximum number of signal hops (default: 1000)onTrace: Callback for tracing signal flowabortSignal: AbortController signal for cancellation
π Key Behavior
Only Returned Signals Are Processed
Important: In CNS, only the signal returned from the reaction function is processed and propagated to other neurons. Signals created with axon.collateral.createSignal() but not returned are NOT processed by the system.
const processor = neuron('processor', {
output: outputCollateral
})
.dendrite({
collateral: inputCollateral,
response: async (payload, axon) => {
// This signal is created but NOT processed
axon.output.createSignal({ message: 'Hello' });
// Only this returned signal is processed
return axon.output.createSignal({ message: 'World' });
}
});Multiple Neurons on Same Collateral
Multiple neurons can listen to the same collateral:
const emailService = neuron('email-service', { emailSent })
.dendrite({
collateral: userCreated,
response: async (payload, axon) => {
return axon.emailSent.createSignal({ to: 'user@example.com' });
}
});
const notificationService = neuron('notification-service', { notificationSent })
.dendrite({
collateral: userCreated,
response: async (payload, axon) => {
return axon.notificationSent.createSignal({ message: 'User created' });
}
});
// Both neurons will process the userCreated signal
const cns = new CNS([emailService, notificationService]);Conditional Logic
const router = neuron('router', {
success: successCollateral,
error: errorCollateral
})
.dendrite({
collateral: requestCollateral,
response: async (payload, axon) => {
try {
const result = await processRequest(payload);
return axon.success.createSignal(result);
} catch (error) {
return axon.error.createSignal({ error: error.message });
}
}
});Context Management
Neurons can maintain local state using the context parameter:
const statefulNeuron = neuron('stateful', { output: outputCollateral })
.dendrite({
collateral: inputCollateral,
response: async (payload, axon, ctx) => {
// Get current state
const currentState = ctx.get();
// Update state
ctx.set({ count: (currentState?.count || 0) + 1 });
return axon.output.createSignal({ count: ctx.get()?.count });
}
});π§ͺ Testing
npm test
npm run test:typesπ¦ Build
npm run buildπ Examples
Run the examples to see CNS in action:
npm run examplesπ€ Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
π License
MIT