@cnstra/core 1.8.0 → 2.0.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 (58) hide show
  1. package/README.md +17 -612
  2. package/dist/CNS.d.ts +6 -9
  3. package/dist/CNS.d.ts.map +1 -1
  4. package/dist/CNSCollateral.d.ts +3 -5
  5. package/dist/CNSCollateral.d.ts.map +1 -1
  6. package/dist/CNSInstanceNeuronQueue.d.ts +0 -1
  7. package/dist/CNSInstanceNeuronQueue.d.ts.map +1 -1
  8. package/dist/CNSNetwork.d.ts +16 -20
  9. package/dist/CNSNetwork.d.ts.map +1 -1
  10. package/dist/CNSNeuronActivationPump.d.ts +5 -5
  11. package/dist/CNSNeuronActivationPump.d.ts.map +1 -1
  12. package/dist/CNSStimulation.d.ts +16 -15
  13. package/dist/CNSStimulation.d.ts.map +1 -1
  14. package/dist/CNSStimulationContextStore.d.ts +6 -6
  15. package/dist/CNSStimulationContextStore.d.ts.map +1 -1
  16. package/dist/factory/index.d.ts +97 -14
  17. package/dist/factory/index.d.ts.map +1 -1
  18. package/dist/index.cjs +235 -197
  19. package/dist/index.cjs.map +1 -1
  20. package/dist/index.d.ts +4 -1
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +234 -198
  23. package/dist/index.js.map +1 -1
  24. package/dist/interfaces/ICNS.d.ts +5 -4
  25. package/dist/interfaces/ICNS.d.ts.map +1 -1
  26. package/dist/interfaces/ICNSCollateral.d.ts +3 -3
  27. package/dist/interfaces/ICNSCollateral.d.ts.map +1 -1
  28. package/dist/interfaces/ICNSStimulationContextStore.d.ts +5 -6
  29. package/dist/interfaces/ICNSStimulationContextStore.d.ts.map +1 -1
  30. package/dist/types/TCNSAfferentPath.d.ts +4 -0
  31. package/dist/types/TCNSAfferentPath.d.ts.map +1 -0
  32. package/dist/types/TCNSAxon.d.ts +23 -2
  33. package/dist/types/TCNSAxon.d.ts.map +1 -1
  34. package/dist/types/TCNSDendrite.d.ts +6 -6
  35. package/dist/types/TCNSDendrite.d.ts.map +1 -1
  36. package/dist/types/TCNSModality.d.ts +5 -0
  37. package/dist/types/TCNSModality.d.ts.map +1 -0
  38. package/dist/types/TCNSNeuron.d.ts +3 -3
  39. package/dist/types/TCNSNeuron.d.ts.map +1 -1
  40. package/dist/types/TCNSNeuronActivationTask.d.ts +5 -5
  41. package/dist/types/TCNSNeuronActivationTask.d.ts.map +1 -1
  42. package/dist/types/TCNSNeuronActivationTaskFailure.d.ts +2 -2
  43. package/dist/types/TCNSNeuronActivationTaskFailure.d.ts.map +1 -1
  44. package/dist/types/TCNSNeuronResponseReturn.d.ts +8 -2
  45. package/dist/types/TCNSNeuronResponseReturn.d.ts.map +1 -1
  46. package/dist/types/TCNSPersist.d.ts +16 -0
  47. package/dist/types/TCNSPersist.d.ts.map +1 -0
  48. package/dist/types/TCNSSignal.d.ts +4 -3
  49. package/dist/types/TCNSSignal.d.ts.map +1 -1
  50. package/dist/types/TCNSStimulationOptions.d.ts +9 -9
  51. package/dist/types/TCNSStimulationOptions.d.ts.map +1 -1
  52. package/dist/types/TCNSStimulationQueueItem.d.ts +4 -3
  53. package/dist/types/TCNSStimulationQueueItem.d.ts.map +1 -1
  54. package/dist/types/TCNSStimulationResponse.d.ts +10 -11
  55. package/dist/types/TCNSStimulationResponse.d.ts.map +1 -1
  56. package/dist/types/TCNSSubscriber.d.ts +1 -1
  57. package/dist/types/TCNSSubscriber.d.ts.map +1 -1
  58. package/package.json +1 -1
package/README.md CHANGED
@@ -2,49 +2,17 @@
2
2
 
3
3
  **Graph-routed, type-safe orchestration for reactive apps — no global event bus.**
