@ioka-technologies/asyncapi-ts-client-template 0.0.7
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 +382 -0
- package/USAGE.md +586 -0
- package/examples/simple/asyncapi.yaml +256 -0
- package/package.json +97 -0
- package/template/index.js +284 -0
- package/template/src/client.ts.js +377 -0
- package/template/src/index.ts.js +13 -0
- package/template/src/models.ts.js +229 -0
- package/template/src/runtime/errors.ts.js +29 -0
- package/template/src/runtime/transports/factory.ts.js +33 -0
- package/template/src/runtime/transports/http.ts.js +122 -0
- package/template/src/runtime/transports/websocket.ts.js +329 -0
- package/template/src/runtime/types.ts.js +75 -0
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/* eslint-disable no-unused-vars */
|
|
2
|
+
import { File } from '@asyncapi/generator-react-sdk';
|
|
3
|
+
|
|
4
|
+
module.exports = function ({ asyncapi, params }) {
|
|
5
|
+
const transports = (params.transports || 'websocket,http').split(',').map(t => t.trim());
|
|
6
|
+
|
|
7
|
+
if (!transports.includes('http')) {
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
return (
|
|
12
|
+
<File name="http.ts">
|
|
13
|
+
{`import { Transport, TransportConfig, RequestOptions, MessageEnvelope } from '../types';
|
|
14
|
+
import { TransportError, ConnectionError, TimeoutError } from '../errors';
|
|
15
|
+
|
|
16
|
+
export class HttpTransport implements Transport {
|
|
17
|
+
private config: TransportConfig;
|
|
18
|
+
private baseUrl: string;
|
|
19
|
+
|
|
20
|
+
constructor(config: TransportConfig) {
|
|
21
|
+
this.config = config;
|
|
22
|
+
this.baseUrl = config.url;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async connect(): Promise<void> {
|
|
26
|
+
// HTTP doesn't require explicit connection
|
|
27
|
+
return Promise.resolve();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async disconnect(): Promise<void> {
|
|
31
|
+
// HTTP doesn't require explicit disconnection
|
|
32
|
+
return Promise.resolve();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async send(channel: string, envelope: MessageEnvelope, options?: RequestOptions): Promise<any> {
|
|
36
|
+
const url = \`\${this.baseUrl}/\${channel}\`;
|
|
37
|
+
const timeout = options?.timeout || 30000;
|
|
38
|
+
|
|
39
|
+
// Prepare the complete envelope for HTTP transport
|
|
40
|
+
const messageEnvelope: MessageEnvelope = {
|
|
41
|
+
...envelope,
|
|
42
|
+
channel: envelope.channel || channel,
|
|
43
|
+
timestamp: envelope.timestamp || new Date().toISOString(),
|
|
44
|
+
id: options?.correlationId || envelope.id
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const controller = new AbortController();
|
|
48
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
const response = await fetch(url, {
|
|
52
|
+
method: 'POST',
|
|
53
|
+
headers: {
|
|
54
|
+
'Content-Type': 'application/json',
|
|
55
|
+
'X-Operation': envelope.operation,
|
|
56
|
+
'X-Correlation-ID': messageEnvelope.id || '',
|
|
57
|
+
...this.config.headers
|
|
58
|
+
},
|
|
59
|
+
body: JSON.stringify(messageEnvelope),
|
|
60
|
+
signal: controller.signal
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
clearTimeout(timeoutId);
|
|
64
|
+
|
|
65
|
+
if (!response.ok) {
|
|
66
|
+
// Try to parse error response as envelope
|
|
67
|
+
try {
|
|
68
|
+
const errorEnvelope: MessageEnvelope = await response.json();
|
|
69
|
+
if (errorEnvelope.error) {
|
|
70
|
+
throw new TransportError(\`\${errorEnvelope.error.code}: \${errorEnvelope.error.message}\`);
|
|
71
|
+
}
|
|
72
|
+
} catch {
|
|
73
|
+
// Fall back to HTTP status error
|
|
74
|
+
throw new TransportError(\`HTTP request failed: \${response.status} \${response.statusText}\`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Parse response as envelope
|
|
79
|
+
const responseEnvelope: MessageEnvelope = await response.json();
|
|
80
|
+
|
|
81
|
+
// Check for envelope-level errors
|
|
82
|
+
if (responseEnvelope.error) {
|
|
83
|
+
throw new TransportError(\`\${responseEnvelope.error.code}: \${responseEnvelope.error.message}\`);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return responseEnvelope.payload;
|
|
87
|
+
} catch (error: any) {
|
|
88
|
+
clearTimeout(timeoutId);
|
|
89
|
+
|
|
90
|
+
if (error.name === 'AbortError') {
|
|
91
|
+
throw new TimeoutError(\`Request timeout after \${timeout}ms\`);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Re-throw TransportError as-is
|
|
95
|
+
if (error instanceof TransportError) {
|
|
96
|
+
throw error;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
throw new TransportError(\`HTTP request failed: \${error.message}\`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
subscribe(channel: string, callback: (envelope: MessageEnvelope) => void): () => void {
|
|
104
|
+
// HTTP transport doesn't support real-time subscriptions
|
|
105
|
+
// This is a placeholder implementation that logs a warning
|
|
106
|
+
console.warn(\`HTTP transport does not support subscriptions. Channel '\${channel}' subscription ignored.\`);
|
|
107
|
+
console.warn('Consider using WebSocket transport for real-time message subscriptions.');
|
|
108
|
+
|
|
109
|
+
// Return a no-op unsubscribe function
|
|
110
|
+
return () => {
|
|
111
|
+
// No-op for HTTP transport
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
unsubscribe(channel: string, callback?: (envelope: MessageEnvelope) => void): void {
|
|
116
|
+
// HTTP transport doesn't support subscriptions, so nothing to unsubscribe
|
|
117
|
+
console.warn(\`HTTP transport does not support subscriptions. Channel '\${channel}' unsubscribe ignored.\`);
|
|
118
|
+
}
|
|
119
|
+
}`}
|
|
120
|
+
</File>
|
|
121
|
+
);
|
|
122
|
+
}
|
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
/* eslint-disable no-unused-vars */
|
|
2
|
+
import { File } from '@asyncapi/generator-react-sdk';
|
|
3
|
+
|
|
4
|
+
module.exports = function ({ asyncapi, params }) {
|
|
5
|
+
const transports = (params.transports || 'websocket,http').split(',').map(t => t.trim());
|
|
6
|
+
|
|
7
|
+
if (!transports.includes('websocket')) {
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
return (
|
|
12
|
+
<File name="websocket.ts">
|
|
13
|
+
{`import { v4 as uuidv4 } from 'uuid';
|
|
14
|
+
import { Transport, TransportConfig, RequestOptions, ResponseHandler, MessageEnvelope, EnvelopeCallback } from '../types';
|
|
15
|
+
import { TransportError, ConnectionError, TimeoutError } from '../errors';
|
|
16
|
+
|
|
17
|
+
// Environment-aware WebSocket implementation
|
|
18
|
+
const getWebSocketImpl = (): typeof WebSocket => {
|
|
19
|
+
if (typeof window !== 'undefined' && window.WebSocket) {
|
|
20
|
+
// Browser environment - use native WebSocket
|
|
21
|
+
return window.WebSocket;
|
|
22
|
+
} else if (typeof global !== 'undefined') {
|
|
23
|
+
// Node.js environment - try to require ws
|
|
24
|
+
try {
|
|
25
|
+
return require('ws');
|
|
26
|
+
} catch (error) {
|
|
27
|
+
throw new Error('WebSocket implementation not available. In Node.js, please install the "ws" package: npm install ws');
|
|
28
|
+
}
|
|
29
|
+
} else {
|
|
30
|
+
throw new Error('WebSocket implementation not available in this environment');
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
// Type definition that works for both browser and Node.js WebSocket
|
|
35
|
+
type WebSocketLike = {
|
|
36
|
+
readonly readyState: number;
|
|
37
|
+
readonly OPEN: number;
|
|
38
|
+
send(data: string): void;
|
|
39
|
+
close(): void;
|
|
40
|
+
addEventListener?(type: string, listener: (event: any) => void): void;
|
|
41
|
+
removeEventListener?(type: string, listener: (event: any) => void): void;
|
|
42
|
+
on?(event: string, listener: (...args: any[]) => void): void;
|
|
43
|
+
off?(event: string, listener: (...args: any[]) => void): void;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export class WebSocketTransport implements Transport {
|
|
47
|
+
private ws: WebSocketLike | null = null;
|
|
48
|
+
private WebSocketImpl: typeof WebSocket;
|
|
49
|
+
private config: TransportConfig;
|
|
50
|
+
private responseHandlers: Map<string, ResponseHandler> = new Map();
|
|
51
|
+
private subscriptions: Map<string, Set<EnvelopeCallback>> = new Map();
|
|
52
|
+
private operationSubscriptions: Map<string, Set<(payload: any) => void>> = new Map();
|
|
53
|
+
private reconnectAttempts = 0;
|
|
54
|
+
private maxReconnectAttempts = 5;
|
|
55
|
+
private reconnectDelay = 1000;
|
|
56
|
+
|
|
57
|
+
constructor(config: TransportConfig) {
|
|
58
|
+
this.config = config;
|
|
59
|
+
this.WebSocketImpl = getWebSocketImpl();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async connect(): Promise<void> {
|
|
63
|
+
return new Promise((resolve, reject) => {
|
|
64
|
+
try {
|
|
65
|
+
this.ws = new this.WebSocketImpl(this.config.url) as WebSocketLike;
|
|
66
|
+
|
|
67
|
+
// Handle both browser and Node.js WebSocket APIs
|
|
68
|
+
if (typeof window !== 'undefined' && this.ws.addEventListener) {
|
|
69
|
+
// Browser WebSocket API
|
|
70
|
+
this.ws.addEventListener('open', () => {
|
|
71
|
+
this.reconnectAttempts = 0;
|
|
72
|
+
resolve();
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
this.ws.addEventListener('message', (event: MessageEvent) => {
|
|
76
|
+
this.handleMessage(event.data);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
this.ws.addEventListener('close', () => {
|
|
80
|
+
this.handleDisconnect();
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
this.ws.addEventListener('error', (event: Event) => {
|
|
84
|
+
reject(new ConnectionError(\`WebSocket connection failed: \${event.type}\`));
|
|
85
|
+
});
|
|
86
|
+
} else if (this.ws.on) {
|
|
87
|
+
// Node.js ws library API
|
|
88
|
+
this.ws.on('open', () => {
|
|
89
|
+
this.reconnectAttempts = 0;
|
|
90
|
+
resolve();
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
this.ws.on('message', (data: any) => {
|
|
94
|
+
this.handleMessage(data.toString());
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
this.ws.on('close', () => {
|
|
98
|
+
this.handleDisconnect();
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
this.ws.on('error', (error: any) => {
|
|
102
|
+
reject(new ConnectionError(\`WebSocket connection failed: \${error.message}\`));
|
|
103
|
+
});
|
|
104
|
+
} else {
|
|
105
|
+
reject(new ConnectionError('WebSocket implementation does not support required event handling'));
|
|
106
|
+
}
|
|
107
|
+
} catch (error: any) {
|
|
108
|
+
reject(new ConnectionError(\`Failed to create WebSocket: \${error.message}\`));
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async disconnect(): Promise<void> {
|
|
114
|
+
if (this.ws) {
|
|
115
|
+
this.ws.close();
|
|
116
|
+
this.ws = null;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async send(channel: string, envelope: MessageEnvelope, options?: RequestOptions): Promise<any> {
|
|
121
|
+
if (!this.ws || this.ws.readyState !== this.ws.OPEN) {
|
|
122
|
+
throw new TransportError('WebSocket is not connected');
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Ensure envelope has required fields
|
|
126
|
+
const requestId = options?.correlationId || envelope.id || uuidv4();
|
|
127
|
+
const messageEnvelope: MessageEnvelope = {
|
|
128
|
+
...envelope,
|
|
129
|
+
id: requestId,
|
|
130
|
+
channel: envelope.channel || channel,
|
|
131
|
+
timestamp: envelope.timestamp || new Date().toISOString()
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
return new Promise((resolve, reject) => {
|
|
135
|
+
const timeout = options?.timeout || 30000;
|
|
136
|
+
const timeoutId = setTimeout(() => {
|
|
137
|
+
this.responseHandlers.delete(requestId);
|
|
138
|
+
reject(new TimeoutError(\`Request timeout after \${timeout}ms\`));
|
|
139
|
+
}, timeout);
|
|
140
|
+
|
|
141
|
+
this.responseHandlers.set(requestId, {
|
|
142
|
+
resolve: (data) => {
|
|
143
|
+
clearTimeout(timeoutId);
|
|
144
|
+
resolve(data);
|
|
145
|
+
},
|
|
146
|
+
reject: (error) => {
|
|
147
|
+
clearTimeout(timeoutId);
|
|
148
|
+
reject(error);
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
this.ws!.send(JSON.stringify(messageEnvelope));
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
subscribe(channel: string, callback: (envelope: MessageEnvelope) => void): () => void {
|
|
157
|
+
if (!this.subscriptions.has(channel)) {
|
|
158
|
+
this.subscriptions.set(channel, new Set());
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
this.subscriptions.get(channel)!.add(callback);
|
|
162
|
+
|
|
163
|
+
// Send subscription message to server using envelope format
|
|
164
|
+
if (this.ws && this.ws.readyState === this.ws.OPEN) {
|
|
165
|
+
const subscribeEnvelope: MessageEnvelope = {
|
|
166
|
+
operation: 'subscribe',
|
|
167
|
+
channel,
|
|
168
|
+
payload: { channel },
|
|
169
|
+
timestamp: new Date().toISOString()
|
|
170
|
+
};
|
|
171
|
+
this.ws.send(JSON.stringify(subscribeEnvelope));
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Return unsubscribe function
|
|
175
|
+
return () => {
|
|
176
|
+
this.unsubscribe(channel, callback);
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
unsubscribe(channel: string, callback?: (envelope: MessageEnvelope) => void): void {
|
|
181
|
+
const channelSubscriptions = this.subscriptions.get(channel);
|
|
182
|
+
if (!channelSubscriptions) {
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (callback) {
|
|
187
|
+
channelSubscriptions.delete(callback);
|
|
188
|
+
if (channelSubscriptions.size === 0) {
|
|
189
|
+
this.subscriptions.delete(channel);
|
|
190
|
+
this.sendUnsubscribeMessage(channel);
|
|
191
|
+
}
|
|
192
|
+
} else {
|
|
193
|
+
// Unsubscribe all callbacks for this channel
|
|
194
|
+
this.subscriptions.delete(channel);
|
|
195
|
+
this.sendUnsubscribeMessage(channel);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Subscribe to a specific operation on a channel
|
|
201
|
+
* This provides operation-based filtering on the client side
|
|
202
|
+
*/
|
|
203
|
+
subscribeToOperation(channel: string, operation: string, callback: (payload: any) => void): () => void {
|
|
204
|
+
const operationKey = \`\${channel}:\${operation}\`;
|
|
205
|
+
|
|
206
|
+
if (!this.operationSubscriptions.has(operationKey)) {
|
|
207
|
+
this.operationSubscriptions.set(operationKey, new Set());
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
this.operationSubscriptions.get(operationKey)!.add(callback);
|
|
211
|
+
|
|
212
|
+
// Subscribe to the channel if not already subscribed
|
|
213
|
+
if (!this.subscriptions.has(channel)) {
|
|
214
|
+
this.subscribe(channel, (envelope: MessageEnvelope) => {
|
|
215
|
+
this.handleOperationMessage(envelope);
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Return unsubscribe function
|
|
220
|
+
return () => {
|
|
221
|
+
const opCallbacks = this.operationSubscriptions.get(operationKey);
|
|
222
|
+
if (opCallbacks) {
|
|
223
|
+
opCallbacks.delete(callback);
|
|
224
|
+
if (opCallbacks.size === 0) {
|
|
225
|
+
this.operationSubscriptions.delete(operationKey);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
private handleOperationMessage(envelope: MessageEnvelope): void {
|
|
232
|
+
if (!envelope.operation || !envelope.channel) {
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const operationKey = \`\${envelope.channel}:\${envelope.operation}\`;
|
|
237
|
+
const operationCallbacks = this.operationSubscriptions.get(operationKey);
|
|
238
|
+
|
|
239
|
+
if (operationCallbacks) {
|
|
240
|
+
operationCallbacks.forEach(callback => {
|
|
241
|
+
try {
|
|
242
|
+
callback(envelope.payload);
|
|
243
|
+
} catch (error) {
|
|
244
|
+
console.error(\`Error in operation callback for \${envelope.operation}:\`, error);
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
private sendUnsubscribeMessage(channel: string): void {
|
|
251
|
+
if (this.ws && this.ws.readyState === this.ws.OPEN) {
|
|
252
|
+
const unsubscribeMessage = {
|
|
253
|
+
type: 'unsubscribe',
|
|
254
|
+
channel,
|
|
255
|
+
timestamp: new Date().toISOString()
|
|
256
|
+
};
|
|
257
|
+
this.ws.send(JSON.stringify(unsubscribeMessage));
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
private handleMessage(data: string): void {
|
|
262
|
+
try {
|
|
263
|
+
const envelope: MessageEnvelope = JSON.parse(data);
|
|
264
|
+
|
|
265
|
+
// Handle response messages (with ID for request/response correlation)
|
|
266
|
+
if (envelope.id) {
|
|
267
|
+
const handler = this.responseHandlers.get(envelope.id);
|
|
268
|
+
if (handler) {
|
|
269
|
+
this.responseHandlers.delete(envelope.id);
|
|
270
|
+
if (envelope.error) {
|
|
271
|
+
handler.reject(new TransportError(\`\${envelope.error.code}: \${envelope.error.message}\`));
|
|
272
|
+
} else {
|
|
273
|
+
handler.resolve(envelope.payload);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Handle subscription messages (broadcast messages without correlation ID)
|
|
280
|
+
if (envelope.channel) {
|
|
281
|
+
const channelSubscriptions = this.subscriptions.get(envelope.channel);
|
|
282
|
+
if (channelSubscriptions) {
|
|
283
|
+
channelSubscriptions.forEach(callback => {
|
|
284
|
+
try {
|
|
285
|
+
callback(envelope);
|
|
286
|
+
} catch (error) {
|
|
287
|
+
console.error(\`Error in subscription callback for channel \${envelope.channel}:\`, error);
|
|
288
|
+
}
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Also handle operation-based subscriptions
|
|
293
|
+
this.handleOperationMessage(envelope);
|
|
294
|
+
}
|
|
295
|
+
} catch (error) {
|
|
296
|
+
console.error('Failed to parse WebSocket message:', error);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
private handleDisconnect(): void {
|
|
301
|
+
if (this.reconnectAttempts < this.maxReconnectAttempts) {
|
|
302
|
+
this.reconnectAttempts++;
|
|
303
|
+
setTimeout(() => {
|
|
304
|
+
this.connect().then(() => {
|
|
305
|
+
// Re-subscribe to all channels after reconnection
|
|
306
|
+
this.resubscribeAll();
|
|
307
|
+
}).catch(() => {
|
|
308
|
+
// Failed to reconnect
|
|
309
|
+
});
|
|
310
|
+
}, this.reconnectDelay * this.reconnectAttempts);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
private resubscribeAll(): void {
|
|
315
|
+
if (this.ws && this.ws.readyState === this.ws.OPEN) {
|
|
316
|
+
for (const channel of this.subscriptions.keys()) {
|
|
317
|
+
const subscribeMessage = {
|
|
318
|
+
type: 'subscribe',
|
|
319
|
+
channel,
|
|
320
|
+
timestamp: new Date().toISOString()
|
|
321
|
+
};
|
|
322
|
+
this.ws.send(JSON.stringify(subscribeMessage));
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}`}
|
|
327
|
+
</File>
|
|
328
|
+
);
|
|
329
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/* eslint-disable no-unused-vars */
|
|
2
|
+
import { File } from '@asyncapi/generator-react-sdk';
|
|
3
|
+
|
|
4
|
+
module.exports = function ({ asyncapi, params }) {
|
|
5
|
+
return (
|
|
6
|
+
<File name="types.ts">
|
|
7
|
+
{`/**
|
|
8
|
+
* Standard message envelope for all AsyncAPI communications
|
|
9
|
+
*/
|
|
10
|
+
export interface MessageEnvelope {
|
|
11
|
+
operation: string; // AsyncAPI operation ID
|
|
12
|
+
id?: string; // Correlation ID for request/response
|
|
13
|
+
channel?: string; // Optional channel context
|
|
14
|
+
payload: any; // Message payload
|
|
15
|
+
timestamp?: string; // Optional ISO 8601 timestamp
|
|
16
|
+
error?: { // Error information
|
|
17
|
+
code: string;
|
|
18
|
+
message: string;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Transport interface for sending and receiving messages
|
|
24
|
+
*/
|
|
25
|
+
export interface Transport {
|
|
26
|
+
connect(): Promise<void>;
|
|
27
|
+
disconnect(): Promise<void>;
|
|
28
|
+
send(channel: string, envelope: MessageEnvelope, options?: RequestOptions): Promise<any>;
|
|
29
|
+
subscribe(channel: string, callback: (envelope: MessageEnvelope) => void): () => void;
|
|
30
|
+
unsubscribe(channel: string, callback?: (envelope: MessageEnvelope) => void): void;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Configuration for transport implementations
|
|
35
|
+
*/
|
|
36
|
+
export interface TransportConfig {
|
|
37
|
+
type: 'websocket' | 'http';
|
|
38
|
+
url: string;
|
|
39
|
+
headers?: Record<string, string>;
|
|
40
|
+
timeout?: number;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Options for individual requests
|
|
45
|
+
*/
|
|
46
|
+
export interface RequestOptions {
|
|
47
|
+
timeout?: number;
|
|
48
|
+
correlationId?: string; // Override correlation ID
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Internal response handler for request/response patterns
|
|
53
|
+
*/
|
|
54
|
+
export interface ResponseHandler {
|
|
55
|
+
resolve: (data: any) => void;
|
|
56
|
+
reject: (error: Error) => void;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Callback function for message subscriptions
|
|
61
|
+
*/
|
|
62
|
+
export type MessageCallback = (payload: any) => void;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Function to unsubscribe from a channel
|
|
66
|
+
*/
|
|
67
|
+
export type UnsubscribeFunction = () => void;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Envelope callback for transport-level message handling
|
|
71
|
+
*/
|
|
72
|
+
export type EnvelopeCallback = (envelope: MessageEnvelope) => void;`}
|
|
73
|
+
</File>
|
|
74
|
+
);
|
|
75
|
+
}
|