JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 75
  • Score
    100M100P100Q88637F
  • License MIT

Neural network-inspired event flow system for building reactive applications with dependency-injected neurons and synapses

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.

Zero dependencies: CNS has no third-party dependencies, making it suitable for any JavaScript/TypeScript environment - browsers, Node.js, serverless, edge functions, React Native, or embedded systems.

Not pub/sub: there are no ambient listeners or global emit. Only the signal you return from a dendrite continues the traversal; returning null/undefined ends that branch. Hop limits guard 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) → { collateral, payload }

Signals

The data structures that flow through the system:

  • collateral — reference to the collateral that created this signal
  • payload — the typed data being transmitted

šŸš€ Quick Start

npm install @cnstra/core
import { 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: (payload, axon) => {
    const userData = payload;
    
    // 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.createSignal({
  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 type

neuron(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, options?)

Parameters:

  • neurons: Array of neurons that process signals
  • options: Optional CNS configuration

stimulate() Method

cns.stimulate(signal, options?)

Parameters:

  • signal: A signal created by collateral.createSignal(payload)
  • options: Optional stimulation configuration

Returns: Promise<void> that resolves when stimulation completes

Example:

await cns.stimulate(
  userCreated.createSignal({ id: '123', name: 'John' })
);

āš™ļø Stimulation Options

maxNeuronHops?: number (default: 1000)

Prevents infinite loops by limiting signal traversal depth.

await cns.stimulate(signal, {
  maxNeuronHops: 50 // Stop after 50 neuron hops
});

onResponse?: (response) => void

Real-time callback for monitoring signal flow and completion.

await cns.stimulate(signal, {
  onResponse: (response) => {
    console.log(`Signal: ${response.outputSignal?.collateral.id}`);
    console.log(`Hops: ${response.hops}`);
    
    if (response.error) {
      console.error('Processing failed:', response.error);
    }
    
    if (response.queueLength === 0) {
      console.log('Stimulation completed');
    }
  }
});

Response Object:

  • outputSignal — The signal being processed (if any)
  • hops — Number of neuron hops taken so far
  • queueLength — Remaining signals in processing queue (0 = complete)
  • error — Any error that occurred during processing
  • stimulationId — Unique identifier for this stimulation

abortSignal?: AbortSignal

Gracefully stop stimulation using AbortController.

const controller = new AbortController();

cns.stimulate(signal, {
  abortSignal: controller.signal
});

// Cancel after 5 seconds
setTimeout(() => controller.abort(), 5000);

stimulationId?: string

Custom identifier for this stimulation cascade. Auto-generated if not provided.

await cns.stimulate(signal, {
  stimulationId: 'user-action-' + Date.now()
});

allowType?: (collateralId: string) => boolean

Filter which collateral types can be processed.

await cns.stimulate(signal, {
  allowType: (type) => type.startsWith('user:') // Only process user-related signals
});

concurrency?: number (default: unlimited)

Limit concurrent operations to prevent resource exhaustion.

await cns.stimulate(signal, {
  concurrency: 10 // Max 10 operations at once
});

ctx?: ICNSStimulationContextStore

Provide existing context store for recovery/retry scenarios.

await cns.stimulate(signal, {
  ctx: savedContextStore // Restore previous state
});

createContextStore?: () => ICNSStimulationContextStore

Factory for custom context store implementations.

await cns.stimulate(signal, {
  createContextStore: () => new CustomContextStore()
});

šŸ”„ Signal Flow Patterns

Basic Chain Processing

const input = collateral<{ value: number }>('input');
const middle = collateral<{ doubled: number }>('middle');
const output = collateral<{ result: string }>('output');

const step1 = neuron('step1', { middle }).dendrite({
  collateral: input,
  response: (payload, axon) => {
    return axon.middle.createSignal({ doubled: payload.value * 2 });
  }
});

const step2 = neuron('step2', { output }).dendrite({
  collateral: middle,
  response: (payload, axon) => {
    return axon.output.createSignal({ result: `Final: ${payload.doubled}` });
  }
});

const cns = new CNS([step1, step2]);

await cns.stimulate(input.createSignal({ value: 5 }));
// Flows: input(5) → middle(10) → output("Final: 10")

Fan-out Processing

const trigger = collateral<{ data: string }>('trigger');
const branch1 = collateral<{ result: string }>('branch1');
const branch2 = collateral<{ result: string }>('branch2');

const processor1 = neuron('proc1', { branch1 }).dendrite({
  collateral: trigger,
  response: (payload, axon) => {
    return axon.branch1.createSignal({ result: `A-${payload.data}` });
  }
});

const processor2 = neuron('proc2', { branch2 }).dendrite({
  collateral: trigger,
  response: (payload, axon) => {
    return axon.branch2.createSignal({ result: `B-${payload.data}` });
  }
});

const cns = new CNS([processor1, processor2]);

await cns.stimulate(trigger.createSignal({ data: 'test' }));
// Flows: trigger("test") → [branch1("A-test"), branch2("B-test")]

Context-Aware Processing

import { withCtx } from '@cnstra/core';

const input = collateral<{ increment: number }>('input');
const output = collateral<{ count: number }>('output');

const counter = withCtx<{ total: number }>()
  .neuron('counter', { output })
  .dendrite({
    collateral: input,
    response: async (payload, axon, ctx) => {
      const current = ctx.get()?.total || 0;
      const newTotal = current + payload.increment;
      
      ctx.set({ total: newTotal });
      
      return axon.output.createSignal({ count: newTotal });
    }
  });

const cns = new CNS([counter]);

await cns.stimulate(input.createSignal({ increment: 5 })); // count: 5
await cns.stimulate(input.createSignal({ increment: 3 })); // count: 8 (separate context)

🧠 Memory & Performance

Memory-Efficient Design

  • Zero dependencies: No third-party packages
  • No error storage: Errors delivered via callbacks, not stored
  • Streaming responses: Signal traces delivered via callbacks
  • Context on-demand: Context stores created only when needed
  • No global state: Clean slate between stimulations

Performance Characteristics

  • Sync-first: Synchronous chains execute in single tick
  • Minimal async overhead: Promises created only when needed
  • Stack-safe: Handles deep chains without stack overflow
  • Bounded execution: maxNeuronHops prevents runaway processing

Best Practices

  • Keep context data minimal (IDs, counters, flags)
  • Use synchronous responses when possible
  • Set reasonable maxNeuronHops limits
  • Implement proper error handling in onResponse

šŸŽÆ Common Use Cases

HTTP Request Processing

const httpRequest = collateral<{ method: string; url: string }>('http:request');
const requestValidated = collateral<{ method: string; url: string }>('request:validated');
const responseReady = collateral<{ status: number; body: any }>('response:ready');

const validator = neuron('validator', { requestValidated }).dendrite({
  collateral: httpRequest,
  response: (payload, axon) => {
    if (!payload.url.startsWith('https://')) {
      throw new Error('Only HTTPS URLs allowed');
    }
    return axon.requestValidated.createSignal(payload);
  }
});

const handler = neuron('handler', { responseReady }).dendrite({
  collateral: requestValidated,
  response: async (payload, axon) => {
    const response = await fetch(payload.url, { method: payload.method });
    const body = await response.json();
    return axon.responseReady.createSignal({ status: response.status, body });
  }
});

const cns = new CNS([validator, handler]);

Event Sourcing

const eventReceived = collateral<{ type: string; data: any }>('event:received');
const eventStored = collateral<{ eventId: string }>('event:stored');
const stateUpdated = collateral<{ aggregateId: string }>('state:updated');

const eventStore = neuron('event-store', { eventStored }).dendrite({
  collateral: eventReceived,
  response: async (payload, axon) => {
    const eventId = await saveEvent(payload);
    return axon.eventStored.createSignal({ eventId });
  }
});

const stateManager = neuron('state-manager', { stateUpdated }).dendrite({
  collateral: eventStored,
  response: async (payload, axon) => {
    const aggregateId = await updateState(payload.eventId);
    return axon.stateUpdated.createSignal({ aggregateId });
  }
});

const cns = new CNS([eventStore, stateManager]);

🚨 Error Handling

Errors are delivered immediately via onResponse callbacks:

await cns.stimulate(signal, {
  onResponse: (response) => {
    if (response.error) {
      console.error(`Error in neuron processing:`, response.error);
      
      // Log error details
      console.error(`Signal: ${response.outputSignal?.collateral.id}`);
      console.error(`Stimulation: ${response.stimulationId}`);
      
      // Handle specific error types
      if (response.error instanceof ValidationError) {
        handleValidationError(response.error);
      }
    }
  }
});

Error Recovery with Context:

let savedContext: ICNSStimulationContextStore | undefined;

await cns.stimulate(signal, {
  onResponse: (response) => {
    if (response.error) {
      // Save context for retry
      savedContext = response.contextStore;
    }
  }
});

// Retry with preserved context
if (savedContext) {
  await cns.stimulate(retrySignal, {
    ctx: savedContext
  });
}

šŸ”§ Advanced Configuration

Custom Context Store

class RedisContextStore implements ICNSStimulationContextStore {
  constructor(private client: RedisClient, private sessionId: string) {}
  
  get<T>(): T | undefined {
    // Implement Redis-backed context retrieval
  }
  
  set<T>(value: T): void {
    // Implement Redis-backed context storage
  }
}

await cns.stimulate(signal, {
  createContextStore: () => new RedisContextStore(redisClient, 'session-123')
});

CNS Configuration

const cns = new CNS(neurons, {
  autoCleanupContexts: true, // Auto-cleanup unused contexts
  defaultConcurrency: 50     // Default concurrency limit
});

āš ļø Performance Warning: autoCleanupContexts adds computational overhead due to:

  • O(V²) initialization cost - building SCC (Strongly Connected Components) structures
  • O(1 + A) runtime cost per cleanup check (where A = number of SCC ancestors)
  • Memory overhead for storing SCC graphs and ancestor relationships

Use only when:

  • Memory leaks are a critical issue
  • You have a small to medium-sized neuron graph (< 1000 neurons)
  • Performance is less critical than memory management

For production systems, consider manual context cleanup or custom cleanup strategies instead.


CNStra provides deterministic, type-safe orchestration without the complexity of traditional event systems. Build reliable, maintainable reactive applications with clear data flow and predictable behavior.