4
4
 
5
- 📚 **[Full Documentation](https://cnstra.org/)** | [Quick Start](https://cnstra.org/docs/core/quick-start) | [API Reference](https://cnstra.org/docs/core/api) | [Recipes](https://cnstra.org/docs/recipes/cancel)
5
+ 📚 **[Full Documentation →](https://cnstra.org/)** | [Quick Start](https://cnstra.org/docs/core/quick-start) | [API Reference](https://cnstra.org/docs/core/api) | [Recipes](https://cnstra.org/docs/recipes)
6
6
 
7
- ## 🧠 What is CNStra?
7
+ ## What is CNStra?
8
8
 
9
- **CNStra (Central Nervous System Orchestrator)** models your app as a **typed neuron graph**.
10
- You explicitly start a run with `cns.stimulate(...)`; CNStra then performs a **deterministic, hop-bounded traversal** from **collateral → dendrite → returned signal**, step by step.
9
+ **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.
11
10
 
12
- > **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.
11
+ **Zero dependencies** • **No pub/sub** • **CNS approach** (Central Neural Network of your app)
13
12
 
14
- **No 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.
13
+ 👉 **[Read the full documentation →](https://cnstra.org/)**
15
14
 
16
- We follow the **IERG approach — Inverted Explicit Reactive Graph**. You explicitly stimulate the graph; reactions are local and deterministic; no background listeners or buses exist.
17
-
18
- ## 💡 Why CNStra
19
-
20
- IERG is our inverted control flow: you start the run, we walk the reaction graph explicitly. This is not Flux and not an event bus.
21
-
22
- - **Deterministic routing**: signals are delivered along an explicit neuron graph, not broadcast to whoever “happens to listen”.
23
- - **Readable, reliable flows**: each step is local and typed; branches are explicit, so debugging feels like reading a storyboard, not a log stream.
24
- - **Backpressure & concurrency**: built‑in per‑stimulation and per‑neuron concurrency limits keep workloads controlled without custom plumbing.
25
- - **Saga‑grade orchestration**: IERG already models long‑running, multi‑step reactions with retries/cancellation hooks (abort), so you rarely need to hand‑roll “sagas”.
26
- - **Safer than ad‑hoc events**: no hidden global listeners, no accidental fan‑out; every continuation must be returned explicitly.
27
-
28
- ## 🏗️ Core Model
29
-
30
- ### Neurons
31
- Units of logic with clear DI and sharp boundaries:
32
- - **ID** — unique name
33
- - **Axon** — the neuron's **output channels** (its collaterals)
34
- - **Dendrites** — **input receptors** (typed reactions bound to specific collaterals)
35
-
36
- ### Collaterals
37
- Typed **output channels** that mint signals:
38
- - **ID** — string identifier (e.g., `'user:created'`)
39
- - **Payload** — the shape carried by the signal
40
- - `createSignal(payload)` → `{ collateral, payload }`
41
-
42
- ### Signals
43
- The data structures that flow through the system:
44
- - **collateral** — reference to the collateral that created this signal
45
- - **payload** — the typed data being transmitted
46
-
47
- ## 🚀 Quick Start
15
+ ## Quick Start
48
16
 
49
17
  ```bash
50
18
  npm install @cnstra/core
@@ -54,24 +22,18 @@ npm install @cnstra/core
54
22
  import { CNS, collateral, neuron } from '@cnstra/core';
55
23
 
56
24
  // Define collaterals (communication channels)
57
- const userCreated = collateral<{ id: string; name: string }>('user:created');
58
- const userRegistered = collateral<{ userId: string; status: string }>('user:registered');
25
+ const userCreated = collateral<{ id: string; name: string }>();
26
+ const userRegistered = collateral<{ userId: string; status: string }>();
59
27
 
60
28
  // Create a neuron
61
- const userService = neuron('user-service', {
29
+ const userService = neuron({
62
30
  userRegistered
63
31
  })
64
32
  .dendrite({
65
33
  collateral: userCreated,
66
34
  response: (payload, axon) => {
67
- const userData = payload;
68
-
69
- // Process the user creation
70
- console.log(`Processing user: ${userData.name}`);
71
-
72
- // Return the signal that will be processed by CNS
73
35
  return axon.userRegistered.createSignal({
74
- userId: userData.id,
36
+ userId: payload.id,
75
37
  status: 'completed'
76
38
  });
77
39
  }
@@ -88,571 +50,14 @@ const stimulation = cns.stimulate(userCreated.createSignal({
88
50
  await stimulation.waitUntilComplete();
89
51
  ```
90
52
 
91
- ## 📚 API Reference
92
-
93
- ### `collateral<T>(id: string)`
94
-
95
- Creates a new collateral (communication channel).
96
-
97
- ```typescript
98
- const userEvent = collateral<{ userId: string }>('user:event');
99
- const simpleEvent = collateral('simple:event'); // No payload type
100
- ```
101
-
102
- ### `neuron(id: string, axon: Axon)`
103
-
104
- Creates a new neuron with the specified axon (output channels).
105
-
106
- ```typescript
107
- const myNeuron = neuron('my-neuron', {
108
- output: myCollateral
109
- });
110
- ```
111
-
112
- ### `neuron.dendrite(dendrite: Dendrite)`
113
-
114
- Adds a dendrite (input receptor) to a neuron. Returns the neuron for chaining.
115
-
116
- The `response` function can return:
117
- - A single signal: `axon.output.createSignal(data)`
118
- - An array of signals: `[axon.output1.createSignal(data1), axon.output2.createSignal(data2)]`
119
- - A Promise of either
120
- - `undefined`/`void` to end processing
121
-
122
- ```typescript
123
- myNeuron
124
- .dendrite({
125
- collateral: inputCollateral,
126
- response: async (payload, axon, ctx) => {
127
- // ctx: { get, set, abortSignal?, cns? }
128
- if (ctx.abortSignal?.aborted) return; // graceful cancel
129
- const prev = ctx.get();
130
- ctx.set({ ...prev, handled: true });
131
-
132
- // Return single signal or array of signals
133
- return axon.output.createSignal(result);
134
- }
135
- });
136
- ```
137
-
138
- ### `neuron.bind(axon, map)` — exhaustive subscriptions (with shorthand)
139
-
140
- Bind this neuron to every collateral of another neuron's axon in one place. The `map` must be exhaustive: you must provide a handler for each collateral key of `axon`. This gives you compile-time safety: if someone adds a new collateral later, TypeScript will immediately flag missing handlers.
141
-
142
- - You can pass either a full dendrite object per key or just a response function shorthand.
143
- - Payload types are inferred from the followed axon.
144
-
145
- This pattern is especially useful for domain-oriented neurons that must react to every way a record can be created/changed. For example, an email-notifier neuron can safely ensure emails are sent for every creation path; if a new creation collateral is introduced, the build will fail until the notifier adds a corresponding handler.
146
-
147
- ```typescript
148
- import { withCtx, collateral } from '@cnstra/core';
149
-
150
- // Order domain model (axon)
151
- const order = {
152
- created: collateral<{ id: string; amount: number }>('order:created'),
153
- updated: collateral<{ id: string; changes: Record<string, unknown> }>('order:updated'),
154
- cancelled: collateral<{ id: string; reason?: string }>('order:cancelled'),
155
- };
156
-
157
- // Mailer neuron must react to ALL order events
158
- withCtx()
159
- .neuron('order-mailer', { /* your axon if you emit follow-up signals */ })
160
- .bind(order, {
161
- created: (payload) => {
162
- sendEmail(`Order created #${payload.id} for $${payload.amount}`);
163
- return undefined;
164
- },
165
- updated: (payload) => {
166
- sendEmail(`Order updated #${payload.id} (changes: ${Object.keys(payload.changes).join(', ')})`);
167
- return undefined;
168
- },
169
- cancelled: (payload) => {
170
- sendEmail(`Order cancelled #${payload.id}${payload.reason ? `: ${payload.reason}` : ''}`);
171
- return undefined;
172
- },
173
- });
174
-
175
- // If later someone adds a new event variant, e.g. refunds:
176
- // const order = { ...order, refunded: collateral<{ id: string; amount: number }>('order:refunded') };
177
- // TypeScript will now error until you also add a `refunded` handler in the .bind(...) map.
178
- ```
179
-
180
- ### `CNS` Class
181
-
182
- The main orchestrator that manages signal flow between neurons.
183
-
184
- #### Constructor
185
- ```typescript
186
- new CNS(neurons, options?)
187
- ```
188
-
189
- **Parameters:**
190
- - `neurons`: Array of neurons that process signals
191
- - `options`: Optional CNS configuration
192
-
193
- #### Global listeners
194
- ```typescript
195
- const unsubscribe = cns.addResponseListener(r => {
196
- // fires for every stimulation (input + outputs)
197
- });
198
- // unsubscribe();
199
- ```
200
-
201
- #### `stimulate()` Method
202
- ```typescript
203
- cns.stimulate(signal | signal[], options?)
204
- ```
205
-
206
- **Parameters:**
207
- - `signal`: A signal or array of signals created by `collateral.createSignal(payload)`
208
- - `options`: Optional stimulation configuration
209
-
210
- **Returns:** `CNSStimulation` instance. Use `stimulation.waitUntilComplete()` to await completion.
211
-
212
- **Example:**
213
- ```typescript
214
- // Single signal
215
- const stimulation = cns.stimulate(
216
- userCreated.createSignal({ id: '123', name: 'John' })
217
- );
218
- await stimulation.waitUntilComplete();
219
-
220
- // Multiple signals at once
221
- const stimulation = cns.stimulate([
222
- userCreated.createSignal({ id: '123', name: 'John' }),
223
- userCreated.createSignal({ id: '456', name: 'Jane' })
224
- ]);
225
- await stimulation.waitUntilComplete();
226
- ```
227
-
228
- ## ⚙️ Stimulation Options
229
-
230
- ### `maxNeuronHops?: number` (default: 1000)
231
- Prevents infinite loops by limiting signal traversal depth.
232
-
233
- ```typescript
234
- const stimulation = cns.stimulate(signal, {
235
- maxNeuronHops: 50 // Stop after 50 neuron hops
236
- });
237
- await stimulation.waitUntilComplete();
238
- ```
239
-
240
- ### `onResponse?: (response) => void`
241
- Real-time callback for monitoring signal flow and completion.
242
-
243
- ```typescript
244
- const stimulation = cns.stimulate(signal, {
245
- onResponse: (response) => {
246
- console.log(`Signal: ${response.outputSignal?.collateral.id}`);
247
- console.log(`Hops: ${response.hops}`);
248
-
249
- if (response.error) {
250
- console.error('Processing failed:', response.error);
251
- }
252
-
253
- if (response.queueLength === 0) {
254
- console.log('Stimulation completed');
255
- }
256
- }
257
- });
258
- await stimulation.waitUntilComplete();
259
- ```
260
-
261
- **Response Object:**
262
- - `inputSignal` — The input signal being ingested (if any)
263
- - `outputSignal` — The output signal being processed (if any)
264
- - `contextValue` — The context value for this stimulation
265
- - `queueLength` — Remaining signals in processing queue (0 = complete)
266
- - `stimulation` — Reference to the CNSStimulation instance
267
- - `error` — Any error that occurred during processing
268
- - `hops` — Number of neuron hops taken so far (only present if maxNeuronHops is set)
269
- - `stimulationId` — Unique identifier for this stimulation
270
-
271
- ### `abortSignal?: AbortSignal`
272
- Gracefully stop stimulation using AbortController.
273
-
274
- ```typescript
275
- const controller = new AbortController();
276
-
277
- const stimulation = cns.stimulate(signal, {
278
- abortSignal: controller.signal
279
- });
280
-
281
- // Cancel after 5 seconds
282
- setTimeout(() => controller.abort(), 5000);
283
- await stimulation.waitUntilComplete();
284
- ```
285
- Inside dendrites, read it via `ctx.abortSignal`.
286
-
287
- ### `stimulationId?: string`
288
- Custom identifier for this stimulation cascade. Auto-generated if not provided.
289
-
290
- ```typescript
291
- const stimulation = cns.stimulate(signal, {
292
- stimulationId: 'user-action-' + Date.now()
293
- });
294
- await stimulation.waitUntilComplete();
295
- ```
296
-
297
- ### `allowName?: (collateralName: string) => boolean`
298
- Filter which collateral names can be processed.
299
-
300
- ```typescript
301
- const stimulation = cns.stimulate(signal, {
302
- allowName: (name) => name.startsWith('user:') // Only process user-related signals
303
- });
304
- await stimulation.waitUntilComplete();
305
- ```
306
-
307
- ### `concurrency?: number` (default: unlimited)
308
- Limit concurrent operations to prevent resource exhaustion.
309
-
310
- ```typescript
311
- const stimulation = cns.stimulate(signal, {
312
- concurrency: 10 // Max 10 operations at once
313
- });
314
- await stimulation.waitUntilComplete();
315
- ```
316
-
317
- #### Per‑neuron global concurrency
318
- Set a limit per neuron; parallel stimulations share the same gate.
319
- ```typescript
320
- const worker = neuron('worker', { out })
321
- .setConcurrency(2)
322
- .dendrite({ /* ... */ });
323
- ```
324
-
325
- ### `ctx?: ICNSStimulationContextStore`
326
- Provide existing context store for recovery/retry scenarios.
327
-
328
- ```typescript
329
- const stimulation = cns.stimulate(signal, {
330
- ctx: savedContextStore // Restore previous state
331
- });
332
- await stimulation.waitUntilComplete();
333
- ```
334
-
335
- ### `createContextStore?: () => ICNSStimulationContextStore`
336
- Factory for custom context store implementations.
337
-
338
- ```typescript
339
- const stimulation = cns.stimulate(signal, {
340
- createContextStore: () => new CustomContextStore()
341
- });
342
- await stimulation.waitUntilComplete();
343
- ```
344
-
345
- ## 🔄 Signal Flow Patterns
346
-
347
- ### Basic Chain Processing
348
-
349
- ```typescript
350
- const input = collateral<{ value: number }>('input');
351
- const middle = collateral<{ doubled: number }>('middle');
352
- const output = collateral<{ result: string }>('output');
353
-
354
- const step1 = neuron('step1', { middle }).dendrite({
355
- collateral: input,
356
- response: (payload, axon) => {
357
- return axon.middle.createSignal({ doubled: payload.value * 2 });
358
- }
359
- });
360
-
361
- const step2 = neuron('step2', { output }).dendrite({
362
- collateral: middle,
363
- response: (payload, axon) => {
364
- return axon.output.createSignal({ result: `Final: ${payload.doubled}` });
365
- }
366
- });
367
-
368
- const cns = new CNS([step1, step2]);
369
-
370
- const stimulation = cns.stimulate(input.createSignal({ value: 5 }));
371
- await stimulation.waitUntilComplete();
372
- // Flows: input(5) → middle(10) → output("Final: 10")
373
- ```
374
-
375
- ### Context-Aware Processing
376
-
377
- ```typescript
378
- import { withCtx } from '@cnstra/core';
379
-
380
- const input = collateral<{ increment: number }>('input');
381
- const output = collateral<{ count: number }>('output');
382
-
383
- const counter = withCtx<{ total: number }>()
384
- .neuron('counter', { output })
385
- .dendrite({
386
- collateral: input,
387
- response: async (payload, axon, ctx) => {
388
- const current = ctx.get()?.total || 0;
389
- const newTotal = current + payload.increment;
390
-
391
- ctx.set({ total: newTotal });
392
-
393
- return axon.output.createSignal({ count: newTotal });
394
- }
395
- });
396
-
397
- const cns = new CNS([counter]);
398
-
399
- const stimulation1 = cns.stimulate(input.createSignal({ increment: 5 }));
400
- await stimulation1.waitUntilComplete(); // count: 5
401
- const stimulation2 = cns.stimulate(input.createSignal({ increment: 3 }));
402
- await stimulation2.waitUntilComplete(); // count: 8 (separate context)
403
- ```
404
-
405
- ### Multiple Signals (Fan-out Pattern)
406
-
407
- Neurons can return arrays of signals for parallel processing:
408
-
409
- ```typescript
410
- const orderPlaced = collateral<{ orderId: string; items: string[] }>('order:placed');
411
- const updateInventory = collateral<{ orderId: string; item: string }>('update:inventory');
412
- const sendEmail = collateral<{ orderId: string }>('send:email');
413
- const logAudit = collateral<{ orderId: string }>('log:audit');
414
-
415
- const orderProcessor = neuron('order-processor', {
416
- updateInventory,
417
- sendEmail,
418
- logAudit
419
- }).dendrite({
420
- collateral: orderPlaced,
421
- response: (payload, axon) => {
422
- // Return array of signals - each will be processed independently
423
- return [
424
- // Create signal for each item
425
- ...payload.items.map(item =>
426
- axon.updateInventory.createSignal({ orderId: payload.orderId, item })
427
- ),
428
- // Also send confirmation email
429
- axon.sendEmail.createSignal({ orderId: payload.orderId }),
430
- // And log the action
431
- axon.logAudit.createSignal({ orderId: payload.orderId })
432
- ];
433
- }
434
- });
435
-
436
- // Downstream neurons process each signal independently
437
- const inventoryService = neuron('inventory-service', {}).dendrite({
438
- collateral: updateInventory,
439
- response: (payload) => {
440
- console.log(`Updating inventory for ${payload.item}`);
441
- // Each item gets its own execution
442
- }
443
- });
444
-
445
- const cns = new CNS([orderProcessor, inventoryService /* ... */]);
446
-
447
- // Single order triggers multiple parallel operations
448
- const stimulation = cns.stimulate(
449
- orderPlaced.createSignal({ orderId: 'ORD-001', items: ['A', 'B', 'C'] })
450
- );
451
- await stimulation.waitUntilComplete();
452
- // Flows: orderPlaced → [inventory(A), inventory(B), inventory(C), email, audit]
453
- ```
454
-
455
- **Conditional Arrays:**
456
- ```typescript
457
- const validator = neuron('validator', { success, error, audit }).dendrite({
458
- collateral: input,
459
- response: (payload, axon) => {
460
- const signals = [];
461
-
462
- // Conditionally add signals
463
- if (isValid(payload.data)) {
464
- signals.push(axon.success.createSignal({ validated: payload.data }));
465
- } else {
466
- signals.push(axon.error.createSignal({ error: 'Invalid' }));
467
- }
468
-
469
- // Always audit
470
- signals.push(axon.audit.createSignal({ attempted: true }));
471
-
472
- return signals; // Return empty array to emit nothing
473
- }
474
- });
475
- ```
476
-
477
- **Starting with Multiple Signals:**
478
- ```typescript
479
- // Process multiple orders in parallel
480
- const stimulation = cns.stimulate([
481
- orderPlaced.createSignal({ orderId: 'ORD-001', items: ['X'] }),
482
- orderPlaced.createSignal({ orderId: 'ORD-002', items: ['Y', 'Z'] })
483
- ]);
484
- await stimulation.waitUntilComplete();
485
- ```
486
-
487
- See [Multiple Signals Recipe](https://cnstra.org/docs/recipes/multiple-signals) for more patterns and best practices.
488
-
489
- ## 🧠 Memory & Performance
490
-
491
- ### Memory-Efficient Design
492
- - **Zero dependencies**: No third-party packages
493
- - **No error storage**: Errors delivered via callbacks, not stored
494
- - **Streaming responses**: Signal traces delivered via callbacks
495
- - **Context on-demand**: Context stores created only when needed
496
- - **No global state**: Clean slate between stimulations
497
-
498
- ### Performance Characteristics
499
- - **Sync-first**: Synchronous chains execute in single tick
500
- - **Minimal async overhead**: Promises created only when needed
501
- - **Stack-safe**: Handles deep chains without stack overflow
502
- - **Bounded execution**: `maxNeuronHops` prevents runaway processing
503
-
504
- ### Best Practices
505
- - Keep context data minimal (IDs, counters, flags)
506
- - Use synchronous responses when possible
507
- - Set reasonable `maxNeuronHops` limits
508
- - Implement proper error handling in `onResponse`
509
- - Use array returns for genuine fan-out patterns, not just convenience
510
- - Consider concurrency limits when returning many signals
511
-
512
- ## 🎯 Common Use Cases
513
-
514
- ### HTTP Request Processing
515
- ```typescript
516
- const httpRequest = collateral<{ method: string; url: string }>('http:request');
517
- const requestValidated = collateral<{ method: string; url: string }>('request:validated');
518
- const responseReady = collateral<{ status: number; body: any }>('response:ready');
519
-
520
- const validator = neuron('validator', { requestValidated }).dendrite({
521
- collateral: httpRequest,
522
- response: (payload, axon) => {
523
- if (!payload.url.startsWith('https://')) {
524
- throw new Error('Only HTTPS URLs allowed');
525
- }
526
- return axon.requestValidated.createSignal(payload);
527
- }
528
- });
529
-
530
- const handler = neuron('handler', { responseReady }).dendrite({
531
- collateral: requestValidated,
532
- response: async (payload, axon) => {
533
- const response = await fetch(payload.url, { method: payload.method });
534
- const body = await response.json();
535
- return axon.responseReady.createSignal({ status: response.status, body });
536
- }
537
- });
538
-
539
- const cns = new CNS([validator, handler]);
540
- ```
541
-
542
- ### Event Sourcing
543
- ```typescript
544
- const eventReceived = collateral<{ name: string; data: any }>('event:received');
545
- const eventStored = collateral<{ eventId: string }>('event:stored');
546
- const stateUpdated = collateral<{ aggregateId: string }>('state:updated');
547
-
548
- const eventStore = neuron('event-store', { eventStored }).dendrite({
549
- collateral: eventReceived,
550
- response: async (payload, axon) => {
551
- const eventId = await saveEvent(payload);
552
- return axon.eventStored.createSignal({ eventId });
553
- }
554
- });
555
-
556
- const stateManager = neuron('state-manager', { stateUpdated }).dendrite({
557
- collateral: eventStored,
558
- response: async (payload, axon) => {
559
- const aggregateId = await updateState(payload.eventId);
560
- return axon.stateUpdated.createSignal({ aggregateId });
561
- }
562
- });
563
-
564
- const cns = new CNS([eventStore, stateManager]);
565
- ```
566
-
567
- ## 🚨 Error Handling
568
-
569
- Errors are delivered immediately via `onResponse` callbacks:
570
-
571
- ```typescript
572
- const stimulation = cns.stimulate(signal, {
573
- onResponse: (response) => {
574
- if (response.error) {
575
- console.error(`Error in neuron processing:`, response.error);
576
-
577
- // Log error details
578
- console.error(`Signal: ${response.outputSignal?.collateral.id}`);
579
- console.error(`Stimulation: ${response.stimulationId}`);
580
-
581
- // Handle specific error types
582
- if (response.error instanceof ValidationError) {
583
- handleValidationError(response.error);
584
- }
585
- }
586
- }
587
- });
588
- await stimulation.waitUntilComplete();
589
- ```
590
-
591
- **Error Recovery with Context:**
592
- ```typescript
593
- let savedContext: ICNSStimulationContextStore | undefined;
594
-
595
- const stimulation = cns.stimulate(signal, {
596
- onResponse: (response) => {
597
- if (response.error) {
598
- // Save context for retry
599
- savedContext = response.stimulation.getContext();
600
- }
601
- }
602
- });
603
- await stimulation.waitUntilComplete();
604
-
605
- // Retry with preserved context
606
- if (savedContext) {
607
- const retryStimulation = cns.stimulate(retrySignal, {
608
- ctx: savedContext
609
- });
610
- await retryStimulation.waitUntilComplete();
611
- }
612
- ```
613
-
614
- ## 🔧 Advanced Configuration
615
-
616
- ### Custom Context Store
617
- ```typescript
618
- class RedisContextStore implements ICNSStimulationContextStore {
619
- constructor(private client: RedisClient, private sessionId: string) {}
620
-
621
- get<T>(): T | undefined {
622
- // Implement Redis-backed context retrieval
623
- }
624
-
625
- set<T>(value: T): void {
626
- // Implement Redis-backed context storage
627
- }
628
- }
629
-
630
- const stimulation = cns.stimulate(signal, {
631
- createContextStore: () => new RedisContextStore(redisClient, 'session-123')
632
- });
633
- await stimulation.waitUntilComplete();
634
- ```
635
-
636
- ### CNS Configuration
637
- ```typescript
638
- const cns = new CNS(neurons, {
639
- autoCleanupContexts: true, // Auto-cleanup unused contexts
640
- defaultConcurrency: 50 // Default concurrency limit
641
- });
642
- ```
643
-
644
- ⚠️ **Performance Warning**: `autoCleanupContexts` adds computational overhead due to:
645
- - **O(V²) initialization cost** - building SCC (Strongly Connected Components) structures
646
- - **O(1 + A) runtime cost** per cleanup check (where A = number of SCC ancestors)
647
- - **Memory overhead** for storing SCC graphs and ancestor relationships
648
-
649
- **Use only when:**
650
- - Memory leaks are a critical issue
651
- - You have a small to medium-sized neuron graph (< 1000 neurons)
652
- - Performance is less critical than memory management
53
+ ## Documentation
653
54
 
654
- **For production systems**, consider manual context cleanup or custom cleanup strategies instead.
55
+ - **[Quick Start Guide](https://cnstra.org/docs/core/quick-start)** — Get up and running in minutes
56
+ - **[API Reference](https://cnstra.org/docs/core/api)** — Complete API documentation
57
+ - **[Concepts](https://cnstra.org/docs/core/concepts)** — Neurons, collaterals, signals, and the CNS model (Central Neural Network of your app)
58
+ - **[Recipes](https://cnstra.org/docs/recipes)** — Common patterns and use cases
59
+ - **[Advanced Topics](https://cnstra.org/docs/advanced)** — Performance, context stores, integrations
655
60
 
656
61
  ---
657
62
 
658
- *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.*
63
+ *CNStra provides deterministic, type-safe orchestration without the complexity of traditional event systems.*
package/dist/CNS.d.ts CHANGED
@@ -9,17 +9,14 @@ import { TCNSStimulationResponse } from './types/TCNSStimulationResponse';
9
9
  import { TCNSNeuronActivationTask } from './types/TCNSNeuronActivationTask';
10
10
  import { CNSNetwork } from './CNSNetwork';
11
11
  import { ICNS } from './interfaces/ICNS';
12
- export declare class CNS<TCollateralName extends string, TNeuronName extends string, TNeuron extends TCNSNeuron<any, TNeuronName, any, any, any, any, any>, TDendrite extends TCNSDendrite<any, any, any, any, any, any>> implements Omit<ICNS<TCollateralName, TNeuronName, TNeuron, TDendrite>, 'stimulate'> {
12
+ import { CNSCollateral } from './CNSCollateral';
13
+ export declare class CNS<TNeuron extends TCNSNeuron<any, any>, TDendrite extends TCNSDendrite<any, any, any> = TCNSDendrite<any, any, any>> implements ICNS<TNeuron, TDendrite> {
13
14
  protected readonly neurons: TNeuron[];
14
15
  readonly options?: TCNSOptions | undefined;
15
16
  /**
16
17
  * Network graph analysis and strongly connected components.
17
18
  */
18
- readonly network: CNSNetwork<TCollateralName, TNeuronName, TNeuron, TDendrite>;
19
- /**
20
- * Global per-neuron concurrency gates shared across all stimulations.
21
- */
22
- private readonly neuronGates;
19
+ readonly network: CNSNetwork<TNeuron, TDendrite>;
23
20
  /**
24
21
  * Global task queue used by stimulation to schedule per-neuron gated work.
25
22
  * Does not replace per-stimulation concurrency; only coordinates global per-neuron limits.
@@ -30,12 +27,12 @@ export declare class CNS<TCollateralName extends string, TNeuronName extends str
30
27
  */
31
28
  private readonly globalResponseListeners;
32
29
  constructor(neurons: TNeuron[], options?: TCNSOptions | undefined);
33
- addResponseListener<TInputPayload, TOutputPayload>(listener: (response: TCNSStimulationResponse<TCollateralName, TInputPayload, TOutputPayload, TNeuronName>) => void | Promise<void>): () => void;
30
+ addResponseListener(listener: (response: TCNSStimulationResponse) => void | Promise<void>): () => void;
34
31
  private wrapOnResponse;
35
- stimulate<TInputPayload extends TOutputPayload, TOutputPayload = TInputPayload>(signalOrSignals: TCNSSignal<TCollateralName, TInputPayload> | TCNSSignal<TCollateralName, TInputPayload>[], options?: TCNSStimulationOptions<TCollateralName, TInputPayload, TOutputPayload, TNeuronName, TNeuron, TDendrite>): CNSStimulation<TCollateralName, TNeuronName, TNeuron, TDendrite, TInputPayload, TOutputPayload>;
32
+ stimulate(signalOrSignals: TCNSSignal<CNSCollateral<unknown>> | TCNSSignal<CNSCollateral<unknown>>[], options?: TCNSStimulationOptions<TCNSStimulationResponse>): CNSStimulation<TNeuron, TDendrite>;
36
33
  /**
37
34
  * Start a stimulation with activation tasks directly
38
35
  */
39
- activate<TInputPayload, TOutputPayload>(tasks: TCNSNeuronActivationTask<TCollateralName, TNeuronName>[], options?: TCNSStimulationOptions<TCollateralName, TInputPayload, TOutputPayload, TNeuronName, TNeuron, TDendrite>): CNSStimulation<TCollateralName, TNeuronName, TNeuron, TDendrite, TInputPayload, TOutputPayload>;
36
+ activate(tasks: TCNSNeuronActivationTask<TNeuron>[], options?: TCNSStimulationOptions<TCNSStimulationResponse>): CNSStimulation<TNeuron, TDendrite>;
40
37
  }
41
38
  //# sourceMappingURL=CNS.d.ts.map