@auto-engineer/message-bus 1.148.0 → 1.150.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.
@@ -1,5 +1,5 @@
1
1
 
2
- > @auto-engineer/message-bus@1.148.0 build /home/runner/work/auto-engineer/auto-engineer/packages/message-bus
2
+ > @auto-engineer/message-bus@1.150.0 build /home/runner/work/auto-engineer/auto-engineer/packages/message-bus
3
3
  > tsc && tsx ../../scripts/fix-esm-imports.ts
4
4
 
5
5
  Fixed ESM imports in dist/
@@ -1,5 +1,5 @@
1
1
 
2
- > @auto-engineer/message-bus@1.147.0 test /home/runner/work/auto-engineer/auto-engineer/packages/message-bus
2
+ > @auto-engineer/message-bus@1.149.0 test /home/runner/work/auto-engineer/auto-engineer/packages/message-bus
3
3
  > vitest run --reporter=dot
4
4
 
5
5
 
@@ -9,6 +9,6 @@
9
9
 
10
10
   Test Files  1 passed (1)
11
11
   Tests  7 passed (7)
12
-  Start at  21:35:27
13
-  Duration  1.24s (transform 339ms, setup 0ms, collect 438ms, tests 32ms, environment 0ms, prepare 285ms)
12
+  Start at  12:20:32
13
+  Duration  2.18s (transform 452ms, setup 0ms, collect 521ms, tests 55ms, environment 0ms, prepare 638ms)
14
14
 
@@ -1,4 +1,4 @@
1
1
 
2
- > @auto-engineer/message-bus@1.147.0 type-check /home/runner/work/auto-engineer/auto-engineer/packages/message-bus
2
+ > @auto-engineer/message-bus@1.149.0 type-check /home/runner/work/auto-engineer/auto-engineer/packages/message-bus
3
3
  > tsc --noEmit
4
4
 
package/CHANGELOG.md CHANGED
@@ -1,5 +1,38 @@
1
1
  # @auto-engineer/message-bus
2
2
 
