web-agent-bridge 3.2.0 → 3.3.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.
- package/LICENSE +72 -72
- package/README.ar.md +1286 -1152
- package/README.md +1764 -1635
- package/bin/agent-runner.js +474 -474
- package/bin/cli.js +237 -138
- package/bin/wab.js +80 -80
- package/examples/bidi-agent.js +119 -119
- package/examples/cross-site-agent.js +91 -91
- package/examples/mcp-agent.js +94 -94
- package/examples/next-app-router/README.md +44 -44
- package/examples/puppeteer-agent.js +108 -108
- package/examples/saas-dashboard/README.md +55 -55
- package/examples/shopify-hydrogen/README.md +74 -74
- package/examples/vision-agent.js +171 -171
- package/examples/wordpress-elementor/README.md +77 -77
- package/package.json +16 -3
- package/public/.well-known/agent-tools.json +180 -180
- package/public/.well-known/ai-assets.json +59 -59
- package/public/.well-known/security.txt +8 -0
- package/public/agent-workspace.html +349 -349
- package/public/ai.html +198 -198
- package/public/api.html +413 -412
- package/public/browser.html +486 -486
- package/public/commander-dashboard.html +243 -243
- package/public/cookies.html +210 -210
- package/public/css/agent-workspace.css +1713 -1713
- package/public/css/premium.css +317 -317
- package/public/css/styles.css +1235 -1235
- package/public/dashboard.html +706 -706
- package/public/dns.html +507 -0
- package/public/docs.html +587 -587
- package/public/feed.xml +89 -89
- package/public/growth.html +463 -463
- package/public/index.html +1070 -982
- package/public/integrations.html +556 -0
- package/public/js/agent-workspace.js +1740 -1740
- package/public/js/auth-nav.js +31 -31
- package/public/js/auth-redirect.js +12 -12
- package/public/js/cookie-consent.js +56 -56
- package/public/js/wab-demo-page.js +721 -721
- package/public/js/ws-client.js +74 -74
- package/public/llms-full.txt +360 -360
- package/public/llms.txt +125 -125
- package/public/login.html +85 -85
- package/public/mesh-dashboard.html +328 -328
- package/public/openapi.json +580 -580
- package/public/phone-shield.html +281 -0
- package/public/premium-dashboard.html +2489 -2489
- package/public/premium.html +793 -793
- package/public/privacy.html +297 -297
- package/public/register.html +105 -105
- package/public/robots.txt +87 -87
- package/public/script/wab-consent.d.ts +36 -36
- package/public/script/wab-consent.js +104 -104
- package/public/script/wab-schema.js +131 -131
- package/public/script/wab.d.ts +108 -108
- package/public/script/wab.min.js +580 -580
- package/public/security.txt +8 -0
- package/public/terms.html +256 -256
- package/script/ai-agent-bridge.js +1754 -1754
- package/sdk/README.md +99 -99
- package/sdk/agent-mesh.js +449 -449
- package/sdk/commander.js +262 -262
- package/sdk/index.d.ts +464 -464
- package/sdk/index.js +12 -1
- package/sdk/multi-agent.js +318 -318
- package/sdk/package.json +1 -1
- package/sdk/safety-shield.js +219 -0
- package/sdk/schema-discovery.js +83 -83
- package/server/adapters/index.js +520 -520
- package/server/config/plans.js +367 -367
- package/server/config/secrets.js +102 -102
- package/server/control-plane/index.js +301 -301
- package/server/data-plane/index.js +354 -354
- package/server/index.js +531 -427
- package/server/llm/index.js +404 -404
- package/server/middleware/adminAuth.js +35 -35
- package/server/middleware/auth.js +50 -50
- package/server/middleware/featureGate.js +88 -88
- package/server/middleware/rateLimits.js +100 -100
- package/server/middleware/sensitiveAction.js +157 -0
- package/server/migrations/001_add_analytics_indexes.sql +7 -7
- package/server/migrations/002_premium_features.sql +418 -418
- package/server/migrations/003_ads_integer_cents.sql +33 -33
- package/server/migrations/004_agent_os.sql +158 -158
- package/server/migrations/005_marketplace_metering.sql +126 -126
- package/server/models/adapters/index.js +33 -33
- package/server/models/adapters/mysql.js +183 -183
- package/server/models/adapters/postgresql.js +172 -172
- package/server/models/adapters/sqlite.js +7 -7
- package/server/models/db.js +681 -681
- package/server/observability/failure-analysis.js +337 -337
- package/server/observability/index.js +394 -394
- package/server/protocol/capabilities.js +223 -223
- package/server/protocol/index.js +243 -243
- package/server/protocol/schema.js +584 -584
- package/server/registry/certification.js +271 -271
- package/server/registry/index.js +326 -326
- package/server/routes/admin-premium.js +671 -671
- package/server/routes/admin.js +261 -261
- package/server/routes/ads.js +130 -130
- package/server/routes/agent-workspace.js +540 -540
- package/server/routes/api.js +150 -150
- package/server/routes/auth.js +71 -71
- package/server/routes/billing.js +45 -45
- package/server/routes/commander.js +316 -316
- package/server/routes/demo-showcase.js +332 -332
- package/server/routes/demo-store.js +154 -0
- package/server/routes/discovery.js +417 -417
- package/server/routes/gateway.js +173 -157
- package/server/routes/license.js +251 -240
- package/server/routes/mesh.js +469 -469
- package/server/routes/noscript.js +543 -543
- package/server/routes/premium-v2.js +686 -686
- package/server/routes/premium.js +724 -724
- package/server/routes/runtime.js +2148 -2147
- package/server/routes/sovereign.js +465 -385
- package/server/routes/universal.js +200 -185
- package/server/routes/wab-api.js +850 -501
- package/server/runtime/container-worker.js +111 -111
- package/server/runtime/container.js +448 -448
- package/server/runtime/distributed-worker.js +362 -362
- package/server/runtime/event-bus.js +210 -210
- package/server/runtime/index.js +253 -253
- package/server/runtime/queue.js +599 -599
- package/server/runtime/replay.js +666 -666
- package/server/runtime/sandbox.js +266 -266
- package/server/runtime/scheduler.js +534 -534
- package/server/runtime/session-engine.js +293 -293
- package/server/runtime/state-manager.js +188 -188
- package/server/security/cross-site-redactor.js +196 -0
- package/server/security/dry-run.js +180 -0
- package/server/security/human-gate-rate-limit.js +147 -0
- package/server/security/human-gate-transports.js +178 -0
- package/server/security/human-gate.js +281 -0
- package/server/security/index.js +368 -368
- package/server/security/intent-engine.js +245 -0
- package/server/security/reward-guard.js +171 -0
- package/server/security/rollback-store.js +239 -0
- package/server/security/token-scope.js +404 -0
- package/server/security/url-policy.js +139 -0
- package/server/services/agent-chat.js +506 -506
- package/server/services/agent-learning.js +601 -575
- package/server/services/agent-memory.js +625 -625
- package/server/services/agent-mesh.js +555 -539
- package/server/services/agent-symphony.js +717 -717
- package/server/services/agent-tasks.js +1807 -1807
- package/server/services/api-key-engine.js +292 -261
- package/server/services/cluster.js +894 -894
- package/server/services/commander.js +738 -738
- package/server/services/edge-compute.js +440 -440
- package/server/services/email.js +204 -204
- package/server/services/hosted-runtime.js +205 -205
- package/server/services/lfd.js +635 -635
- package/server/services/local-ai.js +389 -389
- package/server/services/marketplace.js +270 -270
- package/server/services/metering.js +182 -182
- package/server/services/modules/affiliate-intelligence.js +93 -93
- package/server/services/modules/agent-firewall.js +90 -90
- package/server/services/modules/bounty.js +89 -89
- package/server/services/modules/collective-bargaining.js +92 -92
- package/server/services/modules/dark-pattern.js +66 -66
- package/server/services/modules/gov-intelligence.js +45 -45
- package/server/services/modules/neural.js +55 -55
- package/server/services/modules/notary.js +49 -49
- package/server/services/modules/price-time-machine.js +86 -86
- package/server/services/modules/protocol.js +104 -104
- package/server/services/negotiation.js +439 -439
- package/server/services/plugins.js +771 -771
- package/server/services/price-intelligence.js +566 -566
- package/server/services/price-shield.js +1137 -1137
- package/server/services/reputation.js +465 -465
- package/server/services/search-engine.js +357 -357
- package/server/services/security.js +513 -513
- package/server/services/self-healing.js +843 -843
- package/server/services/sovereign-shield.js +542 -0
- package/server/services/stripe.js +192 -192
- package/server/services/swarm.js +788 -788
- package/server/services/universal-scraper.js +662 -661
- package/server/services/verification.js +481 -481
- package/server/services/vision.js +1163 -1163
- package/server/utils/cache.js +125 -125
- package/server/utils/migrate.js +81 -81
- package/server/utils/safe-fetch.js +228 -0
- package/server/utils/secureFields.js +50 -50
- package/server/ws.js +161 -161
- package/templates/artisan-marketplace.yaml +104 -104
- package/templates/book-price-scout.yaml +98 -98
- package/templates/electronics-price-tracker.yaml +108 -108
- package/templates/flight-deal-hunter.yaml +113 -113
- package/templates/freelancer-direct.yaml +116 -116
- package/templates/grocery-price-compare.yaml +93 -93
- package/templates/hotel-direct-booking.yaml +113 -113
- package/templates/local-services.yaml +98 -98
- package/templates/olive-oil-tunisia.yaml +88 -88
- package/templates/organic-farm-fresh.yaml +101 -101
- package/templates/restaurant-direct.yaml +97 -97
- package/public/score.html +0 -263
- package/server/migrations/006_growth_suite.sql +0 -138
- package/server/routes/growth.js +0 -962
- package/server/services/fairness-engine.js +0 -409
- package/server/services/fairness.js +0 -420
|
@@ -1,210 +1,210 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* WAB Runtime - Event Bus
|
|
5
|
-
*
|
|
6
|
-
* Async event system with typed events, middleware, replay buffer,
|
|
7
|
-
* and dead-letter queue. This is the nervous system of the Agent OS.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
const crypto = require('crypto');
|
|
11
|
-
|
|
12
|
-
class EventBus {
|
|
13
|
-
constructor(options = {}) {
|
|
14
|
-
this._listeners = new Map(); // event → Set<{ id, handler, filter, once }>
|
|
15
|
-
this._middleware = []; // global middleware
|
|
16
|
-
this._history = []; // event replay buffer
|
|
17
|
-
this._deadLetter = []; // failed events
|
|
18
|
-
this._maxHistory = options.maxHistory || 10000;
|
|
19
|
-
this._maxDeadLetter = options.maxDeadLetter || 1000;
|
|
20
|
-
this._stats = { emitted: 0, delivered: 0, failed: 0, dropped: 0 };
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Subscribe to an event
|
|
25
|
-
* @returns {string} subscription ID for unsubscribe
|
|
26
|
-
*/
|
|
27
|
-
on(event, handler, options = {}) {
|
|
28
|
-
if (!this._listeners.has(event)) this._listeners.set(event, new Set());
|
|
29
|
-
const sub = {
|
|
30
|
-
id: `sub_${crypto.randomBytes(8).toString('hex')}`,
|
|
31
|
-
handler,
|
|
32
|
-
filter: options.filter || null,
|
|
33
|
-
once: options.once || false,
|
|
34
|
-
priority: options.priority || 0,
|
|
35
|
-
};
|
|
36
|
-
this._listeners.get(event).add(sub);
|
|
37
|
-
return sub.id;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Subscribe once
|
|
42
|
-
*/
|
|
43
|
-
once(event, handler, options = {}) {
|
|
44
|
-
return this.on(event, handler, { ...options, once: true });
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Unsubscribe by subscription ID
|
|
49
|
-
*/
|
|
50
|
-
off(subId) {
|
|
51
|
-
for (const [, subs] of this._listeners) {
|
|
52
|
-
for (const sub of subs) {
|
|
53
|
-
if (sub.id === subId) { subs.delete(sub); return true; }
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
return false;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Remove all listeners for an event
|
|
61
|
-
*/
|
|
62
|
-
removeAll(event) {
|
|
63
|
-
if (event) this._listeners.delete(event);
|
|
64
|
-
else this._listeners.clear();
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Emit an event
|
|
69
|
-
*/
|
|
70
|
-
async emit(event, data, metadata = {}) {
|
|
71
|
-
const envelope = {
|
|
72
|
-
id: `evt_${crypto.randomBytes(12).toString('hex')}`,
|
|
73
|
-
event,
|
|
74
|
-
data,
|
|
75
|
-
metadata: {
|
|
76
|
-
...metadata,
|
|
77
|
-
timestamp: Date.now(),
|
|
78
|
-
source: metadata.source || 'system',
|
|
79
|
-
},
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
// Run global middleware
|
|
83
|
-
for (const mw of this._middleware) {
|
|
84
|
-
try {
|
|
85
|
-
const result = await mw(envelope);
|
|
86
|
-
if (result === false) {
|
|
87
|
-
this._stats.dropped++;
|
|
88
|
-
return envelope;
|
|
89
|
-
}
|
|
90
|
-
} catch (_) { /* middleware errors don't block */ }
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// Store in history
|
|
94
|
-
this._history.push(envelope);
|
|
95
|
-
if (this._history.length > this._maxHistory) {
|
|
96
|
-
this._history = this._history.slice(-Math.floor(this._maxHistory * 0.8));
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
this._stats.emitted++;
|
|
100
|
-
|
|
101
|
-
// Get listeners (event + wildcard)
|
|
102
|
-
const listeners = [];
|
|
103
|
-
const exact = this._listeners.get(event);
|
|
104
|
-
if (exact) for (const sub of exact) listeners.push(sub);
|
|
105
|
-
const wild = this._listeners.get('*');
|
|
106
|
-
if (wild) for (const sub of wild) listeners.push(sub);
|
|
107
|
-
|
|
108
|
-
// Also match namespace wildcards: 'task.*' matches 'task.completed'
|
|
109
|
-
for (const [pattern, subs] of this._listeners) {
|
|
110
|
-
if (pattern === event || pattern === '*') continue;
|
|
111
|
-
if (pattern.endsWith('.*') && event.startsWith(pattern.slice(0, -1))) {
|
|
112
|
-
for (const sub of subs) listeners.push(sub);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// Sort by priority (higher first)
|
|
117
|
-
listeners.sort((a, b) => b.priority - a.priority);
|
|
118
|
-
|
|
119
|
-
// Dispatch
|
|
120
|
-
const toRemove = [];
|
|
121
|
-
for (const sub of listeners) {
|
|
122
|
-
try {
|
|
123
|
-
if (sub.filter && !sub.filter(envelope.data, envelope.metadata)) continue;
|
|
124
|
-
await sub.handler(envelope.data, envelope.metadata, envelope);
|
|
125
|
-
this._stats.delivered++;
|
|
126
|
-
if (sub.once) toRemove.push(sub);
|
|
127
|
-
} catch (err) {
|
|
128
|
-
this._stats.failed++;
|
|
129
|
-
this._deadLetter.push({ envelope, error: err.message, subscriberId: sub.id, timestamp: Date.now() });
|
|
130
|
-
if (this._deadLetter.length > this._maxDeadLetter) {
|
|
131
|
-
this._deadLetter = this._deadLetter.slice(-Math.floor(this._maxDeadLetter * 0.8));
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// Cleanup one-time subs
|
|
137
|
-
for (const sub of toRemove) {
|
|
138
|
-
for (const [, subs] of this._listeners) subs.delete(sub);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
return envelope;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* Add global middleware
|
|
146
|
-
*/
|
|
147
|
-
use(middleware) {
|
|
148
|
-
this._middleware.push(middleware);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* Replay events matching filter since a timestamp
|
|
153
|
-
*/
|
|
154
|
-
async replay(since, filter, handler) {
|
|
155
|
-
const events = this._history.filter(
|
|
156
|
-
e => e.metadata.timestamp >= since && (!filter || filter(e))
|
|
157
|
-
);
|
|
158
|
-
for (const e of events) {
|
|
159
|
-
await handler(e.data, e.metadata, e);
|
|
160
|
-
}
|
|
161
|
-
return events.length;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Wait for a specific event (returns a promise)
|
|
166
|
-
*/
|
|
167
|
-
waitFor(event, timeout = 30000, filter = null) {
|
|
168
|
-
return new Promise((resolve, reject) => {
|
|
169
|
-
const timer = setTimeout(() => {
|
|
170
|
-
this.off(subId);
|
|
171
|
-
reject(new Error(`Timeout waiting for event: ${event}`));
|
|
172
|
-
}, timeout);
|
|
173
|
-
|
|
174
|
-
const subId = this.once(event, (data, meta) => {
|
|
175
|
-
clearTimeout(timer);
|
|
176
|
-
resolve({ data, meta });
|
|
177
|
-
}, { filter });
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
/**
|
|
182
|
-
* Get dead letter queue
|
|
183
|
-
*/
|
|
184
|
-
getDeadLetters(limit = 50) {
|
|
185
|
-
return this._deadLetter.slice(-limit);
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
/**
|
|
189
|
-
* Get stats
|
|
190
|
-
*/
|
|
191
|
-
getStats() {
|
|
192
|
-
return {
|
|
193
|
-
...this._stats,
|
|
194
|
-
listeners: this._countListeners(),
|
|
195
|
-
historySize: this._history.length,
|
|
196
|
-
deadLetterSize: this._deadLetter.length,
|
|
197
|
-
};
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
_countListeners() {
|
|
201
|
-
let count = 0;
|
|
202
|
-
for (const [, subs] of this._listeners) count += subs.size;
|
|
203
|
-
return count;
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
// Singleton event bus for the runtime
|
|
208
|
-
const bus = new EventBus();
|
|
209
|
-
|
|
210
|
-
module.exports = { EventBus, bus };
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* WAB Runtime - Event Bus
|
|
5
|
+
*
|
|
6
|
+
* Async event system with typed events, middleware, replay buffer,
|
|
7
|
+
* and dead-letter queue. This is the nervous system of the Agent OS.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const crypto = require('crypto');
|
|
11
|
+
|
|
12
|
+
class EventBus {
|
|
13
|
+
constructor(options = {}) {
|
|
14
|
+
this._listeners = new Map(); // event → Set<{ id, handler, filter, once }>
|
|
15
|
+
this._middleware = []; // global middleware
|
|
16
|
+
this._history = []; // event replay buffer
|
|
17
|
+
this._deadLetter = []; // failed events
|
|
18
|
+
this._maxHistory = options.maxHistory || 10000;
|
|
19
|
+
this._maxDeadLetter = options.maxDeadLetter || 1000;
|
|
20
|
+
this._stats = { emitted: 0, delivered: 0, failed: 0, dropped: 0 };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Subscribe to an event
|
|
25
|
+
* @returns {string} subscription ID for unsubscribe
|
|
26
|
+
*/
|
|
27
|
+
on(event, handler, options = {}) {
|
|
28
|
+
if (!this._listeners.has(event)) this._listeners.set(event, new Set());
|
|
29
|
+
const sub = {
|
|
30
|
+
id: `sub_${crypto.randomBytes(8).toString('hex')}`,
|
|
31
|
+
handler,
|
|
32
|
+
filter: options.filter || null,
|
|
33
|
+
once: options.once || false,
|
|
34
|
+
priority: options.priority || 0,
|
|
35
|
+
};
|
|
36
|
+
this._listeners.get(event).add(sub);
|
|
37
|
+
return sub.id;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Subscribe once
|
|
42
|
+
*/
|
|
43
|
+
once(event, handler, options = {}) {
|
|
44
|
+
return this.on(event, handler, { ...options, once: true });
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Unsubscribe by subscription ID
|
|
49
|
+
*/
|
|
50
|
+
off(subId) {
|
|
51
|
+
for (const [, subs] of this._listeners) {
|
|
52
|
+
for (const sub of subs) {
|
|
53
|
+
if (sub.id === subId) { subs.delete(sub); return true; }
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Remove all listeners for an event
|
|
61
|
+
*/
|
|
62
|
+
removeAll(event) {
|
|
63
|
+
if (event) this._listeners.delete(event);
|
|
64
|
+
else this._listeners.clear();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Emit an event
|
|
69
|
+
*/
|
|
70
|
+
async emit(event, data, metadata = {}) {
|
|
71
|
+
const envelope = {
|
|
72
|
+
id: `evt_${crypto.randomBytes(12).toString('hex')}`,
|
|
73
|
+
event,
|
|
74
|
+
data,
|
|
75
|
+
metadata: {
|
|
76
|
+
...metadata,
|
|
77
|
+
timestamp: Date.now(),
|
|
78
|
+
source: metadata.source || 'system',
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
// Run global middleware
|
|
83
|
+
for (const mw of this._middleware) {
|
|
84
|
+
try {
|
|
85
|
+
const result = await mw(envelope);
|
|
86
|
+
if (result === false) {
|
|
87
|
+
this._stats.dropped++;
|
|
88
|
+
return envelope;
|
|
89
|
+
}
|
|
90
|
+
} catch (_) { /* middleware errors don't block */ }
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Store in history
|
|
94
|
+
this._history.push(envelope);
|
|
95
|
+
if (this._history.length > this._maxHistory) {
|
|
96
|
+
this._history = this._history.slice(-Math.floor(this._maxHistory * 0.8));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
this._stats.emitted++;
|
|
100
|
+
|
|
101
|
+
// Get listeners (event + wildcard)
|
|
102
|
+
const listeners = [];
|
|
103
|
+
const exact = this._listeners.get(event);
|
|
104
|
+
if (exact) for (const sub of exact) listeners.push(sub);
|
|
105
|
+
const wild = this._listeners.get('*');
|
|
106
|
+
if (wild) for (const sub of wild) listeners.push(sub);
|
|
107
|
+
|
|
108
|
+
// Also match namespace wildcards: 'task.*' matches 'task.completed'
|
|
109
|
+
for (const [pattern, subs] of this._listeners) {
|
|
110
|
+
if (pattern === event || pattern === '*') continue;
|
|
111
|
+
if (pattern.endsWith('.*') && event.startsWith(pattern.slice(0, -1))) {
|
|
112
|
+
for (const sub of subs) listeners.push(sub);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Sort by priority (higher first)
|
|
117
|
+
listeners.sort((a, b) => b.priority - a.priority);
|
|
118
|
+
|
|
119
|
+
// Dispatch
|
|
120
|
+
const toRemove = [];
|
|
121
|
+
for (const sub of listeners) {
|
|
122
|
+
try {
|
|
123
|
+
if (sub.filter && !sub.filter(envelope.data, envelope.metadata)) continue;
|
|
124
|
+
await sub.handler(envelope.data, envelope.metadata, envelope);
|
|
125
|
+
this._stats.delivered++;
|
|
126
|
+
if (sub.once) toRemove.push(sub);
|
|
127
|
+
} catch (err) {
|
|
128
|
+
this._stats.failed++;
|
|
129
|
+
this._deadLetter.push({ envelope, error: err.message, subscriberId: sub.id, timestamp: Date.now() });
|
|
130
|
+
if (this._deadLetter.length > this._maxDeadLetter) {
|
|
131
|
+
this._deadLetter = this._deadLetter.slice(-Math.floor(this._maxDeadLetter * 0.8));
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Cleanup one-time subs
|
|
137
|
+
for (const sub of toRemove) {
|
|
138
|
+
for (const [, subs] of this._listeners) subs.delete(sub);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return envelope;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Add global middleware
|
|
146
|
+
*/
|
|
147
|
+
use(middleware) {
|
|
148
|
+
this._middleware.push(middleware);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Replay events matching filter since a timestamp
|
|
153
|
+
*/
|
|
154
|
+
async replay(since, filter, handler) {
|
|
155
|
+
const events = this._history.filter(
|
|
156
|
+
e => e.metadata.timestamp >= since && (!filter || filter(e))
|
|
157
|
+
);
|
|
158
|
+
for (const e of events) {
|
|
159
|
+
await handler(e.data, e.metadata, e);
|
|
160
|
+
}
|
|
161
|
+
return events.length;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Wait for a specific event (returns a promise)
|
|
166
|
+
*/
|
|
167
|
+
waitFor(event, timeout = 30000, filter = null) {
|
|
168
|
+
return new Promise((resolve, reject) => {
|
|
169
|
+
const timer = setTimeout(() => {
|
|
170
|
+
this.off(subId);
|
|
171
|
+
reject(new Error(`Timeout waiting for event: ${event}`));
|
|
172
|
+
}, timeout);
|
|
173
|
+
|
|
174
|
+
const subId = this.once(event, (data, meta) => {
|
|
175
|
+
clearTimeout(timer);
|
|
176
|
+
resolve({ data, meta });
|
|
177
|
+
}, { filter });
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Get dead letter queue
|
|
183
|
+
*/
|
|
184
|
+
getDeadLetters(limit = 50) {
|
|
185
|
+
return this._deadLetter.slice(-limit);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Get stats
|
|
190
|
+
*/
|
|
191
|
+
getStats() {
|
|
192
|
+
return {
|
|
193
|
+
...this._stats,
|
|
194
|
+
listeners: this._countListeners(),
|
|
195
|
+
historySize: this._history.length,
|
|
196
|
+
deadLetterSize: this._deadLetter.length,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
_countListeners() {
|
|
201
|
+
let count = 0;
|
|
202
|
+
for (const [, subs] of this._listeners) count += subs.size;
|
|
203
|
+
return count;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Singleton event bus for the runtime
|
|
208
|
+
const bus = new EventBus();
|
|
209
|
+
|
|
210
|
+
module.exports = { EventBus, bus };
|