wicked-bus 1.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.
@@ -0,0 +1,164 @@
1
+ ---
2
+ description: Subscribe to wicked-bus events. Use when consuming events from the bus, setting up event listeners, polling for new events, or integrating as a subscriber. Covers registration, polling, acknowledgment, and filter patterns.
3
+ ---
4
+
5
+ # wicked-bus:subscribe
6
+
7
+ Guide for consuming events from the wicked-bus.
8
+
9
+ ## When to use
10
+
11
+ - User wants to listen for events
12
+ - User asks "how do I subscribe to the bus" or "how do I consume events"
13
+ - Setting up a new subscriber integration
14
+ - User needs help with filter patterns
15
+
16
+ ## Prerequisites
17
+
18
+ Check that wicked-bus is initialized. If not, trigger `wicked-bus-init`.
19
+
20
+ ## CLI Usage (quickest way to start)
21
+
22
+ ### Subscribe to events (streaming)
23
+
24
+ ```bash
25
+ # All run events from any source
26
+ npx wicked-bus subscribe --filter 'wicked.run.*'
27
+
28
+ # Only from a specific domain
29
+ npx wicked-bus subscribe --filter 'wicked.run.*@wicked-testing'
30
+
31
+ # Everything from a domain
32
+ npx wicked-bus subscribe --filter '*@wicked-brain'
33
+
34
+ # Without auto-acknowledgment
35
+ npx wicked-bus subscribe --filter 'wicked.phase.*' --no-ack
36
+ ```
37
+
38
+ Output is NDJSON — one JSON object per line per event.
39
+
40
+ ### Register then poll manually
41
+
42
+ ```bash
43
+ # Register as subscriber
44
+ npx wicked-bus register \
45
+ --role subscriber \
46
+ --plugin my-consumer \
47
+ --filter 'wicked.run.*' \
48
+ --cursor-init latest
49
+
50
+ # Poll (uses cursor from registration)
51
+ npx wicked-bus subscribe --plugin my-consumer --filter 'wicked.run.*'
52
+
53
+ # Manual ack
54
+ npx wicked-bus ack --cursor-id {cursor_id} --event-id {event_id}
55
+ ```
56
+
57
+ ## Programmatic Usage (Node.js)
58
+
59
+ ### Basic poll loop
60
+
61
+ ```javascript
62
+ import { poll, ack } from 'wicked-bus';
63
+ import { loadConfig } from 'wicked-bus/lib/config.js';
64
+ import { openDb } from 'wicked-bus/lib/db.js';
65
+ import { register } from 'wicked-bus/lib/register.js';
66
+
67
+ const config = loadConfig();
68
+ const db = openDb(config);
69
+
70
+ // Register (idempotent — safe to call on every startup)
71
+ const sub = register(db, {
72
+ plugin: 'my-consumer',
73
+ role: 'subscriber',
74
+ event_type_filter: 'wicked.run.*',
75
+ cursor_init: 'latest',
76
+ });
77
+
78
+ // Poll for new events
79
+ const events = poll(db, config, {
80
+ cursor_id: sub.cursor_id,
81
+ filter: 'wicked.run.*',
82
+ batch_size: 10,
83
+ });
84
+
85
+ for (const event of events.events) {
86
+ console.log(event.event_type, event.payload);
87
+ // Process the event...
88
+ }
89
+
90
+ // Acknowledge (advances the cursor)
91
+ if (events.events.length > 0) {
92
+ const lastId = events.events[events.events.length - 1].event_id;
93
+ ack(db, { cursor_id: sub.cursor_id, event_id: lastId });
94
+ }
95
+
96
+ db.close();
97
+ ```
98
+
99
+ ### Fire-and-forget subscriber (for integrations)
100
+
101
+ Plugins that react to bus events but should never block:
102
+
103
+ ```javascript
104
+ async function checkBusEvents() {
105
+ try {
106
+ const { poll, ack } = await import('wicked-bus');
107
+ const { loadConfig } = await import('wicked-bus/lib/config.js');
108
+ const { openDb } = await import('wicked-bus/lib/db.js');
109
+
110
+ const config = loadConfig();
111
+ const db = openDb(config);
112
+
113
+ const result = poll(db, config, {
114
+ cursor_id: myCursorId,
115
+ filter: 'wicked.phase.*@wicked-garden',
116
+ batch_size: 50,
117
+ });
118
+
119
+ for (const event of result.events) {
120
+ // React to the event (non-blocking)
121
+ }
122
+
123
+ if (result.events.length > 0) {
124
+ const lastId = result.events[result.events.length - 1].event_id;
125
+ ack(db, { cursor_id: myCursorId, event_id: lastId });
126
+ }
127
+
128
+ db.close();
129
+ } catch (_) {
130
+ // Bus unavailable — degrade gracefully
131
+ }
132
+ }
133
+ ```
134
+
135
+ ## Filter Patterns
136
+
137
+ | Pattern | Matches |
138
+ |---------|---------|
139
+ | `wicked.run.completed` | Exact match only |
140
+ | `wicked.run.*` | All `wicked.run.` events (single-level wildcard) |
141
+ | `*@wicked-brain` | All events from the `wicked-brain` domain |
142
+ | `wicked.memory.*@wicked-brain` | Memory events from brain only |
143
+
144
+ ### Filter rules
145
+
146
+ 1. `*` matches exactly one segment (single-level wildcard)
147
+ 2. `@domain` suffix scopes by the `domain` column
148
+ 3. Wildcards and `@domain` can combine: `wicked.run.*@my-plugin`
149
+ 4. `*` alone (catch-all) is valid but noisy
150
+
151
+ ## Delivery Semantics
152
+
153
+ - **At-least-once**: unacked events are re-delivered on next poll
154
+ - **Cursor-based**: each subscriber has its own cursor position
155
+ - **Batch polling**: use `batch_size` to control how many events per poll
156
+ - **Visibility filter**: events past `expires_at` are invisible to poll
157
+ - **WB-003 warning**: if your cursor is behind the oldest event, you may have missed events
158
+
159
+ ## Error Handling
160
+
161
+ | Error | Code | Meaning |
162
+ |-------|------|---------|
163
+ | WB-003 | CURSOR_BEHIND | Cursor is behind oldest available event — events may have been missed |
164
+ | WB-006 | CURSOR_NOT_FOUND | Cursor ID doesn't exist or was deregistered |