@private.me/xbind 3.0.0 → 3.0.2
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/README.md +55 -7
- package/dist-standalone/_deps/mldsa-wasm/dist/mldsa.js +1920 -1
- package/dist-standalone/_deps/shared/cjs/errors.js +729 -1
- package/dist-standalone/_deps/shared/cjs/index.js +463 -1
- package/dist-standalone/_deps/shared/cjs/types.js +315 -1
- package/dist-standalone/_deps/shared/errors.js +244 -1
- package/dist-standalone/_deps/shared/index.js +72 -1
- package/dist-standalone/_deps/shared/types.js +86 -1
- package/dist-standalone/_deps/ux-helpers/cjs/errors.js +1 -1
- package/dist-standalone/_deps/ux-helpers/cjs/index.js +1 -1
- package/dist-standalone/_deps/ux-helpers/cjs/pagination.js +1 -1
- package/dist-standalone/_deps/ux-helpers/cjs/progress.js +1 -1
- package/dist-standalone/_deps/ux-helpers/cjs/search.js +1 -1
- package/dist-standalone/_deps/ux-helpers/cjs/types.js +1 -1
- package/dist-standalone/_deps/ux-helpers/errors.js +1 -1
- package/dist-standalone/_deps/ux-helpers/index.js +1 -1
- package/dist-standalone/_deps/ux-helpers/pagination.js +1 -1
- package/dist-standalone/_deps/ux-helpers/progress.js +1 -1
- package/dist-standalone/_deps/ux-helpers/search.js +1 -1
- package/dist-standalone/_deps/xchange/auto-accept.js +1 -1
- package/dist-standalone/_deps/xchange/cjs/auto-accept.js +1 -1
- package/dist-standalone/_deps/xchange/cjs/errors.js +1 -1
- package/dist-standalone/_deps/xchange/cjs/index.js +1 -1
- package/dist-standalone/_deps/xchange/cjs/invite-client.js +1 -1
- package/dist-standalone/_deps/xchange/cjs/lazy-init.js +1 -1
- package/dist-standalone/_deps/xchange/cjs/trust-integration.js +1 -1
- package/dist-standalone/_deps/xchange/cjs/xchange.js +1 -1
- package/dist-standalone/_deps/xchange/errors.js +1 -1
- package/dist-standalone/_deps/xchange/index.js +1 -1
- package/dist-standalone/_deps/xchange/invite-client.js +1 -1
- package/dist-standalone/_deps/xchange/lazy-init.js +1 -1
- package/dist-standalone/_deps/xchange/trust-integration.js +1 -1
- package/dist-standalone/_deps/xchange/xchange.js +1 -1
- package/dist-standalone/_deps/xregistry/cjs/discovery.js +1 -1
- package/dist-standalone/_deps/xregistry/cjs/errors.js +1 -1
- package/dist-standalone/_deps/xregistry/cjs/index.js +1 -1
- package/dist-standalone/_deps/xregistry/cjs/registry.js +1 -1
- package/dist-standalone/_deps/xregistry/cjs/schema.js +1 -1
- package/dist-standalone/_deps/xregistry/cjs/types.js +1 -1
- package/dist-standalone/_deps/xregistry/discovery.js +1 -1
- package/dist-standalone/_deps/xregistry/errors.js +1 -1
- package/dist-standalone/_deps/xregistry/index.js +1 -1
- package/dist-standalone/_deps/xregistry/registry.js +1 -1
- package/dist-standalone/_deps/xregistry/schema.js +1 -1
- package/dist-standalone/_deps/xregistry/types.js +1 -1
- package/dist-standalone/agent-call.js +659 -1
- package/dist-standalone/agent-sdk.js +328 -1
- package/dist-standalone/agent.js +1800 -1
- package/dist-standalone/approval.js +193 -1
- package/dist-standalone/async-iterators.js +382 -1
- package/dist-standalone/auth.js +219 -1
- package/dist-standalone/auto-accept.js +229 -1
- package/dist-standalone/backup-config.js +201 -1
- package/dist-standalone/backup.js +326 -1
- package/dist-standalone/batch-operations.js +388 -1
- package/dist-standalone/cancellation.js +477 -1
- package/dist-standalone/checkpoint.js +186 -1
- package/dist-standalone/circuit-breaker.js +468 -1
- package/dist-standalone/cjs/agent-call.js +701 -1
- package/dist-standalone/cjs/agent-sdk.js +332 -1
- package/dist-standalone/cjs/agent.js +1837 -1
- package/dist-standalone/cjs/approval.js +199 -1
- package/dist-standalone/cjs/async-iterators.js +392 -1
- package/dist-standalone/cjs/auth.js +225 -1
- package/dist-standalone/cjs/auto-accept.js +233 -1
- package/dist-standalone/cjs/backup-config.js +207 -1
- package/dist-standalone/cjs/backup.js +330 -1
- package/dist-standalone/cjs/batch-operations.js +397 -1
- package/dist-standalone/cjs/cancellation.js +490 -1
- package/dist-standalone/cjs/checkpoint.js +193 -1
- package/dist-standalone/cjs/circuit-breaker.js +476 -1
- package/dist-standalone/cjs/cli/init.js +492 -1
- package/dist-standalone/cjs/config-validation.js +522 -1
- package/dist-standalone/cjs/connect.js +312 -1
- package/dist-standalone/cjs/connection-pool.js +506 -1
- package/dist-standalone/cjs/correlation-id.js +339 -1
- package/dist-standalone/cjs/crypto-utils.js +176 -1
- package/dist-standalone/cjs/debug-mode.js +534 -1
- package/dist-standalone/cjs/did-document.js +101 -1
- package/dist-standalone/cjs/did-privateme.js +130 -1
- package/dist-standalone/cjs/did-web.js +201 -1
- package/dist-standalone/cjs/discovery.js +462 -1
- package/dist-standalone/cjs/dual-mode.js +251 -1
- package/dist-standalone/cjs/email-templates.js +313 -1
- package/dist-standalone/cjs/email-transport.js +239 -1
- package/dist-standalone/cjs/envelope.js +538 -1
- package/dist-standalone/cjs/errors.js +913 -1
- package/dist-standalone/cjs/event-emitter.js +461 -1
- package/dist-standalone/cjs/gateway-state.js +55 -1
- package/dist-standalone/cjs/gateway-transport.js +120 -1
- package/dist-standalone/cjs/graceful-degradation.js +403 -1
- package/dist-standalone/cjs/guardrails.js +223 -1
- package/dist-standalone/cjs/health-check.js +336 -1
- package/dist-standalone/cjs/http-compat.js +272 -1
- package/dist-standalone/cjs/http-status-map.js +571 -1
- package/dist-standalone/cjs/identity.js +645 -1
- package/dist-standalone/cjs/index.js +406 -1
- package/dist-standalone/cjs/invitation.js +421 -1
- package/dist-standalone/cjs/invite.js +328 -1
- package/dist-standalone/cjs/key-agreement.js +335 -1
- package/dist-standalone/cjs/lazy-init.js +300 -1
- package/dist-standalone/cjs/logger.js +291 -1
- package/dist-standalone/cjs/mdns-discovery.js +202 -1
- package/dist-standalone/cjs/nonce-store.js +80 -1
- package/dist-standalone/cjs/pairing-manager.js +223 -1
- package/dist-standalone/cjs/plugin-system.js +264 -1
- package/dist-standalone/cjs/plugins/logging.js +168 -1
- package/dist-standalone/cjs/plugins/metrics.js +181 -1
- package/dist-standalone/cjs/plugins/validation.js +302 -1
- package/dist-standalone/cjs/policy.js +320 -1
- package/dist-standalone/cjs/progress-callbacks.js +583 -1
- package/dist-standalone/cjs/redis-nonce-store.js +76 -1
- package/dist-standalone/cjs/registry-middleware.js +50 -1
- package/dist-standalone/cjs/retry-strategies.js +544 -1
- package/dist-standalone/cjs/retry-transport.js +102 -1
- package/dist-standalone/cjs/runtime/browser.js +533 -1
- package/dist-standalone/cjs/runtime/edge.js +526 -1
- package/dist-standalone/cjs/runtime/react-native.js +394 -1
- package/dist-standalone/cjs/security-policy.js +245 -1
- package/dist-standalone/cjs/serialization.js +1040 -1
- package/dist-standalone/cjs/split-channel.js +225 -1
- package/dist-standalone/cjs/subscription-proof.js +230 -1
- package/dist-standalone/cjs/succession.js +148 -1
- package/dist-standalone/cjs/timeouts.js +412 -1
- package/dist-standalone/cjs/trace-context.js +424 -1
- package/dist-standalone/cjs/trace-spans.js +495 -1
- package/dist-standalone/cjs/transport.js +63 -1
- package/dist-standalone/cjs/trust-registry.js +991 -1
- package/dist-standalone/cjs/types/error-response.js +56 -1
- package/dist-standalone/cjs/vault-auth.js +178 -1
- package/dist-standalone/cjs/vault-store-loader.js +194 -1
- package/dist-standalone/cjs/verify.js +25 -1
- package/dist-standalone/cjs/version-info.js +543 -1
- package/dist-standalone/cjs/xfetch.js +340 -1
- package/dist-standalone/cli/init.js +455 -1
- package/dist-standalone/cli/setup.js +514 -1
- package/dist-standalone/cli/types.js +27 -1
- package/dist-standalone/cli/xbind.js +148 -1
- package/dist-standalone/config-validation.js +513 -1
- package/dist-standalone/connect.js +274 -1
- package/dist-standalone/connection-pool.js +500 -1
- package/dist-standalone/correlation-id.js +326 -1
- package/dist-standalone/crypto-utils.js +157 -1
- package/dist-standalone/debug-mode.js +510 -1
- package/dist-standalone/did-document.js +96 -1
- package/dist-standalone/did-privateme.js +121 -1
- package/dist-standalone/did-web.js +196 -1
- package/dist-standalone/discovery.js +458 -1
- package/dist-standalone/dual-mode.js +247 -1
- package/dist-standalone/email-templates.js +309 -1
- package/dist-standalone/email-transport.js +232 -1
- package/dist-standalone/envelope.js +525 -1
- package/dist-standalone/errors.js +896 -1
- package/dist-standalone/event-emitter.js +456 -1
- package/dist-standalone/gateway-state.js +51 -1
- package/dist-standalone/gateway-transport.js +116 -1
- package/dist-standalone/graceful-degradation.js +396 -1
- package/dist-standalone/guardrails.js +216 -1
- package/dist-standalone/health-check.js +332 -1
- package/dist-standalone/http-compat.js +267 -1
- package/dist-standalone/http-status-map.js +561 -1
- package/dist-standalone/identity.js +619 -1
- package/dist-standalone/index.js +78 -1
- package/dist-standalone/invitation.js +415 -1
- package/dist-standalone/invite.js +324 -1
- package/dist-standalone/key-agreement.js +325 -1
- package/dist-standalone/lazy-init.js +295 -1
- package/dist-standalone/logger.js +285 -1
- package/dist-standalone/mdns-discovery.js +195 -1
- package/dist-standalone/nonce-store.js +76 -1
- package/dist-standalone/pairing-manager.js +219 -1
- package/dist-standalone/plugin-system.js +257 -1
- package/dist-standalone/plugins/logging.d.ts +84 -0
- package/dist-standalone/plugins/logging.js +163 -0
- package/dist-standalone/plugins/metrics.d.ts +111 -0
- package/dist-standalone/plugins/metrics.js +176 -0
- package/dist-standalone/plugins/validation.d.ts +104 -0
- package/dist-standalone/plugins/validation.js +297 -0
- package/dist-standalone/policy.js +315 -1
- package/dist-standalone/progress-callbacks.js +576 -1
- package/dist-standalone/redis-nonce-store.js +72 -1
- package/dist-standalone/registry-middleware.js +47 -1
- package/dist-standalone/retry-strategies.js +534 -1
- package/dist-standalone/retry-transport.js +98 -1
- package/dist-standalone/runtime/browser.d.ts +311 -0
- package/dist-standalone/runtime/browser.js +516 -0
- package/dist-standalone/runtime/edge.d.ts +282 -0
- package/dist-standalone/runtime/edge.js +511 -0
- package/dist-standalone/runtime/react-native.d.ts +157 -0
- package/dist-standalone/runtime/react-native.js +383 -0
- package/dist-standalone/security-policy.js +239 -1
- package/dist-standalone/serialization.js +1031 -1
- package/dist-standalone/split-channel.js +219 -1
- package/dist-standalone/subscription-proof.js +224 -1
- package/dist-standalone/succession.js +142 -1
- package/dist-standalone/timeouts.js +398 -1
- package/dist-standalone/trace-context.js +414 -1
- package/dist-standalone/trace-spans.js +488 -1
- package/dist-standalone/transport.js +59 -1
- package/dist-standalone/trust-registry.js +950 -1
- package/dist-standalone/types/error-response.d.ts +209 -0
- package/dist-standalone/types/error-response.js +52 -0
- package/dist-standalone/vault-auth.js +174 -1
- package/dist-standalone/vault-store-loader.js +187 -1
- package/dist-standalone/verify.js +16 -1
- package/dist-standalone/version-info.js +530 -1
- package/dist-standalone/xfetch.js +335 -1
- package/package.json +4 -10
- package/share1.dat +0 -0
- package/dist-standalone/_deps/mldsa-wasm/LICENSE +0 -24
- package/dist-standalone/_deps/mldsa-wasm/package.json +0 -46
- package/dist-standalone/_deps/shared/cjs/package.json +0 -1
- package/dist-standalone/_deps/ux-helpers/cjs/package.json +0 -1
- package/dist-standalone/_deps/xchange/cjs/package.json +0 -1
- package/dist-standalone/_deps/xregistry/cjs/package.json +0 -1
- package/dist-standalone/cjs/package.json +0 -3
- package/dist-standalone/package.json +0 -10
|
@@ -1 +1,456 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* @module event-emitter
|
|
3
|
+
* Type-safe event emitter for xBind agent events.
|
|
4
|
+
*
|
|
5
|
+
* Provides event-driven architecture for agent lifecycle events:
|
|
6
|
+
* - message-sent: Emitted when a message is successfully sent
|
|
7
|
+
* - message-received: Emitted when a message is received
|
|
8
|
+
* - key-rotated: Emitted when encryption keys are rotated
|
|
9
|
+
* - error: Emitted when an error occurs
|
|
10
|
+
* - connection-status: Emitted when connection state changes
|
|
11
|
+
*/
|
|
12
|
+
import { ok, err } from"./_deps/shared/index.js";
|
|
13
|
+
/**
|
|
14
|
+
* Type-safe event emitter for xBind agent events.
|
|
15
|
+
*
|
|
16
|
+
* Features:
|
|
17
|
+
* - Type-safe event names and payloads
|
|
18
|
+
* - Listener priority support
|
|
19
|
+
* - Once listeners
|
|
20
|
+
* - Event bubbling to parent emitters
|
|
21
|
+
* - Async listener support with error handling
|
|
22
|
+
* - Memory leak detection
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```typescript
|
|
26
|
+
* const emitter = new XBindEventEmitter();
|
|
27
|
+
*
|
|
28
|
+
* // Register listener
|
|
29
|
+
* emitter.on('message-received', (event) => {
|
|
30
|
+
* console.log('Received from:', event.senderDid);
|
|
31
|
+
* });
|
|
32
|
+
*
|
|
33
|
+
* // Register one-time listener
|
|
34
|
+
* emitter.once('connection-status', (event) => {
|
|
35
|
+
* console.log('Status:', event.status);
|
|
36
|
+
* });
|
|
37
|
+
*
|
|
38
|
+
* // Emit event
|
|
39
|
+
* await emitter.emit('message-received', {
|
|
40
|
+
* type: 'message-received',
|
|
41
|
+
* timestamp: new Date().toISOString(),
|
|
42
|
+
* eventId: '123',
|
|
43
|
+
* senderDid: 'did:key:z6Mk...',
|
|
44
|
+
* message: { payload: 'Hello' },
|
|
45
|
+
* messageId: 'msg-1',
|
|
46
|
+
* verified: true
|
|
47
|
+
* });
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
export class XBindEventEmitter {
|
|
51
|
+
listeners = new Map();
|
|
52
|
+
parent;
|
|
53
|
+
disposed = false;
|
|
54
|
+
eventCounter = 0;
|
|
55
|
+
/** Maximum listeners per event type before warning (0 = unlimited) */
|
|
56
|
+
maxListeners = 10;
|
|
57
|
+
/** Whether warnings are enabled */
|
|
58
|
+
warningsEnabled = true;
|
|
59
|
+
/**
|
|
60
|
+
* Create a new event emitter.
|
|
61
|
+
*
|
|
62
|
+
* @param parent - Optional parent emitter for event bubbling
|
|
63
|
+
*/
|
|
64
|
+
constructor(parent) {
|
|
65
|
+
this.parent = parent;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Register an event listener.
|
|
69
|
+
*
|
|
70
|
+
* @param eventType - Event type to listen for
|
|
71
|
+
* @param listener - Listener function
|
|
72
|
+
* @param options - Listener options (priority, once)
|
|
73
|
+
* @returns This emitter for chaining
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* ```typescript
|
|
77
|
+
* emitter.on('error', (event) => {
|
|
78
|
+
* console.error('Error:', event.message);
|
|
79
|
+
* }, { priority: 10 });
|
|
80
|
+
* ```
|
|
81
|
+
*/
|
|
82
|
+
on(eventType, listener, options = {}) {
|
|
83
|
+
this.checkDisposed();
|
|
84
|
+
if (typeof listener !== 'function') {
|
|
85
|
+
throw new Error('Listener must be a function');
|
|
86
|
+
}
|
|
87
|
+
const listeners = this.listeners.get(eventType) ?? [];
|
|
88
|
+
const registered = {
|
|
89
|
+
listener: listener,
|
|
90
|
+
once: options.once ?? false,
|
|
91
|
+
priority: options.priority ?? 0,
|
|
92
|
+
};
|
|
93
|
+
listeners.push(registered);
|
|
94
|
+
// Sort by priority (highest first)
|
|
95
|
+
listeners.sort((a, b) => b.priority - a.priority);
|
|
96
|
+
this.listeners.set(eventType, listeners);
|
|
97
|
+
// Check for potential memory leaks
|
|
98
|
+
if (this.warningsEnabled && this.maxListeners > 0 && listeners.length > this.maxListeners) {
|
|
99
|
+
// eslint-disable-next-line no-console
|
|
100
|
+
console.warn(`[xBind EventEmitter] Possible memory leak detected: ${listeners.length} listeners for event '${eventType}'. ` +
|
|
101
|
+
`Use emitter.setMaxListeners() to increase limit.`);
|
|
102
|
+
}
|
|
103
|
+
return this;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Register a one-time event listener.
|
|
107
|
+
* Listener is automatically removed after first invocation.
|
|
108
|
+
*
|
|
109
|
+
* @param eventType - Event type to listen for
|
|
110
|
+
* @param listener - Listener function
|
|
111
|
+
* @param options - Listener options (priority)
|
|
112
|
+
* @returns This emitter for chaining
|
|
113
|
+
*
|
|
114
|
+
* @example
|
|
115
|
+
* ```typescript
|
|
116
|
+
* emitter.once('connection-status', (event) => {
|
|
117
|
+
* console.log('First status change:', event.status);
|
|
118
|
+
* });
|
|
119
|
+
* ```
|
|
120
|
+
*/
|
|
121
|
+
once(eventType, listener, options = {}) {
|
|
122
|
+
return this.on(eventType, listener, { ...options, once: true });
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Remove an event listener.
|
|
126
|
+
*
|
|
127
|
+
* @param eventType - Event type
|
|
128
|
+
* @param listener - Listener function to remove
|
|
129
|
+
* @returns This emitter for chaining
|
|
130
|
+
*
|
|
131
|
+
* @example
|
|
132
|
+
* ```typescript
|
|
133
|
+
* const handler = (event) => console.log(event);
|
|
134
|
+
* emitter.on('error', handler);
|
|
135
|
+
* emitter.off('error', handler);
|
|
136
|
+
* ```
|
|
137
|
+
*/
|
|
138
|
+
off(eventType, listener) {
|
|
139
|
+
this.checkDisposed();
|
|
140
|
+
const listeners = this.listeners.get(eventType);
|
|
141
|
+
if (!listeners)
|
|
142
|
+
return this;
|
|
143
|
+
const filtered = listeners.filter(l => l.listener !== listener);
|
|
144
|
+
if (filtered.length === 0) {
|
|
145
|
+
this.listeners.delete(eventType);
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
this.listeners.set(eventType, filtered);
|
|
149
|
+
}
|
|
150
|
+
return this;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Remove all listeners for an event type, or all listeners if no type specified.
|
|
154
|
+
*
|
|
155
|
+
* @param eventType - Optional event type (removes all if not specified)
|
|
156
|
+
* @returns This emitter for chaining
|
|
157
|
+
*
|
|
158
|
+
* @example
|
|
159
|
+
* ```typescript
|
|
160
|
+
* emitter.removeAllListeners('error'); // Remove all error listeners
|
|
161
|
+
* emitter.removeAllListeners(); // Remove all listeners
|
|
162
|
+
* ```
|
|
163
|
+
*/
|
|
164
|
+
removeAllListeners(eventType) {
|
|
165
|
+
this.checkDisposed();
|
|
166
|
+
if (eventType) {
|
|
167
|
+
this.listeners.delete(eventType);
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
this.listeners.clear();
|
|
171
|
+
}
|
|
172
|
+
return this;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Emit an event to all registered listeners.
|
|
176
|
+
* Listeners are called in priority order (highest first).
|
|
177
|
+
* Async listeners are awaited in sequence.
|
|
178
|
+
*
|
|
179
|
+
* @param eventType - Event type to emit
|
|
180
|
+
* @param event - Event payload
|
|
181
|
+
* @param options - Emission options (bubble, stopPropagation)
|
|
182
|
+
* @returns Result indicating success or first error encountered
|
|
183
|
+
*
|
|
184
|
+
* @example
|
|
185
|
+
* ```typescript
|
|
186
|
+
* await emitter.emit('error', {
|
|
187
|
+
* type: 'error',
|
|
188
|
+
* timestamp: new Date().toISOString(),
|
|
189
|
+
* eventId: '123',
|
|
190
|
+
* code: 'NETWORK_ERROR',
|
|
191
|
+
* message: 'Connection failed',
|
|
192
|
+
* context: 'transport'
|
|
193
|
+
* });
|
|
194
|
+
* ```
|
|
195
|
+
*/
|
|
196
|
+
async emit(eventType, event, options = {}) {
|
|
197
|
+
this.checkDisposed();
|
|
198
|
+
// Add standard fields if not present
|
|
199
|
+
const fullEvent = {
|
|
200
|
+
...event,
|
|
201
|
+
eventId: options.eventId ?? this.generateEventId(),
|
|
202
|
+
timestamp: new Date().toISOString(),
|
|
203
|
+
};
|
|
204
|
+
const listeners = this.listeners.get(eventType);
|
|
205
|
+
if (listeners && listeners.length > 0) {
|
|
206
|
+
// Create a copy to avoid issues if listeners modify the array
|
|
207
|
+
const listenersCopy = [...listeners];
|
|
208
|
+
for (const registered of listenersCopy) {
|
|
209
|
+
try {
|
|
210
|
+
await registered.listener(fullEvent);
|
|
211
|
+
// Remove once listeners
|
|
212
|
+
if (registered.once) {
|
|
213
|
+
this.off(eventType, registered.listener);
|
|
214
|
+
}
|
|
215
|
+
// Stop propagation if requested
|
|
216
|
+
if (options.stopPropagation) {
|
|
217
|
+
return ok(undefined);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
catch (error) {
|
|
221
|
+
// Log error but continue with other listeners
|
|
222
|
+
// eslint-disable-next-line no-console
|
|
223
|
+
console.error(`[xBind EventEmitter] Error in listener for '${eventType}':`, error);
|
|
224
|
+
// Return first error encountered
|
|
225
|
+
return err('LISTENER_ERROR');
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
// Bubble to parent if requested
|
|
230
|
+
if (options.bubble !== false && this.parent) {
|
|
231
|
+
return this.parent.emit(eventType, event, { ...options, bubble: true });
|
|
232
|
+
}
|
|
233
|
+
return ok(undefined);
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Check if there are any listeners for an event type.
|
|
237
|
+
*
|
|
238
|
+
* @param eventType - Event type to check
|
|
239
|
+
* @returns True if there are listeners
|
|
240
|
+
*
|
|
241
|
+
* @example
|
|
242
|
+
* ```typescript
|
|
243
|
+
* if (emitter.hasListeners('error')) {
|
|
244
|
+
* await emitter.emit('error', errorEvent);
|
|
245
|
+
* }
|
|
246
|
+
* ```
|
|
247
|
+
*/
|
|
248
|
+
hasListeners(eventType) {
|
|
249
|
+
const listeners = this.listeners.get(eventType);
|
|
250
|
+
return listeners !== undefined && listeners.length > 0;
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Get the number of listeners for an event type.
|
|
254
|
+
*
|
|
255
|
+
* @param eventType - Event type
|
|
256
|
+
* @returns Number of listeners
|
|
257
|
+
*
|
|
258
|
+
* @example
|
|
259
|
+
* ```typescript
|
|
260
|
+
* const count = emitter.listenerCount('message-received');
|
|
261
|
+
* console.log(`${count} listeners registered`);
|
|
262
|
+
* ```
|
|
263
|
+
*/
|
|
264
|
+
listenerCount(eventType) {
|
|
265
|
+
const listeners = this.listeners.get(eventType);
|
|
266
|
+
return listeners?.length ?? 0;
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Get all registered event types.
|
|
270
|
+
*
|
|
271
|
+
* @returns Array of event types with active listeners
|
|
272
|
+
*
|
|
273
|
+
* @example
|
|
274
|
+
* ```typescript
|
|
275
|
+
* const events = emitter.eventNames();
|
|
276
|
+
* console.log('Active events:', events);
|
|
277
|
+
* ```
|
|
278
|
+
*/
|
|
279
|
+
eventNames() {
|
|
280
|
+
return Array.from(this.listeners.keys());
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Set the maximum number of listeners per event before warning.
|
|
284
|
+
* Use 0 for unlimited listeners.
|
|
285
|
+
*
|
|
286
|
+
* @param n - Maximum listeners (0 = unlimited)
|
|
287
|
+
* @returns This emitter for chaining
|
|
288
|
+
*
|
|
289
|
+
* @example
|
|
290
|
+
* ```typescript
|
|
291
|
+
* emitter.setMaxListeners(20); // Allow up to 20 listeners
|
|
292
|
+
* emitter.setMaxListeners(0); // Unlimited
|
|
293
|
+
* ```
|
|
294
|
+
*/
|
|
295
|
+
setMaxListeners(n) {
|
|
296
|
+
if (n < 0 || !Number.isInteger(n)) {
|
|
297
|
+
throw new Error('Max listeners must be a non-negative integer');
|
|
298
|
+
}
|
|
299
|
+
this.maxListeners = n;
|
|
300
|
+
return this;
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* Get the maximum number of listeners per event.
|
|
304
|
+
*
|
|
305
|
+
* @returns Maximum listeners (0 = unlimited)
|
|
306
|
+
*/
|
|
307
|
+
getMaxListeners() {
|
|
308
|
+
return this.maxListeners;
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Enable or disable memory leak warnings.
|
|
312
|
+
*
|
|
313
|
+
* @param enabled - Whether warnings are enabled
|
|
314
|
+
* @returns This emitter for chaining
|
|
315
|
+
*/
|
|
316
|
+
setWarningsEnabled(enabled) {
|
|
317
|
+
this.warningsEnabled = enabled;
|
|
318
|
+
return this;
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Set the parent emitter for event bubbling.
|
|
322
|
+
*
|
|
323
|
+
* @param parent - Parent emitter (undefined to remove parent)
|
|
324
|
+
* @returns This emitter for chaining
|
|
325
|
+
*
|
|
326
|
+
* @example
|
|
327
|
+
* ```typescript
|
|
328
|
+
* const parent = new XBindEventEmitter();
|
|
329
|
+
* const child = new XBindEventEmitter();
|
|
330
|
+
* child.setParent(parent);
|
|
331
|
+
*
|
|
332
|
+
* parent.on('error', (event) => console.log('Parent received:', event));
|
|
333
|
+
* await child.emit('error', errorEvent, { bubble: true });
|
|
334
|
+
* ```
|
|
335
|
+
*/
|
|
336
|
+
setParent(parent) {
|
|
337
|
+
this.parent = parent;
|
|
338
|
+
return this;
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Get the parent emitter.
|
|
342
|
+
*
|
|
343
|
+
* @returns Parent emitter or undefined
|
|
344
|
+
*/
|
|
345
|
+
getParent() {
|
|
346
|
+
return this.parent;
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Wait for a specific event to be emitted.
|
|
350
|
+
* Returns a promise that resolves with the event payload.
|
|
351
|
+
*
|
|
352
|
+
* @param eventType - Event type to wait for
|
|
353
|
+
* @param timeout - Optional timeout in milliseconds
|
|
354
|
+
* @returns Promise resolving to event or rejecting on timeout
|
|
355
|
+
*
|
|
356
|
+
* @example
|
|
357
|
+
* ```typescript
|
|
358
|
+
* const event = await emitter.waitFor('connection-status', 5000);
|
|
359
|
+
* console.log('Status changed to:', event.status);
|
|
360
|
+
* ```
|
|
361
|
+
*/
|
|
362
|
+
waitFor(eventType, timeout) {
|
|
363
|
+
return new Promise((resolve, reject) => {
|
|
364
|
+
let timeoutId;
|
|
365
|
+
const handler = (event) => {
|
|
366
|
+
if (timeoutId)
|
|
367
|
+
clearTimeout(timeoutId);
|
|
368
|
+
resolve(event);
|
|
369
|
+
};
|
|
370
|
+
this.once(eventType, handler);
|
|
371
|
+
if (timeout !== undefined && timeout > 0) {
|
|
372
|
+
timeoutId = setTimeout(() => {
|
|
373
|
+
this.off(eventType, handler);
|
|
374
|
+
reject(new Error(`Timeout waiting for event '${eventType}' after ${timeout}ms`));
|
|
375
|
+
}, timeout);
|
|
376
|
+
}
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Dispose the event emitter and remove all listeners.
|
|
381
|
+
* After disposal, no events can be emitted and listeners cannot be registered.
|
|
382
|
+
*
|
|
383
|
+
* @example
|
|
384
|
+
* ```typescript
|
|
385
|
+
* emitter.dispose();
|
|
386
|
+
* ```
|
|
387
|
+
*/
|
|
388
|
+
dispose() {
|
|
389
|
+
this.disposed = true;
|
|
390
|
+
this.listeners.clear();
|
|
391
|
+
this.parent = undefined;
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* Check if the emitter has been disposed.
|
|
395
|
+
*
|
|
396
|
+
* @returns True if disposed
|
|
397
|
+
*/
|
|
398
|
+
isDisposed() {
|
|
399
|
+
return this.disposed;
|
|
400
|
+
}
|
|
401
|
+
checkDisposed() {
|
|
402
|
+
if (this.disposed) {
|
|
403
|
+
throw new Error('EventEmitter has been disposed');
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
generateEventId() {
|
|
407
|
+
return `evt-${Date.now()}-${++this.eventCounter}`;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
/**
|
|
411
|
+
* Create a scoped event emitter that only emits specific event types.
|
|
412
|
+
* Useful for isolating event channels.
|
|
413
|
+
*
|
|
414
|
+
* @param parent - Parent emitter
|
|
415
|
+
* @param allowedEvents - Array of allowed event types
|
|
416
|
+
* @returns Scoped event emitter
|
|
417
|
+
*
|
|
418
|
+
* @example
|
|
419
|
+
* ```typescript
|
|
420
|
+
* const parent = new XBindEventEmitter();
|
|
421
|
+
* const errorOnly = createScopedEmitter(parent, ['error']);
|
|
422
|
+
*
|
|
423
|
+
* errorOnly.emit('error', errorEvent); // OK
|
|
424
|
+
* errorOnly.emit('message-sent', event); // TypeScript error
|
|
425
|
+
* ```
|
|
426
|
+
*/
|
|
427
|
+
export function createScopedEmitter(parent, allowedEvents) {
|
|
428
|
+
const emitter = new XBindEventEmitter(parent);
|
|
429
|
+
const allowedSet = new Set(allowedEvents);
|
|
430
|
+
return {
|
|
431
|
+
on: (type, listener, options) => {
|
|
432
|
+
if (!allowedSet.has(type)) {
|
|
433
|
+
throw new Error(`Event type '${String(type)}' not allowed in scoped emitter`);
|
|
434
|
+
}
|
|
435
|
+
return emitter.on(type, listener, options);
|
|
436
|
+
},
|
|
437
|
+
once: (type, listener, options) => {
|
|
438
|
+
if (!allowedSet.has(type)) {
|
|
439
|
+
throw new Error(`Event type '${String(type)}' not allowed in scoped emitter`);
|
|
440
|
+
}
|
|
441
|
+
return emitter.once(type, listener, options);
|
|
442
|
+
},
|
|
443
|
+
off: (type, listener) => {
|
|
444
|
+
return emitter.off(type, listener);
|
|
445
|
+
},
|
|
446
|
+
emit: (type, event, options) => {
|
|
447
|
+
if (!allowedSet.has(type)) {
|
|
448
|
+
return Promise.resolve(err('INVALID_EVENT_TYPE'));
|
|
449
|
+
}
|
|
450
|
+
return emitter.emit(type, event, options);
|
|
451
|
+
},
|
|
452
|
+
hasListeners: (type) => emitter.hasListeners(type),
|
|
453
|
+
listenerCount: (type) => emitter.listenerCount(type),
|
|
454
|
+
dispose: () => emitter.dispose(),
|
|
455
|
+
};
|
|
456
|
+
}
|
|
@@ -1 +1,51 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Gateway connection state manager.
|
|
3
|
+
*
|
|
4
|
+
* Online-only model: State evaporates immediately on disconnect.
|
|
5
|
+
* No 12-hour windows, no persistent state.
|
|
6
|
+
*
|
|
7
|
+
* Note: WebSocket type is runtime-dependent (browser WebSocket or 'ws' package).
|
|
8
|
+
*/
|
|
9
|
+
export class GatewayConnectionState {
|
|
10
|
+
connections = new Map(); // did → socket
|
|
11
|
+
/**
|
|
12
|
+
* Register agent connection.
|
|
13
|
+
*
|
|
14
|
+
* @param did - DID of the connecting agent
|
|
15
|
+
* @param socket - WebSocket connection (browser or Node.js 'ws' package)
|
|
16
|
+
*/
|
|
17
|
+
connect(did, socket) {
|
|
18
|
+
this.connections.set(did, socket);
|
|
19
|
+
// Handle close event (works for both browser and Node.js ws)
|
|
20
|
+
if (typeof socket.on === 'function') {
|
|
21
|
+
// Node.js 'ws' package style
|
|
22
|
+
socket.on('close', () => {
|
|
23
|
+
this.connections.delete(did); // Immediate evaporation
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
else if (socket.addEventListener) {
|
|
27
|
+
// Browser WebSocket style
|
|
28
|
+
socket.addEventListener('close', () => {
|
|
29
|
+
this.connections.delete(did);
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Check if agent is currently online.
|
|
35
|
+
*/
|
|
36
|
+
isOnline(did) {
|
|
37
|
+
return this.connections.has(did);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Get WebSocket for online agent (or null if offline).
|
|
41
|
+
*/
|
|
42
|
+
getSocket(did) {
|
|
43
|
+
return this.connections.get(did) ?? null;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Get all online DIDs.
|
|
47
|
+
*/
|
|
48
|
+
getOnlineDids() {
|
|
49
|
+
return Array.from(this.connections.keys());
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -1 +1,116 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Gateway transport adapter for delivering envelopes to Xail inbox users.
|
|
3
|
+
*
|
|
4
|
+
* Sends envelopes via POST /gateway/deliver on the hosted gateway API.
|
|
5
|
+
* For agent-to-agent (M2M), use HttpsTransportAdapter with direct URLs.
|
|
6
|
+
* Optionally polls for incoming messages.
|
|
7
|
+
*/
|
|
8
|
+
import { ok, err } from"./_deps/shared/index.js";
|
|
9
|
+
/**
|
|
10
|
+
* Transport adapter for the Xail Gateway.
|
|
11
|
+
*
|
|
12
|
+
* Sends envelopes via POST /gateway/deliver with API key auth.
|
|
13
|
+
* Optionally polls GET /gateway/messages/:did for incoming messages.
|
|
14
|
+
*/
|
|
15
|
+
export class GatewayTransport {
|
|
16
|
+
apiKey;
|
|
17
|
+
gateway;
|
|
18
|
+
timeoutMs;
|
|
19
|
+
fetchFn;
|
|
20
|
+
pollIntervalMs;
|
|
21
|
+
handlers = [];
|
|
22
|
+
pollTimer = null;
|
|
23
|
+
senderDid = null;
|
|
24
|
+
constructor(opts) {
|
|
25
|
+
this.apiKey = opts.apiKey;
|
|
26
|
+
this.gateway = opts.gateway.replace(/\/$/, '');
|
|
27
|
+
this.timeoutMs = opts.timeoutMs ?? 15_000;
|
|
28
|
+
this.fetchFn = opts.fetch ?? globalThis.fetch.bind(globalThis);
|
|
29
|
+
this.pollIntervalMs = opts.pollIntervalMs ?? 0;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Send a signed envelope to a recipient via the gateway.
|
|
33
|
+
* @param envelope - The signed transport envelope.
|
|
34
|
+
* @param _recipientDid - Recipient DID (already in envelope.recipient).
|
|
35
|
+
* @returns Success or transport error.
|
|
36
|
+
*/
|
|
37
|
+
async send(envelope, _recipientDid) {
|
|
38
|
+
const url = `${this.gateway}/gateway/deliver`;
|
|
39
|
+
try {
|
|
40
|
+
const controller = new AbortController();
|
|
41
|
+
const timer = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
42
|
+
const response = await this.fetchFn(url, {
|
|
43
|
+
method: 'POST',
|
|
44
|
+
headers: {
|
|
45
|
+
'Content-Type': 'application/json',
|
|
46
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
47
|
+
},
|
|
48
|
+
body: JSON.stringify(envelope),
|
|
49
|
+
signal: controller.signal,
|
|
50
|
+
});
|
|
51
|
+
clearTimeout(timer);
|
|
52
|
+
if (!response.ok) {
|
|
53
|
+
return err(response.status === 404
|
|
54
|
+
? 'RECIPIENT_UNREACHABLE'
|
|
55
|
+
: 'SEND_FAILED');
|
|
56
|
+
}
|
|
57
|
+
return ok(undefined);
|
|
58
|
+
}
|
|
59
|
+
catch (e) {
|
|
60
|
+
if (e instanceof DOMException && e.name === 'AbortError') {
|
|
61
|
+
return err('TIMEOUT');
|
|
62
|
+
}
|
|
63
|
+
return err('NETWORK_ERROR');
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/** Register a handler for incoming envelopes. */
|
|
67
|
+
onReceive(handler) {
|
|
68
|
+
this.handlers.push(handler);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Start polling for incoming messages.
|
|
72
|
+
* Must be called after Agent.create() sets the sender DID.
|
|
73
|
+
* @param did - The agent's own DID for polling.
|
|
74
|
+
*/
|
|
75
|
+
startPolling(did) {
|
|
76
|
+
this.senderDid = did;
|
|
77
|
+
if (this.pollIntervalMs > 0 && !this.pollTimer) {
|
|
78
|
+
this.pollTimer = setInterval(() => void this.poll(), this.pollIntervalMs);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/** Shut down the transport (stop polling, clear handlers). */
|
|
82
|
+
dispose() {
|
|
83
|
+
if (this.pollTimer) {
|
|
84
|
+
clearInterval(this.pollTimer);
|
|
85
|
+
this.pollTimer = null;
|
|
86
|
+
}
|
|
87
|
+
this.handlers = [];
|
|
88
|
+
}
|
|
89
|
+
/** Poll for incoming messages and dispatch to handlers. */
|
|
90
|
+
async poll() {
|
|
91
|
+
if (!this.senderDid)
|
|
92
|
+
return;
|
|
93
|
+
try {
|
|
94
|
+
const url = `${this.gateway}/gateway/messages/${encodeURIComponent(this.senderDid)}`;
|
|
95
|
+
const res = await this.fetchFn(url, {
|
|
96
|
+
headers: { Authorization: `Bearer ${this.apiKey}` },
|
|
97
|
+
});
|
|
98
|
+
if (!res.ok)
|
|
99
|
+
return;
|
|
100
|
+
const data = (await res.json());
|
|
101
|
+
for (const msg of data.messages) {
|
|
102
|
+
for (const handler of this.handlers) {
|
|
103
|
+
handler(msg.envelope);
|
|
104
|
+
}
|
|
105
|
+
// Ack delivery
|
|
106
|
+
await this.fetchFn(`${this.gateway}/gateway/ack/${msg.id}`, {
|
|
107
|
+
method: 'POST',
|
|
108
|
+
headers: { Authorization: `Bearer ${this.apiKey}` },
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
// Silent failure — polling errors are non-fatal
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|