3
+ ## 1.150.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`a502d47`](https://github.com/BeOnAuto/auto-engineer/commit/a502d472df528a1c5d53905898220c2a3a49d8a5) Thanks [@osamanar](https://github.com/osamanar)! -
8
+
9
+ - [`c96a70e`](https://github.com/BeOnAuto/auto-engineer/commit/c96a70e49f9f64213450d0b9840bc9a8b2b1d77d) Thanks [@osamanar](https://github.com/osamanar)! -
10
+
11
+ - [`4343bca`](https://github.com/BeOnAuto/auto-engineer/commit/4343bcaa2576703ae578fd9b7f5ec5b9776702a9) Thanks [@osamanar](https://github.com/osamanar)! -
12
+
13
+ - [`28ffce9`](https://github.com/BeOnAuto/auto-engineer/commit/28ffce9c8b95f0c3ca61728bd1f667fa9416d461) Thanks [@osamanar](https://github.com/osamanar)! -
14
+
15
+ - [`3fd5dbf`](https://github.com/BeOnAuto/auto-engineer/commit/3fd5dbfbfb4f7a5fe71ae53105b31a1b0f30f911) Thanks [@osamanar](https://github.com/osamanar)! -
16
+
17
+ ### Patch Changes
18
+
19
+ - [`41f3df3`](https://github.com/BeOnAuto/auto-engineer/commit/41f3df3025445ba92208c2b007b8e29a40489309) Thanks [@github-actions[bot]](https://github.com/github-actions%5Bbot%5D)! - - **global**: version packages
20
+ - **global**: major refresh to all the docs
21
+
22
+ ## 1.149.0
23
+
24
+ ### Minor Changes
25
+
26
+ - [`e1eebbd`](https://github.com/BeOnAuto/auto-engineer/commit/e1eebbdf4f209780e790094d2e6887c4fa809f98) Thanks [@github-actions[bot]](https://github.com/github-actions%5Bbot%5D)! - - **server-generator-apollo-emmett**: add Given state ref hints to state.ts.ejs
27
+ - **server-generator-apollo-emmett**: context-aware nonCommandField instructions
28
+ - **server-generator-apollo-emmett**: add state context instruction
29
+ - **server-generator-apollo-emmett**: extract shared template helpers
30
+ - **server-generator-apollo-emmett**: filter state refs from hasGivenEvents in decide.ts.ejs
31
+
32
+ ### Patch Changes
33
+
34
+ - [`d38c81e`](https://github.com/BeOnAuto/auto-engineer/commit/d38c81e7bb442a39626564cf4f6d8d55b60d0a38) Thanks [@SamHatoum](https://github.com/SamHatoum)! -
35
+
3
36
  ## 1.148.0
4
37
 
5
38
  ### Minor Changes
package/README.md CHANGED
@@ -8,7 +8,15 @@ Type-safe message bus implementing the CQRS pattern with command handling and ev
8
8
 
9
9
  Without `@auto-engineer/message-bus`, you would have to implement your own command/event routing, handle handler registration, manage request/correlation ID propagation, and ensure proper error isolation between handlers.
10
10
 
11
- This package provides the core messaging infrastructure for the Auto Engineer ecosystem. It enables decoupled communication through commands (write operations) and events (state changes) with robust debugging support.
11
+ This package provides the core messaging infrastructure for the Auto Engineer ecosystem. It enables decoupled communication through commands (write operations) and events (state changes). Commands have exactly one handler; events fan out to many subscribers. Correlation IDs flow automatically from commands to their resulting events, enabling end-to-end traceability.
12
+
13
+ ## Key Concepts
14
+
15
+ - **One handler per command type** -- ensures deterministic command processing. Registering a second handler for the same command throws.
16
+ - **Multiple handlers per event type** -- enables fan-out notification. Handlers run concurrently via `Promise.allSettled`, so one failure does not block others.
17
+ - **Request/Correlation ID propagation** -- when a command handler returns events, the bus copies `requestId` and `correlationId` from the command onto each event (unless the event already carries its own).
18
+ - **Correlation listeners** -- subscribe to events by exact `correlationId` or by prefix, useful for tracking the progress of a specific workflow or job graph.
19
+ - **`defineCommandHandler`** -- a factory that attaches CLI metadata (alias, description, fields, examples) to a command handler, so the same object can drive both the message bus and a CLI interface.
12
20
 
13
21
  ---
14
22
 
@@ -22,17 +30,23 @@ pnpm add @auto-engineer/message-bus
22
30
 
23
31
  ```typescript
24
32
  import { createMessageBus, defineCommandHandler } from '@auto-engineer/message-bus';
33
+ import type { Command, Event } from '@auto-engineer/message-bus';
25
34
 
35
+ // 1. Create the bus
26
36
  const bus = createMessageBus();
27
37
 
28
- const handler = defineCommandHandler({
38
+ // 2. Define and register a command handler
39
+ type CreateUser = Command<'CreateUser', { name: string }>;
40
+ type UserCreated = Event<'UserCreated', { userId: string; name: string }>;
41
+
42
+ const handler = defineCommandHandler<CreateUser, (cmd: CreateUser) => Promise<UserCreated>>({
29
43
  name: 'CreateUser',
30
44
  alias: 'create-user',
31
45
  description: 'Creates a new user',
32
46
  fields: {
33
- name: { description: 'User name', required: true },
47
+ name: { description: 'User name' },
34
48
  },
35
- examples: [],
49
+ examples: ['create-user --name John'],
36
50
  events: ['UserCreated'],
37
51
  handle: async (cmd) => ({
38
52
  type: 'UserCreated',
@@ -42,13 +56,20 @@ const handler = defineCommandHandler({
42
56
 
43
57
  bus.registerCommandHandler(handler);
44
58
 
45
- const result = await bus.sendCommand({
59
+ // 3. Subscribe to the resulting event
60
+ bus.subscribeToEvent('UserCreated', {
61
+ name: 'UserCreatedLogger',
62
+ handle: (event) => {
63
+ console.log('User created:', event.data);
64
+ },
65
+ });
66
+
67
+ // 4. Send a command
68
+ await bus.sendCommand({
46
69
  type: 'CreateUser',
47
70
  data: { name: 'John' },
71
+ requestId: 'req-001',
48
72
  });
49
-
50
- console.log(result);
51
- // → { type: 'UserCreated', data: { userId: 'u-001', name: 'John' } }
52
73
  ```
53
74
 
54
75
  ---
@@ -57,6 +78,8 @@ console.log(result);
57
78
 
58
79
  ### Register a Command Handler
59
80
 
81
+ Use `defineCommandHandler` to create a handler with CLI metadata, then register it on the bus:
82
+
60
83
  ```typescript
61
84
  import { createMessageBus, defineCommandHandler } from '@auto-engineer/message-bus';
62
85
 
@@ -76,11 +99,14 @@ bus.registerCommandHandler(handler);
76
99
 
77
100
  ### Send a Command
78
101
 
102
+ `sendCommand` finds the registered handler, executes it, and publishes the resulting events:
103
+
79
104
  ```typescript
80
- const result = await bus.sendCommand({
105
+ await bus.sendCommand({
81
106
  type: 'CreateUser',
82
107
  data: { name: 'John', email: 'john@example.com' },
83
108
  requestId: 'req-123',
109
+ correlationId: 'signup-flow-1',
84
110
  });
85
111
  ```
86
112
 
@@ -94,6 +120,7 @@ const subscription = bus.subscribeToEvent('UserCreated', {
94
120
  },
95
121
  });
96
122
 
123
+ // Later, stop receiving events
97
124
  subscription.unsubscribe();
98
125
  ```
99
126
 
@@ -108,21 +135,51 @@ const subscription = bus.subscribeAll({
108
135
  });
109
136
  ```
110
137
 
111
- ### Return Multiple Events
138
+ ### Return Multiple Events from a Handler
139
+
140
+ A command handler can return a single event or an array:
112
141
 
113
142
  ```typescript
114
143
  const handler = defineCommandHandler({
115
144
  name: 'BatchCreate',
116
- // ...
145
+ alias: 'batch-create',
146
+ description: 'Creates multiple items',
147
+ fields: {},
148
+ examples: [],
149
+ events: ['ItemCreated'],
117
150
  handle: async (cmd) => {
118
- return cmd.data.items.map(item => ({
119
- type: 'ItemCreated',
151
+ return cmd.data.items.map((item: { id: string }) => ({
152
+ type: 'ItemCreated' as const,
120
153
  data: item,
121
154
  }));
122
155
  },
123
156
  });
124
157
  ```
125
158
 
159
+ ### Track Events by Correlation ID
160
+
161
+ Listen for events tied to a specific correlation ID:
162
+
163
+ ```typescript
164
+ const subscription = bus.onCorrelation('signup-flow-1', (event) => {
165
+ console.log('Correlated event:', event.type);
166
+ });
167
+
168
+ subscription.unsubscribe();
169
+ ```
170
+
171
+ ### Track Events by Correlation Prefix
172
+
173
+ Listen for all events whose correlation ID starts with a given prefix. Useful for job graphs where each job appends a suffix:
174
+
175
+ ```typescript
176
+ const subscription = bus.onCorrelationPrefix('graph:g1:', (event) => {
177
+ console.log('Graph event:', event.type, event.correlationId);
178
+ });
179
+
180
+ subscription.unsubscribe();
181
+ ```
182
+
126
183
  ---
127
184
 
128
185
  ## API Reference
@@ -141,26 +198,67 @@ import type {
141
198
  CommandHandler,
142
199
  EventHandler,
143
200
  EventSubscription,
201
+ EventDefinition,
144
202
  MessageBus,
145
203
  UnifiedCommandHandler,
146
204
  FieldDefinition,
205
+ PackageMetadata,
147
206
  } from '@auto-engineer/message-bus';
148
207
  ```
149
208
 
150
209
  ### Functions
151
210
 
152
- #### `createMessageBus(): MessageBus`
211
+ #### `createMessageBus()`
212
+
213
+ ```typescript
214
+ function createMessageBus(): MessageBus
215
+ ```
216
+
217
+ Create a new message bus instance with isolated state.
218
+
219
+ #### `defineCommandHandler(config)`
153
220
 
154
- Create a new message bus instance.
221
+ ```typescript
222
+ function defineCommandHandler<C extends Command>(config: {
223
+ name: string;
224
+ alias: string;
225
+ description: string;
226
+ displayName?: string;
227
+ category?: string;
228
+ icon?: string;
229
+ package?: PackageMetadata;
230
+ fields: Record<string, FieldDefinition<unknown>>;
231
+ examples: string[];
232
+ events: EventDefinition[];
233
+ handle: (command: C) => Promise<Event | Event[]>;
234
+ }): UnifiedCommandHandler<C>
235
+ ```
155
236
 
156
- #### `defineCommandHandler(options): UnifiedCommandHandler`
237
+ Define a command handler with metadata for CLI integration.
157
238
 
158
- Define a command handler with CLI metadata.
239
+ | Parameter | Type | Description |
240
+ |---|---|---|
241
+ | `name` | `string` | Command type string (e.g. `'CreateUser'`) |
242
+ | `alias` | `string` | CLI alias (e.g. `'create-user'`) |
243
+ | `description` | `string` | Human-readable description |
244
+ | `displayName` | `string?` | Optional display name |
245
+ | `category` | `string?` | Optional grouping category |
246
+ | `icon` | `string?` | Optional icon |
247
+ | `package` | `PackageMetadata?` | Source package metadata |
248
+ | `fields` | `Record<string, FieldDefinition>` | Parameter definitions |
249
+ | `examples` | `string[]` | Usage examples |
250
+ | `events` | `EventDefinition[]` | Events this handler may emit |
251
+ | `handle` | `(command) => Promise<Event \| Event[]>` | The handler function |
159
252
 
160
- ### Command Type
253
+ ### Interfaces
254
+
255
+ #### `Command<Type, Data>`
161
256
 
162
257
  ```typescript
163
- type Command<Type extends string, Data> = Readonly<{
258
+ type Command<
259
+ Type extends string = string,
260
+ Data extends Record<string, unknown> = Record<string, unknown>
261
+ > = Readonly<{
164
262
  type: Type;
165
263
  data: Readonly<Data>;
166
264
  timestamp?: Date;
@@ -169,10 +267,13 @@ type Command<Type extends string, Data> = Readonly<{
169
267
  }>;
170
268
  ```
171
269
 
172
- ### Event Type
270
+ #### `Event<Type, Data>`
173
271
 
174
272
  ```typescript
175
- type Event<Type extends string, Data> = Readonly<{
273
+ type Event<
274
+ Type extends string = string,
275
+ Data extends Record<string, unknown> = Record<string, unknown>
276
+ > = Readonly<{
176
277
  type: Type;
177
278
  data: Data;
178
279
  timestamp?: Date;
@@ -181,51 +282,73 @@ type Event<Type extends string, Data> = Readonly<{
181
282
  }>;
182
283
  ```
183
284
 
184
- ### MessageBus Interface
285
+ #### `CommandHandler`
185
286
 
186
287
  ```typescript
187
- interface MessageBus {
188
- registerCommandHandler(handler: CommandHandler): void;
189
- registerEventHandler(eventType: string, handler: EventHandler): void;
190
- sendCommand(command: Command): Promise<Event | Event[]>;
191
- publishEvent(event: Event): Promise<void>;
192
- subscribeToEvent(eventType: string, handler: EventHandler): EventSubscription;
193
- subscribeAll(handler: EventHandler): EventSubscription;
194
- getCommandHandlers(): Record<string, CommandHandler>;
195
- }
288
+ type CommandHandler<TCommand extends Command = Command, TEvent extends Event = Event> = {
289
+ name: string;
290
+ handle: (command: TCommand) => Promise<TEvent | TEvent[]>;
291
+ };
196
292
  ```
197
293
 
294
+ #### `EventHandler`
295
+
296
+ ```typescript
297
+ type EventHandler<TEvent extends Event = Event> = {
298
+ name: string;
299
+ handle: (event: TEvent) => Promise<void> | void;
300
+ };
301
+ ```
302
+
303
+ #### `EventSubscription`
304
+
305
+ ```typescript
306
+ type EventSubscription = {
307
+ unsubscribe: () => void;
308
+ };
309
+ ```
310
+
311
+ ### MessageBus Methods
312
+
313
+ | Method | Signature | Description |
314
+ |---|---|---|
315
+ | `registerCommandHandler` | `(handler: CommandHandler) => void` | Register a handler for a command type. Throws if one is already registered. |
316
+ | `sendCommand` | `(command: Command) => Promise<void>` | Dispatch a command to its handler. Resulting events are published automatically. |
317
+ | `publishEvent` | `(event: Event) => Promise<void>` | Publish an event to all matching subscribers. |
318
+ | `subscribeToEvent` | `(eventType: string, handler: EventHandler) => EventSubscription` | Subscribe to a specific event type. |
319
+ | `subscribeAll` | `(handler: EventHandler) => EventSubscription` | Subscribe to all events regardless of type. |
320
+ | `registerEventHandler` | `(handler: EventHandler) => EventSubscription` | Register an event handler, inferring event type from the handler name (strips `Handler` suffix). |
321
+ | `getCommandHandlers` | `() => Record<string, CommandHandler>` | Return a shallow copy of all registered command handlers. |
322
+ | `onCorrelation` | `(correlationId: string, listener: (event: Event) => void) => EventSubscription` | Listen for events with an exact correlation ID match. |
323
+ | `onCorrelationPrefix` | `(prefix: string, listener: (event: Event) => void) => EventSubscription` | Listen for events whose correlation ID starts with the given prefix. |
324
+
198
325
  ---
199
326
 
200
327
  ## Architecture
201
328
 
202
329
  ```
203
330
  src/
204
- ├── index.ts
205
- ├── message-bus.ts
206
- ├── define-command.ts
207
- └── types.ts
331
+ ├── index.ts # Re-exports all public API
332
+ ├── message-bus.ts # createMessageBus factory and MessageBus type
333
+ ├── define-command.ts # defineCommandHandler and related types
334
+ ├── types.ts # Core Command, Event, Handler types
335
+ └── message-bus.specs.ts # Tests for correlation features
208
336
  ```
209
337
 
210
- The following diagram shows the message flow:
211
-
212
338
  ```mermaid
213
339
  flowchart LR
214
- A[sendCommand] --> B[CommandHandler]
215
- B --> C[Event]
340
+ A[sendCommand] --> B[CommandHandler.handle]
341
+ B --> C[Event / Event array]
216
342
  C --> D[publishEvent]
217
- D --> E[EventHandlers]
343
+ D --> E[Event subscribers]
344
+ D --> F[Correlation listeners]
218
345
  ```
219
346
 
220
- *Flow: Command is sent, handler processes it, returns event(s), events are published to subscribers.*
221
-
222
- ### Key Concepts
223
-
224
- - **One handler per command type**: Ensures deterministic command processing
225
- - **Multiple handlers per event type**: Enables fan-out notification
226
- - **Request/Correlation ID propagation**: Maintains traceability
227
- - **Error isolation**: Handler failures don't affect other handlers
228
-
229
347
  ### Dependencies
230
348
 
231
- This package has no dependencies on other `@auto-engineer/*` packages. It is a foundational package used throughout the monorepo.
349
+ | Type | Packages |
350
+ |---|---|
351
+ | Runtime | `debug` (via `createDebug`) |
352
+ | Dev | `typescript`, `tsx`, `@types/node` |
353
+
354
+ This package has no dependencies on other `@auto-engineer/` packages. It is a foundational package used throughout the monorepo.
package/package.json CHANGED
@@ -17,7 +17,7 @@
17
17
  "tsx": "^4.20.3",
18
18
  "typescript": "^5.0.0"
19
19
  },
20
- "version": "1.148.0",
20
+ "version": "1.150.0",
21
21
  "scripts": {
22
22
  "build": "tsc && tsx ../../scripts/fix-esm-imports.ts",
23
23
  "clean": "rm -rf dist",