@sygnl/talon 1.0.2 → 1.0.4
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/dist/index.cjs +130 -293
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +83 -203
- package/dist/index.d.ts +83 -203
- package/dist/index.js +0 -195
- package/dist/transports.cjs +347 -0
- package/dist/transports.cjs.map +1 -0
- package/dist/transports.d.cts +173 -0
- package/dist/transports.d.ts +173 -0
- package/dist/transports.js +344 -0
- package/dist/transports.js.map +1 -0
- package/dist/types-DsDnVD2W.d.cts +87 -0
- package/dist/types-DsDnVD2W.d.ts +87 -0
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1,347 +1,184 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
// src/
|
|
4
|
-
var
|
|
3
|
+
// src/TalonClient.ts
|
|
4
|
+
var TalonClient = class {
|
|
5
5
|
constructor(options) {
|
|
6
|
-
this.
|
|
7
|
-
this.
|
|
8
|
-
this.reconnectAttempts = 0;
|
|
9
|
-
this.reconnectTimeout = null;
|
|
10
|
-
this.messageHandlers = /* @__PURE__ */ new Set();
|
|
11
|
-
this.stateHandlers = /* @__PURE__ */ new Set();
|
|
12
|
-
this.errorHandlers = /* @__PURE__ */ new Set();
|
|
13
|
-
this.messageQueue = [];
|
|
14
|
-
this.url = options.url;
|
|
15
|
-
this.autoReconnect = options.autoReconnect ?? true;
|
|
16
|
-
this.reconnectDelay = options.reconnectDelay ?? 1e3;
|
|
17
|
-
this.maxReconnectAttempts = options.maxReconnectAttempts ?? 0;
|
|
18
|
-
this.connectionTimeout = options.connectionTimeout ?? 1e4;
|
|
6
|
+
this.transport = options.transport;
|
|
7
|
+
this.eventHandlers = /* @__PURE__ */ new Map();
|
|
19
8
|
this.debug = options.debug ?? false;
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
this.log("Already connected or connecting");
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
this.setState("connecting");
|
|
30
|
-
this.log(`Connecting to ${this.url}`);
|
|
31
|
-
return new Promise((resolve, reject) => {
|
|
32
|
-
try {
|
|
33
|
-
this.ws = new WebSocket(this.url);
|
|
34
|
-
const timeout = setTimeout(() => {
|
|
35
|
-
reject(new Error("Connection timeout"));
|
|
36
|
-
this.ws?.close();
|
|
37
|
-
}, this.connectionTimeout);
|
|
38
|
-
this.ws.onopen = () => {
|
|
39
|
-
clearTimeout(timeout);
|
|
40
|
-
this.setState("connected");
|
|
41
|
-
this.reconnectAttempts = 0;
|
|
42
|
-
this.log("Connected");
|
|
43
|
-
this.flushMessageQueue();
|
|
44
|
-
resolve();
|
|
45
|
-
};
|
|
46
|
-
this.ws.onerror = () => {
|
|
47
|
-
clearTimeout(timeout);
|
|
48
|
-
const error = new Error("WebSocket connection error");
|
|
49
|
-
this.log("Connection error:", error);
|
|
50
|
-
this.emitError(error);
|
|
51
|
-
reject(error);
|
|
52
|
-
};
|
|
53
|
-
this.ws.onclose = () => {
|
|
54
|
-
clearTimeout(timeout);
|
|
55
|
-
this.handleDisconnect();
|
|
56
|
-
};
|
|
57
|
-
this.ws.onmessage = (event) => {
|
|
58
|
-
this.handleMessage(event.data);
|
|
59
|
-
};
|
|
60
|
-
} catch (error) {
|
|
61
|
-
this.setState("error");
|
|
62
|
-
const err = error instanceof Error ? error : new Error("Unknown error");
|
|
63
|
-
this.emitError(err);
|
|
64
|
-
reject(err);
|
|
65
|
-
}
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
disconnect() {
|
|
69
|
-
this.log("Disconnecting");
|
|
70
|
-
this.autoReconnect = false;
|
|
71
|
-
if (this.reconnectTimeout) {
|
|
72
|
-
clearTimeout(this.reconnectTimeout);
|
|
73
|
-
this.reconnectTimeout = null;
|
|
74
|
-
}
|
|
75
|
-
if (this.ws) {
|
|
76
|
-
this.ws.close();
|
|
77
|
-
this.ws = null;
|
|
78
|
-
}
|
|
79
|
-
this.setState("disconnected");
|
|
80
|
-
}
|
|
81
|
-
async send(message) {
|
|
82
|
-
if (this._state !== "connected" || !this.ws) {
|
|
83
|
-
this.log("Not connected, queueing message");
|
|
84
|
-
this.messageQueue.push(message);
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
try {
|
|
88
|
-
const payload = JSON.stringify(message);
|
|
89
|
-
this.ws.send(payload);
|
|
90
|
-
this.log("Sent message:", message);
|
|
91
|
-
} catch (error) {
|
|
92
|
-
const err = error instanceof Error ? error : new Error("Send failed");
|
|
93
|
-
this.log("Send error:", err);
|
|
94
|
-
this.emitError(err);
|
|
95
|
-
throw err;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
onMessage(handler) {
|
|
99
|
-
this.messageHandlers.add(handler);
|
|
100
|
-
return () => this.messageHandlers.delete(handler);
|
|
101
|
-
}
|
|
102
|
-
onStateChange(handler) {
|
|
103
|
-
this.stateHandlers.add(handler);
|
|
104
|
-
return () => this.stateHandlers.delete(handler);
|
|
105
|
-
}
|
|
106
|
-
onError(handler) {
|
|
107
|
-
this.errorHandlers.add(handler);
|
|
108
|
-
return () => this.errorHandlers.delete(handler);
|
|
109
|
-
}
|
|
110
|
-
/**
|
|
111
|
-
* Handle incoming WebSocket message
|
|
112
|
-
*/
|
|
113
|
-
handleMessage(data) {
|
|
114
|
-
try {
|
|
115
|
-
const message = JSON.parse(data);
|
|
116
|
-
this.log("Received message:", message);
|
|
117
|
-
this.messageHandlers.forEach((handler) => {
|
|
118
|
-
try {
|
|
119
|
-
handler(message);
|
|
120
|
-
} catch (error) {
|
|
121
|
-
this.log("Handler error:", error);
|
|
122
|
-
}
|
|
9
|
+
this.messageUnsubscribe = this.transport.onMessage(this.handleMessage.bind(this));
|
|
10
|
+
this.stateUnsubscribe = this.transport.onStateChange(this.handleStateChange.bind(this));
|
|
11
|
+
this.errorUnsubscribe = this.transport.onError(this.handleError.bind(this));
|
|
12
|
+
if (options.autoConnect !== false) {
|
|
13
|
+
this.connect().catch((error) => {
|
|
14
|
+
this.log("Auto-connect failed:", error);
|
|
123
15
|
});
|
|
124
|
-
} catch (error) {
|
|
125
|
-
this.log("Failed to parse message:", error);
|
|
126
|
-
this.emitError(new Error("Invalid message format"));
|
|
127
16
|
}
|
|
128
17
|
}
|
|
129
18
|
/**
|
|
130
|
-
*
|
|
19
|
+
* Connect to the edge
|
|
131
20
|
*/
|
|
132
|
-
|
|
133
|
-
this.
|
|
134
|
-
this.ws = null;
|
|
135
|
-
if (this.autoReconnect && (this.maxReconnectAttempts === 0 || this.reconnectAttempts < this.maxReconnectAttempts)) {
|
|
136
|
-
this.setState("reconnecting");
|
|
137
|
-
this.scheduleReconnect();
|
|
138
|
-
} else {
|
|
139
|
-
this.setState("disconnected");
|
|
140
|
-
}
|
|
21
|
+
async connect() {
|
|
22
|
+
return this.transport.connect();
|
|
141
23
|
}
|
|
142
24
|
/**
|
|
143
|
-
*
|
|
25
|
+
* Disconnect from the edge
|
|
144
26
|
*/
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
this.
|
|
148
|
-
this.reconnectTimeout = setTimeout(() => {
|
|
149
|
-
this.reconnectAttempts++;
|
|
150
|
-
this.connect().catch((error) => {
|
|
151
|
-
this.log("Reconnect failed:", error);
|
|
152
|
-
this.handleDisconnect();
|
|
153
|
-
});
|
|
154
|
-
}, delay);
|
|
27
|
+
disconnect() {
|
|
28
|
+
this.transport.disconnect();
|
|
29
|
+
this.cleanup();
|
|
155
30
|
}
|
|
156
31
|
/**
|
|
157
|
-
*
|
|
32
|
+
* Send event TO the edge
|
|
33
|
+
*
|
|
34
|
+
* @param event - Event data to send
|
|
35
|
+
* @returns Promise that resolves when sent
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```typescript
|
|
39
|
+
* await talon.send({
|
|
40
|
+
* event: 'add_to_cart',
|
|
41
|
+
* product_id: 'prod_123',
|
|
42
|
+
* price: 99.99
|
|
43
|
+
* });
|
|
44
|
+
* ```
|
|
158
45
|
*/
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
this.log("Failed to flush message:", error);
|
|
169
|
-
});
|
|
170
|
-
});
|
|
46
|
+
async send(event) {
|
|
47
|
+
const message = {
|
|
48
|
+
type: "event",
|
|
49
|
+
data: event,
|
|
50
|
+
id: this.generateId(),
|
|
51
|
+
timestamp: Date.now()
|
|
52
|
+
};
|
|
53
|
+
this.log("Sending message:", message);
|
|
54
|
+
return this.transport.send(message);
|
|
171
55
|
}
|
|
172
56
|
/**
|
|
173
|
-
*
|
|
57
|
+
* Subscribe to events FROM the edge
|
|
58
|
+
*
|
|
59
|
+
* @param eventType - Event type to listen for
|
|
60
|
+
* @param handler - Function to call when event received
|
|
61
|
+
* @returns Unsubscribe function
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```typescript
|
|
65
|
+
* const unsubscribe = talon.on('attribution_updated', (data) => {
|
|
66
|
+
* console.log('Attribution:', data);
|
|
67
|
+
* });
|
|
68
|
+
*
|
|
69
|
+
* // Later: unsubscribe()
|
|
70
|
+
* ```
|
|
174
71
|
*/
|
|
175
|
-
|
|
176
|
-
if (this.
|
|
177
|
-
|
|
178
|
-
}
|
|
179
|
-
this.
|
|
180
|
-
this.
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
72
|
+
on(eventType, handler) {
|
|
73
|
+
if (!this.eventHandlers.has(eventType)) {
|
|
74
|
+
this.eventHandlers.set(eventType, /* @__PURE__ */ new Set());
|
|
75
|
+
}
|
|
76
|
+
this.eventHandlers.get(eventType).add(handler);
|
|
77
|
+
this.log(`Subscribed to event: ${eventType}`);
|
|
78
|
+
return () => {
|
|
79
|
+
const handlers = this.eventHandlers.get(eventType);
|
|
80
|
+
if (handlers) {
|
|
81
|
+
handlers.delete(handler);
|
|
82
|
+
if (handlers.size === 0) {
|
|
83
|
+
this.eventHandlers.delete(eventType);
|
|
84
|
+
}
|
|
185
85
|
}
|
|
186
|
-
|
|
86
|
+
this.log(`Unsubscribed from event: ${eventType}`);
|
|
87
|
+
};
|
|
187
88
|
}
|
|
188
89
|
/**
|
|
189
|
-
*
|
|
90
|
+
* Subscribe to event once (auto-unsubscribe after first call)
|
|
190
91
|
*/
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
});
|
|
92
|
+
once(eventType, handler) {
|
|
93
|
+
const wrappedHandler = (data) => {
|
|
94
|
+
handler(data);
|
|
95
|
+
unsubscribe();
|
|
96
|
+
};
|
|
97
|
+
const unsubscribe = this.on(eventType, wrappedHandler);
|
|
98
|
+
return unsubscribe;
|
|
199
99
|
}
|
|
200
100
|
/**
|
|
201
|
-
*
|
|
101
|
+
* Remove all handlers for an event type
|
|
202
102
|
*/
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
};
|
|
209
|
-
|
|
210
|
-
// src/transports/WebhookTransport.ts
|
|
211
|
-
var WebhookTransport = class {
|
|
212
|
-
constructor(options) {
|
|
213
|
-
this._state = "disconnected";
|
|
214
|
-
this.messageHandlers = /* @__PURE__ */ new Set();
|
|
215
|
-
this.stateHandlers = /* @__PURE__ */ new Set();
|
|
216
|
-
this.errorHandlers = /* @__PURE__ */ new Set();
|
|
217
|
-
this.url = options.url;
|
|
218
|
-
this.method = options.method ?? "POST";
|
|
219
|
-
this.headers = {
|
|
220
|
-
"Content-Type": "application/json",
|
|
221
|
-
...options.headers
|
|
222
|
-
};
|
|
223
|
-
this.timeout = options.timeout ?? 1e4;
|
|
224
|
-
this.retry = options.retry ?? false;
|
|
225
|
-
this.maxRetries = options.maxRetries ?? 3;
|
|
226
|
-
this.retryDelay = options.retryDelay ?? 1e3;
|
|
227
|
-
this.debug = options.debug ?? false;
|
|
103
|
+
off(eventType) {
|
|
104
|
+
this.eventHandlers.delete(eventType);
|
|
105
|
+
this.log(`Removed all handlers for: ${eventType}`);
|
|
228
106
|
}
|
|
107
|
+
/**
|
|
108
|
+
* Get current connection state
|
|
109
|
+
*/
|
|
229
110
|
get state() {
|
|
230
|
-
return this.
|
|
231
|
-
}
|
|
232
|
-
async connect() {
|
|
233
|
-
this.log("Webhook transport ready");
|
|
234
|
-
this.setState("connected");
|
|
111
|
+
return this.transport.state;
|
|
235
112
|
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
113
|
+
/**
|
|
114
|
+
* Check if connected
|
|
115
|
+
*/
|
|
116
|
+
get connected() {
|
|
117
|
+
return this.transport.state === "connected";
|
|
239
118
|
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
const
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
lastError = error instanceof Error ? error : new Error("Send failed");
|
|
253
|
-
this.log(`Send failed (attempt ${attempt + 1}):`, lastError);
|
|
254
|
-
if (attempt < attempts - 1) {
|
|
255
|
-
const delay = this.retryDelay * Math.pow(2, attempt);
|
|
256
|
-
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
119
|
+
/**
|
|
120
|
+
* Handle incoming message from transport
|
|
121
|
+
*/
|
|
122
|
+
handleMessage(message) {
|
|
123
|
+
this.log("Received message:", message);
|
|
124
|
+
const handlers = this.eventHandlers.get(message.type);
|
|
125
|
+
if (handlers && handlers.size > 0) {
|
|
126
|
+
handlers.forEach((handler) => {
|
|
127
|
+
try {
|
|
128
|
+
handler(message.data);
|
|
129
|
+
} catch (error) {
|
|
130
|
+
this.log(`Error in handler for ${message.type}:`, error);
|
|
257
131
|
}
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
this.emitError(lastError);
|
|
262
|
-
throw lastError;
|
|
132
|
+
});
|
|
133
|
+
} else {
|
|
134
|
+
this.log(`No handlers for message type: ${message.type}`);
|
|
263
135
|
}
|
|
264
136
|
}
|
|
265
|
-
onMessage(handler) {
|
|
266
|
-
this.messageHandlers.add(handler);
|
|
267
|
-
return () => this.messageHandlers.delete(handler);
|
|
268
|
-
}
|
|
269
|
-
onStateChange(handler) {
|
|
270
|
-
this.stateHandlers.add(handler);
|
|
271
|
-
return () => this.stateHandlers.delete(handler);
|
|
272
|
-
}
|
|
273
|
-
onError(handler) {
|
|
274
|
-
this.errorHandlers.add(handler);
|
|
275
|
-
return () => this.errorHandlers.delete(handler);
|
|
276
|
-
}
|
|
277
137
|
/**
|
|
278
|
-
*
|
|
138
|
+
* Handle transport state change
|
|
279
139
|
*/
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
const
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
method: this.method,
|
|
286
|
-
headers: this.headers,
|
|
287
|
-
body: JSON.stringify(message),
|
|
288
|
-
signal: controller.signal
|
|
289
|
-
});
|
|
290
|
-
clearTimeout(timeoutId);
|
|
291
|
-
if (!response.ok) {
|
|
292
|
-
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
293
|
-
}
|
|
294
|
-
this.log(`Request successful: ${response.status}`);
|
|
295
|
-
} catch (error) {
|
|
296
|
-
clearTimeout(timeoutId);
|
|
297
|
-
if (error instanceof Error) {
|
|
298
|
-
if (error.name === "AbortError") {
|
|
299
|
-
throw new Error("Request timeout");
|
|
300
|
-
}
|
|
301
|
-
throw error;
|
|
302
|
-
}
|
|
303
|
-
throw new Error("Request failed");
|
|
140
|
+
handleStateChange(state) {
|
|
141
|
+
this.log(`State changed: ${state}`);
|
|
142
|
+
const handlers = this.eventHandlers.get("_state_change");
|
|
143
|
+
if (handlers) {
|
|
144
|
+
handlers.forEach((handler) => handler({ state }));
|
|
304
145
|
}
|
|
305
146
|
}
|
|
306
147
|
/**
|
|
307
|
-
*
|
|
148
|
+
* Handle transport error
|
|
308
149
|
*/
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
150
|
+
handleError(error) {
|
|
151
|
+
this.log("Transport error:", error);
|
|
152
|
+
const handlers = this.eventHandlers.get("_error");
|
|
153
|
+
if (handlers) {
|
|
154
|
+
handlers.forEach((handler) => handler({ error }));
|
|
312
155
|
}
|
|
313
|
-
this._state = state;
|
|
314
|
-
this.stateHandlers.forEach((handler) => {
|
|
315
|
-
try {
|
|
316
|
-
handler(state);
|
|
317
|
-
} catch (error) {
|
|
318
|
-
this.log("State handler error:", error);
|
|
319
|
-
}
|
|
320
|
-
});
|
|
321
156
|
}
|
|
322
157
|
/**
|
|
323
|
-
*
|
|
158
|
+
* Cleanup subscriptions
|
|
324
159
|
*/
|
|
325
|
-
|
|
326
|
-
this.
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
160
|
+
cleanup() {
|
|
161
|
+
this.messageUnsubscribe?.();
|
|
162
|
+
this.stateUnsubscribe?.();
|
|
163
|
+
this.errorUnsubscribe?.();
|
|
164
|
+
this.eventHandlers.clear();
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Generate unique message ID
|
|
168
|
+
*/
|
|
169
|
+
generateId() {
|
|
170
|
+
return `msg_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
333
171
|
}
|
|
334
172
|
/**
|
|
335
173
|
* Debug logging
|
|
336
174
|
*/
|
|
337
175
|
log(message, data) {
|
|
338
176
|
if (this.debug && typeof console !== "undefined") {
|
|
339
|
-
console.log(`[
|
|
177
|
+
console.log(`[TalonClient] ${message}`, data || "");
|
|
340
178
|
}
|
|
341
179
|
}
|
|
342
180
|
};
|
|
343
181
|
|
|
344
|
-
exports.
|
|
345
|
-
exports.WebhookTransport = WebhookTransport;
|
|
182
|
+
exports.TalonClient = TalonClient;
|
|
346
183
|
//# sourceMappingURL=index.cjs.map
|
|
347
184
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/transports/WebSocketTransport.ts","../src/transports/WebhookTransport.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["import type {\n TalonTransport,\n OutboundMessage,\n InboundMessage,\n TransportState,\n} from '../types';\n\n/**\n * WebSocket transport options\n */\nexport interface WebSocketTransportOptions {\n /** WebSocket URL */\n url: string;\n \n /** Auto-reconnect on disconnect */\n autoReconnect?: boolean;\n \n /** Reconnect delay in ms */\n reconnectDelay?: number;\n \n /** Max reconnect attempts (0 = infinite) */\n maxReconnectAttempts?: number;\n \n /** Connection timeout in ms */\n connectionTimeout?: number;\n \n /** Debug mode */\n debug?: boolean;\n}\n\n/**\n * WebSocket transport for bidirectional communication\n * \n * Features:\n * - Real-time bidirectional communication\n * - Auto-reconnect with exponential backoff\n * - Connection state management\n * - Message queuing during disconnection\n * \n * @example\n * ```typescript\n * const transport = new WebSocketTransport({\n * url: 'wss://edge.sygnl.io',\n * autoReconnect: true\n * });\n * ```\n */\nexport class WebSocketTransport implements TalonTransport {\n private url: string;\n private ws: WebSocket | null = null;\n private _state: TransportState = 'disconnected';\n private autoReconnect: boolean;\n private reconnectDelay: number;\n private maxReconnectAttempts: number;\n private reconnectAttempts = 0;\n private reconnectTimeout: ReturnType<typeof setTimeout> | null = null;\n private connectionTimeout: number;\n private debug: boolean;\n \n private messageHandlers = new Set<(message: InboundMessage) => void>();\n private stateHandlers = new Set<(state: TransportState) => void>();\n private errorHandlers = new Set<(error: Error) => void>();\n private messageQueue: OutboundMessage[] = [];\n\n constructor(options: WebSocketTransportOptions) {\n this.url = options.url;\n this.autoReconnect = options.autoReconnect ?? true;\n this.reconnectDelay = options.reconnectDelay ?? 1000;\n this.maxReconnectAttempts = options.maxReconnectAttempts ?? 0; // 0 = infinite\n this.connectionTimeout = options.connectionTimeout ?? 10000;\n this.debug = options.debug ?? false;\n }\n\n public get state(): TransportState {\n return this._state;\n }\n\n public async connect(): Promise<void> {\n if (this._state === 'connected' || this._state === 'connecting') {\n this.log('Already connected or connecting');\n return;\n }\n\n this.setState('connecting');\n this.log(`Connecting to ${this.url}`);\n\n return new Promise((resolve, reject) => {\n try {\n this.ws = new WebSocket(this.url);\n\n const timeout = setTimeout(() => {\n reject(new Error('Connection timeout'));\n this.ws?.close();\n }, this.connectionTimeout);\n\n this.ws.onopen = () => {\n clearTimeout(timeout);\n this.setState('connected');\n this.reconnectAttempts = 0;\n this.log('Connected');\n \n // Flush queued messages\n this.flushMessageQueue();\n \n resolve();\n };\n\n this.ws.onerror = () => {\n clearTimeout(timeout);\n const error = new Error('WebSocket connection error');\n this.log('Connection error:', error);\n this.emitError(error);\n reject(error);\n };\n\n this.ws.onclose = () => {\n clearTimeout(timeout);\n this.handleDisconnect();\n };\n\n this.ws.onmessage = (event) => {\n this.handleMessage(event.data);\n };\n } catch (error) {\n this.setState('error');\n const err = error instanceof Error ? error : new Error('Unknown error');\n this.emitError(err);\n reject(err);\n }\n });\n }\n\n public disconnect(): void {\n this.log('Disconnecting');\n this.autoReconnect = false; // Disable auto-reconnect on manual disconnect\n \n if (this.reconnectTimeout) {\n clearTimeout(this.reconnectTimeout);\n this.reconnectTimeout = null;\n }\n\n if (this.ws) {\n this.ws.close();\n this.ws = null;\n }\n\n this.setState('disconnected');\n }\n\n public async send(message: OutboundMessage): Promise<void> {\n if (this._state !== 'connected' || !this.ws) {\n // Queue message if not connected\n this.log('Not connected, queueing message');\n this.messageQueue.push(message);\n return;\n }\n\n try {\n const payload = JSON.stringify(message);\n this.ws.send(payload);\n this.log('Sent message:', message);\n } catch (error) {\n const err = error instanceof Error ? error : new Error('Send failed');\n this.log('Send error:', err);\n this.emitError(err);\n throw err;\n }\n }\n\n public onMessage(handler: (message: InboundMessage) => void): () => void {\n this.messageHandlers.add(handler);\n return () => this.messageHandlers.delete(handler);\n }\n\n public onStateChange(handler: (state: TransportState) => void): () => void {\n this.stateHandlers.add(handler);\n return () => this.stateHandlers.delete(handler);\n }\n\n public onError(handler: (error: Error) => void): () => void {\n this.errorHandlers.add(handler);\n return () => this.errorHandlers.delete(handler);\n }\n\n /**\n * Handle incoming WebSocket message\n */\n private handleMessage(data: string): void {\n try {\n const message: InboundMessage = JSON.parse(data);\n this.log('Received message:', message);\n \n this.messageHandlers.forEach((handler) => {\n try {\n handler(message);\n } catch (error) {\n this.log('Handler error:', error);\n }\n });\n } catch (error) {\n this.log('Failed to parse message:', error);\n this.emitError(new Error('Invalid message format'));\n }\n }\n\n /**\n * Handle WebSocket disconnect\n */\n private handleDisconnect(): void {\n this.log('Disconnected');\n this.ws = null;\n\n if (this.autoReconnect && \n (this.maxReconnectAttempts === 0 || this.reconnectAttempts < this.maxReconnectAttempts)) {\n this.setState('reconnecting');\n this.scheduleReconnect();\n } else {\n this.setState('disconnected');\n }\n }\n\n /**\n * Schedule reconnection attempt with exponential backoff\n */\n private scheduleReconnect(): void {\n const delay = this.reconnectDelay * Math.pow(2, this.reconnectAttempts);\n this.log(`Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts + 1})`);\n\n this.reconnectTimeout = setTimeout(() => {\n this.reconnectAttempts++;\n this.connect().catch((error) => {\n this.log('Reconnect failed:', error);\n this.handleDisconnect();\n });\n }, delay);\n }\n\n /**\n * Flush queued messages\n */\n private flushMessageQueue(): void {\n if (this.messageQueue.length === 0) {\n return;\n }\n\n this.log(`Flushing ${this.messageQueue.length} queued messages`);\n const queue = [...this.messageQueue];\n this.messageQueue = [];\n\n queue.forEach((message) => {\n this.send(message).catch((error) => {\n this.log('Failed to flush message:', error);\n });\n });\n }\n\n /**\n * Set connection state\n */\n private setState(state: TransportState): void {\n if (this._state === state) {\n return;\n }\n\n this._state = state;\n this.stateHandlers.forEach((handler) => {\n try {\n handler(state);\n } catch (error) {\n this.log('State handler error:', error);\n }\n });\n }\n\n /**\n * Emit error to handlers\n */\n private emitError(error: Error): void {\n this.errorHandlers.forEach((handler) => {\n try {\n handler(error);\n } catch (err) {\n this.log('Error handler error:', err);\n }\n });\n }\n\n /**\n * Debug logging\n */\n private log(message: string, data?: unknown): void {\n if (this.debug && typeof console !== 'undefined') {\n console.log(`[WebSocketTransport] ${message}`, data || '');\n }\n }\n}\n","import type {\n TalonTransport,\n OutboundMessage,\n InboundMessage,\n TransportState,\n} from '../types';\n\n/**\n * Webhook transport options\n */\nexport interface WebhookTransportOptions {\n /** Webhook URL */\n url: string;\n \n /** HTTP method */\n method?: 'POST' | 'PUT';\n \n /** Custom headers */\n headers?: Record<string, string>;\n \n /** Request timeout in ms */\n timeout?: number;\n \n /** Retry failed requests */\n retry?: boolean;\n \n /** Max retry attempts */\n maxRetries?: number;\n \n /** Retry delay in ms */\n retryDelay?: number;\n \n /** Debug mode */\n debug?: boolean;\n}\n\n/**\n * Webhook transport for fire-and-forget HTTP requests\n * \n * Features:\n * - One-way communication (no responses)\n * - HTTP POST/PUT requests\n * - Optional retry logic\n * - Custom headers support\n * \n * Use this for:\n * - Server-side event delivery\n * - Fire-and-forget scenarios\n * - When WebSocket not available/needed\n * \n * @example\n * ```typescript\n * const transport = new WebhookTransport({\n * url: 'https://edge.sygnl.io/webhook',\n * method: 'POST',\n * retry: true\n * });\n * ```\n */\nexport class WebhookTransport implements TalonTransport {\n private url: string;\n private method: 'POST' | 'PUT';\n private headers: Record<string, string>;\n private timeout: number;\n private retry: boolean;\n private maxRetries: number;\n private retryDelay: number;\n private debug: boolean;\n private _state: TransportState = 'disconnected';\n \n private messageHandlers = new Set<(message: InboundMessage) => void>();\n private stateHandlers = new Set<(state: TransportState) => void>();\n private errorHandlers = new Set<(error: Error) => void>();\n\n constructor(options: WebhookTransportOptions) {\n this.url = options.url;\n this.method = options.method ?? 'POST';\n this.headers = {\n 'Content-Type': 'application/json',\n ...options.headers,\n };\n this.timeout = options.timeout ?? 10000;\n this.retry = options.retry ?? false;\n this.maxRetries = options.maxRetries ?? 3;\n this.retryDelay = options.retryDelay ?? 1000;\n this.debug = options.debug ?? false;\n }\n\n public get state(): TransportState {\n return this._state;\n }\n\n public async connect(): Promise<void> {\n // Webhook is \"connected\" immediately (stateless)\n this.log('Webhook transport ready');\n this.setState('connected');\n }\n\n public disconnect(): void {\n this.log('Disconnecting webhook transport');\n this.setState('disconnected');\n }\n\n public async send(message: OutboundMessage): Promise<void> {\n if (this._state !== 'connected') {\n throw new Error('Transport not connected');\n }\n\n let lastError: Error | null = null;\n const attempts = this.retry ? this.maxRetries + 1 : 1;\n\n for (let attempt = 0; attempt < attempts; attempt++) {\n try {\n await this.sendRequest(message);\n this.log(`Sent message (attempt ${attempt + 1})`);\n return;\n } catch (error) {\n lastError = error instanceof Error ? error : new Error('Send failed');\n this.log(`Send failed (attempt ${attempt + 1}):`, lastError);\n\n if (attempt < attempts - 1) {\n // Wait before retry (exponential backoff)\n const delay = this.retryDelay * Math.pow(2, attempt);\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n }\n }\n\n // All attempts failed\n if (lastError) {\n this.emitError(lastError);\n throw lastError;\n }\n }\n\n public onMessage(handler: (message: InboundMessage) => void): () => void {\n // Webhook is one-way, but keep interface consistent\n this.messageHandlers.add(handler);\n return () => this.messageHandlers.delete(handler);\n }\n\n public onStateChange(handler: (state: TransportState) => void): () => void {\n this.stateHandlers.add(handler);\n return () => this.stateHandlers.delete(handler);\n }\n\n public onError(handler: (error: Error) => void): () => void {\n this.errorHandlers.add(handler);\n return () => this.errorHandlers.delete(handler);\n }\n\n /**\n * Send HTTP request\n */\n private async sendRequest(message: OutboundMessage): Promise<void> {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n try {\n const response = await fetch(this.url, {\n method: this.method,\n headers: this.headers,\n body: JSON.stringify(message),\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n\n this.log(`Request successful: ${response.status}`);\n } catch (error) {\n clearTimeout(timeoutId);\n \n if (error instanceof Error) {\n if (error.name === 'AbortError') {\n throw new Error('Request timeout');\n }\n throw error;\n }\n \n throw new Error('Request failed');\n }\n }\n\n /**\n * Set connection state\n */\n private setState(state: TransportState): void {\n if (this._state === state) {\n return;\n }\n\n this._state = state;\n this.stateHandlers.forEach((handler) => {\n try {\n handler(state);\n } catch (error) {\n this.log('State handler error:', error);\n }\n });\n }\n\n /**\n * Emit error to handlers\n */\n private emitError(error: Error): void {\n this.errorHandlers.forEach((handler) => {\n try {\n handler(error);\n } catch (err) {\n this.log('Error handler error:', err);\n }\n });\n }\n\n /**\n * Debug logging\n */\n private log(message: string, data?: unknown): void {\n if (this.debug && typeof console !== 'undefined') {\n console.log(`[WebhookTransport] ${message}`, data || '');\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/TalonClient.ts"],"names":[],"mappings":";;;AAoCO,IAAM,cAAN,MAAkB;AAAA,EAQvB,YAAY,OAAA,EAA6B;AACvC,IAAA,IAAA,CAAK,YAAY,OAAA,CAAQ,SAAA;AACzB,IAAA,IAAA,CAAK,aAAA,uBAAoB,GAAA,EAAI;AAC7B,IAAA,IAAA,CAAK,KAAA,GAAQ,QAAQ,KAAA,IAAS,KAAA;AAG9B,IAAA,IAAA,CAAK,kBAAA,GAAqB,KAAK,SAAA,CAAU,SAAA,CAAU,KAAK,aAAA,CAAc,IAAA,CAAK,IAAI,CAAC,CAAA;AAChF,IAAA,IAAA,CAAK,gBAAA,GAAmB,KAAK,SAAA,CAAU,aAAA,CAAc,KAAK,iBAAA,CAAkB,IAAA,CAAK,IAAI,CAAC,CAAA;AACtF,IAAA,IAAA,CAAK,gBAAA,GAAmB,KAAK,SAAA,CAAU,OAAA,CAAQ,KAAK,WAAA,CAAY,IAAA,CAAK,IAAI,CAAC,CAAA;AAG1E,IAAA,IAAI,OAAA,CAAQ,gBAAgB,KAAA,EAAO;AACjC,MAAA,IAAA,CAAK,OAAA,EAAQ,CAAE,KAAA,CAAM,CAAC,KAAA,KAAU;AAC9B,QAAA,IAAA,CAAK,GAAA,CAAI,wBAAwB,KAAK,CAAA;AAAA,MACxC,CAAC,CAAA;AAAA,IACH;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAa,OAAA,GAAyB;AACpC,IAAA,OAAO,IAAA,CAAK,UAAU,OAAA,EAAQ;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA,EAKO,UAAA,GAAmB;AACxB,IAAA,IAAA,CAAK,UAAU,UAAA,EAAW;AAC1B,IAAA,IAAA,CAAK,OAAA,EAAQ;AAAA,EACf;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiBA,MAAa,KAAK,KAAA,EAA+C;AAC/D,IAAA,MAAM,OAAA,GAA2B;AAAA,MAC/B,IAAA,EAAM,OAAA;AAAA,MACN,IAAA,EAAM,KAAA;AAAA,MACN,EAAA,EAAI,KAAK,UAAA,EAAW;AAAA,MACpB,SAAA,EAAW,KAAK,GAAA;AAAI,KACtB;AAEA,IAAA,IAAA,CAAK,GAAA,CAAI,oBAAoB,OAAO,CAAA;AACpC,IAAA,OAAO,IAAA,CAAK,SAAA,CAAU,IAAA,CAAK,OAAO,CAAA;AAAA,EACpC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkBO,EAAA,CAAY,WAAmB,OAAA,EAAsC;AAC1E,IAAA,IAAI,CAAC,IAAA,CAAK,aAAA,CAAc,GAAA,CAAI,SAAS,CAAA,EAAG;AACtC,MAAA,IAAA,CAAK,aAAA,CAAc,GAAA,CAAI,SAAA,kBAAW,IAAI,KAAK,CAAA;AAAA,IAC7C;AAEA,IAAA,IAAA,CAAK,aAAA,CAAc,GAAA,CAAI,SAAS,CAAA,CAAG,IAAI,OAAO,CAAA;AAC9C,IAAA,IAAA,CAAK,GAAA,CAAI,CAAA,qBAAA,EAAwB,SAAS,CAAA,CAAE,CAAA;AAG5C,IAAA,OAAO,MAAM;AACX,MAAA,MAAM,QAAA,GAAW,IAAA,CAAK,aAAA,CAAc,GAAA,CAAI,SAAS,CAAA;AACjD,MAAA,IAAI,QAAA,EAAU;AACZ,QAAA,QAAA,CAAS,OAAO,OAAO,CAAA;AACvB,QAAA,IAAI,QAAA,CAAS,SAAS,CAAA,EAAG;AACvB,UAAA,IAAA,CAAK,aAAA,CAAc,OAAO,SAAS,CAAA;AAAA,QACrC;AAAA,MACF;AACA,MAAA,IAAA,CAAK,GAAA,CAAI,CAAA,yBAAA,EAA4B,SAAS,CAAA,CAAE,CAAA;AAAA,IAClD,CAAA;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKO,IAAA,CAAc,WAAmB,OAAA,EAAsC;AAC5E,IAAA,MAAM,cAAA,GAAiB,CAAC,IAAA,KAAY;AAClC,MAAA,OAAA,CAAQ,IAAI,CAAA;AACZ,MAAA,WAAA,EAAY;AAAA,IACd,CAAA;AAEA,IAAA,MAAM,WAAA,GAAc,IAAA,CAAK,EAAA,CAAG,SAAA,EAAW,cAAc,CAAA;AACrD,IAAA,OAAO,WAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKO,IAAI,SAAA,EAAyB;AAClC,IAAA,IAAA,CAAK,aAAA,CAAc,OAAO,SAAS,CAAA;AACnC,IAAA,IAAA,CAAK,GAAA,CAAI,CAAA,0BAAA,EAA6B,SAAS,CAAA,CAAE,CAAA;AAAA,EACnD;AAAA;AAAA;AAAA;AAAA,EAKA,IAAW,KAAA,GAAwB;AACjC,IAAA,OAAO,KAAK,SAAA,CAAU,KAAA;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA,EAKA,IAAW,SAAA,GAAqB;AAC9B,IAAA,OAAO,IAAA,CAAK,UAAU,KAAA,KAAU,WAAA;AAAA,EAClC;AAAA;AAAA;AAAA;AAAA,EAKQ,cAAc,OAAA,EAA+B;AACnD,IAAA,IAAA,CAAK,GAAA,CAAI,qBAAqB,OAAO,CAAA;AAErC,IAAA,MAAM,QAAA,GAAW,IAAA,CAAK,aAAA,CAAc,GAAA,CAAI,QAAQ,IAAI,CAAA;AACpD,IAAA,IAAI,QAAA,IAAY,QAAA,CAAS,IAAA,GAAO,CAAA,EAAG;AACjC,MAAA,QAAA,CAAS,OAAA,CAAQ,CAAC,OAAA,KAAY;AAC5B,QAAA,IAAI;AACF,UAAA,OAAA,CAAQ,QAAQ,IAAI,CAAA;AAAA,QACtB,SAAS,KAAA,EAAO;AACd,UAAA,IAAA,CAAK,GAAA,CAAI,CAAA,qBAAA,EAAwB,OAAA,CAAQ,IAAI,KAAK,KAAK,CAAA;AAAA,QACzD;AAAA,MACF,CAAC,CAAA;AAAA,IACH,CAAA,MAAO;AACL,MAAA,IAAA,CAAK,GAAA,CAAI,CAAA,8BAAA,EAAiC,OAAA,CAAQ,IAAI,CAAA,CAAE,CAAA;AAAA,IAC1D;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,kBAAkB,KAAA,EAA6B;AACrD,IAAA,IAAA,CAAK,GAAA,CAAI,CAAA,eAAA,EAAkB,KAAK,CAAA,CAAE,CAAA;AAGlC,IAAA,MAAM,QAAA,GAAW,IAAA,CAAK,aAAA,CAAc,GAAA,CAAI,eAAe,CAAA;AACvD,IAAA,IAAI,QAAA,EAAU;AACZ,MAAA,QAAA,CAAS,QAAQ,CAAC,OAAA,KAAY,QAAQ,EAAE,KAAA,EAAO,CAAC,CAAA;AAAA,IAClD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,YAAY,KAAA,EAAoB;AACtC,IAAA,IAAA,CAAK,GAAA,CAAI,oBAAoB,KAAK,CAAA;AAGlC,IAAA,MAAM,QAAA,GAAW,IAAA,CAAK,aAAA,CAAc,GAAA,CAAI,QAAQ,CAAA;AAChD,IAAA,IAAI,QAAA,EAAU;AACZ,MAAA,QAAA,CAAS,QAAQ,CAAC,OAAA,KAAY,QAAQ,EAAE,KAAA,EAAO,CAAC,CAAA;AAAA,IAClD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,OAAA,GAAgB;AACtB,IAAA,IAAA,CAAK,kBAAA,IAAqB;AAC1B,IAAA,IAAA,CAAK,gBAAA,IAAmB;AACxB,IAAA,IAAA,CAAK,gBAAA,IAAmB;AACxB,IAAA,IAAA,CAAK,cAAc,KAAA,EAAM;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA,EAKQ,UAAA,GAAqB;AAC3B,IAAA,OAAO,CAAA,IAAA,EAAO,IAAA,CAAK,GAAA,EAAK,IAAI,IAAA,CAAK,MAAA,EAAO,CAAE,QAAA,CAAS,EAAE,CAAA,CAAE,MAAA,CAAO,CAAA,EAAG,CAAC,CAAC,CAAA,CAAA;AAAA,EACrE;AAAA;AAAA;AAAA;AAAA,EAKQ,GAAA,CAAI,SAAiB,IAAA,EAAsB;AACjD,IAAA,IAAI,IAAA,CAAK,KAAA,IAAS,OAAO,OAAA,KAAY,WAAA,EAAa;AAChD,MAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,cAAA,EAAiB,OAAO,CAAA,CAAA,EAAI,QAAQ,EAAE,CAAA;AAAA,IACpD;AAAA,EACF;AACF","file":"index.cjs","sourcesContent":["import type {\n TalonTransport,\n TalonClientOptions,\n OutboundMessage,\n InboundMessage,\n EventHandler,\n TransportState,\n} from './types';\n\n/**\n * TalonClient - Bidirectional event delivery client\n * \n * Features:\n * - Send events TO the edge\n * - Receive updates FROM the edge\n * - Transport-agnostic (WebSocket, Webhook, etc.)\n * - Event emitter pattern for edge responses\n * \n * @example\n * ```typescript\n * import { TalonClient } from '@sygnl/talon';\n * import { WebSocketTransport } from '@sygnl/talon/transports';\n * \n * const talon = new TalonClient({\n * transport: new WebSocketTransport('wss://edge.sygnl.io')\n * });\n * \n * // Send event TO edge\n * await talon.send({ event: 'add_to_cart', product_id: 'abc' });\n * \n * // Receive updates FROM edge\n * talon.on('attribution_updated', (data) => {\n * console.log('Attribution:', data);\n * });\n * ```\n */\nexport class TalonClient {\n private transport: TalonTransport;\n private eventHandlers: Map<string, Set<EventHandler>>;\n private messageUnsubscribe?: () => void;\n private stateUnsubscribe?: () => void;\n private errorUnsubscribe?: () => void;\n private debug: boolean;\n\n constructor(options: TalonClientOptions) {\n this.transport = options.transport;\n this.eventHandlers = new Map();\n this.debug = options.debug ?? false;\n\n // Subscribe to transport messages\n this.messageUnsubscribe = this.transport.onMessage(this.handleMessage.bind(this));\n this.stateUnsubscribe = this.transport.onStateChange(this.handleStateChange.bind(this));\n this.errorUnsubscribe = this.transport.onError(this.handleError.bind(this));\n\n // Auto-connect if requested\n if (options.autoConnect !== false) {\n this.connect().catch((error) => {\n this.log('Auto-connect failed:', error);\n });\n }\n }\n\n /**\n * Connect to the edge\n */\n public async connect(): Promise<void> {\n return this.transport.connect();\n }\n\n /**\n * Disconnect from the edge\n */\n public disconnect(): void {\n this.transport.disconnect();\n this.cleanup();\n }\n\n /**\n * Send event TO the edge\n * \n * @param event - Event data to send\n * @returns Promise that resolves when sent\n * \n * @example\n * ```typescript\n * await talon.send({\n * event: 'add_to_cart',\n * product_id: 'prod_123',\n * price: 99.99\n * });\n * ```\n */\n public async send(event: Record<string, unknown>): Promise<void> {\n const message: OutboundMessage = {\n type: 'event',\n data: event,\n id: this.generateId(),\n timestamp: Date.now(),\n };\n\n this.log('Sending message:', message);\n return this.transport.send(message);\n }\n\n /**\n * Subscribe to events FROM the edge\n * \n * @param eventType - Event type to listen for\n * @param handler - Function to call when event received\n * @returns Unsubscribe function\n * \n * @example\n * ```typescript\n * const unsubscribe = talon.on('attribution_updated', (data) => {\n * console.log('Attribution:', data);\n * });\n * \n * // Later: unsubscribe()\n * ```\n */\n public on<T = any>(eventType: string, handler: EventHandler<T>): () => void {\n if (!this.eventHandlers.has(eventType)) {\n this.eventHandlers.set(eventType, new Set());\n }\n\n this.eventHandlers.get(eventType)!.add(handler);\n this.log(`Subscribed to event: ${eventType}`);\n\n // Return unsubscribe function\n return () => {\n const handlers = this.eventHandlers.get(eventType);\n if (handlers) {\n handlers.delete(handler);\n if (handlers.size === 0) {\n this.eventHandlers.delete(eventType);\n }\n }\n this.log(`Unsubscribed from event: ${eventType}`);\n };\n }\n\n /**\n * Subscribe to event once (auto-unsubscribe after first call)\n */\n public once<T = any>(eventType: string, handler: EventHandler<T>): () => void {\n const wrappedHandler = (data: T) => {\n handler(data);\n unsubscribe();\n };\n\n const unsubscribe = this.on(eventType, wrappedHandler);\n return unsubscribe;\n }\n\n /**\n * Remove all handlers for an event type\n */\n public off(eventType: string): void {\n this.eventHandlers.delete(eventType);\n this.log(`Removed all handlers for: ${eventType}`);\n }\n\n /**\n * Get current connection state\n */\n public get state(): TransportState {\n return this.transport.state;\n }\n\n /**\n * Check if connected\n */\n public get connected(): boolean {\n return this.transport.state === 'connected';\n }\n\n /**\n * Handle incoming message from transport\n */\n private handleMessage(message: InboundMessage): void {\n this.log('Received message:', message);\n\n const handlers = this.eventHandlers.get(message.type);\n if (handlers && handlers.size > 0) {\n handlers.forEach((handler) => {\n try {\n handler(message.data);\n } catch (error) {\n this.log(`Error in handler for ${message.type}:`, error);\n }\n });\n } else {\n this.log(`No handlers for message type: ${message.type}`);\n }\n }\n\n /**\n * Handle transport state change\n */\n private handleStateChange(state: TransportState): void {\n this.log(`State changed: ${state}`);\n \n // Emit state change event\n const handlers = this.eventHandlers.get('_state_change');\n if (handlers) {\n handlers.forEach((handler) => handler({ state }));\n }\n }\n\n /**\n * Handle transport error\n */\n private handleError(error: Error): void {\n this.log('Transport error:', error);\n \n // Emit error event\n const handlers = this.eventHandlers.get('_error');\n if (handlers) {\n handlers.forEach((handler) => handler({ error }));\n }\n }\n\n /**\n * Cleanup subscriptions\n */\n private cleanup(): void {\n this.messageUnsubscribe?.();\n this.stateUnsubscribe?.();\n this.errorUnsubscribe?.();\n this.eventHandlers.clear();\n }\n\n /**\n * Generate unique message ID\n */\n private generateId(): string {\n return `msg_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;\n }\n\n /**\n * Debug logging\n */\n private log(message: string, data?: unknown): void {\n if (this.debug && typeof console !== 'undefined') {\n console.log(`[TalonClient] ${message}`, data || '');\n }\n }\n}\n"]}
|